55import json
66import tempfile
77import zipfile
8+ from enum import Enum
89from pathlib import Path
910from unittest .mock import MagicMock , patch
1011
@@ -205,6 +206,33 @@ def test_basic_message(self, client):
205206 msg_events = _ai_events (events )
206207 assert msg_events [0 ].data ["content" ] == "Hello!"
207208
209+ def test_custom_events_are_forwarded (self , client ):
210+ """stream() forwards custom stream events alongside normal values output."""
211+ ai = AIMessage (content = "Hello!" , id = "ai-1" )
212+ agent = MagicMock ()
213+ agent .stream .return_value = iter (
214+ [
215+ ("custom" , {"type" : "task_started" , "task_id" : "task-1" }),
216+ ("values" , {"messages" : [HumanMessage (content = "hi" , id = "h-1" ), ai ]}),
217+ ]
218+ )
219+
220+ with (
221+ patch .object (client , "_ensure_agent" ),
222+ patch .object (client , "_agent" , agent ),
223+ ):
224+ events = list (client .stream ("hi" , thread_id = "t-custom" ))
225+
226+ agent .stream .assert_called_once ()
227+ call_kwargs = agent .stream .call_args .kwargs
228+ assert call_kwargs ["stream_mode" ] == ["values" , "custom" ]
229+
230+ assert events [0 ].type == "custom"
231+ assert events [0 ].data == {"type" : "task_started" , "task_id" : "task-1" }
232+ assert any (event .type == "messages-tuple" and event .data ["content" ] == "Hello!" for event in events )
233+ assert any (event .type == "values" for event in events )
234+ assert events [- 1 ].type == "end"
235+
208236 def test_context_propagation (self , client ):
209237 """stream() passes agent_name to the context."""
210238 agent = _make_agent_mock ([{"messages" : [AIMessage (content = "ok" , id = "ai-1" )]}])
@@ -222,6 +250,33 @@ def test_context_propagation(self, client):
222250 assert call_kwargs ["context" ]["thread_id" ] == "t1"
223251 assert call_kwargs ["context" ]["agent_name" ] == "test-agent-1"
224252
253+ def test_custom_mode_is_normalized_to_string (self , client ):
254+ """stream() forwards custom events even when the mode is not a plain string."""
255+
256+ class StreamMode (Enum ):
257+ CUSTOM = "custom"
258+
259+ def __str__ (self ):
260+ return self .value
261+
262+ agent = _make_agent_mock (
263+ [
264+ (StreamMode .CUSTOM , {"type" : "task_started" , "task_id" : "task-1" }),
265+ {"messages" : [AIMessage (content = "Hello!" , id = "ai-1" )]},
266+ ]
267+ )
268+
269+ with (
270+ patch .object (client , "_ensure_agent" ),
271+ patch .object (client , "_agent" , agent ),
272+ ):
273+ events = list (client .stream ("hi" , thread_id = "t-custom-enum" ))
274+
275+ assert events [0 ].type == "custom"
276+ assert events [0 ].data == {"type" : "task_started" , "task_id" : "task-1" }
277+ assert any (event .type == "messages-tuple" and event .data ["content" ] == "Hello!" for event in events )
278+ assert events [- 1 ].type == "end"
279+
225280 def test_tool_call_and_result (self , client ):
226281 """stream() emits messages-tuple events for tool calls and results."""
227282 ai = AIMessage (content = "" , id = "ai-1" , tool_calls = [{"name" : "bash" , "args" : {"cmd" : "ls" }, "id" : "tc-1" }])
0 commit comments