@@ -851,6 +851,81 @@ async def completion_side_effect(*args, **kwargs):
851851 assert all_chunks [1 ].chunk == "The answer is humorous."
852852
853853
854+ @pytest .mark .anyio
855+ async def test_stream_listener_returns_correct_chunk_baml_adapter ():
856+ class MyProgram (dspy .Module ):
857+ def __init__ (self ):
858+ super ().__init__ ()
859+ self .predict1 = dspy .Predict ("question->answer" )
860+ self .predict2 = dspy .Predict ("question,answer->judgement" )
861+
862+ def forward (self , question , ** kwargs ):
863+ answer = self .predict1 (question = question , ** kwargs ).answer
864+ judgement = self .predict2 (question = question , answer = answer , ** kwargs )
865+ return judgement
866+
867+ async def baml_stream_1 (* args , ** kwargs ):
868+ # BAML uses JSON format for responses but ChatAdapter-style field delimiters in prompts
869+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = '{"' ))])
870+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "answer" ))])
871+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = '":' ))])
872+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "To" ))])
873+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = " get" ))])
874+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = " to" ))])
875+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = " the" ))])
876+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = " other" ))])
877+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = " side" ))])
878+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "!" ))])
879+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = '"' ))])
880+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "}\n " ))])
881+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "None" ))])
882+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "None" ))])
883+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "None" ))])
884+
885+ async def baml_stream_2 (* args , ** kwargs ):
886+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = '{"' ))])
887+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "judgement" ))])
888+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = '":"' ))])
889+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "The" ))])
890+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = " answer" ))])
891+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = " is" ))])
892+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = " humorous" ))])
893+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "." ))])
894+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = '"' ))])
895+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "}" ))])
896+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "None" ))])
897+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "None" ))])
898+ yield ModelResponseStream (model = "gpt-4o-mini" , choices = [StreamingChoices (delta = Delta (content = "None" ))])
899+
900+ stream_generators = [baml_stream_1 , baml_stream_2 ]
901+
902+ async def completion_side_effect (* args , ** kwargs ):
903+ return stream_generators .pop (0 )()
904+
905+ with mock .patch ("litellm.acompletion" , side_effect = completion_side_effect ):
906+ program = dspy .streamify (
907+ MyProgram (),
908+ stream_listeners = [
909+ dspy .streaming .StreamListener (signature_field_name = "answer" ),
910+ dspy .streaming .StreamListener (signature_field_name = "judgement" ),
911+ ],
912+ )
913+ with dspy .context (lm = dspy .LM ("openai/gpt-4o-mini" , cache = False ), adapter = dspy .BAMLAdapter ()):
914+ output = program (question = "why did a chicken cross the kitchen?" )
915+ all_chunks = []
916+ async for value in output :
917+ if isinstance (value , dspy .streaming .StreamResponse ):
918+ all_chunks .append (value )
919+
920+ assert all_chunks [0 ].predict_name == "predict1"
921+ assert all_chunks [0 ].signature_field_name == "answer"
922+ assert all_chunks [0 ].chunk == "To get to the other side!"
923+
924+ assert all_chunks [1 ].predict_name == "predict2"
925+ assert all_chunks [1 ].signature_field_name == "judgement"
926+ assert all_chunks [1 ].chunk == "The answer is humorous."
927+
928+
854929@pytest .mark .anyio
855930async def test_streaming_allows_custom_chunk_types ():
856931 @dataclass
0 commit comments