docs(comments): 注释中文化 · internal/runtime/model + internal/services/backthink(54 文件,纯替换)
- 只翻注释/文档字符串:`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:
1 parent
f5cf117482
commit
3dc1232bc2
54 files changed
+1923
-1923
No files matched your search
@@ -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
|
||||
|
||||
Reference in new issue
Block a user