Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions py/src/braintrust/span_identifier_v4.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ def _try_make_hex_trace_id(s):
if isinstance(s, str) and len(s) == 32: # 32 hex chars = 16 bytes
ret = bytes.fromhex(s)
assert len(ret) == 16
return ret, True
if ret.hex() == s:
return ret, True
except Exception:
pass
return None, False
Expand All @@ -33,7 +34,8 @@ def _try_make_hex_span_id(s):
if isinstance(s, str) and len(s) == 16: # 16 hex chars = 8 bytes
ret = bytes.fromhex(s)
assert len(ret) == 8
return ret, True
if ret.hex() == s:
return ret, True
except Exception:
pass
return None, False
Expand Down
34 changes: 34 additions & 0 deletions py/src/braintrust/test_span_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,40 @@ def test_otel_ids_fail_roundtrip(self):
class TestSpanComponentsV4:
"""Test SpanComponentsV4 functionality and OTEL compatibility."""

@pytest.mark.parametrize(
("span_id", "root_span_id"),
[
("ABCDEF1234567890", "abcdef0123456789abcdef0123456789"),
("abcdef1234567890", "ABCDEF0123456789ABCDEF0123456789"),
("AbCdEf1234567890", "AbCdEf0123456789AbCdEf0123456789"),
("abcdef1234567890", "abcdef0123456789abcdef0123456789"),
("0123456789012345", "01234567890123456789012345678901"),
("custom-span", "custom-root"),
("72EAC0DD-0775-4D40-BAC8-1DFC8CC14059", "72EAC0DD-0775-4D40-BAC8-1DFC8CC14059"),
],
)
def test_exported_custom_ids_preserve_parent_link(self, span_id, root_span_id, with_memory_logger):
from braintrust.test_helpers import init_test_logger, preserve_env_vars

with preserve_env_vars("BRAINTRUST_LEGACY_IDS"):
os.environ.pop("BRAINTRUST_LEGACY_IDS", None)
log = init_test_logger("custom-id-roundtrip")
with log.start_span(name="parent", span_id=span_id, root_span_id=root_span_id) as parent:
exported = parent.export()
with log.start_span(name="child", parent=exported):
pass

rows = with_memory_logger.pop()
parent_row = next(row for row in rows if row["span_attributes"]["name"] == "parent")
child_row = next(row for row in rows if row["span_attributes"]["name"] == "child")
assert parent_row["span_id"] == span_id
assert parent_row["root_span_id"] == root_span_id
assert child_row["span_parents"] == [parent_row["span_id"]]
assert child_row["root_span_id"] == parent_row["root_span_id"]
decoded = SpanComponentsV4.from_str(exported)
assert decoded.span_id == span_id
assert decoded.root_span_id == root_span_id

def test_otel_hex_strings_preserved(self):
"""Test that V4 preserves OTEL hex strings exactly."""
otel_gen = OTELIDGenerator()
Expand Down