@@ -223,6 +223,89 @@ async def test_collector_async_stream_success():
223223 assert function_call_count () > 0
224224
225225
226+ @pytest .mark .asyncio
227+ async def test_collector_async_stream_success_gemini ():
228+ collector = Collector (name = "my-collector" )
229+ function_logs = collector .logs
230+ assert len (function_logs ) == 0
231+
232+ stream = b .stream .TestGemini ("hi there" , baml_options = {"collector" : collector })
233+
234+ async for chunk in stream :
235+ print (f"### chunk: { chunk } " )
236+
237+ res = await stream .get_final_response ()
238+ print (f"### res: { res } " )
239+ function_logs = collector .logs
240+
241+ function_logs = collector .logs
242+ assert len (function_logs ) == 1
243+
244+ log = collector .last
245+ assert log is not None
246+ assert log .function_name == "TestGemini"
247+ assert log .log_type == "stream"
248+
249+ function_logs = collector .logs
250+ assert len (function_logs ) == 1
251+
252+ log = collector .last
253+ assert log is not None
254+ assert log .function_name == "TestGemini"
255+ assert log .log_type == "stream"
256+
257+ # Verify timing fields
258+ assert log .timing .start_time_utc_ms > 0
259+ assert log .timing .duration_ms is not None and log .timing .duration_ms > 0
260+
261+ # Verify usage fields
262+ assert log .usage .input_tokens is not None and log .usage .input_tokens > 0
263+ assert log .usage .output_tokens is not None and log .usage .output_tokens > 0
264+
265+ # Verify calls
266+ calls = log .calls
267+ assert len (calls ) == 1
268+
269+ call = calls [0 ]
270+
271+ assert call .provider == "google-ai"
272+ assert call .client_name == "Gemini"
273+ assert call .selected
274+
275+ # Verify request/response
276+ request = call .http_request
277+ assert request is not None
278+ print (f"### request.body: { request .body } \n { type (request .body )} " , file = sys .stderr )
279+ body = request .body .json ()
280+
281+ print (f"{ call .usage } " )
282+
283+ assert isinstance (body , dict )
284+ assert "contents" in body
285+
286+ # Verify http response
287+ response = call .http_response
288+ assert response is None
289+
290+ # Verify call timing
291+ call_timing = call .timing
292+ assert call_timing .start_time_utc_ms > 0
293+ assert call_timing .duration_ms is not None and call_timing .duration_ms > 0
294+
295+ # Verify call usage
296+ call_usage = call .usage
297+ assert call_usage .input_tokens is not None and call_usage .input_tokens > 0
298+ assert call_usage .output_tokens is not None and call_usage .output_tokens > 0
299+
300+ # Verify raw response exists
301+ assert log .raw_llm_response is not None
302+
303+ gc .collect ()
304+ print ("----- gc.collect() -----" , file = sys .stderr )
305+ # still not collected cause it's in use
306+ assert function_call_count () > 0
307+
308+
226309@pytest .mark .asyncio
227310async def test_collector_async_multiple_calls_usage ():
228311 collector = Collector (name = "my-collector" )
@@ -890,7 +973,9 @@ async def test_collector_openai_stream_chunk_verification():
890973 assert call .client_name == "GPT4oMini"
891974 sse_chunks = call .sse_responses ()
892975 assert sse_chunks is not None
893- assert len (sse_chunks ) >= len (chunks_received ), f"Expected { len (chunks_received )} chunks, got { sse_chunks } "
976+ assert len (sse_chunks ) >= len (
977+ chunks_received
978+ ), f"Expected { len (chunks_received )} chunks, got { sse_chunks } "
894979 for chunk in sse_chunks :
895980 print (f"Chunk: { chunk .json ()} " )
896981
@@ -907,52 +992,52 @@ async def test_collector_openai_stream_chunk_verification():
907992async def test_collector_openai_multiple_concurrent_streams ():
908993 """Test streaming collector with multiple concurrent OpenAI streams"""
909994 collector = Collector (name = "openai-concurrent-streams" )
910-
995+
911996 async def stream_and_collect (prompt : str ) -> tuple [list [str ], str ]:
912997 """Helper to run a stream and collect chunks"""
913998 chunks = []
914999 stream = b .stream .TestOpenAIGPT4oMini (
9151000 prompt , baml_options = {"collector" : collector }
9161001 )
917-
1002+
9181003 async for chunk in stream :
9191004 chunks .append (chunk )
920-
1005+
9211006 final = await stream .get_final_response ()
9221007 return chunks , final
923-
1008+
9241009 # Run multiple streams concurrently
9251010 results = await asyncio .gather (
9261011 stream_and_collect ("Say hello" ),
9271012 stream_and_collect ("Say goodbye" ),
928- stream_and_collect ("Count to 3" )
1013+ stream_and_collect ("Count to 3" ),
9291014 )
930-
1015+
9311016 # Verify we got results from all streams
9321017 assert len (results ) == 3
9331018 for chunks , final in results :
9341019 assert len (chunks ) > 0
9351020 assert len (final ) > 0
936-
1021+
9371022 # Verify collector captured all streams
9381023 logs = collector .logs
9391024 assert len (logs ) == 3
940-
1025+
9411026 # Verify each log is properly formed
9421027 for i , log in enumerate (logs ):
9431028 assert log .function_name == "TestOpenAIGPT4oMini"
9441029 assert log .log_type == "stream"
9451030 assert log .timing .duration_ms is not None and log .timing .duration_ms > 0
9461031 assert log .usage .input_tokens is not None and log .usage .input_tokens > 0
9471032 assert log .usage .output_tokens is not None and log .usage .output_tokens > 0
948-
1033+
9491034 # Verify streaming request
9501035 call = log .calls [0 ]
9511036 assert call .provider == "openai"
9521037 assert call .http_request
9531038 request_body = call .http_request .body .json ()
9541039 assert request_body .get ("stream" ) is True
955-
1040+
9561041 # Verify total usage is sum of all streams
9571042 total_input = sum (log .usage .input_tokens or 0 for log in logs )
9581043 total_output = sum (log .usage .output_tokens or 0 for log in logs )
@@ -964,56 +1049,58 @@ async def stream_and_collect(prompt: str) -> tuple[list[str], str]:
9641049async def test_collector_openai_stream_usage_accumulation ():
9651050 """Test that streaming collector properly accumulates usage across multiple calls"""
9661051 collector = Collector (name = "openai-stream-usage" )
967-
1052+
9681053 # First streaming call
9691054 stream1 = b .stream .TestOpenAIGPT4oMini (
9701055 "First stream" , baml_options = {"collector" : collector }
9711056 )
9721057 async for _ in stream1 :
9731058 pass
9741059 await stream1 .get_final_response ()
975-
1060+
9761061 # Capture usage after first stream
9771062 first_usage = collector .usage
9781063 first_input = first_usage .input_tokens
9791064 first_output = first_usage .output_tokens
9801065 assert first_input is not None and first_input > 0
9811066 assert first_output is not None and first_output > 0
982-
1067+
9831068 # Second streaming call
9841069 stream2 = b .stream .TestOpenAIGPT4oMini (
9851070 "Second stream with more content" , baml_options = {"collector" : collector }
9861071 )
9871072 async for _ in stream2 :
9881073 pass
9891074 await stream2 .get_final_response ()
990-
1075+
9911076 # Verify usage accumulated
9921077 assert collector .usage .input_tokens is not None
9931078 assert collector .usage .output_tokens is not None
9941079 assert collector .usage .input_tokens > first_input
9951080 assert collector .usage .output_tokens > first_output
9961081 assert collector .usage .input_tokens > 0
9971082 assert collector .usage .output_tokens > 0
998-
1083+
9991084 # Non-streaming call
1000- await b .TestOpenAIGPT4oMini ("Non-streaming call" , baml_options = {"collector" : collector })
1001-
1085+ await b .TestOpenAIGPT4oMini (
1086+ "Non-streaming call" , baml_options = {"collector" : collector }
1087+ )
1088+
10021089 # Verify we have 3 logs total
10031090 logs = collector .logs
10041091 assert len (logs ) == 3
1005-
1092+
10061093 # Verify total usage matches sum of individual calls
10071094 total_input = sum (log .usage .input_tokens or 0 for log in logs )
10081095 total_output = sum (log .usage .output_tokens or 0 for log in logs )
10091096 assert collector .usage .input_tokens == total_input
10101097 assert collector .usage .output_tokens == total_output
1011-
1098+
10121099 # Verify first two are streaming, last is not
10131100 for i in range (2 ):
10141101 request_body = logs [i ].calls [0 ].http_request .body .json ()
10151102 assert request_body .get ("stream" ) is True
1016-
1103+
10171104 # Last call should not be streaming
10181105 last_request_body = logs [2 ].calls [0 ].http_request .body .json ()
10191106 assert last_request_body .get ("stream" ) is not True
0 commit comments