Repository navigation
OpenTelemetry: export a sparse set of model-routing attributes and document them #67237
Description
Summary
Export a small, fixed set of model-routing attributes to OpenTelemetry and document every gh-aw.model_routing.* attribute, as requested by @pelikhan ("save some model-routing data into OTEL, sparingly").
Current state (main)
actions/setup/js/send_otlp_span.cjscallsresolveEffectiveModel(fromactions/setup/js/model_attribution.cjs) againstaw_info.jsonand adds these attributes to job conclusion spans:gen_ai.request.model,gh-aw.model.requested,gh-aw.model.effort,gh-aw.model_routing.status,gh-aw.model_routing.modeandgh-aw.model_routing.router_version.- None of the
gh-aw.model_routing.*attributes are documented indocs/src/content/docs/reference/open-telemetry-attributes.mdx. - Since audit/logs: agent job's final aw_info.json and harness routing outcome are lost (deleted on download, absent from aw_session.jsonl) #67113 / Preserve model routing attribution in audit and logs #67114, the agent unified session file (
usage/aw_session.jsonl) carriesworkflow.info,model_routing.outcomeand thefirewall.model_routingselection and request records. @pelikhan's rule is that consumers read routing data from that file. - The Go audit/logs code (
pkg/cli/model_routing.go,pkg/cli/model_routing_session.go) already derives failure code, degraded classification, classifier cost and deviated request counts from the same records.
Proposal
Emit one set of values per run, only on the agent job's conclusion span, and never per request. All values must be low-cardinality, with no free text derived from the prompt.
New attributes:
| Attribute | Type | When emitted | Source |
|---|---|---|---|
gh-aw.model_routing.failure_code |
string | Only when status is not selected |
model_routing.outcome failure code, or the routing failure in aw_info.json |
gh-aw.model_routing.objective |
string | When present, for example cost |
Selection record objective goal |
gh-aw.model_routing.task_type |
string (enum) | When classified | Selection record labels |
gh-aw.model_routing.scope |
string (enum) | When classified | Selection record labels |
gh-aw.model_routing.complexity |
string (enum) | When classified | Selection record task_complexity label |
gh-aw.model_routing.degraded |
boolean | When a selection record exists | Selection record degraded-classification flag |
gh-aw.model_routing.classifier_aic |
double | When classifier usage is attributable | Classifier credits, kept separate from gh-aw.aic |
gh-aw.model_routing.deviated_requests |
integer | When request records exist | Count of request records not served as selected |
Explicitly excluded: ranked choices, selected ids, conversation hashes, classifier rationale or degraded reason text, and any per-request data.
Dependency: classifier cost fix (#67092)
gh-aw.model_routing.classifier_aic depends on the classifier-cost fix in #67092 (fixes #67079), which is not merged yet. On main, classifier cost for routed runs currently comes out as 0.
- If Restore classifier cost attribution in routed-run audits #67092 has merged by the time this issue is implemented, derive
classifier_aicfrom its corrected classifier-cost attribution. - If Restore classifier cost attribution in routed-run audits #67092 has not merged, either build on its branch or ship the other attributes now and leave
classifier_aicfor a follow-up. - Either way, do not re-implement classifier detection or cost bucketing in
send_otlp_span.cjsormodel_attribution.cjs.
Implementation plan
- Session reader. In
actions/setup/js/model_attribution.cjs, add a helper that readsusage/aw_session.jsonlonce and returns a compact routing summary from theworkflow.info,model_routing.outcomeandfirewall.model_routingevents. Only events with agent provenance should count, matching howpkg/cli/model_routing_session.gofilters them. The helper returns only the allow-listed fields above. Enum labels are passed through the existing identifier validation (validateModelIdentifier) and dropped if they fail it. - Fallbacks. If the session file or a record is missing, fall back to
aw_info.jsonthroughresolveEffectiveModel/getModelRouting. Then use the proxy's model-routing log (sandbox/firewall/logs/api-proxy-logs/model-routing.jsonland the alternate paths already listed inactions/setup/js/parse_token_usage.cjs) for selection labels, degraded flag and request counts. - Parity with audit. Classifier AIC and deviated-request counting must match
pkg/cli/model_routing.go, so OTEL andgh aw audit/gh aw logsreport the same numbers. That includes classifier cost bucketing and the endpoint-only deviation handling. Classifier AIC must not be added to or subtracted fromgh-aw.aic. - Span emission. In
send_otlp_span.cjs, in the conclusion-span attribute block that already emitsgh-aw.model_routing.status, add the new attributes only when the conclusion span is for the agent job (jobName === "agent") and routing is active. Non-routed runs must emit nogh-aw.model_routing.*attributes. Keep the existing attributes unchanged, and give the session file precedence overaw_info.jsonfor status, mode and router version when both are present. - Ordering check. Confirm that
usage/aw_session.jsonlis written before the agent job's conclusion span is sent. If it isn't, rely on the fallbacks above and note this in a code comment. - Docs. In
docs/src/content/docs/reference/open-telemetry-attributes.mdx, document allgh-aw.model_routing.*attributes (existingstatus,mode,router_version, plus the new ones). Also covergh-aw.model.requestedandgh-aw.model.effortif they are not already listed. Include a short note on the emission scope (agent conclusion span only, once per run), the exclusions, and the fact thatclassifier_aicis separate fromgh-aw.aic.
Tests
In actions/setup/js/send_otlp_span.test.cjs, add:
- Routed run. Given a session file with a selected outcome, a selection record and request records (including one deviated), the agent conclusion span carries status, mode, router version, objective, task type, scope, complexity, degraded, classifier AIC and deviated requests. It has no
failure_code, and none of the excluded fields appear. - Failed routing. With a non-
selectedoutcome,gh-aw.model_routing.failure_codeis present alongside the status. - Non-routed run. No
gh-aw.model_routing.*attributes are emitted.
Also add coverage for the aw_info.json / proxy-log fallback when the session file is absent, and for the rule that non-agent conclusion spans do not carry the new attributes. Add unit tests for the new helper in actions/setup/js/model_attribution.test.cjs.
Acceptance criteria
- At most one set of routing values per run, only on the agent conclusion span.
- No prompt-derived text, ranked choices, selected ids, conversation hashes or per-request data in any span.
classifier_aic(once Restore classifier cost attribution in routed-run audits #67092 is in) anddeviated_requestsmatchgh aw auditfor the same run.- All
gh-aw.model_routing.*attributes are documented inopen-telemetry-attributes.mdx. make fmt,make lintand the targeted JS tests pass.
Activity