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

+46 -46
View File
@@ -1,27 +1,27 @@
package model
// Provider stop/finish reasons that mean the token budget cut the generation
// off: Anthropic reports stop_reason "max_tokens", OpenAI-compatible providers
// report finish_reason "length". Both are normalized into Response.StopReason
// verbatim so a trace keeps the provider's own vocabulary.
// 表示 token 预算把生成切断的提供商停止/结束原因:
// Anthropic 上报 stop_reason "max_tokens",OpenAI 兼容提供商
// 上报 finish_reason "length"。两者都被原样归一化进 Response.StopReason,
// 因此链路追踪保留提供商自己的词汇。
const (
stopReasonMaxTokens = "max_tokens"
stopReasonLength = "length"
)
// Truncation-recovery bounds. At most one retry with a doubled budget is
// allowed; the cap keeps a pathological budget from growing without bound. The
// hint is the non-empty placeholder returned when even the retry produced no
// visible text, so a truncated turn never surfaces as a silent empty reply.
// It never carries thinking: reasoning is not the answer.
// 截断恢复的边界。最多允许一次带翻倍预算的重试;
// 上限防止病态预算无界增长。hint 是连重试
// 也没产出可见文本时返回的非空占位,
// 因此被截断的回合绝不会以静默空回复的形式浮出。
// 它绝不携带思考:推理不是答案。
const (
truncationRetryCap = 32768
truncationEmptyHint = "(思考过长,未能生成回复)"
)
// effectiveMaxTokens resolves the budget an attempt actually sends: the explicit
// Request.MaxTokens, else the adapter default. def may be zero when the protocol
// has no adapter default, in which case the provider default applies.
// effectiveMaxTokens 解出一次尝试实际发送的预算:显式的
// Request.MaxTokens,否则用适配器默认值。当协议没有适配器
// 默认值时 def 可以为零,此时适用提供商默认值。
func effectiveMaxTokens(n, def int) int {
if n > 0 {
return n
@@ -29,10 +29,10 @@ func effectiveMaxTokens(n, def int) int {
return def
}
// retryMaxTokens doubles the effective budget for the one-shot truncation retry,
// capped at truncationRetryCap. It returns the effective budget unchanged when
// there is nothing to double (both n and def are zero, i.e. the provider default
// applies), letting the caller skip a retry that would send an identical request.
// retryMaxTokens 为一次性的截断重试翻倍实际生效预算,
// 上限为 truncationRetryCap。当没有什么可翻倍时(n 与 def 都是零,
// 即适用提供商默认值),它原样返回实际生效预算,从而让
// 调用方跳过那次会发出完全相同请求的重试。
func retryMaxTokens(n, def int) int {
eff := effectiveMaxTokens(n, def)
if eff <= 0 {
@@ -44,31 +44,31 @@ func retryMaxTokens(n, def int) int {
return truncationRetryCap
}
// isTruncatedStopReason reports whether a stop reason means the token budget cut
// the generation off.
// isTruncatedStopReason 报告某个停止原因是否意味着 token 预算
// 切断了生成。
func isTruncatedStopReason(reason string) bool {
return reason == stopReasonMaxTokens || reason == stopReasonLength
}
// isEmptyBody reports whether a response carries neither visible text nor tool
// calls. A tool-only round is NOT empty: its empty body is normal and belongs to
// the tool loop.
// isEmptyBody 报告响应是否既无可见文本也无工具
// 调用。仅工具的一轮并不是空:它的空正文是正常的、属于
// 工具循环。
func isEmptyBody(r Response) bool {
return r.Content == "" && len(r.ToolCalls) == 0
}
// isTruncatedEmpty reports whether an attempt was cut off by the token budget
// before producing any visible output (no text, no tool call). Only this shape
// triggers the truncation retry/fallback; a normal tool_use round, whose body is
// legitimately empty, does not.
// isTruncatedEmpty 报告某次尝试是否在产出任何可见输出之前
// 就被 token 预算切断(无文本、无工具调用)。只有这种形状
// 才触发截断重试/兜底;正常的 tool_use 回合,其正文
// 合法地为空,不会触发。
func isTruncatedEmpty(r Response) bool {
return isEmptyBody(r) && isTruncatedStopReason(r.StopReason)
}
// withEmptyBodyHint fills a non-empty placeholder body when a response has
// neither text nor tool calls, so a truncated turn never surfaces as a silent
// empty reply. A response that already carries text or tool calls is returned
// unchanged.
// withEmptyBodyHint 在响应既无文本也无工具调用时填入一段
// 非空占位正文,因此被截断的回合绝不会以静默
// 空回复的形式浮出。已携带文本或工具调用的响应
// 原样返回。
func withEmptyBodyHint(r Response) Response {
if !isEmptyBody(r) {
return r
@@ -78,12 +78,12 @@ func withEmptyBodyHint(r Response) Response {
return r
}
// callWithTruncationRetry runs a non-streaming attempt, retrying once with a
// doubled token budget when the first attempt was cut off with no visible
// output, then returning a non-empty placeholder when the retry still produced
// nothing. attempt receives the budget to send. An upstream error on the first
// attempt is returned unchanged (no retry); a failed retry falls back to the
// placeholder rather than surfacing an empty reply.
// callWithTruncationRetry 执行一次非流式尝试,当第一次尝试
// 在无可见输出下被切断时,用翻倍 token 预算重试一次,
// 随后在重试仍无产出时返回非空占位。attempt 接收要发送
// 的预算。第一次尝试的上游错误会被原样返回(不重试);
// 重试失败则退回占位,
// 而不是浮出一个空回复。
func callWithTruncationRetry(maxTokens, def int, attempt func(limit int) (Response, error)) (Response, error) {
out, err := attempt(maxTokens)
if err != nil {
@@ -100,13 +100,13 @@ func callWithTruncationRetry(maxTokens, def int, attempt func(limit int) (Respon
return withEmptyBodyHint(out), nil
}
// callStreamWithTruncationRetry is the streaming counterpart of
// callWithTruncationRetry. attempt receives the budget and the event callback
// for that attempt. The retry drops thinking events (the first attempt already
// streamed its reasoning, so re-streaming it would duplicate the block) while
// forwarding text/tool events live; when even the retry yields no visible
// output, a non-empty placeholder is emitted as a text segment and returned as
// the response body, so the stream never ends on a silent truncation.
// callStreamWithTruncationRetry 是 callWithTruncationRetry 的
// 流式对位实现。attempt 接收该次尝试的预算与事件回调。
// 重试会丢弃思考事件(第一次尝试已经
// 流过它的推理,再流一次会产生重复块),
// 同时实时转发文本/工具事件;当连重试也没有可见
// 输出时,会以文本片段发出非空占位并把它作为
// 响应正文返回,因此流绝不会以静默截断收尾。
func callStreamWithTruncationRetry(
maxTokens, def int,
attempt func(limit int, onEvent func(StreamEvent)) (Response, error),
@@ -131,8 +131,8 @@ func callStreamWithTruncationRetry(
return out, nil
}
// dropThinking wraps onEvent so a retry attempt does not re-emit thinking
// segments. Text and tool_call events pass through unchanged.
// dropThinking 包裹 onEvent,使重试尝试不会再次发出思考
// 片段。文本与 tool_call 事件原样通过。
func dropThinking(onEvent func(StreamEvent)) func(StreamEvent) {
if onEvent == nil {
return nil
@@ -147,8 +147,8 @@ func dropThinking(onEvent func(StreamEvent)) func(StreamEvent) {
}
}
// emitTextSegment emits a single-delta text segment so a synthesized placeholder
// reaches streaming consumers that render only message.* events.
// emitTextSegment 发出一个单增量的文本片段,使合成的占位
// 能到达只渲染 message.* 事件的流式消费者。
func emitTextSegment(onEvent func(StreamEvent), text string) {
if onEvent == nil || text == "" {
return