文件 v0.4.0

核心概念

了解所有 go-llm-router 供應商共用的合約與正規化規則。

Agent 合約

所有供應商都實作相同介面:

type Agent interface {
    Name() string
    Send(
        ctx context.Context,
        messages []Message,
        toolDefs []Tool,
        reasoning Reasoning,
        mode Mode,
    ) (*Output, int, error)
}

Send 接受 context、聊天訊息、可選的工具定義、正規化的推理強度與執行模式,回傳正規化的 Output、HTTP status code 與 error。呼叫端因此可以替換供應商,而不必改寫請求迴圈。

支援串流的供應商額外實作 StreamAgent,參數與 Send 完全一致:

type StreamAgent interface {
    SendStream(
        ctx context.Context,
        messages []Message,
        toolDefs []Tool,
        reasoning Reasoning,
        mode Mode,
    ) (<-chan StreamEvent, error)
}

core/claudecore/copilot 對外提供 SendStreamcore/openaiCodexcore/grokOauth 在內部消化上游 SSE,仍以 Send 回傳完整的 core.Output

事件型別 內容
StreamEventText TextDelta 輸出文字增量
StreamEventReasoning ReasoningDelta 推理摘要增量
StreamEventToolCall ToolCall 增量,含 index、ID、名稱或參數片段
StreamEventUsage 正規化的 Usage
StreamEventDone FinishReason
StreamEventError Err

訊息與多模態內容

Message 包含 Role、任意型別的 Content、可選的 ReasoningContent、模型產生的 ToolCalls,以及工具結果使用的 ToolCallIDContent 可以是字串或供應商支援的結構化內容。多模態請求使用 ContentPart,其中含 text 或帶 URL 與可選 DetailImageURL

messages := []core.Message{
    {Role: "user", Content: "Describe this image."},
    {Role: "user", Content: []core.ContentPart{
        {Type: "text", Text: "What is shown here?"},
        {Type: "image_url", ImageURL: &core.ImageURL{URL: imageURL}},
    }},
}

供應商路由

provider@model 名稱呼叫 router.New。選擇供應商時會忽略可選的方括號標籤,因此 claude[eu]@claude-sonnet-5 仍路由至 claudecompat@model 走 OpenAI 相容實作,並使用 BaseURL

目前的 router key:claudeopenaigeminigrokdeepseeknvidiaopenroutercloudflarecompatcopilotcodexgrok-oauth。未知的 key 由 router.New 回傳錯誤。

推理強度

Reasoning 是具型別的列舉,取代早期的字串參數:

常數 字面值 別名
ReasoningNone none
ReasoningLow low minimal
ReasoningMedium medium
ReasoningHigh high
ReasoningXHigh xhigh extra
ReasoningMax max ultra

ReasoningDefaultReasoningMediumParseReasoning 解析字面值與別名,第二個回傳值標示是否命中;未命中時回傳預設值。

每個模型支援的區間不同。供應商適配器實作 ReasoningAgent,對外公開自身上下限:

type ReasoningAgent interface {
    ReasoningLimits() (min, max Reasoning)
}

送出前由 ClampReasoning(r, lo, hi, provider, model) 夾到區間內,被夾住時以 slog 記錄 debug 訊息。呼叫端只需傳共用強度,不必自行組供應商欄位——Claude 走 thinking budget 或 output_config.effort、OpenAI 走 reasoning_effort 或 Responses reasoning.effort、Gemini 走 thinkingLevelthinkingBudget,皆由適配器轉換。

執行模式與加速層

Mode 描述要求的服務層級:ModeDefaultModeFastParseMode 解析 defaultfast

ModeFast 是能力請求。適配器先以 core.SupportFast(provider, model) 比對白名單,命中才加上供應商原生欄位;未命中則靜默維持標準層,不回傳錯誤——未列名的模型有些會直接拒絕該參數(例如 Claude Opus 4.7 回 400),事前擋掉比事後處理錯誤便宜。

路由 原生控制 白名單
Claude speed: "fast" + beta header fast-mode-2026-02-01 Opus 5、Opus 4.8
OpenAI service_tier: "fast" gpt-5.4 以上;codex 變體 5.3 以上;排除 -pro-nano
Grok、Grok OAuth service_tier: "priority" 文字推理模型
OpenRouter service_tier: "priority" openai/google/x-ai/ 上游,再套用該上游的白名單
其他適配器 不送任何層級欄位

Gemini 的模型清單保留在 SupportFast 內,用於判斷 OpenRouter 的 google/ 路由;Gemini 適配器本身目前不送層級欄位。

供應商不保證請求值等於實際服務層級,因此歸因一律讀回應:OpenAI、Grok、OpenRouter 讀頂層 service_tier(正規化後放入 Output.ServiceTier),Claude 讀 usage.speed。回報值非 fast/priority 時,core.WarnFastDowngradeslog 記錄降級警告;欄位為空代表供應商未回報,不視為降級。

工具呼叫

ToolTypeToolFunction(名稱、描述、JSON Schema 參數)組成。模型回傳的呼叫落在 Output.Choices[*].Message.ToolCalls。典型迴圈是把 assistant 訊息送回,逐一執行函式,再以 ToolCallID 送出 tool 訊息。

tools := []core.Tool{{
    Type: "function",
    Function: core.ToolFunction{
        Name:        "get_weather",
        Description: "Look up weather for a city",
        Parameters:  json.RawMessage(`{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}`),
    },
}}

out, _, err := agent.Send(ctx, messages, tools, core.ReasoningMedium, core.ModeDefault)
if err != nil {
    return err
}
for _, call := range out.Choices[0].Message.ToolCalls {
    fmt.Println(call.Function.Name, call.Function.Arguments)
}

Gemini 的 thinking 模型會在 ToolCall.ThoughtSignature 帶回簽章,續傳時需原樣送回。

用量正規化

各家 token 計數欄位命名不同。Usage.UnmarshalJSON 吸收 input_tokens / output_tokens、OpenAI 風格的 prompt_tokens / completion_tokens、cache 建立與 cache 讀取欄位,合併為單一結構:

欄位 意義
Input 非快取的 input token
Output 產生的 output token
CacheCreate 寫入 prompt cache 的 token
CacheRead 由 cache 讀取的 input token

計費與 UI 程式碼因此可直接消費 output.Usage,不必依供應商分支。

模型探索

各供應商套件提供 Models(ctx, config, filter) 回傳模型 ID 清單;core/copilotcore/gemini 另提供 ModelInfos,回傳含 ThinkingEffortsEndpointscore.ModelInfo

core.ModelFilter{TextOnly: true} 會透過 core.IsTextModel 濾除影像、語音、影片、embedding 等非文字模型。

OAuth 與擴充

core/oauth/copilotcore/oauth/codexcore/oauth/grok 提供登入、載入、刷新與清除。Token 物件透過 router.Config.Token 傳入;CodexTokenGrokToken 提供 Expired(),並保留 60 秒安全緩衝。

core/openaiCodex 的 Agent 額外提供 GenerateImage,回傳 base64 影像資料與修訂後的 prompt。

相關頁面

EN