Skip to content

OpenTelemetry: export a sparse set of model-routing attributes and document them #67237

Description

@SivaKesava1

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.cjs calls resolveEffectiveModel (from actions/setup/js/model_attribution.cjs) against aw_info.json and 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.mode and gh-aw.model_routing.router_version.
  • None of the gh-aw.model_routing.* attributes are documented in docs/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) carries workflow.info, model_routing.outcome and the firewall.model_routing selection 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.

Implementation plan

  1. Session reader. In actions/setup/js/model_attribution.cjs, add a helper that reads usage/aw_session.jsonl once and returns a compact routing summary from the workflow.info, model_routing.outcome and firewall.model_routing events. Only events with agent provenance should count, matching how pkg/cli/model_routing_session.go filters 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.
  2. Fallbacks. If the session file or a record is missing, fall back to aw_info.json through resolveEffectiveModel / getModelRouting. Then use the proxy's model-routing log (sandbox/firewall/logs/api-proxy-logs/model-routing.jsonl and the alternate paths already listed in actions/setup/js/parse_token_usage.cjs) for selection labels, degraded flag and request counts.
  3. Parity with audit. Classifier AIC and deviated-request counting must match pkg/cli/model_routing.go, so OTEL and gh aw audit / gh aw logs report the same numbers. That includes classifier cost bucketing and the endpoint-only deviation handling. Classifier AIC must not be added to or subtracted from gh-aw.aic.
  4. Span emission. In send_otlp_span.cjs, in the conclusion-span attribute block that already emits gh-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 no gh-aw.model_routing.* attributes. Keep the existing attributes unchanged, and give the session file precedence over aw_info.json for status, mode and router version when both are present.
  5. Ordering check. Confirm that usage/aw_session.jsonl is 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.
  6. Docs. In docs/src/content/docs/reference/open-telemetry-attributes.mdx, document all gh-aw.model_routing.* attributes (existing status, mode, router_version, plus the new ones). Also cover gh-aw.model.requested and gh-aw.model.effort if 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 that classifier_aic is separate from gh-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-selected outcome, gh-aw.model_routing.failure_code is 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) and deviated_requests match gh aw audit for the same run.
  • All gh-aw.model_routing.* attributes are documented in open-telemetry-attributes.mdx.
  • make fmt, make lint and the targeted JS tests pass.

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions