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
@@ -8,32 +8,32 @@ import (
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/logger"
|
||||
)
|
||||
|
||||
// discardLogger returns a logger that drops every record but still carries the
|
||||
// adapter's module source, so an adapter built without an injected logger can
|
||||
// never emit an unsourced record.
|
||||
// discardLogger 返回一个丢弃所有记录的日志器,但仍带有
|
||||
// 适配器的模块来源,因此未注入日志器而构建的适配器
|
||||
// 绝不会产生无来源记录。
|
||||
func discardLogger(source string) *slog.Logger {
|
||||
return logger.WithSource(slog.New(slog.NewTextHandler(io.Discard, nil)), source)
|
||||
}
|
||||
|
||||
// Reasoning wire values shared by the OpenAI-compatible and Anthropic adapters
|
||||
// (design/04 §3.2, checked against the provider documents on 2026-10-03).
|
||||
// OpenAI 兼容与 Anthropic 适配器共用的推理线取值
|
||||
// (design/04 §3.2,已于 2026-10-03 对照各提供商文档核对)。
|
||||
const (
|
||||
// thinkingTypeEnabled and thinkingTypeDisabled are the values of the
|
||||
// {"thinking":{"type":...}} switch. DeepSeek's default is enabled, so omitting
|
||||
// the field leaves thinking ON — "do not intervene" is not a way to switch it
|
||||
// off; only an explicit disabled (or reasoning_effort "none") is.
|
||||
// thinkingTypeEnabled 与 thinkingTypeDisabled 是
|
||||
// {"thinking":{"type":...}} 开关的取值。DeepSeek 默认开启,所以省略
|
||||
// 该字段会让思考保持开启——「不干预」不是关闭它的办法;
|
||||
// 只有显式 disabled(或 reasoning_effort "none")才是。
|
||||
thinkingTypeEnabled = "enabled"
|
||||
thinkingTypeDisabled = "disabled"
|
||||
// effortNone is the reasoning_effort value that switches thinking off. It is a
|
||||
// switch, not a strength tier, so it must never be replaced by a tier: mapping
|
||||
// it onto "low" would invert the caller's intent (thinking off → thinking on).
|
||||
// effortNone 是关闭思考的 reasoning_effort 取值。它是
|
||||
// 开关而非强度档位,所以绝不能被替换成某个档位:把它
|
||||
// 映射到 "low" 会颠倒调用方的意图(关闭思考 → 开启思考)。
|
||||
effortNone = "none"
|
||||
)
|
||||
|
||||
// reasoningVendor identifies the vendor behind an OpenAI-compatible endpoint.
|
||||
// The wire protocol is the same for all of them, but the accepted
|
||||
// reasoning_effort values are not, so a value must be checked against the right
|
||||
// whitelist instead of being forwarded blindly.
|
||||
// reasoningVendor 标识 OpenAI 兼容端点背后的厂商。
|
||||
// 它们的线协议相同,但可接受的
|
||||
// reasoning_effort 取值不同,所以必须按正确的
|
||||
// 白名单校验,而不是盲目转发。
|
||||
type reasoningVendor string
|
||||
|
||||
const (
|
||||
@@ -43,10 +43,10 @@ const (
|
||||
vendorUnknown reasoningVendor = "unknown"
|
||||
)
|
||||
|
||||
// reasoningEffortAliases is DeepSeek's documented compatibility mapping: the
|
||||
// service accepts these aliases and applies the mapped tier. The mapping is
|
||||
// applied on our side too, so the request body always carries the tier the
|
||||
// provider documents as the effective one (design/04 §3.2 fact 1).
|
||||
// reasoningEffortAliases 是 DeepSeek 记录的兼容映射:
|
||||
// 服务接受这些别名并采用映射后的档位。我们在自己这一侧
|
||||
// 也做同一映射,因此请求体总是携带提供商
|
||||
// 记录为有效值的那一档(design/04 §3.2 事实 1)。
|
||||
var reasoningEffortAliases = map[string]string{
|
||||
"minimal": "low",
|
||||
"medium": "high",
|
||||
@@ -54,20 +54,20 @@ var reasoningEffortAliases = map[string]string{
|
||||
"ultra": "max",
|
||||
}
|
||||
|
||||
// reasoningEffortAllowed is the per-vendor set of accepted reasoning_effort
|
||||
// values. A vendor absent from this table has no reasoning_effort support at all,
|
||||
// which is reported rather than guessed.
|
||||
// reasoningEffortAllowed 是按厂商划分的可接受 reasoning_effort
|
||||
// 取值集合。不在本表里的厂商完全没有 reasoning_effort 支持,
|
||||
// 这一点会被上报,而不是靠猜。
|
||||
//
|
||||
// There is deliberately no "round down to the nearest tier" fallback: an
|
||||
// unsupported value is dropped and reported instead, because silently changing it
|
||||
// can invert the caller's intent — the original bug mapped DeepSeek's "none"
|
||||
// (thinking off) onto "low" (thinking on) exactly that way.
|
||||
// 这里刻意没有「向下取到最近档位」的降级:不支持的取值
|
||||
// 会被丢弃并上报,因为静默改写它可能会颠倒调用方的
|
||||
// 意图——早先的 bug 正是这样把 DeepSeek 的 "none"(关闭思考)
|
||||
// 映射到了 "low"(开启思考)。
|
||||
var reasoningEffortAllowed = map[reasoningVendor]map[string]bool{
|
||||
vendorOpenAI: tierSet("none", "minimal", "low", "medium", "high", "xhigh", "max"),
|
||||
vendorDeepSeek: tierSet("none", "low", "high", "max"),
|
||||
}
|
||||
|
||||
// tierSet builds a membership set from the given tiers.
|
||||
// tierSet 由给定档位构建一个成员集合。
|
||||
func tierSet(tiers ...string) map[string]bool {
|
||||
set := make(map[string]bool, len(tiers))
|
||||
for _, t := range tiers {
|
||||
@@ -76,12 +76,12 @@ func tierSet(tiers ...string) map[string]bool {
|
||||
return set
|
||||
}
|
||||
|
||||
// reasoningVendorFor classifies the vendor of an OpenAI-compatible request from
|
||||
// its model name, falling back to the base URL when the name does not identify
|
||||
// one. An unrecognized pair yields vendorUnknown so the caller reports "not
|
||||
// supported" instead of assuming OpenAI semantics. The Anthropic adapter uses the
|
||||
// same classification to tell a DeepSeek /anthropic endpoint (which ignores
|
||||
// budget_tokens) from the Anthropic endpoint.
|
||||
// reasoningVendorFor 按模型名分类 OpenAI 兼容请求的厂商,
|
||||
// 模型名无法识别出厂商时退回到 base URL。
|
||||
// 无法识别的组合得到 vendorUnknown,好让调用方上报「不
|
||||
// 支持」而不是假定 OpenAI 语义。Anthropic 适配器用
|
||||
// 同一套分类来区分 DeepSeek 的 /anthropic 端点(它忽略
|
||||
// budget_tokens)与 Anthropic 端点。
|
||||
func reasoningVendorFor(model, baseURL string) reasoningVendor {
|
||||
m := strings.ToLower(strings.TrimSpace(model))
|
||||
switch {
|
||||
@@ -105,7 +105,7 @@ func reasoningVendorFor(model, baseURL string) reasoningVendor {
|
||||
return vendorUnknown
|
||||
}
|
||||
|
||||
// thinkingSwitchValue maps an explicit on/off switch onto its wire value.
|
||||
// thinkingSwitchValue 把显式开/关开关映射到它的线取值。
|
||||
func thinkingSwitchValue(enable bool) string {
|
||||
if enable {
|
||||
return thinkingTypeEnabled
|
||||
@@ -113,10 +113,10 @@ func thinkingSwitchValue(enable bool) string {
|
||||
return thinkingTypeDisabled
|
||||
}
|
||||
|
||||
// normalizeDeepSeekEffort maps an effort onto the tier DeepSeek applies and
|
||||
// reports whether the value is one the service documents: the documented set
|
||||
// (none/low/high/max) passes through, the compatibility aliases map to their
|
||||
// official tier.
|
||||
// normalizeDeepSeekEffort 把 effort 映射到 DeepSeek 实际采用的档位,
|
||||
// 并报告该值是否为服务记录的取值:记录在案的集合
|
||||
// (none/low/high/max)原样通过,兼容别名映射到其
|
||||
// 官方档位。
|
||||
func normalizeDeepSeekEffort(effort string) (string, bool) {
|
||||
if tier, ok := reasoningEffortAliases[effort]; ok {
|
||||
return tier, true
|
||||
@@ -127,16 +127,16 @@ func normalizeDeepSeekEffort(effort string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// resolveReasoningEffort validates effort against the vendor's whitelist and
|
||||
// returns the value to send; "" drops the field. An unrecognized value is never
|
||||
// turned into a different tier — it is dropped and reported with provider, field,
|
||||
// value and model, because changing it can invert the caller's intent. A DeepSeek
|
||||
// compatibility alias is normalized to the tier the service applies and reported.
|
||||
// resolveReasoningEffort 针对厂商白名单校验 effort 并
|
||||
// 返回要发送的值;"" 表示丢弃该字段。无法识别的值绝不会
|
||||
// 被换成别的档位——它会被丢弃,并把 provider、field、
|
||||
// value 与 model 一并上报,因为改动它会颠倒调用方的意图。DeepSeek
|
||||
// 的兼容别名会被归一化为服务采用的档位并上报。
|
||||
//
|
||||
// vendor must be a vendor with a reasoning_effort whitelist (DeepSeek / OpenAI):
|
||||
// the shapes that carry no such field (vLLM / DashScope) and the endpoints whose
|
||||
// shape is unknown drop the effort themselves and report it, so their callers
|
||||
// never reach this function (see reasoning_shape.go, 裁决 03.4.2).
|
||||
// vendor 必须是带 reasoning_effort 白名单的厂商(DeepSeek / OpenAI):
|
||||
// 不带该字段的形状(vLLM / DashScope)以及形状未知的
|
||||
// 端点会自行丢弃 effort 并上报,因此它们的调用方
|
||||
// 永远不会走到本函数(见 reasoning_shape.go,裁决 03.4.2)。
|
||||
func resolveReasoningEffort(log *slog.Logger, vendor reasoningVendor, effort, model string) string {
|
||||
effort = strings.TrimSpace(effort)
|
||||
if effort == "" {
|
||||
@@ -159,8 +159,8 @@ func resolveReasoningEffort(log *slog.Logger, vendor reasoningVendor, effort, mo
|
||||
return ""
|
||||
}
|
||||
|
||||
// toolChoiceForces reports whether a tool_choice directive obliges the model to
|
||||
// call a tool ("required", Anthropic's "any", or a named tool). auto/none do not.
|
||||
// toolChoiceForces 报告 tool_choice 指令是否强制模型
|
||||
// 调用工具("required"、Anthropic 的 "any",或具名工具)。auto/none 不会。
|
||||
func toolChoiceForces(toolChoice any) bool {
|
||||
switch v := toolChoice.(type) {
|
||||
case nil:
|
||||
@@ -183,7 +183,7 @@ func toolChoiceForces(toolChoice any) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// forcedToolChoiceType reports whether a tool_choice "type" value forces a call.
|
||||
// forcedToolChoiceType 报告 tool_choice 的 "type" 取值是否强制调用。
|
||||
func forcedToolChoiceType(value any) bool {
|
||||
t, _ := value.(string)
|
||||
switch strings.ToLower(strings.TrimSpace(t)) {
|
||||
@@ -193,14 +193,14 @@ func forcedToolChoiceType(value any) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// reportForcedToolChoiceWithThinking warns about the DeepSeek red line that a
|
||||
// forced tool call — tool_choice "required"/"any"/a named tool — and thinking are
|
||||
// not compatible: while thinking is on (its default) the API rejects the forcing
|
||||
// directive with a 400, so the caller must switch thinking off explicitly first
|
||||
// (design/04 §3.2 fact 5). It only reports: resolution must never switch thinking
|
||||
// off on its own. On the OpenAI-compatible adapter the directive is not on the
|
||||
// wire yet (M2), so the warning is prospective there — it still records that the
|
||||
// requested combination can never be honoured.
|
||||
// reportForcedToolChoiceWithThinking 就 DeepSeek 红线报警:
|
||||
// 强制型工具调用——tool_choice "required"/"any"/具名工具——与思考
|
||||
// 不兼容:思考开启(其默认)时 API 会用 400 拒绝该强制
|
||||
// 指令,所以调用方必须先把思考显式关掉
|
||||
// (design/04 §3.2 事实 5)。它只上报:解算过程绝不能自行
|
||||
// 关掉思考。在 OpenAI 兼容适配器上该指令尚未上线
|
||||
// (M2),所以那里只是前瞻性警告——它仍然记录了
|
||||
// 所请求的组合永远无法被满足。
|
||||
func reportForcedToolChoiceWithThinking(log *slog.Logger, provider, model string, toolChoice any, thinkingOn bool) {
|
||||
if !thinkingOn || !toolChoiceForces(toolChoice) {
|
||||
return
|
||||
|
||||
Reference in new issue
Block a user