docs(comments): 注释中文化 · internal/runtime/model + internal/services/backthink(54 文件,纯替换)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 只翻注释/文档字符串:`go/scanner` 剥注释后按 token 序列+literal 逐 token 比对,**54/54 文件 SAME**;
  改动文件总行数与 HEAD 漂移 = 0(严格 1:1,无合并/拆行)
- 裁决编号与 design 节号逐字保留(03.4.1/03.4.2/03.4.3/14.4.1/14.4.5/07.4.2-4、design/04 §3.1/§3.2 等);
  `chat_template_kwargs` / `enable_thinking` / `reasoning_effort` / `thinking:{type}` 字段名与形状说明未动
- 存疑译法(已登记):`persona` 在叙事语境用「角色」、标识符语境保留 `persona`(仓库既有模块本身混用);
  `rung`→「级」;`provider` 存在「提供商/提供者」两种落笔(可按需一键统一)
- 复核:build/vet 绿 + 两包测试绿;gofmt 干净;残留英文注释 0 条
- 既有存量(未修,只报告):`model/anthropic_test.go` 612 行 >500;`applyAnthropicEvent` 115 行 >80
This commit is contained in:
AskaEth committed 2026-10-04 16:27:54 +08:00
1 parent f5cf117482
commit 3dc1232bc2
54 files changed
+1923 -1923

No files matched your search

