GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — feat(tracing): correlate business spans with active observability trace by NiteshDhanpal · Pull Request #465 · scaleapi/scale-agentex-python Conversation
|
| Filename | Overview |
|---|---|
| src/agentex/lib/core/tracing/obs_ids.py | New correlation helper — correctly handles ImportError and OTel is_valid checks, but exception handling is too narrow: any runtime error from ddtrace/OTel propagates into start_span. Also emits the OTel null-sentinel span_id (already flagged in a previous thread). |
| src/agentex/lib/core/tracing/trace.py | Merges obs_correlation() into serialized_data at span creation. The dict-unpack pattern crashes with TypeError when data is list[dict] (already flagged in previous thread); otherwise the logic is sound. |
| .github/workflows/ci.yml | Runner condition broadened from an exact repo-name match to startsWith('stainless-sdks/'), allowing the depot runner for any fork under that org prefix. Change is intentional and safe. |
| .stats.yml | Auto-generated openapi spec URL and hash update — no logic changes. |
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Caller
participant Trace/AsyncTrace
participant obs_ids
participant OTel/ddtrace
Caller->>Trace/AsyncTrace: "start_span(name, data=...)"
Trace/AsyncTrace->>obs_ids: obs_correlation()
obs_ids->>obs_ids: get_obs_mode() reads SGP_OBS_MODE
alt "mode == lgtm"
obs_ids->>OTel/ddtrace: _lgtm_ids() get_current_span()
else "mode == dual"
obs_ids->>OTel/ddtrace: _lgtm_ids() or _ddtrace_ids()
else "mode == dd_only default"
obs_ids->>OTel/ddtrace: _ddtrace_ids() current_trace_context()
end
OTel/ddtrace-->>obs_ids: (trace_id, span_id) or None
obs_ids-->>Trace/AsyncTrace: obs.trace_id and obs.span_id or empty dict
Trace/AsyncTrace->>Trace/AsyncTrace: merge obs into serialized_data
Trace/AsyncTrace->>Trace/AsyncTrace: create Span with merged data
Trace/AsyncTrace-->>Caller: Span
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Caller
participant Trace/AsyncTrace
participant obs_ids
participant OTel/ddtrace
Caller->>Trace/AsyncTrace: "start_span(name, data=...)"
Trace/AsyncTrace->>obs_ids: obs_correlation()
obs_ids->>obs_ids: get_obs_mode() reads SGP_OBS_MODE
alt "mode == lgtm"
obs_ids->>OTel/ddtrace: _lgtm_ids() get_current_span()
else "mode == dual"
obs_ids->>OTel/ddtrace: _lgtm_ids() or _ddtrace_ids()
else "mode == dd_only default"
obs_ids->>OTel/ddtrace: _ddtrace_ids() current_trace_context()
end
OTel/ddtrace-->>obs_ids: (trace_id, span_id) or None
obs_ids-->>Trace/AsyncTrace: obs.trace_id and obs.span_id or empty dict
Trace/AsyncTrace->>Trace/AsyncTrace: merge obs into serialized_data
Trace/AsyncTrace->>Trace/AsyncTrace: create Span with merged data
Trace/AsyncTrace-->>Caller: Span
Reviews (2): Last reviewed commit: "feat(tracing): correlate business spans ..." | Re-trigger Greptile
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| obs = obs_correlation() | ||
| if obs: | ||
| serialized_data = {**(serialized_data or {}), **obs} |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
serialized_data can be a list when data is passed as list[dict[str, Any]] — recursive_model_dump returns a list for that case, and a non-empty list is truthy, so serialized_data or {} evaluates to the list. {**<list>, **obs} then raises TypeError: argument after ** must be a mapping, not list at span-creation time whenever an obs context is active. The same pattern is duplicated in AsyncTrace.start_span (line 243–244). A safe guard would be: if obs and isinstance(serialized_data, dict): serialized_data = {**serialized_data, **obs} (or similarly initialise to {} when None).
This is a comment left during a code review. Path: src/agentex/lib/core/tracing/trace.py Line: 86-88 Comment: `serialized_data` can be a list when `data` is passed as `list[dict[str, Any]]` — `recursive_model_dump` returns a list for that case, and a non-empty list is truthy, so `serialized_data or {}` evaluates to the list. `{**<list>, **obs}` then raises `TypeError: argument after ** must be a mapping, not list` at span-creation time whenever an obs context is active. The same pattern is duplicated in `AsyncTrace.start_span` (line 243–244). A safe guard would be: `if obs and isinstance(serialized_data, dict): serialized_data = {**serialized_data, **obs}` (or similarly initialise to `{}` when `None`). How can I resolve this? If you propose a fix, please make it concise.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| ctx = tracer.current_trace_context() | ||
| if ctx and ctx.trace_id: | ||
| return format(ctx.trace_id, "032x"), format(ctx.span_id or 0, "016x") | ||
| return None |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
ctx.span_id or 0 falls back to 0 when span_id is None or zero, which formats to "0000000000000000" — the OTel invalid/null sentinel. This produces a correlation pair where obs.trace_id is populated but obs.span_id is the null sentinel, which consumers may interpret as "no active span." If span_id is absent, skipping the correlation entirely is more correct.
| ctx = tracer.current_trace_context() | |
| if ctx and ctx.trace_id: | |
| return format(ctx.trace_id, "032x"), format(ctx.span_id or 0, "016x") | |
| return None | |
| ctx = tracer.current_trace_context() | |
| if ctx and ctx.trace_id and ctx.span_id: | |
| return format(ctx.trace_id, "032x"), format(ctx.span_id, "016x") | |
| return None |
This is a comment left during a code review. Path: src/agentex/lib/core/tracing/obs_ids.py Line: 61-64 Comment: `ctx.span_id or 0` falls back to `0` when `span_id` is `None` or zero, which formats to `"0000000000000000"` — the OTel invalid/null sentinel. This produces a correlation pair where `obs.trace_id` is populated but `obs.span_id` is the null sentinel, which consumers may interpret as "no active span." If `span_id` is absent, skipping the correlation entirely is more correct. ```suggestion ctx = tracer.current_trace_context() if ctx and ctx.trace_id and ctx.span_id: return format(ctx.trace_id, "032x"), format(ctx.span_id, "016x") return None ``` How can I resolve this? If you propose a fix, please make it concise.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Tag each adk business span at creation time with the active observability trace_id/span_id (OpenTelemetry preferred, ddtrace fallback), selected via SGP_OBS_MODE. The business trace_id stays the run-level task id; the obs ids are attached as span data (obs.trace_id/obs.span_id) using the span-link pattern so a span can be pivoted to its per-turn Tempo/Datadog trace without collapsing the agent-run grouping. Capture is emit-time (in start_span), never at ingest, so batched /v5/spans/batch flushing cannot shatter one run across N obs traces. Returns no tag when no obs context is active (safe no-op; default SGP_OBS_MODE=dd_only). Co-Authored-By: Claude Opus 4.8 <[email protected]>
a3a7ae1 to
cd91ba7
Compare
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.