fix(usage): Normalize None token details on Usage initialization - #2034
Merged
Conversation
This was referenced Nov 3, 2025
codefromthecrypt
force-pushed
the
fix-usage-none-handling
branch
2 times, most recently
from
November 4, 2025 01:48
ef862d7 to
c1a9802
Compare
Contributor
Author
|
FYI I'm aware it could be a bikeshed discussion if these fields are really optional or not. for example, they are more or less implied or explicitly required in the latest openai openapi yaml. However, there is defensive code around other token fields, that coerce to zero. Plus the impact is tricky to workaround otherwise even if PRs are raised everywhere. So, hoping we can add similar defense here |
seratch
requested changes
Nov 4, 2025
seratch
left a comment
Member
There was a problem hiding this comment.
We'e fine to deal with this reality, but can you update the code comment a bit more for future maintainers?
Some providers don't populate optional token detail fields, resulting in None values that bypass Pydantic validation. This fix adds a __post_init__ method to normalize None to 0 for cached_tokens and reasoning_tokens on any Usage object creation. This defensive approach handles the issue at the boundary we control, regardless of how providers construct their response objects. Signed-off-by: Adrian Cole <adrian@tetrate.io>
codefromthecrypt
force-pushed
the
fix-usage-none-handling
branch
from
November 5, 2025 01:51
c1a9802 to
376530d
Compare
codefromthecrypt
added a commit
to codefromthecrypt/openai-agents-python
that referenced
this pull request
Dec 2, 2025
Extends openai#2034 to handle providers that return None for entire input_tokens_details and output_tokens_details objects (not just the fields within them). This affects non-streaming responses. Related to openai#1179 (which fixed the streaming case). Some providers like llama-stack return null for these optional fields in their JSON responses. The OpenAI SDK maps these to None in Python. Previously, passing None to the Usage constructor would fail Pydantic validation before __post_init__ could normalize them. This PR uses Pydantic's BeforeValidator to normalize None values at the field level, before Pydantic's type validation runs. Signed-off-by: Adrian Cole <adrian@tetrate.io>
codefromthecrypt
added a commit
to codefromthecrypt/openai-agents-python
that referenced
this pull request
Dec 2, 2025
Extends openai#2034 to handle providers that return None for entire input_tokens_details and output_tokens_details objects (not just the fields within them). This affects non-streaming responses. Related to openai#1179 (which fixed the streaming case). Some providers like llama-stack return null for these optional fields in their JSON responses. The OpenAI SDK maps these to None in Python. Previously, passing None to the Usage constructor would fail Pydantic validation before __post_init__ could normalize them. This PR uses Pydantic's BeforeValidator to normalize None values at the field level, before Pydantic's type validation runs. Signed-off-by: Adrian Cole <adrian@tetrate.io>
Contributor
Author
|
ps looks like there was another case like this that popped up. Hopefully #2141 is a more sustainable fix, lemme know if you agree |
codefromthecrypt
added a commit
to codefromthecrypt/openai-agents-python
that referenced
this pull request
Dec 3, 2025
Extends openai#2034 to handle providers that return None for entire input_tokens_details and output_tokens_details objects (not just the fields within them). This affects non-streaming responses. Related to openai#1179 (which fixed the streaming case). Some providers like llama-stack return null for these optional fields in their JSON responses. The OpenAI SDK maps these to None in Python. Previously, passing None to the Usage constructor would fail Pydantic validation before __post_init__ could normalize them. This PR uses Pydantic's BeforeValidator to normalize None values at the field level, before Pydantic's type validation runs. The validators also convert Chat Completions API types (PromptTokensDetails, CompletionTokensDetails) to Responses API types (InputTokensDetails, OutputTokensDetails).
codefromthecrypt
added a commit
to codefromthecrypt/openai-agents-python
that referenced
this pull request
Dec 3, 2025
Extends openai#2034 to handle providers that return None for entire input_tokens_details and output_tokens_details objects (not just the fields within them). This affects non-streaming responses. Related to openai#1179 (which fixed the streaming case). Some providers like llama-stack return null for these optional fields in their JSON responses. The OpenAI SDK maps these to None in Python. Previously, passing None to the Usage constructor would fail Pydantic validation before __post_init__ could normalize them. This PR uses Pydantic's BeforeValidator to normalize None values at the field level, before Pydantic's type validation runs. The validators also convert Chat Completions API types (PromptTokensDetails, CompletionTokensDetails) to Responses API types (InputTokensDetails, OutputTokensDetails).
codefromthecrypt
added a commit
to codefromthecrypt/openai-agents-python
that referenced
this pull request
Dec 3, 2025
Extends openai#2034 to handle providers that return None for entire input_tokens_details and output_tokens_details objects (not just the fields within them). This affects non-streaming responses. Related to openai#1179 (which fixed the streaming case). Some providers like llama-stack return null for these optional fields in their JSON responses. The OpenAI SDK maps these to None in Python. Previously, passing None to the Usage constructor would fail Pydantic validation before __post_init__ could normalize them. This PR uses Pydantic's BeforeValidator to normalize None values at the field level, before Pydantic's type validation runs. The validators also convert Chat Completions API types (PromptTokensDetails, CompletionTokensDetails) to Responses API types (InputTokensDetails, OutputTokensDetails).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Some openai API clones (such as ollama) don't populate new token detail fields, resulting in None values that bypass Pydantic validation. This results in agent crashes like this:
This fix adds a
__post_init__method to normalize None to 0 for "cached_tokens" and "reasoning_tokens" on any Usage object creation. This defensive approach handles the issue at the boundary we control (just after the stainless generated code), allowing absence of these newer fields to not break agents.