feat(llmloop): backfill comment thinking from turn output - #773
Conversation
… 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.
|
✅ OpenCodeReview: Review complete: 0 finding(s) across 2 selected item(s). |
|
On the The backfill prefers The concern: for a tool-calling turn, the assistant message is often not real reasoning — it tends to be a short, user-facing preamble like "I'll now leave a comment on this file" or "Here's my feedback". Backfilling that into Could you run a few review passes against a couple of models without native Either way, capturing what you observe would make the fallback's necessity clear. |
wu21-web
left a comment
There was a problem hiding this comment.
You idea is great and we appreciate it. Here are several suggestions.
By the way, im not sure if this is true, but it is worth considering: JSON output marshals the whole LlmComment and terminal renderComment never renders it.
| if thinking == "" { | ||
| thinking = content | ||
| } |
There was a problem hiding this comment.
| if thinking == "" { | |
| thinking = content | |
| } |
There was a problem hiding this comment.
Capture the model's native reasoning content for this turn.
| "context" | ||
| "strings" | ||
| "testing" | ||
|
|
There was a problem hiding this comment.
| "github.com/alibaba/open-code-review/internal/config/template" |
There was a problem hiding this comment.
Tests are needed to lock in the new behavior and regression tests that would fail if the fallback returns.
Tip on L11: this is a package you need to include for new tests, e.g TestRunPerFile_BackfillsThinkingFromReasoningContent
is a test to verify the full wiring
There was a problem hiding this comment.
Added in 2b18430: TestRunPerFile_BackfillsThinkingFromReasoningContent drives the full RunPerFile loop via a scripted fake client and asserts the comment gets the turn's reasoning_content; TestRunPerFile_NoFallbackToContent asserts thinking stays empty when only content exists — verified it fails if the fallback is re-added.
There was a problem hiding this comment.
Russian site documentation is missing, use a translator to translate this to pages/scr/content/docs/ru/tools.md
| // Prefer the model's native reasoning content; fall back to the | ||
| // assistant message of this turn for models that do not expose it. |
There was a problem hiding this comment.
| // Prefer the model's native reasoning content; fall back to the | |
| // assistant message of this turn for models that do not expose it. | |
| // Capture the model's native reasoning content for this turn. Models | |
| // without a reasoning channel leave it empty. |
| captures the model's reasoning and is preserved on the comment but not | ||
| shown in the final review output. | ||
| captures the model's reasoning and is preserved on the comment; OCR | ||
| backfills it from the reasoning content the model emits on the current | ||
| turn (or the turn's message when there is none), and includes it in | ||
| the JSON output (the terminal output does not | ||
| render it). |
There was a problem hiding this comment.
If you applied the suggestion here: https://github.com/alibaba/open-code-review/pull/773/changes#r3740773775
This means you have to likely update the documentation here.
| モデルが明示的に設定する必要はほとんどありません。`thinking`(コメントごと)はモデルの推論を捕捉し、コメントに保持されますが、 | ||
| 最終的なレビュー出力には表示されません。 | ||
| モデルが明示的に設定する必要はほとんどありません。`thinking`(コメントごと)はモデルの推論を捕捉し、コメントに保持されます。 | ||
| OCR はモデルが現在のターンで出力した推論内容(推論内容がない場合はそのターンの本文)で自動的に補完し、JSON 出力に含めます(ターミナル出力には表示されません)。 |
| 模型极少需要显式设置。`thinking`(按评论)捕获模型推理,保留在评论上,但不会 | ||
| 在最终评审输出中显示。 | ||
| 模型极少需要显式设置。`thinking`(按评论)捕获模型推理,保留在评论上;OCR 会用 | ||
| 模型当轮输出的推理内容(无推理内容时用当轮正文)自动回填,并包含在 JSON 输出中(终端输出不渲染)。 |
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.
Removed. In an opus 4.6 run without thinking, the fallback fired on only 8/23 comments and just 3 of those were valuable — the rest were preambles like "I'll now leave a comment on this file". thinking is now backfilled only from reasoning_content and left empty otherwise; docs and tests updated. |
lizhengfeng101
left a comment
There was a problem hiding this comment.
@yingjiexu2002 When multiple tool calls share the same thinking behavior in the same turn, it is recommended to explicitly state in the code comments that this is an intentional design to avoid future maintainers mistakenly thinking it is a bug.
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.
Done, documented at both sites in loop.go |
* 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.
* 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.
* 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.
* 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.
Description
Comments parsed from
code_commenttool calls have a runtime-onlythinkingfield that most models never populate (it is deliberately not advertised in the tool schema). This PR backfills it from the model's output of the current turn, so--format jsonoutput carries the review reasoning:ChatResponse.ReasoningContent()to expose the native reasoning content of a response (OpenAIreasoning_contentin both streaming and non-streaming paths; Anthropic thinking blocks).thinkingis empty. Explicit model-provided thinking always wins.renderCommentdoes not render thinking); only the JSON output gains the field.Type of Change
How Has This Been Tested?
make testpasses locallyAdded unit tests: backfill uses the turn's thinking text when a comment has no explicit thinking; explicit thinking is preserved; no backfill when the turn has no thinking text. Also verified end-to-end with a local review run (
--format json) against providers with and without native reasoning output.Checklist
go fmt,go vet)Related Issues
N/A