Skip to content

[openai] Record cache-write and modality token usage - #681

Open
1fanwang wants to merge 5 commits into
open-telemetry:mainfrom
1fanwang:1fannnw/openai-token-details-603
Open

[openai] Record cache-write and modality token usage#681
1fanwang wants to merge 5 commits into
open-telemetry:mainfrom
1fanwang:1fannnw/openai-token-details-603

Conversation

@1fanwang

@1fanwang 1fanwang commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Description

Chat Completions drops optional cache-write and modality counts. This exports them, with streaming details applied at cleanup.

OpenAI 3.1.0 defines the text/image fields. Missing and zero values remain omitted.

Fixes #603.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How has this been tested?

Client mocks drive real instrumentation with typed SDK responses.

  • Test A

From the configured latest tox environment:

P=instrumentation/opentelemetry-instrumentation-genai-openai
T=tests/test_chat_token_usage.py
PY="$PWD/.tox/py312-test-instrumentation-genai-openai-latest/bin/python"
B=$(mktemp -d)
git archive 7ca90a33f163baa6146749335438488ee90d4fba "$P/src" "$P/tests" | tar -x -C "$B"
cp "$P/$T" "$B/$P/$T"
PYTHONPATH="$B/$P/src" "$PY" -m pytest "$B/$P/$T" -k all-details -q -s
"$PY" -m pytest "$P/$T" -k all-details -q -s
Raw logs

Before:

E         Right contains 6 more items:
E         {'gen_ai.usage.audio.input_tokens': 10,

After:

{"exported_usage": {"gen_ai.usage.audio.input_tokens": 10, "gen_ai.usage.audio.output_tokens": 2, "gen_ai.usage.cache_read.input_tokens": 5, "gen_ai.usage.cache_write.input_tokens": 10, "gen_ai.usage.image.input_tokens": 20, "gen_ai.usage.input_tokens": 100, "gen_ai.usage.output_tokens": 20, "gen_ai.usage.text.input_tokens": 70, "gen_ai.usage.text.output_tokens": 18}}

These runs do not call the provider or exercise Weaver.

Checklist

See CONTRIBUTING.md
for the style guide, changelog guidance, and more.

  • Followed the style guidelines of this project
  • Changelog updated if the change requires an entry
  • Unit tests added
  • Documentation updated

Signed-off-by: 1fanwang <1fannnw@gmail.com>
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Sep 11, 2026

Copy link
Copy Markdown

Pull request dashboard status

Waiting on the author · refreshed 2026-09-13 22:36 UTC

Respond to 2 review items (e.g. link a commit, explain why not, ask a follow-up):

  • Inline threads: 1
  • Top-level threads: 2
Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Should this be with reviewers? Comment /dashboard route:reviewers to route it to them.
  • Anything wrong — including the routing? Report it with what you expected; it helps us improve the dashboard.

Signed-off-by: 1fanwang <1fannnw@gmail.com>
Signed-off-by: 1fanwang <1fannnw@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Add integer type assertions for exported usage attributes in the regression tests.

Pull request overview

Adds OpenAI Chat Completions cache-write and modality token usage recording for synchronous, asynchronous, and streaming calls.

Changes:

  • Extracts detailed usage fields into telemetry.
  • Adds local HTTP/SSE regression coverage.
  • Adds a changelog entry.
File summaries
File Description
instrumentation/opentelemetry-instrumentation-genai-openai/tests/test_chat_token_usage.py Tests usage-detail variants across call modes.
instrumentation/opentelemetry-instrumentation-genai-openai/src/opentelemetry/instrumentation/genai/openai/utils.py Extracts detailed usage fields.
instrumentation/opentelemetry-instrumentation-genai-openai/src/opentelemetry/instrumentation/genai/openai/patch.py Applies extraction to non-streaming responses.
instrumentation/opentelemetry-instrumentation-genai-openai/src/opentelemetry/instrumentation/genai/openai/chat_wrappers.py Applies extraction to streaming responses.
instrumentation/opentelemetry-instrumentation-genai-openai/.changelog/603.added Documents the feature.
Review details

Suppressed comments (1)

instrumentation/opentelemetry-instrumentation-genai-openai/tests/test_chat_token_usage.py:222

  • This only checks equality, so a bool or float usage value would still pass (True == 1 and 1.0 == 1). The new semconv usage attributes must be integers; assert the types of the exported values here so the regression test catches invalid attribute values.
    assert actual == expected
  • Files reviewed: 5/5 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@lmolkova lmolkova left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please make sure to follow existing test practices in this repo

)
invocation.text_input_tokens = get_property_value(
prompt_details, "text_tokens"
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OpenAI Chat Completions does not expose text_tokens or image_tokens in prompt_tokens_details or completion_tokens_details. Only cache_write_tokens and audio_tokens exist on PromptTokensDetails, and audio_tokens on CompletionTokensDetails. Please remove the non-existent modality mappings.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

self._self_cached_prompt_tokens = get_property_value(
prompt_tokens_details, "cached_tokens"
)
set_chat_usage_details(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do not mutate invocation directly during chunk processing. Buffer the token details on the wrapper instance with a _self_ prefix and assign them to self._self_invocation in _cleanup().

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 7ff833e.

"choices": [
{
"index": 0,
"delta": {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do not spin up a custom ThreadingHTTPServer with synthetic responses. Use standard VCR cassettes or client unit mocks consistent with the rest of the test suite.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 7ff833e.

Signed-off-by: 1fanwang <1fannnw@gmail.com>
Signed-off-by: 1fanwang <1fannnw@gmail.com>
return getattr(obj, property_name, None)


def set_chat_usage_details(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The token-detail extraction is duplicated between this helper and the stream wrapper. Please share extraction across both paths, buffering the values until stream cleanup.

Please also wait for #674 to land, then reuse its set_input_tokens() and set_output_tokens() helpers for modality breakdowns. Keep the aggregate cache-write count separate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

[openai] Capture detailed token usage (cache write and modality breakdown)

3 participants