From 2fef38f955cbea7954a884c76d46a4b7caef0ded Mon Sep 17 00:00:00 2001 From: Wang Qi Date: Fri, 7 Aug 2026 17:30:14 +0800 Subject: [PATCH] Fix answer in think (#17998) --- api/db/services/dialog_service.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/api/db/services/dialog_service.py b/api/db/services/dialog_service.py index c6d1fba61d..79ef4bc1c9 100644 --- a/api/db/services/dialog_service.py +++ b/api/db/services/dialog_service.py @@ -2027,7 +2027,7 @@ async def rag_agent(dialog, messages, stream=True, **kwargs): else: stream_iter = chat_mdl.async_chat_streamly_delta(rag_tools.sys_prompt(), agent_messages, gen_conf, images=image_files) async for kind, value, state in _stream_with_think_delta(stream_iter): - event_queue.put_nowait(("stream", kind, value, state)) + event_queue.put_nowait(("stream", kind, value, state.in_think if state is not None else False)) except Exception: logging.exception("rag_agent: agentic stream failed") finally: @@ -2039,6 +2039,7 @@ async def rag_agent(dialog, messages, stream=True, **kwargs): answer_started = False think_closed = False outer_tool_started = False + pending_outer_text = [] async def _close_think_and_flush_answer(): nonlocal answer_started, think_closed @@ -2064,6 +2065,9 @@ async def rag_agent(dialog, messages, stream=True, **kwargs): continue if item[0] == "tool_started": outer_tool_started = True + for value in pending_outer_text: + yield {"answer": value, "reference": {}, "audio_binary": None, "final": False} + pending_outer_text.clear() continue if item[0] == "answer": if not answer_started: @@ -2080,8 +2084,15 @@ async def rag_agent(dialog, messages, stream=True, **kwargs): yield {"answer": value, "reference": {}, "audio_binary": None, "final": False} continue if item[0] == "stream_done": + if not outer_tool_started and pending_outer_text: + async for output in _close_think_and_flush_answer(): + yield output + for value in pending_outer_text: + answer_deltas.append(value) + yield {"answer": value, "reference": {}, "audio_binary": tts(tts_mdl, value), "final": False} + pending_outer_text.clear() break - _, kind, value, state = item + _, kind, value, in_think = item if kind != "text" or not value: # The outer model's think markers are folded into the one # block opened above; they must not create extra markers. @@ -2093,17 +2104,18 @@ async def rag_agent(dialog, messages, stream=True, **kwargs): # Once that section is closed and the terminal tool has # started, subsequent text is the aggregate tool result and is # intentionally ignored. - if state is not None and state.in_think: + if in_think: value = re.sub(r"", "", value) if value: yield {"answer": value, "reference": {}, "audio_binary": None, "final": False} elif not outer_tool_started: # Some providers omit explicit reasoning metadata and - # emit plain text before the tool call. Preserve it as - # outer thinking for compatibility with async_chat. + # emit plain text before the tool call. Keep it pending + # until we know whether a tool call or a direct answer + # follows, so a direct answer is not left in . value = re.sub(r"", "", value) if value: - yield {"answer": value, "reference": {}, "audio_binary": None, "final": False} + pending_outer_text.append(value) if not think_closed: async for output in _close_think_and_flush_answer(): yield output