LLM Driver Integration#
RAGFlow maintains two parallel LLM driver stacks: a Python layer (rag/llm/) for document processing, embeddings, and the main chat API, and a Go layer (internal/entity/models/) for the agent model runner. Both expose the same logical operations — chat, embed, rerank — but are implemented independently: the Go layer uses hand-rolled HTTP clients against provider REST APIs, while Python relies on SDK wrappers and LiteLLM.
Go Layer Architecture#
ModelDriver Interface#
All Go providers implement the ModelDriver interface in internal/entity/models/types.go, which declares 17 methods covering the full LLM capability surface: ChatWithMessages, ChatStreamlyWithSender, Embed, Rerank, TranscribeAudio, AudioSpeech, OCRFile, ParseFile, ListModels, Balance, CheckConnection, and others. Providers that don't implement a capability return a "no such method" error rather than omitting the method.
BaseModel: Shared Infrastructure#
BaseModel in internal/entity/models/base_model.go provides the scaffolding shared by every provider driver:
- HTTP client:
NewDriverHTTPClient()cloneshttp.DefaultTransportand setsMaxIdleConns=100,MaxIdleConnsPerHost=10,IdleConnTimeout=90s,ResponseHeaderTimeout=2min. No client-wide timeout is set so SSE streams are never cut short; non-streaming calls usecontext.WithTimeoutindividually. - Request building:
buildRequestBody()produces an OpenAI-compatible request map withmodel,messages,stream,temperature,max_tokens,top_p,stop,tools, andtool_choice. - SSE parsing:
ParseSSEStream[T]is a generic parser that errors on malformed JSON events.ParseSSEStreamTolerant[T]silently skips bad frames and is reserved for providers whose upstreams are documented to send invalid frames. - Tool call accumulation:
accumulateToolCallDeltas,setSortedToolCallsResult, andextractToolCallsreconstruct tool-call payloads across streaming delta events. - Auth helper:
BearerAuth()formats theAuthorization: Bearer <key>header value, returning an empty string when no key is configured.
Factory and Registry#
ModelFactory.CreateModelDriver() maps 60+ provider names to their constructor functions via a switch statement, falling back to a DummyModel for unknown providers. The singleton ProviderManager reads JSON provider configuration files at startup, instantiates drivers via the factory, and exposes FindProvider, FindModel, and ListModels for runtime lookup.
Provider-specific wrapper types — ChatModel, EmbeddingModel, RerankModel — pair a ModelDriver with task-specific configuration and token usage tracking. ToolConfig defaults to 5 max rounds and 3 retries.
Baidu ERNIE: Go Driver#
File: internal/entity/models/baidu.go — driver name "BaiduYiyan" .
The Go Baidu driver is a fully hand-rolled HTTP client. All requests use Authorization: Bearer <api_key> and a URL constructed from a configurable base URL plus a provider-specific suffix.
Thinking/reasoning parameter mapping : The driver inspects the model name to pick the right parameter format:
ernie/qwenprefix →enable_thinking: true|false- Other models →
thinking: {"type": "enabled"|"disabled"} deepseek-v4additionally setsreasoning_effort: "high"|"max"|…
Streaming uses ParseSSEStream, validates that the stream ends with either [DONE] or a non-empty finish_reason to detect truncated streams , and appends a [DONE] marker for OpenAI downstream compatibility .
Embeddings post to the embedding URL suffix and enforce input-order preservation via the Index field in the response .
OCR accepts either a file URL or raw bytes (base64-encoded), auto-detects PDF vs. image via MIME type, and extracts text from layoutParsingResults[0].markdown.text .
Not implemented: TranscribeAudio, AudioSpeech, ParseFile, Balance, ListTasks, ShowTask — these return "BaiduYiyan, no such method" .
Baidu ERNIE: Python Driver#
Class: BaiduYiyanChat in rag/llm/chat_model.py, _FACTORY_NAME = "BaiduYiyan".
Unlike the Go driver, the Python layer uses the Qianfan SDK (qianfan.ChatCompletion) rather than direct HTTP . Credentials are a JSON blob with yiyan_ak and yiyan_sk keys — incompatible with the Go driver's raw API key format .
Parameter translation :
penalty_score = ((presence_penalty + frequency_penalty) / 2) + 1max_tokensis stripped (Baidu SDK uses a different field name)
Streaming calls self.client.do(..., stream=True) and iterates over resp.body["result"] chunks . The async path wraps the blocking SDK call in asyncio.to_thread() to avoid blocking the event loop .
The Python Baidu driver covers chat only; embedding and reranking for BaiduYiyan use separate classes in embedding_model.py and rerank_model.py backed by the same Qianfan SDK.
Python Layer: Broader Provider Support#
Most providers in the Python layer route through LiteLLMBase in chat_model.py. Three dictionaries in rag/llm/__init__.py define the catalog :
| Dict | Purpose |
|---|---|
SupportedLiteLLMProvider | Authoritative enum of all LiteLLM-routed providers |
LITELLM_PROVIDER_PREFIX | Maps each provider to its LiteLLM model prefix (e.g., azure/, ollama_chat/) |
FACTORY_DEFAULT_BASE_URL | Default API base URLs; empty string for self-hosted providers requiring user-supplied URLs |
At import time, rag/llm/__init__.py auto-discovers all _FACTORY_NAME-annotated subclasses of Base across chat_model, embedding_model, and related modules, and registers them in the ChatModel, EmbeddingModel, and other factory dicts .
The ALLOWED_GEN_CONF_KEYS allowlist (temperature, max_completion_tokens, top_p, stream, stop, tools, tool_choice, etc.) filters generation config before forwarding to providers, preventing provider rejection errors from RAGFlow-internal metadata keys.
For provider-specific credential handling and API versioning details, see the Azure OpenAI Integration and LLM Provider Integration knowledge base articles.
Cross-Layer Comparison: Baidu ERNIE#
| Concern | Python BaiduYiyanChat | Go BaiduModel |
|---|---|---|
| HTTP | Qianfan SDK | Native net/http |
| Auth | AK+SK via SDK | Authorization: Bearer <api_key> |
| Credential format | {"yiyan_ak":…,"yiyan_sk":…} JSON blob | Raw API key string |
| Thinking parameter | SDK handles internally | enable_thinking (ernie/qwen) or thinking:{type} + reasoning_effort |
| Streaming | SDK iterator over resp.body | Generic ParseSSEStream with [DONE] marker |
| Supported ops | Chat only | Chat, Embed, Rerank, OCR, ListModels |
| Async | asyncio.to_thread() wrapping blocking SDK | Native Go goroutines with context cancellation |
The two drivers are not interchangeable at runtime: the Python layer is invoked by the document-processing and REST API service, while the Go layer is used exclusively by the agent model runner. Credential formats differ between the two, so users configuring BaiduYiyan must supply the correct format for each runtime.