From 07c9cf6cbe4d05402acf0f5188a94218ab1533b2 Mon Sep 17 00:00:00 2001 From: yzy <183124902@qq.com> Date: Tue, 10 Mar 2026 19:22:04 +0800 Subject: [PATCH] Fix: return structured JSON output for non-streaming agent API (#13389) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What problem does this PR solve? Previously, when an Agent component was configured with structured output, the non-streaming /agents/{agent_id}/completions API never returned the structured field in its response. The root cause: the non-streaming code path only collected message events to build full_content, then returned the workflow_finished payload — which only contains the output of the last component in the execution path (typically a Message component). Any structured output set by upstream components (e.g., Agent or LLM) was silently discarded. This PR fixes the non-streaming handler to iterate node_finished events and collect structured output from intermediate components. If any component produced a non-empty structured value, it is included in the final response under data.structured. The streaming path is unaffected, as it already exposes node_finished events to the caller. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/apps/sdk/session.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/api/apps/sdk/session.py b/api/apps/sdk/session.py index 10439564d5..e628a3c960 100644 --- a/api/apps/sdk/session.py +++ b/api/apps/sdk/session.py @@ -584,6 +584,7 @@ async def agent_completions(tenant_id, agent_id): reference = {} final_ans = "" trace_items = [] + structured_output = {} async for answer in agent_completion(tenant_id=tenant_id, agent_id=agent_id, **req): try: ans = json.loads(answer[5:]) @@ -594,20 +595,26 @@ async def agent_completions(tenant_id, agent_id): if ans.get("data", {}).get("reference", None): reference.update(ans["data"]["reference"]) - if return_trace and ans.get("event") == "node_finished": - data = ans.get("data", {}) - trace_items.append( - { - "component_id": data.get("component_id"), - "trace": [copy.deepcopy(data)], - } - ) + if ans.get("event") == "node_finished": + node_out = ans.get("data", {}).get("outputs", {}) + if node_out.get("structured"): + structured_output = node_out["structured"] + if return_trace: + data = ans.get("data", {}) + trace_items.append( + { + "component_id": data.get("component_id"), + "trace": [copy.deepcopy(data)], + } + ) final_ans = ans except Exception as e: return get_result(data=f"**ERROR**: {str(e)}") final_ans["data"]["content"] = full_content final_ans["data"]["reference"] = reference + if structured_output: + final_ans["data"]["structured"] = structured_output if return_trace and final_ans: final_ans["data"]["trace"] = trace_items return get_result(data=final_ans)