Skip to content

Commit 9fab62b

Browse files
authored
gen ai uploader encode bytes as base64 (#3771)
1 parent 4fb00c9 commit 9fab62b

File tree

2 files changed

+50
-19
lines changed

2 files changed

+50
-19
lines changed

util/opentelemetry-util-genai/src/opentelemetry/util/genai/_fsspec_upload/fsspec_hook.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import logging
2020
import posixpath
2121
import threading
22+
from base64 import b64encode
2223
from concurrent.futures import Future, ThreadPoolExecutor
2324
from dataclasses import asdict, dataclass
2425
from functools import partial
@@ -151,7 +152,12 @@ def _do_upload(
151152
path: str, json_encodeable: Callable[[], JsonEncodeable]
152153
) -> None:
153154
with fsspec_open(path, "w") as file:
154-
json.dump(json_encodeable(), file, separators=(",", ":"))
155+
json.dump(
156+
json_encodeable(),
157+
file,
158+
separators=(",", ":"),
159+
cls=Base64JsonEncoder,
160+
)
155161

156162
def upload(
157163
self,
@@ -206,3 +212,10 @@ def to_dict(
206212
def shutdown(self) -> None:
207213
# TODO: support timeout
208214
self._executor.shutdown()
215+
216+
217+
class Base64JsonEncoder(json.JSONEncoder):
218+
def default(self, o: Any) -> Any:
219+
if isinstance(o, bytes):
220+
return b64encode(o).decode()
221+
return super().default(o)

util/opentelemetry-util-genai/tests/test_fsspec_upload.py

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515

1616
# pylint: disable=import-outside-toplevel,no-name-in-module
17-
1817
import importlib
1918
import logging
2019
import sys
@@ -200,26 +199,31 @@ def test_upload(self):
200199

201200

202201
class TestFsspecUploadHookIntegration(TestBase):
202+
def setUp(self):
203+
super().setUp()
204+
self.hook = FsspecUploadHook(base_path=BASE_PATH)
205+
206+
def tearDown(self):
207+
super().tearDown()
208+
self.hook.shutdown()
209+
203210
def assert_fsspec_equal(self, path: str, value: str) -> None:
204211
with fsspec.open(path, "r") as file:
205212
self.assertEqual(file.read(), value)
206213

207214
def test_upload_completions(self):
208-
hook = FsspecUploadHook(
209-
base_path=BASE_PATH,
210-
)
211215
tracer = self.tracer_provider.get_tracer(__name__)
212216
log_record = LogRecord()
213217

214218
with tracer.start_as_current_span("chat mymodel") as span:
215-
hook.upload(
219+
self.hook.upload(
216220
inputs=FAKE_INPUTS,
217221
outputs=FAKE_OUTPUTS,
218222
system_instruction=FAKE_SYSTEM_INSTRUCTION,
219223
span=span,
220224
log_record=log_record,
221225
)
222-
hook.shutdown()
226+
self.hook.shutdown()
223227

224228
finished_spans = self.get_finished_spans()
225229
self.assertEqual(len(finished_spans), 1)
@@ -250,25 +254,39 @@ def test_upload_completions(self):
250254
'[{"content":"You are a helpful assistant.","type":"text"}]',
251255
)
252256

253-
@staticmethod
254-
def upload_with_log(log_record: LogRecord):
255-
hook = FsspecUploadHook(
256-
base_path=BASE_PATH,
257-
)
258-
259-
hook.upload(
257+
def test_stamps_empty_log(self):
258+
log_record = LogRecord()
259+
self.hook.upload(
260260
inputs=FAKE_INPUTS,
261261
outputs=FAKE_OUTPUTS,
262262
system_instruction=FAKE_SYSTEM_INSTRUCTION,
263263
log_record=log_record,
264264
)
265-
hook.shutdown()
266-
267-
def test_stamps_empty_log(self):
268-
log_record = LogRecord()
269-
self.upload_with_log(log_record)
270265

271266
# stamp on both body and attributes
272267
self.assertIn("gen_ai.input.messages_ref", log_record.attributes)
273268
self.assertIn("gen_ai.output.messages_ref", log_record.attributes)
274269
self.assertIn("gen_ai.system_instructions_ref", log_record.attributes)
270+
271+
def test_upload_bytes(self) -> None:
272+
log_record = LogRecord()
273+
self.hook.upload(
274+
inputs=[
275+
types.InputMessage(
276+
role="user",
277+
parts=[
278+
types.Text(content="What is the capital of France?"),
279+
{"type": "generic_bytes", "bytes": b"hello"},
280+
],
281+
)
282+
],
283+
outputs=FAKE_OUTPUTS,
284+
system_instruction=FAKE_SYSTEM_INSTRUCTION,
285+
log_record=log_record,
286+
)
287+
self.hook.shutdown()
288+
289+
self.assert_fsspec_equal(
290+
log_record.attributes["gen_ai.input.messages_ref"],
291+
'[{"role":"user","parts":[{"content":"What is the capital of France?","type":"text"},{"type":"generic_bytes","bytes":"aGVsbG8="}]}]',
292+
)

0 commit comments

Comments
 (0)