trodo-python 2.4.0__py3-none-any.whl → 2.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trodo/otel/auto_instrument.py +54 -12
- trodo/otel/helpers.py +9 -4
- {trodo_python-2.4.0.dist-info → trodo_python-2.4.1.dist-info}/METADATA +1 -1
- {trodo_python-2.4.0.dist-info → trodo_python-2.4.1.dist-info}/RECORD +6 -6
- {trodo_python-2.4.0.dist-info → trodo_python-2.4.1.dist-info}/WHEEL +0 -0
- {trodo_python-2.4.0.dist-info → trodo_python-2.4.1.dist-info}/top_level.txt +0 -0
trodo/otel/auto_instrument.py
CHANGED
|
@@ -34,11 +34,17 @@ def _infer_kind(attrs: Dict[str, Any]) -> str:
|
|
|
34
34
|
return "generic"
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
_ATTR_TRODO_RUN_ID = "trodo.run_id"
|
|
38
|
+
_ATTR_TRODO_PARENT_SPAN_ID = "trodo.parent_span_id"
|
|
39
|
+
|
|
40
|
+
|
|
37
41
|
def otel_span_to_trodo_span(otel_span: Any) -> Optional[TrodoSpan]:
|
|
38
|
-
"""Translate an OTel ReadableSpan to our TrodoSpan using GenAI semconv.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
"""Translate an OTel ReadableSpan to our TrodoSpan using GenAI semconv.
|
|
43
|
+
|
|
44
|
+
Recovers the active run from on_start-stamped attributes when contextvars
|
|
45
|
+
have been clobbered by httpx/asyncio context loss at span end. Mirrors
|
|
46
|
+
the trodo-node 2.4.3 bridge behaviour.
|
|
47
|
+
"""
|
|
42
48
|
try:
|
|
43
49
|
span_ctx = (
|
|
44
50
|
otel_span.get_span_context()
|
|
@@ -53,15 +59,36 @@ def otel_span_to_trodo_span(otel_span: Any) -> Optional[TrodoSpan]:
|
|
|
53
59
|
return None
|
|
54
60
|
span_id = f"{span_id_int:016x}" if isinstance(span_id_int, int) else str(span_id_int)
|
|
55
61
|
|
|
62
|
+
attrs = dict(getattr(otel_span, "attributes", {}) or {})
|
|
63
|
+
|
|
64
|
+
# Prefer on_start-stamped run/parent so async-context loss can't drop
|
|
65
|
+
# auto-instrumented LLM spans.
|
|
66
|
+
stamped_run_id = attrs.get(_ATTR_TRODO_RUN_ID)
|
|
67
|
+
stamped_parent_span_id = attrs.get(_ATTR_TRODO_PARENT_SPAN_ID)
|
|
68
|
+
|
|
69
|
+
ctx = get_active_context()
|
|
70
|
+
run_id = stamped_run_id if isinstance(stamped_run_id, str) else (ctx.run_id if ctx else None)
|
|
71
|
+
if not run_id:
|
|
72
|
+
return None # emitted outside any wrap_agent — drop
|
|
73
|
+
|
|
56
74
|
parent = getattr(otel_span, "parent", None)
|
|
57
|
-
|
|
75
|
+
otel_parent_span_id: Optional[str] = None
|
|
58
76
|
if parent is not None:
|
|
59
77
|
pid = getattr(parent, "span_id", None)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
78
|
+
otel_parent_span_id = (
|
|
79
|
+
f"{pid:016x}" if isinstance(pid, int) else str(pid) if pid else None
|
|
80
|
+
)
|
|
63
81
|
|
|
64
|
-
|
|
82
|
+
if isinstance(stamped_parent_span_id, str):
|
|
83
|
+
parent_span_id: Optional[str] = stamped_parent_span_id
|
|
84
|
+
elif otel_parent_span_id:
|
|
85
|
+
parent_span_id = otel_parent_span_id
|
|
86
|
+
else:
|
|
87
|
+
parent_span_id = ctx.span_id if ctx else None
|
|
88
|
+
|
|
89
|
+
# Strip the internal attrs so they don't leak into the persisted span.
|
|
90
|
+
attrs.pop(_ATTR_TRODO_RUN_ID, None)
|
|
91
|
+
attrs.pop(_ATTR_TRODO_PARENT_SPAN_ID, None)
|
|
65
92
|
kind = _infer_kind(attrs)
|
|
66
93
|
|
|
67
94
|
start_time = getattr(otel_span, "start_time", None)
|
|
@@ -70,7 +97,9 @@ def otel_span_to_trodo_span(otel_span: Any) -> Optional[TrodoSpan]:
|
|
|
70
97
|
ended_at = _hr_to_iso(end_time)
|
|
71
98
|
duration_ms = None
|
|
72
99
|
if start_time and end_time:
|
|
73
|
-
|
|
100
|
+
# round-half-to-even semantics are fine here; the constraint is
|
|
101
|
+
# "integer ms" matching the agent_spans.duration_ms column.
|
|
102
|
+
duration_ms = max(0, round((end_time - start_time) / 1e6))
|
|
74
103
|
|
|
75
104
|
status = getattr(otel_span, "status", None)
|
|
76
105
|
status_code = getattr(status, "status_code", None)
|
|
@@ -104,7 +133,7 @@ def otel_span_to_trodo_span(otel_span: Any) -> Optional[TrodoSpan]:
|
|
|
104
133
|
|
|
105
134
|
return TrodoSpan(
|
|
106
135
|
span_id=span_id,
|
|
107
|
-
run_id=
|
|
136
|
+
run_id=run_id,
|
|
108
137
|
parent_span_id=parent_span_id,
|
|
109
138
|
kind=kind,
|
|
110
139
|
name=getattr(otel_span, "name", kind),
|
|
@@ -131,7 +160,20 @@ class _OtelAdapter:
|
|
|
131
160
|
self._processor = processor
|
|
132
161
|
|
|
133
162
|
def on_start(self, span: Any, parent_context: Any = None) -> None:
|
|
134
|
-
|
|
163
|
+
# Stamp active run/parent ids while contextvars are still alive.
|
|
164
|
+
# See otel_span_to_trodo_span() docstring for the failure mode this
|
|
165
|
+
# guards against (async-context loss across httpx await boundaries).
|
|
166
|
+
ctx = get_active_context()
|
|
167
|
+
if ctx is None:
|
|
168
|
+
return
|
|
169
|
+
try:
|
|
170
|
+
set_attr = getattr(span, "set_attribute", None)
|
|
171
|
+
if callable(set_attr):
|
|
172
|
+
set_attr(_ATTR_TRODO_RUN_ID, ctx.run_id)
|
|
173
|
+
if ctx.span_id:
|
|
174
|
+
set_attr(_ATTR_TRODO_PARENT_SPAN_ID, ctx.span_id)
|
|
175
|
+
except Exception:
|
|
176
|
+
pass # never break user code
|
|
135
177
|
|
|
136
178
|
def on_end(self, span: Any) -> None:
|
|
137
179
|
trodo_span = otel_span_to_trodo_span(span)
|
trodo/otel/helpers.py
CHANGED
|
@@ -320,14 +320,19 @@ def track_mcp(
|
|
|
320
320
|
span_id = str(_uuid.uuid4())
|
|
321
321
|
conv_id = session_id or str(_uuid.uuid4())
|
|
322
322
|
|
|
323
|
+
# Coerce duration to a non-negative integer. agent_spans.duration_ms is an
|
|
324
|
+
# integer column and callers occasionally pass floats from time.perf_counter()
|
|
325
|
+
# deltas — without this, fractional values 500 the ingest.
|
|
326
|
+
if isinstance(duration_ms, (int, float)) and duration_ms > 0:
|
|
327
|
+
duration_ms = int(round(duration_ms))
|
|
328
|
+
else:
|
|
329
|
+
duration_ms = 0
|
|
330
|
+
|
|
323
331
|
now = datetime.now(timezone.utc)
|
|
324
332
|
if ended_at is None:
|
|
325
333
|
ended_at = now.isoformat()
|
|
326
334
|
if started_at is None:
|
|
327
|
-
|
|
328
|
-
started_at = (now - timedelta(milliseconds=offset_ms)).isoformat()
|
|
329
|
-
if duration_ms is None:
|
|
330
|
-
duration_ms = 0
|
|
335
|
+
started_at = (now - timedelta(milliseconds=duration_ms)).isoformat()
|
|
331
336
|
|
|
332
337
|
status = "error" if error else "ok"
|
|
333
338
|
if error:
|
|
@@ -12,9 +12,9 @@ trodo/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
12
12
|
trodo/managers/group_manager.py,sha256=ki3Se3qEoSZfREX63oeDeBmEfZF-ISHLE8azEtLg0tM,3542
|
|
13
13
|
trodo/managers/people_manager.py,sha256=mMVnx40Mlifx6NGgChvohC9ViK6dQu2mkXNHbV8pK1E,2882
|
|
14
14
|
trodo/otel/__init__.py,sha256=yiRFXWUU45bAM2CV37XeO7zf1hmnmjufdP4XO50yEyE,624
|
|
15
|
-
trodo/otel/auto_instrument.py,sha256=
|
|
15
|
+
trodo/otel/auto_instrument.py,sha256=7uKhir0o0Mo_od1H2oMf5PHZovcUocHtgV18mRm2Erc,11193
|
|
16
16
|
trodo/otel/context.py,sha256=iJ1rE42-SbO8VZHAxhIl2ZJXgNwLIVps5xLg8GKgfFc,1165
|
|
17
|
-
trodo/otel/helpers.py,sha256=
|
|
17
|
+
trodo/otel/helpers.py,sha256=IEAHxAEN-Bvv_ZODrmRzC6PCGGhGTXU7IPcp6iO2nbA,16405
|
|
18
18
|
trodo/otel/processor.py,sha256=jVZkslZlw50G5uRAa7-GMRgn_yvae58EmlWTZL8tMkQ,6285
|
|
19
19
|
trodo/otel/register.py,sha256=YV2EnkUoa-_54YAuChOe-Mg28UUKg8JO7-qhVP9G6u4,7644
|
|
20
20
|
trodo/otel/transport.py,sha256=hzZz8gwSMGJ8CxdijmLn1Ljt18owr9XTWy13DLbwYbw,2441
|
|
@@ -25,7 +25,7 @@ trodo/queue/event_queue.py,sha256=EVFZrhlq_kwC3jJ2GK0wMhHISf9UzLCZNDnT_aZ2I2A,87
|
|
|
25
25
|
trodo/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
trodo/session/server_session.py,sha256=4bQZc_Zxktmu8RVoyh0qI7tvr8AKsHI5xkGf3jEpWVE,2005
|
|
27
27
|
trodo/session/session_manager.py,sha256=JrgH1VeicmtlxPR4dXEuJbxhi23OelkgwW3-9Slv80o,2525
|
|
28
|
-
trodo_python-2.4.
|
|
29
|
-
trodo_python-2.4.
|
|
30
|
-
trodo_python-2.4.
|
|
31
|
-
trodo_python-2.4.
|
|
28
|
+
trodo_python-2.4.1.dist-info/METADATA,sha256=_V5plcgVHmz0vb9S9fQXgivuVvmsYs2Aict8OViy5FU,17882
|
|
29
|
+
trodo_python-2.4.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
30
|
+
trodo_python-2.4.1.dist-info/top_level.txt,sha256=VCQu1CJWFmNsqTs1YxMcw4Mq35Tc3z3uI9RwHEXAayQ,6
|
|
31
|
+
trodo_python-2.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|