Repository navigation
Conversation
In skills_like mode, step() adopts the re-query's tool names, args and IDs but kept the first response's tools_call_extra_content. Provider metadata such as Gemini thought signatures is keyed by tool-call ID, so the recorded assistant turn either lost the signature (new IDs) or replayed the stale selection signature (same IDs), and Gemini rejected the next request. Fixes AstrBotDevs#9217
Contributor
There was a problem hiding this comment.
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="astrbot/core/agent/runners/tool_loop_agent_runner.py" line_range="1014-1015" />
<code_context>
+ # the final tool-call IDs, so it must move with them. Keeping the
+ # first response's metadata here replays a missing or stale
+ # signature to the provider on the next turn.
+ llm_resp.tools_call_extra_content = (
+ requery_resp.tools_call_extra_content
+ )
</code_context>
<issue_to_address>
**Stale signature is replayed**
When the selection response has a Gemini thought signature and the re-query returns a different one, `llm_resp.reasoning_signature` remains from the selection response while the tool-call metadata comes from the re-query, so the runner stores the selection signature alongside the re-query’s call and `ProviderGoogleGenAI._prepare_conversation()` replays mismatched signature state to Gemini.
Adopt the re-query’s reasoning signature when adopting its tool-call metadata, while preserving the selection response’s user-visible narration.
</issue_to_address>
### Comment 2
<location path="astrbot/core/agent/runners/tool_loop_agent_runner.py" line_range="1014-1016" />
<code_context>
+ # the final tool-call IDs, so it must move with them. Keeping the
+ # first response's metadata here replays a missing or stale
+ # signature to the provider on the next turn.
+ llm_resp.tools_call_extra_content = (
+ requery_resp.tools_call_extra_content
+ )
tool_call_result_blocks = []
</code_context>
<issue_to_address>
**Repeated calls share signatures**
When a re-query returns multiple calls to the same Gemini tool and those calls have no function-call IDs, `_process_content_parts()` uses the tool name as the ID when a Gemini function call has no ID, and stores signatures in a dict keyed by that ID. For repeated calls to the same tool, only the last signature survives; `to_openai_tool_calls_model()` then replays it for both calls, so Gemini rejects the mismatched call.
Preserve per-call signatures when IDs are absent or duplicated, for example by assigning unique IDs and retaining a per-call metadata mapping.
</issue_to_address>Sourcery assessment
Approval pending. 2 findings to address first.
Blocking findings: astrbot/core/agent/runners/tool_loop_agent_runner.py:1015, astrbot/core/agent/runners/tool_loop_agent_runner.py:1016
The re-query's reasoning_content and reasoning_signature now move with its tool-call bundle. Anthropic rebuilds the thinking block from both and validates the signature against the text, so they cannot be split, and Gemini otherwise replays two signatures from different turns inside one model content. Only the first response's narration is kept, as in the existing no-tool-call fallback. Refs AstrBotDevs#9217
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #9217. Supersedes #9220, which was closed as stale.
In
skills_liketool schema mode,ToolLoopAgentRunner.step()first asks the model to select tools with the light schema, then_resolve_tool_exec()re-queries with the param-only schema. The runner adopts the re-query'stools_call_name/tools_call_args/tools_call_idsbut keeps the first response'stools_call_extra_content.LLMResponse.to_openai_tool_calls_model()looks that metadata up by the final tool-call ID, so the assistant turn written to history either has no signature (the re-query returned new IDs) or carries the signature of the selection call (same IDs; Gemini falls back to the tool name as the ID, so this is the common case). On the next requestProviderGoogleGenAI._prepare_conversation()replays the function call without the signature of the actual call, Gemini rejects it withFunction call is missing a thought_signature, and the provider fallback kicks in.Modifications / 改动点
astrbot/core/agent/runners/tool_loop_agent_runner.py: whenstep()adopts the re-query result, also adopttools_call_extra_content. One assignment next to the existing three; no helper.fullmode and providers without per-call metadata are unaffected, and the first response's user-visible narration is still kept.tests/test_tool_loop_agent_runner.py: a two-step provider and three regression tests: a single tool with different and with identical IDs, two tools each keeping their own metadata (nothing is broadcast), and a round trip that feeds the recorded history intoProviderGoogleGenAI._prepare_conversation()and asserts the function-call part carries the re-query'sthought_signature.Out of scope: the
tool_schema_mode=fullpath is unchanged. If the error still reproduces there, that is a separate path, as noted in the issue analysis.Screenshots or Test Results / 运行截图或测试结果
With the runner change stashed, the new tests fail in the two expected ways:
extra_contentisNonewhen the re-query returns new IDs, and equals the selection signature when the IDs are identical.uv run ruff formatanduv run ruff checkare clean on the changed files. No live Gemini request was made; the round trip runs against the real runner and the real Gemini history converter.Checklist / 检查清单
docs/zh/docs/enupdate is needed.Summary by Sourcery
Preserve executed tool-call metadata across skills-like re-queries to prevent missing or stale signatures during subsequent provider requests.
Bug Fixes:
Enhancements:
Tests: