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,9 +8,9 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// openAIToolAcc accumulates the partial state of one streaming tool call (keyed
|
||||
// by the provider's tool_calls[].index) so its arguments re-assemble across the
|
||||
// delta fragments. started guards a single ToolCallStart emission per block.
|
||||
// openAIToolAcc 累积单个流式工具调用的部分状态(按
|
||||
// 提供商的 tool_calls[].index 作键),以便其参数跨
|
||||
// 增量片段重新拼装。started 保证每块只发出一次 ToolCallStart。
|
||||
type openAIToolAcc struct {
|
||||
id string
|
||||
name string
|
||||
@@ -18,8 +18,8 @@ type openAIToolAcc struct {
|
||||
started bool
|
||||
}
|
||||
|
||||
// idOrName resolves the block identifier used to correlate start/delta/end: the
|
||||
// provider tool-call id when present, else the function name.
|
||||
// idOrName 解出用于关联 start/delta/end 的块标识:
|
||||
// 有提供商工具调用 id 时用它,否则用函数名。
|
||||
func (a *openAIToolAcc) idOrName() string {
|
||||
if a.id != "" {
|
||||
return a.id
|
||||
@@ -27,12 +27,12 @@ func (a *openAIToolAcc) idOrName() string {
|
||||
return a.name
|
||||
}
|
||||
|
||||
// openAIStreamState is the running aggregation of one streamed
|
||||
// /chat/completions response: the assembled Response, the bracketing flags of the
|
||||
// currently open segment, and the per-index tool-call accumulators. It is the
|
||||
// OpenAI counterpart of anthropic's streamBlock bookkeeping, so both adapters
|
||||
// share one parse → apply → finalize shape. emit is the nil-tolerant event sink
|
||||
// (a nil onEvent simply drops every event).
|
||||
// openAIStreamState 是一次流式 /chat/completions 响应的
|
||||
// 运行中聚合状态:拼装好的 Response、当前打开片段的
|
||||
// 包裹标志,以及按索引的工具调用累加器。它是
|
||||
// Anthropic 的 streamBlock 记账的对位实现,因此两个适配器
|
||||
// 共用同一套「解析 → 应用 → 收尾」形态。emit 是容忍 nil 的事件槽
|
||||
// (onEvent 为 nil 时只是丢弃每个事件)。
|
||||
type openAIStreamState struct {
|
||||
agg Response
|
||||
text strings.Builder
|
||||
@@ -43,13 +43,13 @@ type openAIStreamState struct {
|
||||
onEvent func(StreamEvent)
|
||||
}
|
||||
|
||||
// scanSSE incrementally decodes stream chunks, accumulates the full body text
|
||||
// with a strings.Builder (never reading the whole body at once) and captures
|
||||
// usage from the final chunk. It splits each delta into its segment and brackets
|
||||
// them: reasoning_content as thinking_start/delta/end, tool_calls fragments as
|
||||
// tool_call_start/delta/end, and body text as text_start/delta/end. The
|
||||
// accumulated tool-call arguments re-assemble across fragments into a structured
|
||||
// Args map, and are attached to the returned Response alongside the text.
|
||||
// scanSSE 增量解码流块,用 strings.Builder 累积完整正文
|
||||
// 文本(绝不一次性读完整个响应体),并从最后一个块
|
||||
// 捕获用量。它把每个增量拆到其片段并按对包裹:
|
||||
// reasoning_content 作为 thinking_start/delta/end,tool_calls 片段作为
|
||||
// tool_call_start/delta/end,正文文本作为 text_start/delta/end。
|
||||
// 累积的工具调用参数会跨片段重新拼装成结构化的
|
||||
// Args map,并与文本一起挂到返回的 Response 上。
|
||||
func scanSSE(r io.Reader, onEvent func(StreamEvent)) (Response, error) {
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner.Buffer(make([]byte, 0, sseBufInit), maxSSELineBytes)
|
||||
@@ -76,16 +76,16 @@ func scanSSE(r io.Reader, onEvent func(StreamEvent)) (Response, error) {
|
||||
return finalizeOpenAIStream(st), nil
|
||||
}
|
||||
|
||||
// emit forwards one structured event, tolerating a nil sink.
|
||||
// emit 转发一个结构化事件,容忍 nil 事件槽。
|
||||
func (st *openAIStreamState) emit(ev StreamEvent) {
|
||||
if st.onEvent != nil {
|
||||
st.onEvent(ev)
|
||||
}
|
||||
}
|
||||
|
||||
// applyChunk folds a single decoded stream chunk into the running aggregation:
|
||||
// usage and stop reason are recorded even on a choice-less terminal chunk, and
|
||||
// the segment delta (if any) is routed to its handler.
|
||||
// applyChunk 把单个已解码的流块折叠进运行中的聚合状态:
|
||||
// 用量与停止原因即使在无 choice 的终止块上也会被记录,
|
||||
// 片段增量(若有)则被路由到其处理器。
|
||||
func (st *openAIStreamState) applyChunk(chunk openAIChatResponse) {
|
||||
if u := chunk.Usage; u != nil {
|
||||
st.agg.PromptTokens = u.PromptTokens
|
||||
@@ -104,9 +104,9 @@ func (st *openAIStreamState) applyChunk(chunk openAIChatResponse) {
|
||||
st.applyDelta(chunk.Choices[0].Delta)
|
||||
}
|
||||
|
||||
// applyDelta routes one delta fragment to its segment handler. The precedence is
|
||||
// the provider's: a reasoning fragment wins over a tool-call fragment, which wins
|
||||
// over body text, so a delta never contributes to two segments at once.
|
||||
// applyDelta 把一个增量片段路由到其片段处理器。优先级
|
||||
// 取自提供商:推理片段压过工具调用片段,后者又压过
|
||||
// 正文文本,因此一个增量绝不会同时进入两个片段。
|
||||
func (st *openAIStreamState) applyDelta(delta *openAIDelta) {
|
||||
switch {
|
||||
case delta.ReasoningContent != "":
|
||||
@@ -122,8 +122,8 @@ func (st *openAIStreamState) applyDelta(delta *openAIDelta) {
|
||||
}
|
||||
}
|
||||
|
||||
// applyThinking forwards one reasoning fragment, opening the thinking segment on
|
||||
// the first fragment only.
|
||||
// applyThinking 转发一个推理片段,只在第一个
|
||||
// 片段上打开 thinking 片段。
|
||||
func (st *openAIStreamState) applyThinking(fragment string) {
|
||||
if !st.thinkingStarted {
|
||||
st.thinkingStarted = true
|
||||
@@ -132,8 +132,8 @@ func (st *openAIStreamState) applyThinking(fragment string) {
|
||||
st.emit(StreamEvent{Kind: StreamKindThinkingDelta, Delta: fragment})
|
||||
}
|
||||
|
||||
// endThinking closes the open thinking segment (no-op when none is open), so a
|
||||
// following tool-call or text segment starts a fresh one.
|
||||
// endThinking 关闭已打开的 thinking 片段(未打开时是空操作),
|
||||
// 这样随后的工具调用或文本片段会开一个新的。
|
||||
func (st *openAIStreamState) endThinking() {
|
||||
if !st.thinkingStarted {
|
||||
return
|
||||
@@ -142,9 +142,9 @@ func (st *openAIStreamState) endThinking() {
|
||||
st.emit(StreamEvent{Kind: StreamKindThinkingEnd})
|
||||
}
|
||||
|
||||
// applyToolCall folds one streaming tool-call fragment into its per-index
|
||||
// accumulator, opening the block's start event once its id/name is known and
|
||||
// forwarding the argument fragments as they arrive.
|
||||
// applyToolCall 把一个流式工具调用片段折叠进它按索引的
|
||||
// 累加器,在 id/name 已知时打开该块的 start 事件,
|
||||
// 并随着参数片段到达而转发它们。
|
||||
func (st *openAIStreamState) applyToolCall(tc openAIToolCallDelta) {
|
||||
acc := st.toolCalls[tc.Index]
|
||||
if acc == nil {
|
||||
@@ -169,8 +169,8 @@ func (st *openAIStreamState) applyToolCall(tc openAIToolCallDelta) {
|
||||
st.emit(StreamEvent{Kind: StreamKindToolCallDelta, Index: tc.Index, ID: acc.idOrName(), Delta: tc.Function.Arguments})
|
||||
}
|
||||
|
||||
// applyText appends one body-text fragment, opening the text segment on the first
|
||||
// fragment only.
|
||||
// applyText 追加一个正文文本片段,只在第一个
|
||||
// 片段上打开文本片段。
|
||||
func (st *openAIStreamState) applyText(fragment string) {
|
||||
st.text.WriteString(fragment)
|
||||
if !st.textStarted {
|
||||
@@ -180,9 +180,9 @@ func (st *openAIStreamState) applyText(fragment string) {
|
||||
st.emit(StreamEvent{Kind: StreamKindTextDelta, Delta: fragment})
|
||||
}
|
||||
|
||||
// closeSegments emits the end event of every segment still open at stream
|
||||
// termination. OpenAI has no per-block stop event, so each started tool_call
|
||||
// block is closed here.
|
||||
// closeSegments 在流终止时发出每个仍打开片段的结束
|
||||
// 事件。OpenAI 没有按块的停止事件,所以每个已开始的 tool_call
|
||||
// 块都在此关闭。
|
||||
func (st *openAIStreamState) closeSegments() {
|
||||
if st.thinkingStarted {
|
||||
st.emit(StreamEvent{Kind: StreamKindThinkingEnd})
|
||||
@@ -199,9 +199,9 @@ func (st *openAIStreamState) closeSegments() {
|
||||
}
|
||||
}
|
||||
|
||||
// assembleToolCalls decodes each accumulated tool-call argument buffer into its
|
||||
// structured Args map, in provider order. Unparseable fragments degrade to an
|
||||
// empty map (the call is still surfaced).
|
||||
// assembleToolCalls 按提供商顺序把每个累积的工具调用参数缓冲
|
||||
// 解码成结构化的 Args map。无法解析的片段降级为
|
||||
// 空 map(该调用仍会浮出)。
|
||||
func (st *openAIStreamState) assembleToolCalls() {
|
||||
for _, idx := range st.toolOrder {
|
||||
acc := st.toolCalls[idx]
|
||||
@@ -218,9 +218,9 @@ func (st *openAIStreamState) assembleToolCalls() {
|
||||
}
|
||||
}
|
||||
|
||||
// finalizeOpenAIStream closes the open segments and assembles the aggregated
|
||||
// Response: the concatenated body text (reasoning never leaks into it) plus the
|
||||
// re-assembled tool calls.
|
||||
// finalizeOpenAIStream 关闭打开的片段并拼装聚合后的
|
||||
// Response:拼接的正文文本(推理永不泄漏进去)加上
|
||||
// 重新拼装的工具调用。
|
||||
func finalizeOpenAIStream(st *openAIStreamState) Response {
|
||||
st.closeSegments()
|
||||
st.assembleToolCalls()
|
||||
|
||||
Reference in new issue
Block a user