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()