Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions NIGHTLY_CODEX_FINAL_ATTEMPT_1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Implemented a small related contribution for openai/openai-python issue #2544.

Changed:
- [tests/test_models.py](/home/runner/work/oss-nightly-control/oss-nightly-control/target/tests/test_models.py): added a regression test that `CompletionUsage` preserves provider-specific extra usage fields like `promptTokensDetails`.
- [README.md](/home/runner/work/oss-nightly-control/oss-nightly-control/target/README.md): clarified nested undocumented response fields are available from the nested Pydantic model.
- [NIGHTLY_REPORT.md](/home/runner/work/oss-nightly-control/oss-nightly-control/target/NIGHTLY_REPORT.md): added the required nightly summary.

Validation run:
- `uv run pytest tests/test_models.py::test_completion_usage_preserves_unknown_token_details -q -o addopts=''`
- `uv run pytest tests/test_models.py -q -o addopts=''`
- `uv run ruff format --check tests/test_models.py`
- `uv run ruff check tests/test_models.py`
- `uv run python scripts/utils/ruffen-docs.py README.md`

Note: I avoided the direct `prompt_tokens_details=None` behavior change because GitHub shows an existing open PR for that exact approach: https://github.com/openai/openai-python/pull/2773. The issue context used was https://github.com/openai/openai-python/issues/2544.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,7 @@ options.
To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You
can also get all the extra fields on the Pydantic model as a dict with
[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra).
For nested response objects, access extra fields from that nested model, such as `response.usage.model_extra`.

### Configuring the HTTP client

Expand Down
31 changes: 31 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pydantic
from pydantic import Field

from openai.types import CompletionUsage
from openai._utils import PropertyInfo
from openai._compat import PYDANTIC_V1, parse_obj, model_dump, model_json
from openai._models import DISCRIMINATOR_CACHE, BaseModel, construct_type
Expand Down Expand Up @@ -157,6 +158,36 @@ def test_unknown_fields() -> None:
assert model_dump(m2) == {"foo": "foo", "unknown": {"foo_bar": True}}


def test_completion_usage_preserves_unknown_token_details() -> None:
usage = parse_obj(
CompletionUsage,
{
"completion_tokens": 57,
"prompt_tokens": 2181,
"total_tokens": 2518,
"prompt_tokens_details": None,
"reasoning_tokens": 280,
"traffic_type": "ON_DEMAND",
"promptTokensDetails": [{"modality": "TEXT", "tokenCount": 2181}],
"candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 57}],
},
)

assert usage.prompt_tokens_details is None
assert cast(Any, usage).reasoning_tokens == 280
assert cast(Any, usage).traffic_type == "ON_DEMAND"
assert cast(Any, usage).promptTokensDetails == [{"modality": "TEXT", "tokenCount": 2181}]
assert usage.to_dict(exclude_none=True) == {
"completion_tokens": 57,
"prompt_tokens": 2181,
"total_tokens": 2518,
"reasoning_tokens": 280,
"traffic_type": "ON_DEMAND",
"promptTokensDetails": [{"modality": "TEXT", "tokenCount": 2181}],
"candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 57}],
}


def test_strict_validation_unknown_fields() -> None:
class Model(BaseModel):
foo: str
Expand Down