// Package types — 统一线格式 v2(最大集合 / maximal set) // // 设计原则(feature/unified-wire-v2): // // 内核只调度,不知道任何具体协议。 // 适配器负责两个方向的协议转换: // 入向 上游原生格式 → 本文件的统一格式 // 出向 本文件的统一格式 → 对外服务协议 // // 因此本文件定义的是**市面所有主流 LLM 协议所需字段的并集**: // OpenAI Chat Completions / Responses、Anthropic Messages、Gemini // generateContent、Ollama、DeepSeek(reasoning_content)等。 // 适配器**按需取用**——每个具体协议都是这个超集的一个子集。 // // 命名约定(重要): // - 字段名取"该能力在业界最广为人知的拼写",便于适配器直接映射, // 而不是为中立而中立地另造一套名字(那会让每个适配器都要维护一张 // 翻译表,反而更容易出错)。 // - 但**协议专属容器**(如 Gemini 的 generationConfig、Ollama 的 options) // 不进入统一格式:它们是"上游协议的形状",属于适配器的内部事务。 // 统一格式只保留**语义**(temperature / top_k / max_tokens …), // 由适配器决定装进哪个容器。 // // 与 v1 的关键差异: // - 内核私有字段不再泄漏给适配器(v1 的 disable_thinking / extra_body // 会被序列化进请求体,10 个适配器被迫各自 = nil 清理) // - 新增 Anthropic/Gemini/Ollama 需要的字段(system / top_k / // stop_sequences / safety_settings / seed …),使适配器不必再从 // extra_body 这种旁路偷字段 package types import "encoding/json" // UnifiedRequest 是内核交给适配器的**完整请求**(最大集合)。 // // 适配器只读它需要的字段;未用到的字段是"内核提供了但你这条协议不用", // 不是"内核不知道该字段"。这个方向的信息损失为零。 type UnifiedRequest struct { Model string `json:"model"` Messages []ChatMessage `json:"messages"` // ---- 采样参数(所有协议都有,名称归一) ---- Temperature *float64 `json:"temperature,omitempty"` TopP *float64 `json:"top_p,omitempty"` TopK int `json:"top_k,omitempty"` // Gemini/Ollama/Anthropic 有 MaxTokens int `json:"max_tokens,omitempty"` // Anthropic 必需 FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"` // OpenAI PresencePenalty *float64 `json:"presence_penalty,omitempty"` // OpenAI Seed *int64 `json:"seed,omitempty"` // OpenAI/Gemini // ---- 停止条件 ---- Stop []string `json:"stop,omitempty"` // OpenAI StopSequences []string `json:"stop_sequences,omitempty"` // Anthropic // ---- 工具调用 ---- // Tools 保留 json.RawMessage(宽松的原始形状)而不是结构化的 []ToolDef, // 原因是**渐进迁移**:内核现有的 12 个适配器与 gateway 都按 []interface{} // /json.RawMessage 形状序列化请求到适配器。若此处改成 []ToolDef,则 // 所有适配器与调用点立即编译失败,必须同步改写 19 个非测试点 + 30 个测试。 // 而 Tools 的真实消费者是适配器(transform_request),内核本身从不解析它, // 所以用原始形状不损失任何内核侧的统一性——它本就是"原样交给适配器的载荷"。 // // ToolDef 类型保留在此包内供新适配器选用(maximal-set 的结构化语义), // 但内核序列化路径暂不强推它。 Tools json.RawMessage `json:"tools,omitempty"` ToolChoice json.RawMessage `json:"tool_choice,omitempty"` // ParallelToolCalls *bool 允许显式 false(OpenAI 语义)。 ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty"` // OpenAI // ---- 结构化输出 ---- ResponseFormat *ResponseFormat `json:"response_format,omitempty"` // OpenAI // ---- 系统提示 ---- // Anthropic 与 Gemini 都把 system 放在顶层而非 messages 里; // OpenAI 系则放进 messages[role=system]。统一格式同时提供两条路: // System 保存语义,Messages 里可能也有一条 role=system。 // 适配器按自己的协议取用(Anthropic 取 System 并递归剥离 messages 里的 // system;OpenAI 直接用 messages)。 System string `json:"system,omitempty"` // ---- 推理/思考 ---- // Thinking 取代 v1 的内核私有字段 DisableThinking:那个字段曾被序列化进 // 交给适配器的 body,迫使 10 个适配器各自写 req.disable_thinking = nil。 // 这里它是双方都认可的对称语义(客户端要什么 / 上游怎么映射)。 // Thinking 是"要不要思考、思考预算多少"的语义; // 适配器各自映射(Anthropic thinking.budget_tokens、Gemini // thinkingConfig.thinkingBudget、DeepSeek extra_body.thinking.type)。 Thinking *ThinkingConfig `json:"thinking,omitempty"` // ---- 多模态 ---- // 图片/音频等随 message content 的 parts 传递(见 ChatMessage.Content), // 这里放协议级的生成参数(如 Gemini candidateCount、OpenAI n)。 GenerationCount int `json:"n,omitempty"` // ---- 厂商扩展逃生舱 ---- // ExtraBody 是**明确标注的**厂商透传通道:客户端可以把它不认识的字段 // 塞进来,适配器自行决定是否读取。它不参与"内核私有字段"泄漏问题, // 因为它的语义就是"客户端原样要求的额外字段"。 // // 与 v1 的区别:v1 的 ExtraBody 会被 MarshalJSON 手工注入到 OpenAI 形状 // 的顶层,从而和内核私有字段混在一起;v2 里它是统一格式里的一个**明确定义 // 的字段**,内核不再需要 MarsalJSON 后门。 ExtraBody map[string]any `json:"extra_body,omitempty"` // ---- 网关内部(不序列化,不交给适配器 body) ---- // ClientSession 只用于让适配器生成稳定的上游会话,不能出现在上游 body 里。 ClientSession string `json:"-"` // Stream 由网关按客户端请求设置;适配器负责映射到上游的流式开关。 Stream bool `json:"stream,omitempty"` } // ThinkingConfig 是推理预算的语义表示(跨协议)。 // // Enabled=false 表示客户端明确要求关闭思考。不同上游的关闭方式不一 // (DeepSeek 用 extra_body.thinking.type=disabled,Anthropic 直接不传 // thinking 字段,Gemini 用 thinkingBudget=0),映射由适配器负责。 type ThinkingConfig struct { Enabled *bool `json:"enabled,omitempty"` BudgetTokens int64 `json:"budget_tokens,omitempty"` } // ToolDef 是工具声明的统一形状(OpenAI 的 {type,function} 外壳被摊平)。 type ToolDef struct { Name string `json:"name"` Description string `json:"description,omitempty"` // Parameters 是 JSON Schema,原样透传。 Parameters json.RawMessage `json:"parameters,omitempty"` // Type 保留协议侧的 kind(OpenAI 恒为 "function";其它协议可能为空)。 Type string `json:"type,omitempty"` } // ResponseFormat 是结构化输出的统一形状。 type ResponseFormat struct { Type string `json:"type"` // text | json_object | json_schema JSONSchema json.RawMessage `json:"json_schema,omitempty"` } // UnifiedResult 是适配器从上游响应转换出来的**统一结果**(最大集合)。 // // 这是 v1 的 UnifiedResponse 的超集:v1 只有 content/finish_reason/ // token_usage/tool_calls;v2 补齐各协议会产出的其它语义。 type UnifiedResult struct { Model string `json:"model,omitempty"` Content string `json:"content"` Thinking string `json:"thinking,omitempty"` // FinishReason 是**归一后**的结束原因:stop / length / tool_calls / // content_filter / 空。适配器负责把上游措辞映射到这套词表 // (如 Anthropic 的 end_turn→stop、max_tokens→length、 // tool_use→tool_calls;Gemini 的 STOP→stop、MAX_TOKENS→length)。 FinishReason string `json:"finish_reason,omitempty"` TokenUsage TokenUsage `json:"token_usage"` ToolCalls []ToolCall `json:"tool_calls,omitempty"` // ---- 其它协议可能产出的语义 ---- // Citations 是 Anthropic 的引用块、Gemini 的 grounding。 Citations []json.RawMessage `json:"citations,omitempty"` // Refusal 是 OpenAI 的 refusal 字段(内容被安全策略拒绝)。 Refusal string `json:"refusal,omitempty"` // SafetyRatings 是 Gemini 的安全评分。 SafetyRatings []json.RawMessage `json:"safety_ratings,omitempty"` // ImageData 供生图适配器使用。 ImageData []ImageData `json:"image_data,omitempty"` // Cost 是上游自报的费用(小数**字符串**),原样透传。 // 刻意不在内核里解析或累加:它是上游事实,且只有部分上游提供。 Cost string `json:"cost,omitempty"` } // UnifiedDelta 是流式分块的统一形状(最大集合)。 type UnifiedDelta struct { Content string `json:"content"` Thinking string `json:"thinking,omitempty"` Done bool `json:"done"` // FinishReason 只在终止块上有值。 FinishReason string `json:"finish_reason,omitempty"` // ToolCalls 是流式工具调用的增量片段(原始 JSON,由适配器决定形状)。 ToolCalls json.RawMessage `json:"tool_calls,omitempty"` // Usage 出现在带用量的块上(通常是终止块)。 Usage *TokenUsage `json:"usage,omitempty"` // Citations 流式引用块。 Citations []json.RawMessage `json:"citations,omitempty"` // Cost 上游自报费用。 Cost string `json:"cost,omitempty"` } // ChatRequest is the historical name for the in-bound request type. // // Deprecated: it is an alias of UnifiedRequest. The rename happened because the // kernel's internal format stopped being "whatever the client sent" once the // maximal set landed — it is now the maximal set, and the client's own protocol // shape only survives until the adapter sees it. // // Kept as an alias (not a distinct type) so the 49 existing references keep // compiling while call sites migrate deliberately rather than in one sweep. type ChatRequest = UnifiedRequest