feat(llmloop): backfill comment thinking from turn output (#773)

* feat(llmloop): backfill comment thinking from the turn's reasoning or message

Expose ChatResponse.ReasoningContent and backfill per-comment thinking
with the current turn's reasoning content, falling back to the assistant
message for models that do not expose reasoning, so --format json output
carries thinking even when the model omits it.

* fix(llmloop): drop content fallback for comment thinking backfill

The turn's assistant message is usually a short user-facing preamble
rather than real reasoning, so backfill per-comment thinking only from
the model's native reasoning_content and leave it empty otherwise.

Add a full-wiring RunPerFile test for the reasoning backfill and a
regression test that fails if the content fallback returns. Sync the
thinking docs across en/zh/ja/ru.

* docs(llmloop): note that turn-level thinking is shared by design

Document in the main loop and at the code_comment backfill site that
the model emits reasoning once per turn, so every tool call and
comment in the same turn intentionally shares the same thinking.
This commit is contained in:
xujiejie 2026-08-10 14:52:51 +08:00 committed by kite
parent bbb8d78005
commit 4287d761b1

View file

@ -61,7 +61,7 @@ func codeCommentResponse(reasoning, content string) *llm.ChatResponse {
Type: "function",
Function: llm.FunctionCall{
Name: tool.CodeComment.Name(),
Arguments: `{"comments":[{"content":"issue","existing_code":"x","path":"file.go"}]}`,
Arguments: `{"comments":[{"content":"issue","existing_code":"x"}]}`,
},
}},
}}},
@ -281,8 +281,8 @@ func TestExecuteToolCall_CodeCommentThinkingBackfill(t *testing.T) {
Function: llm.FunctionCall{
Name: tool.CodeComment.Name(),
Arguments: `{"comments":[` +
`{"content":"a","existing_code":"x","path":"file.go"},` +
`{"content":"b","existing_code":"y","thinking":"explicit","path":"file.go"}]}`,
`{"content":"a","existing_code":"x"},` +
`{"content":"b","existing_code":"y","thinking":"explicit"}]}`,
},
}, nil, "turn reasoning")
@ -314,7 +314,7 @@ func TestExecuteToolCall_CodeCommentNoReasoning(t *testing.T) {
cp := r.executeToolCall(context.Background(), "file.go", llm.ToolCall{
Function: llm.FunctionCall{
Name: tool.CodeComment.Name(),
Arguments: `{"comments":[{"content":"a","existing_code":"x","path":"file.go"}]}`,
Arguments: `{"comments":[{"content":"a","existing_code":"x"}]}`,
},
}, nil, "")