Skip to content

Commit bb4ec25

Browse files
committed
cleanup
1 parent 384eff0 commit bb4ec25

3 files changed

Lines changed: 54 additions & 14 deletions

File tree

py/src/braintrust/integrations/agno/tracing.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from inspect import isawaitable
33
from typing import Any
44

5-
from braintrust.integrations.utils import _try_to_dict as _shared_try_to_dict
5+
from braintrust.integrations.utils import _try_to_dict
66
from braintrust.logger import start_span
77
from braintrust.span_types import SpanTypeAttribute
88
from braintrust.util import is_numeric
@@ -25,18 +25,6 @@ def get_args_kwargs(args: list[str], kwargs: dict[str, Any], keys: list[str]):
2525
return {k: args[i] if args else kwargs.get(k) for i, k in enumerate(keys)}, omit(kwargs, keys)
2626

2727

28-
def _try_to_dict(obj: Any) -> Any:
29-
"""Convert object to dict, handling different object types like OpenAI wrapper."""
30-
result = _shared_try_to_dict(obj)
31-
# Agno also falls back to __dict__ copy
32-
if result is obj and not isinstance(result, dict) and hasattr(obj, "__dict__"):
33-
try:
34-
return obj.__dict__.copy()
35-
except Exception:
36-
pass
37-
return result
38-
39-
4028
def is_sync_iterator(result: Any) -> bool:
4129
return hasattr(result, "__iter__") and hasattr(result, "__next__")
4230

py/src/braintrust/integrations/test_utils.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ def test_is_supported_metric_value_excludes_booleans():
3535
assert not _is_supported_metric_value("1")
3636

3737

38+
def test_try_to_dict_uses_pydantic_model_dump_for_basemodel_instances():
39+
pydantic = pytest.importorskip("pydantic")
40+
41+
class Usage(pydantic.BaseModel):
42+
tokens: int
43+
cached_tokens: int
44+
45+
result = _try_to_dict(Usage(tokens=3, cached_tokens=1))
46+
47+
assert result == {"tokens": 3, "cached_tokens": 1}
48+
49+
3850
def test_try_to_dict_uses_to_dict_when_available():
3951
class ToDictOnly:
4052
__slots__ = ("_payload",)
@@ -50,6 +62,31 @@ def to_dict(self):
5062
assert result == {"tokens": 3}
5163

5264

65+
def test_try_to_dict_falls_back_from_model_dump_python_to_bare_model_dump():
66+
class BareModelDumpOnly:
67+
def model_dump(self, mode=None):
68+
if mode == "python":
69+
raise TypeError("mode not supported")
70+
return {"tokens": 3}
71+
72+
result = _try_to_dict(BareModelDumpOnly())
73+
74+
assert result == {"tokens": 3}
75+
76+
77+
def test_try_to_dict_continues_past_non_dict_converter_results():
78+
class MixedConverters:
79+
def model_dump(self, mode=None):
80+
return [mode]
81+
82+
def to_dict(self):
83+
return {"tokens": 3}
84+
85+
result = _try_to_dict(MixedConverters())
86+
87+
assert result == {"tokens": 3}
88+
89+
5390
def test_try_to_dict_falls_back_to_vars_for_plain_objects():
5491
class PlainObject:
5592
def __init__(self):
@@ -130,6 +167,16 @@ def test_convert_data_url_to_attachment_preserves_invalid_base64():
130167
assert converted == data_url
131168

132169

170+
def test_convert_data_url_to_attachment_uses_file_prefix_for_non_image_mime_types():
171+
data_url = "data:application/pdf;base64,aGVsbG8="
172+
173+
attachment = _convert_data_url_to_attachment(data_url)
174+
175+
assert isinstance(attachment, Attachment)
176+
assert attachment.reference["content_type"] == "application/pdf"
177+
assert attachment.reference["filename"] == "file.pdf"
178+
179+
133180
def test_convert_data_url_to_attachment_preserves_non_data_urls():
134181
value = "https://example.com/image.png"
135182

py/src/braintrust/integrations/utils.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ def _try_to_dict(obj: Any) -> dict[str, Any] | Any:
3636
5. ``vars(obj)`` (plain Python attribute bags)
3737
6. returns *obj* unchanged
3838
39+
Only dict-like conversion results are accepted; non-dict results are
40+
ignored so later fallbacks still run.
41+
3942
Pydantic serializer warnings (common with generic/discriminated-union
4043
models such as OpenAI's ``ParsedResponse[T]``) are suppressed.
4144
"""
@@ -68,9 +71,11 @@ def _call_model_dump() -> Any:
6871

6972
for converter in converters:
7073
try:
71-
return converter()
74+
result = converter()
7275
except Exception:
7376
continue
77+
if isinstance(result, dict):
78+
return result
7479

7580
return obj
7681

0 commit comments

Comments
 (0)