From 66add8c5a67718cc9ce6ee9f80c02225396ebb9b Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:39:31 +0800 Subject: [PATCH 01/15] Add 'extra' field to ToolInvokeMeta class --- api/core/tools/entities/tool_entities.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/core/tools/entities/tool_entities.py b/api/core/tools/entities/tool_entities.py index 353f3a646a9542..23e6c0c5e11140 100644 --- a/api/core/tools/entities/tool_entities.py +++ b/api/core/tools/entities/tool_entities.py @@ -427,6 +427,7 @@ class ToolInvokeMeta(BaseModel): time_cost: float = Field(..., description="The time cost of the tool invoke") error: str | None = None tool_config: dict | None = None + extra: dict | None = None @classmethod def empty(cls) -> "ToolInvokeMeta": @@ -447,6 +448,7 @@ def to_dict(self): "time_cost": self.time_cost, "error": self.error, "tool_config": self.tool_config, + "extra": self.extra, } From 5aa563447fb4d6862df526c2eb0f60f39db87925 Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:40:13 +0800 Subject: [PATCH 02/15] Implement return_direct signal detection Added detection for 'return_direct' signal in tool messages. --- api/core/tools/tool_engine.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/api/core/tools/tool_engine.py b/api/core/tools/tool_engine.py index 13fd579e20aa01..911a5bafacbbc2 100644 --- a/api/core/tools/tool_engine.py +++ b/api/core/tools/tool_engine.py @@ -107,9 +107,21 @@ def message_callback( tool_messages=binary_files, agent_message=message, invoke_from=invoke_from, user_id=user_id ) + # detect return_direct signal from variable messages + return_direct = False + for m in message_list: + if m.type == ToolInvokeMessage.MessageType.VARIABLE: + variable = cast(ToolInvokeMessage.VariableMessage, m.message) + if variable and variable.variable_name == "return_direct": + try: + return_direct = bool(variable.variable_value) + except Exception: + return_direct = False + plain_text = ToolEngine._convert_tool_response_to_str(message_list) meta = invocation_meta_dict["meta"] + meta.extra = {"return_direct": return_direct} # hit the callback handler agent_tool_callback.on_tool_end( @@ -254,6 +266,9 @@ def _convert_tool_response_to_str(tool_response: list[ToolInvokeMessage]) -> str ensure_ascii=False, ) ) + elif response.type == ToolInvokeMessage.MessageType.VARIABLE: + # internal variable messages should not be surfaced into plain text + continue else: parts.append(str(response.message)) From 2a5a6ec1ae481abee14673d0e25c37d0dc0a5dbd Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:41:05 +0800 Subject: [PATCH 03/15] Update fc_agent_runner.py --- api/core/agent/fc_agent_runner.py | 53 ++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/api/core/agent/fc_agent_runner.py b/api/core/agent/fc_agent_runner.py index dcc1326b33bc34..e9da2510446200 100644 --- a/api/core/agent/fc_agent_runner.py +++ b/api/core/agent/fc_agent_runner.py @@ -265,7 +265,14 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU } tool_responses.append(tool_response) - if tool_response["tool_response"] is not None: + # check direct return flag + direct_flag = False + try: + direct_flag = bool(tool_invoke_meta.extra and tool_invoke_meta.extra.get("return_direct")) + except Exception: + direct_flag = False + + if tool_response["tool_response"] is not None and not direct_flag: self._current_thoughts.append( ToolPromptMessage( content=str(tool_response["tool_response"]), @@ -274,6 +281,50 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU ) ) + if direct_flag: + # save agent thought for this tool call + self.save_agent_thought( + agent_thought_id=agent_thought_id, + tool_name="", + tool_input="", + thought="", + tool_invoke_meta={tool_call_name: tool_invoke_meta.to_dict()}, + observation={tool_call_name: tool_invoke_response}, + answer=str(tool_invoke_response or ""), + messages_ids=message_file_ids, + ) + self.queue_manager.publish( + QueueAgentThoughtEvent(agent_thought_id=agent_thought_id), PublishFrom.APPLICATION_MANAGER + ) + + # publish end event immediately and return + final_answer = str(tool_invoke_response or "") + llm_final_usage = llm_usage.get("usage") or LLMUsage.empty_usage() + self.queue_manager.publish( + QueueMessageEndEvent( + llm_result=LLMResult( + model=model_instance.model, + prompt_messages=prompt_messages, + message=AssistantPromptMessage(content=final_answer), + usage=llm_final_usage, + system_fingerprint="", + ) + ), + PublishFrom.APPLICATION_MANAGER, + ) + + yield LLMResultChunk( + model=model_instance.model, + prompt_messages=prompt_messages, + system_fingerprint="", + delta=LLMResultChunkDelta( + index=0, + message=AssistantPromptMessage(content=final_answer), + usage=llm_final_usage, + ), + ) + return + if len(tool_responses) > 0: # save agent thought self.save_agent_thought( From 6ea3dcbd873b2c8ea12d6611636c77f25d5e5526 Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:41:45 +0800 Subject: [PATCH 04/15] Update cot_agent_runner.py --- api/core/agent/cot_agent_runner.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/api/core/agent/cot_agent_runner.py b/api/core/agent/cot_agent_runner.py index 25ad6dc06017db..86d63a21d99a9a 100644 --- a/api/core/agent/cot_agent_runner.py +++ b/api/core/agent/cot_agent_runner.py @@ -202,7 +202,6 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU except TypeError: final_answer = f"{scratchpad.action.action_input}" else: - function_call_state = True # action is tool call, invoke tool tool_invoke_response, tool_invoke_meta = self._handle_invoke_action( action=scratchpad.action, @@ -213,6 +212,13 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU scratchpad.observation = tool_invoke_response scratchpad.agent_response = tool_invoke_response + # detect direct return + direct_flag = False + try: + direct_flag = bool(tool_invoke_meta.extra and tool_invoke_meta.extra.get("return_direct")) + except Exception: + direct_flag = False + self.save_agent_thought( agent_thought_id=agent_thought_id, tool_name=scratchpad.action.action_name, @@ -229,6 +235,12 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU QueueAgentThoughtEvent(agent_thought_id=agent_thought_id), PublishFrom.APPLICATION_MANAGER ) + if direct_flag: + final_answer = str(tool_invoke_response or "") + # keep function_call_state as False to end iterations + else: + function_call_state = True + # update prompt tool message for prompt_tool in self._prompt_messages_tools: self.update_prompt_message_tool(tool_instances[prompt_tool.name], prompt_tool) From fe55883e561d9454127984027a14b1c9cb2e7ead Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 16:10:03 +0800 Subject: [PATCH 05/15] Update api/core/agent/fc_agent_runner.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- api/core/agent/fc_agent_runner.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/api/core/agent/fc_agent_runner.py b/api/core/agent/fc_agent_runner.py index e9da2510446200..34b2cd5615b1cb 100644 --- a/api/core/agent/fc_agent_runner.py +++ b/api/core/agent/fc_agent_runner.py @@ -266,11 +266,7 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU tool_responses.append(tool_response) # check direct return flag - direct_flag = False - try: - direct_flag = bool(tool_invoke_meta.extra and tool_invoke_meta.extra.get("return_direct")) - except Exception: - direct_flag = False + direct_flag = bool(tool_invoke_meta.extra and tool_invoke_meta.extra.get("return_direct")) if tool_response["tool_response"] is not None and not direct_flag: self._current_thoughts.append( From c0d41faa12831c576e0795f661fe042b12c027e8 Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 16:10:17 +0800 Subject: [PATCH 06/15] Update api/core/agent/cot_agent_runner.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- api/core/agent/cot_agent_runner.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/api/core/agent/cot_agent_runner.py b/api/core/agent/cot_agent_runner.py index 86d63a21d99a9a..67c5769d9e605b 100644 --- a/api/core/agent/cot_agent_runner.py +++ b/api/core/agent/cot_agent_runner.py @@ -213,11 +213,7 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU scratchpad.agent_response = tool_invoke_response # detect direct return - direct_flag = False - try: - direct_flag = bool(tool_invoke_meta.extra and tool_invoke_meta.extra.get("return_direct")) - except Exception: - direct_flag = False + direct_flag = bool(tool_invoke_meta.extra and tool_invoke_meta.extra.get("return_direct")) self.save_agent_thought( agent_thought_id=agent_thought_id, From 9d144e5c833c1c8908946cd3fbf15a4c80debce8 Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 16:10:42 +0800 Subject: [PATCH 07/15] Update api/core/agent/fc_agent_runner.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- api/core/agent/fc_agent_runner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/core/agent/fc_agent_runner.py b/api/core/agent/fc_agent_runner.py index 34b2cd5615b1cb..45e95c28a6690c 100644 --- a/api/core/agent/fc_agent_runner.py +++ b/api/core/agent/fc_agent_runner.py @@ -281,8 +281,8 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU # save agent thought for this tool call self.save_agent_thought( agent_thought_id=agent_thought_id, - tool_name="", - tool_input="", + tool_name=tool_call_name, + tool_input=tool_call_args, thought="", tool_invoke_meta={tool_call_name: tool_invoke_meta.to_dict()}, observation={tool_call_name: tool_invoke_response}, From a87f1165f20d4306a2327e705722580ff53e12e4 Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 16:32:07 +0800 Subject: [PATCH 08/15] Update tool_engine.py --- api/core/tools/tool_engine.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/api/core/tools/tool_engine.py b/api/core/tools/tool_engine.py index 911a5bafacbbc2..946c2d8a27d9ea 100644 --- a/api/core/tools/tool_engine.py +++ b/api/core/tools/tool_engine.py @@ -113,10 +113,7 @@ def message_callback( if m.type == ToolInvokeMessage.MessageType.VARIABLE: variable = cast(ToolInvokeMessage.VariableMessage, m.message) if variable and variable.variable_name == "return_direct": - try: - return_direct = bool(variable.variable_value) - except Exception: - return_direct = False + return_direct = bool(variable.variable_value) plain_text = ToolEngine._convert_tool_response_to_str(message_list) From 31dee2340b4259398be141ff4cec35be7d858ea2 Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 16:37:36 +0800 Subject: [PATCH 09/15] Update api/core/tools/tool_engine.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- api/core/tools/tool_engine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/tools/tool_engine.py b/api/core/tools/tool_engine.py index 946c2d8a27d9ea..4253b5b9ea9c0b 100644 --- a/api/core/tools/tool_engine.py +++ b/api/core/tools/tool_engine.py @@ -113,7 +113,7 @@ def message_callback( if m.type == ToolInvokeMessage.MessageType.VARIABLE: variable = cast(ToolInvokeMessage.VariableMessage, m.message) if variable and variable.variable_name == "return_direct": - return_direct = bool(variable.variable_value) + return_direct = bool(variable.variable_value) plain_text = ToolEngine._convert_tool_response_to_str(message_list) From e0e278cf4ceede057234ab9b6286e82f998a93f6 Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 16:47:58 +0800 Subject: [PATCH 10/15] Update fc_agent_runner.py --- api/core/agent/fc_agent_runner.py | 70 +++++++++++++++---------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/api/core/agent/fc_agent_runner.py b/api/core/agent/fc_agent_runner.py index 45e95c28a6690c..23d0b25db25363 100644 --- a/api/core/agent/fc_agent_runner.py +++ b/api/core/agent/fc_agent_runner.py @@ -296,29 +296,7 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU # publish end event immediately and return final_answer = str(tool_invoke_response or "") llm_final_usage = llm_usage.get("usage") or LLMUsage.empty_usage() - self.queue_manager.publish( - QueueMessageEndEvent( - llm_result=LLMResult( - model=model_instance.model, - prompt_messages=prompt_messages, - message=AssistantPromptMessage(content=final_answer), - usage=llm_final_usage, - system_fingerprint="", - ) - ), - PublishFrom.APPLICATION_MANAGER, - ) - - yield LLMResultChunk( - model=model_instance.model, - prompt_messages=prompt_messages, - system_fingerprint="", - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=final_answer), - usage=llm_final_usage, - ), - ) + yield from self._yield_final_answer(prompt_messages, final_answer, llm_final_usage) return if len(tool_responses) > 0: @@ -348,18 +326,10 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU iteration_step += 1 - # publish end event - self.queue_manager.publish( - QueueMessageEndEvent( - llm_result=LLMResult( - model=model_instance.model, - prompt_messages=prompt_messages, - message=AssistantPromptMessage(content=final_answer), - usage=llm_usage["usage"] or LLMUsage.empty_usage(), - system_fingerprint="", - ) - ), - PublishFrom.APPLICATION_MANAGER, + yield from self._yield_final_answer( + prompt_messages, + final_answer, + llm_usage["usage"] or LLMUsage.empty_usage(), ) def check_tool_calls(self, llm_result_chunk: LLMResultChunk) -> bool: @@ -424,6 +394,36 @@ def extract_blocking_tool_calls(self, llm_result: LLMResult) -> list[tuple[str, return tool_calls + def _yield_final_answer( + self, + prompt_messages: list, + final_answer: str, + usage: LLMUsage, + ) -> Generator[LLMResultChunk, None, None]: + self.queue_manager.publish( + QueueMessageEndEvent( + llm_result=LLMResult( + model=self.model_instance.model, + prompt_messages=prompt_messages, + message=AssistantPromptMessage(content=final_answer), + usage=usage, + system_fingerprint="", + ) + ), + PublishFrom.APPLICATION_MANAGER, + ) + + yield LLMResultChunk( + model=self.model_instance.model, + prompt_messages=prompt_messages, + system_fingerprint="", + delta=LLMResultChunkDelta( + index=0, + message=AssistantPromptMessage(content=final_answer), + usage=usage, + ), + ) + def _init_system_message(self, prompt_template: str, prompt_messages: list[PromptMessage]) -> list[PromptMessage]: """ Initialize system message From db81680f6fd016ee9991c8324c0ef5b0be5b40a1 Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:02:28 +0800 Subject: [PATCH 11/15] Update tool_engine.py --- api/core/tools/tool_engine.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/api/core/tools/tool_engine.py b/api/core/tools/tool_engine.py index 4253b5b9ea9c0b..95239c62d9bd4b 100644 --- a/api/core/tools/tool_engine.py +++ b/api/core/tools/tool_engine.py @@ -107,13 +107,14 @@ def message_callback( tool_messages=binary_files, agent_message=message, invoke_from=invoke_from, user_id=user_id ) - # detect return_direct signal from variable messages - return_direct = False - for m in message_list: - if m.type == ToolInvokeMessage.MessageType.VARIABLE: - variable = cast(ToolInvokeMessage.VariableMessage, m.message) - if variable and variable.variable_name == "return_direct": - return_direct = bool(variable.variable_value) + # detect return_direct signal from variable messages (short-circuit) + return_direct = any( + m.type == ToolInvokeMessage.MessageType.VARIABLE + and (variable := cast(ToolInvokeMessage.VariableMessage, m.message)) + and variable.variable_name == "return_direct" + and bool(variable.variable_value) + for m in message_list + ) plain_text = ToolEngine._convert_tool_response_to_str(message_list) From c33056c24f19e1311914733fd9c244c5f2212070 Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:23:32 +0800 Subject: [PATCH 12/15] Update api/core/agent/cot_agent_runner.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- api/core/agent/cot_agent_runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/agent/cot_agent_runner.py b/api/core/agent/cot_agent_runner.py index 67c5769d9e605b..eea40821282c28 100644 --- a/api/core/agent/cot_agent_runner.py +++ b/api/core/agent/cot_agent_runner.py @@ -213,7 +213,7 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU scratchpad.agent_response = tool_invoke_response # detect direct return - direct_flag = bool(tool_invoke_meta.extra and tool_invoke_meta.extra.get("return_direct")) + direct_flag = (tool_invoke_meta.extra or {}).get("return_direct", False) self.save_agent_thought( agent_thought_id=agent_thought_id, From b902395ce897d657daedb3dda50db2bb40cb8d42 Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:23:59 +0800 Subject: [PATCH 13/15] Update api/core/agent/fc_agent_runner.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- api/core/agent/fc_agent_runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/agent/fc_agent_runner.py b/api/core/agent/fc_agent_runner.py index 23d0b25db25363..95fe18b4f56cc7 100644 --- a/api/core/agent/fc_agent_runner.py +++ b/api/core/agent/fc_agent_runner.py @@ -266,7 +266,7 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU tool_responses.append(tool_response) # check direct return flag - direct_flag = bool(tool_invoke_meta.extra and tool_invoke_meta.extra.get("return_direct")) + direct_flag = (tool_invoke_meta.extra or {}).get("return_direct", False) if tool_response["tool_response"] is not None and not direct_flag: self._current_thoughts.append( From c1b2a69491b3ee49749eec12ee0e24da25df01b1 Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:24:37 +0800 Subject: [PATCH 14/15] Update api/core/tools/tool_engine.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- api/core/tools/tool_engine.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/api/core/tools/tool_engine.py b/api/core/tools/tool_engine.py index 95239c62d9bd4b..0c49ffda2e8440 100644 --- a/api/core/tools/tool_engine.py +++ b/api/core/tools/tool_engine.py @@ -108,13 +108,13 @@ def message_callback( ) # detect return_direct signal from variable messages (short-circuit) - return_direct = any( - m.type == ToolInvokeMessage.MessageType.VARIABLE - and (variable := cast(ToolInvokeMessage.VariableMessage, m.message)) - and variable.variable_name == "return_direct" - and bool(variable.variable_value) - for m in message_list - ) + return_direct = False + for m in message_list: + if m.type == ToolInvokeMessage.MessageType.VARIABLE: + variable = cast(ToolInvokeMessage.VariableMessage, m.message) + if variable.variable_name == "return_direct" and bool(variable.variable_value): + return_direct = True + break plain_text = ToolEngine._convert_tool_response_to_str(message_list) From 9327270a8a832b7c62d689025b0c758725736572 Mon Sep 17 00:00:00 2001 From: Cursx <33718736+Cursx@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:47:49 +0800 Subject: [PATCH 15/15] Update api/core/agent/fc_agent_runner.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- api/core/agent/fc_agent_runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/agent/fc_agent_runner.py b/api/core/agent/fc_agent_runner.py index 95fe18b4f56cc7..fe24a27491d39e 100644 --- a/api/core/agent/fc_agent_runner.py +++ b/api/core/agent/fc_agent_runner.py @@ -283,7 +283,7 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage | None], usage: LLMU agent_thought_id=agent_thought_id, tool_name=tool_call_name, tool_input=tool_call_args, - thought="", + thought=llm_result.message.content or "", tool_invoke_meta={tool_call_name: tool_invoke_meta.to_dict()}, observation={tool_call_name: tool_invoke_response}, answer=str(tool_invoke_response or ""),