diff --git a/.castiron.stats.yml b/.castiron.stats.yml index a75b847028..74e5a0f40a 100644 --- a/.castiron.stats.yml +++ b/.castiron.stats.yml @@ -1,8 +1,8 @@ schema_version: 1 -generation_id: baa135fa-765a-4ab9-9ec1-5c9e432437ce -openapi_spec_hash: f56829f9ec48b31ed299c5fb497984d7 -openapi_transformed_spec_hash: 400d42fb468bbe078e4f0c87c366490b +generation_id: 021e41cf-0817-45e4-a61f-ed6fae979669 +openapi_spec_hash: 72d34e76f62e434dc0b71e0b4ac0ce8a +openapi_transformed_spec_hash: d432497f20d18a61808b4cd56a74dd06 config_hash: db72038ac88cdb94ae1cafef6aada78e -codegen_sha: 17f71f06a352b4e7b5ef168c3fd4182844620954 -codegen_hash: e5f930fec1f997ea02bd1746597be5ff2a24c1917743b5bf2d4a2ee617ef8e3b -public_codegen_sha: 249df1f919236e7aa68b460c504dafdee7c23bb1 +codegen_sha: 2f57e709a2d02130135d501497f67d2c279150a6 +codegen_hash: b5dab72c84981b6bbd1709863e017a16f1902b9aa11cd721143186487fd2787b +public_codegen_sha: 12578924cd37047e1bff02ea0e6f90114b363d15 diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml index 81380197d8..797a0aa9d0 100644 --- a/api_reference/openapi.transformed.yml +++ b/api_reference/openapi.transformed.yml @@ -70237,6 +70237,11 @@ components: - type: string description: When `group_by=service_tier`, this field provides the service tier of the grouped usage result. - type: 'null' + input_cache_write_30m_tokens: + anyOf: + - type: integer + description: The aggregated number of input tokens written to the cache with a 30-minute retention period. May be absent or null for older responses. + - type: 'null' required: - object - input_tokens @@ -79227,6 +79232,11 @@ components: - type: string description: When `group_by=service_tier`, this field provides the service tier of the grouped usage result. - type: 'null' + input_cache_write_30m_tokens: + anyOf: + - type: integer + description: The aggregated number of input tokens written to the cache with a 30-minute retention period. + - type: 'null' type: object required: - object diff --git a/scripts/castiron/generated-files.json b/scripts/castiron/generated-files.json index f7bd1277ee..e5b09e89e7 100644 --- a/scripts/castiron/generated-files.json +++ b/scripts/castiron/generated-files.json @@ -1319,7 +1319,7 @@ }, "src/openai/types/admin/organization/usage_audio_speeches_response.py": { "mode": "100644", - "sha256": "05e2e670a19dfb438098204920c9a3f35ee378e0af36125fd4cd3ee7429aa9e5" + "sha256": "c5adb8cfc3780128c64bbae6a4cdeff87f42b616c771a8284aa6196b5ed68ff6" }, "src/openai/types/admin/organization/usage_audio_transcriptions_params.py": { "mode": "100644", @@ -1327,7 +1327,7 @@ }, "src/openai/types/admin/organization/usage_audio_transcriptions_response.py": { "mode": "100644", - "sha256": "3825e0f36eb5896571dc6f73e5c457d88694e17c4f9502aa84071df084fc2b54" + "sha256": "7d94093feee69d6e5a9175b8cf6ba8be06c23fedf2ce6a6059ccb94792f02783" }, "src/openai/types/admin/organization/usage_code_interpreter_sessions_params.py": { "mode": "100644", @@ -1335,7 +1335,7 @@ }, "src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py": { "mode": "100644", - "sha256": "91e50fc917e87b8bb86c49cfac0199fd987dbad640ed0c49f716c7b7ade7df31" + "sha256": "24aa79da1f2f5f69f0301e936ce8f3d2d4c9b43431b1c8ef6b77379d69527eb7" }, "src/openai/types/admin/organization/usage_completions_params.py": { "mode": "100644", @@ -1343,7 +1343,7 @@ }, "src/openai/types/admin/organization/usage_completions_response.py": { "mode": "100644", - "sha256": "5b1ae255aa4e43f3e016587b55fe06ffa961ba6a974a0909e3062d0553419b92" + "sha256": "ca7979c8feecb983fa7d8f3e84e8ad039d66981a09e71e3d62c555bc919ef8e6" }, "src/openai/types/admin/organization/usage_costs_params.py": { "mode": "100644", @@ -1351,7 +1351,7 @@ }, "src/openai/types/admin/organization/usage_costs_response.py": { "mode": "100644", - "sha256": "5e1be2b7f3fa9db827971109ed06fa2a6b2c0997303acae0096e95e63db168f9" + "sha256": "5933dcc20e7958bc3aa0462317960e17c711f048f7da4ec3f513d1ceaed7e5f3" }, "src/openai/types/admin/organization/usage_decisions_params.py": { "mode": "100644", @@ -1359,7 +1359,7 @@ }, "src/openai/types/admin/organization/usage_decisions_response.py": { "mode": "100644", - "sha256": "07bc9e3f5754e47549a938b6a03cc17c14c518d9664dc08f896c5bbeb9f95f49" + "sha256": "e5eac6edfa2a8950f6fc88d63bc33b3197f91567f703c1593a711e2373fa507d" }, "src/openai/types/admin/organization/usage_embeddings_params.py": { "mode": "100644", @@ -1367,7 +1367,7 @@ }, "src/openai/types/admin/organization/usage_embeddings_response.py": { "mode": "100644", - "sha256": "b7b378a194bf08d2a6fb3bf7b9b29efbd1e2b4e791d381274b6a068e3aac04b0" + "sha256": "30f5799ed022742399a4392e81698eb1ba8a417af67af604319c04e6899a1898" }, "src/openai/types/admin/organization/usage_file_search_calls_params.py": { "mode": "100644", @@ -1375,7 +1375,7 @@ }, "src/openai/types/admin/organization/usage_file_search_calls_response.py": { "mode": "100644", - "sha256": "01e442c497e9a5ff95dacf164620e576b2b621bc8479cc7d6db2d828064aa501" + "sha256": "b46f1387a0c5c0957b9db1ce64ee9c9e316903ef36c5793e08d56685e4b4a01f" }, "src/openai/types/admin/organization/usage_images_params.py": { "mode": "100644", @@ -1383,7 +1383,7 @@ }, "src/openai/types/admin/organization/usage_images_response.py": { "mode": "100644", - "sha256": "03c8cc3e73cb76c59dc19f632c3e2b16f62b57733b311e245ac0602c5d53d20a" + "sha256": "3657868b75ebe6d56904fc8704aaf94787d823066fc42a57637d20bde77b435e" }, "src/openai/types/admin/organization/usage_moderations_params.py": { "mode": "100644", @@ -1391,7 +1391,7 @@ }, "src/openai/types/admin/organization/usage_moderations_response.py": { "mode": "100644", - "sha256": "db407436c0c157a2355d5e5b571097cb75f80ca43303132dce051d96f2533342" + "sha256": "00a711f7b0fe635b1857f786c91b7d6df048299c662f9b09431b8f441c666e84" }, "src/openai/types/admin/organization/usage_vector_stores_params.py": { "mode": "100644", @@ -1399,7 +1399,7 @@ }, "src/openai/types/admin/organization/usage_vector_stores_response.py": { "mode": "100644", - "sha256": "41b4f619ae60b7b2991644334ca5d88380dc2e7b752eb5782a96da4525fec6af" + "sha256": "fd68d096785555fcf6528f6ddebab3c3cbe74f6f9bd097e8073dfa0e2620ad8c" }, "src/openai/types/admin/organization/usage_web_search_calls_params.py": { "mode": "100644", @@ -1407,7 +1407,7 @@ }, "src/openai/types/admin/organization/usage_web_search_calls_response.py": { "mode": "100644", - "sha256": "4c3026676d61a1951c249ad69165019dae2184fa69c63032eef5a66132637697" + "sha256": "1f4868a2b6e92f299173d380c1b345a90a87204bc8f0215375ad66e7f5f74be9" }, "src/openai/types/admin/organization/user_delete_response.py": { "mode": "100644", diff --git a/src/openai/types/admin/organization/usage_audio_speeches_response.py b/src/openai/types/admin/organization/usage_audio_speeches_response.py index 131a1889d2..84752bb8d9 100644 --- a/src/openai/types/admin/organization/usage_audio_speeches_response.py +++ b/src/openai/types/admin/organization/usage_audio_speeches_response.py @@ -77,6 +77,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): retention period. """ + input_cache_write_30m_tokens: Optional[int] = None + """ + The aggregated number of input tokens written to the cache with a 30-minute + retention period. May be absent or null for older responses. + """ + input_cache_write_tokens: Optional[int] = None """ The aggregated number of input tokens written to the cache with a 30-minute diff --git a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py index dd806c1b04..5451ac2a45 100644 --- a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py +++ b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py @@ -77,6 +77,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): retention period. """ + input_cache_write_30m_tokens: Optional[int] = None + """ + The aggregated number of input tokens written to the cache with a 30-minute + retention period. May be absent or null for older responses. + """ + input_cache_write_tokens: Optional[int] = None """ The aggregated number of input tokens written to the cache with a 30-minute diff --git a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py index 52a29b76f3..b3b262080f 100644 --- a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py +++ b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py @@ -77,6 +77,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): retention period. """ + input_cache_write_30m_tokens: Optional[int] = None + """ + The aggregated number of input tokens written to the cache with a 30-minute + retention period. May be absent or null for older responses. + """ + input_cache_write_tokens: Optional[int] = None """ The aggregated number of input tokens written to the cache with a 30-minute diff --git a/src/openai/types/admin/organization/usage_completions_response.py b/src/openai/types/admin/organization/usage_completions_response.py index 6da6a62033..3174dcf8a3 100644 --- a/src/openai/types/admin/organization/usage_completions_response.py +++ b/src/openai/types/admin/organization/usage_completions_response.py @@ -77,6 +77,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): retention period. """ + input_cache_write_30m_tokens: Optional[int] = None + """ + The aggregated number of input tokens written to the cache with a 30-minute + retention period. May be absent or null for older responses. + """ + input_cache_write_tokens: Optional[int] = None """ The aggregated number of input tokens written to the cache with a 30-minute diff --git a/src/openai/types/admin/organization/usage_costs_response.py b/src/openai/types/admin/organization/usage_costs_response.py index 3f8f5ce050..317ad8035e 100644 --- a/src/openai/types/admin/organization/usage_costs_response.py +++ b/src/openai/types/admin/organization/usage_costs_response.py @@ -77,6 +77,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): retention period. """ + input_cache_write_30m_tokens: Optional[int] = None + """ + The aggregated number of input tokens written to the cache with a 30-minute + retention period. May be absent or null for older responses. + """ + input_cache_write_tokens: Optional[int] = None """ The aggregated number of input tokens written to the cache with a 30-minute diff --git a/src/openai/types/admin/organization/usage_decisions_response.py b/src/openai/types/admin/organization/usage_decisions_response.py index 0d8a377069..7f1cb82e8a 100644 --- a/src/openai/types/admin/organization/usage_decisions_response.py +++ b/src/openai/types/admin/organization/usage_decisions_response.py @@ -59,6 +59,12 @@ class DataResult(BaseModel): retention period. """ + input_cache_write_30m_tokens: Optional[int] = None + """ + The aggregated number of input tokens written to the cache with a 30-minute + retention period. + """ + input_cache_write_tokens: Optional[int] = None """ The aggregated number of input tokens written to the cache with a 30-minute diff --git a/src/openai/types/admin/organization/usage_embeddings_response.py b/src/openai/types/admin/organization/usage_embeddings_response.py index ef777ddd1e..762a612bb8 100644 --- a/src/openai/types/admin/organization/usage_embeddings_response.py +++ b/src/openai/types/admin/organization/usage_embeddings_response.py @@ -77,6 +77,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): retention period. """ + input_cache_write_30m_tokens: Optional[int] = None + """ + The aggregated number of input tokens written to the cache with a 30-minute + retention period. May be absent or null for older responses. + """ + input_cache_write_tokens: Optional[int] = None """ The aggregated number of input tokens written to the cache with a 30-minute diff --git a/src/openai/types/admin/organization/usage_file_search_calls_response.py b/src/openai/types/admin/organization/usage_file_search_calls_response.py index 84798a80ee..adfbbd3202 100644 --- a/src/openai/types/admin/organization/usage_file_search_calls_response.py +++ b/src/openai/types/admin/organization/usage_file_search_calls_response.py @@ -77,6 +77,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): retention period. """ + input_cache_write_30m_tokens: Optional[int] = None + """ + The aggregated number of input tokens written to the cache with a 30-minute + retention period. May be absent or null for older responses. + """ + input_cache_write_tokens: Optional[int] = None """ The aggregated number of input tokens written to the cache with a 30-minute diff --git a/src/openai/types/admin/organization/usage_images_response.py b/src/openai/types/admin/organization/usage_images_response.py index f84dc97fbf..237db52621 100644 --- a/src/openai/types/admin/organization/usage_images_response.py +++ b/src/openai/types/admin/organization/usage_images_response.py @@ -77,6 +77,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): retention period. """ + input_cache_write_30m_tokens: Optional[int] = None + """ + The aggregated number of input tokens written to the cache with a 30-minute + retention period. May be absent or null for older responses. + """ + input_cache_write_tokens: Optional[int] = None """ The aggregated number of input tokens written to the cache with a 30-minute diff --git a/src/openai/types/admin/organization/usage_moderations_response.py b/src/openai/types/admin/organization/usage_moderations_response.py index e6fd124738..806125a4cb 100644 --- a/src/openai/types/admin/organization/usage_moderations_response.py +++ b/src/openai/types/admin/organization/usage_moderations_response.py @@ -77,6 +77,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): retention period. """ + input_cache_write_30m_tokens: Optional[int] = None + """ + The aggregated number of input tokens written to the cache with a 30-minute + retention period. May be absent or null for older responses. + """ + input_cache_write_tokens: Optional[int] = None """ The aggregated number of input tokens written to the cache with a 30-minute diff --git a/src/openai/types/admin/organization/usage_vector_stores_response.py b/src/openai/types/admin/organization/usage_vector_stores_response.py index 85195b2cd3..f5f2b4b43b 100644 --- a/src/openai/types/admin/organization/usage_vector_stores_response.py +++ b/src/openai/types/admin/organization/usage_vector_stores_response.py @@ -77,6 +77,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): retention period. """ + input_cache_write_30m_tokens: Optional[int] = None + """ + The aggregated number of input tokens written to the cache with a 30-minute + retention period. May be absent or null for older responses. + """ + input_cache_write_tokens: Optional[int] = None """ The aggregated number of input tokens written to the cache with a 30-minute diff --git a/src/openai/types/admin/organization/usage_web_search_calls_response.py b/src/openai/types/admin/organization/usage_web_search_calls_response.py index 5dc60ed031..6cd8fb9ad0 100644 --- a/src/openai/types/admin/organization/usage_web_search_calls_response.py +++ b/src/openai/types/admin/organization/usage_web_search_calls_response.py @@ -77,6 +77,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): retention period. """ + input_cache_write_30m_tokens: Optional[int] = None + """ + The aggregated number of input tokens written to the cache with a 30-minute + retention period. May be absent or null for older responses. + """ + input_cache_write_tokens: Optional[int] = None """ The aggregated number of input tokens written to the cache with a 30-minute diff --git a/tests/test_usage_cache_write.py b/tests/test_usage_cache_write.py new file mode 100644 index 0000000000..a15e425b55 --- /dev/null +++ b/tests/test_usage_cache_write.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +from typing import Any, Literal + +import httpx2 +import pytest + +from openai import OpenAI, AsyncOpenAI +from openai.types.admin.organization import UsageDecisionsResponse, UsageCompletionsResponse + + +def payload(endpoint: str, value: int | None | Literal["omitted"]) -> dict[str, Any]: + result: dict[str, Any] = { + "object": f"organization.usage.{endpoint}.result", + "input_tokens": 200, + "output_tokens": 20, + "num_model_requests": 1, + "input_cache_write_tokens": 17, + "input_cache_write_12h_tokens": 19, + } + if value != "omitted": + result["input_cache_write_30m_tokens"] = value + return { + "object": "page", + "data": [{"object": "bucket", "start_time": 0, "end_time": 60, "results": [result]}], + "has_more": False, + "next_page": None, + } + + +def assert_metric(response: UsageCompletionsResponse | UsageDecisionsResponse, expected: int | None) -> None: + result = response.data[0].results[0] + assert ( + result.object == "organization.usage.completions.result" + or result.object == "organization.usage.decisions.result" + ) + assert result.input_cache_write_30m_tokens == expected + assert result.input_cache_write_tokens == 17 + assert result.input_cache_write_12h_tokens == 19 + + +@pytest.mark.parametrize("endpoint", ["completions", "decisions"]) +@pytest.mark.parametrize("value", [73, 0, None, "omitted"]) +def test_usage_cache_write(endpoint: str, value: int | None | Literal["omitted"]) -> None: + requests: list[httpx2.Request] = [] + + def handler(request: httpx2.Request) -> httpx2.Response: + requests.append(request) + assert request.method == "GET" + assert request.url.path == f"/organization/usage/{endpoint}" + return httpx2.Response(200, json=payload(endpoint, value)) + + with OpenAI( + api_key="fake-project-key", + admin_api_key="fake-admin-key", + base_url="https://example.test", + max_retries=0, + http_client=httpx2.Client(transport=httpx2.MockTransport(handler)), + ) as client: + response = ( + client.admin.organization.usage.completions(start_time=0) + if endpoint == "completions" + else client.admin.organization.usage.decisions(start_time=0) + ) + assert_metric(response, None if value == "omitted" else value) + assert len(requests) == 1 + + +@pytest.mark.asyncio +@pytest.mark.parametrize("endpoint", ["completions", "decisions"]) +@pytest.mark.parametrize("value", [73, 0, None, "omitted"]) +async def test_async_usage_cache_write(endpoint: str, value: int | None | Literal["omitted"]) -> None: + requests: list[httpx2.Request] = [] + + def handler(request: httpx2.Request) -> httpx2.Response: + requests.append(request) + assert request.method == "GET" + assert request.url.path == f"/organization/usage/{endpoint}" + return httpx2.Response(200, json=payload(endpoint, value)) + + async with AsyncOpenAI( + api_key="fake-project-key", + admin_api_key="fake-admin-key", + base_url="https://example.test", + max_retries=0, + http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler)), + ) as client: + response = ( + await client.admin.organization.usage.completions(start_time=0) + if endpoint == "completions" + else await client.admin.organization.usage.decisions(start_time=0) + ) + assert_metric(response, None if value == "omitted" else value) + assert len(requests) == 1