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
+
+
[)](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
-
-
[)](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
+
+
[)](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
-
-
[)](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