Skip to content

Add gen_ai.usage.raw attribute to OpenAI spans#1777

Merged
alexmojaki merged 2 commits intomainfrom
alex/openai-responses-raw-usage
Mar 13, 2026
Merged

Add gen_ai.usage.raw attribute to OpenAI spans#1777
alexmojaki merged 2 commits intomainfrom
alex/openai-responses-raw-usage

Conversation

@alexmojaki
Copy link
Contributor

@alexmojaki alexmojaki commented Mar 13, 2026

Summary

  • Adds gen_ai.usage.raw span attribute containing the full serialized usage object from OpenAI responses (via model_dump(exclude_none=True))
  • Captures detailed token breakdowns (e.g. cached_tokens, reasoning_tokens) that are lost by the existing flat input_tokens/output_tokens attributes
  • Applied in the shared usage extraction path so all non-streaming OpenAI response types (chat completions, completions, embeddings, responses API) get it

Test plan

  • All 61 existing OpenAI tests pass with updated snapshots
  • Pyright and ruff pass

🤖 Generated with Claude Code


Summary by cubic

Adds the gen_ai.usage.raw span attribute to store the full OpenAI usage object, preserving detailed token metrics. Applies to all non‑streaming responses so spans include rich usage data across chat, completions, embeddings, and the responses API.

  • New Features
    • Introduced USAGE_RAW semconv key (gen_ai.usage.raw) and set it via usage.model_dump(exclude_none=True).
    • Captures fine‑grained token details (e.g., cached, reasoning, audio, text) beyond flat input/output counters.
    • Implemented in the shared extraction path to cover chat completions, completions, embeddings, and responses API.

Written for commit 5ef3d8b. Summary will update on new commits.

Serializes the full usage object (including token detail breakdowns like
cached_tokens and reasoning_tokens) via model_dump(exclude_none=True).
Applied in the shared usage extraction path so all OpenAI response types
(chat, completions, embeddings, responses API) get it.
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment on lines +621 to +622
if usage is not None and hasattr(usage, 'model_dump'):
span.set_attribute(USAGE_RAW, usage.model_dump(exclude_none=True))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 USAGE_RAW not set for streaming responses

The USAGE_RAW attribute is only set in the non-streaming on_response path (openai.py:621-622). For streaming responses, the flow goes through StreamState.get_attributes() instead (via record_streaming in llm_provider.py:217), and none of the stream state classes (OpenaiChatCompletionStreamState, OpenaiCompletionStreamState, OpenaiResponsesStreamState) set USAGE_RAW. This is consistent with how INPUT_TOKENS and OUTPUT_TOKENS are also not set in stream states — but it means streaming responses won't have the raw usage data. This may be intentional since streaming usage data is often incomplete or unavailable, but worth noting if the goal is comprehensive usage tracking.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional scope limiting — this PR covers OpenAI non-streaming only. Streaming and Anthropic are planned as follow-up work.

Comment on lines +621 to +622
if usage is not None and hasattr(usage, 'model_dump'):
span.set_attribute(USAGE_RAW, usage.model_dump(exclude_none=True))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 USAGE_RAW not added to Anthropic integration

The USAGE_RAW attribute is only added to the OpenAI integration. The Anthropic integration (anthropic.py:342-344) also has usage data (response.usage) that is a Pydantic model with model_dump, but does not set USAGE_RAW. If the goal is to capture raw usage across all providers, Anthropic should be updated similarly. This may be intentional scope limiting for this PR.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional scope limiting — Anthropic support is planned as follow-up work.

@alexmojaki alexmojaki merged commit 903ed56 into main Mar 13, 2026
24 checks passed
@alexmojaki alexmojaki deleted the alex/openai-responses-raw-usage branch March 13, 2026 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant