1919import sys
2020import threading
2121from contextlib import contextmanager
22- from dataclasses import asdict
2322from typing import Any
2423from unittest import TestCase
2524from unittest .mock import MagicMock , patch
@@ -76,6 +75,24 @@ def test_fsspec_entry_point_no_fsspec(self):
7675 role = "user" ,
7776 parts = [types .Text (content = "What is the capital of France?" )],
7877 ),
78+ types .InputMessage (
79+ role = "assistant" ,
80+ parts = [
81+ types .ToolCall (
82+ id = "get_capital_0" ,
83+ name = "get_capital" ,
84+ arguments = {"city" : "Paris" },
85+ )
86+ ],
87+ ),
88+ types .InputMessage (
89+ role = "user" ,
90+ parts = [
91+ types .ToolCallResponse (
92+ id = "get_capital_0" , response = {"capital" : "Paris" }
93+ )
94+ ],
95+ ),
7996]
8097FAKE_OUTPUTS = [
8198 types .OutputMessage (
@@ -197,6 +214,44 @@ def test_failed_upload_logs(self):
197214
198215 self .assertIn ("fsspec uploader failed" , logs .output [0 ])
199216
217+ def test_invalid_upload_format (self ):
218+ with self .assertRaisesRegex (ValueError , "Invalid upload_format" ):
219+ FsspecUploadCompletionHook (
220+ base_path = BASE_PATH , upload_format = "invalid"
221+ )
222+
223+ def test_parse_upload_format_envvar (self ):
224+ for envvar_value , expect in (
225+ ("" , "json" ),
226+ ("json" , "json" ),
227+ ("invalid" , "json" ),
228+ ("jsonl" , "jsonl" ),
229+ ("jSoNl" , "jsonl" ),
230+ ):
231+ with patch .dict (
232+ "os.environ" ,
233+ {"OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT" : envvar_value },
234+ clear = True ,
235+ ):
236+ hook = FsspecUploadCompletionHook (base_path = BASE_PATH )
237+ self .addCleanup (hook .shutdown )
238+ self .assertEqual (
239+ hook ._format ,
240+ expect ,
241+ f"expected upload format { expect = } with { envvar_value = } got { hook ._format } " ,
242+ )
243+
244+ with patch .dict (
245+ "os.environ" ,
246+ {"OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT" : "json" },
247+ clear = True ,
248+ ):
249+ hook = FsspecUploadCompletionHook (
250+ base_path = BASE_PATH , upload_format = "jsonl"
251+ )
252+ self .addCleanup (hook .shutdown )
253+ self .assertEqual (hook ._format , "jsonl" )
254+
200255 def test_upload_after_shutdown_logs (self ):
201256 self .hook .shutdown ()
202257 with self .assertLogs (level = logging .INFO ) as logs :
@@ -212,25 +267,15 @@ def test_upload_after_shutdown_logs(self):
212267 )
213268
214269
215- class FsspecUploaderTest (TestCase ):
216- def test_upload (self ):
217- FsspecUploadCompletionHook ._do_upload (
218- "memory://my_path" ,
219- lambda : [asdict (fake_input ) for fake_input in FAKE_INPUTS ],
220- )
221-
222- with fsspec .open ("memory://my_path" , "r" ) as file :
223- self .assertEqual (
224- file .read (),
225- '[{"role":"user","parts":[{"content":"What is the capital of France?","type":"text"}]}]' ,
226- )
227-
228-
229270class TestFsspecUploadCompletionHookIntegration (TestBase ):
230271 def setUp (self ):
231272 super ().setUp ()
232273 self .hook = FsspecUploadCompletionHook (base_path = BASE_PATH )
233274
275+ def create_hook (self ) -> FsspecUploadCompletionHook :
276+ self .hook = FsspecUploadCompletionHook (base_path = BASE_PATH )
277+ return self .hook
278+
234279 def tearDown (self ):
235280 super ().tearDown ()
236281 self .hook .shutdown ()
@@ -271,15 +316,15 @@ def test_upload_completions(self):
271316
272317 self .assert_fsspec_equal (
273318 span .attributes ["gen_ai.input.messages_ref" ],
274- '[{"role":"user","parts":[{"content":"What is the capital of France?","type":"text"}]}] ' ,
319+ '[{"role":"user","parts":[{"content":"What is the capital of France?","type":"text"}]},{"role":"assistant","parts":[{"arguments":{"city":"Paris"},"name":"get_capital","id":"get_capital_0","type":"tool_call"}]},{"role":"user","parts":[{"response":{"capital":"Paris"},"id":"get_capital_0","type":"tool_call_response"}]}] \n ' ,
275320 )
276321 self .assert_fsspec_equal (
277322 span .attributes ["gen_ai.output.messages_ref" ],
278- '[{"role":"assistant","parts":[{"content":"Paris","type":"text"}],"finish_reason":"stop"}]' ,
323+ '[{"role":"assistant","parts":[{"content":"Paris","type":"text"}],"finish_reason":"stop"}]\n ' ,
279324 )
280325 self .assert_fsspec_equal (
281326 span .attributes ["gen_ai.system_instructions_ref" ],
282- '[{"content":"You are a helpful assistant.","type":"text"}]' ,
327+ '[{"content":"You are a helpful assistant.","type":"text"}]\n ' ,
283328 )
284329
285330 def test_stamps_empty_log (self ):
@@ -316,5 +361,59 @@ def test_upload_bytes(self) -> None:
316361
317362 self .assert_fsspec_equal (
318363 log_record .attributes ["gen_ai.input.messages_ref" ],
319- '[{"role":"user","parts":[{"content":"What is the capital of France?","type":"text"},{"type":"generic_bytes","bytes":"aGVsbG8="}]}]' ,
364+ '[{"role":"user","parts":[{"content":"What is the capital of France?","type":"text"},{"type":"generic_bytes","bytes":"aGVsbG8="}]}]\n ' ,
365+ )
366+
367+ def test_upload_json (self ) -> None :
368+ hook = FsspecUploadCompletionHook (
369+ base_path = BASE_PATH , upload_format = "json"
370+ )
371+ self .addCleanup (hook .shutdown )
372+ log_record = LogRecord ()
373+
374+ hook .on_completion (
375+ inputs = FAKE_INPUTS ,
376+ outputs = FAKE_OUTPUTS ,
377+ system_instruction = FAKE_SYSTEM_INSTRUCTION ,
378+ log_record = log_record ,
379+ )
380+ hook .shutdown ()
381+
382+ ref_uri : str = log_record .attributes ["gen_ai.input.messages_ref" ]
383+ self .assertTrue (
384+ ref_uri .endswith (".json" ), f"{ ref_uri = } does not end with .json"
385+ )
386+
387+ self .assert_fsspec_equal (
388+ ref_uri ,
389+ '[{"role":"user","parts":[{"content":"What is the capital of France?","type":"text"}]},{"role":"assistant","parts":[{"arguments":{"city":"Paris"},"name":"get_capital","id":"get_capital_0","type":"tool_call"}]},{"role":"user","parts":[{"response":{"capital":"Paris"},"id":"get_capital_0","type":"tool_call_response"}]}]\n ' ,
390+ )
391+
392+ def test_upload_jsonlines (self ) -> None :
393+ hook = FsspecUploadCompletionHook (
394+ base_path = BASE_PATH , upload_format = "jsonl"
395+ )
396+ self .addCleanup (hook .shutdown )
397+ log_record = LogRecord ()
398+
399+ hook .on_completion (
400+ inputs = FAKE_INPUTS ,
401+ outputs = FAKE_OUTPUTS ,
402+ system_instruction = FAKE_SYSTEM_INSTRUCTION ,
403+ log_record = log_record ,
404+ )
405+ hook .shutdown ()
406+
407+ ref_uri : str = log_record .attributes ["gen_ai.input.messages_ref" ]
408+ self .assertTrue (
409+ ref_uri .endswith (".jsonl" ), f"{ ref_uri = } does not end with .jsonl"
410+ )
411+
412+ self .assert_fsspec_equal (
413+ ref_uri ,
414+ """\
415+ {"role":"user","parts":[{"content":"What is the capital of France?","type":"text"}],"index":0}
416+ {"role":"assistant","parts":[{"arguments":{"city":"Paris"},"name":"get_capital","id":"get_capital_0","type":"tool_call"}],"index":1}
417+ {"role":"user","parts":[{"response":{"capital":"Paris"},"id":"get_capital_0","type":"tool_call_response"}],"index":2}
418+ """ ,
320419 )
0 commit comments