diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 25d9dabb53..c33eb8cbe1 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "2.41.1"
+ ".": "2.42.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index a62e7b979c..693dbe3631 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 262
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-0161cb2a0faadedfc17ff59c7fcad9671962dbd170f83811003c23702148cf36.yml
-openapi_spec_hash: 1023c7442d0e2e7e213e8b3a253921c5
-config_hash: e02ca1082421dfe55b145c45e95d6126
+configured_endpoints: 264
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-4db8edc05bd5503d4aaad74e8b9c783aa1f4d40382a8075c53355bd18dfaf68c.yml
+openapi_spec_hash: 2466f6ad496b27334217999202e185c0
+config_hash: ef3ce17315a31703e7af0567b3e9738c
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1a2a2e0b16..ed57a6ca21 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,21 @@
# Changelog
+## 2.42.0 (2026-06-16)
+
+Full Changelog: [v2.41.1...v2.42.0](https://github.com/openai/openai-python/compare/v2.41.1...v2.42.0)
+
+### Features
+
+* **api:** admin spend_alerts ([6134198](https://github.com/openai/openai-python/commit/6134198a488996c4ff6fca4551afd55fb3294fdc))
+* **api:** manual updates ([f337bf4](https://github.com/openai/openai-python/commit/f337bf43276c880d2daf09a5d7f9fc9a886c4bf2))
+* **api:** update OpenAPI spec or Stainless config ([7015158](https://github.com/openai/openai-python/commit/7015158c3119acf57af6c20903587cef928530a9))
+
+
+### Build System
+
+* fix release workflow permissions ([#3389](https://github.com/openai/openai-python/issues/3389)) ([a526ee8](https://github.com/openai/openai-python/commit/a526ee813f085318fe3c6923ac3fa10c1cf56420))
+* Use CI environment for examples API key ([#3394](https://github.com/openai/openai-python/issues/3394)) ([d64d811](https://github.com/openai/openai-python/commit/d64d811e82aff724397e32d593e50657fee3f905))
+
## 2.41.1 (2026-06-05)
Full Changelog: [v2.41.0...v2.41.1](https://github.com/openai/openai-python/compare/v2.41.0...v2.41.1)
diff --git a/api.md b/api.md
index 10a729d995..56e01254dc 100644
--- a/api.md
+++ b/api.md
@@ -922,6 +922,7 @@ from openai.types.admin.organization import OrganizationSpendAlert, Organization
Methods:
- client.admin.organization.spend_alerts.create(\*\*params) -> OrganizationSpendAlert
+- client.admin.organization.spend_alerts.retrieve(alert_id) -> 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
@@ -1154,6 +1155,7 @@ from openai.types.admin.organization.projects import ProjectSpendAlert, ProjectS
Methods:
- client.admin.organization.projects.spend_alerts.create(project_id, \*\*params) -> ProjectSpendAlert
+- client.admin.organization.projects.spend_alerts.retrieve(alert_id, \*, project_id) -> 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
diff --git a/pyproject.toml b/pyproject.toml
index 75d0d5e246..f3d0b5edf2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "2.41.1"
+version = "2.42.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 55f1df75a9..aeadf9ba57 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.41.1" # x-release-please-version
+__version__ = "2.42.0" # x-release-please-version
diff --git a/src/openai/resources/admin/organization/admin_api_keys.py b/src/openai/resources/admin/organization/admin_api_keys.py
index 80ddc39b49..fe10e236f5 100644
--- a/src/openai/resources/admin/organization/admin_api_keys.py
+++ b/src/openai/resources/admin/organization/admin_api_keys.py
@@ -47,6 +47,7 @@ def create(
self,
*,
name: str,
+ expires_in_seconds: int | 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,
@@ -58,6 +59,9 @@ def create(
Create an organization admin API key
Args:
+ expires_in_seconds: The number of seconds until the API key expires. Omit this field for a key that
+ does not expire.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -68,7 +72,13 @@ def create(
"""
return self._post(
"/organization/admin_api_keys",
- body=maybe_transform({"name": name}, admin_api_key_create_params.AdminAPIKeyCreateParams),
+ body=maybe_transform(
+ {
+ "name": name,
+ "expires_in_seconds": expires_in_seconds,
+ },
+ admin_api_key_create_params.AdminAPIKeyCreateParams,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -234,6 +244,7 @@ async def create(
self,
*,
name: str,
+ expires_in_seconds: int | 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,
@@ -245,6 +256,9 @@ async def create(
Create an organization admin API key
Args:
+ expires_in_seconds: The number of seconds until the API key expires. Omit this field for a key that
+ does not expire.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -255,7 +269,13 @@ async def create(
"""
return await self._post(
"/organization/admin_api_keys",
- body=await async_maybe_transform({"name": name}, admin_api_key_create_params.AdminAPIKeyCreateParams),
+ body=await async_maybe_transform(
+ {
+ "name": name,
+ "expires_in_seconds": expires_in_seconds,
+ },
+ admin_api_key_create_params.AdminAPIKeyCreateParams,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
diff --git a/src/openai/resources/admin/organization/audit_logs.py b/src/openai/resources/admin/organization/audit_logs.py
index 760fb4b034..1774a80aed 100644
--- a/src/openai/resources/admin/organization/audit_logs.py
+++ b/src/openai/resources/admin/organization/audit_logs.py
@@ -102,6 +102,8 @@ def list(
"role.deleted",
"role.assignment.created",
"role.assignment.deleted",
+ "role.bound_to_resource",
+ "role.unbound_from_resource",
"scim.enabled",
"scim.disabled",
"service_account.created",
@@ -116,6 +118,7 @@ def list(
limit: int | Omit = omit,
project_ids: SequenceNotStr[str] | Omit = omit,
resource_ids: SequenceNotStr[str] | Omit = omit,
+ tenant_only: bool | 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,
@@ -154,7 +157,13 @@ def list(
project_ids: Return only events for these projects.
resource_ids: Return only events performed on these targets. For example, a project ID
- updated.
+ updated. For ChatGPT connector role events, use the workspace connector resource
+ ID shown in `details.id`, such as `__`.
+
+ tenant_only: Return only tenant-scoped events associated with this organization. Required for
+ tenant-scoped events such as `role.bound_to_resource` and
+ `role.unbound_from_resource`. When `true`, all supplied event types must be
+ tenant-scoped.
extra_headers: Send extra headers
@@ -183,6 +192,7 @@ def list(
"limit": limit,
"project_ids": project_ids,
"resource_ids": resource_ids,
+ "tenant_only": tenant_only,
},
audit_log_list_params.AuditLogListParams,
),
@@ -273,6 +283,8 @@ def list(
"role.deleted",
"role.assignment.created",
"role.assignment.deleted",
+ "role.bound_to_resource",
+ "role.unbound_from_resource",
"scim.enabled",
"scim.disabled",
"service_account.created",
@@ -287,6 +299,7 @@ def list(
limit: int | Omit = omit,
project_ids: SequenceNotStr[str] | Omit = omit,
resource_ids: SequenceNotStr[str] | Omit = omit,
+ tenant_only: bool | 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,
@@ -325,7 +338,13 @@ def list(
project_ids: Return only events for these projects.
resource_ids: Return only events performed on these targets. For example, a project ID
- updated.
+ updated. For ChatGPT connector role events, use the workspace connector resource
+ ID shown in `details.id`, such as `__`.
+
+ tenant_only: Return only tenant-scoped events associated with this organization. Required for
+ tenant-scoped events such as `role.bound_to_resource` and
+ `role.unbound_from_resource`. When `true`, all supplied event types must be
+ tenant-scoped.
extra_headers: Send extra headers
@@ -354,6 +373,7 @@ def list(
"limit": limit,
"project_ids": project_ids,
"resource_ids": resource_ids,
+ "tenant_only": tenant_only,
},
audit_log_list_params.AuditLogListParams,
),
diff --git a/src/openai/resources/admin/organization/projects/spend_alerts.py b/src/openai/resources/admin/organization/projects/spend_alerts.py
index 3bc5d0c1f9..9eb0922df5 100644
--- a/src/openai/resources/admin/organization/projects/spend_alerts.py
+++ b/src/openai/resources/admin/organization/projects/spend_alerts.py
@@ -103,6 +103,48 @@ def create(
cast_to=ProjectSpendAlert,
)
+ def retrieve(
+ 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,
+ ) -> ProjectSpendAlert:
+ """
+ Retrieves 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._get(
+ 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=ProjectSpendAlert,
+ )
+
def update(
self,
alert_id: str,
@@ -349,6 +391,48 @@ async def create(
cast_to=ProjectSpendAlert,
)
+ async def retrieve(
+ 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,
+ ) -> ProjectSpendAlert:
+ """
+ Retrieves 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._get(
+ 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=ProjectSpendAlert,
+ )
+
async def update(
self,
alert_id: str,
@@ -524,6 +608,9 @@ def __init__(self, spend_alerts: SpendAlerts) -> None:
self.create = _legacy_response.to_raw_response_wrapper(
spend_alerts.create,
)
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ spend_alerts.retrieve,
+ )
self.update = _legacy_response.to_raw_response_wrapper(
spend_alerts.update,
)
@@ -542,6 +629,9 @@ def __init__(self, spend_alerts: AsyncSpendAlerts) -> None:
self.create = _legacy_response.async_to_raw_response_wrapper(
spend_alerts.create,
)
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ spend_alerts.retrieve,
+ )
self.update = _legacy_response.async_to_raw_response_wrapper(
spend_alerts.update,
)
@@ -560,6 +650,9 @@ def __init__(self, spend_alerts: SpendAlerts) -> None:
self.create = to_streamed_response_wrapper(
spend_alerts.create,
)
+ self.retrieve = to_streamed_response_wrapper(
+ spend_alerts.retrieve,
+ )
self.update = to_streamed_response_wrapper(
spend_alerts.update,
)
@@ -578,6 +671,9 @@ def __init__(self, spend_alerts: AsyncSpendAlerts) -> None:
self.create = async_to_streamed_response_wrapper(
spend_alerts.create,
)
+ self.retrieve = async_to_streamed_response_wrapper(
+ spend_alerts.retrieve,
+ )
self.update = async_to_streamed_response_wrapper(
spend_alerts.update,
)
diff --git a/src/openai/resources/admin/organization/spend_alerts.py b/src/openai/resources/admin/organization/spend_alerts.py
index 4f3e223269..0da46176b6 100644
--- a/src/openai/resources/admin/organization/spend_alerts.py
+++ b/src/openai/resources/admin/organization/spend_alerts.py
@@ -96,6 +96,43 @@ def create(
cast_to=OrganizationSpendAlert,
)
+ def retrieve(
+ 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,
+ ) -> OrganizationSpendAlert:
+ """
+ Retrieves 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._get(
+ 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=OrganizationSpendAlert,
+ )
+
def update(
self,
alert_id: str,
@@ -326,6 +363,43 @@ async def create(
cast_to=OrganizationSpendAlert,
)
+ async def retrieve(
+ 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,
+ ) -> OrganizationSpendAlert:
+ """
+ Retrieves 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._get(
+ 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=OrganizationSpendAlert,
+ )
+
async def update(
self,
alert_id: str,
@@ -488,6 +562,9 @@ def __init__(self, spend_alerts: SpendAlerts) -> None:
self.create = _legacy_response.to_raw_response_wrapper(
spend_alerts.create,
)
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ spend_alerts.retrieve,
+ )
self.update = _legacy_response.to_raw_response_wrapper(
spend_alerts.update,
)
@@ -506,6 +583,9 @@ def __init__(self, spend_alerts: AsyncSpendAlerts) -> None:
self.create = _legacy_response.async_to_raw_response_wrapper(
spend_alerts.create,
)
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ spend_alerts.retrieve,
+ )
self.update = _legacy_response.async_to_raw_response_wrapper(
spend_alerts.update,
)
@@ -524,6 +604,9 @@ def __init__(self, spend_alerts: SpendAlerts) -> None:
self.create = to_streamed_response_wrapper(
spend_alerts.create,
)
+ self.retrieve = to_streamed_response_wrapper(
+ spend_alerts.retrieve,
+ )
self.update = to_streamed_response_wrapper(
spend_alerts.update,
)
@@ -542,6 +625,9 @@ def __init__(self, spend_alerts: AsyncSpendAlerts) -> None:
self.create = async_to_streamed_response_wrapper(
spend_alerts.create,
)
+ self.retrieve = async_to_streamed_response_wrapper(
+ spend_alerts.retrieve,
+ )
self.update = async_to_streamed_response_wrapper(
spend_alerts.update,
)
diff --git a/src/openai/types/admin/organization/admin_api_key.py b/src/openai/types/admin/organization/admin_api_key.py
index 5d2e5840f9..b9323dd465 100644
--- a/src/openai/types/admin/organization/admin_api_key.py
+++ b/src/openai/types/admin/organization/admin_api_key.py
@@ -37,6 +37,9 @@ class AdminAPIKey(BaseModel):
created_at: int
"""The Unix timestamp (in seconds) of when the API key was created"""
+ expires_at: Optional[int] = None
+ """The Unix timestamp (in seconds) of when the API key expires"""
+
object: Literal["organization.admin_api_key"]
"""The object type, which is always `organization.admin_api_key`"""
diff --git a/src/openai/types/admin/organization/admin_api_key_create_params.py b/src/openai/types/admin/organization/admin_api_key_create_params.py
index dccdfb8a75..ac7b22ea30 100644
--- a/src/openai/types/admin/organization/admin_api_key_create_params.py
+++ b/src/openai/types/admin/organization/admin_api_key_create_params.py
@@ -9,3 +9,9 @@
class AdminAPIKeyCreateParams(TypedDict, total=False):
name: Required[str]
+
+ expires_in_seconds: int
+ """The number of seconds until the API key expires.
+
+ Omit this field for a key that does not expire.
+ """
diff --git a/src/openai/types/admin/organization/audit_log_list_params.py b/src/openai/types/admin/organization/audit_log_list_params.py
index 25224d47dc..e77e1c8d96 100644
--- a/src/openai/types/admin/organization/audit_log_list_params.py
+++ b/src/openai/types/admin/organization/audit_log_list_params.py
@@ -92,6 +92,8 @@ class AuditLogListParams(TypedDict, total=False):
"role.deleted",
"role.assignment.created",
"role.assignment.deleted",
+ "role.bound_to_resource",
+ "role.unbound_from_resource",
"scim.enabled",
"scim.disabled",
"service_account.created",
@@ -120,7 +122,17 @@ class AuditLogListParams(TypedDict, total=False):
resource_ids: SequenceNotStr[str]
"""Return only events performed on these targets.
- For example, a project ID updated.
+ For example, a project ID updated. For ChatGPT connector role events, use the
+ workspace connector resource ID shown in `details.id`, such as
+ `__`.
+ """
+
+ tenant_only: bool
+ """Return only tenant-scoped events associated with this organization.
+
+ Required for tenant-scoped events such as `role.bound_to_resource` and
+ `role.unbound_from_resource`. When `true`, all supplied event types must be
+ tenant-scoped.
"""
diff --git a/src/openai/types/admin/organization/audit_log_list_response.py b/src/openai/types/admin/organization/audit_log_list_response.py
index 9fa99bd677..f2f63c2e74 100644
--- a/src/openai/types/admin/organization/audit_log_list_response.py
+++ b/src/openai/types/admin/organization/audit_log_list_response.py
@@ -64,8 +64,10 @@
"RateLimitUpdatedChangesRequested",
"RoleAssignmentCreated",
"RoleAssignmentDeleted",
+ "RoleBoundToResource",
"RoleCreated",
"RoleDeleted",
+ "RoleUnboundFromResource",
"RoleUpdated",
"RoleUpdatedChangesRequested",
"ScimDisabled",
@@ -653,6 +655,48 @@ class RoleAssignmentDeleted(BaseModel):
"""The type of resource the role assignment was scoped to."""
+class RoleBoundToResource(BaseModel):
+ """The details for events with this `type`."""
+
+ id: Optional[str] = None
+ """The ID of the resource the role was bound to.
+
+ ChatGPT workspace connector resources use `__`.
+ """
+
+ connector_id: Optional[str] = None
+ """The connector ID for a ChatGPT workspace connector resource."""
+
+ connector_name: Optional[str] = None
+ """
+ The connector display name for a ChatGPT workspace connector resource, or the
+ connector ID when the display name could not be resolved.
+ """
+
+ enabled: Optional[bool] = None
+ """Whether the connector is enabled for the role."""
+
+ permissions: Optional[List[str]] = None
+ """The permissions granted to the role for the resource."""
+
+ resource_id: Optional[str] = None
+ """The ID of the resource the role was bound to."""
+
+ resource_type: Optional[str] = None
+ """The type of resource the role was bound to."""
+
+ role_id: Optional[str] = None
+ """The ID of the role that was bound to the resource."""
+
+ source: Optional[
+ Literal["role_toggle", "role_connector_update", "role_delete", "workspace_permissions", "connector_publish"]
+ ] = None
+ """The connector role mutation path that produced the event."""
+
+ workspace_id: Optional[str] = None
+ """The workspace ID for a ChatGPT workspace connector resource."""
+
+
class RoleCreated(BaseModel):
"""The details for events with this `type`."""
@@ -679,6 +723,48 @@ class RoleDeleted(BaseModel):
"""The role ID."""
+class RoleUnboundFromResource(BaseModel):
+ """The details for events with this `type`."""
+
+ id: Optional[str] = None
+ """The ID of the resource the role was unbound from.
+
+ ChatGPT workspace connector resources use `__`.
+ """
+
+ connector_id: Optional[str] = None
+ """The connector ID for a ChatGPT workspace connector resource."""
+
+ connector_name: Optional[str] = None
+ """
+ The connector display name for a ChatGPT workspace connector resource, or the
+ connector ID when the display name could not be resolved.
+ """
+
+ enabled: Optional[bool] = None
+ """Whether the connector is enabled for the role."""
+
+ permissions: Optional[List[str]] = None
+ """The permissions remaining for the role after the change."""
+
+ resource_id: Optional[str] = None
+ """The ID of the resource the role was unbound from."""
+
+ resource_type: Optional[str] = None
+ """The type of resource the role was unbound from."""
+
+ role_id: Optional[str] = None
+ """The ID of the role that was unbound from the resource."""
+
+ source: Optional[
+ Literal["role_toggle", "role_connector_update", "role_delete", "workspace_permissions", "connector_publish"]
+ ] = None
+ """The connector role mutation path that produced the event."""
+
+ workspace_id: Optional[str] = None
+ """The workspace ID for a ChatGPT workspace connector resource."""
+
+
class RoleUpdatedChangesRequested(BaseModel):
"""The payload used to update the role."""
@@ -941,6 +1027,8 @@ class AuditLogListResponse(BaseModel):
"role.deleted",
"role.assignment.created",
"role.assignment.deleted",
+ "role.bound_to_resource",
+ "role.unbound_from_resource",
"scim.enabled",
"scim.disabled",
"service_account.created",
@@ -1083,12 +1171,20 @@ class AuditLogListResponse(BaseModel):
role_assignment_deleted: Optional[RoleAssignmentDeleted] = FieldInfo(alias="role.assignment.deleted", default=None)
"""The details for events with this `type`."""
+ role_bound_to_resource: Optional[RoleBoundToResource] = FieldInfo(alias="role.bound_to_resource", default=None)
+ """The details for events with this `type`."""
+
role_created: Optional[RoleCreated] = FieldInfo(alias="role.created", default=None)
"""The details for events with this `type`."""
role_deleted: Optional[RoleDeleted] = FieldInfo(alias="role.deleted", default=None)
"""The details for events with this `type`."""
+ role_unbound_from_resource: Optional[RoleUnboundFromResource] = FieldInfo(
+ alias="role.unbound_from_resource", default=None
+ )
+ """The details for events with this `type`."""
+
role_updated: Optional[RoleUpdated] = FieldInfo(alias="role.updated", default=None)
"""The details for events with this `type`."""
diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py
index 14f56a04cd..6a7b3e4b7d 100644
--- a/src/openai/types/shared/reasoning.py
+++ b/src/openai/types/shared/reasoning.py
@@ -16,6 +16,13 @@ class Reasoning(BaseModel):
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
+ context: Optional[Literal["auto", "current_turn", "all_turns"]] = None
+ """
+ Controls which reasoning items are rendered back to the model on later turns.
+ When returned on a response, this is the effective reasoning context mode used
+ for the response.
+ """
+
effort: Optional[ReasoningEffort] = None
"""
Constrains effort on reasoning for
diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py
index 2bd7ce7268..86ebc259f1 100644
--- a/src/openai/types/shared_params/reasoning.py
+++ b/src/openai/types/shared_params/reasoning.py
@@ -17,6 +17,13 @@ class Reasoning(TypedDict, total=False):
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
+ context: Optional[Literal["auto", "current_turn", "all_turns"]]
+ """
+ Controls which reasoning items are rendered back to the model on later turns.
+ When returned on a response, this is the effective reasoning context mode used
+ for the response.
+ """
+
effort: Optional[ReasoningEffort]
"""
Constrains effort on reasoning for
diff --git a/tests/api_resources/admin/organization/projects/test_spend_alerts.py b/tests/api_resources/admin/organization/projects/test_spend_alerts.py
index c763401af3..ee53d014dd 100644
--- a/tests/api_resources/admin/organization/projects/test_spend_alerts.py
+++ b/tests/api_resources/admin/organization/projects/test_spend_alerts.py
@@ -102,6 +102,54 @@ def test_path_params_create(self, client: OpenAI) -> None:
threshold_amount=0,
)
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ spend_alert = client.admin.organization.projects.spend_alerts.retrieve(
+ alert_id="alert_id",
+ project_id="project_id",
+ )
+ assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.admin.organization.projects.spend_alerts.with_raw_response.retrieve(
+ 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(ProjectSpendAlert, spend_alert, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.admin.organization.projects.spend_alerts.with_streaming_response.retrieve(
+ 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(ProjectSpendAlert, spend_alert, 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.spend_alerts.with_raw_response.retrieve(
+ 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.retrieve(
+ alert_id="",
+ project_id="project_id",
+ )
+
@parametrize
def test_method_update(self, client: OpenAI) -> None:
spend_alert = client.admin.organization.projects.spend_alerts.update(
@@ -385,6 +433,54 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None:
threshold_amount=0,
)
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ spend_alert = await async_client.admin.organization.projects.spend_alerts.retrieve(
+ alert_id="alert_id",
+ project_id="project_id",
+ )
+ assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.admin.organization.projects.spend_alerts.with_raw_response.retrieve(
+ 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(ProjectSpendAlert, spend_alert, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.admin.organization.projects.spend_alerts.with_streaming_response.retrieve(
+ 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(ProjectSpendAlert, spend_alert, 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.spend_alerts.with_raw_response.retrieve(
+ 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.retrieve(
+ alert_id="",
+ project_id="project_id",
+ )
+
@parametrize
async def test_method_update(self, async_client: AsyncOpenAI) -> None:
spend_alert = await async_client.admin.organization.projects.spend_alerts.update(
diff --git a/tests/api_resources/admin/organization/test_admin_api_keys.py b/tests/api_resources/admin/organization/test_admin_api_keys.py
index 59eed910e8..3384ee32df 100644
--- a/tests/api_resources/admin/organization/test_admin_api_keys.py
+++ b/tests/api_resources/admin/organization/test_admin_api_keys.py
@@ -29,6 +29,14 @@ def test_method_create(self, client: OpenAI) -> None:
)
assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"])
+ @parametrize
+ def test_method_create_with_all_params(self, client: OpenAI) -> None:
+ admin_api_key = client.admin.organization.admin_api_keys.create(
+ name="New Admin Key",
+ expires_in_seconds=2592000,
+ )
+ assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"])
+
@parametrize
def test_raw_response_create(self, client: OpenAI) -> None:
response = client.admin.organization.admin_api_keys.with_raw_response.create(
@@ -176,6 +184,14 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
)
assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"])
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ admin_api_key = await async_client.admin.organization.admin_api_keys.create(
+ name="New Admin Key",
+ expires_in_seconds=2592000,
+ )
+ assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"])
+
@parametrize
async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
response = await async_client.admin.organization.admin_api_keys.with_raw_response.create(
diff --git a/tests/api_resources/admin/organization/test_audit_logs.py b/tests/api_resources/admin/organization/test_audit_logs.py
index 5e696461fa..cf60c407b3 100644
--- a/tests/api_resources/admin/organization/test_audit_logs.py
+++ b/tests/api_resources/admin/organization/test_audit_logs.py
@@ -40,6 +40,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None:
limit=0,
project_ids=["string"],
resource_ids=["string"],
+ tenant_only=True,
)
assert_matches_type(SyncConversationCursorPage[AuditLogListResponse], audit_log, path=["response"])
@@ -91,6 +92,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N
limit=0,
project_ids=["string"],
resource_ids=["string"],
+ tenant_only=True,
)
assert_matches_type(AsyncConversationCursorPage[AuditLogListResponse], audit_log, path=["response"])
diff --git a/tests/api_resources/admin/organization/test_spend_alerts.py b/tests/api_resources/admin/organization/test_spend_alerts.py
index 08a3c445c3..13e80d7afa 100644
--- a/tests/api_resources/admin/organization/test_spend_alerts.py
+++ b/tests/api_resources/admin/organization/test_spend_alerts.py
@@ -84,6 +84,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:
+ spend_alert = client.admin.organization.spend_alerts.retrieve(
+ "alert_id",
+ )
+ assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.admin.organization.spend_alerts.with_raw_response.retrieve(
+ "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(OrganizationSpendAlert, spend_alert, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.admin.organization.spend_alerts.with_streaming_response.retrieve(
+ "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(OrganizationSpendAlert, spend_alert, 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 `alert_id` but received ''"):
+ client.admin.organization.spend_alerts.with_raw_response.retrieve(
+ "",
+ )
+
@parametrize
def test_method_update(self, client: OpenAI) -> None:
spend_alert = client.admin.organization.spend_alerts.update(
@@ -307,6 +345,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:
+ spend_alert = await async_client.admin.organization.spend_alerts.retrieve(
+ "alert_id",
+ )
+ assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.admin.organization.spend_alerts.with_raw_response.retrieve(
+ "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(OrganizationSpendAlert, spend_alert, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.admin.organization.spend_alerts.with_streaming_response.retrieve(
+ "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(OrganizationSpendAlert, spend_alert, 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 `alert_id` but received ''"):
+ await async_client.admin.organization.spend_alerts.with_raw_response.retrieve(
+ "",
+ )
+
@parametrize
async def test_method_update(self, async_client: AsyncOpenAI) -> None:
spend_alert = await async_client.admin.organization.spend_alerts.update(
diff --git a/tests/api_resources/responses/test_input_tokens.py b/tests/api_resources/responses/test_input_tokens.py
index 04c0bb698e..59ff4252b9 100644
--- a/tests/api_resources/responses/test_input_tokens.py
+++ b/tests/api_resources/responses/test_input_tokens.py
@@ -33,6 +33,7 @@ def test_method_count_with_all_params(self, client: OpenAI) -> None:
personality="friendly",
previous_response_id="resp_123",
reasoning={
+ "context": "auto",
"effort": "none",
"generate_summary": "auto",
"summary": "auto",
@@ -98,6 +99,7 @@ async def test_method_count_with_all_params(self, async_client: AsyncOpenAI) ->
personality="friendly",
previous_response_id="resp_123",
reasoning={
+ "context": "auto",
"effort": "none",
"generate_summary": "auto",
"summary": "auto",
diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py
index dc6083e78c..761b410943 100644
--- a/tests/api_resources/test_responses.py
+++ b/tests/api_resources/test_responses.py
@@ -55,6 +55,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
prompt_cache_key="prompt-cache-key-1234",
prompt_cache_retention="in_memory",
reasoning={
+ "context": "auto",
"effort": "none",
"generate_summary": "auto",
"summary": "auto",
@@ -144,6 +145,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
prompt_cache_key="prompt-cache-key-1234",
prompt_cache_retention="in_memory",
reasoning={
+ "context": "auto",
"effort": "none",
"generate_summary": "auto",
"summary": "auto",
@@ -470,6 +472,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
prompt_cache_key="prompt-cache-key-1234",
prompt_cache_retention="in_memory",
reasoning={
+ "context": "auto",
"effort": "none",
"generate_summary": "auto",
"summary": "auto",
@@ -559,6 +562,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
prompt_cache_key="prompt-cache-key-1234",
prompt_cache_retention="in_memory",
reasoning={
+ "context": "auto",
"effort": "none",
"generate_summary": "auto",
"summary": "auto",