From 9476c2a5e8b8f15af10f8a9828dbb7324d4d8f8d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 18:51:08 +0000 Subject: [PATCH 01/12] ci: pin GitHub Actions to commit SHAs Pin all GitHub Actions referenced in generated workflows (both first-party `actions/*` and third-party) to immutable commit SHAs. Updating pinned actions is now a deliberate codegen-side bump rather than implicit on every workflow run. From 97fa3c60714c5383756d15326e8e719f869fd903 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 04:09:10 +0000 Subject: [PATCH 02/12] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 38a36fd922..4bfc577213 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-50d816559ef0935e64d07789ff936a2b762e26ab0714a2fa6bc06d06d4484294.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-7f30f2390f57603d13ed862d360185cfb4f48b4bf9e56e73d1d17ae7e3dfc423.yml openapi_spec_hash: c5d8f37edbf66c1fef627d787b4c54fd -config_hash: b64135fff1fe9cf4069b9ecf59ae8b07 +config_hash: b496fe9c594a1820ad169a6f2d841a32 From 7dc2afdb6925689fca62f4408748c71a2f053c4e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 17:26:19 +0000 Subject: [PATCH 03/12] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4bfc577213..a3e07379dd 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-7f30f2390f57603d13ed862d360185cfb4f48b4bf9e56e73d1d17ae7e3dfc423.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-0127d9dfdff671141b07eb8fda98f6297f70267d2fa9249f4b0defb6826e5aba.yml openapi_spec_hash: c5d8f37edbf66c1fef627d787b4c54fd -config_hash: b496fe9c594a1820ad169a6f2d841a32 +config_hash: 70ff4da62a6a6ef9f91d566c38b7bddf From 00265c5daba4d2481452ad35220f1556dab6bcf6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 23:12:26 +0000 Subject: [PATCH 04/12] feat(api): update OpenAPI spec or Stainless config --- .stats.yml | 4 +- .../resources/admin/organization/invites.py | 6 +- .../organization/invite_create_params.py | 3 +- .../usage_audio_speeches_response.py | 79 +++++++++++++++++++ .../usage_audio_transcriptions_response.py | 79 +++++++++++++++++++ ...sage_code_interpreter_sessions_response.py | 79 +++++++++++++++++++ .../usage_completions_response.py | 79 +++++++++++++++++++ .../organization/usage_costs_response.py | 79 +++++++++++++++++++ .../organization/usage_embeddings_response.py | 79 +++++++++++++++++++ .../organization/usage_images_response.py | 79 +++++++++++++++++++ .../usage_moderations_response.py | 79 +++++++++++++++++++ .../usage_vector_stores_response.py | 79 +++++++++++++++++++ .../types/responses/response_input_item.py | 9 +++ .../responses/response_input_item_param.py | 9 +++ .../types/responses/response_input_param.py | 9 +++ 15 files changed, 746 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index a3e07379dd..f805dd3c95 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-0127d9dfdff671141b07eb8fda98f6297f70267d2fa9249f4b0defb6826e5aba.yml -openapi_spec_hash: c5d8f37edbf66c1fef627d787b4c54fd +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-fcfdf0bb920ca15a6d01d1cfe988accb7559b2d991b6e2ad6b25cfae81ac2ae9.yml +openapi_spec_hash: 72ee7d4fe582e75d16e67b6c97881fed config_hash: 70ff4da62a6a6ef9f91d566c38b7bddf diff --git a/src/openai/resources/admin/organization/invites.py b/src/openai/resources/admin/organization/invites.py index 23b83ccb18..b9db8c622c 100644 --- a/src/openai/resources/admin/organization/invites.py +++ b/src/openai/resources/admin/organization/invites.py @@ -67,7 +67,8 @@ def create( projects: An array of projects to which membership is granted at the same time the org invite is accepted. If omitted, the user will be invited to the default project - for compatibility with legacy behavior. + for compatibility with legacy behavior. If empty list is passed, the user will + not be invited to any projects, including the default one. extra_headers: Send extra headers @@ -270,7 +271,8 @@ async def create( projects: An array of projects to which membership is granted at the same time the org invite is accepted. If omitted, the user will be invited to the default project - for compatibility with legacy behavior. + for compatibility with legacy behavior. If empty list is passed, the user will + not be invited to any projects, including the default one. extra_headers: Send extra headers diff --git a/src/openai/types/admin/organization/invite_create_params.py b/src/openai/types/admin/organization/invite_create_params.py index 7709003fe3..51430d8694 100644 --- a/src/openai/types/admin/organization/invite_create_params.py +++ b/src/openai/types/admin/organization/invite_create_params.py @@ -19,7 +19,8 @@ class InviteCreateParams(TypedDict, total=False): """ An array of projects to which membership is granted at the same time the org invite is accepted. If omitted, the user will be invited to the default project - for compatibility with legacy behavior. + for compatibility with legacy behavior. If empty list is passed, the user will + not be invited to any projects, including the default one. """ 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 e5fed36b03..f99a73eeb0 100644 --- a/src/openai/types/admin/organization/usage_audio_speeches_response.py +++ b/src/openai/types/admin/organization/usage_audio_speeches_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), 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 c5b77f34e1..d8f436b0b5 100644 --- a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py +++ b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), 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 aafda6f113..0c92f61446 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 @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_completions_response.py b/src/openai/types/admin/organization/usage_completions_response.py index f179149995..57f0b699ec 100644 --- a/src/openai/types/admin/organization/usage_completions_response.py +++ b/src/openai/types/admin/organization/usage_completions_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_costs_response.py b/src/openai/types/admin/organization/usage_costs_response.py index fd1d344e26..71eee1188a 100644 --- a/src/openai/types/admin/organization/usage_costs_response.py +++ b/src/openai/types/admin/organization/usage_costs_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_embeddings_response.py b/src/openai/types/admin/organization/usage_embeddings_response.py index adbc389d89..69697ee4e7 100644 --- a/src/openai/types/admin/organization/usage_embeddings_response.py +++ b/src/openai/types/admin/organization/usage_embeddings_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_images_response.py b/src/openai/types/admin/organization/usage_images_response.py index 7c6a096c98..33c3ebb130 100644 --- a/src/openai/types/admin/organization/usage_images_response.py +++ b/src/openai/types/admin/organization/usage_images_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_moderations_response.py b/src/openai/types/admin/organization/usage_moderations_response.py index 5e40ef1164..3aa5d9193d 100644 --- a/src/openai/types/admin/organization/usage_moderations_response.py +++ b/src/openai/types/admin/organization/usage_moderations_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), 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 089aa23119..a5ecc31185 100644 --- a/src/openai/types/admin/organization/usage_vector_stores_response.py +++ b/src/openai/types/admin/organization/usage_vector_stores_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index af3ce5bdc9..9f12b429bd 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -50,6 +50,7 @@ "McpApprovalRequest", "McpApprovalResponse", "McpCall", + "CompactionTrigger", "ItemReference", ] @@ -527,6 +528,13 @@ class McpCall(BaseModel): """ +class CompactionTrigger(BaseModel): + """Compacts the current context. Must be the final input item.""" + + type: Literal["compaction_trigger"] + """The type of the item. Always `compaction_trigger`.""" + + class ItemReference(BaseModel): """An internal identifier for an item to reference.""" @@ -566,6 +574,7 @@ class ItemReference(BaseModel): McpCall, ResponseCustomToolCallOutput, ResponseCustomToolCall, + CompactionTrigger, ItemReference, ], PropertyInfo(discriminator="type"), diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 87ea1bc572..156ac92d39 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -51,6 +51,7 @@ "McpApprovalRequest", "McpApprovalResponse", "McpCall", + "CompactionTrigger", "ItemReference", ] @@ -525,6 +526,13 @@ class McpCall(TypedDict, total=False): """ +class CompactionTrigger(TypedDict, total=False): + """Compacts the current context. Must be the final input item.""" + + type: Required[Literal["compaction_trigger"]] + """The type of the item. Always `compaction_trigger`.""" + + class ItemReference(TypedDict, total=False): """An internal identifier for an item to reference.""" @@ -563,5 +571,6 @@ class ItemReference(TypedDict, total=False): McpCall, ResponseCustomToolCallOutputParam, ResponseCustomToolCallParam, + CompactionTrigger, ItemReference, ] diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index cf4d529521..656c70359b 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -52,6 +52,7 @@ "McpApprovalRequest", "McpApprovalResponse", "McpCall", + "CompactionTrigger", "ItemReference", ] @@ -526,6 +527,13 @@ class McpCall(TypedDict, total=False): """ +class CompactionTrigger(TypedDict, total=False): + """Compacts the current context. Must be the final input item.""" + + type: Required[Literal["compaction_trigger"]] + """The type of the item. Always `compaction_trigger`.""" + + class ItemReference(TypedDict, total=False): """An internal identifier for an item to reference.""" @@ -564,6 +572,7 @@ class ItemReference(TypedDict, total=False): McpCall, ResponseCustomToolCallOutputParam, ResponseCustomToolCallParam, + CompactionTrigger, ItemReference, ] From a21700a2cd510cb9e6c88065ac8e942d4c041aa8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 18:55:17 +0000 Subject: [PATCH 05/12] feat(api): manual updates Update specs --- .stats.yml | 6 +- api.md | 34 ++ .../admin/organization/projects/__init__.py | 28 ++ .../projects/hosted_tool_permissions.py | 307 +++++++++++ .../projects/model_permissions.py | 370 ++++++++++++++ .../admin/organization/projects/projects.py | 64 +++ .../resources/admin/organization/usage.py | 384 ++++++++++++++ .../types/admin/organization/__init__.py | 4 + .../admin/organization/projects/__init__.py | 5 + .../hosted_tool_permission_update_params.py | 60 +++ .../model_permission_update_params.py | 17 + .../project_hosted_tool_permissions.py | 59 +++ .../projects/project_model_permissions.py | 23 + .../project_model_permissions_deleted.py | 17 + .../usage_file_search_calls_params.py | 57 +++ .../usage_file_search_calls_response.py | 475 ++++++++++++++++++ .../usage_web_search_calls_params.py | 60 +++ .../usage_web_search_calls_response.py | 475 ++++++++++++++++++ .../projects/test_hosted_tool_permissions.py | 200 ++++++++ .../projects/test_model_permissions.py | 271 ++++++++++ .../admin/organization/test_usage.py | 192 +++++++ 21 files changed, 3105 insertions(+), 3 deletions(-) create mode 100644 src/openai/resources/admin/organization/projects/hosted_tool_permissions.py create mode 100644 src/openai/resources/admin/organization/projects/model_permissions.py create mode 100644 src/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py create mode 100644 src/openai/types/admin/organization/projects/model_permission_update_params.py create mode 100644 src/openai/types/admin/organization/projects/project_hosted_tool_permissions.py create mode 100644 src/openai/types/admin/organization/projects/project_model_permissions.py create mode 100644 src/openai/types/admin/organization/projects/project_model_permissions_deleted.py create mode 100644 src/openai/types/admin/organization/usage_file_search_calls_params.py create mode 100644 src/openai/types/admin/organization/usage_file_search_calls_response.py create mode 100644 src/openai/types/admin/organization/usage_web_search_calls_params.py create mode 100644 src/openai/types/admin/organization/usage_web_search_calls_response.py create mode 100644 tests/api_resources/admin/organization/projects/test_hosted_tool_permissions.py create mode 100644 tests/api_resources/admin/organization/projects/test_model_permissions.py diff --git a/.stats.yml b/.stats.yml index f805dd3c95..60770c0e7a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-fcfdf0bb920ca15a6d01d1cfe988accb7559b2d991b6e2ad6b25cfae81ac2ae9.yml +configured_endpoints: 240 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-d7d5adb20c516e7aca6e35ed9f58aba0bc14dd5aa0096fd980061c4a5ab406e3.yml openapi_spec_hash: 72ee7d4fe582e75d16e67b6c97881fed -config_hash: 70ff4da62a6a6ef9f91d566c38b7bddf +config_hash: af72288617facaeed4d014024a9c946d diff --git a/api.md b/api.md index e04636937c..ab2f3f75a2 100644 --- a/api.md +++ b/api.md @@ -754,9 +754,11 @@ from openai.types.admin.organization import ( UsageCompletionsResponse, UsageCostsResponse, UsageEmbeddingsResponse, + UsageFileSearchCallsResponse, UsageImagesResponse, UsageModerationsResponse, UsageVectorStoresResponse, + UsageWebSearchCallsResponse, ) ``` @@ -768,9 +770,11 @@ Methods: - client.admin.organization.usage.completions(\*\*params) -> UsageCompletionsResponse - client.admin.organization.usage.costs(\*\*params) -> UsageCostsResponse - client.admin.organization.usage.embeddings(\*\*params) -> UsageEmbeddingsResponse +- client.admin.organization.usage.file_search_calls(\*\*params) -> UsageFileSearchCallsResponse - client.admin.organization.usage.images(\*\*params) -> UsageImagesResponse - client.admin.organization.usage.moderations(\*\*params) -> UsageModerationsResponse - client.admin.organization.usage.vector_stores(\*\*params) -> UsageVectorStoresResponse +- client.admin.organization.usage.web_search_calls(\*\*params) -> UsageWebSearchCallsResponse ### Invites @@ -1006,6 +1010,36 @@ Methods: - client.admin.organization.projects.rate_limits.list_rate_limits(project_id, \*\*params) -> SyncConversationCursorPage[ProjectRateLimit] - client.admin.organization.projects.rate_limits.update_rate_limit(rate_limit_id, \*, project_id, \*\*params) -> ProjectRateLimit +#### ModelPermissions + +Types: + +```python +from openai.types.admin.organization.projects import ( + ProjectModelPermissions, + ProjectModelPermissionsDeleted, +) +``` + +Methods: + +- client.admin.organization.projects.model_permissions.retrieve(project_id) -> ProjectModelPermissions +- client.admin.organization.projects.model_permissions.update(project_id, \*\*params) -> ProjectModelPermissions +- client.admin.organization.projects.model_permissions.delete(project_id) -> ProjectModelPermissionsDeleted + +#### HostedToolPermissions + +Types: + +```python +from openai.types.admin.organization.projects import ProjectHostedToolPermissions +``` + +Methods: + +- client.admin.organization.projects.hosted_tool_permissions.retrieve(project_id) -> ProjectHostedToolPermissions +- client.admin.organization.projects.hosted_tool_permissions.update(project_id, \*\*params) -> ProjectHostedToolPermissions + #### Groups Types: diff --git a/src/openai/resources/admin/organization/projects/__init__.py b/src/openai/resources/admin/organization/projects/__init__.py index a64326bfa9..7f77863a2d 100644 --- a/src/openai/resources/admin/organization/projects/__init__.py +++ b/src/openai/resources/admin/organization/projects/__init__.py @@ -64,6 +64,22 @@ ServiceAccountsWithStreamingResponse, AsyncServiceAccountsWithStreamingResponse, ) +from .model_permissions import ( + ModelPermissions, + AsyncModelPermissions, + ModelPermissionsWithRawResponse, + AsyncModelPermissionsWithRawResponse, + ModelPermissionsWithStreamingResponse, + AsyncModelPermissionsWithStreamingResponse, +) +from .hosted_tool_permissions import ( + HostedToolPermissions, + AsyncHostedToolPermissions, + HostedToolPermissionsWithRawResponse, + AsyncHostedToolPermissionsWithRawResponse, + HostedToolPermissionsWithStreamingResponse, + AsyncHostedToolPermissionsWithStreamingResponse, +) __all__ = [ "Users", @@ -90,6 +106,18 @@ "AsyncRateLimitsWithRawResponse", "RateLimitsWithStreamingResponse", "AsyncRateLimitsWithStreamingResponse", + "ModelPermissions", + "AsyncModelPermissions", + "ModelPermissionsWithRawResponse", + "AsyncModelPermissionsWithRawResponse", + "ModelPermissionsWithStreamingResponse", + "AsyncModelPermissionsWithStreamingResponse", + "HostedToolPermissions", + "AsyncHostedToolPermissions", + "HostedToolPermissionsWithRawResponse", + "AsyncHostedToolPermissionsWithRawResponse", + "HostedToolPermissionsWithStreamingResponse", + "AsyncHostedToolPermissionsWithStreamingResponse", "Groups", "AsyncGroups", "GroupsWithRawResponse", diff --git a/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py b/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py new file mode 100644 index 0000000000..27bcee5f5e --- /dev/null +++ b/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py @@ -0,0 +1,307 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._base_client import make_request_options +from .....types.admin.organization.projects import hosted_tool_permission_update_params +from .....types.admin.organization.projects.project_hosted_tool_permissions import ProjectHostedToolPermissions + +__all__ = ["HostedToolPermissions", "AsyncHostedToolPermissions"] + + +class HostedToolPermissions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> HostedToolPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return HostedToolPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> HostedToolPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return HostedToolPermissionsWithStreamingResponse(self) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectHostedToolPermissions: + """ + Returns hosted tool permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}/hosted_tool_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectHostedToolPermissions, + ) + + def update( + self, + project_id: str, + *, + code_interpreter: Optional[hosted_tool_permission_update_params.CodeInterpreter] | Omit = omit, + file_search: Optional[hosted_tool_permission_update_params.FileSearch] | Omit = omit, + image_generation: Optional[hosted_tool_permission_update_params.ImageGeneration] | Omit = omit, + mcp: Optional[hosted_tool_permission_update_params.Mcp] | Omit = omit, + web_search: Optional[hosted_tool_permission_update_params.WebSearch] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectHostedToolPermissions: + """ + Updates hosted tool permissions for a project. + + Args: + code_interpreter: The code interpreter permission update. + + file_search: The file search permission update. + + image_generation: The image generation permission update. + + mcp: The MCP permission update. + + web_search: The web search permission update. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/hosted_tool_permissions", project_id=project_id), + body=maybe_transform( + { + "code_interpreter": code_interpreter, + "file_search": file_search, + "image_generation": image_generation, + "mcp": mcp, + "web_search": web_search, + }, + hosted_tool_permission_update_params.HostedToolPermissionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectHostedToolPermissions, + ) + + +class AsyncHostedToolPermissions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncHostedToolPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncHostedToolPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncHostedToolPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncHostedToolPermissionsWithStreamingResponse(self) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectHostedToolPermissions: + """ + Returns hosted tool permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}/hosted_tool_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectHostedToolPermissions, + ) + + async def update( + self, + project_id: str, + *, + code_interpreter: Optional[hosted_tool_permission_update_params.CodeInterpreter] | Omit = omit, + file_search: Optional[hosted_tool_permission_update_params.FileSearch] | Omit = omit, + image_generation: Optional[hosted_tool_permission_update_params.ImageGeneration] | Omit = omit, + mcp: Optional[hosted_tool_permission_update_params.Mcp] | Omit = omit, + web_search: Optional[hosted_tool_permission_update_params.WebSearch] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectHostedToolPermissions: + """ + Updates hosted tool permissions for a project. + + Args: + code_interpreter: The code interpreter permission update. + + file_search: The file search permission update. + + image_generation: The image generation permission update. + + mcp: The MCP permission update. + + web_search: The web search permission update. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/hosted_tool_permissions", project_id=project_id), + body=await async_maybe_transform( + { + "code_interpreter": code_interpreter, + "file_search": file_search, + "image_generation": image_generation, + "mcp": mcp, + "web_search": web_search, + }, + hosted_tool_permission_update_params.HostedToolPermissionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectHostedToolPermissions, + ) + + +class HostedToolPermissionsWithRawResponse: + def __init__(self, hosted_tool_permissions: HostedToolPermissions) -> None: + self._hosted_tool_permissions = hosted_tool_permissions + + self.retrieve = _legacy_response.to_raw_response_wrapper( + hosted_tool_permissions.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + hosted_tool_permissions.update, + ) + + +class AsyncHostedToolPermissionsWithRawResponse: + def __init__(self, hosted_tool_permissions: AsyncHostedToolPermissions) -> None: + self._hosted_tool_permissions = hosted_tool_permissions + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + hosted_tool_permissions.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + hosted_tool_permissions.update, + ) + + +class HostedToolPermissionsWithStreamingResponse: + def __init__(self, hosted_tool_permissions: HostedToolPermissions) -> None: + self._hosted_tool_permissions = hosted_tool_permissions + + self.retrieve = to_streamed_response_wrapper( + hosted_tool_permissions.retrieve, + ) + self.update = to_streamed_response_wrapper( + hosted_tool_permissions.update, + ) + + +class AsyncHostedToolPermissionsWithStreamingResponse: + def __init__(self, hosted_tool_permissions: AsyncHostedToolPermissions) -> None: + self._hosted_tool_permissions = hosted_tool_permissions + + self.retrieve = async_to_streamed_response_wrapper( + hosted_tool_permissions.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + hosted_tool_permissions.update, + ) diff --git a/src/openai/resources/admin/organization/projects/model_permissions.py b/src/openai/resources/admin/organization/projects/model_permissions.py new file mode 100644 index 0000000000..157b4a4f72 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/model_permissions.py @@ -0,0 +1,370 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Query, Headers, NotGiven, SequenceNotStr, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._base_client import make_request_options +from .....types.admin.organization.projects import model_permission_update_params +from .....types.admin.organization.projects.project_model_permissions import ProjectModelPermissions +from .....types.admin.organization.projects.project_model_permissions_deleted import ProjectModelPermissionsDeleted + +__all__ = ["ModelPermissions", "AsyncModelPermissions"] + + +class ModelPermissions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ModelPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ModelPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ModelPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ModelPermissionsWithStreamingResponse(self) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissions: + """ + Returns model permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissions, + ) + + def update( + self, + project_id: str, + *, + mode: Literal["allow_list", "deny_list"], + model_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissions: + """ + Updates model permissions for a project. + + Args: + mode: The model permissions mode to apply. + + model_ids: The model IDs included in this permissions policy. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + body=maybe_transform( + { + "mode": mode, + "model_ids": model_ids, + }, + model_permission_update_params.ModelPermissionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissions, + ) + + def delete( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissionsDeleted: + """ + Deletes model permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._delete( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissionsDeleted, + ) + + +class AsyncModelPermissions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncModelPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncModelPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncModelPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncModelPermissionsWithStreamingResponse(self) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissions: + """ + Returns model permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissions, + ) + + async def update( + self, + project_id: str, + *, + mode: Literal["allow_list", "deny_list"], + model_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissions: + """ + Updates model permissions for a project. + + Args: + mode: The model permissions mode to apply. + + model_ids: The model IDs included in this permissions policy. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + body=await async_maybe_transform( + { + "mode": mode, + "model_ids": model_ids, + }, + model_permission_update_params.ModelPermissionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissions, + ) + + async def delete( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissionsDeleted: + """ + Deletes model permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._delete( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissionsDeleted, + ) + + +class ModelPermissionsWithRawResponse: + def __init__(self, model_permissions: ModelPermissions) -> None: + self._model_permissions = model_permissions + + self.retrieve = _legacy_response.to_raw_response_wrapper( + model_permissions.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + model_permissions.update, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + model_permissions.delete, + ) + + +class AsyncModelPermissionsWithRawResponse: + def __init__(self, model_permissions: AsyncModelPermissions) -> None: + self._model_permissions = model_permissions + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + model_permissions.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + model_permissions.update, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + model_permissions.delete, + ) + + +class ModelPermissionsWithStreamingResponse: + def __init__(self, model_permissions: ModelPermissions) -> None: + self._model_permissions = model_permissions + + self.retrieve = to_streamed_response_wrapper( + model_permissions.retrieve, + ) + self.update = to_streamed_response_wrapper( + model_permissions.update, + ) + self.delete = to_streamed_response_wrapper( + model_permissions.delete, + ) + + +class AsyncModelPermissionsWithStreamingResponse: + def __init__(self, model_permissions: AsyncModelPermissions) -> None: + self._model_permissions = model_permissions + + self.retrieve = async_to_streamed_response_wrapper( + model_permissions.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + model_permissions.update, + ) + self.delete = async_to_streamed_response_wrapper( + model_permissions.delete, + ) diff --git a/src/openai/resources/admin/organization/projects/projects.py b/src/openai/resources/admin/organization/projects/projects.py index 60d252cd46..b69e69d0dd 100644 --- a/src/openai/resources/admin/organization/projects/projects.py +++ b/src/openai/resources/admin/organization/projects/projects.py @@ -70,6 +70,22 @@ ServiceAccountsWithStreamingResponse, AsyncServiceAccountsWithStreamingResponse, ) +from .model_permissions import ( + ModelPermissions, + AsyncModelPermissions, + ModelPermissionsWithRawResponse, + AsyncModelPermissionsWithRawResponse, + ModelPermissionsWithStreamingResponse, + AsyncModelPermissionsWithStreamingResponse, +) +from .hosted_tool_permissions import ( + HostedToolPermissions, + AsyncHostedToolPermissions, + HostedToolPermissionsWithRawResponse, + AsyncHostedToolPermissionsWithRawResponse, + HostedToolPermissionsWithStreamingResponse, + AsyncHostedToolPermissionsWithStreamingResponse, +) from .....types.admin.organization import project_list_params, project_create_params, project_update_params from .....types.admin.organization.project import Project @@ -93,6 +109,14 @@ def api_keys(self) -> APIKeys: def rate_limits(self) -> RateLimits: return RateLimits(self._client) + @cached_property + def model_permissions(self) -> ModelPermissions: + return ModelPermissions(self._client) + + @cached_property + def hosted_tool_permissions(self) -> HostedToolPermissions: + return HostedToolPermissions(self._client) + @cached_property def groups(self) -> Groups: return Groups(self._client) @@ -386,6 +410,14 @@ def api_keys(self) -> AsyncAPIKeys: def rate_limits(self) -> AsyncRateLimits: return AsyncRateLimits(self._client) + @cached_property + def model_permissions(self) -> AsyncModelPermissions: + return AsyncModelPermissions(self._client) + + @cached_property + def hosted_tool_permissions(self) -> AsyncHostedToolPermissions: + return AsyncHostedToolPermissions(self._client) + @cached_property def groups(self) -> AsyncGroups: return AsyncGroups(self._client) @@ -698,6 +730,14 @@ def api_keys(self) -> APIKeysWithRawResponse: def rate_limits(self) -> RateLimitsWithRawResponse: return RateLimitsWithRawResponse(self._projects.rate_limits) + @cached_property + def model_permissions(self) -> ModelPermissionsWithRawResponse: + return ModelPermissionsWithRawResponse(self._projects.model_permissions) + + @cached_property + def hosted_tool_permissions(self) -> HostedToolPermissionsWithRawResponse: + return HostedToolPermissionsWithRawResponse(self._projects.hosted_tool_permissions) + @cached_property def groups(self) -> GroupsWithRawResponse: return GroupsWithRawResponse(self._projects.groups) @@ -747,6 +787,14 @@ def api_keys(self) -> AsyncAPIKeysWithRawResponse: def rate_limits(self) -> AsyncRateLimitsWithRawResponse: return AsyncRateLimitsWithRawResponse(self._projects.rate_limits) + @cached_property + def model_permissions(self) -> AsyncModelPermissionsWithRawResponse: + return AsyncModelPermissionsWithRawResponse(self._projects.model_permissions) + + @cached_property + def hosted_tool_permissions(self) -> AsyncHostedToolPermissionsWithRawResponse: + return AsyncHostedToolPermissionsWithRawResponse(self._projects.hosted_tool_permissions) + @cached_property def groups(self) -> AsyncGroupsWithRawResponse: return AsyncGroupsWithRawResponse(self._projects.groups) @@ -796,6 +844,14 @@ def api_keys(self) -> APIKeysWithStreamingResponse: def rate_limits(self) -> RateLimitsWithStreamingResponse: return RateLimitsWithStreamingResponse(self._projects.rate_limits) + @cached_property + def model_permissions(self) -> ModelPermissionsWithStreamingResponse: + return ModelPermissionsWithStreamingResponse(self._projects.model_permissions) + + @cached_property + def hosted_tool_permissions(self) -> HostedToolPermissionsWithStreamingResponse: + return HostedToolPermissionsWithStreamingResponse(self._projects.hosted_tool_permissions) + @cached_property def groups(self) -> GroupsWithStreamingResponse: return GroupsWithStreamingResponse(self._projects.groups) @@ -845,6 +901,14 @@ def api_keys(self) -> AsyncAPIKeysWithStreamingResponse: def rate_limits(self) -> AsyncRateLimitsWithStreamingResponse: return AsyncRateLimitsWithStreamingResponse(self._projects.rate_limits) + @cached_property + def model_permissions(self) -> AsyncModelPermissionsWithStreamingResponse: + return AsyncModelPermissionsWithStreamingResponse(self._projects.model_permissions) + + @cached_property + def hosted_tool_permissions(self) -> AsyncHostedToolPermissionsWithStreamingResponse: + return AsyncHostedToolPermissionsWithStreamingResponse(self._projects.hosted_tool_permissions) + @cached_property def groups(self) -> AsyncGroupsWithStreamingResponse: return AsyncGroupsWithStreamingResponse(self._projects.groups) diff --git a/src/openai/resources/admin/organization/usage.py b/src/openai/resources/admin/organization/usage.py index 2725d5e884..99306c6828 100644 --- a/src/openai/resources/admin/organization/usage.py +++ b/src/openai/resources/admin/organization/usage.py @@ -22,6 +22,8 @@ usage_moderations_params, usage_vector_stores_params, usage_audio_speeches_params, + usage_web_search_calls_params, + usage_file_search_calls_params, usage_audio_transcriptions_params, usage_code_interpreter_sessions_params, ) @@ -32,6 +34,8 @@ from ....types.admin.organization.usage_moderations_response import UsageModerationsResponse from ....types.admin.organization.usage_vector_stores_response import UsageVectorStoresResponse from ....types.admin.organization.usage_audio_speeches_response import UsageAudioSpeechesResponse +from ....types.admin.organization.usage_web_search_calls_response import UsageWebSearchCallsResponse +from ....types.admin.organization.usage_file_search_calls_response import UsageFileSearchCallsResponse from ....types.admin.organization.usage_audio_transcriptions_response import UsageAudioTranscriptionsResponse from ....types.admin.organization.usage_code_interpreter_sessions_response import UsageCodeInterpreterSessionsResponse @@ -557,6 +561,93 @@ def embeddings( cast_to=UsageEmbeddingsResponse, ) + def file_search_calls( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "vector_store_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + vector_store_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageFileSearchCallsResponse: + """ + Get file search calls usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `vector_store_id` or any combination of + them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + vector_store_ids: Return only usage for these vector stores. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/file_search_calls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + "vector_store_ids": vector_store_ids, + }, + usage_file_search_calls_params.UsageFileSearchCallsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageFileSearchCallsResponse, + ) + def images( self, *, @@ -814,6 +905,97 @@ def vector_stores( cast_to=UsageVectorStoresResponse, ) + def web_search_calls( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + context_levels: List[Literal["low", "medium", "high"]] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "context_level"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageWebSearchCallsResponse: + """ + Get web search calls usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + context_levels: Return only web search usage for these context levels. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `context_level` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/web_search_calls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "context_levels": context_levels, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_web_search_calls_params.UsageWebSearchCallsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageWebSearchCallsResponse, + ) + class AsyncUsage(AsyncAPIResource): @cached_property @@ -1334,6 +1516,93 @@ async def embeddings( cast_to=UsageEmbeddingsResponse, ) + async def file_search_calls( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "vector_store_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + vector_store_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageFileSearchCallsResponse: + """ + Get file search calls usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `vector_store_id` or any combination of + them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + vector_store_ids: Return only usage for these vector stores. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/file_search_calls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + "vector_store_ids": vector_store_ids, + }, + usage_file_search_calls_params.UsageFileSearchCallsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageFileSearchCallsResponse, + ) + async def images( self, *, @@ -1591,6 +1860,97 @@ async def vector_stores( cast_to=UsageVectorStoresResponse, ) + async def web_search_calls( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + context_levels: List[Literal["low", "medium", "high"]] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "context_level"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageWebSearchCallsResponse: + """ + Get web search calls usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + context_levels: Return only web search usage for these context levels. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `context_level` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/web_search_calls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "context_levels": context_levels, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_web_search_calls_params.UsageWebSearchCallsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageWebSearchCallsResponse, + ) + class UsageWithRawResponse: def __init__(self, usage: Usage) -> None: @@ -1614,6 +1974,9 @@ def __init__(self, usage: Usage) -> None: self.embeddings = _legacy_response.to_raw_response_wrapper( usage.embeddings, ) + self.file_search_calls = _legacy_response.to_raw_response_wrapper( + usage.file_search_calls, + ) self.images = _legacy_response.to_raw_response_wrapper( usage.images, ) @@ -1623,6 +1986,9 @@ def __init__(self, usage: Usage) -> None: self.vector_stores = _legacy_response.to_raw_response_wrapper( usage.vector_stores, ) + self.web_search_calls = _legacy_response.to_raw_response_wrapper( + usage.web_search_calls, + ) class AsyncUsageWithRawResponse: @@ -1647,6 +2013,9 @@ def __init__(self, usage: AsyncUsage) -> None: self.embeddings = _legacy_response.async_to_raw_response_wrapper( usage.embeddings, ) + self.file_search_calls = _legacy_response.async_to_raw_response_wrapper( + usage.file_search_calls, + ) self.images = _legacy_response.async_to_raw_response_wrapper( usage.images, ) @@ -1656,6 +2025,9 @@ def __init__(self, usage: AsyncUsage) -> None: self.vector_stores = _legacy_response.async_to_raw_response_wrapper( usage.vector_stores, ) + self.web_search_calls = _legacy_response.async_to_raw_response_wrapper( + usage.web_search_calls, + ) class UsageWithStreamingResponse: @@ -1680,6 +2052,9 @@ def __init__(self, usage: Usage) -> None: self.embeddings = to_streamed_response_wrapper( usage.embeddings, ) + self.file_search_calls = to_streamed_response_wrapper( + usage.file_search_calls, + ) self.images = to_streamed_response_wrapper( usage.images, ) @@ -1689,6 +2064,9 @@ def __init__(self, usage: Usage) -> None: self.vector_stores = to_streamed_response_wrapper( usage.vector_stores, ) + self.web_search_calls = to_streamed_response_wrapper( + usage.web_search_calls, + ) class AsyncUsageWithStreamingResponse: @@ -1713,6 +2091,9 @@ def __init__(self, usage: AsyncUsage) -> None: self.embeddings = async_to_streamed_response_wrapper( usage.embeddings, ) + self.file_search_calls = async_to_streamed_response_wrapper( + usage.file_search_calls, + ) self.images = async_to_streamed_response_wrapper( usage.images, ) @@ -1722,3 +2103,6 @@ def __init__(self, usage: AsyncUsage) -> None: self.vector_stores = async_to_streamed_response_wrapper( usage.vector_stores, ) + self.web_search_calls = async_to_streamed_response_wrapper( + usage.web_search_calls, + ) diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py index dbb11795e5..ebb62b0eb8 100644 --- a/src/openai/types/admin/organization/__init__.py +++ b/src/openai/types/admin/organization/__init__.py @@ -56,7 +56,11 @@ from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams from .usage_audio_speeches_response import UsageAudioSpeechesResponse as UsageAudioSpeechesResponse +from .usage_web_search_calls_params import UsageWebSearchCallsParams as UsageWebSearchCallsParams +from .usage_file_search_calls_params import UsageFileSearchCallsParams as UsageFileSearchCallsParams from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse +from .usage_web_search_calls_response import UsageWebSearchCallsResponse as UsageWebSearchCallsResponse +from .usage_file_search_calls_response import UsageFileSearchCallsResponse as UsageFileSearchCallsResponse from .usage_audio_transcriptions_params import UsageAudioTranscriptionsParams as UsageAudioTranscriptionsParams from .usage_audio_transcriptions_response import UsageAudioTranscriptionsResponse as UsageAudioTranscriptionsResponse from .usage_code_interpreter_sessions_params import ( diff --git a/src/openai/types/admin/organization/projects/__init__.py b/src/openai/types/admin/organization/projects/__init__.py index ea627ce7d6..1b78bae7a8 100644 --- a/src/openai/types/admin/organization/projects/__init__.py +++ b/src/openai/types/admin/organization/projects/__init__.py @@ -22,13 +22,18 @@ from .certificate_list_params import CertificateListParams as CertificateListParams from .project_service_account import ProjectServiceAccount as ProjectServiceAccount from .certificate_list_response import CertificateListResponse as CertificateListResponse +from .project_model_permissions import ProjectModelPermissions as ProjectModelPermissions from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams from .service_account_list_params import ServiceAccountListParams as ServiceAccountListParams from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams from .service_account_create_params import ServiceAccountCreateParams as ServiceAccountCreateParams +from .model_permission_update_params import ModelPermissionUpdateParams as ModelPermissionUpdateParams from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse +from .project_hosted_tool_permissions import ProjectHostedToolPermissions as ProjectHostedToolPermissions from .service_account_create_response import ServiceAccountCreateResponse as ServiceAccountCreateResponse from .service_account_delete_response import ServiceAccountDeleteResponse as ServiceAccountDeleteResponse +from .project_model_permissions_deleted import ProjectModelPermissionsDeleted as ProjectModelPermissionsDeleted from .rate_limit_list_rate_limits_params import RateLimitListRateLimitsParams as RateLimitListRateLimitsParams from .rate_limit_update_rate_limit_params import RateLimitUpdateRateLimitParams as RateLimitUpdateRateLimitParams +from .hosted_tool_permission_update_params import HostedToolPermissionUpdateParams as HostedToolPermissionUpdateParams diff --git a/src/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py b/src/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py new file mode 100644 index 0000000000..e14f03741f --- /dev/null +++ b/src/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["HostedToolPermissionUpdateParams", "CodeInterpreter", "FileSearch", "ImageGeneration", "Mcp", "WebSearch"] + + +class HostedToolPermissionUpdateParams(TypedDict, total=False): + code_interpreter: Optional[CodeInterpreter] + """The code interpreter permission update.""" + + file_search: Optional[FileSearch] + """The file search permission update.""" + + image_generation: Optional[ImageGeneration] + """The image generation permission update.""" + + mcp: Optional[Mcp] + """The MCP permission update.""" + + web_search: Optional[WebSearch] + """The web search permission update.""" + + +class CodeInterpreter(TypedDict, total=False): + """The code interpreter permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" + + +class FileSearch(TypedDict, total=False): + """The file search permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" + + +class ImageGeneration(TypedDict, total=False): + """The image generation permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" + + +class Mcp(TypedDict, total=False): + """The MCP permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" + + +class WebSearch(TypedDict, total=False): + """The web search permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" diff --git a/src/openai/types/admin/organization/projects/model_permission_update_params.py b/src/openai/types/admin/organization/projects/model_permission_update_params.py new file mode 100644 index 0000000000..bf7cb1d56c --- /dev/null +++ b/src/openai/types/admin/organization/projects/model_permission_update_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["ModelPermissionUpdateParams"] + + +class ModelPermissionUpdateParams(TypedDict, total=False): + mode: Required[Literal["allow_list", "deny_list"]] + """The model permissions mode to apply.""" + + model_ids: Required[SequenceNotStr[str]] + """The model IDs included in this permissions policy.""" diff --git a/src/openai/types/admin/organization/projects/project_hosted_tool_permissions.py b/src/openai/types/admin/organization/projects/project_hosted_tool_permissions.py new file mode 100644 index 0000000000..e89ed18af0 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_hosted_tool_permissions.py @@ -0,0 +1,59 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ....._models import BaseModel + +__all__ = ["ProjectHostedToolPermissions", "CodeInterpreter", "FileSearch", "ImageGeneration", "Mcp", "WebSearch"] + + +class CodeInterpreter(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class FileSearch(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class ImageGeneration(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class Mcp(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class WebSearch(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class ProjectHostedToolPermissions(BaseModel): + """Represents hosted tool permissions for a project.""" + + code_interpreter: CodeInterpreter + """Permission state for a single hosted tool on a project.""" + + file_search: FileSearch + """Permission state for a single hosted tool on a project.""" + + image_generation: ImageGeneration + """Permission state for a single hosted tool on a project.""" + + mcp: Mcp + """Permission state for a single hosted tool on a project.""" + + web_search: WebSearch + """Permission state for a single hosted tool on a project.""" diff --git a/src/openai/types/admin/organization/projects/project_model_permissions.py b/src/openai/types/admin/organization/projects/project_model_permissions.py new file mode 100644 index 0000000000..597ae4e257 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_model_permissions.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ....._models import BaseModel + +__all__ = ["ProjectModelPermissions"] + + +class ProjectModelPermissions(BaseModel): + """Represents the model allowlist or denylist policy for a project.""" + + mode: Literal["allow_list", "deny_list"] + """Whether the project uses an allowlist or a denylist.""" + + api_model_ids: List[str] = FieldInfo(alias="model_ids") + """The model IDs included in the model permissions policy.""" + + object: Literal["project.model_permissions"] + """The object type, which is always `project.model_permissions`.""" diff --git a/src/openai/types/admin/organization/projects/project_model_permissions_deleted.py b/src/openai/types/admin/organization/projects/project_model_permissions_deleted.py new file mode 100644 index 0000000000..cb3972dce8 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_model_permissions_deleted.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectModelPermissionsDeleted"] + + +class ProjectModelPermissionsDeleted(BaseModel): + """Confirmation payload returned after deleting project model permissions.""" + + deleted: bool + """Whether the project model permissions were deleted.""" + + object: Literal["project.model_permissions.deleted"] + """The object type, which is always `project.model_permissions.deleted`.""" diff --git a/src/openai/types/admin/organization/usage_file_search_calls_params.py b/src/openai/types/admin/organization/usage_file_search_calls_params.py new file mode 100644 index 0000000000..914d568c33 --- /dev/null +++ b/src/openai/types/admin/organization/usage_file_search_calls_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageFileSearchCallsParams"] + + +class UsageFileSearchCallsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "vector_store_id"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `vector_store_id` + or any combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" + + vector_store_ids: SequenceNotStr[str] + """Return only usage for these vector stores.""" 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 new file mode 100644 index 0000000000..0e093b8346 --- /dev/null +++ b/src/openai/types/admin/organization/usage_file_search_calls_response.py @@ -0,0 +1,475 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageFileSearchCallsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageFileSearchCallsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_web_search_calls_params.py b/src/openai/types/admin/organization/usage_web_search_calls_params.py new file mode 100644 index 0000000000..544060ade4 --- /dev/null +++ b/src/openai/types/admin/organization/usage_web_search_calls_params.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageWebSearchCallsParams"] + + +class UsageWebSearchCallsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + context_levels: List[Literal["low", "medium", "high"]] + """Return only web search usage for these context levels.""" + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "context_level"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model`, + `context_level` or any combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" 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 new file mode 100644 index 0000000000..b6fcd4ac6c --- /dev/null +++ b/src/openai/types/admin/organization/usage_web_search_calls_response.py @@ -0,0 +1,475 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageWebSearchCallsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageWebSearchCallsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/tests/api_resources/admin/organization/projects/test_hosted_tool_permissions.py b/tests/api_resources/admin/organization/projects/test_hosted_tool_permissions.py new file mode 100644 index 0000000000..d7d2486874 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_hosted_tool_permissions.py @@ -0,0 +1,200 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.admin.organization.projects import ProjectHostedToolPermissions + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestHostedToolPermissions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + hosted_tool_permission = client.admin.organization.projects.hosted_tool_permissions.retrieve( + "project_id", + ) + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.hosted_tool_permissions.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + hosted_tool_permission = response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.hosted_tool_permissions.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + hosted_tool_permission = response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.hosted_tool_permissions.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + hosted_tool_permission = client.admin.organization.projects.hosted_tool_permissions.update( + project_id="project_id", + ) + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + hosted_tool_permission = client.admin.organization.projects.hosted_tool_permissions.update( + project_id="project_id", + code_interpreter={"enabled": True}, + file_search={"enabled": True}, + image_generation={"enabled": True}, + mcp={"enabled": True}, + web_search={"enabled": True}, + ) + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.hosted_tool_permissions.with_raw_response.update( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + hosted_tool_permission = response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.hosted_tool_permissions.with_streaming_response.update( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + hosted_tool_permission = response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.hosted_tool_permissions.with_raw_response.update( + project_id="", + ) + + +class TestAsyncHostedToolPermissions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + hosted_tool_permission = await async_client.admin.organization.projects.hosted_tool_permissions.retrieve( + "project_id", + ) + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.hosted_tool_permissions.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + hosted_tool_permission = response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.hosted_tool_permissions.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + hosted_tool_permission = await response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.hosted_tool_permissions.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + hosted_tool_permission = await async_client.admin.organization.projects.hosted_tool_permissions.update( + project_id="project_id", + ) + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + hosted_tool_permission = await async_client.admin.organization.projects.hosted_tool_permissions.update( + project_id="project_id", + code_interpreter={"enabled": True}, + file_search={"enabled": True}, + image_generation={"enabled": True}, + mcp={"enabled": True}, + web_search={"enabled": True}, + ) + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.hosted_tool_permissions.with_raw_response.update( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + hosted_tool_permission = response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.hosted_tool_permissions.with_streaming_response.update( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + hosted_tool_permission = await response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.hosted_tool_permissions.with_raw_response.update( + project_id="", + ) diff --git a/tests/api_resources/admin/organization/projects/test_model_permissions.py b/tests/api_resources/admin/organization/projects/test_model_permissions.py new file mode 100644 index 0000000000..1749a70fa5 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_model_permissions.py @@ -0,0 +1,271 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.admin.organization.projects import ( + ProjectModelPermissions, + ProjectModelPermissionsDeleted, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestModelPermissions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + model_permission = client.admin.organization.projects.model_permissions.retrieve( + "project_id", + ) + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.model_permissions.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model_permission = response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.model_permissions.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model_permission = response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.model_permissions.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + model_permission = client.admin.organization.projects.model_permissions.update( + project_id="project_id", + mode="allow_list", + model_ids=["string"], + ) + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.model_permissions.with_raw_response.update( + project_id="project_id", + mode="allow_list", + model_ids=["string"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model_permission = response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.model_permissions.with_streaming_response.update( + project_id="project_id", + mode="allow_list", + model_ids=["string"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model_permission = response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.model_permissions.with_raw_response.update( + project_id="", + mode="allow_list", + model_ids=["string"], + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + model_permission = client.admin.organization.projects.model_permissions.delete( + "project_id", + ) + assert_matches_type(ProjectModelPermissionsDeleted, model_permission, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.projects.model_permissions.with_raw_response.delete( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model_permission = response.parse() + assert_matches_type(ProjectModelPermissionsDeleted, model_permission, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.projects.model_permissions.with_streaming_response.delete( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model_permission = response.parse() + assert_matches_type(ProjectModelPermissionsDeleted, model_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.model_permissions.with_raw_response.delete( + "", + ) + + +class TestAsyncModelPermissions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + model_permission = await async_client.admin.organization.projects.model_permissions.retrieve( + "project_id", + ) + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.model_permissions.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model_permission = response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.model_permissions.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model_permission = await response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.model_permissions.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + model_permission = await async_client.admin.organization.projects.model_permissions.update( + project_id="project_id", + mode="allow_list", + model_ids=["string"], + ) + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.model_permissions.with_raw_response.update( + project_id="project_id", + mode="allow_list", + model_ids=["string"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model_permission = response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.model_permissions.with_streaming_response.update( + project_id="project_id", + mode="allow_list", + model_ids=["string"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model_permission = await response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.model_permissions.with_raw_response.update( + project_id="", + mode="allow_list", + model_ids=["string"], + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + model_permission = await async_client.admin.organization.projects.model_permissions.delete( + "project_id", + ) + assert_matches_type(ProjectModelPermissionsDeleted, model_permission, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.model_permissions.with_raw_response.delete( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model_permission = response.parse() + assert_matches_type(ProjectModelPermissionsDeleted, model_permission, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.model_permissions.with_streaming_response.delete( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model_permission = await response.parse() + assert_matches_type(ProjectModelPermissionsDeleted, model_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.model_permissions.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/admin/organization/test_usage.py b/tests/api_resources/admin/organization/test_usage.py index f9c4a8e2c5..b7aef88dcb 100644 --- a/tests/api_resources/admin/organization/test_usage.py +++ b/tests/api_resources/admin/organization/test_usage.py @@ -17,6 +17,8 @@ UsageModerationsResponse, UsageVectorStoresResponse, UsageAudioSpeechesResponse, + UsageWebSearchCallsResponse, + UsageFileSearchCallsResponse, UsageAudioTranscriptionsResponse, UsageCodeInterpreterSessionsResponse, ) @@ -305,6 +307,53 @@ def test_streaming_response_embeddings(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_file_search_calls(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.file_search_calls( + start_time=0, + ) + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + @parametrize + def test_method_file_search_calls_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.file_search_calls( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + page="page", + project_ids=["string"], + user_ids=["string"], + vector_store_ids=["string"], + ) + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_file_search_calls(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.file_search_calls( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_file_search_calls(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.file_search_calls( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_images(self, client: OpenAI) -> None: usage = client.admin.organization.usage.images( @@ -445,6 +494,54 @@ def test_streaming_response_vector_stores(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_web_search_calls(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.web_search_calls( + start_time=0, + ) + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + @parametrize + def test_method_web_search_calls_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.web_search_calls( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + context_levels=["low"], + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_web_search_calls(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.web_search_calls( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_web_search_calls(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.web_search_calls( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncUsage: parametrize = pytest.mark.parametrize( @@ -729,6 +826,53 @@ async def test_streaming_response_embeddings(self, async_client: AsyncOpenAI) -> assert cast(Any, response.is_closed) is True + @parametrize + async def test_method_file_search_calls(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.file_search_calls( + start_time=0, + ) + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + @parametrize + async def test_method_file_search_calls_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.file_search_calls( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + page="page", + project_ids=["string"], + user_ids=["string"], + vector_store_ids=["string"], + ) + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_file_search_calls(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.file_search_calls( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_file_search_calls(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.file_search_calls( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_images(self, async_client: AsyncOpenAI) -> None: usage = await async_client.admin.organization.usage.images( @@ -868,3 +1012,51 @@ async def test_streaming_response_vector_stores(self, async_client: AsyncOpenAI) assert_matches_type(UsageVectorStoresResponse, usage, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_web_search_calls(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.web_search_calls( + start_time=0, + ) + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + @parametrize + async def test_method_web_search_calls_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.web_search_calls( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + context_levels=["low"], + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_web_search_calls(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.web_search_calls( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_web_search_calls(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.web_search_calls( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True From ee101520d49e22c09cf8096f8cbb3848ea58a1f9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 19:53:09 +0000 Subject: [PATCH 06/12] chore(api): docs updates --- .stats.yml | 6 +++--- src/openai/types/responses/response_function_web_search.py | 2 +- .../types/responses/response_function_web_search_param.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 60770c0e7a..fb3f316cec 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 240 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-d7d5adb20c516e7aca6e35ed9f58aba0bc14dd5aa0096fd980061c4a5ab406e3.yml -openapi_spec_hash: 72ee7d4fe582e75d16e67b6c97881fed -config_hash: af72288617facaeed4d014024a9c946d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-66f110677cd924205b0a4e2a1f567c8b56349ee9658bd0ce86561a413a2aabfe.yml +openapi_spec_hash: ab8f320a7a6b2d30d5f1665b8a8bf84e +config_hash: 425042cc0e99cf31866f6c98fef2be27 diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py index de6001e146..d3d4afbee3 100644 --- a/src/openai/types/responses/response_function_web_search.py +++ b/src/openai/types/responses/response_function_web_search.py @@ -46,7 +46,7 @@ class ActionOpenPage(BaseModel): """Action type "open_page" - Opens a specific URL from search results.""" type: Literal["open_page"] - """The action type.""" + """The action type. Always `open_page`.""" url: Optional[str] = None """The URL opened by the model.""" diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py index 15e313b0d3..e0d50757a3 100644 --- a/src/openai/types/responses/response_function_web_search_param.py +++ b/src/openai/types/responses/response_function_web_search_param.py @@ -47,7 +47,7 @@ class ActionOpenPage(TypedDict, total=False): """Action type "open_page" - Opens a specific URL from search results.""" type: Required[Literal["open_page"]] - """The action type.""" + """The action type. Always `open_page`.""" url: Optional[str] """The URL opened by the model.""" From f62d08201eea8e08d4bb3385662f934d4adccb29 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Tue, 19 May 2026 16:33:19 -0400 Subject: [PATCH 07/12] chore: trigger release automation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 90f1c2cbd9..13b508e81f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # OpenAI Python API library + + [![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/) From bd6eea559f2996d914258a65e645981bdce3cad4 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Tue, 19 May 2026 16:41:41 -0400 Subject: [PATCH 08/12] chore: remove release automation trigger --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 13b508e81f..90f1c2cbd9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # OpenAI Python API library - - [![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/) From 2638779a5b8fffaa8fdb6eebc1d734f15d2491f8 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Tue, 19 May 2026 16:50:17 -0400 Subject: [PATCH 09/12] chore: check release PR custom code sync --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 90f1c2cbd9..8a956145c2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # OpenAI Python API library + + [![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/) From 36916ac8eeba6862dd99e419c19d3ac657ec428d Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Tue, 19 May 2026 16:55:01 -0400 Subject: [PATCH 10/12] Revert "chore: check release PR custom code sync" This reverts commit 2638779a5b8fffaa8fdb6eebc1d734f15d2491f8. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 8a956145c2..90f1c2cbd9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # OpenAI Python API library - - [![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/) From 33d1d013250053886a73d178136e6bd1b09df059 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 07:31:42 +0000 Subject: [PATCH 11/12] feat(api): api update --- .stats.yml | 8 +- api.md | 71 +++ .../resources/admin/organization/__init__.py | 28 + .../admin/organization/data_retention.py | 245 ++++++++ .../admin/organization/groups/groups.py | 86 +++ .../admin/organization/groups/roles.py | 93 +++ .../admin/organization/groups/users.py | 93 +++ .../admin/organization/organization.py | 64 ++ .../admin/organization/projects/__init__.py | 28 + .../organization/projects/data_retention.py | 283 +++++++++ .../organization/projects/groups/groups.py | 108 +++- .../organization/projects/groups/roles.py | 109 ++++ .../admin/organization/projects/projects.py | 64 ++ .../admin/organization/projects/roles.py | 92 +++ .../organization/projects/service_accounts.py | 134 +++- .../organization/projects/spend_alerts.py | 589 ++++++++++++++++++ .../organization/projects/users/roles.py | 109 ++++ .../resources/admin/organization/roles.py | 86 +++ .../admin/organization/spend_alerts.py | 553 ++++++++++++++++ .../admin/organization/users/roles.py | 93 +++ .../types/admin/organization/__init__.py | 7 + .../data_retention_update_params.py | 19 + src/openai/types/admin/organization/group.py | 4 +- .../admin/organization/groups/__init__.py | 2 + .../organization/groups/role_list_response.py | 11 +- .../groups/role_retrieve_response.py | 55 ++ .../groups/user_retrieve_response.py | 30 + .../organization_data_retention.py | 22 + .../organization/organization_spend_alert.py | 43 ++ .../organization_spend_alert_deleted.py | 20 + .../admin/organization/projects/__init__.py | 9 + .../projects/data_retention_update_params.py | 21 + .../projects/group_retrieve_params.py | 14 + .../organization/projects/groups/__init__.py | 1 + .../projects/groups/role_list_response.py | 11 +- .../projects/groups/role_retrieve_response.py | 55 ++ .../projects/project_data_retention.py | 24 + .../organization/projects/project_group.py | 2 +- .../projects/project_spend_alert.py | 43 ++ .../projects/project_spend_alert_deleted.py | 20 + .../projects/service_account_update_params.py | 17 + .../projects/spend_alert_create_params.py | 37 ++ .../projects/spend_alert_list_params.py | 29 + .../projects/spend_alert_update_params.py | 39 ++ .../organization/projects/users/__init__.py | 1 + .../projects/users/role_list_response.py | 11 +- .../projects/users/role_retrieve_response.py | 55 ++ .../organization/spend_alert_create_params.py | 37 ++ .../organization/spend_alert_list_params.py | 29 + .../organization/spend_alert_update_params.py | 37 ++ .../admin/organization/users/__init__.py | 1 + .../organization/users/role_list_response.py | 11 +- .../users/role_retrieve_response.py | 55 ++ .../responses/response_function_web_search.py | 2 +- .../response_function_web_search_param.py | 2 +- .../admin/organization/groups/test_roles.py | 97 +++ .../admin/organization/groups/test_users.py | 97 +++ .../projects/groups/test_roles.py | 121 ++++ .../projects/test_data_retention.py | 184 ++++++ .../organization/projects/test_groups.py | 114 ++++ .../admin/organization/projects/test_roles.py | 96 +++ .../projects/test_service_accounts.py | 116 ++++ .../projects/test_spend_alerts.py | 582 +++++++++++++++++ .../organization/projects/users/test_roles.py | 121 ++++ .../admin/organization/test_data_retention.py | 136 ++++ .../admin/organization/test_groups.py | 76 +++ .../admin/organization/test_roles.py | 76 +++ .../admin/organization/test_spend_alerts.py | 462 ++++++++++++++ .../admin/organization/users/test_roles.py | 97 +++ 69 files changed, 6073 insertions(+), 14 deletions(-) create mode 100644 src/openai/resources/admin/organization/data_retention.py create mode 100644 src/openai/resources/admin/organization/projects/data_retention.py create mode 100644 src/openai/resources/admin/organization/projects/spend_alerts.py create mode 100644 src/openai/resources/admin/organization/spend_alerts.py create mode 100644 src/openai/types/admin/organization/data_retention_update_params.py create mode 100644 src/openai/types/admin/organization/groups/role_retrieve_response.py create mode 100644 src/openai/types/admin/organization/groups/user_retrieve_response.py create mode 100644 src/openai/types/admin/organization/organization_data_retention.py create mode 100644 src/openai/types/admin/organization/organization_spend_alert.py create mode 100644 src/openai/types/admin/organization/organization_spend_alert_deleted.py create mode 100644 src/openai/types/admin/organization/projects/data_retention_update_params.py create mode 100644 src/openai/types/admin/organization/projects/group_retrieve_params.py create mode 100644 src/openai/types/admin/organization/projects/groups/role_retrieve_response.py create mode 100644 src/openai/types/admin/organization/projects/project_data_retention.py create mode 100644 src/openai/types/admin/organization/projects/project_spend_alert.py create mode 100644 src/openai/types/admin/organization/projects/project_spend_alert_deleted.py create mode 100644 src/openai/types/admin/organization/projects/service_account_update_params.py create mode 100644 src/openai/types/admin/organization/projects/spend_alert_create_params.py create mode 100644 src/openai/types/admin/organization/projects/spend_alert_list_params.py create mode 100644 src/openai/types/admin/organization/projects/spend_alert_update_params.py create mode 100644 src/openai/types/admin/organization/projects/users/role_retrieve_response.py create mode 100644 src/openai/types/admin/organization/spend_alert_create_params.py create mode 100644 src/openai/types/admin/organization/spend_alert_list_params.py create mode 100644 src/openai/types/admin/organization/spend_alert_update_params.py create mode 100644 src/openai/types/admin/organization/users/role_retrieve_response.py create mode 100644 tests/api_resources/admin/organization/projects/test_data_retention.py create mode 100644 tests/api_resources/admin/organization/projects/test_spend_alerts.py create mode 100644 tests/api_resources/admin/organization/test_data_retention.py create mode 100644 tests/api_resources/admin/organization/test_spend_alerts.py diff --git a/.stats.yml b/.stats.yml index fb3f316cec..b9d21daa06 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 240 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-66f110677cd924205b0a4e2a1f567c8b56349ee9658bd0ce86561a413a2aabfe.yml -openapi_spec_hash: ab8f320a7a6b2d30d5f1665b8a8bf84e -config_hash: 425042cc0e99cf31866f6c98fef2be27 +configured_endpoints: 262 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-afacc4343d0efc074c8c5667eb83570642c8b9acaa7792ca8e075c6d18ef9f3a.yml +openapi_spec_hash: a62a557c61532681963fd21e748b0eb4 +config_hash: bb69d8d0771dbac4a84fc6dca11e3ceb diff --git a/api.md b/api.md index ab2f3f75a2..10a729d995 100644 --- a/api.md +++ b/api.md @@ -813,6 +813,7 @@ Types: ```python from openai.types.admin.organization.users import ( RoleCreateResponse, + RoleRetrieveResponse, RoleListResponse, RoleDeleteResponse, ) @@ -821,6 +822,7 @@ from openai.types.admin.organization.users import ( Methods: - client.admin.organization.users.roles.create(user_id, \*\*params) -> RoleCreateResponse +- client.admin.organization.users.roles.retrieve(role_id, \*, user_id) -> RoleRetrieveResponse - client.admin.organization.users.roles.list(user_id, \*\*params) -> SyncNextCursorPage[RoleListResponse] - client.admin.organization.users.roles.delete(role_id, \*, user_id) -> RoleDeleteResponse @@ -835,6 +837,7 @@ from openai.types.admin.organization import Group, GroupUpdateResponse, GroupDel Methods: - client.admin.organization.groups.create(\*\*params) -> Group +- client.admin.organization.groups.retrieve(group_id) -> Group - client.admin.organization.groups.update(group_id, \*\*params) -> GroupUpdateResponse - client.admin.organization.groups.list(\*\*params) -> SyncNextCursorPage[Group] - client.admin.organization.groups.delete(group_id) -> GroupDeleteResponse @@ -847,6 +850,7 @@ Types: from openai.types.admin.organization.groups import ( OrganizationGroupUser, UserCreateResponse, + UserRetrieveResponse, UserDeleteResponse, ) ``` @@ -854,6 +858,7 @@ from openai.types.admin.organization.groups import ( Methods: - client.admin.organization.groups.users.create(group_id, \*\*params) -> UserCreateResponse +- client.admin.organization.groups.users.retrieve(user_id, \*, group_id) -> UserRetrieveResponse - client.admin.organization.groups.users.list(group_id, \*\*params) -> SyncNextCursorPage[OrganizationGroupUser] - client.admin.organization.groups.users.delete(user_id, \*, group_id) -> UserDeleteResponse @@ -864,6 +869,7 @@ Types: ```python from openai.types.admin.organization.groups import ( RoleCreateResponse, + RoleRetrieveResponse, RoleListResponse, RoleDeleteResponse, ) @@ -872,6 +878,7 @@ from openai.types.admin.organization.groups import ( Methods: - client.admin.organization.groups.roles.create(group_id, \*\*params) -> RoleCreateResponse +- client.admin.organization.groups.roles.retrieve(role_id, \*, group_id) -> RoleRetrieveResponse - client.admin.organization.groups.roles.list(group_id, \*\*params) -> SyncNextCursorPage[RoleListResponse] - client.admin.organization.groups.roles.delete(role_id, \*, group_id) -> RoleDeleteResponse @@ -886,10 +893,39 @@ from openai.types.admin.organization import Role, RoleDeleteResponse Methods: - client.admin.organization.roles.create(\*\*params) -> Role +- client.admin.organization.roles.retrieve(role_id) -> Role - client.admin.organization.roles.update(role_id, \*\*params) -> Role - client.admin.organization.roles.list(\*\*params) -> SyncNextCursorPage[Role] - client.admin.organization.roles.delete(role_id) -> RoleDeleteResponse +### DataRetention + +Types: + +```python +from openai.types.admin.organization import OrganizationDataRetention +``` + +Methods: + +- client.admin.organization.data_retention.retrieve() -> OrganizationDataRetention +- client.admin.organization.data_retention.update(\*\*params) -> OrganizationDataRetention + +### SpendAlerts + +Types: + +```python +from openai.types.admin.organization import OrganizationSpendAlert, OrganizationSpendAlertDeleted +``` + +Methods: + +- client.admin.organization.spend_alerts.create(\*\*params) -> OrganizationSpendAlert +- client.admin.organization.spend_alerts.update(alert_id, \*\*params) -> OrganizationSpendAlert +- client.admin.organization.spend_alerts.list(\*\*params) -> SyncConversationCursorPage[OrganizationSpendAlert] +- client.admin.organization.spend_alerts.delete(alert_id) -> OrganizationSpendAlertDeleted + ### Certificates Types: @@ -953,6 +989,7 @@ Types: ```python from openai.types.admin.organization.projects.users import ( RoleCreateResponse, + RoleRetrieveResponse, RoleListResponse, RoleDeleteResponse, ) @@ -961,6 +998,7 @@ from openai.types.admin.organization.projects.users import ( Methods: - client.admin.organization.projects.users.roles.create(user_id, \*, project_id, \*\*params) -> RoleCreateResponse +- client.admin.organization.projects.users.roles.retrieve(role_id, \*, project_id, user_id) -> RoleRetrieveResponse - client.admin.organization.projects.users.roles.list(user_id, \*, project_id, \*\*params) -> SyncNextCursorPage[RoleListResponse] - client.admin.organization.projects.users.roles.delete(role_id, \*, project_id, user_id) -> RoleDeleteResponse @@ -980,6 +1018,7 @@ Methods: - client.admin.organization.projects.service_accounts.create(project_id, \*\*params) -> ServiceAccountCreateResponse - client.admin.organization.projects.service_accounts.retrieve(service_account_id, \*, project_id) -> ProjectServiceAccount +- client.admin.organization.projects.service_accounts.update(service_account_id, \*, project_id, \*\*params) -> ProjectServiceAccount - client.admin.organization.projects.service_accounts.list(project_id, \*\*params) -> SyncConversationCursorPage[ProjectServiceAccount] - client.admin.organization.projects.service_accounts.delete(service_account_id, \*, project_id) -> ServiceAccountDeleteResponse @@ -1051,6 +1090,7 @@ from openai.types.admin.organization.projects import ProjectGroup, GroupDeleteRe Methods: - client.admin.organization.projects.groups.create(project_id, \*\*params) -> ProjectGroup +- client.admin.organization.projects.groups.retrieve(group_id, \*, project_id, \*\*params) -> ProjectGroup - client.admin.organization.projects.groups.list(project_id, \*\*params) -> SyncNextCursorPage[ProjectGroup] - client.admin.organization.projects.groups.delete(group_id, \*, project_id) -> GroupDeleteResponse @@ -1061,6 +1101,7 @@ Types: ```python from openai.types.admin.organization.projects.groups import ( RoleCreateResponse, + RoleRetrieveResponse, RoleListResponse, RoleDeleteResponse, ) @@ -1069,6 +1110,7 @@ from openai.types.admin.organization.projects.groups import ( Methods: - client.admin.organization.projects.groups.roles.create(group_id, \*, project_id, \*\*params) -> RoleCreateResponse +- client.admin.organization.projects.groups.roles.retrieve(role_id, \*, project_id, group_id) -> RoleRetrieveResponse - client.admin.organization.projects.groups.roles.list(group_id, \*, project_id, \*\*params) -> SyncNextCursorPage[RoleListResponse] - client.admin.organization.projects.groups.roles.delete(role_id, \*, project_id, group_id) -> RoleDeleteResponse @@ -1083,10 +1125,39 @@ from openai.types.admin.organization.projects import RoleDeleteResponse Methods: - client.admin.organization.projects.roles.create(project_id, \*\*params) -> Role +- client.admin.organization.projects.roles.retrieve(role_id, \*, project_id) -> Role - client.admin.organization.projects.roles.update(role_id, \*, project_id, \*\*params) -> Role - client.admin.organization.projects.roles.list(project_id, \*\*params) -> SyncNextCursorPage[Role] - client.admin.organization.projects.roles.delete(role_id, \*, project_id) -> RoleDeleteResponse +#### DataRetention + +Types: + +```python +from openai.types.admin.organization.projects import ProjectDataRetention +``` + +Methods: + +- client.admin.organization.projects.data_retention.retrieve(project_id) -> ProjectDataRetention +- client.admin.organization.projects.data_retention.update(project_id, \*\*params) -> ProjectDataRetention + +#### SpendAlerts + +Types: + +```python +from openai.types.admin.organization.projects import ProjectSpendAlert, ProjectSpendAlertDeleted +``` + +Methods: + +- client.admin.organization.projects.spend_alerts.create(project_id, \*\*params) -> ProjectSpendAlert +- client.admin.organization.projects.spend_alerts.update(alert_id, \*, project_id, \*\*params) -> ProjectSpendAlert +- client.admin.organization.projects.spend_alerts.list(project_id, \*\*params) -> SyncConversationCursorPage[ProjectSpendAlert] +- client.admin.organization.projects.spend_alerts.delete(alert_id, \*, project_id) -> ProjectSpendAlertDeleted + #### Certificates Types: diff --git a/src/openai/resources/admin/organization/__init__.py b/src/openai/resources/admin/organization/__init__.py index 50641088bb..d87fbbc81d 100644 --- a/src/openai/resources/admin/organization/__init__.py +++ b/src/openai/resources/admin/organization/__init__.py @@ -72,6 +72,14 @@ OrganizationWithStreamingResponse, AsyncOrganizationWithStreamingResponse, ) +from .spend_alerts import ( + SpendAlerts, + AsyncSpendAlerts, + SpendAlertsWithRawResponse, + AsyncSpendAlertsWithRawResponse, + SpendAlertsWithStreamingResponse, + AsyncSpendAlertsWithStreamingResponse, +) from .admin_api_keys import ( AdminAPIKeys, AsyncAdminAPIKeys, @@ -80,6 +88,14 @@ AdminAPIKeysWithStreamingResponse, AsyncAdminAPIKeysWithStreamingResponse, ) +from .data_retention import ( + DataRetention, + AsyncDataRetention, + DataRetentionWithRawResponse, + AsyncDataRetentionWithRawResponse, + DataRetentionWithStreamingResponse, + AsyncDataRetentionWithStreamingResponse, +) __all__ = [ "AuditLogs", @@ -124,6 +140,18 @@ "AsyncRolesWithRawResponse", "RolesWithStreamingResponse", "AsyncRolesWithStreamingResponse", + "DataRetention", + "AsyncDataRetention", + "DataRetentionWithRawResponse", + "AsyncDataRetentionWithRawResponse", + "DataRetentionWithStreamingResponse", + "AsyncDataRetentionWithStreamingResponse", + "SpendAlerts", + "AsyncSpendAlerts", + "SpendAlertsWithRawResponse", + "AsyncSpendAlertsWithRawResponse", + "SpendAlertsWithStreamingResponse", + "AsyncSpendAlertsWithStreamingResponse", "Certificates", "AsyncCertificates", "CertificatesWithRawResponse", diff --git a/src/openai/resources/admin/organization/data_retention.py b/src/openai/resources/admin/organization/data_retention.py new file mode 100644 index 0000000000..c3b7325a92 --- /dev/null +++ b/src/openai/resources/admin/organization/data_retention.py @@ -0,0 +1,245 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.admin.organization import data_retention_update_params +from ....types.admin.organization.organization_data_retention import OrganizationDataRetention + +__all__ = ["DataRetention", "AsyncDataRetention"] + + +class DataRetention(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DataRetentionWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return DataRetentionWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DataRetentionWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return DataRetentionWithStreamingResponse(self) + + def retrieve( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationDataRetention: + """Retrieves organization data retention controls.""" + return self._get( + "/organization/data_retention", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationDataRetention, + ) + + def update( + self, + *, + retention_type: Literal[ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationDataRetention: + """ + Updates organization data retention controls. + + Args: + retention_type: The desired organization data retention type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/data_retention", + body=maybe_transform( + {"retention_type": retention_type}, data_retention_update_params.DataRetentionUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationDataRetention, + ) + + +class AsyncDataRetention(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDataRetentionWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncDataRetentionWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDataRetentionWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncDataRetentionWithStreamingResponse(self) + + async def retrieve( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationDataRetention: + """Retrieves organization data retention controls.""" + return await self._get( + "/organization/data_retention", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationDataRetention, + ) + + async def update( + self, + *, + retention_type: Literal[ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationDataRetention: + """ + Updates organization data retention controls. + + Args: + retention_type: The desired organization data retention type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/data_retention", + body=await async_maybe_transform( + {"retention_type": retention_type}, data_retention_update_params.DataRetentionUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationDataRetention, + ) + + +class DataRetentionWithRawResponse: + def __init__(self, data_retention: DataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = _legacy_response.to_raw_response_wrapper( + data_retention.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + data_retention.update, + ) + + +class AsyncDataRetentionWithRawResponse: + def __init__(self, data_retention: AsyncDataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + data_retention.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + data_retention.update, + ) + + +class DataRetentionWithStreamingResponse: + def __init__(self, data_retention: DataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = to_streamed_response_wrapper( + data_retention.retrieve, + ) + self.update = to_streamed_response_wrapper( + data_retention.update, + ) + + +class AsyncDataRetentionWithStreamingResponse: + def __init__(self, data_retention: AsyncDataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = async_to_streamed_response_wrapper( + data_retention.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + data_retention.update, + ) diff --git a/src/openai/resources/admin/organization/groups/groups.py b/src/openai/resources/admin/organization/groups/groups.py index 80baa38926..fa7a6b2354 100644 --- a/src/openai/resources/admin/organization/groups/groups.py +++ b/src/openai/resources/admin/organization/groups/groups.py @@ -104,6 +104,43 @@ def create( cast_to=Group, ) + def retrieve( + self, + group_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Group: + """ + Retrieves a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get( + path_template("/organization/groups/{group_id}", group_id=group_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Group, + ) + def update( self, group_id: str, @@ -305,6 +342,43 @@ async def create( cast_to=Group, ) + async def retrieve( + self, + group_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Group: + """ + Retrieves a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._get( + path_template("/organization/groups/{group_id}", group_id=group_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Group, + ) + async def update( self, group_id: str, @@ -447,6 +521,9 @@ def __init__(self, groups: Groups) -> None: self.create = _legacy_response.to_raw_response_wrapper( groups.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + groups.retrieve, + ) self.update = _legacy_response.to_raw_response_wrapper( groups.update, ) @@ -473,6 +550,9 @@ def __init__(self, groups: AsyncGroups) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( groups.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + groups.retrieve, + ) self.update = _legacy_response.async_to_raw_response_wrapper( groups.update, ) @@ -499,6 +579,9 @@ def __init__(self, groups: Groups) -> None: self.create = to_streamed_response_wrapper( groups.create, ) + self.retrieve = to_streamed_response_wrapper( + groups.retrieve, + ) self.update = to_streamed_response_wrapper( groups.update, ) @@ -525,6 +608,9 @@ def __init__(self, groups: AsyncGroups) -> None: self.create = async_to_streamed_response_wrapper( groups.create, ) + self.retrieve = async_to_streamed_response_wrapper( + groups.retrieve, + ) self.update = async_to_streamed_response_wrapper( groups.update, ) diff --git a/src/openai/resources/admin/organization/groups/roles.py b/src/openai/resources/admin/organization/groups/roles.py index c85efccfef..c4405c487c 100644 --- a/src/openai/resources/admin/organization/groups/roles.py +++ b/src/openai/resources/admin/organization/groups/roles.py @@ -18,6 +18,7 @@ from .....types.admin.organization.groups.role_list_response import RoleListResponse from .....types.admin.organization.groups.role_create_response import RoleCreateResponse from .....types.admin.organization.groups.role_delete_response import RoleDeleteResponse +from .....types.admin.organization.groups.role_retrieve_response import RoleRetrieveResponse __all__ = ["Roles", "AsyncRoles"] @@ -83,6 +84,46 @@ def create( cast_to=RoleCreateResponse, ) + def retrieve( + self, + role_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves an organization role assigned to a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template("/organization/groups/{group_id}/roles/{role_id}", group_id=group_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, group_id: str, @@ -241,6 +282,46 @@ async def create( cast_to=RoleCreateResponse, ) + async def retrieve( + self, + role_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves an organization role assigned to a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template("/organization/groups/{group_id}/roles/{role_id}", group_id=group_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, group_id: str, @@ -345,6 +426,9 @@ def __init__(self, roles: Roles) -> None: self.create = _legacy_response.to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.to_raw_response_wrapper( roles.list, ) @@ -360,6 +444,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.async_to_raw_response_wrapper( roles.list, ) @@ -375,6 +462,9 @@ def __init__(self, roles: Roles) -> None: self.create = to_streamed_response_wrapper( roles.create, ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) self.list = to_streamed_response_wrapper( roles.list, ) @@ -390,6 +480,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = async_to_streamed_response_wrapper( roles.create, ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) self.list = async_to_streamed_response_wrapper( roles.list, ) diff --git a/src/openai/resources/admin/organization/groups/users.py b/src/openai/resources/admin/organization/groups/users.py index 89a2996e13..e0ac71afd9 100644 --- a/src/openai/resources/admin/organization/groups/users.py +++ b/src/openai/resources/admin/organization/groups/users.py @@ -17,6 +17,7 @@ from .....types.admin.organization.groups import user_list_params, user_create_params from .....types.admin.organization.groups.user_create_response import UserCreateResponse from .....types.admin.organization.groups.user_delete_response import UserDeleteResponse +from .....types.admin.organization.groups.user_retrieve_response import UserRetrieveResponse from .....types.admin.organization.groups.organization_group_user import OrganizationGroupUser __all__ = ["Users", "AsyncUsers"] @@ -83,6 +84,46 @@ def create( cast_to=UserCreateResponse, ) + def retrieve( + self, + user_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UserRetrieveResponse: + """ + Retrieves a user in a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get( + path_template("/organization/groups/{group_id}/users/{user_id}", group_id=group_id, user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserRetrieveResponse, + ) + def list( self, group_id: str, @@ -242,6 +283,46 @@ async def create( cast_to=UserCreateResponse, ) + async def retrieve( + self, + user_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UserRetrieveResponse: + """ + Retrieves a user in a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._get( + path_template("/organization/groups/{group_id}/users/{user_id}", group_id=group_id, user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserRetrieveResponse, + ) + def list( self, group_id: str, @@ -347,6 +428,9 @@ def __init__(self, users: Users) -> None: self.create = _legacy_response.to_raw_response_wrapper( users.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + users.retrieve, + ) self.list = _legacy_response.to_raw_response_wrapper( users.list, ) @@ -362,6 +446,9 @@ def __init__(self, users: AsyncUsers) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( users.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + users.retrieve, + ) self.list = _legacy_response.async_to_raw_response_wrapper( users.list, ) @@ -377,6 +464,9 @@ def __init__(self, users: Users) -> None: self.create = to_streamed_response_wrapper( users.create, ) + self.retrieve = to_streamed_response_wrapper( + users.retrieve, + ) self.list = to_streamed_response_wrapper( users.list, ) @@ -392,6 +482,9 @@ def __init__(self, users: AsyncUsers) -> None: self.create = async_to_streamed_response_wrapper( users.create, ) + self.retrieve = async_to_streamed_response_wrapper( + users.retrieve, + ) self.list = async_to_streamed_response_wrapper( users.list, ) diff --git a/src/openai/resources/admin/organization/organization.py b/src/openai/resources/admin/organization/organization.py index abbbadf575..62bba207cb 100644 --- a/src/openai/resources/admin/organization/organization.py +++ b/src/openai/resources/admin/organization/organization.py @@ -52,6 +52,14 @@ CertificatesWithStreamingResponse, AsyncCertificatesWithStreamingResponse, ) +from .spend_alerts import ( + SpendAlerts, + AsyncSpendAlerts, + SpendAlertsWithRawResponse, + AsyncSpendAlertsWithRawResponse, + SpendAlertsWithStreamingResponse, + AsyncSpendAlertsWithStreamingResponse, +) from .groups.groups import ( Groups, AsyncGroups, @@ -68,6 +76,14 @@ AdminAPIKeysWithStreamingResponse, AsyncAdminAPIKeysWithStreamingResponse, ) +from .data_retention import ( + DataRetention, + AsyncDataRetention, + DataRetentionWithRawResponse, + AsyncDataRetentionWithRawResponse, + DataRetentionWithStreamingResponse, + AsyncDataRetentionWithStreamingResponse, +) from .projects.projects import ( Projects, AsyncProjects, @@ -110,6 +126,14 @@ def groups(self) -> Groups: def roles(self) -> Roles: return Roles(self._client) + @cached_property + def data_retention(self) -> DataRetention: + return DataRetention(self._client) + + @cached_property + def spend_alerts(self) -> SpendAlerts: + return SpendAlerts(self._client) + @cached_property def certificates(self) -> Certificates: return Certificates(self._client) @@ -168,6 +192,14 @@ def groups(self) -> AsyncGroups: def roles(self) -> AsyncRoles: return AsyncRoles(self._client) + @cached_property + def data_retention(self) -> AsyncDataRetention: + return AsyncDataRetention(self._client) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlerts: + return AsyncSpendAlerts(self._client) + @cached_property def certificates(self) -> AsyncCertificates: return AsyncCertificates(self._client) @@ -229,6 +261,14 @@ def groups(self) -> GroupsWithRawResponse: def roles(self) -> RolesWithRawResponse: return RolesWithRawResponse(self._organization.roles) + @cached_property + def data_retention(self) -> DataRetentionWithRawResponse: + return DataRetentionWithRawResponse(self._organization.data_retention) + + @cached_property + def spend_alerts(self) -> SpendAlertsWithRawResponse: + return SpendAlertsWithRawResponse(self._organization.spend_alerts) + @cached_property def certificates(self) -> CertificatesWithRawResponse: return CertificatesWithRawResponse(self._organization.certificates) @@ -271,6 +311,14 @@ def groups(self) -> AsyncGroupsWithRawResponse: def roles(self) -> AsyncRolesWithRawResponse: return AsyncRolesWithRawResponse(self._organization.roles) + @cached_property + def data_retention(self) -> AsyncDataRetentionWithRawResponse: + return AsyncDataRetentionWithRawResponse(self._organization.data_retention) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlertsWithRawResponse: + return AsyncSpendAlertsWithRawResponse(self._organization.spend_alerts) + @cached_property def certificates(self) -> AsyncCertificatesWithRawResponse: return AsyncCertificatesWithRawResponse(self._organization.certificates) @@ -313,6 +361,14 @@ def groups(self) -> GroupsWithStreamingResponse: def roles(self) -> RolesWithStreamingResponse: return RolesWithStreamingResponse(self._organization.roles) + @cached_property + def data_retention(self) -> DataRetentionWithStreamingResponse: + return DataRetentionWithStreamingResponse(self._organization.data_retention) + + @cached_property + def spend_alerts(self) -> SpendAlertsWithStreamingResponse: + return SpendAlertsWithStreamingResponse(self._organization.spend_alerts) + @cached_property def certificates(self) -> CertificatesWithStreamingResponse: return CertificatesWithStreamingResponse(self._organization.certificates) @@ -355,6 +411,14 @@ def groups(self) -> AsyncGroupsWithStreamingResponse: def roles(self) -> AsyncRolesWithStreamingResponse: return AsyncRolesWithStreamingResponse(self._organization.roles) + @cached_property + def data_retention(self) -> AsyncDataRetentionWithStreamingResponse: + return AsyncDataRetentionWithStreamingResponse(self._organization.data_retention) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlertsWithStreamingResponse: + return AsyncSpendAlertsWithStreamingResponse(self._organization.spend_alerts) + @cached_property def certificates(self) -> AsyncCertificatesWithStreamingResponse: return AsyncCertificatesWithStreamingResponse(self._organization.certificates) diff --git a/src/openai/resources/admin/organization/projects/__init__.py b/src/openai/resources/admin/organization/projects/__init__.py index 7f77863a2d..3bc97170fd 100644 --- a/src/openai/resources/admin/organization/projects/__init__.py +++ b/src/openai/resources/admin/organization/projects/__init__.py @@ -56,6 +56,22 @@ CertificatesWithStreamingResponse, AsyncCertificatesWithStreamingResponse, ) +from .spend_alerts import ( + SpendAlerts, + AsyncSpendAlerts, + SpendAlertsWithRawResponse, + AsyncSpendAlertsWithRawResponse, + SpendAlertsWithStreamingResponse, + AsyncSpendAlertsWithStreamingResponse, +) +from .data_retention import ( + DataRetention, + AsyncDataRetention, + DataRetentionWithRawResponse, + AsyncDataRetentionWithRawResponse, + DataRetentionWithStreamingResponse, + AsyncDataRetentionWithStreamingResponse, +) from .service_accounts import ( ServiceAccounts, AsyncServiceAccounts, @@ -130,6 +146,18 @@ "AsyncRolesWithRawResponse", "RolesWithStreamingResponse", "AsyncRolesWithStreamingResponse", + "DataRetention", + "AsyncDataRetention", + "DataRetentionWithRawResponse", + "AsyncDataRetentionWithRawResponse", + "DataRetentionWithStreamingResponse", + "AsyncDataRetentionWithStreamingResponse", + "SpendAlerts", + "AsyncSpendAlerts", + "SpendAlertsWithRawResponse", + "AsyncSpendAlertsWithRawResponse", + "SpendAlertsWithStreamingResponse", + "AsyncSpendAlertsWithStreamingResponse", "Certificates", "AsyncCertificates", "CertificatesWithRawResponse", diff --git a/src/openai/resources/admin/organization/projects/data_retention.py b/src/openai/resources/admin/organization/projects/data_retention.py new file mode 100644 index 0000000000..2d0df3e7e2 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/data_retention.py @@ -0,0 +1,283 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Query, Headers, NotGiven, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._base_client import make_request_options +from .....types.admin.organization.projects import data_retention_update_params +from .....types.admin.organization.projects.project_data_retention import ProjectDataRetention + +__all__ = ["DataRetention", "AsyncDataRetention"] + + +class DataRetention(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DataRetentionWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return DataRetentionWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DataRetentionWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return DataRetentionWithStreamingResponse(self) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDataRetention: + """ + Retrieves project data retention controls. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}/data_retention", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectDataRetention, + ) + + def update( + self, + project_id: str, + *, + retention_type: Literal[ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDataRetention: + """ + Updates project data retention controls. + + Args: + retention_type: The desired project data retention type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/data_retention", project_id=project_id), + body=maybe_transform( + {"retention_type": retention_type}, data_retention_update_params.DataRetentionUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectDataRetention, + ) + + +class AsyncDataRetention(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDataRetentionWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncDataRetentionWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDataRetentionWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncDataRetentionWithStreamingResponse(self) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDataRetention: + """ + Retrieves project data retention controls. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}/data_retention", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectDataRetention, + ) + + async def update( + self, + project_id: str, + *, + retention_type: Literal[ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDataRetention: + """ + Updates project data retention controls. + + Args: + retention_type: The desired project data retention type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/data_retention", project_id=project_id), + body=await async_maybe_transform( + {"retention_type": retention_type}, data_retention_update_params.DataRetentionUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectDataRetention, + ) + + +class DataRetentionWithRawResponse: + def __init__(self, data_retention: DataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = _legacy_response.to_raw_response_wrapper( + data_retention.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + data_retention.update, + ) + + +class AsyncDataRetentionWithRawResponse: + def __init__(self, data_retention: AsyncDataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + data_retention.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + data_retention.update, + ) + + +class DataRetentionWithStreamingResponse: + def __init__(self, data_retention: DataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = to_streamed_response_wrapper( + data_retention.retrieve, + ) + self.update = to_streamed_response_wrapper( + data_retention.update, + ) + + +class AsyncDataRetentionWithStreamingResponse: + def __init__(self, data_retention: AsyncDataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = async_to_streamed_response_wrapper( + data_retention.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + data_retention.update, + ) diff --git a/src/openai/resources/admin/organization/projects/groups/groups.py b/src/openai/resources/admin/organization/projects/groups/groups.py index aad9bfd6ec..a7ef36fd64 100644 --- a/src/openai/resources/admin/organization/projects/groups/groups.py +++ b/src/openai/resources/admin/organization/projects/groups/groups.py @@ -22,7 +22,7 @@ from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ......pagination import SyncNextCursorPage, AsyncNextCursorPage from ......_base_client import AsyncPaginator, make_request_options -from ......types.admin.organization.projects import group_list_params, group_create_params +from ......types.admin.organization.projects import group_list_params, group_create_params, group_retrieve_params from ......types.admin.organization.projects.project_group import ProjectGroup from ......types.admin.organization.projects.group_delete_response import GroupDeleteResponse @@ -103,6 +103,52 @@ def create( cast_to=ProjectGroup, ) + def retrieve( + self, + group_id: str, + *, + project_id: str, + group_type: Literal["group", "tenant_group"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectGroup: + """ + Retrieves a project's group. + + Args: + group_type: The type of group to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get( + path_template( + "/organization/projects/{project_id}/groups/{group_id}", project_id=project_id, group_id=group_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"group_type": group_type}, group_retrieve_params.GroupRetrieveParams), + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectGroup, + ) + def list( self, project_id: str, @@ -276,6 +322,54 @@ async def create( cast_to=ProjectGroup, ) + async def retrieve( + self, + group_id: str, + *, + project_id: str, + group_type: Literal["group", "tenant_group"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectGroup: + """ + Retrieves a project's group. + + Args: + group_type: The type of group to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._get( + path_template( + "/organization/projects/{project_id}/groups/{group_id}", project_id=project_id, group_id=group_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"group_type": group_type}, group_retrieve_params.GroupRetrieveParams + ), + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectGroup, + ) + def list( self, project_id: str, @@ -382,6 +476,9 @@ def __init__(self, groups: Groups) -> None: self.create = _legacy_response.to_raw_response_wrapper( groups.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + groups.retrieve, + ) self.list = _legacy_response.to_raw_response_wrapper( groups.list, ) @@ -401,6 +498,9 @@ def __init__(self, groups: AsyncGroups) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( groups.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + groups.retrieve, + ) self.list = _legacy_response.async_to_raw_response_wrapper( groups.list, ) @@ -420,6 +520,9 @@ def __init__(self, groups: Groups) -> None: self.create = to_streamed_response_wrapper( groups.create, ) + self.retrieve = to_streamed_response_wrapper( + groups.retrieve, + ) self.list = to_streamed_response_wrapper( groups.list, ) @@ -439,6 +542,9 @@ def __init__(self, groups: AsyncGroups) -> None: self.create = async_to_streamed_response_wrapper( groups.create, ) + self.retrieve = async_to_streamed_response_wrapper( + groups.retrieve, + ) self.list = async_to_streamed_response_wrapper( groups.list, ) diff --git a/src/openai/resources/admin/organization/projects/groups/roles.py b/src/openai/resources/admin/organization/projects/groups/roles.py index e3fe3b54fe..5961d05e70 100644 --- a/src/openai/resources/admin/organization/projects/groups/roles.py +++ b/src/openai/resources/admin/organization/projects/groups/roles.py @@ -18,6 +18,7 @@ from ......types.admin.organization.projects.groups.role_list_response import RoleListResponse from ......types.admin.organization.projects.groups.role_create_response import RoleCreateResponse from ......types.admin.organization.projects.groups.role_delete_response import RoleDeleteResponse +from ......types.admin.organization.projects.groups.role_retrieve_response import RoleRetrieveResponse __all__ = ["Roles", "AsyncRoles"] @@ -86,6 +87,54 @@ def create( cast_to=RoleCreateResponse, ) + def retrieve( + self, + role_id: str, + *, + project_id: str, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves a project role assigned to a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template( + "/projects/{project_id}/groups/{group_id}/roles/{role_id}", + project_id=project_id, + group_id=group_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, group_id: str, @@ -258,6 +307,54 @@ async def create( cast_to=RoleCreateResponse, ) + async def retrieve( + self, + role_id: str, + *, + project_id: str, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves a project role assigned to a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template( + "/projects/{project_id}/groups/{group_id}/roles/{role_id}", + project_id=project_id, + group_id=group_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, group_id: str, @@ -373,6 +470,9 @@ def __init__(self, roles: Roles) -> None: self.create = _legacy_response.to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.to_raw_response_wrapper( roles.list, ) @@ -388,6 +488,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.async_to_raw_response_wrapper( roles.list, ) @@ -403,6 +506,9 @@ def __init__(self, roles: Roles) -> None: self.create = to_streamed_response_wrapper( roles.create, ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) self.list = to_streamed_response_wrapper( roles.list, ) @@ -418,6 +524,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = async_to_streamed_response_wrapper( roles.create, ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) self.list = async_to_streamed_response_wrapper( roles.list, ) diff --git a/src/openai/resources/admin/organization/projects/projects.py b/src/openai/resources/admin/organization/projects/projects.py index b69e69d0dd..6ffd4c0f85 100644 --- a/src/openai/resources/admin/organization/projects/projects.py +++ b/src/openai/resources/admin/organization/projects/projects.py @@ -50,6 +50,14 @@ CertificatesWithStreamingResponse, AsyncCertificatesWithStreamingResponse, ) +from .spend_alerts import ( + SpendAlerts, + AsyncSpendAlerts, + SpendAlertsWithRawResponse, + AsyncSpendAlertsWithRawResponse, + SpendAlertsWithStreamingResponse, + AsyncSpendAlertsWithStreamingResponse, +) from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .groups.groups import ( @@ -61,6 +69,14 @@ AsyncGroupsWithStreamingResponse, ) from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from .data_retention import ( + DataRetention, + AsyncDataRetention, + DataRetentionWithRawResponse, + AsyncDataRetentionWithRawResponse, + DataRetentionWithStreamingResponse, + AsyncDataRetentionWithStreamingResponse, +) from ....._base_client import AsyncPaginator, make_request_options from .service_accounts import ( ServiceAccounts, @@ -125,6 +141,14 @@ def groups(self) -> Groups: def roles(self) -> Roles: return Roles(self._client) + @cached_property + def data_retention(self) -> DataRetention: + return DataRetention(self._client) + + @cached_property + def spend_alerts(self) -> SpendAlerts: + return SpendAlerts(self._client) + @cached_property def certificates(self) -> Certificates: return Certificates(self._client) @@ -426,6 +450,14 @@ def groups(self) -> AsyncGroups: def roles(self) -> AsyncRoles: return AsyncRoles(self._client) + @cached_property + def data_retention(self) -> AsyncDataRetention: + return AsyncDataRetention(self._client) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlerts: + return AsyncSpendAlerts(self._client) + @cached_property def certificates(self) -> AsyncCertificates: return AsyncCertificates(self._client) @@ -746,6 +778,14 @@ def groups(self) -> GroupsWithRawResponse: def roles(self) -> RolesWithRawResponse: return RolesWithRawResponse(self._projects.roles) + @cached_property + def data_retention(self) -> DataRetentionWithRawResponse: + return DataRetentionWithRawResponse(self._projects.data_retention) + + @cached_property + def spend_alerts(self) -> SpendAlertsWithRawResponse: + return SpendAlertsWithRawResponse(self._projects.spend_alerts) + @cached_property def certificates(self) -> CertificatesWithRawResponse: return CertificatesWithRawResponse(self._projects.certificates) @@ -803,6 +843,14 @@ def groups(self) -> AsyncGroupsWithRawResponse: def roles(self) -> AsyncRolesWithRawResponse: return AsyncRolesWithRawResponse(self._projects.roles) + @cached_property + def data_retention(self) -> AsyncDataRetentionWithRawResponse: + return AsyncDataRetentionWithRawResponse(self._projects.data_retention) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlertsWithRawResponse: + return AsyncSpendAlertsWithRawResponse(self._projects.spend_alerts) + @cached_property def certificates(self) -> AsyncCertificatesWithRawResponse: return AsyncCertificatesWithRawResponse(self._projects.certificates) @@ -860,6 +908,14 @@ def groups(self) -> GroupsWithStreamingResponse: def roles(self) -> RolesWithStreamingResponse: return RolesWithStreamingResponse(self._projects.roles) + @cached_property + def data_retention(self) -> DataRetentionWithStreamingResponse: + return DataRetentionWithStreamingResponse(self._projects.data_retention) + + @cached_property + def spend_alerts(self) -> SpendAlertsWithStreamingResponse: + return SpendAlertsWithStreamingResponse(self._projects.spend_alerts) + @cached_property def certificates(self) -> CertificatesWithStreamingResponse: return CertificatesWithStreamingResponse(self._projects.certificates) @@ -917,6 +973,14 @@ def groups(self) -> AsyncGroupsWithStreamingResponse: def roles(self) -> AsyncRolesWithStreamingResponse: return AsyncRolesWithStreamingResponse(self._projects.roles) + @cached_property + def data_retention(self) -> AsyncDataRetentionWithStreamingResponse: + return AsyncDataRetentionWithStreamingResponse(self._projects.data_retention) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlertsWithStreamingResponse: + return AsyncSpendAlertsWithStreamingResponse(self._projects.spend_alerts) + @cached_property def certificates(self) -> AsyncCertificatesWithStreamingResponse: return AsyncCertificatesWithStreamingResponse(self._projects.certificates) diff --git a/src/openai/resources/admin/organization/projects/roles.py b/src/openai/resources/admin/organization/projects/roles.py index c958b037bb..4c603ec5b1 100644 --- a/src/openai/resources/admin/organization/projects/roles.py +++ b/src/openai/resources/admin/organization/projects/roles.py @@ -96,6 +96,46 @@ def create( cast_to=Role, ) + def retrieve( + self, + role_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Retrieves a project role. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + def update( self, role_id: str, @@ -325,6 +365,46 @@ async def create( cast_to=Role, ) + async def retrieve( + self, + role_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Retrieves a project role. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + async def update( self, role_id: str, @@ -487,6 +567,9 @@ def __init__(self, roles: Roles) -> None: self.create = _legacy_response.to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) self.update = _legacy_response.to_raw_response_wrapper( roles.update, ) @@ -505,6 +588,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) self.update = _legacy_response.async_to_raw_response_wrapper( roles.update, ) @@ -523,6 +609,9 @@ def __init__(self, roles: Roles) -> None: self.create = to_streamed_response_wrapper( roles.create, ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) self.update = to_streamed_response_wrapper( roles.update, ) @@ -541,6 +630,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = async_to_streamed_response_wrapper( roles.create, ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) self.update = async_to_streamed_response_wrapper( roles.update, ) diff --git a/src/openai/resources/admin/organization/projects/service_accounts.py b/src/openai/resources/admin/organization/projects/service_accounts.py index 9c265fd766..0de0ced9cf 100644 --- a/src/openai/resources/admin/organization/projects/service_accounts.py +++ b/src/openai/resources/admin/organization/projects/service_accounts.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing_extensions import Literal + import httpx from ..... import _legacy_response @@ -12,7 +14,11 @@ from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage from ....._base_client import AsyncPaginator, make_request_options -from .....types.admin.organization.projects import service_account_list_params, service_account_create_params +from .....types.admin.organization.projects import ( + service_account_list_params, + service_account_create_params, + service_account_update_params, +) from .....types.admin.organization.projects.project_service_account import ProjectServiceAccount from .....types.admin.organization.projects.service_account_create_response import ServiceAccountCreateResponse from .....types.admin.organization.projects.service_account_delete_response import ServiceAccountDeleteResponse @@ -127,6 +133,63 @@ def retrieve( cast_to=ProjectServiceAccount, ) + def update( + self, + service_account_id: str, + *, + project_id: str, + name: str | Omit = omit, + role: Literal["member", "owner"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectServiceAccount: + """ + Updates a service account in the project. + + Args: + name: The updated service account name. + + role: The updated service account role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return self._post( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + body=maybe_transform( + { + "name": name, + "role": role, + }, + service_account_update_params.ServiceAccountUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectServiceAccount, + ) + def list( self, project_id: str, @@ -337,6 +400,63 @@ async def retrieve( cast_to=ProjectServiceAccount, ) + async def update( + self, + service_account_id: str, + *, + project_id: str, + name: str | Omit = omit, + role: Literal["member", "owner"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectServiceAccount: + """ + Updates a service account in the project. + + Args: + name: The updated service account name. + + role: The updated service account role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return await self._post( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + body=await async_maybe_transform( + { + "name": name, + "role": role, + }, + service_account_update_params.ServiceAccountUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectServiceAccount, + ) + def list( self, project_id: str, @@ -450,6 +570,9 @@ def __init__(self, service_accounts: ServiceAccounts) -> None: self.retrieve = _legacy_response.to_raw_response_wrapper( service_accounts.retrieve, ) + self.update = _legacy_response.to_raw_response_wrapper( + service_accounts.update, + ) self.list = _legacy_response.to_raw_response_wrapper( service_accounts.list, ) @@ -468,6 +591,9 @@ def __init__(self, service_accounts: AsyncServiceAccounts) -> None: self.retrieve = _legacy_response.async_to_raw_response_wrapper( service_accounts.retrieve, ) + self.update = _legacy_response.async_to_raw_response_wrapper( + service_accounts.update, + ) self.list = _legacy_response.async_to_raw_response_wrapper( service_accounts.list, ) @@ -486,6 +612,9 @@ def __init__(self, service_accounts: ServiceAccounts) -> None: self.retrieve = to_streamed_response_wrapper( service_accounts.retrieve, ) + self.update = to_streamed_response_wrapper( + service_accounts.update, + ) self.list = to_streamed_response_wrapper( service_accounts.list, ) @@ -504,6 +633,9 @@ def __init__(self, service_accounts: AsyncServiceAccounts) -> None: self.retrieve = async_to_streamed_response_wrapper( service_accounts.retrieve, ) + self.update = async_to_streamed_response_wrapper( + service_accounts.update, + ) self.list = async_to_streamed_response_wrapper( service_accounts.list, ) diff --git a/src/openai/resources/admin/organization/projects/spend_alerts.py b/src/openai/resources/admin/organization/projects/spend_alerts.py new file mode 100644 index 0000000000..3bc5d0c1f9 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/spend_alerts.py @@ -0,0 +1,589 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.projects import ( + spend_alert_list_params, + spend_alert_create_params, + spend_alert_update_params, +) +from .....types.admin.organization.projects.project_spend_alert import ProjectSpendAlert +from .....types.admin.organization.projects.project_spend_alert_deleted import ProjectSpendAlertDeleted + +__all__ = ["SpendAlerts", "AsyncSpendAlerts"] + + +class SpendAlerts(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SpendAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SpendAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpendAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SpendAlertsWithStreamingResponse(self) + + def create( + self, + project_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_create_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Creates a project spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/spend_alerts", project_id=project_id), + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_create_params.SpendAlertCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + def update( + self, + alert_id: str, + *, + project_id: str, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_update_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Updates a project spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._post( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_update_params.SpendAlertUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ProjectSpendAlert]: + """Lists project spend alerts. + + Args: + after: Cursor for pagination. + + Provide the ID of the last spend alert from the previous + response to fetch the next page. + + before: Cursor for pagination. Provide the ID of the first spend alert from the previous + response to fetch the previous page. + + limit: A limit on the number of spend alerts to return. Defaults to 20. + + order: Sort order for the returned spend alerts. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/spend_alerts", project_id=project_id), + page=SyncConversationCursorPage[ProjectSpendAlert], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + spend_alert_list_params.SpendAlertListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectSpendAlert, + ) + + def delete( + self, + alert_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlertDeleted: + """ + Deletes a project spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._delete( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlertDeleted, + ) + + +class AsyncSpendAlerts(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSpendAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSpendAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpendAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSpendAlertsWithStreamingResponse(self) + + async def create( + self, + project_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_create_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Creates a project spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/spend_alerts", project_id=project_id), + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_create_params.SpendAlertCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + async def update( + self, + alert_id: str, + *, + project_id: str, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_update_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Updates a project spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._post( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_update_params.SpendAlertUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectSpendAlert, AsyncConversationCursorPage[ProjectSpendAlert]]: + """Lists project spend alerts. + + Args: + after: Cursor for pagination. + + Provide the ID of the last spend alert from the previous + response to fetch the next page. + + before: Cursor for pagination. Provide the ID of the first spend alert from the previous + response to fetch the previous page. + + limit: A limit on the number of spend alerts to return. Defaults to 20. + + order: Sort order for the returned spend alerts. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/spend_alerts", project_id=project_id), + page=AsyncConversationCursorPage[ProjectSpendAlert], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + spend_alert_list_params.SpendAlertListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectSpendAlert, + ) + + async def delete( + self, + alert_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlertDeleted: + """ + Deletes a project spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._delete( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlertDeleted, + ) + + +class SpendAlertsWithRawResponse: + def __init__(self, spend_alerts: SpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = _legacy_response.to_raw_response_wrapper( + spend_alerts.create, + ) + self.update = _legacy_response.to_raw_response_wrapper( + spend_alerts.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + spend_alerts.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + spend_alerts.delete, + ) + + +class AsyncSpendAlertsWithRawResponse: + def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.create, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.delete, + ) + + +class SpendAlertsWithStreamingResponse: + def __init__(self, spend_alerts: SpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = to_streamed_response_wrapper( + spend_alerts.create, + ) + self.update = to_streamed_response_wrapper( + spend_alerts.update, + ) + self.list = to_streamed_response_wrapper( + spend_alerts.list, + ) + self.delete = to_streamed_response_wrapper( + spend_alerts.delete, + ) + + +class AsyncSpendAlertsWithStreamingResponse: + def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = async_to_streamed_response_wrapper( + spend_alerts.create, + ) + self.update = async_to_streamed_response_wrapper( + spend_alerts.update, + ) + self.list = async_to_streamed_response_wrapper( + spend_alerts.list, + ) + self.delete = async_to_streamed_response_wrapper( + spend_alerts.delete, + ) diff --git a/src/openai/resources/admin/organization/projects/users/roles.py b/src/openai/resources/admin/organization/projects/users/roles.py index 6a3233e275..38b79acec3 100644 --- a/src/openai/resources/admin/organization/projects/users/roles.py +++ b/src/openai/resources/admin/organization/projects/users/roles.py @@ -18,6 +18,7 @@ from ......types.admin.organization.projects.users.role_list_response import RoleListResponse from ......types.admin.organization.projects.users.role_create_response import RoleCreateResponse from ......types.admin.organization.projects.users.role_delete_response import RoleDeleteResponse +from ......types.admin.organization.projects.users.role_retrieve_response import RoleRetrieveResponse __all__ = ["Roles", "AsyncRoles"] @@ -86,6 +87,54 @@ def create( cast_to=RoleCreateResponse, ) + def retrieve( + self, + role_id: str, + *, + project_id: str, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves a project role assigned to a user. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template( + "/projects/{project_id}/users/{user_id}/roles/{role_id}", + project_id=project_id, + user_id=user_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, user_id: str, @@ -258,6 +307,54 @@ async def create( cast_to=RoleCreateResponse, ) + async def retrieve( + self, + role_id: str, + *, + project_id: str, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves a project role assigned to a user. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template( + "/projects/{project_id}/users/{user_id}/roles/{role_id}", + project_id=project_id, + user_id=user_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, user_id: str, @@ -373,6 +470,9 @@ def __init__(self, roles: Roles) -> None: self.create = _legacy_response.to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.to_raw_response_wrapper( roles.list, ) @@ -388,6 +488,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.async_to_raw_response_wrapper( roles.list, ) @@ -403,6 +506,9 @@ def __init__(self, roles: Roles) -> None: self.create = to_streamed_response_wrapper( roles.create, ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) self.list = to_streamed_response_wrapper( roles.list, ) @@ -418,6 +524,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = async_to_streamed_response_wrapper( roles.create, ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) self.list = async_to_streamed_response_wrapper( roles.list, ) diff --git a/src/openai/resources/admin/organization/roles.py b/src/openai/resources/admin/organization/roles.py index b25bbfb21e..056a47d7b2 100644 --- a/src/openai/resources/admin/organization/roles.py +++ b/src/openai/resources/admin/organization/roles.py @@ -93,6 +93,43 @@ def create( cast_to=Role, ) + def retrieve( + self, + role_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Retrieves an organization role. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template("/organization/roles/{role_id}", role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + def update( self, role_id: str, @@ -309,6 +346,43 @@ async def create( cast_to=Role, ) + async def retrieve( + self, + role_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Retrieves an organization role. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template("/organization/roles/{role_id}", role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + async def update( self, role_id: str, @@ -461,6 +535,9 @@ def __init__(self, roles: Roles) -> None: self.create = _legacy_response.to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) self.update = _legacy_response.to_raw_response_wrapper( roles.update, ) @@ -479,6 +556,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) self.update = _legacy_response.async_to_raw_response_wrapper( roles.update, ) @@ -497,6 +577,9 @@ def __init__(self, roles: Roles) -> None: self.create = to_streamed_response_wrapper( roles.create, ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) self.update = to_streamed_response_wrapper( roles.update, ) @@ -515,6 +598,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = async_to_streamed_response_wrapper( roles.create, ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) self.update = async_to_streamed_response_wrapper( roles.update, ) diff --git a/src/openai/resources/admin/organization/spend_alerts.py b/src/openai/resources/admin/organization/spend_alerts.py new file mode 100644 index 0000000000..4f3e223269 --- /dev/null +++ b/src/openai/resources/admin/organization/spend_alerts.py @@ -0,0 +1,553 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import spend_alert_list_params, spend_alert_create_params, spend_alert_update_params +from ....types.admin.organization.organization_spend_alert import OrganizationSpendAlert +from ....types.admin.organization.organization_spend_alert_deleted import OrganizationSpendAlertDeleted + +__all__ = ["SpendAlerts", "AsyncSpendAlerts"] + + +class SpendAlerts(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SpendAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SpendAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpendAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SpendAlertsWithStreamingResponse(self) + + def create( + self, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_create_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Creates an organization spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/spend_alerts", + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_create_params.SpendAlertCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + def update( + self, + alert_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_update_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Updates an organization spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._post( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_update_params.SpendAlertUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[OrganizationSpendAlert]: + """Lists organization spend alerts. + + Args: + after: Cursor for pagination. + + Provide the ID of the last spend alert from the previous + response to fetch the next page. + + before: Cursor for pagination. Provide the ID of the first spend alert from the previous + response to fetch the previous page. + + limit: A limit on the number of spend alerts to return. Defaults to 20. + + order: Sort order for the returned spend alerts. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/spend_alerts", + page=SyncConversationCursorPage[OrganizationSpendAlert], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + spend_alert_list_params.SpendAlertListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationSpendAlert, + ) + + def delete( + self, + alert_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlertDeleted: + """ + Deletes an organization spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._delete( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlertDeleted, + ) + + +class AsyncSpendAlerts(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSpendAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSpendAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpendAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSpendAlertsWithStreamingResponse(self) + + async def create( + self, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_create_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Creates an organization spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/spend_alerts", + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_create_params.SpendAlertCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + async def update( + self, + alert_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_update_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Updates an organization spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._post( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_update_params.SpendAlertUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[OrganizationSpendAlert, AsyncConversationCursorPage[OrganizationSpendAlert]]: + """Lists organization spend alerts. + + Args: + after: Cursor for pagination. + + Provide the ID of the last spend alert from the previous + response to fetch the next page. + + before: Cursor for pagination. Provide the ID of the first spend alert from the previous + response to fetch the previous page. + + limit: A limit on the number of spend alerts to return. Defaults to 20. + + order: Sort order for the returned spend alerts. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/spend_alerts", + page=AsyncConversationCursorPage[OrganizationSpendAlert], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + spend_alert_list_params.SpendAlertListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationSpendAlert, + ) + + async def delete( + self, + alert_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlertDeleted: + """ + Deletes an organization spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._delete( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlertDeleted, + ) + + +class SpendAlertsWithRawResponse: + def __init__(self, spend_alerts: SpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = _legacy_response.to_raw_response_wrapper( + spend_alerts.create, + ) + self.update = _legacy_response.to_raw_response_wrapper( + spend_alerts.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + spend_alerts.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + spend_alerts.delete, + ) + + +class AsyncSpendAlertsWithRawResponse: + def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.create, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.delete, + ) + + +class SpendAlertsWithStreamingResponse: + def __init__(self, spend_alerts: SpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = to_streamed_response_wrapper( + spend_alerts.create, + ) + self.update = to_streamed_response_wrapper( + spend_alerts.update, + ) + self.list = to_streamed_response_wrapper( + spend_alerts.list, + ) + self.delete = to_streamed_response_wrapper( + spend_alerts.delete, + ) + + +class AsyncSpendAlertsWithStreamingResponse: + def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = async_to_streamed_response_wrapper( + spend_alerts.create, + ) + self.update = async_to_streamed_response_wrapper( + spend_alerts.update, + ) + self.list = async_to_streamed_response_wrapper( + spend_alerts.list, + ) + self.delete = async_to_streamed_response_wrapper( + spend_alerts.delete, + ) diff --git a/src/openai/resources/admin/organization/users/roles.py b/src/openai/resources/admin/organization/users/roles.py index 01ea5f4844..6a20ab52ff 100644 --- a/src/openai/resources/admin/organization/users/roles.py +++ b/src/openai/resources/admin/organization/users/roles.py @@ -18,6 +18,7 @@ from .....types.admin.organization.users.role_list_response import RoleListResponse from .....types.admin.organization.users.role_create_response import RoleCreateResponse from .....types.admin.organization.users.role_delete_response import RoleDeleteResponse +from .....types.admin.organization.users.role_retrieve_response import RoleRetrieveResponse __all__ = ["Roles", "AsyncRoles"] @@ -83,6 +84,46 @@ def create( cast_to=RoleCreateResponse, ) + def retrieve( + self, + role_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves an organization role assigned to a user. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template("/organization/users/{user_id}/roles/{role_id}", user_id=user_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, user_id: str, @@ -241,6 +282,46 @@ async def create( cast_to=RoleCreateResponse, ) + async def retrieve( + self, + role_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves an organization role assigned to a user. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template("/organization/users/{user_id}/roles/{role_id}", user_id=user_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, user_id: str, @@ -345,6 +426,9 @@ def __init__(self, roles: Roles) -> None: self.create = _legacy_response.to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.to_raw_response_wrapper( roles.list, ) @@ -360,6 +444,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.async_to_raw_response_wrapper( roles.list, ) @@ -375,6 +462,9 @@ def __init__(self, roles: Roles) -> None: self.create = to_streamed_response_wrapper( roles.create, ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) self.list = to_streamed_response_wrapper( roles.list, ) @@ -390,6 +480,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = async_to_streamed_response_wrapper( roles.create, ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) self.list = async_to_streamed_response_wrapper( roles.list, ) diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py index ebb62b0eb8..d14d8dce41 100644 --- a/src/openai/types/admin/organization/__init__.py +++ b/src/openai/types/admin/organization/__init__.py @@ -34,13 +34,17 @@ from .invite_delete_response import InviteDeleteResponse as InviteDeleteResponse from .audit_log_list_response import AuditLogListResponse as AuditLogListResponse from .certificate_list_params import CertificateListParams as CertificateListParams +from .spend_alert_list_params import SpendAlertListParams as SpendAlertListParams from .usage_embeddings_params import UsageEmbeddingsParams as UsageEmbeddingsParams +from .organization_spend_alert import OrganizationSpendAlert as OrganizationSpendAlert from .usage_completions_params import UsageCompletionsParams as UsageCompletionsParams from .usage_moderations_params import UsageModerationsParams as UsageModerationsParams from .admin_api_key_list_params import AdminAPIKeyListParams as AdminAPIKeyListParams from .certificate_create_params import CertificateCreateParams as CertificateCreateParams from .certificate_list_response import CertificateListResponse as CertificateListResponse from .certificate_update_params import CertificateUpdateParams as CertificateUpdateParams +from .spend_alert_create_params import SpendAlertCreateParams as SpendAlertCreateParams +from .spend_alert_update_params import SpendAlertUpdateParams as SpendAlertUpdateParams from .usage_embeddings_response import UsageEmbeddingsResponse as UsageEmbeddingsResponse from .usage_completions_response import UsageCompletionsResponse as UsageCompletionsResponse from .usage_moderations_response import UsageModerationsResponse as UsageModerationsResponse @@ -49,7 +53,9 @@ from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams from .certificate_delete_response import CertificateDeleteResponse as CertificateDeleteResponse from .certificate_retrieve_params import CertificateRetrieveParams as CertificateRetrieveParams +from .organization_data_retention import OrganizationDataRetention as OrganizationDataRetention from .usage_audio_speeches_params import UsageAudioSpeechesParams as UsageAudioSpeechesParams +from .data_retention_update_params import DataRetentionUpdateParams as DataRetentionUpdateParams from .usage_vector_stores_response import UsageVectorStoresResponse as UsageVectorStoresResponse from .admin_api_key_create_response import AdminAPIKeyCreateResponse as AdminAPIKeyCreateResponse from .admin_api_key_delete_response import AdminAPIKeyDeleteResponse as AdminAPIKeyDeleteResponse @@ -60,6 +66,7 @@ from .usage_file_search_calls_params import UsageFileSearchCallsParams as UsageFileSearchCallsParams from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse from .usage_web_search_calls_response import UsageWebSearchCallsResponse as UsageWebSearchCallsResponse +from .organization_spend_alert_deleted import OrganizationSpendAlertDeleted as OrganizationSpendAlertDeleted from .usage_file_search_calls_response import UsageFileSearchCallsResponse as UsageFileSearchCallsResponse from .usage_audio_transcriptions_params import UsageAudioTranscriptionsParams as UsageAudioTranscriptionsParams from .usage_audio_transcriptions_response import UsageAudioTranscriptionsResponse as UsageAudioTranscriptionsResponse diff --git a/src/openai/types/admin/organization/data_retention_update_params.py b/src/openai/types/admin/organization/data_retention_update_params.py new file mode 100644 index 0000000000..b6510e7955 --- /dev/null +++ b/src/openai/types/admin/organization/data_retention_update_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["DataRetentionUpdateParams"] + + +class DataRetentionUpdateParams(TypedDict, total=False): + retention_type: Required[ + Literal[ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ] + ] + """The desired organization data retention type.""" diff --git a/src/openai/types/admin/organization/group.py b/src/openai/types/admin/organization/group.py index a5823b1442..045980478f 100644 --- a/src/openai/types/admin/organization/group.py +++ b/src/openai/types/admin/organization/group.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing_extensions import Literal + from ...._models import BaseModel __all__ = ["Group"] @@ -14,7 +16,7 @@ class Group(BaseModel): created_at: int """Unix timestamp (in seconds) when the group was created.""" - group_type: str + group_type: Literal["group", "tenant_group"] """The type of the group.""" is_scim_managed: bool diff --git a/src/openai/types/admin/organization/groups/__init__.py b/src/openai/types/admin/organization/groups/__init__.py index 22189c1085..884cc0d92f 100644 --- a/src/openai/types/admin/organization/groups/__init__.py +++ b/src/openai/types/admin/organization/groups/__init__.py @@ -11,4 +11,6 @@ from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse from .user_create_response import UserCreateResponse as UserCreateResponse from .user_delete_response import UserDeleteResponse as UserDeleteResponse +from .role_retrieve_response import RoleRetrieveResponse as RoleRetrieveResponse +from .user_retrieve_response import UserRetrieveResponse as UserRetrieveResponse from .organization_group_user import OrganizationGroupUser as OrganizationGroupUser diff --git a/src/openai/types/admin/organization/groups/role_list_response.py b/src/openai/types/admin/organization/groups/role_list_response.py index 337d517ba1..fc64f8e9a0 100644 --- a/src/openai/types/admin/organization/groups/role_list_response.py +++ b/src/openai/types/admin/organization/groups/role_list_response.py @@ -4,7 +4,13 @@ from ....._models import BaseModel -__all__ = ["RoleListResponse"] +__all__ = ["RoleListResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str class RoleListResponse(BaseModel): @@ -15,6 +21,9 @@ class RoleListResponse(BaseModel): id: str """Identifier for the role.""" + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + created_at: Optional[int] = None """When the role was created.""" diff --git a/src/openai/types/admin/organization/groups/role_retrieve_response.py b/src/openai/types/admin/organization/groups/role_retrieve_response.py new file mode 100644 index 0000000000..576010daad --- /dev/null +++ b/src/openai/types/admin/organization/groups/role_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ....._models import BaseModel + +__all__ = ["RoleRetrieveResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleRetrieveResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/src/openai/types/admin/organization/groups/user_retrieve_response.py b/src/openai/types/admin/organization/groups/user_retrieve_response.py new file mode 100644 index 0000000000..977db3577f --- /dev/null +++ b/src/openai/types/admin/organization/groups/user_retrieve_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["UserRetrieveResponse"] + + +class UserRetrieveResponse(BaseModel): + """Details about a user returned from an organization group membership lookup.""" + + id: str + """Identifier for the user.""" + + email: Optional[str] = None + """Email address of the user, or `null` for users without an email.""" + + is_service_account: Optional[bool] = None + """Whether the user is a service account.""" + + name: str + """Display name of the user.""" + + picture: Optional[str] = None + """URL of the user's profile picture, if available.""" + + user_type: Literal["user", "tenant_user"] + """The type of user.""" diff --git a/src/openai/types/admin/organization/organization_data_retention.py b/src/openai/types/admin/organization/organization_data_retention.py new file mode 100644 index 0000000000..0022094f69 --- /dev/null +++ b/src/openai/types/admin/organization/organization_data_retention.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationDataRetention"] + + +class OrganizationDataRetention(BaseModel): + """Represents the organization's data retention control setting.""" + + object: Literal["organization.data_retention"] + """The object type, which is always `organization.data_retention`.""" + + type: Literal[ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ] + """The configured organization data retention type.""" diff --git a/src/openai/types/admin/organization/organization_spend_alert.py b/src/openai/types/admin/organization/organization_spend_alert.py new file mode 100644 index 0000000000..a541926a01 --- /dev/null +++ b/src/openai/types/admin/organization/organization_spend_alert.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationSpendAlert", "NotificationChannel"] + + +class NotificationChannel(BaseModel): + """Email notification settings for a spend alert.""" + + recipients: List[str] + """Email addresses that receive the spend alert notification.""" + + type: Literal["email"] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] = None + """Optional subject prefix for alert emails.""" + + +class OrganizationSpendAlert(BaseModel): + """Represents a spend alert configured at the organization level.""" + + id: str + """The identifier, which can be referenced in API endpoints.""" + + currency: Literal["USD"] + """The currency for the threshold amount.""" + + interval: Literal["month"] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: NotificationChannel + """Email notification settings for a spend alert.""" + + object: Literal["organization.spend_alert"] + """The object type, which is always `organization.spend_alert`.""" + + threshold_amount: int + """The alert threshold amount, in cents.""" diff --git a/src/openai/types/admin/organization/organization_spend_alert_deleted.py b/src/openai/types/admin/organization/organization_spend_alert_deleted.py new file mode 100644 index 0000000000..74fab027ee --- /dev/null +++ b/src/openai/types/admin/organization/organization_spend_alert_deleted.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationSpendAlertDeleted"] + + +class OrganizationSpendAlertDeleted(BaseModel): + """Confirmation payload returned after deleting an organization spend alert.""" + + id: str + """The deleted spend alert ID.""" + + deleted: bool + """Whether the spend alert was deleted.""" + + object: Literal["organization.spend_alert.deleted"] + """Always `organization.spend_alert.deleted`.""" diff --git a/src/openai/types/admin/organization/projects/__init__.py b/src/openai/types/admin/organization/projects/__init__.py index 1b78bae7a8..1c510be58d 100644 --- a/src/openai/types/admin/organization/projects/__init__.py +++ b/src/openai/types/admin/organization/projects/__init__.py @@ -15,19 +15,28 @@ from .user_update_params import UserUpdateParams as UserUpdateParams from .api_key_list_params import APIKeyListParams as APIKeyListParams from .group_create_params import GroupCreateParams as GroupCreateParams +from .project_spend_alert import ProjectSpendAlert as ProjectSpendAlert from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse from .user_delete_response import UserDeleteResponse as UserDeleteResponse from .group_delete_response import GroupDeleteResponse as GroupDeleteResponse +from .group_retrieve_params import GroupRetrieveParams as GroupRetrieveParams +from .project_data_retention import ProjectDataRetention as ProjectDataRetention from .api_key_delete_response import APIKeyDeleteResponse as APIKeyDeleteResponse from .certificate_list_params import CertificateListParams as CertificateListParams from .project_service_account import ProjectServiceAccount as ProjectServiceAccount +from .spend_alert_list_params import SpendAlertListParams as SpendAlertListParams from .certificate_list_response import CertificateListResponse as CertificateListResponse from .project_model_permissions import ProjectModelPermissions as ProjectModelPermissions +from .spend_alert_create_params import SpendAlertCreateParams as SpendAlertCreateParams +from .spend_alert_update_params import SpendAlertUpdateParams as SpendAlertUpdateParams from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams +from .project_spend_alert_deleted import ProjectSpendAlertDeleted as ProjectSpendAlertDeleted from .service_account_list_params import ServiceAccountListParams as ServiceAccountListParams +from .data_retention_update_params import DataRetentionUpdateParams as DataRetentionUpdateParams from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams from .service_account_create_params import ServiceAccountCreateParams as ServiceAccountCreateParams +from .service_account_update_params import ServiceAccountUpdateParams as ServiceAccountUpdateParams from .model_permission_update_params import ModelPermissionUpdateParams as ModelPermissionUpdateParams from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse from .project_hosted_tool_permissions import ProjectHostedToolPermissions as ProjectHostedToolPermissions diff --git a/src/openai/types/admin/organization/projects/data_retention_update_params.py b/src/openai/types/admin/organization/projects/data_retention_update_params.py new file mode 100644 index 0000000000..e7291d207a --- /dev/null +++ b/src/openai/types/admin/organization/projects/data_retention_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["DataRetentionUpdateParams"] + + +class DataRetentionUpdateParams(TypedDict, total=False): + retention_type: Required[ + Literal[ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ] + ] + """The desired project data retention type.""" diff --git a/src/openai/types/admin/organization/projects/group_retrieve_params.py b/src/openai/types/admin/organization/projects/group_retrieve_params.py new file mode 100644 index 0000000000..084f345f43 --- /dev/null +++ b/src/openai/types/admin/organization/projects/group_retrieve_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["GroupRetrieveParams"] + + +class GroupRetrieveParams(TypedDict, total=False): + project_id: Required[str] + + group_type: Literal["group", "tenant_group"] + """The type of group to retrieve.""" diff --git a/src/openai/types/admin/organization/projects/groups/__init__.py b/src/openai/types/admin/organization/projects/groups/__init__.py index ed464fde83..5e67517593 100644 --- a/src/openai/types/admin/organization/projects/groups/__init__.py +++ b/src/openai/types/admin/organization/projects/groups/__init__.py @@ -7,3 +7,4 @@ from .role_list_response import RoleListResponse as RoleListResponse from .role_create_response import RoleCreateResponse as RoleCreateResponse from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .role_retrieve_response import RoleRetrieveResponse as RoleRetrieveResponse diff --git a/src/openai/types/admin/organization/projects/groups/role_list_response.py b/src/openai/types/admin/organization/projects/groups/role_list_response.py index 72934bde13..d43d0f806b 100644 --- a/src/openai/types/admin/organization/projects/groups/role_list_response.py +++ b/src/openai/types/admin/organization/projects/groups/role_list_response.py @@ -4,7 +4,13 @@ from ......_models import BaseModel -__all__ = ["RoleListResponse"] +__all__ = ["RoleListResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str class RoleListResponse(BaseModel): @@ -15,6 +21,9 @@ class RoleListResponse(BaseModel): id: str """Identifier for the role.""" + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + created_at: Optional[int] = None """When the role was created.""" diff --git a/src/openai/types/admin/organization/projects/groups/role_retrieve_response.py b/src/openai/types/admin/organization/projects/groups/role_retrieve_response.py new file mode 100644 index 0000000000..0c10cf0092 --- /dev/null +++ b/src/openai/types/admin/organization/projects/groups/role_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ......_models import BaseModel + +__all__ = ["RoleRetrieveResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleRetrieveResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/src/openai/types/admin/organization/projects/project_data_retention.py b/src/openai/types/admin/organization/projects/project_data_retention.py new file mode 100644 index 0000000000..30329e60f4 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_data_retention.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectDataRetention"] + + +class ProjectDataRetention(BaseModel): + """Represents a project's data retention control setting.""" + + object: Literal["project.data_retention"] + """The object type, which is always `project.data_retention`.""" + + type: Literal[ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ] + """The configured project data retention type.""" diff --git a/src/openai/types/admin/organization/projects/project_group.py b/src/openai/types/admin/organization/projects/project_group.py index b3da08ca32..4efb29f91e 100644 --- a/src/openai/types/admin/organization/projects/project_group.py +++ b/src/openai/types/admin/organization/projects/project_group.py @@ -19,7 +19,7 @@ class ProjectGroup(BaseModel): group_name: str """Display name of the group.""" - group_type: str + group_type: Literal["group", "tenant_group"] """The type of the group.""" object: Literal["project.group"] diff --git a/src/openai/types/admin/organization/projects/project_spend_alert.py b/src/openai/types/admin/organization/projects/project_spend_alert.py new file mode 100644 index 0000000000..269ea54db5 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_spend_alert.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectSpendAlert", "NotificationChannel"] + + +class NotificationChannel(BaseModel): + """Email notification settings for a spend alert.""" + + recipients: List[str] + """Email addresses that receive the spend alert notification.""" + + type: Literal["email"] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] = None + """Optional subject prefix for alert emails.""" + + +class ProjectSpendAlert(BaseModel): + """Represents a spend alert configured at the project level.""" + + id: str + """The identifier, which can be referenced in API endpoints.""" + + currency: Literal["USD"] + """The currency for the threshold amount.""" + + interval: Literal["month"] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: NotificationChannel + """Email notification settings for a spend alert.""" + + object: Literal["project.spend_alert"] + """The object type, which is always `project.spend_alert`.""" + + threshold_amount: int + """The alert threshold amount, in cents.""" diff --git a/src/openai/types/admin/organization/projects/project_spend_alert_deleted.py b/src/openai/types/admin/organization/projects/project_spend_alert_deleted.py new file mode 100644 index 0000000000..68be581307 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_spend_alert_deleted.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectSpendAlertDeleted"] + + +class ProjectSpendAlertDeleted(BaseModel): + """Confirmation payload returned after deleting a project spend alert.""" + + id: str + """The deleted spend alert ID.""" + + deleted: bool + """Whether the spend alert was deleted.""" + + object: Literal["project.spend_alert.deleted"] + """Always `project.spend_alert.deleted`.""" diff --git a/src/openai/types/admin/organization/projects/service_account_update_params.py b/src/openai/types/admin/organization/projects/service_account_update_params.py new file mode 100644 index 0000000000..852e5d5a5a --- /dev/null +++ b/src/openai/types/admin/organization/projects/service_account_update_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ServiceAccountUpdateParams"] + + +class ServiceAccountUpdateParams(TypedDict, total=False): + project_id: Required[str] + + name: str + """The updated service account name.""" + + role: Literal["member", "owner"] + """The updated service account role.""" diff --git a/src/openai/types/admin/organization/projects/spend_alert_create_params.py b/src/openai/types/admin/organization/projects/spend_alert_create_params.py new file mode 100644 index 0000000000..74914d8646 --- /dev/null +++ b/src/openai/types/admin/organization/projects/spend_alert_create_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["SpendAlertCreateParams", "NotificationChannel"] + + +class SpendAlertCreateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: Required[NotificationChannel] + """Email notification settings for a spend alert.""" + + threshold_amount: Required[int] + """The alert threshold amount, in cents.""" + + +class NotificationChannel(TypedDict, total=False): + """Email notification settings for a spend alert.""" + + recipients: Required[SequenceNotStr[str]] + """Email addresses that receive the spend alert notification.""" + + type: Required[Literal["email"]] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] + """Optional subject prefix for alert emails.""" diff --git a/src/openai/types/admin/organization/projects/spend_alert_list_params.py b/src/openai/types/admin/organization/projects/spend_alert_list_params.py new file mode 100644 index 0000000000..a37bad5879 --- /dev/null +++ b/src/openai/types/admin/organization/projects/spend_alert_list_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["SpendAlertListParams"] + + +class SpendAlertListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the ID of the last spend alert from the previous response to fetch the + next page. + """ + + before: str + """Cursor for pagination. + + Provide the ID of the first spend alert from the previous response to fetch the + previous page. + """ + + limit: int + """A limit on the number of spend alerts to return. Defaults to 20.""" + + order: Literal["asc", "desc"] + """Sort order for the returned spend alerts.""" diff --git a/src/openai/types/admin/organization/projects/spend_alert_update_params.py b/src/openai/types/admin/organization/projects/spend_alert_update_params.py new file mode 100644 index 0000000000..1611c531e8 --- /dev/null +++ b/src/openai/types/admin/organization/projects/spend_alert_update_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["SpendAlertUpdateParams", "NotificationChannel"] + + +class SpendAlertUpdateParams(TypedDict, total=False): + project_id: Required[str] + + currency: Required[Literal["USD"]] + """The currency for the threshold amount.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: Required[NotificationChannel] + """Email notification settings for a spend alert.""" + + threshold_amount: Required[int] + """The alert threshold amount, in cents.""" + + +class NotificationChannel(TypedDict, total=False): + """Email notification settings for a spend alert.""" + + recipients: Required[SequenceNotStr[str]] + """Email addresses that receive the spend alert notification.""" + + type: Required[Literal["email"]] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] + """Optional subject prefix for alert emails.""" diff --git a/src/openai/types/admin/organization/projects/users/__init__.py b/src/openai/types/admin/organization/projects/users/__init__.py index ed464fde83..5e67517593 100644 --- a/src/openai/types/admin/organization/projects/users/__init__.py +++ b/src/openai/types/admin/organization/projects/users/__init__.py @@ -7,3 +7,4 @@ from .role_list_response import RoleListResponse as RoleListResponse from .role_create_response import RoleCreateResponse as RoleCreateResponse from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .role_retrieve_response import RoleRetrieveResponse as RoleRetrieveResponse diff --git a/src/openai/types/admin/organization/projects/users/role_list_response.py b/src/openai/types/admin/organization/projects/users/role_list_response.py index 72934bde13..d43d0f806b 100644 --- a/src/openai/types/admin/organization/projects/users/role_list_response.py +++ b/src/openai/types/admin/organization/projects/users/role_list_response.py @@ -4,7 +4,13 @@ from ......_models import BaseModel -__all__ = ["RoleListResponse"] +__all__ = ["RoleListResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str class RoleListResponse(BaseModel): @@ -15,6 +21,9 @@ class RoleListResponse(BaseModel): id: str """Identifier for the role.""" + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + created_at: Optional[int] = None """When the role was created.""" diff --git a/src/openai/types/admin/organization/projects/users/role_retrieve_response.py b/src/openai/types/admin/organization/projects/users/role_retrieve_response.py new file mode 100644 index 0000000000..0c10cf0092 --- /dev/null +++ b/src/openai/types/admin/organization/projects/users/role_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ......_models import BaseModel + +__all__ = ["RoleRetrieveResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleRetrieveResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/src/openai/types/admin/organization/spend_alert_create_params.py b/src/openai/types/admin/organization/spend_alert_create_params.py new file mode 100644 index 0000000000..f787abdfe1 --- /dev/null +++ b/src/openai/types/admin/organization/spend_alert_create_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["SpendAlertCreateParams", "NotificationChannel"] + + +class SpendAlertCreateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: Required[NotificationChannel] + """Email notification settings for a spend alert.""" + + threshold_amount: Required[int] + """The alert threshold amount, in cents.""" + + +class NotificationChannel(TypedDict, total=False): + """Email notification settings for a spend alert.""" + + recipients: Required[SequenceNotStr[str]] + """Email addresses that receive the spend alert notification.""" + + type: Required[Literal["email"]] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] + """Optional subject prefix for alert emails.""" diff --git a/src/openai/types/admin/organization/spend_alert_list_params.py b/src/openai/types/admin/organization/spend_alert_list_params.py new file mode 100644 index 0000000000..a37bad5879 --- /dev/null +++ b/src/openai/types/admin/organization/spend_alert_list_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["SpendAlertListParams"] + + +class SpendAlertListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the ID of the last spend alert from the previous response to fetch the + next page. + """ + + before: str + """Cursor for pagination. + + Provide the ID of the first spend alert from the previous response to fetch the + previous page. + """ + + limit: int + """A limit on the number of spend alerts to return. Defaults to 20.""" + + order: Literal["asc", "desc"] + """Sort order for the returned spend alerts.""" diff --git a/src/openai/types/admin/organization/spend_alert_update_params.py b/src/openai/types/admin/organization/spend_alert_update_params.py new file mode 100644 index 0000000000..ddba8a81e1 --- /dev/null +++ b/src/openai/types/admin/organization/spend_alert_update_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["SpendAlertUpdateParams", "NotificationChannel"] + + +class SpendAlertUpdateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: Required[NotificationChannel] + """Email notification settings for a spend alert.""" + + threshold_amount: Required[int] + """The alert threshold amount, in cents.""" + + +class NotificationChannel(TypedDict, total=False): + """Email notification settings for a spend alert.""" + + recipients: Required[SequenceNotStr[str]] + """Email addresses that receive the spend alert notification.""" + + type: Required[Literal["email"]] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] + """Optional subject prefix for alert emails.""" diff --git a/src/openai/types/admin/organization/users/__init__.py b/src/openai/types/admin/organization/users/__init__.py index ed464fde83..5e67517593 100644 --- a/src/openai/types/admin/organization/users/__init__.py +++ b/src/openai/types/admin/organization/users/__init__.py @@ -7,3 +7,4 @@ from .role_list_response import RoleListResponse as RoleListResponse from .role_create_response import RoleCreateResponse as RoleCreateResponse from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .role_retrieve_response import RoleRetrieveResponse as RoleRetrieveResponse diff --git a/src/openai/types/admin/organization/users/role_list_response.py b/src/openai/types/admin/organization/users/role_list_response.py index 337d517ba1..fc64f8e9a0 100644 --- a/src/openai/types/admin/organization/users/role_list_response.py +++ b/src/openai/types/admin/organization/users/role_list_response.py @@ -4,7 +4,13 @@ from ....._models import BaseModel -__all__ = ["RoleListResponse"] +__all__ = ["RoleListResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str class RoleListResponse(BaseModel): @@ -15,6 +21,9 @@ class RoleListResponse(BaseModel): id: str """Identifier for the role.""" + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + created_at: Optional[int] = None """When the role was created.""" diff --git a/src/openai/types/admin/organization/users/role_retrieve_response.py b/src/openai/types/admin/organization/users/role_retrieve_response.py new file mode 100644 index 0000000000..576010daad --- /dev/null +++ b/src/openai/types/admin/organization/users/role_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ....._models import BaseModel + +__all__ = ["RoleRetrieveResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleRetrieveResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py index d3d4afbee3..de6001e146 100644 --- a/src/openai/types/responses/response_function_web_search.py +++ b/src/openai/types/responses/response_function_web_search.py @@ -46,7 +46,7 @@ class ActionOpenPage(BaseModel): """Action type "open_page" - Opens a specific URL from search results.""" type: Literal["open_page"] - """The action type. Always `open_page`.""" + """The action type.""" url: Optional[str] = None """The URL opened by the model.""" diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py index e0d50757a3..15e313b0d3 100644 --- a/src/openai/types/responses/response_function_web_search_param.py +++ b/src/openai/types/responses/response_function_web_search_param.py @@ -47,7 +47,7 @@ class ActionOpenPage(TypedDict, total=False): """Action type "open_page" - Opens a specific URL from search results.""" type: Required[Literal["open_page"]] - """The action type. Always `open_page`.""" + """The action type.""" url: Optional[str] """The URL opened by the model.""" diff --git a/tests/api_resources/admin/organization/groups/test_roles.py b/tests/api_resources/admin/organization/groups/test_roles.py index 08702fddd2..6f2235a468 100644 --- a/tests/api_resources/admin/organization/groups/test_roles.py +++ b/tests/api_resources/admin/organization/groups/test_roles.py @@ -14,6 +14,7 @@ RoleListResponse, RoleCreateResponse, RoleDeleteResponse, + RoleRetrieveResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -64,6 +65,54 @@ def test_path_params_create(self, client: OpenAI) -> None: role_id="role_id", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + role = client.admin.organization.groups.roles.retrieve( + role_id="role_id", + group_id="group_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.groups.roles.with_raw_response.retrieve( + role_id="role_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.groups.roles.with_streaming_response.retrieve( + role_id="role_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.roles.with_raw_response.retrieve( + role_id="role_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.groups.roles.with_raw_response.retrieve( + role_id="", + group_id="group_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: role = client.admin.organization.groups.roles.list( @@ -208,6 +257,54 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: role_id="role_id", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.groups.roles.retrieve( + role_id="role_id", + group_id="group_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.roles.with_raw_response.retrieve( + role_id="role_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.roles.with_streaming_response.retrieve( + role_id="role_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.roles.with_raw_response.retrieve( + role_id="role_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.groups.roles.with_raw_response.retrieve( + role_id="", + group_id="group_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.groups.roles.list( diff --git a/tests/api_resources/admin/organization/groups/test_users.py b/tests/api_resources/admin/organization/groups/test_users.py index eda6be6bbf..5003db2ad1 100644 --- a/tests/api_resources/admin/organization/groups/test_users.py +++ b/tests/api_resources/admin/organization/groups/test_users.py @@ -13,6 +13,7 @@ from openai.types.admin.organization.groups import ( UserCreateResponse, UserDeleteResponse, + UserRetrieveResponse, OrganizationGroupUser, ) @@ -64,6 +65,54 @@ def test_path_params_create(self, client: OpenAI) -> None: user_id="user_id", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + user = client.admin.organization.groups.users.retrieve( + user_id="user_id", + group_id="group_id", + ) + assert_matches_type(UserRetrieveResponse, user, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.groups.users.with_raw_response.retrieve( + user_id="user_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserRetrieveResponse, user, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.groups.users.with_streaming_response.retrieve( + user_id="user_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(UserRetrieveResponse, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.users.with_raw_response.retrieve( + user_id="user_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.groups.users.with_raw_response.retrieve( + user_id="", + group_id="group_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: user = client.admin.organization.groups.users.list( @@ -208,6 +257,54 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: user_id="user_id", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.groups.users.retrieve( + user_id="user_id", + group_id="group_id", + ) + assert_matches_type(UserRetrieveResponse, user, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.users.with_raw_response.retrieve( + user_id="user_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserRetrieveResponse, user, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.users.with_streaming_response.retrieve( + user_id="user_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(UserRetrieveResponse, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.users.with_raw_response.retrieve( + user_id="user_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.groups.users.with_raw_response.retrieve( + user_id="", + group_id="group_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: user = await async_client.admin.organization.groups.users.list( diff --git a/tests/api_resources/admin/organization/projects/groups/test_roles.py b/tests/api_resources/admin/organization/projects/groups/test_roles.py index a129142ea9..4e62facc55 100644 --- a/tests/api_resources/admin/organization/projects/groups/test_roles.py +++ b/tests/api_resources/admin/organization/projects/groups/test_roles.py @@ -14,6 +14,7 @@ RoleListResponse, RoleCreateResponse, RoleDeleteResponse, + RoleRetrieveResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -75,6 +76,66 @@ def test_path_params_create(self, client: OpenAI) -> None: role_id="role_id", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + role = client.admin.organization.projects.groups.roles.retrieve( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.groups.roles.with_streaming_response.retrieve( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="", + group_id="group_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="", + project_id="project_id", + group_id="group_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: role = client.admin.organization.projects.groups.roles.list( @@ -253,6 +314,66 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: role_id="role_id", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.groups.roles.retrieve( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.groups.roles.with_streaming_response.retrieve( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="", + group_id="group_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="", + project_id="project_id", + group_id="group_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.projects.groups.roles.list( diff --git a/tests/api_resources/admin/organization/projects/test_data_retention.py b/tests/api_resources/admin/organization/projects/test_data_retention.py new file mode 100644 index 0000000000..d640e78e8d --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_data_retention.py @@ -0,0 +1,184 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.admin.organization.projects import ProjectDataRetention + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDataRetention: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + data_retention = client.admin.organization.projects.data_retention.retrieve( + "project_id", + ) + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.data_retention.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.data_retention.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.data_retention.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + data_retention = client.admin.organization.projects.data_retention.update( + project_id="project_id", + retention_type="organization_default", + ) + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.data_retention.with_raw_response.update( + project_id="project_id", + retention_type="organization_default", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.data_retention.with_streaming_response.update( + project_id="project_id", + retention_type="organization_default", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.data_retention.with_raw_response.update( + project_id="", + retention_type="organization_default", + ) + + +class TestAsyncDataRetention: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + data_retention = await async_client.admin.organization.projects.data_retention.retrieve( + "project_id", + ) + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.data_retention.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.data_retention.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = await response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.data_retention.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + data_retention = await async_client.admin.organization.projects.data_retention.update( + project_id="project_id", + retention_type="organization_default", + ) + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.data_retention.with_raw_response.update( + project_id="project_id", + retention_type="organization_default", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.data_retention.with_streaming_response.update( + project_id="project_id", + retention_type="organization_default", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = await response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.data_retention.with_raw_response.update( + project_id="", + retention_type="organization_default", + ) diff --git a/tests/api_resources/admin/organization/projects/test_groups.py b/tests/api_resources/admin/organization/projects/test_groups.py index 2db448e9b2..46a68076df 100644 --- a/tests/api_resources/admin/organization/projects/test_groups.py +++ b/tests/api_resources/admin/organization/projects/test_groups.py @@ -67,6 +67,63 @@ def test_path_params_create(self, client: OpenAI) -> None: role="role", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + group = client.admin.organization.projects.groups.retrieve( + group_id="group_id", + project_id="project_id", + ) + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + def test_method_retrieve_with_all_params(self, client: OpenAI) -> None: + group = client.admin.organization.projects.groups.retrieve( + group_id="group_id", + project_id="project_id", + group_type="group", + ) + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.groups.with_raw_response.retrieve( + group_id="group_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.groups.with_streaming_response.retrieve( + group_id="group_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = response.parse() + assert_matches_type(ProjectGroup, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.groups.with_raw_response.retrieve( + group_id="group_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.projects.groups.with_raw_response.retrieve( + group_id="", + project_id="project_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: group = client.admin.organization.projects.groups.list( @@ -215,6 +272,63 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: role="role", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.projects.groups.retrieve( + group_id="group_id", + project_id="project_id", + ) + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.projects.groups.retrieve( + group_id="group_id", + project_id="project_id", + group_type="group", + ) + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.groups.with_raw_response.retrieve( + group_id="group_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.groups.with_streaming_response.retrieve( + group_id="group_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = await response.parse() + assert_matches_type(ProjectGroup, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.groups.with_raw_response.retrieve( + group_id="group_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.projects.groups.with_raw_response.retrieve( + group_id="", + project_id="project_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: group = await async_client.admin.organization.projects.groups.list( diff --git a/tests/api_resources/admin/organization/projects/test_roles.py b/tests/api_resources/admin/organization/projects/test_roles.py index 8c78ea21b4..862ea1f412 100644 --- a/tests/api_resources/admin/organization/projects/test_roles.py +++ b/tests/api_resources/admin/organization/projects/test_roles.py @@ -77,6 +77,54 @@ def test_path_params_create(self, client: OpenAI) -> None: role_name="role_name", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + role = client.admin.organization.projects.roles.retrieve( + role_id="role_id", + project_id="project_id", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.roles.with_streaming_response.retrieve( + role_id="role_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.projects.roles.with_raw_response.retrieve( + role_id="", + project_id="project_id", + ) + @parametrize def test_method_update(self, client: OpenAI) -> None: role = client.admin.organization.projects.roles.update( @@ -294,6 +342,54 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: role_name="role_name", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.roles.retrieve( + role_id="role_id", + project_id="project_id", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.roles.with_streaming_response.retrieve( + role_id="role_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.projects.roles.with_raw_response.retrieve( + role_id="", + project_id="project_id", + ) + @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.projects.roles.update( diff --git a/tests/api_resources/admin/organization/projects/test_service_accounts.py b/tests/api_resources/admin/organization/projects/test_service_accounts.py index 7c94283323..e8e68596a2 100644 --- a/tests/api_resources/admin/organization/projects/test_service_accounts.py +++ b/tests/api_resources/admin/organization/projects/test_service_accounts.py @@ -112,6 +112,64 @@ def test_path_params_retrieve(self, client: OpenAI) -> None: project_id="project_id", ) + @parametrize + def test_method_update(self, client: OpenAI) -> None: + service_account = client.admin.organization.projects.service_accounts.update( + service_account_id="service_account_id", + project_id="project_id", + ) + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + service_account = client.admin.organization.projects.service_accounts.update( + service_account_id="service_account_id", + project_id="project_id", + name="name", + role="member", + ) + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.service_accounts.with_raw_response.update( + service_account_id="service_account_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + service_account = response.parse() + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.service_accounts.with_streaming_response.update( + service_account_id="service_account_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + service_account = response.parse() + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.service_accounts.with_raw_response.update( + service_account_id="service_account_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `service_account_id` but received ''"): + client.admin.organization.projects.service_accounts.with_raw_response.update( + service_account_id="", + project_id="project_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: service_account = client.admin.organization.projects.service_accounts.list( @@ -303,6 +361,64 @@ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: project_id="project_id", ) + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + service_account = await async_client.admin.organization.projects.service_accounts.update( + service_account_id="service_account_id", + project_id="project_id", + ) + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + service_account = await async_client.admin.organization.projects.service_accounts.update( + service_account_id="service_account_id", + project_id="project_id", + name="name", + role="member", + ) + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.service_accounts.with_raw_response.update( + service_account_id="service_account_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + service_account = response.parse() + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.service_accounts.with_streaming_response.update( + service_account_id="service_account_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + service_account = await response.parse() + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.service_accounts.with_raw_response.update( + service_account_id="service_account_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `service_account_id` but received ''"): + await async_client.admin.organization.projects.service_accounts.with_raw_response.update( + service_account_id="", + project_id="project_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: service_account = await async_client.admin.organization.projects.service_accounts.list( diff --git a/tests/api_resources/admin/organization/projects/test_spend_alerts.py b/tests/api_resources/admin/organization/projects/test_spend_alerts.py new file mode 100644 index 0000000000..c763401af3 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_spend_alerts.py @@ -0,0 +1,582 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization.projects import ( + ProjectSpendAlert, + ProjectSpendAlertDeleted, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSpendAlerts: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.projects.spend_alerts.with_raw_response.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.projects.spend_alerts.with_streaming_response.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.create( + project_id="", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.spend_alerts.with_raw_response.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.spend_alerts.with_streaming_response.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.update( + alert_id="alert_id", + project_id="", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.update( + alert_id="", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.list( + project_id="project_id", + ) + assert_matches_type(SyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.list( + project_id="project_id", + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(SyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.projects.spend_alerts.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(SyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.projects.spend_alerts.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(SyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.list( + project_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.delete( + alert_id="alert_id", + project_id="project_id", + ) + assert_matches_type(ProjectSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.projects.spend_alerts.with_raw_response.delete( + alert_id="alert_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.projects.spend_alerts.with_streaming_response.delete( + alert_id="alert_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlertDeleted, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.delete( + alert_id="alert_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.delete( + alert_id="", + project_id="project_id", + ) + + +class TestAsyncSpendAlerts: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.spend_alerts.with_raw_response.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.spend_alerts.with_streaming_response.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.create( + project_id="", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.spend_alerts.with_raw_response.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.spend_alerts.with_streaming_response.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.update( + alert_id="alert_id", + project_id="", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.update( + alert_id="", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.list( + project_id="project_id", + ) + assert_matches_type(AsyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.list( + project_id="project_id", + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(AsyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.spend_alerts.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(AsyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.spend_alerts.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(AsyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.list( + project_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.delete( + alert_id="alert_id", + project_id="project_id", + ) + assert_matches_type(ProjectSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.spend_alerts.with_raw_response.delete( + alert_id="alert_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.spend_alerts.with_streaming_response.delete( + alert_id="alert_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(ProjectSpendAlertDeleted, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.delete( + alert_id="alert_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.delete( + alert_id="", + project_id="project_id", + ) diff --git a/tests/api_resources/admin/organization/projects/users/test_roles.py b/tests/api_resources/admin/organization/projects/users/test_roles.py index 99c52d494c..5212e5a59c 100644 --- a/tests/api_resources/admin/organization/projects/users/test_roles.py +++ b/tests/api_resources/admin/organization/projects/users/test_roles.py @@ -14,6 +14,7 @@ RoleListResponse, RoleCreateResponse, RoleDeleteResponse, + RoleRetrieveResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -75,6 +76,66 @@ def test_path_params_create(self, client: OpenAI) -> None: role_id="role_id", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + role = client.admin.organization.projects.users.roles.retrieve( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.users.roles.with_streaming_response.retrieve( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="", + user_id="user_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + user_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="", + project_id="project_id", + user_id="user_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: role = client.admin.organization.projects.users.roles.list( @@ -253,6 +314,66 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: role_id="role_id", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.users.roles.retrieve( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.users.roles.with_streaming_response.retrieve( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="", + user_id="user_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + user_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="", + project_id="project_id", + user_id="user_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.projects.users.roles.list( diff --git a/tests/api_resources/admin/organization/test_data_retention.py b/tests/api_resources/admin/organization/test_data_retention.py new file mode 100644 index 0000000000..8d943832e6 --- /dev/null +++ b/tests/api_resources/admin/organization/test_data_retention.py @@ -0,0 +1,136 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.admin.organization import OrganizationDataRetention + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDataRetention: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + data_retention = client.admin.organization.data_retention.retrieve() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.data_retention.with_raw_response.retrieve() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.data_retention.with_streaming_response.retrieve() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + data_retention = client.admin.organization.data_retention.update( + retention_type="zero_data_retention", + ) + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.data_retention.with_raw_response.update( + retention_type="zero_data_retention", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.data_retention.with_streaming_response.update( + retention_type="zero_data_retention", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncDataRetention: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + data_retention = await async_client.admin.organization.data_retention.retrieve() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.data_retention.with_raw_response.retrieve() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.data_retention.with_streaming_response.retrieve() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = await response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + data_retention = await async_client.admin.organization.data_retention.update( + retention_type="zero_data_retention", + ) + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.data_retention.with_raw_response.update( + retention_type="zero_data_retention", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.data_retention.with_streaming_response.update( + retention_type="zero_data_retention", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = await response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/test_groups.py b/tests/api_resources/admin/organization/test_groups.py index 0eca89f06c..d97ddf579e 100644 --- a/tests/api_resources/admin/organization/test_groups.py +++ b/tests/api_resources/admin/organization/test_groups.py @@ -53,6 +53,44 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + group = client.admin.organization.groups.retrieve( + "group_id", + ) + assert_matches_type(Group, group, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.groups.with_raw_response.retrieve( + "group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(Group, group, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.groups.with_streaming_response.retrieve( + "group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = response.parse() + assert_matches_type(Group, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.with_raw_response.retrieve( + "", + ) + @parametrize def test_method_update(self, client: OpenAI) -> None: group = client.admin.organization.groups.update( @@ -204,6 +242,44 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non assert cast(Any, response.is_closed) is True + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.groups.retrieve( + "group_id", + ) + assert_matches_type(Group, group, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.with_raw_response.retrieve( + "group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(Group, group, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.with_streaming_response.retrieve( + "group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = await response.parse() + assert_matches_type(Group, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.with_raw_response.retrieve( + "", + ) + @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: group = await async_client.admin.organization.groups.update( diff --git a/tests/api_resources/admin/organization/test_roles.py b/tests/api_resources/admin/organization/test_roles.py index ee70020c23..fbb8515f1c 100644 --- a/tests/api_resources/admin/organization/test_roles.py +++ b/tests/api_resources/admin/organization/test_roles.py @@ -64,6 +64,44 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + role = client.admin.organization.roles.retrieve( + "role_id", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.roles.with_raw_response.retrieve( + "role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.roles.with_streaming_response.retrieve( + "role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.roles.with_raw_response.retrieve( + "", + ) + @parametrize def test_method_update(self, client: OpenAI) -> None: role = client.admin.organization.roles.update( @@ -233,6 +271,44 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non assert cast(Any, response.is_closed) is True + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.roles.retrieve( + "role_id", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.roles.with_raw_response.retrieve( + "role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.roles.with_streaming_response.retrieve( + "role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.roles.with_raw_response.retrieve( + "", + ) + @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.roles.update( diff --git a/tests/api_resources/admin/organization/test_spend_alerts.py b/tests/api_resources/admin/organization/test_spend_alerts.py new file mode 100644 index 0000000000..08a3c445c3 --- /dev/null +++ b/tests/api_resources/admin/organization/test_spend_alerts.py @@ -0,0 +1,462 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization import ( + OrganizationSpendAlert, + OrganizationSpendAlertDeleted, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSpendAlerts: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.spend_alerts.with_raw_response.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.spend_alerts.with_streaming_response.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.spend_alerts.with_raw_response.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.spend_alerts.with_streaming_response.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + client.admin.organization.spend_alerts.with_raw_response.update( + alert_id="", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.list() + assert_matches_type(SyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.list( + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(SyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.spend_alerts.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(SyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.spend_alerts.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(SyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.delete( + "alert_id", + ) + assert_matches_type(OrganizationSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.spend_alerts.with_raw_response.delete( + "alert_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.spend_alerts.with_streaming_response.delete( + "alert_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlertDeleted, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + client.admin.organization.spend_alerts.with_raw_response.delete( + "", + ) + + +class TestAsyncSpendAlerts: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.spend_alerts.with_raw_response.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.spend_alerts.with_streaming_response.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.spend_alerts.with_raw_response.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.spend_alerts.with_streaming_response.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + await async_client.admin.organization.spend_alerts.with_raw_response.update( + alert_id="", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.list() + assert_matches_type(AsyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.list( + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(AsyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.spend_alerts.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(AsyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.spend_alerts.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(AsyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.delete( + "alert_id", + ) + assert_matches_type(OrganizationSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.spend_alerts.with_raw_response.delete( + "alert_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.spend_alerts.with_streaming_response.delete( + "alert_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(OrganizationSpendAlertDeleted, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + await async_client.admin.organization.spend_alerts.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/admin/organization/users/test_roles.py b/tests/api_resources/admin/organization/users/test_roles.py index 2455a38cff..81247b2416 100644 --- a/tests/api_resources/admin/organization/users/test_roles.py +++ b/tests/api_resources/admin/organization/users/test_roles.py @@ -14,6 +14,7 @@ RoleListResponse, RoleCreateResponse, RoleDeleteResponse, + RoleRetrieveResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -64,6 +65,54 @@ def test_path_params_create(self, client: OpenAI) -> None: role_id="role_id", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + role = client.admin.organization.users.roles.retrieve( + role_id="role_id", + user_id="user_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.users.roles.with_raw_response.retrieve( + role_id="role_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.users.roles.with_streaming_response.retrieve( + role_id="role_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.users.roles.with_raw_response.retrieve( + role_id="role_id", + user_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.users.roles.with_raw_response.retrieve( + role_id="", + user_id="user_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: role = client.admin.organization.users.roles.list( @@ -208,6 +257,54 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: role_id="role_id", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.users.roles.retrieve( + role_id="role_id", + user_id="user_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.users.roles.with_raw_response.retrieve( + role_id="role_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.users.roles.with_streaming_response.retrieve( + role_id="role_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.users.roles.with_raw_response.retrieve( + role_id="role_id", + user_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.users.roles.with_raw_response.retrieve( + role_id="", + user_id="user_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.users.roles.list( From 4f3e7d6b80d3131dec9472bceee205540a315450 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 17:00:15 +0000 Subject: [PATCH 12/12] release: 2.38.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 28c811f943..0e1c697558 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.37.0" + ".": "2.38.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 803d3d3a39..b4f9d758d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 2.38.0 (2026-05-21) + +Full Changelog: [v2.37.0...v2.38.0](https://github.com/openai/openai-python/compare/v2.37.0...v2.38.0) + +### Features + +* **api:** api update ([33d1d01](https://github.com/openai/openai-python/commit/33d1d013250053886a73d178136e6bd1b09df059)) +* **api:** manual updates ([a21700a](https://github.com/openai/openai-python/commit/a21700a2cd510cb9e6c88065ac8e942d4c041aa8)) +* **api:** update OpenAPI spec or Stainless config ([00265c5](https://github.com/openai/openai-python/commit/00265c5daba4d2481452ad35220f1556dab6bcf6)) + + +### Chores + +* **api:** docs updates ([ee10152](https://github.com/openai/openai-python/commit/ee101520d49e22c09cf8096f8cbb3848ea58a1f9)) +* check release PR custom code sync ([2638779](https://github.com/openai/openai-python/commit/2638779a5b8fffaa8fdb6eebc1d734f15d2491f8)) +* remove release automation trigger ([bd6eea5](https://github.com/openai/openai-python/commit/bd6eea559f2996d914258a65e645981bdce3cad4)) +* trigger release automation ([f62d082](https://github.com/openai/openai-python/commit/f62d08201eea8e08d4bb3385662f934d4adccb29)) + ## 2.37.0 (2026-05-13) Full Changelog: [v2.36.0...v2.37.0](https://github.com/openai/openai-python/compare/v2.36.0...v2.37.0) diff --git a/pyproject.toml b/pyproject.toml index 452ac3125a..aedb6d8f51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.37.0" +version = "2.38.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 43d6a12d19..ba9c5f3a3c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.37.0" # x-release-please-version +__version__ = "2.38.0" # x-release-please-version