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",