From 10edb46f8038a2f71ae1572daa6dcaeaef5b7c19 Mon Sep 17 00:00:00 2001 From: Roland Tannous Date: Sat, 11 Apr 2026 22:30:46 +0400 Subject: [PATCH] Emit custom tool_result SSE event in Anthropic stream Adds a non-standard tool_result event between the tool_use block close and the next text block, so clients can see server-side tool execution results. Anthropic SDKs ignore unknown event types. --- studio/backend/core/inference/anthropic_compat.py | 6 ++++++ studio/backend/tests/test_anthropic_messages.py | 12 ++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/studio/backend/core/inference/anthropic_compat.py b/studio/backend/core/inference/anthropic_compat.py index fc466a4aa9..ec056052ca 100644 --- a/studio/backend/core/inference/anthropic_compat.py +++ b/studio/backend/core/inference/anthropic_compat.py @@ -224,6 +224,12 @@ class AnthropicStreamEmitter: events = [] # Close the tool_use block events.append(self._close_block()) + # Emit custom tool_result event (non-standard, ignored by SDKs) + events.append(build_anthropic_sse_event("tool_result", { + "type": "tool_result", + "tool_use_id": event.get("tool_call_id", ""), + "content": event.get("result", ""), + })) # Open a new text block for the model's next response self.block_index += 1 events.extend(self._open_text_block()) diff --git a/studio/backend/tests/test_anthropic_messages.py b/studio/backend/tests/test_anthropic_messages.py index 51af665c1c..19055b63b8 100644 --- a/studio/backend/tests/test_anthropic_messages.py +++ b/studio/backend/tests/test_anthropic_messages.py @@ -328,11 +328,15 @@ class TestAnthropicStreamEmitter: e.start("msg_1", "m") e.feed({"type": "tool_start", "tool_name": "t", "tool_call_id": "tc_1", "arguments": {}}) events = e.feed({"type": "tool_end", "tool_name": "t", "tool_call_id": "tc_1", "result": "done"}) - # content_block_stop (tool) + content_block_start (new text) - assert len(events) == 2 + # content_block_stop (tool) + tool_result + content_block_start (new text) + assert len(events) == 3 assert "content_block_stop" in events[0] - assert "content_block_start" in events[1] - assert '"type": "text"' in events[1] + assert "tool_result" in events[1] + parsed = json.loads(events[1].split("data: ")[1]) + assert parsed["content"] == "done" + assert parsed["tool_use_id"] == "tc_1" + assert "content_block_start" in events[2] + assert '"type": "text"' in events[2] def test_finish_emits_stop_events(self): e = AnthropicStreamEmitter()