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