Skip to content

Commit 504bcfc

Browse files
fix(otel): relax semantic-conventions upper bound (#605)
The `<0.61` cap pinned the whole OpenTelemetry stack for downstream consumers, not just semconv. OTel ships semconv and the instrumentation packages as a lockstep set pinned with `==`, and semconv itself requires an exact opentelemetry-api version, so the ceiling held consumers' api and sdk at the 1.39.x generation. Replace the ceiling with a floor on semantic-conventions and instrumentation-httpx. All 39 semconv symbols the SDK references resolve to identical values in 0.60b1 and 0.65b0, so no span attribute names change. Lifting the cap surfaced a latent bug. W3C trace-context level 2 adds the random-trace-id flag (0x02), so a sampled span serializes as "-03" from opentelemetry-api 1.44.0. TraceparentInjectionHook compared the flags byte with `endswith("-01")`, read that as unsampled, and discarded the propagated context, starting a new trace on every workflow /execute call. Test the sampled bit as a bitmask instead. Verified on semconv 0.60b1 / api 1.39.1 and semconv 0.65b0 / api 1.44.0. Generated by Mistral Vibe. Co-authored-by: Mistral Vibe <[email protected]>
1 parent 1cc2d2c commit 504bcfc

3 files changed

Lines changed: 17 additions & 4 deletions

File tree

‎pyproject.toml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ dependencies = [
1212
"python-dateutil >=2.8.2",
1313
"typing-inspection >=0.4.0",
1414
"opentelemetry-api (>=1.33.1,<2.0.0)",
15-
"opentelemetry-semantic-conventions (>=0.60b1,<0.61)",
15+
"opentelemetry-semantic-conventions (>=0.60b1)",
1616
"jsonpath-python >=1.0.6", # required for speakeasy generated path with pagination
1717
]
1818

@@ -68,7 +68,7 @@ dev = [
6868
"pyyaml>=6.0.2,<7",
6969
"mypy==1.15.0",
7070
"msgpack>=1.1.0,<2.0.0",
71-
"opentelemetry-instrumentation-httpx (>=0.60b1,<0.61)",
71+
"opentelemetry-instrumentation-httpx (>=0.60b1)",
7272
"opentelemetry-sdk (>=1.33.1,<2.0.0)",
7373
"opentelemetry-exporter-otlp-proto-http (>=1.33.1,<2.0.0)",
7474
"pylint==3.2.3",

‎src/mistralai/client/_hooks/traceparent.py‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@
1212
"execute_workflow_registration_v1_workflows_registrations__workflow_registration_id__execute_post",
1313
}
1414

15+
_SAMPLED_FLAG = 0x01
16+
17+
18+
# https://www.w3.org/TR/trace-context/#traceparent-header
19+
def _is_sampled(traceparent: str) -> bool:
20+
parts = traceparent.split("-")
21+
if len(parts) != 4:
22+
return False
23+
try:
24+
return bool(int(parts[3], 16) & _SAMPLED_FLAG)
25+
except ValueError:
26+
return False
27+
1528

1629
class TraceparentInjectionHook(BeforeRequestHook):
1730
"""Inject a sampled traceparent on /execute requests so worker traces are always recorded."""
@@ -29,7 +42,7 @@ def before_request(
2942
carrier: Dict[str, str] = {}
3043
inject(carrier)
3144
traceparent = carrier.get("traceparent", "")
32-
if not traceparent.endswith("-01"):
45+
if not _is_sampled(traceparent):
3346
trace_id = random.getrandbits(128)
3447
span_id = random.getrandbits(64)
3548
traceparent = f"00-{trace_id:032x}-{span_id:016x}-01"

‎src/mistralai/extra/tests/test_traceparent_hook.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def test_propagates_sampled_active_span(self):
8686

8787
assert isinstance(result, httpx.Request)
8888
injected = result.headers["traceparent"]
89-
self.assertTrue(injected.endswith("-01"))
89+
self.assertTrue(int(injected.split("-")[3], 16) & 0x01)
9090
trace_id_hex = f"{span.get_span_context().trace_id:032x}"
9191
self.assertIn(trace_id_hex, injected)
9292

0 commit comments

Comments
 (0)