+48 -48
View File
@@ -5,8 +5,8 @@ import (
"fmt"
)
// openAIChatRequest is the JSON body sent to /chat/completions. Field names
// match the provider protocol so the struct marshals directly.
// openAIChatRequest 是发给 /chat/completions 的 JSON 请求体。字段名
// 与提供商协议一致,因此该结构体可以直接序列化。
type openAIChatRequest struct {
Model string `json:"model"`
Messages []openAIMessage `json:"messages"`
@@ -15,49 +15,49 @@ type openAIChatRequest struct {
Stream bool `json:"stream,omitempty"`
StreamOptions *openAIStreamOpts `json:"stream_options,omitempty"`
Tools []openAITool `json:"tools,omitempty"`
// Thinking is DeepSeek's top-level thinking switch. It is sent only for
// DeepSeek and only from an explicit switch (or an explicit off that must be
// honoured): leaving it out keeps the provider default, which is ENABLED, so
// an omitted field is not a way to switch thinking off (design/04 §3.2
// fact 2).
// Thinking 是 DeepSeek 的顶层思考开关。它只对
// DeepSeek 发送,且只在有显式开关(或必须被
// 尊重的显式关闭)时发送:省略它会保留提供商默认值,而该默认值
// 是「开启」,所以省略字段并不是关闭思考的办法(design/04 §3.2
// 事实 2)。
Thinking *openAIThinking `json:"thinking,omitempty"`
// ReasoningEffort is the OpenAI-compatible reasoning strength knob. It is set
// only after validation against the detected vendor's whitelist (design/04
// §3.2); an unrecognized value is dropped, never turned into a different tier.
// ReasoningEffort 是 OpenAI 兼容的推理强度旋钮。它只在
// 对已识别厂商的白名单校验通过后才设置(design/04
// §3.2);无法识别的值会被丢弃,绝不会被换成别的档位。
ReasoningEffort string `json:"reasoning_effort,omitempty"`
// ChatTemplateKwargs carries the vLLM-style thinking switch
// (chat_template_kwargs.enable_thinking, design/04 §3.2 Qwen3 行). It is sent
// only to an endpoint whose provider declared reasoning.kind=vllm.
// ChatTemplateKwargs 承载 vLLM 风格的思考开关
// (chat_template_kwargs.enable_thinking,design/04 §3.2 Qwen3 行)。它只
// 发送给提供商声明了 reasoning.kind=vllm 的端点。
ChatTemplateKwargs *openAIChatTemplateKwargs `json:"chat_template_kwargs,omitempty"`
// EnableThinking carries the DashScope compatible-mode thinking switch (the
// top-level enable_thinking). It is sent only to an endpoint whose provider
// declared reasoning.kind=dashscope — the shape is never inferred, because a
// wrong shape 400s the request.
// EnableThinking 承载 DashScope 兼容模式的思考开关(
// 顶层 enable_thinking)。它只发送给提供商
// 声明了 reasoning.kind=dashscope 的端点——该形状永不推断,因为
// 形状错了会让请求 400。
EnableThinking *bool `json:"enable_thinking,omitempty"`
}
// openAIThinking is the wire form of DeepSeek's thinking switch: enabled or
// disabled. It is only ever sent to a DeepSeek endpoint.
// openAIThinking 是 DeepSeek 思考开关的线格式:enabled 或
// disabled。它只会发给 DeepSeek 端点。
type openAIThinking struct {
Type string `json:"type"`
}
// openAIChatTemplateKwargs is the wire form of the vLLM (Qwen3) thinking switch:
// the boolean lives nested under chat_template_kwargs.
// openAIChatTemplateKwargs 是 vLLM(Qwen3)思考开关的线格式:
// 该布尔值嵌在 chat_template_kwargs 之下。
type openAIChatTemplateKwargs struct {
EnableThinking *bool `json:"enable_thinking,omitempty"`
}
// openAIStreamOpts asks the provider to include usage in the final stream chunk.
// openAIStreamOpts 请求提供商在最后一个流块里带上用量。
type openAIStreamOpts struct {
IncludeUsage bool `json:"include_usage"`
}
// openAIMessage is one request message in the wire body. Content is either a
// plain string (text-only) or an array of {type,text|image_url} parts. Tool
// messages carry ToolCallID (required by OpenAI); assistant messages that
// requested functions carry ToolCalls so the following tool messages can be
// correlated.
// openAIMessage 是线格式请求体里的一条消息。Content 要么是
// 纯字符串(仅文本),要么是 {type,text|image_url} 片段数组。Tool
// 消息携带 ToolCallID(OpenAI 要求);请求过函数的助手消息
// 携带 ToolCalls,以便随后的 tool 消息能够
// 被关联。
type openAIMessage struct {
Role string `json:"role"`
Content any `json:"content"`
@@ -65,28 +65,28 @@ type openAIMessage struct {
ToolCalls []openAIToolCall `json:"tool_calls,omitempty"`
}
// openAIToolCall is the wire form of one assistant tool_call in a request. OpenAI
// requires each subsequent tool message to reference the id back to these calls.
// openAIToolCall 是请求里单个助手 tool_call 的线格式。OpenAI
// 要求每条后续 tool 消息都把这些调用的 id 引用回来。
type openAIToolCall struct {
ID string `json:"id"`
Type string `json:"type"`
Function openAIToolCallFn `json:"function"`
}
// openAITool is the wire form of a function tool schema (M2 placeholder).
// openAITool 是函数工具 schema 的线格式(M2 占位)。
type openAITool struct {
Type string `json:"type"`
Function openAIToolFn `json:"function"`
}
// openAIToolFn carries the function definition.
// openAIToolFn 承载函数定义。
type openAIToolFn struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Parameters any `json:"parameters,omitempty"`
}
// openAIToolCallFn is the function part of a streaming tool-call fragment.
// openAIToolCallFn 是流式工具调用片段的函数部分。
type openAIToolCallFn struct {
Name string `json:"name"`
Arguments string `json:"arguments"`
@@ -97,12 +97,12 @@ func (a *OpenAIChatAdapter) buildRequest(req Request, stream bool) ([]byte, erro
if err != nil {
return nil, err
}
// The vendor behind an OpenAI-compatible endpoint decides which
// reasoning_effort tiers are legal and whether it has a thinking switch;
// classify it from the resolved model (and the base URL as a fallback) before
// touching the request. The endpoint's configured shape (models.json
// reasoning.kind, 裁决 03.4.2/08.4.1) wins over that classification, because
// the same wire protocol carries different switch shapes (vLLM vs DashScope).
// OpenAI 兼容端点背后的厂商决定哪些
// reasoning_effort 档位合法、以及它是否有思考开关;
// 在改动请求之前,先按已解析出的模型(并以 base URL 作降级)
// 分类。端点已声明的形状(models.json
// 的 reasoning.kind,裁决 03.4.2/08.4.1)优先于该分类,因为
// 同一线协议承载着不同的开关形状(vLLM 对 DashScope)。
vendor := reasoningVendorFor(req.Model, a.baseURL)
plan := resolveOpenAIReasoning(a.log, a.reasoningKind, vendor, req.Model, req.Thinking, req.ReasoningEffort)
reportForcedToolChoiceWithThinking(a.log, plan.provider, req.Model, req.ToolChoice, plan.thinkingOn())
@@ -140,10 +140,10 @@ func buildMessages(msgs []Message) ([]openAIMessage, error) {
return out, nil
}
// encodeOpenAIMessage renders one normalized message in OpenAI wire form. A
// RoleTool turn becomes a tool message carrying tool_call_id (required by OpenAI,
// and the audit's gap); an assistant turn that advertised tool calls carries the
// corresponding tool_calls array so the provider can correlate the tool messages.
// encodeOpenAIMessage 把一条归一化消息渲染成 OpenAI 线格式。
// RoleTool 回合变成携带 tool_call_id 的 tool 消息(OpenAI 要求,
// 也是审计指出的缺口);声明过工具调用的助手回合携带
// 对应的 tool_calls 数组,好让提供商能关联 tool 消息。
func encodeOpenAIMessage(m Message) (openAIMessage, error) {
if m.Role == RoleTool {
return openAIMessage{
@@ -159,8 +159,8 @@ func encodeOpenAIMessage(m Message) (openAIMessage, error) {
return openAIMessage{Role: string(m.Role), Content: content, ToolCalls: encodeToolCalls(m.Content)}, nil
}
// toolMessageCallID resolves the tool_call_id for a RoleTool turn: the
// message-level id, else the id carried on the first tool-result part.
// toolMessageCallID 解出 RoleTool 回合的 tool_call_id:
// 先是消息级 id,否则取第一个工具结果片段上的 id。
func toolMessageCallID(m Message) string {
if m.ToolCallID != "" {
return m.ToolCallID
@@ -173,8 +173,8 @@ func toolMessageCallID(m Message) string {
return ""
}
// encodeToolCalls builds the assistant tool_calls array from a turn's tool-call
// parts. Arguments are serialized to JSON because OpenAI expects a string.
// encodeToolCalls 由回合的工具调用片段构建助手的 tool_calls
// 数组。参数序列化为 JSON,因为 OpenAI 期望字符串。
func encodeToolCalls(parts []Content) []openAIToolCall {
calls := make([]openAIToolCall, 0, len(parts))
for _, p := range parts {
@@ -211,8 +211,8 @@ func encodeContent(parts []Content) (any, error) {
"image_url": map[string]any{"url": p.ImageURL},
})
case ContentTypeToolCall, ContentTypeToolResult:
// Tool-call and tool-result parts are encoded at the message level
// (tool_calls array / tool_call_id), never as text-image content.
// 工具调用与工具结果片段在消息级编码
// (tool_calls 数组 / tool_call_id),绝不作为文本-图片内容。
continue
default:
return nil, fmt.Errorf("model: %w: unsupported content type %q", ErrBadRequest, p.Type)