19
19
import sys
20
20
import threading
21
21
from contextlib import contextmanager
22
- from dataclasses import asdict
23
22
from typing import Any
24
23
from unittest import TestCase
25
24
from unittest .mock import MagicMock , patch
@@ -76,6 +75,24 @@ def test_fsspec_entry_point_no_fsspec(self):
76
75
role = "user" ,
77
76
parts = [types .Text (content = "What is the capital of France?" )],
78
77
),
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
+ ),
79
96
]
80
97
FAKE_OUTPUTS = [
81
98
types .OutputMessage (
@@ -197,6 +214,44 @@ def test_failed_upload_logs(self):
197
214
198
215
self .assertIn ("fsspec uploader failed" , logs .output [0 ])
199
216
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
+
200
255
def test_upload_after_shutdown_logs (self ):
201
256
self .hook .shutdown ()
202
257
with self .assertLogs (level = logging .INFO ) as logs :
@@ -212,25 +267,15 @@ def test_upload_after_shutdown_logs(self):
212
267
)
213
268
214
269
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
-
229
270
class TestFsspecUploadCompletionHookIntegration (TestBase ):
230
271
def setUp (self ):
231
272
super ().setUp ()
232
273
self .hook = FsspecUploadCompletionHook (base_path = BASE_PATH )
233
274
275
+ def create_hook (self ) -> FsspecUploadCompletionHook :
276
+ self .hook = FsspecUploadCompletionHook (base_path = BASE_PATH )
277
+ return self .hook
278
+
234
279
def tearDown (self ):
235
280
super ().tearDown ()
236
281
self .hook .shutdown ()
@@ -271,15 +316,15 @@ def test_upload_completions(self):
271
316
272
317
self .assert_fsspec_equal (
273
318
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 ' ,
275
320
)
276
321
self .assert_fsspec_equal (
277
322
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 ' ,
279
324
)
280
325
self .assert_fsspec_equal (
281
326
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 ' ,
283
328
)
284
329
285
330
def test_stamps_empty_log (self ):
@@ -316,5 +361,59 @@ def test_upload_bytes(self) -> None:
316
361
317
362
self .assert_fsspec_equal (
318
363
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
+ """ ,
320
419
)
0 commit comments