Found this reading _query_stream in astrbot/core/provider/sources/gemini_source.py, not from a running deployment, so most of the bug report fields below don't really apply.
What happens: when Gemini streams narration before a tool-call chunk (for example "Sure, let me check that for you." followed by a separate chunk carrying the function_call), that narration is sent to the user live as streaming deltas, but the turn's final saved response is built only from the function-call chunk. The narration never reaches conversation history.
Why: the function-call branch (around line 769) does llm_response = LLMResponse(..., is_chunk=False), builds it from that one chunk, yields it, and returns as soon as any chunk carries a function_call part. That skips the accumulated_text/accumulated_reasoning recovery block further down (around lines 793-826), which is what #2429 added for the plain-text-finish path. The tool-call branch never got the same treatment.
Reproduction: fed three chunks (two narration, one function_call) into the real _query_stream, mocking only self.client.models.generate_content_stream. What the user sees live: ["Sure, ", "let me check that for you."]. What ends up in the final saved result_chain: empty.
AstrBot Version: N/A, found in source at HEAD 763a5ee, not a running deployment
Operating System / Deployment Method / Message Platform Adapter: N/A, code-level finding
Error Log: none, no runtime crash involved
Happy to send a PR extending the accumulated_text/accumulated_reasoning recovery to the tool-call branch if that's useful.
Found this reading
_query_streaminastrbot/core/provider/sources/gemini_source.py, not from a running deployment, so most of the bug report fields below don't really apply.What happens: when Gemini streams narration before a tool-call chunk (for example "Sure, let me check that for you." followed by a separate chunk carrying the
function_call), that narration is sent to the user live as streaming deltas, but the turn's final saved response is built only from the function-call chunk. The narration never reaches conversation history.Why: the function-call branch (around line 769) does
llm_response = LLMResponse(..., is_chunk=False), builds it from that one chunk, yields it, and returns as soon as any chunk carries afunction_callpart. That skips theaccumulated_text/accumulated_reasoningrecovery block further down (around lines 793-826), which is what #2429 added for the plain-text-finish path. The tool-call branch never got the same treatment.Reproduction: fed three chunks (two narration, one function_call) into the real
_query_stream, mocking onlyself.client.models.generate_content_stream. What the user sees live:["Sure, ", "let me check that for you."]. What ends up in the final savedresult_chain: empty.AstrBot Version: N/A, found in source at HEAD 763a5ee, not a running deployment
Operating System / Deployment Method / Message Platform Adapter: N/A, code-level finding
Error Log: none, no runtime crash involved
Happy to send a PR extending the accumulated_text/accumulated_reasoning recovery to the tool-call branch if that's useful.