From 88821c710de290b37a6dbc9a81ef0a1cc396db62 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 18 Apr 2025 22:17:20 +0000
Subject: [PATCH 01/17] chore(internal): version bump
From 03cd93334de3db9beea06ae0f62bcb0ab61466a4 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 22 Apr 2025 17:35:59 +0000
Subject: [PATCH 02/17] chore(internal): version bump
From 444cd038fc03a545fc2da0a6e5ab14ea84102408 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sat, 19 Apr 2025 02:14:16 +0000
Subject: [PATCH 03/17] chore(internal): update models test
---
tests/test_models.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/tests/test_models.py b/tests/test_models.py
index fb9d99b1..c96609ce 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -492,12 +492,15 @@ class Model(BaseModel):
resource_id: Optional[str] = None
m = Model.construct()
+ assert m.resource_id is None
assert "resource_id" not in m.model_fields_set
m = Model.construct(resource_id=None)
+ assert m.resource_id is None
assert "resource_id" in m.model_fields_set
m = Model.construct(resource_id="foo")
+ assert m.resource_id == "foo"
assert "resource_id" in m.model_fields_set
From 4f39e46167b88a0c7198bdd8ad7d31bf118d485c Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sun, 20 Apr 2025 23:16:04 +0000
Subject: [PATCH 04/17] feat(api): api update
---
.stats.yml | 2 +-
src/codex/resources/projects/clusters.py | 6 ++++--
src/codex/types/projects/cluster_list_params.py | 2 +-
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index c1c522e8..f033bbda 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,3 +1,3 @@
configured_endpoints: 36
-openapi_spec_hash: 6311021a3aba7ac56cc3b474762945c0
+openapi_spec_hash: 71ff1de391293cdfb6dcb761ed89210d
config_hash: adbedb6317fca6f566f54564cc341846
diff --git a/src/codex/resources/projects/clusters.py b/src/codex/resources/projects/clusters.py
index e35e8ae8..584cde18 100644
--- a/src/codex/resources/projects/clusters.py
+++ b/src/codex/resources/projects/clusters.py
@@ -50,7 +50,8 @@ def list(
self,
project_id: str,
*,
- eval_issue_types: List[Literal["hallucination", "search_failure", "unhelpful"]] | NotGiven = NOT_GIVEN,
+ eval_issue_types: List[Literal["hallucination", "search_failure", "unhelpful", "difficult_query"]]
+ | NotGiven = NOT_GIVEN,
limit: int | NotGiven = NOT_GIVEN,
offset: int | NotGiven = NOT_GIVEN,
order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN,
@@ -164,7 +165,8 @@ def list(
self,
project_id: str,
*,
- eval_issue_types: List[Literal["hallucination", "search_failure", "unhelpful"]] | NotGiven = NOT_GIVEN,
+ eval_issue_types: List[Literal["hallucination", "search_failure", "unhelpful", "difficult_query"]]
+ | NotGiven = NOT_GIVEN,
limit: int | NotGiven = NOT_GIVEN,
offset: int | NotGiven = NOT_GIVEN,
order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN,
diff --git a/src/codex/types/projects/cluster_list_params.py b/src/codex/types/projects/cluster_list_params.py
index d1d818e3..fa84a6b0 100644
--- a/src/codex/types/projects/cluster_list_params.py
+++ b/src/codex/types/projects/cluster_list_params.py
@@ -9,7 +9,7 @@
class ClusterListParams(TypedDict, total=False):
- eval_issue_types: List[Literal["hallucination", "search_failure", "unhelpful"]]
+ eval_issue_types: List[Literal["hallucination", "search_failure", "unhelpful", "difficult_query"]]
limit: int
From b038fbe3e90f3096b0913256db9e31ca52cd4001 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 22 Apr 2025 17:41:55 +0000
Subject: [PATCH 05/17] feat(api): add project increment_queries and other
recent endpoints
---
.stats.yml | 4 +-
api.md | 15 +-
.../resources/organizations/organizations.py | 158 ++++++++
src/codex/resources/projects/entries.py | 289 +++++++++++++-
src/codex/resources/projects/projects.py | 78 ++++
src/codex/types/__init__.py | 4 +
.../organization_list_members_response.py | 17 +
...anization_retrieve_permissions_response.py | 8 +
src/codex/types/projects/__init__.py | 2 +
.../types/projects/entry_notify_sme_params.py | 21 +
.../projects/entry_notify_sme_response.py | 14 +
tests/api_resources/projects/test_entries.py | 366 +++++++++++++++++-
tests/api_resources/test_organizations.py | 174 ++++++++-
tests/api_resources/test_projects.py | 84 ++++
14 files changed, 1227 insertions(+), 7 deletions(-)
create mode 100644 src/codex/types/organization_list_members_response.py
create mode 100644 src/codex/types/organization_retrieve_permissions_response.py
create mode 100644 src/codex/types/projects/entry_notify_sme_params.py
create mode 100644 src/codex/types/projects/entry_notify_sme_response.py
diff --git a/.stats.yml b/.stats.yml
index f033bbda..d78252d8 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,3 +1,3 @@
-configured_endpoints: 36
+configured_endpoints: 42
openapi_spec_hash: 71ff1de391293cdfb6dcb761ed89210d
-config_hash: adbedb6317fca6f566f54564cc341846
+config_hash: 93ac12138700569dc57329400410c0fd
diff --git a/api.md b/api.md
index e9dd565d..5a314a2d 100644
--- a/api.md
+++ b/api.md
@@ -16,12 +16,18 @@ Methods:
Types:
```python
-from codex.types import OrganizationSchemaPublic
+from codex.types import (
+ OrganizationSchemaPublic,
+ OrganizationListMembersResponse,
+ OrganizationRetrievePermissionsResponse,
+)
```
Methods:
- client.organizations.retrieve(organization_id) -> OrganizationSchemaPublic
+- client.organizations.list_members(organization_id) -> OrganizationListMembersResponse
+- client.organizations.retrieve_permissions(organization_id) -> OrganizationRetrievePermissionsResponse
## Billing
@@ -140,6 +146,7 @@ from codex.types import (
ProjectRetrieveResponse,
ProjectListResponse,
ProjectExportResponse,
+ ProjectIncrementQueriesResponse,
)
```
@@ -151,6 +158,7 @@ Methods:
- client.projects.list(\*\*params) -> ProjectListResponse
- client.projects.delete(project_id) -> None
- client.projects.export(project_id) -> object
+- client.projects.increment_queries(project_id) -> object
## AccessKeys
@@ -179,7 +187,7 @@ Methods:
Types:
```python
-from codex.types.projects import Entry, EntryQueryResponse
+from codex.types.projects import Entry, EntryNotifySmeResponse, EntryQueryResponse
```
Methods:
@@ -188,7 +196,10 @@ Methods:
- client.projects.entries.retrieve(entry_id, \*, project_id) -> Entry
- client.projects.entries.update(entry_id, \*, project_id, \*\*params) -> Entry
- client.projects.entries.delete(entry_id, \*, project_id) -> None
+- client.projects.entries.notify_sme(entry_id, \*, project_id, \*\*params) -> EntryNotifySmeResponse
+- client.projects.entries.publish_draft_answer(entry_id, \*, project_id) -> Entry
- client.projects.entries.query(project_id, \*\*params) -> EntryQueryResponse
+- client.projects.entries.unpublish_answer(entry_id, \*, project_id) -> Entry
## Clusters
diff --git a/src/codex/resources/organizations/organizations.py b/src/codex/resources/organizations/organizations.py
index 025ecba4..f1eb4d5e 100644
--- a/src/codex/resources/organizations/organizations.py
+++ b/src/codex/resources/organizations/organizations.py
@@ -23,6 +23,8 @@
AsyncBillingResourceWithStreamingResponse,
)
from ...types.organization_schema_public import OrganizationSchemaPublic
+from ...types.organization_list_members_response import OrganizationListMembersResponse
+from ...types.organization_retrieve_permissions_response import OrganizationRetrievePermissionsResponse
__all__ = ["OrganizationsResource", "AsyncOrganizationsResource"]
@@ -84,6 +86,72 @@ def retrieve(
cast_to=OrganizationSchemaPublic,
)
+ def list_members(
+ self,
+ organization_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,
+ ) -> OrganizationListMembersResponse:
+ """
+ Get a list of organization members with their names and emails.
+
+ 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 organization_id:
+ raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
+ return self._get(
+ f"/api/organizations/{organization_id}/members",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=OrganizationListMembersResponse,
+ )
+
+ def retrieve_permissions(
+ self,
+ organization_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,
+ ) -> OrganizationRetrievePermissionsResponse:
+ """
+ Get the user's permissions for this organization.
+
+ 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 organization_id:
+ raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
+ return self._get(
+ f"/api/organizations/{organization_id}/permissions",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=OrganizationRetrievePermissionsResponse,
+ )
+
class AsyncOrganizationsResource(AsyncAPIResource):
@cached_property
@@ -142,6 +210,72 @@ async def retrieve(
cast_to=OrganizationSchemaPublic,
)
+ async def list_members(
+ self,
+ organization_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,
+ ) -> OrganizationListMembersResponse:
+ """
+ Get a list of organization members with their names and emails.
+
+ 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 organization_id:
+ raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
+ return await self._get(
+ f"/api/organizations/{organization_id}/members",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=OrganizationListMembersResponse,
+ )
+
+ async def retrieve_permissions(
+ self,
+ organization_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,
+ ) -> OrganizationRetrievePermissionsResponse:
+ """
+ Get the user's permissions for this organization.
+
+ 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 organization_id:
+ raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
+ return await self._get(
+ f"/api/organizations/{organization_id}/permissions",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=OrganizationRetrievePermissionsResponse,
+ )
+
class OrganizationsResourceWithRawResponse:
def __init__(self, organizations: OrganizationsResource) -> None:
@@ -150,6 +284,12 @@ def __init__(self, organizations: OrganizationsResource) -> None:
self.retrieve = to_raw_response_wrapper(
organizations.retrieve,
)
+ self.list_members = to_raw_response_wrapper(
+ organizations.list_members,
+ )
+ self.retrieve_permissions = to_raw_response_wrapper(
+ organizations.retrieve_permissions,
+ )
@cached_property
def billing(self) -> BillingResourceWithRawResponse:
@@ -163,6 +303,12 @@ def __init__(self, organizations: AsyncOrganizationsResource) -> None:
self.retrieve = async_to_raw_response_wrapper(
organizations.retrieve,
)
+ self.list_members = async_to_raw_response_wrapper(
+ organizations.list_members,
+ )
+ self.retrieve_permissions = async_to_raw_response_wrapper(
+ organizations.retrieve_permissions,
+ )
@cached_property
def billing(self) -> AsyncBillingResourceWithRawResponse:
@@ -176,6 +322,12 @@ def __init__(self, organizations: OrganizationsResource) -> None:
self.retrieve = to_streamed_response_wrapper(
organizations.retrieve,
)
+ self.list_members = to_streamed_response_wrapper(
+ organizations.list_members,
+ )
+ self.retrieve_permissions = to_streamed_response_wrapper(
+ organizations.retrieve_permissions,
+ )
@cached_property
def billing(self) -> BillingResourceWithStreamingResponse:
@@ -189,6 +341,12 @@ def __init__(self, organizations: AsyncOrganizationsResource) -> None:
self.retrieve = async_to_streamed_response_wrapper(
organizations.retrieve,
)
+ self.list_members = async_to_streamed_response_wrapper(
+ organizations.list_members,
+ )
+ self.retrieve_permissions = async_to_streamed_response_wrapper(
+ organizations.retrieve_permissions,
+ )
@cached_property
def billing(self) -> AsyncBillingResourceWithStreamingResponse:
diff --git a/src/codex/resources/projects/entries.py b/src/codex/resources/projects/entries.py
index f0fa39cb..f88e2243 100644
--- a/src/codex/resources/projects/entries.py
+++ b/src/codex/resources/projects/entries.py
@@ -21,9 +21,10 @@
async_to_streamed_response_wrapper,
)
from ..._base_client import make_request_options
-from ...types.projects import entry_query_params, entry_create_params, entry_update_params
+from ...types.projects import entry_query_params, entry_create_params, entry_update_params, entry_notify_sme_params
from ...types.projects.entry import Entry
from ...types.projects.entry_query_response import EntryQueryResponse
+from ...types.projects.entry_notify_sme_response import EntryNotifySmeResponse
__all__ = ["EntriesResource", "AsyncEntriesResource"]
@@ -229,6 +230,92 @@ def delete(
cast_to=NoneType,
)
+ def notify_sme(
+ self,
+ entry_id: str,
+ *,
+ project_id: str,
+ email: str,
+ view_context: entry_notify_sme_params.ViewContext,
+ # 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,
+ ) -> EntryNotifySmeResponse:
+ """
+ Notify a subject matter expert to review and answer a specific entry.
+
+ Returns: SMENotificationResponse with status and notification details
+
+ 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 entry_id:
+ raise ValueError(f"Expected a non-empty value for `entry_id` but received {entry_id!r}")
+ return self._post(
+ f"/api/projects/{project_id}/entries/{entry_id}/notifications",
+ body=maybe_transform(
+ {
+ "email": email,
+ "view_context": view_context,
+ },
+ entry_notify_sme_params.EntryNotifySmeParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EntryNotifySmeResponse,
+ )
+
+ def publish_draft_answer(
+ self,
+ entry_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,
+ ) -> Entry:
+ """Promote a draft answer to a published answer for a knowledge entry.
+
+ This always
+ results in the entry's draft answer being removed. If the entry already has a
+ published answer, it will be overwritten and permanently lost.
+
+ 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 entry_id:
+ raise ValueError(f"Expected a non-empty value for `entry_id` but received {entry_id!r}")
+ return self._put(
+ f"/api/projects/{project_id}/entries/{entry_id}/publish_draft_answer",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Entry,
+ )
+
def query(
self,
project_id: str,
@@ -291,6 +378,45 @@ def query(
cast_to=EntryQueryResponse,
)
+ def unpublish_answer(
+ self,
+ entry_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,
+ ) -> Entry:
+ """Unpublish an answer for a knowledge entry.
+
+ This always results in the entry's
+ answer being removed. If the entry does not already have a draft answer, the
+ current answer will be retained as the draft answer.
+
+ 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 entry_id:
+ raise ValueError(f"Expected a non-empty value for `entry_id` but received {entry_id!r}")
+ return self._put(
+ f"/api/projects/{project_id}/entries/{entry_id}/unpublish_answer",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Entry,
+ )
+
class AsyncEntriesResource(AsyncAPIResource):
@cached_property
@@ -493,6 +619,92 @@ async def delete(
cast_to=NoneType,
)
+ async def notify_sme(
+ self,
+ entry_id: str,
+ *,
+ project_id: str,
+ email: str,
+ view_context: entry_notify_sme_params.ViewContext,
+ # 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,
+ ) -> EntryNotifySmeResponse:
+ """
+ Notify a subject matter expert to review and answer a specific entry.
+
+ Returns: SMENotificationResponse with status and notification details
+
+ 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 entry_id:
+ raise ValueError(f"Expected a non-empty value for `entry_id` but received {entry_id!r}")
+ return await self._post(
+ f"/api/projects/{project_id}/entries/{entry_id}/notifications",
+ body=await async_maybe_transform(
+ {
+ "email": email,
+ "view_context": view_context,
+ },
+ entry_notify_sme_params.EntryNotifySmeParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EntryNotifySmeResponse,
+ )
+
+ async def publish_draft_answer(
+ self,
+ entry_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,
+ ) -> Entry:
+ """Promote a draft answer to a published answer for a knowledge entry.
+
+ This always
+ results in the entry's draft answer being removed. If the entry already has a
+ published answer, it will be overwritten and permanently lost.
+
+ 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 entry_id:
+ raise ValueError(f"Expected a non-empty value for `entry_id` but received {entry_id!r}")
+ return await self._put(
+ f"/api/projects/{project_id}/entries/{entry_id}/publish_draft_answer",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Entry,
+ )
+
async def query(
self,
project_id: str,
@@ -557,6 +769,45 @@ async def query(
cast_to=EntryQueryResponse,
)
+ async def unpublish_answer(
+ self,
+ entry_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,
+ ) -> Entry:
+ """Unpublish an answer for a knowledge entry.
+
+ This always results in the entry's
+ answer being removed. If the entry does not already have a draft answer, the
+ current answer will be retained as the draft answer.
+
+ 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 entry_id:
+ raise ValueError(f"Expected a non-empty value for `entry_id` but received {entry_id!r}")
+ return await self._put(
+ f"/api/projects/{project_id}/entries/{entry_id}/unpublish_answer",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Entry,
+ )
+
class EntriesResourceWithRawResponse:
def __init__(self, entries: EntriesResource) -> None:
@@ -574,9 +825,18 @@ def __init__(self, entries: EntriesResource) -> None:
self.delete = to_raw_response_wrapper(
entries.delete,
)
+ self.notify_sme = to_raw_response_wrapper(
+ entries.notify_sme,
+ )
+ self.publish_draft_answer = to_raw_response_wrapper(
+ entries.publish_draft_answer,
+ )
self.query = to_raw_response_wrapper(
entries.query,
)
+ self.unpublish_answer = to_raw_response_wrapper(
+ entries.unpublish_answer,
+ )
class AsyncEntriesResourceWithRawResponse:
@@ -595,9 +855,18 @@ def __init__(self, entries: AsyncEntriesResource) -> None:
self.delete = async_to_raw_response_wrapper(
entries.delete,
)
+ self.notify_sme = async_to_raw_response_wrapper(
+ entries.notify_sme,
+ )
+ self.publish_draft_answer = async_to_raw_response_wrapper(
+ entries.publish_draft_answer,
+ )
self.query = async_to_raw_response_wrapper(
entries.query,
)
+ self.unpublish_answer = async_to_raw_response_wrapper(
+ entries.unpublish_answer,
+ )
class EntriesResourceWithStreamingResponse:
@@ -616,9 +885,18 @@ def __init__(self, entries: EntriesResource) -> None:
self.delete = to_streamed_response_wrapper(
entries.delete,
)
+ self.notify_sme = to_streamed_response_wrapper(
+ entries.notify_sme,
+ )
+ self.publish_draft_answer = to_streamed_response_wrapper(
+ entries.publish_draft_answer,
+ )
self.query = to_streamed_response_wrapper(
entries.query,
)
+ self.unpublish_answer = to_streamed_response_wrapper(
+ entries.unpublish_answer,
+ )
class AsyncEntriesResourceWithStreamingResponse:
@@ -637,6 +915,15 @@ def __init__(self, entries: AsyncEntriesResource) -> None:
self.delete = async_to_streamed_response_wrapper(
entries.delete,
)
+ self.notify_sme = async_to_streamed_response_wrapper(
+ entries.notify_sme,
+ )
+ self.publish_draft_answer = async_to_streamed_response_wrapper(
+ entries.publish_draft_answer,
+ )
self.query = async_to_streamed_response_wrapper(
entries.query,
)
+ self.unpublish_answer = async_to_streamed_response_wrapper(
+ entries.unpublish_answer,
+ )
diff --git a/src/codex/resources/projects/projects.py b/src/codex/resources/projects/projects.py
index ccc77268..47639a5a 100644
--- a/src/codex/resources/projects/projects.py
+++ b/src/codex/resources/projects/projects.py
@@ -324,6 +324,39 @@ def export(
cast_to=object,
)
+ def increment_queries(
+ 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,
+ ) -> object:
+ """
+ Increment the queries metric 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._post(
+ f"/api/projects/{project_id}/increment_queries",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=object,
+ )
+
class AsyncProjectsResource(AsyncAPIResource):
@cached_property
@@ -596,6 +629,39 @@ async def export(
cast_to=object,
)
+ async def increment_queries(
+ 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,
+ ) -> object:
+ """
+ Increment the queries metric 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._post(
+ f"/api/projects/{project_id}/increment_queries",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=object,
+ )
+
class ProjectsResourceWithRawResponse:
def __init__(self, projects: ProjectsResource) -> None:
@@ -619,6 +685,9 @@ def __init__(self, projects: ProjectsResource) -> None:
self.export = to_raw_response_wrapper(
projects.export,
)
+ self.increment_queries = to_raw_response_wrapper(
+ projects.increment_queries,
+ )
@cached_property
def access_keys(self) -> AccessKeysResourceWithRawResponse:
@@ -655,6 +724,9 @@ def __init__(self, projects: AsyncProjectsResource) -> None:
self.export = async_to_raw_response_wrapper(
projects.export,
)
+ self.increment_queries = async_to_raw_response_wrapper(
+ projects.increment_queries,
+ )
@cached_property
def access_keys(self) -> AsyncAccessKeysResourceWithRawResponse:
@@ -691,6 +763,9 @@ def __init__(self, projects: ProjectsResource) -> None:
self.export = to_streamed_response_wrapper(
projects.export,
)
+ self.increment_queries = to_streamed_response_wrapper(
+ projects.increment_queries,
+ )
@cached_property
def access_keys(self) -> AccessKeysResourceWithStreamingResponse:
@@ -727,6 +802,9 @@ def __init__(self, projects: AsyncProjectsResource) -> None:
self.export = async_to_streamed_response_wrapper(
projects.export,
)
+ self.increment_queries = async_to_streamed_response_wrapper(
+ projects.increment_queries,
+ )
@cached_property
def access_keys(self) -> AsyncAccessKeysResourceWithStreamingResponse:
diff --git a/src/codex/types/__init__.py b/src/codex/types/__init__.py
index 6c184372..3e65a680 100644
--- a/src/codex/types/__init__.py
+++ b/src/codex/types/__init__.py
@@ -16,3 +16,7 @@
from .project_retrieve_response import ProjectRetrieveResponse as ProjectRetrieveResponse
from .organization_schema_public import OrganizationSchemaPublic as OrganizationSchemaPublic
from .user_activate_account_params import UserActivateAccountParams as UserActivateAccountParams
+from .organization_list_members_response import OrganizationListMembersResponse as OrganizationListMembersResponse
+from .organization_retrieve_permissions_response import (
+ OrganizationRetrievePermissionsResponse as OrganizationRetrievePermissionsResponse,
+)
diff --git a/src/codex/types/organization_list_members_response.py b/src/codex/types/organization_list_members_response.py
new file mode 100644
index 00000000..37897d56
--- /dev/null
+++ b/src/codex/types/organization_list_members_response.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import TypeAlias
+
+from .._models import BaseModel
+
+__all__ = ["OrganizationListMembersResponse", "OrganizationListMembersResponseItem"]
+
+
+class OrganizationListMembersResponseItem(BaseModel):
+ email: str
+
+ name: str
+
+
+OrganizationListMembersResponse: TypeAlias = List[OrganizationListMembersResponseItem]
diff --git a/src/codex/types/organization_retrieve_permissions_response.py b/src/codex/types/organization_retrieve_permissions_response.py
new file mode 100644
index 00000000..ee7b0671
--- /dev/null
+++ b/src/codex/types/organization_retrieve_permissions_response.py
@@ -0,0 +1,8 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict
+from typing_extensions import TypeAlias
+
+__all__ = ["OrganizationRetrievePermissionsResponse"]
+
+OrganizationRetrievePermissionsResponse: TypeAlias = Dict[str, bool]
diff --git a/src/codex/types/projects/__init__.py b/src/codex/types/projects/__init__.py
index 2733406c..2b69e570 100644
--- a/src/codex/types/projects/__init__.py
+++ b/src/codex/types/projects/__init__.py
@@ -10,9 +10,11 @@
from .entry_update_params import EntryUpdateParams as EntryUpdateParams
from .entry_query_response import EntryQueryResponse as EntryQueryResponse
from .cluster_list_response import ClusterListResponse as ClusterListResponse
+from .entry_notify_sme_params import EntryNotifySmeParams as EntryNotifySmeParams
from .access_key_create_params import AccessKeyCreateParams as AccessKeyCreateParams
from .access_key_list_response import AccessKeyListResponse as AccessKeyListResponse
from .access_key_update_params import AccessKeyUpdateParams as AccessKeyUpdateParams
+from .entry_notify_sme_response import EntryNotifySmeResponse as EntryNotifySmeResponse
from .cluster_list_variants_response import ClusterListVariantsResponse as ClusterListVariantsResponse
from .access_key_retrieve_project_id_response import (
AccessKeyRetrieveProjectIDResponse as AccessKeyRetrieveProjectIDResponse,
diff --git a/src/codex/types/projects/entry_notify_sme_params.py b/src/codex/types/projects/entry_notify_sme_params.py
new file mode 100644
index 00000000..409f8bc5
--- /dev/null
+++ b/src/codex/types/projects/entry_notify_sme_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__ = ["EntryNotifySmeParams", "ViewContext"]
+
+
+class EntryNotifySmeParams(TypedDict, total=False):
+ project_id: Required[str]
+
+ email: Required[str]
+
+ view_context: Required[ViewContext]
+
+
+class ViewContext(TypedDict, total=False):
+ page: Required[int]
+
+ filter: Literal["unanswered", "answered", "all", "hallucination", "search_failure", "unhelpful", "difficult_query"]
diff --git a/src/codex/types/projects/entry_notify_sme_response.py b/src/codex/types/projects/entry_notify_sme_response.py
new file mode 100644
index 00000000..dd05a6cf
--- /dev/null
+++ b/src/codex/types/projects/entry_notify_sme_response.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+
+from ..._models import BaseModel
+
+__all__ = ["EntryNotifySmeResponse"]
+
+
+class EntryNotifySmeResponse(BaseModel):
+ entry_id: str
+
+ recipient_email: str
+
+ status: str
diff --git a/tests/api_resources/projects/test_entries.py b/tests/api_resources/projects/test_entries.py
index ca7eecbb..31a5e408 100644
--- a/tests/api_resources/projects/test_entries.py
+++ b/tests/api_resources/projects/test_entries.py
@@ -9,7 +9,11 @@
from codex import Codex, AsyncCodex
from tests.utils import assert_matches_type
-from codex.types.projects import Entry, EntryQueryResponse
+from codex.types.projects import (
+ Entry,
+ EntryQueryResponse,
+ EntryNotifySmeResponse,
+)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -247,6 +251,134 @@ def test_path_params_delete(self, client: Codex) -> None:
project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_notify_sme(self, client: Codex) -> None:
+ entry = client.projects.entries.notify_sme(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ email="email",
+ view_context={"page": 0},
+ )
+ assert_matches_type(EntryNotifySmeResponse, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_notify_sme_with_all_params(self, client: Codex) -> None:
+ entry = client.projects.entries.notify_sme(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ email="email",
+ view_context={
+ "page": 0,
+ "filter": "unanswered",
+ },
+ )
+ assert_matches_type(EntryNotifySmeResponse, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_notify_sme(self, client: Codex) -> None:
+ response = client.projects.entries.with_raw_response.notify_sme(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ email="email",
+ view_context={"page": 0},
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ entry = response.parse()
+ assert_matches_type(EntryNotifySmeResponse, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_streaming_response_notify_sme(self, client: Codex) -> None:
+ with client.projects.entries.with_streaming_response.notify_sme(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ email="email",
+ view_context={"page": 0},
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ entry = response.parse()
+ assert_matches_type(EntryNotifySmeResponse, entry, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_path_params_notify_sme(self, client: Codex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ client.projects.entries.with_raw_response.notify_sme(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="",
+ email="email",
+ view_context={"page": 0},
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `entry_id` but received ''"):
+ client.projects.entries.with_raw_response.notify_sme(
+ entry_id="",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ email="email",
+ view_context={"page": 0},
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_publish_draft_answer(self, client: Codex) -> None:
+ entry = client.projects.entries.publish_draft_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(Entry, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_publish_draft_answer(self, client: Codex) -> None:
+ response = client.projects.entries.with_raw_response.publish_draft_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ entry = response.parse()
+ assert_matches_type(Entry, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_streaming_response_publish_draft_answer(self, client: Codex) -> None:
+ with client.projects.entries.with_streaming_response.publish_draft_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ entry = response.parse()
+ assert_matches_type(Entry, entry, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_path_params_publish_draft_answer(self, client: Codex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ client.projects.entries.with_raw_response.publish_draft_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `entry_id` but received ''"):
+ client.projects.entries.with_raw_response.publish_draft_answer(
+ entry_id="",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
@pytest.mark.skip()
@parametrize
def test_method_query(self, client: Codex) -> None:
@@ -308,6 +440,58 @@ def test_path_params_query(self, client: Codex) -> None:
question="question",
)
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_unpublish_answer(self, client: Codex) -> None:
+ entry = client.projects.entries.unpublish_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(Entry, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_unpublish_answer(self, client: Codex) -> None:
+ response = client.projects.entries.with_raw_response.unpublish_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ entry = response.parse()
+ assert_matches_type(Entry, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_streaming_response_unpublish_answer(self, client: Codex) -> None:
+ with client.projects.entries.with_streaming_response.unpublish_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ entry = response.parse()
+ assert_matches_type(Entry, entry, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_path_params_unpublish_answer(self, client: Codex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ client.projects.entries.with_raw_response.unpublish_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `entry_id` but received ''"):
+ client.projects.entries.with_raw_response.unpublish_answer(
+ entry_id="",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
class TestAsyncEntries:
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
@@ -542,6 +726,134 @@ async def test_path_params_delete(self, async_client: AsyncCodex) -> None:
project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_notify_sme(self, async_client: AsyncCodex) -> None:
+ entry = await async_client.projects.entries.notify_sme(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ email="email",
+ view_context={"page": 0},
+ )
+ assert_matches_type(EntryNotifySmeResponse, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_notify_sme_with_all_params(self, async_client: AsyncCodex) -> None:
+ entry = await async_client.projects.entries.notify_sme(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ email="email",
+ view_context={
+ "page": 0,
+ "filter": "unanswered",
+ },
+ )
+ assert_matches_type(EntryNotifySmeResponse, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_notify_sme(self, async_client: AsyncCodex) -> None:
+ response = await async_client.projects.entries.with_raw_response.notify_sme(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ email="email",
+ view_context={"page": 0},
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ entry = await response.parse()
+ assert_matches_type(EntryNotifySmeResponse, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_streaming_response_notify_sme(self, async_client: AsyncCodex) -> None:
+ async with async_client.projects.entries.with_streaming_response.notify_sme(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ email="email",
+ view_context={"page": 0},
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ entry = await response.parse()
+ assert_matches_type(EntryNotifySmeResponse, entry, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_path_params_notify_sme(self, async_client: AsyncCodex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ await async_client.projects.entries.with_raw_response.notify_sme(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="",
+ email="email",
+ view_context={"page": 0},
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `entry_id` but received ''"):
+ await async_client.projects.entries.with_raw_response.notify_sme(
+ entry_id="",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ email="email",
+ view_context={"page": 0},
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_publish_draft_answer(self, async_client: AsyncCodex) -> None:
+ entry = await async_client.projects.entries.publish_draft_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(Entry, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_publish_draft_answer(self, async_client: AsyncCodex) -> None:
+ response = await async_client.projects.entries.with_raw_response.publish_draft_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ entry = await response.parse()
+ assert_matches_type(Entry, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_streaming_response_publish_draft_answer(self, async_client: AsyncCodex) -> None:
+ async with async_client.projects.entries.with_streaming_response.publish_draft_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ entry = await response.parse()
+ assert_matches_type(Entry, entry, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_path_params_publish_draft_answer(self, async_client: AsyncCodex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ await async_client.projects.entries.with_raw_response.publish_draft_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `entry_id` but received ''"):
+ await async_client.projects.entries.with_raw_response.publish_draft_answer(
+ entry_id="",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
@pytest.mark.skip()
@parametrize
async def test_method_query(self, async_client: AsyncCodex) -> None:
@@ -602,3 +914,55 @@ async def test_path_params_query(self, async_client: AsyncCodex) -> None:
project_id="",
question="question",
)
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_unpublish_answer(self, async_client: AsyncCodex) -> None:
+ entry = await async_client.projects.entries.unpublish_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(Entry, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_unpublish_answer(self, async_client: AsyncCodex) -> None:
+ response = await async_client.projects.entries.with_raw_response.unpublish_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ entry = await response.parse()
+ assert_matches_type(Entry, entry, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_streaming_response_unpublish_answer(self, async_client: AsyncCodex) -> None:
+ async with async_client.projects.entries.with_streaming_response.unpublish_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ entry = await response.parse()
+ assert_matches_type(Entry, entry, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_path_params_unpublish_answer(self, async_client: AsyncCodex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ await async_client.projects.entries.with_raw_response.unpublish_answer(
+ entry_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `entry_id` but received ''"):
+ await async_client.projects.entries.with_raw_response.unpublish_answer(
+ entry_id="",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
diff --git a/tests/api_resources/test_organizations.py b/tests/api_resources/test_organizations.py
index 1a13f77a..c665e85c 100644
--- a/tests/api_resources/test_organizations.py
+++ b/tests/api_resources/test_organizations.py
@@ -8,7 +8,11 @@
import pytest
from codex import Codex, AsyncCodex
-from codex.types import OrganizationSchemaPublic
+from codex.types import (
+ OrganizationSchemaPublic,
+ OrganizationListMembersResponse,
+ OrganizationRetrievePermissionsResponse,
+)
from tests.utils import assert_matches_type
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -59,6 +63,90 @@ def test_path_params_retrieve(self, client: Codex) -> None:
"",
)
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_list_members(self, client: Codex) -> None:
+ organization = client.organizations.list_members(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(OrganizationListMembersResponse, organization, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_list_members(self, client: Codex) -> None:
+ response = client.organizations.with_raw_response.list_members(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ organization = response.parse()
+ assert_matches_type(OrganizationListMembersResponse, organization, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_streaming_response_list_members(self, client: Codex) -> None:
+ with client.organizations.with_streaming_response.list_members(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ organization = response.parse()
+ assert_matches_type(OrganizationListMembersResponse, organization, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_path_params_list_members(self, client: Codex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `organization_id` but received ''"):
+ client.organizations.with_raw_response.list_members(
+ "",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_retrieve_permissions(self, client: Codex) -> None:
+ organization = client.organizations.retrieve_permissions(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(OrganizationRetrievePermissionsResponse, organization, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_retrieve_permissions(self, client: Codex) -> None:
+ response = client.organizations.with_raw_response.retrieve_permissions(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ organization = response.parse()
+ assert_matches_type(OrganizationRetrievePermissionsResponse, organization, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_streaming_response_retrieve_permissions(self, client: Codex) -> None:
+ with client.organizations.with_streaming_response.retrieve_permissions(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ organization = response.parse()
+ assert_matches_type(OrganizationRetrievePermissionsResponse, organization, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_path_params_retrieve_permissions(self, client: Codex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `organization_id` but received ''"):
+ client.organizations.with_raw_response.retrieve_permissions(
+ "",
+ )
+
class TestAsyncOrganizations:
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
@@ -104,3 +192,87 @@ async def test_path_params_retrieve(self, async_client: AsyncCodex) -> None:
await async_client.organizations.with_raw_response.retrieve(
"",
)
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_list_members(self, async_client: AsyncCodex) -> None:
+ organization = await async_client.organizations.list_members(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(OrganizationListMembersResponse, organization, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_list_members(self, async_client: AsyncCodex) -> None:
+ response = await async_client.organizations.with_raw_response.list_members(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ organization = await response.parse()
+ assert_matches_type(OrganizationListMembersResponse, organization, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_streaming_response_list_members(self, async_client: AsyncCodex) -> None:
+ async with async_client.organizations.with_streaming_response.list_members(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ organization = await response.parse()
+ assert_matches_type(OrganizationListMembersResponse, organization, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_path_params_list_members(self, async_client: AsyncCodex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `organization_id` but received ''"):
+ await async_client.organizations.with_raw_response.list_members(
+ "",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_retrieve_permissions(self, async_client: AsyncCodex) -> None:
+ organization = await async_client.organizations.retrieve_permissions(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(OrganizationRetrievePermissionsResponse, organization, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_retrieve_permissions(self, async_client: AsyncCodex) -> None:
+ response = await async_client.organizations.with_raw_response.retrieve_permissions(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ organization = await response.parse()
+ assert_matches_type(OrganizationRetrievePermissionsResponse, organization, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_streaming_response_retrieve_permissions(self, async_client: AsyncCodex) -> None:
+ async with async_client.organizations.with_streaming_response.retrieve_permissions(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ organization = await response.parse()
+ assert_matches_type(OrganizationRetrievePermissionsResponse, organization, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_path_params_retrieve_permissions(self, async_client: AsyncCodex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `organization_id` but received ''"):
+ await async_client.organizations.with_raw_response.retrieve_permissions(
+ "",
+ )
diff --git a/tests/api_resources/test_projects.py b/tests/api_resources/test_projects.py
index 1e2fccbc..bfa657d2 100644
--- a/tests/api_resources/test_projects.py
+++ b/tests/api_resources/test_projects.py
@@ -323,6 +323,48 @@ def test_path_params_export(self, client: Codex) -> None:
"",
)
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_increment_queries(self, client: Codex) -> None:
+ project = client.projects.increment_queries(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(object, project, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_increment_queries(self, client: Codex) -> None:
+ response = client.projects.with_raw_response.increment_queries(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ project = response.parse()
+ assert_matches_type(object, project, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_streaming_response_increment_queries(self, client: Codex) -> None:
+ with client.projects.with_streaming_response.increment_queries(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ project = response.parse()
+ assert_matches_type(object, project, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_path_params_increment_queries(self, client: Codex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ client.projects.with_raw_response.increment_queries(
+ "",
+ )
+
class TestAsyncProjects:
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
@@ -628,3 +670,45 @@ async def test_path_params_export(self, async_client: AsyncCodex) -> None:
await async_client.projects.with_raw_response.export(
"",
)
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_increment_queries(self, async_client: AsyncCodex) -> None:
+ project = await async_client.projects.increment_queries(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(object, project, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_increment_queries(self, async_client: AsyncCodex) -> None:
+ response = await async_client.projects.with_raw_response.increment_queries(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ project = await response.parse()
+ assert_matches_type(object, project, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_streaming_response_increment_queries(self, async_client: AsyncCodex) -> None:
+ async with async_client.projects.with_streaming_response.increment_queries(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ project = await response.parse()
+ assert_matches_type(object, project, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_path_params_increment_queries(self, async_client: AsyncCodex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ await async_client.projects.with_raw_response.increment_queries(
+ "",
+ )
From e81cc3d451c7c8d163c0b80d983140506a0adb66 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 22 Apr 2025 17:48:01 +0000
Subject: [PATCH 06/17] feat(api): add project increment_queries and other
recent endpoints
---
.stats.yml | 2 +-
api.md | 12 +++---------
src/codex/resources/users/myself/api_key.py | 10 +++++-----
src/codex/resources/users/myself/myself.py | 10 +++++-----
src/codex/resources/users/users.py | 10 +++++-----
src/codex/types/__init__.py | 1 -
src/codex/types/users/__init__.py | 1 +
.../{user.py => users/user_schema_public.py} | 6 +++---
tests/api_resources/test_users.py | 18 +++++++++---------
.../api_resources/users/myself/test_api_key.py | 15 +++++++--------
tests/api_resources/users/test_myself.py | 14 +++++++-------
11 files changed, 46 insertions(+), 53 deletions(-)
rename src/codex/types/{user.py => users/user_schema_public.py} (80%)
diff --git a/.stats.yml b/.stats.yml
index d78252d8..aa1535bd 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,3 +1,3 @@
configured_endpoints: 42
openapi_spec_hash: 71ff1de391293cdfb6dcb761ed89210d
-config_hash: 93ac12138700569dc57329400410c0fd
+config_hash: 2d88a0a41f5faca603ff2789a116d988
diff --git a/api.md b/api.md
index 5a314a2d..617cdd24 100644
--- a/api.md
+++ b/api.md
@@ -83,15 +83,9 @@ Methods:
# Users
-Types:
-
-```python
-from codex.types import User
-```
-
Methods:
-- client.users.activate_account(\*\*params) -> User
+- client.users.activate_account(\*\*params) -> UserSchemaPublic
## Myself
@@ -103,13 +97,13 @@ from codex.types.users import UserSchema, UserSchemaPublic
Methods:
-- client.users.myself.retrieve() -> User
+- client.users.myself.retrieve() -> UserSchemaPublic
### APIKey
Methods:
-- client.users.myself.api_key.retrieve() -> User
+- client.users.myself.api_key.retrieve() -> UserSchemaPublic
- client.users.myself.api_key.refresh() -> UserSchema
### Organizations
diff --git a/src/codex/resources/users/myself/api_key.py b/src/codex/resources/users/myself/api_key.py
index 87f647d3..72f1502b 100644
--- a/src/codex/resources/users/myself/api_key.py
+++ b/src/codex/resources/users/myself/api_key.py
@@ -13,9 +13,9 @@
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
-from ....types.user import User
from ...._base_client import make_request_options
from ....types.users.user_schema import UserSchema
+from ....types.users.user_schema_public import UserSchemaPublic
__all__ = ["APIKeyResource", "AsyncAPIKeyResource"]
@@ -49,14 +49,14 @@ def retrieve(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> User:
+ ) -> UserSchemaPublic:
"""Get user when authenticated with API key."""
return self._get(
"/api/users/myself/api-key",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=User,
+ cast_to=UserSchemaPublic,
)
def refresh(
@@ -108,14 +108,14 @@ async def retrieve(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> User:
+ ) -> UserSchemaPublic:
"""Get user when authenticated with API key."""
return await self._get(
"/api/users/myself/api-key",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=User,
+ cast_to=UserSchemaPublic,
)
async def refresh(
diff --git a/src/codex/resources/users/myself/myself.py b/src/codex/resources/users/myself/myself.py
index 2cc4868b..3ee27229 100644
--- a/src/codex/resources/users/myself/myself.py
+++ b/src/codex/resources/users/myself/myself.py
@@ -21,7 +21,6 @@
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
-from ....types.user import User
from .organizations import (
OrganizationsResource,
AsyncOrganizationsResource,
@@ -31,6 +30,7 @@
AsyncOrganizationsResourceWithStreamingResponse,
)
from ...._base_client import make_request_options
+from ....types.users.user_schema_public import UserSchemaPublic
__all__ = ["MyselfResource", "AsyncMyselfResource"]
@@ -72,14 +72,14 @@ def retrieve(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> User:
+ ) -> UserSchemaPublic:
"""Get user info for frontend."""
return self._get(
"/api/users/myself",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=User,
+ cast_to=UserSchemaPublic,
)
@@ -120,14 +120,14 @@ async def retrieve(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> User:
+ ) -> UserSchemaPublic:
"""Get user info for frontend."""
return await self._get(
"/api/users/myself",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=User,
+ cast_to=UserSchemaPublic,
)
diff --git a/src/codex/resources/users/users.py b/src/codex/resources/users/users.py
index a7d9d2ab..c276f950 100644
--- a/src/codex/resources/users/users.py
+++ b/src/codex/resources/users/users.py
@@ -21,7 +21,6 @@
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
-from ...types.user import User
from .verification import (
VerificationResource,
AsyncVerificationResource,
@@ -39,6 +38,7 @@
AsyncMyselfResourceWithStreamingResponse,
)
from ..._base_client import make_request_options
+from ...types.users.user_schema_public import UserSchemaPublic
__all__ = ["UsersResource", "AsyncUsersResource"]
@@ -87,7 +87,7 @@ def activate_account(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> User:
+ ) -> UserSchemaPublic:
"""
Activate an authenticated user's account
@@ -117,7 +117,7 @@ def activate_account(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=User,
+ cast_to=UserSchemaPublic,
)
@@ -165,7 +165,7 @@ async def activate_account(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> User:
+ ) -> UserSchemaPublic:
"""
Activate an authenticated user's account
@@ -195,7 +195,7 @@ async def activate_account(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=User,
+ cast_to=UserSchemaPublic,
)
diff --git a/src/codex/types/__init__.py b/src/codex/types/__init__.py
index 3e65a680..008c4a67 100644
--- a/src/codex/types/__init__.py
+++ b/src/codex/types/__init__.py
@@ -2,7 +2,6 @@
from __future__ import annotations
-from .user import User as User
from .tlm_score_params import TlmScoreParams as TlmScoreParams
from .tlm_prompt_params import TlmPromptParams as TlmPromptParams
from .tlm_score_response import TlmScoreResponse as TlmScoreResponse
diff --git a/src/codex/types/users/__init__.py b/src/codex/types/users/__init__.py
index 438bc6f3..23dbc910 100644
--- a/src/codex/types/users/__init__.py
+++ b/src/codex/types/users/__init__.py
@@ -3,4 +3,5 @@
from __future__ import annotations
from .user_schema import UserSchema as UserSchema
+from .user_schema_public import UserSchemaPublic as UserSchemaPublic
from .verification_resend_response import VerificationResendResponse as VerificationResendResponse
diff --git a/src/codex/types/user.py b/src/codex/types/users/user_schema_public.py
similarity index 80%
rename from src/codex/types/user.py
rename to src/codex/types/users/user_schema_public.py
index 3d7ec233..181113b0 100644
--- a/src/codex/types/user.py
+++ b/src/codex/types/users/user_schema_public.py
@@ -2,12 +2,12 @@
from typing import Optional
-from .._models import BaseModel
+from ..._models import BaseModel
-__all__ = ["User"]
+__all__ = ["UserSchemaPublic"]
-class User(BaseModel):
+class UserSchemaPublic(BaseModel):
id: str
api_key: str
diff --git a/tests/api_resources/test_users.py b/tests/api_resources/test_users.py
index 7e78b99e..101d0f66 100644
--- a/tests/api_resources/test_users.py
+++ b/tests/api_resources/test_users.py
@@ -8,9 +8,9 @@
import pytest
from codex import Codex, AsyncCodex
-from codex.types import User
from tests.utils import assert_matches_type
from codex._utils import parse_datetime
+from codex.types.users import UserSchemaPublic
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -25,7 +25,7 @@ def test_method_activate_account(self, client: Codex) -> None:
first_name="first_name",
last_name="last_name",
)
- assert_matches_type(User, user, path=["response"])
+ assert_matches_type(UserSchemaPublic, user, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -39,7 +39,7 @@ def test_method_activate_account_with_all_params(self, client: Codex) -> None:
phone_number="phone_number",
user_provided_company_name="user_provided_company_name",
)
- assert_matches_type(User, user, path=["response"])
+ assert_matches_type(UserSchemaPublic, user, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -52,7 +52,7 @@ def test_raw_response_activate_account(self, client: Codex) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
user = response.parse()
- assert_matches_type(User, user, path=["response"])
+ assert_matches_type(UserSchemaPublic, user, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -65,7 +65,7 @@ def test_streaming_response_activate_account(self, client: Codex) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
user = response.parse()
- assert_matches_type(User, user, path=["response"])
+ assert_matches_type(UserSchemaPublic, user, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -80,7 +80,7 @@ async def test_method_activate_account(self, async_client: AsyncCodex) -> None:
first_name="first_name",
last_name="last_name",
)
- assert_matches_type(User, user, path=["response"])
+ assert_matches_type(UserSchemaPublic, user, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -94,7 +94,7 @@ async def test_method_activate_account_with_all_params(self, async_client: Async
phone_number="phone_number",
user_provided_company_name="user_provided_company_name",
)
- assert_matches_type(User, user, path=["response"])
+ assert_matches_type(UserSchemaPublic, user, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -107,7 +107,7 @@ async def test_raw_response_activate_account(self, async_client: AsyncCodex) ->
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
user = await response.parse()
- assert_matches_type(User, user, path=["response"])
+ assert_matches_type(UserSchemaPublic, user, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -120,6 +120,6 @@ async def test_streaming_response_activate_account(self, async_client: AsyncCode
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
user = await response.parse()
- assert_matches_type(User, user, path=["response"])
+ assert_matches_type(UserSchemaPublic, user, path=["response"])
assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/users/myself/test_api_key.py b/tests/api_resources/users/myself/test_api_key.py
index a58477bc..c6ed0209 100644
--- a/tests/api_resources/users/myself/test_api_key.py
+++ b/tests/api_resources/users/myself/test_api_key.py
@@ -8,9 +8,8 @@
import pytest
from codex import Codex, AsyncCodex
-from codex.types import User
from tests.utils import assert_matches_type
-from codex.types.users import UserSchema
+from codex.types.users import UserSchema, UserSchemaPublic
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -22,7 +21,7 @@ class TestAPIKey:
@parametrize
def test_method_retrieve(self, client: Codex) -> None:
api_key = client.users.myself.api_key.retrieve()
- assert_matches_type(User, api_key, path=["response"])
+ assert_matches_type(UserSchemaPublic, api_key, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -32,7 +31,7 @@ def test_raw_response_retrieve(self, client: Codex) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
api_key = response.parse()
- assert_matches_type(User, api_key, path=["response"])
+ assert_matches_type(UserSchemaPublic, api_key, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -42,7 +41,7 @@ def test_streaming_response_retrieve(self, client: Codex) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
api_key = response.parse()
- assert_matches_type(User, api_key, path=["response"])
+ assert_matches_type(UserSchemaPublic, api_key, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -82,7 +81,7 @@ class TestAsyncAPIKey:
@parametrize
async def test_method_retrieve(self, async_client: AsyncCodex) -> None:
api_key = await async_client.users.myself.api_key.retrieve()
- assert_matches_type(User, api_key, path=["response"])
+ assert_matches_type(UserSchemaPublic, api_key, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -92,7 +91,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncCodex) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
api_key = await response.parse()
- assert_matches_type(User, api_key, path=["response"])
+ assert_matches_type(UserSchemaPublic, api_key, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -102,7 +101,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncCodex) -> No
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
api_key = await response.parse()
- assert_matches_type(User, api_key, path=["response"])
+ assert_matches_type(UserSchemaPublic, api_key, path=["response"])
assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/users/test_myself.py b/tests/api_resources/users/test_myself.py
index 174f2cec..63123275 100644
--- a/tests/api_resources/users/test_myself.py
+++ b/tests/api_resources/users/test_myself.py
@@ -8,8 +8,8 @@
import pytest
from codex import Codex, AsyncCodex
-from codex.types import User
from tests.utils import assert_matches_type
+from codex.types.users import UserSchemaPublic
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -21,7 +21,7 @@ class TestMyself:
@parametrize
def test_method_retrieve(self, client: Codex) -> None:
myself = client.users.myself.retrieve()
- assert_matches_type(User, myself, path=["response"])
+ assert_matches_type(UserSchemaPublic, myself, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -31,7 +31,7 @@ def test_raw_response_retrieve(self, client: Codex) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
myself = response.parse()
- assert_matches_type(User, myself, path=["response"])
+ assert_matches_type(UserSchemaPublic, myself, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -41,7 +41,7 @@ def test_streaming_response_retrieve(self, client: Codex) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
myself = response.parse()
- assert_matches_type(User, myself, path=["response"])
+ assert_matches_type(UserSchemaPublic, myself, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -53,7 +53,7 @@ class TestAsyncMyself:
@parametrize
async def test_method_retrieve(self, async_client: AsyncCodex) -> None:
myself = await async_client.users.myself.retrieve()
- assert_matches_type(User, myself, path=["response"])
+ assert_matches_type(UserSchemaPublic, myself, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -63,7 +63,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncCodex) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
myself = await response.parse()
- assert_matches_type(User, myself, path=["response"])
+ assert_matches_type(UserSchemaPublic, myself, path=["response"])
@pytest.mark.skip()
@parametrize
@@ -73,6 +73,6 @@ async def test_streaming_response_retrieve(self, async_client: AsyncCodex) -> No
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
myself = await response.parse()
- assert_matches_type(User, myself, path=["response"])
+ assert_matches_type(UserSchemaPublic, myself, path=["response"])
assert cast(Any, response.is_closed) is True
From 5bf31f74b6008c9bda7b4ec290b1216a51ef9d1f Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 22 Apr 2025 17:59:42 +0000
Subject: [PATCH 07/17] feat(api): add project increment_queries and other
recent endpoints
---
.stats.yml | 2 +-
src/codex/_client.py | 71 -------------------
src/codex/resources/projects/access_keys.py | 4 ++
src/codex/resources/projects/clusters.py | 4 +-
src/codex/resources/projects/entries.py | 12 +++-
src/codex/resources/projects/projects.py | 8 +--
src/codex/types/project_list_params.py | 6 +-
.../projects/access_key_create_params.py | 2 +
.../types/projects/entry_create_params.py | 2 +
.../types/projects/entry_query_params.py | 2 +
.../projects/test_access_keys.py | 2 +
tests/api_resources/projects/test_entries.py | 4 ++
tests/api_resources/test_projects.py | 28 +++-----
13 files changed, 44 insertions(+), 103 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index aa1535bd..fd7bea9a 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,3 +1,3 @@
configured_endpoints: 42
-openapi_spec_hash: 71ff1de391293cdfb6dcb761ed89210d
+openapi_spec_hash: 8d2e7726c60bca0dcfc72b1e2df34ef1
config_hash: 2d88a0a41f5faca603ff2789a116d988
diff --git a/src/codex/_client.py b/src/codex/_client.py
index 2641513f..f035421c 100644
--- a/src/codex/_client.py
+++ b/src/codex/_client.py
@@ -13,7 +13,6 @@
from ._types import (
NOT_GIVEN,
Omit,
- Headers,
Timeout,
NotGiven,
Transport,
@@ -151,25 +150,6 @@ def __init__(
def qs(self) -> Querystring:
return Querystring(array_format="comma")
- @property
- @override
- def auth_headers(self) -> dict[str, str]:
- return {**self._authenticated_api_key, **self._public_access_key}
-
- @property
- def _authenticated_api_key(self) -> dict[str, str]:
- api_key = self.api_key
- if api_key is None:
- return {}
- return {"X-API-Key": api_key}
-
- @property
- def _public_access_key(self) -> dict[str, str]:
- access_key = self.access_key
- if access_key is None:
- return {}
- return {"X-Access-Key": access_key}
-
@property
@override
def default_headers(self) -> dict[str, str | Omit]:
@@ -179,22 +159,6 @@ def default_headers(self) -> dict[str, str | Omit]:
**self._custom_headers,
}
- @override
- def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
- if self.api_key and headers.get("X-API-Key"):
- return
- if isinstance(custom_headers.get("X-API-Key"), Omit):
- return
-
- if self.access_key and headers.get("X-Access-Key"):
- return
- if isinstance(custom_headers.get("X-Access-Key"), Omit):
- return
-
- raise TypeError(
- '"Could not resolve authentication method. Expected either api_key or access_key to be set. Or for one of the `X-API-Key` or `X-Access-Key` headers to be explicitly omitted"'
- )
-
def copy(
self,
*,
@@ -379,25 +343,6 @@ def __init__(
def qs(self) -> Querystring:
return Querystring(array_format="comma")
- @property
- @override
- def auth_headers(self) -> dict[str, str]:
- return {**self._authenticated_api_key, **self._public_access_key}
-
- @property
- def _authenticated_api_key(self) -> dict[str, str]:
- api_key = self.api_key
- if api_key is None:
- return {}
- return {"X-API-Key": api_key}
-
- @property
- def _public_access_key(self) -> dict[str, str]:
- access_key = self.access_key
- if access_key is None:
- return {}
- return {"X-Access-Key": access_key}
-
@property
@override
def default_headers(self) -> dict[str, str | Omit]:
@@ -407,22 +352,6 @@ def default_headers(self) -> dict[str, str | Omit]:
**self._custom_headers,
}
- @override
- def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
- if self.api_key and headers.get("X-API-Key"):
- return
- if isinstance(custom_headers.get("X-API-Key"), Omit):
- return
-
- if self.access_key and headers.get("X-Access-Key"):
- return
- if isinstance(custom_headers.get("X-Access-Key"), Omit):
- return
-
- raise TypeError(
- '"Could not resolve authentication method. Expected either api_key or access_key to be set. Or for one of the `X-API-Key` or `X-Access-Key` headers to be explicitly omitted"'
- )
-
def copy(
self,
*,
diff --git a/src/codex/resources/projects/access_keys.py b/src/codex/resources/projects/access_keys.py
index 61987399..9ba2e131 100644
--- a/src/codex/resources/projects/access_keys.py
+++ b/src/codex/resources/projects/access_keys.py
@@ -57,6 +57,7 @@ def create(
name: str,
description: Optional[str] | NotGiven = NOT_GIVEN,
expires_at: Union[str, datetime, None] | NotGiven = NOT_GIVEN,
+ x_access_key: str | NotGiven = NOT_GIVEN,
x_client_library_version: str | NotGiven = NOT_GIVEN,
x_integration_type: str | NotGiven = NOT_GIVEN,
x_source: str | NotGiven = NOT_GIVEN,
@@ -85,6 +86,7 @@ def create(
extra_headers = {
**strip_not_given(
{
+ "x-access-key": x_access_key,
"x-client-library-version": x_client_library_version,
"x-integration-type": x_integration_type,
"x-source": x_source,
@@ -346,6 +348,7 @@ async def create(
name: str,
description: Optional[str] | NotGiven = NOT_GIVEN,
expires_at: Union[str, datetime, None] | NotGiven = NOT_GIVEN,
+ x_access_key: str | NotGiven = NOT_GIVEN,
x_client_library_version: str | NotGiven = NOT_GIVEN,
x_integration_type: str | NotGiven = NOT_GIVEN,
x_source: str | NotGiven = NOT_GIVEN,
@@ -374,6 +377,7 @@ async def create(
extra_headers = {
**strip_not_given(
{
+ "x-access-key": x_access_key,
"x-client-library-version": x_client_library_version,
"x-integration-type": x_integration_type,
"x-source": x_source,
diff --git a/src/codex/resources/projects/clusters.py b/src/codex/resources/projects/clusters.py
index 584cde18..eabbe60e 100644
--- a/src/codex/resources/projects/clusters.py
+++ b/src/codex/resources/projects/clusters.py
@@ -115,7 +115,7 @@ def list_variants(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> ClusterListVariantsResponse:
"""
- Get Cluster Variants Route
+ Get Cluster Variants
Args:
extra_headers: Send extra headers
@@ -230,7 +230,7 @@ async def list_variants(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> ClusterListVariantsResponse:
"""
- Get Cluster Variants Route
+ Get Cluster Variants
Args:
extra_headers: Send extra headers
diff --git a/src/codex/resources/projects/entries.py b/src/codex/resources/projects/entries.py
index f88e2243..bdfee06d 100644
--- a/src/codex/resources/projects/entries.py
+++ b/src/codex/resources/projects/entries.py
@@ -57,6 +57,7 @@ def create(
answer: Optional[str] | NotGiven = NOT_GIVEN,
client_query_metadata: Iterable[object] | NotGiven = NOT_GIVEN,
draft_answer: Optional[str] | NotGiven = NOT_GIVEN,
+ x_access_key: str | NotGiven = NOT_GIVEN,
x_client_library_version: str | NotGiven = NOT_GIVEN,
x_integration_type: str | NotGiven = NOT_GIVEN,
x_source: str | NotGiven = NOT_GIVEN,
@@ -85,6 +86,7 @@ def create(
extra_headers = {
**strip_not_given(
{
+ "x-access-key": x_access_key,
"x-client-library-version": x_client_library_version,
"x-integration-type": x_integration_type,
"x-source": x_source,
@@ -323,6 +325,7 @@ def query(
question: str,
use_llm_matching: bool | NotGiven = NOT_GIVEN,
client_metadata: Optional[object] | NotGiven = NOT_GIVEN,
+ x_access_key: str | NotGiven = NOT_GIVEN,
x_client_library_version: str | NotGiven = NOT_GIVEN,
x_integration_type: str | NotGiven = NOT_GIVEN,
x_source: str | NotGiven = NOT_GIVEN,
@@ -335,7 +338,7 @@ def query(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> EntryQueryResponse:
"""
- Query Entries Route
+ Query Entries
Args:
extra_headers: Send extra headers
@@ -351,6 +354,7 @@ def query(
extra_headers = {
**strip_not_given(
{
+ "x-access-key": x_access_key,
"x-client-library-version": x_client_library_version,
"x-integration-type": x_integration_type,
"x-source": x_source,
@@ -446,6 +450,7 @@ async def create(
answer: Optional[str] | NotGiven = NOT_GIVEN,
client_query_metadata: Iterable[object] | NotGiven = NOT_GIVEN,
draft_answer: Optional[str] | NotGiven = NOT_GIVEN,
+ x_access_key: str | NotGiven = NOT_GIVEN,
x_client_library_version: str | NotGiven = NOT_GIVEN,
x_integration_type: str | NotGiven = NOT_GIVEN,
x_source: str | NotGiven = NOT_GIVEN,
@@ -474,6 +479,7 @@ async def create(
extra_headers = {
**strip_not_given(
{
+ "x-access-key": x_access_key,
"x-client-library-version": x_client_library_version,
"x-integration-type": x_integration_type,
"x-source": x_source,
@@ -712,6 +718,7 @@ async def query(
question: str,
use_llm_matching: bool | NotGiven = NOT_GIVEN,
client_metadata: Optional[object] | NotGiven = NOT_GIVEN,
+ x_access_key: str | NotGiven = NOT_GIVEN,
x_client_library_version: str | NotGiven = NOT_GIVEN,
x_integration_type: str | NotGiven = NOT_GIVEN,
x_source: str | NotGiven = NOT_GIVEN,
@@ -724,7 +731,7 @@ async def query(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> EntryQueryResponse:
"""
- Query Entries Route
+ Query Entries
Args:
extra_headers: Send extra headers
@@ -740,6 +747,7 @@ async def query(
extra_headers = {
**strip_not_given(
{
+ "x-access-key": x_access_key,
"x-client-library-version": x_client_library_version,
"x-integration-type": x_integration_type,
"x-source": x_source,
diff --git a/src/codex/resources/projects/projects.py b/src/codex/resources/projects/projects.py
index 47639a5a..76237fb6 100644
--- a/src/codex/resources/projects/projects.py
+++ b/src/codex/resources/projects/projects.py
@@ -208,11 +208,11 @@ def update(
def list(
self,
*,
- organization_id: str,
include_entry_counts: bool | NotGiven = NOT_GIVEN,
limit: int | NotGiven = NOT_GIVEN,
offset: int | NotGiven = NOT_GIVEN,
order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN,
+ organization_id: str | NotGiven = NOT_GIVEN,
query: Optional[str] | NotGiven = NOT_GIVEN,
sort: Literal["created_at", "updated_at"] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -243,11 +243,11 @@ def list(
timeout=timeout,
query=maybe_transform(
{
- "organization_id": organization_id,
"include_entry_counts": include_entry_counts,
"limit": limit,
"offset": offset,
"order": order,
+ "organization_id": organization_id,
"query": query,
"sort": sort,
},
@@ -513,11 +513,11 @@ async def update(
async def list(
self,
*,
- organization_id: str,
include_entry_counts: bool | NotGiven = NOT_GIVEN,
limit: int | NotGiven = NOT_GIVEN,
offset: int | NotGiven = NOT_GIVEN,
order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN,
+ organization_id: str | NotGiven = NOT_GIVEN,
query: Optional[str] | NotGiven = NOT_GIVEN,
sort: Literal["created_at", "updated_at"] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -548,11 +548,11 @@ async def list(
timeout=timeout,
query=await async_maybe_transform(
{
- "organization_id": organization_id,
"include_entry_counts": include_entry_counts,
"limit": limit,
"offset": offset,
"order": order,
+ "organization_id": organization_id,
"query": query,
"sort": sort,
},
diff --git a/src/codex/types/project_list_params.py b/src/codex/types/project_list_params.py
index c2589598..0ab3b84b 100644
--- a/src/codex/types/project_list_params.py
+++ b/src/codex/types/project_list_params.py
@@ -3,14 +3,12 @@
from __future__ import annotations
from typing import Optional
-from typing_extensions import Literal, Required, TypedDict
+from typing_extensions import Literal, TypedDict
__all__ = ["ProjectListParams"]
class ProjectListParams(TypedDict, total=False):
- organization_id: Required[str]
-
include_entry_counts: bool
limit: int
@@ -19,6 +17,8 @@ class ProjectListParams(TypedDict, total=False):
order: Literal["asc", "desc"]
+ organization_id: str
+
query: Optional[str]
sort: Literal["created_at", "updated_at"]
diff --git a/src/codex/types/projects/access_key_create_params.py b/src/codex/types/projects/access_key_create_params.py
index cf5f00fb..5035836d 100644
--- a/src/codex/types/projects/access_key_create_params.py
+++ b/src/codex/types/projects/access_key_create_params.py
@@ -18,6 +18,8 @@ class AccessKeyCreateParams(TypedDict, total=False):
expires_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")]
+ x_access_key: Annotated[str, PropertyInfo(alias="x-access-key")]
+
x_client_library_version: Annotated[str, PropertyInfo(alias="x-client-library-version")]
x_integration_type: Annotated[str, PropertyInfo(alias="x-integration-type")]
diff --git a/src/codex/types/projects/entry_create_params.py b/src/codex/types/projects/entry_create_params.py
index f06846bb..490b0e83 100644
--- a/src/codex/types/projects/entry_create_params.py
+++ b/src/codex/types/projects/entry_create_params.py
@@ -19,6 +19,8 @@ class EntryCreateParams(TypedDict, total=False):
draft_answer: Optional[str]
+ x_access_key: Annotated[str, PropertyInfo(alias="x-access-key")]
+
x_client_library_version: Annotated[str, PropertyInfo(alias="x-client-library-version")]
x_integration_type: Annotated[str, PropertyInfo(alias="x-integration-type")]
diff --git a/src/codex/types/projects/entry_query_params.py b/src/codex/types/projects/entry_query_params.py
index d58b7bfa..db1aec77 100644
--- a/src/codex/types/projects/entry_query_params.py
+++ b/src/codex/types/projects/entry_query_params.py
@@ -17,6 +17,8 @@ class EntryQueryParams(TypedDict, total=False):
client_metadata: Optional[object]
+ x_access_key: Annotated[str, PropertyInfo(alias="x-access-key")]
+
x_client_library_version: Annotated[str, PropertyInfo(alias="x-client-library-version")]
x_integration_type: Annotated[str, PropertyInfo(alias="x-integration-type")]
diff --git a/tests/api_resources/projects/test_access_keys.py b/tests/api_resources/projects/test_access_keys.py
index ad4ee5e4..bcf3bb7a 100644
--- a/tests/api_resources/projects/test_access_keys.py
+++ b/tests/api_resources/projects/test_access_keys.py
@@ -39,6 +39,7 @@ def test_method_create_with_all_params(self, client: Codex) -> None:
name="name",
description="description",
expires_at=parse_datetime("2019-12-27T18:11:19.117Z"),
+ x_access_key="x-access-key",
x_client_library_version="x-client-library-version",
x_integration_type="x-integration-type",
x_source="x-source",
@@ -399,6 +400,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncCodex) ->
name="name",
description="description",
expires_at=parse_datetime("2019-12-27T18:11:19.117Z"),
+ x_access_key="x-access-key",
x_client_library_version="x-client-library-version",
x_integration_type="x-integration-type",
x_source="x-source",
diff --git a/tests/api_resources/projects/test_entries.py b/tests/api_resources/projects/test_entries.py
index 31a5e408..710d2146 100644
--- a/tests/api_resources/projects/test_entries.py
+++ b/tests/api_resources/projects/test_entries.py
@@ -39,6 +39,7 @@ def test_method_create_with_all_params(self, client: Codex) -> None:
answer="answer",
client_query_metadata=[{}],
draft_answer="draft_answer",
+ x_access_key="x-access-key",
x_client_library_version="x-client-library-version",
x_integration_type="x-integration-type",
x_source="x-source",
@@ -396,6 +397,7 @@ def test_method_query_with_all_params(self, client: Codex) -> None:
question="question",
use_llm_matching=True,
client_metadata={},
+ x_access_key="x-access-key",
x_client_library_version="x-client-library-version",
x_integration_type="x-integration-type",
x_source="x-source",
@@ -514,6 +516,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncCodex) ->
answer="answer",
client_query_metadata=[{}],
draft_answer="draft_answer",
+ x_access_key="x-access-key",
x_client_library_version="x-client-library-version",
x_integration_type="x-integration-type",
x_source="x-source",
@@ -871,6 +874,7 @@ async def test_method_query_with_all_params(self, async_client: AsyncCodex) -> N
question="question",
use_llm_matching=True,
client_metadata={},
+ x_access_key="x-access-key",
x_client_library_version="x-client-library-version",
x_integration_type="x-integration-type",
x_source="x-source",
diff --git a/tests/api_resources/test_projects.py b/tests/api_resources/test_projects.py
index bfa657d2..d71d0e19 100644
--- a/tests/api_resources/test_projects.py
+++ b/tests/api_resources/test_projects.py
@@ -194,20 +194,18 @@ def test_path_params_update(self, client: Codex) -> None:
@pytest.mark.skip()
@parametrize
def test_method_list(self, client: Codex) -> None:
- project = client.projects.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
+ project = client.projects.list()
assert_matches_type(ProjectListResponse, project, path=["response"])
@pytest.mark.skip()
@parametrize
def test_method_list_with_all_params(self, client: Codex) -> None:
project = client.projects.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
include_entry_counts=True,
limit=0,
offset=0,
order="asc",
+ organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
query="query",
sort="created_at",
)
@@ -216,9 +214,7 @@ def test_method_list_with_all_params(self, client: Codex) -> None:
@pytest.mark.skip()
@parametrize
def test_raw_response_list(self, client: Codex) -> None:
- response = client.projects.with_raw_response.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
+ response = client.projects.with_raw_response.list()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -228,9 +224,7 @@ def test_raw_response_list(self, client: Codex) -> None:
@pytest.mark.skip()
@parametrize
def test_streaming_response_list(self, client: Codex) -> None:
- with client.projects.with_streaming_response.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
+ with client.projects.with_streaming_response.list() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -542,20 +536,18 @@ async def test_path_params_update(self, async_client: AsyncCodex) -> None:
@pytest.mark.skip()
@parametrize
async def test_method_list(self, async_client: AsyncCodex) -> None:
- project = await async_client.projects.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
+ project = await async_client.projects.list()
assert_matches_type(ProjectListResponse, project, path=["response"])
@pytest.mark.skip()
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncCodex) -> None:
project = await async_client.projects.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
include_entry_counts=True,
limit=0,
offset=0,
order="asc",
+ organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
query="query",
sort="created_at",
)
@@ -564,9 +556,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncCodex) -> No
@pytest.mark.skip()
@parametrize
async def test_raw_response_list(self, async_client: AsyncCodex) -> None:
- response = await async_client.projects.with_raw_response.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
+ response = await async_client.projects.with_raw_response.list()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -576,9 +566,7 @@ async def test_raw_response_list(self, async_client: AsyncCodex) -> None:
@pytest.mark.skip()
@parametrize
async def test_streaming_response_list(self, async_client: AsyncCodex) -> None:
- async with async_client.projects.with_streaming_response.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
+ async with async_client.projects.with_streaming_response.list() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
From 3f857575176d200a639880dfa7e60e3a9949750c Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 22 Apr 2025 18:17:09 +0000
Subject: [PATCH 08/17] feat(api): api update
---
.stats.yml | 2 +-
src/codex/_client.py | 71 +++++++++++++++++++
src/codex/resources/projects/access_keys.py | 4 --
src/codex/resources/projects/clusters.py | 4 +-
src/codex/resources/projects/entries.py | 12 +---
src/codex/resources/projects/projects.py | 8 +--
src/codex/types/project_list_params.py | 6 +-
.../projects/access_key_create_params.py | 2 -
.../types/projects/entry_create_params.py | 2 -
.../types/projects/entry_query_params.py | 2 -
.../projects/test_access_keys.py | 2 -
tests/api_resources/projects/test_entries.py | 4 --
tests/api_resources/test_projects.py | 28 +++++---
13 files changed, 103 insertions(+), 44 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index fd7bea9a..aa1535bd 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,3 +1,3 @@
configured_endpoints: 42
-openapi_spec_hash: 8d2e7726c60bca0dcfc72b1e2df34ef1
+openapi_spec_hash: 71ff1de391293cdfb6dcb761ed89210d
config_hash: 2d88a0a41f5faca603ff2789a116d988
diff --git a/src/codex/_client.py b/src/codex/_client.py
index f035421c..2641513f 100644
--- a/src/codex/_client.py
+++ b/src/codex/_client.py
@@ -13,6 +13,7 @@
from ._types import (
NOT_GIVEN,
Omit,
+ Headers,
Timeout,
NotGiven,
Transport,
@@ -150,6 +151,25 @@ def __init__(
def qs(self) -> Querystring:
return Querystring(array_format="comma")
+ @property
+ @override
+ def auth_headers(self) -> dict[str, str]:
+ return {**self._authenticated_api_key, **self._public_access_key}
+
+ @property
+ def _authenticated_api_key(self) -> dict[str, str]:
+ api_key = self.api_key
+ if api_key is None:
+ return {}
+ return {"X-API-Key": api_key}
+
+ @property
+ def _public_access_key(self) -> dict[str, str]:
+ access_key = self.access_key
+ if access_key is None:
+ return {}
+ return {"X-Access-Key": access_key}
+
@property
@override
def default_headers(self) -> dict[str, str | Omit]:
@@ -159,6 +179,22 @@ def default_headers(self) -> dict[str, str | Omit]:
**self._custom_headers,
}
+ @override
+ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
+ if self.api_key and headers.get("X-API-Key"):
+ return
+ if isinstance(custom_headers.get("X-API-Key"), Omit):
+ return
+
+ if self.access_key and headers.get("X-Access-Key"):
+ return
+ if isinstance(custom_headers.get("X-Access-Key"), Omit):
+ return
+
+ raise TypeError(
+ '"Could not resolve authentication method. Expected either api_key or access_key to be set. Or for one of the `X-API-Key` or `X-Access-Key` headers to be explicitly omitted"'
+ )
+
def copy(
self,
*,
@@ -343,6 +379,25 @@ def __init__(
def qs(self) -> Querystring:
return Querystring(array_format="comma")
+ @property
+ @override
+ def auth_headers(self) -> dict[str, str]:
+ return {**self._authenticated_api_key, **self._public_access_key}
+
+ @property
+ def _authenticated_api_key(self) -> dict[str, str]:
+ api_key = self.api_key
+ if api_key is None:
+ return {}
+ return {"X-API-Key": api_key}
+
+ @property
+ def _public_access_key(self) -> dict[str, str]:
+ access_key = self.access_key
+ if access_key is None:
+ return {}
+ return {"X-Access-Key": access_key}
+
@property
@override
def default_headers(self) -> dict[str, str | Omit]:
@@ -352,6 +407,22 @@ def default_headers(self) -> dict[str, str | Omit]:
**self._custom_headers,
}
+ @override
+ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
+ if self.api_key and headers.get("X-API-Key"):
+ return
+ if isinstance(custom_headers.get("X-API-Key"), Omit):
+ return
+
+ if self.access_key and headers.get("X-Access-Key"):
+ return
+ if isinstance(custom_headers.get("X-Access-Key"), Omit):
+ return
+
+ raise TypeError(
+ '"Could not resolve authentication method. Expected either api_key or access_key to be set. Or for one of the `X-API-Key` or `X-Access-Key` headers to be explicitly omitted"'
+ )
+
def copy(
self,
*,
diff --git a/src/codex/resources/projects/access_keys.py b/src/codex/resources/projects/access_keys.py
index 9ba2e131..61987399 100644
--- a/src/codex/resources/projects/access_keys.py
+++ b/src/codex/resources/projects/access_keys.py
@@ -57,7 +57,6 @@ def create(
name: str,
description: Optional[str] | NotGiven = NOT_GIVEN,
expires_at: Union[str, datetime, None] | NotGiven = NOT_GIVEN,
- x_access_key: str | NotGiven = NOT_GIVEN,
x_client_library_version: str | NotGiven = NOT_GIVEN,
x_integration_type: str | NotGiven = NOT_GIVEN,
x_source: str | NotGiven = NOT_GIVEN,
@@ -86,7 +85,6 @@ def create(
extra_headers = {
**strip_not_given(
{
- "x-access-key": x_access_key,
"x-client-library-version": x_client_library_version,
"x-integration-type": x_integration_type,
"x-source": x_source,
@@ -348,7 +346,6 @@ async def create(
name: str,
description: Optional[str] | NotGiven = NOT_GIVEN,
expires_at: Union[str, datetime, None] | NotGiven = NOT_GIVEN,
- x_access_key: str | NotGiven = NOT_GIVEN,
x_client_library_version: str | NotGiven = NOT_GIVEN,
x_integration_type: str | NotGiven = NOT_GIVEN,
x_source: str | NotGiven = NOT_GIVEN,
@@ -377,7 +374,6 @@ async def create(
extra_headers = {
**strip_not_given(
{
- "x-access-key": x_access_key,
"x-client-library-version": x_client_library_version,
"x-integration-type": x_integration_type,
"x-source": x_source,
diff --git a/src/codex/resources/projects/clusters.py b/src/codex/resources/projects/clusters.py
index eabbe60e..584cde18 100644
--- a/src/codex/resources/projects/clusters.py
+++ b/src/codex/resources/projects/clusters.py
@@ -115,7 +115,7 @@ def list_variants(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> ClusterListVariantsResponse:
"""
- Get Cluster Variants
+ Get Cluster Variants Route
Args:
extra_headers: Send extra headers
@@ -230,7 +230,7 @@ async def list_variants(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> ClusterListVariantsResponse:
"""
- Get Cluster Variants
+ Get Cluster Variants Route
Args:
extra_headers: Send extra headers
diff --git a/src/codex/resources/projects/entries.py b/src/codex/resources/projects/entries.py
index bdfee06d..f88e2243 100644
--- a/src/codex/resources/projects/entries.py
+++ b/src/codex/resources/projects/entries.py
@@ -57,7 +57,6 @@ def create(
answer: Optional[str] | NotGiven = NOT_GIVEN,
client_query_metadata: Iterable[object] | NotGiven = NOT_GIVEN,
draft_answer: Optional[str] | NotGiven = NOT_GIVEN,
- x_access_key: str | NotGiven = NOT_GIVEN,
x_client_library_version: str | NotGiven = NOT_GIVEN,
x_integration_type: str | NotGiven = NOT_GIVEN,
x_source: str | NotGiven = NOT_GIVEN,
@@ -86,7 +85,6 @@ def create(
extra_headers = {
**strip_not_given(
{
- "x-access-key": x_access_key,
"x-client-library-version": x_client_library_version,
"x-integration-type": x_integration_type,
"x-source": x_source,
@@ -325,7 +323,6 @@ def query(
question: str,
use_llm_matching: bool | NotGiven = NOT_GIVEN,
client_metadata: Optional[object] | NotGiven = NOT_GIVEN,
- x_access_key: str | NotGiven = NOT_GIVEN,
x_client_library_version: str | NotGiven = NOT_GIVEN,
x_integration_type: str | NotGiven = NOT_GIVEN,
x_source: str | NotGiven = NOT_GIVEN,
@@ -338,7 +335,7 @@ def query(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> EntryQueryResponse:
"""
- Query Entries
+ Query Entries Route
Args:
extra_headers: Send extra headers
@@ -354,7 +351,6 @@ def query(
extra_headers = {
**strip_not_given(
{
- "x-access-key": x_access_key,
"x-client-library-version": x_client_library_version,
"x-integration-type": x_integration_type,
"x-source": x_source,
@@ -450,7 +446,6 @@ async def create(
answer: Optional[str] | NotGiven = NOT_GIVEN,
client_query_metadata: Iterable[object] | NotGiven = NOT_GIVEN,
draft_answer: Optional[str] | NotGiven = NOT_GIVEN,
- x_access_key: str | NotGiven = NOT_GIVEN,
x_client_library_version: str | NotGiven = NOT_GIVEN,
x_integration_type: str | NotGiven = NOT_GIVEN,
x_source: str | NotGiven = NOT_GIVEN,
@@ -479,7 +474,6 @@ async def create(
extra_headers = {
**strip_not_given(
{
- "x-access-key": x_access_key,
"x-client-library-version": x_client_library_version,
"x-integration-type": x_integration_type,
"x-source": x_source,
@@ -718,7 +712,6 @@ async def query(
question: str,
use_llm_matching: bool | NotGiven = NOT_GIVEN,
client_metadata: Optional[object] | NotGiven = NOT_GIVEN,
- x_access_key: str | NotGiven = NOT_GIVEN,
x_client_library_version: str | NotGiven = NOT_GIVEN,
x_integration_type: str | NotGiven = NOT_GIVEN,
x_source: str | NotGiven = NOT_GIVEN,
@@ -731,7 +724,7 @@ async def query(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> EntryQueryResponse:
"""
- Query Entries
+ Query Entries Route
Args:
extra_headers: Send extra headers
@@ -747,7 +740,6 @@ async def query(
extra_headers = {
**strip_not_given(
{
- "x-access-key": x_access_key,
"x-client-library-version": x_client_library_version,
"x-integration-type": x_integration_type,
"x-source": x_source,
diff --git a/src/codex/resources/projects/projects.py b/src/codex/resources/projects/projects.py
index 76237fb6..47639a5a 100644
--- a/src/codex/resources/projects/projects.py
+++ b/src/codex/resources/projects/projects.py
@@ -208,11 +208,11 @@ def update(
def list(
self,
*,
+ organization_id: str,
include_entry_counts: bool | NotGiven = NOT_GIVEN,
limit: int | NotGiven = NOT_GIVEN,
offset: int | NotGiven = NOT_GIVEN,
order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN,
- organization_id: str | NotGiven = NOT_GIVEN,
query: Optional[str] | NotGiven = NOT_GIVEN,
sort: Literal["created_at", "updated_at"] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -243,11 +243,11 @@ def list(
timeout=timeout,
query=maybe_transform(
{
+ "organization_id": organization_id,
"include_entry_counts": include_entry_counts,
"limit": limit,
"offset": offset,
"order": order,
- "organization_id": organization_id,
"query": query,
"sort": sort,
},
@@ -513,11 +513,11 @@ async def update(
async def list(
self,
*,
+ organization_id: str,
include_entry_counts: bool | NotGiven = NOT_GIVEN,
limit: int | NotGiven = NOT_GIVEN,
offset: int | NotGiven = NOT_GIVEN,
order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN,
- organization_id: str | NotGiven = NOT_GIVEN,
query: Optional[str] | NotGiven = NOT_GIVEN,
sort: Literal["created_at", "updated_at"] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -548,11 +548,11 @@ async def list(
timeout=timeout,
query=await async_maybe_transform(
{
+ "organization_id": organization_id,
"include_entry_counts": include_entry_counts,
"limit": limit,
"offset": offset,
"order": order,
- "organization_id": organization_id,
"query": query,
"sort": sort,
},
diff --git a/src/codex/types/project_list_params.py b/src/codex/types/project_list_params.py
index 0ab3b84b..c2589598 100644
--- a/src/codex/types/project_list_params.py
+++ b/src/codex/types/project_list_params.py
@@ -3,12 +3,14 @@
from __future__ import annotations
from typing import Optional
-from typing_extensions import Literal, TypedDict
+from typing_extensions import Literal, Required, TypedDict
__all__ = ["ProjectListParams"]
class ProjectListParams(TypedDict, total=False):
+ organization_id: Required[str]
+
include_entry_counts: bool
limit: int
@@ -17,8 +19,6 @@ class ProjectListParams(TypedDict, total=False):
order: Literal["asc", "desc"]
- organization_id: str
-
query: Optional[str]
sort: Literal["created_at", "updated_at"]
diff --git a/src/codex/types/projects/access_key_create_params.py b/src/codex/types/projects/access_key_create_params.py
index 5035836d..cf5f00fb 100644
--- a/src/codex/types/projects/access_key_create_params.py
+++ b/src/codex/types/projects/access_key_create_params.py
@@ -18,8 +18,6 @@ class AccessKeyCreateParams(TypedDict, total=False):
expires_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")]
- x_access_key: Annotated[str, PropertyInfo(alias="x-access-key")]
-
x_client_library_version: Annotated[str, PropertyInfo(alias="x-client-library-version")]
x_integration_type: Annotated[str, PropertyInfo(alias="x-integration-type")]
diff --git a/src/codex/types/projects/entry_create_params.py b/src/codex/types/projects/entry_create_params.py
index 490b0e83..f06846bb 100644
--- a/src/codex/types/projects/entry_create_params.py
+++ b/src/codex/types/projects/entry_create_params.py
@@ -19,8 +19,6 @@ class EntryCreateParams(TypedDict, total=False):
draft_answer: Optional[str]
- x_access_key: Annotated[str, PropertyInfo(alias="x-access-key")]
-
x_client_library_version: Annotated[str, PropertyInfo(alias="x-client-library-version")]
x_integration_type: Annotated[str, PropertyInfo(alias="x-integration-type")]
diff --git a/src/codex/types/projects/entry_query_params.py b/src/codex/types/projects/entry_query_params.py
index db1aec77..d58b7bfa 100644
--- a/src/codex/types/projects/entry_query_params.py
+++ b/src/codex/types/projects/entry_query_params.py
@@ -17,8 +17,6 @@ class EntryQueryParams(TypedDict, total=False):
client_metadata: Optional[object]
- x_access_key: Annotated[str, PropertyInfo(alias="x-access-key")]
-
x_client_library_version: Annotated[str, PropertyInfo(alias="x-client-library-version")]
x_integration_type: Annotated[str, PropertyInfo(alias="x-integration-type")]
diff --git a/tests/api_resources/projects/test_access_keys.py b/tests/api_resources/projects/test_access_keys.py
index bcf3bb7a..ad4ee5e4 100644
--- a/tests/api_resources/projects/test_access_keys.py
+++ b/tests/api_resources/projects/test_access_keys.py
@@ -39,7 +39,6 @@ def test_method_create_with_all_params(self, client: Codex) -> None:
name="name",
description="description",
expires_at=parse_datetime("2019-12-27T18:11:19.117Z"),
- x_access_key="x-access-key",
x_client_library_version="x-client-library-version",
x_integration_type="x-integration-type",
x_source="x-source",
@@ -400,7 +399,6 @@ async def test_method_create_with_all_params(self, async_client: AsyncCodex) ->
name="name",
description="description",
expires_at=parse_datetime("2019-12-27T18:11:19.117Z"),
- x_access_key="x-access-key",
x_client_library_version="x-client-library-version",
x_integration_type="x-integration-type",
x_source="x-source",
diff --git a/tests/api_resources/projects/test_entries.py b/tests/api_resources/projects/test_entries.py
index 710d2146..31a5e408 100644
--- a/tests/api_resources/projects/test_entries.py
+++ b/tests/api_resources/projects/test_entries.py
@@ -39,7 +39,6 @@ def test_method_create_with_all_params(self, client: Codex) -> None:
answer="answer",
client_query_metadata=[{}],
draft_answer="draft_answer",
- x_access_key="x-access-key",
x_client_library_version="x-client-library-version",
x_integration_type="x-integration-type",
x_source="x-source",
@@ -397,7 +396,6 @@ def test_method_query_with_all_params(self, client: Codex) -> None:
question="question",
use_llm_matching=True,
client_metadata={},
- x_access_key="x-access-key",
x_client_library_version="x-client-library-version",
x_integration_type="x-integration-type",
x_source="x-source",
@@ -516,7 +514,6 @@ async def test_method_create_with_all_params(self, async_client: AsyncCodex) ->
answer="answer",
client_query_metadata=[{}],
draft_answer="draft_answer",
- x_access_key="x-access-key",
x_client_library_version="x-client-library-version",
x_integration_type="x-integration-type",
x_source="x-source",
@@ -874,7 +871,6 @@ async def test_method_query_with_all_params(self, async_client: AsyncCodex) -> N
question="question",
use_llm_matching=True,
client_metadata={},
- x_access_key="x-access-key",
x_client_library_version="x-client-library-version",
x_integration_type="x-integration-type",
x_source="x-source",
diff --git a/tests/api_resources/test_projects.py b/tests/api_resources/test_projects.py
index d71d0e19..bfa657d2 100644
--- a/tests/api_resources/test_projects.py
+++ b/tests/api_resources/test_projects.py
@@ -194,18 +194,20 @@ def test_path_params_update(self, client: Codex) -> None:
@pytest.mark.skip()
@parametrize
def test_method_list(self, client: Codex) -> None:
- project = client.projects.list()
+ project = client.projects.list(
+ organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
assert_matches_type(ProjectListResponse, project, path=["response"])
@pytest.mark.skip()
@parametrize
def test_method_list_with_all_params(self, client: Codex) -> None:
project = client.projects.list(
+ organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
include_entry_counts=True,
limit=0,
offset=0,
order="asc",
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
query="query",
sort="created_at",
)
@@ -214,7 +216,9 @@ def test_method_list_with_all_params(self, client: Codex) -> None:
@pytest.mark.skip()
@parametrize
def test_raw_response_list(self, client: Codex) -> None:
- response = client.projects.with_raw_response.list()
+ response = client.projects.with_raw_response.list(
+ organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -224,7 +228,9 @@ def test_raw_response_list(self, client: Codex) -> None:
@pytest.mark.skip()
@parametrize
def test_streaming_response_list(self, client: Codex) -> None:
- with client.projects.with_streaming_response.list() as response:
+ with client.projects.with_streaming_response.list(
+ organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -536,18 +542,20 @@ async def test_path_params_update(self, async_client: AsyncCodex) -> None:
@pytest.mark.skip()
@parametrize
async def test_method_list(self, async_client: AsyncCodex) -> None:
- project = await async_client.projects.list()
+ project = await async_client.projects.list(
+ organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
assert_matches_type(ProjectListResponse, project, path=["response"])
@pytest.mark.skip()
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncCodex) -> None:
project = await async_client.projects.list(
+ organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
include_entry_counts=True,
limit=0,
offset=0,
order="asc",
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
query="query",
sort="created_at",
)
@@ -556,7 +564,9 @@ async def test_method_list_with_all_params(self, async_client: AsyncCodex) -> No
@pytest.mark.skip()
@parametrize
async def test_raw_response_list(self, async_client: AsyncCodex) -> None:
- response = await async_client.projects.with_raw_response.list()
+ response = await async_client.projects.with_raw_response.list(
+ organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -566,7 +576,9 @@ async def test_raw_response_list(self, async_client: AsyncCodex) -> None:
@pytest.mark.skip()
@parametrize
async def test_streaming_response_list(self, async_client: AsyncCodex) -> None:
- async with async_client.projects.with_streaming_response.list() as response:
+ async with async_client.projects.with_streaming_response.list(
+ organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
From ed0b337167c713bebd54c21fe587ee05c57a90d4 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 22 Apr 2025 23:16:17 +0000
Subject: [PATCH 09/17] feat(api): api update
---
.stats.yml | 2 +-
src/codex/resources/projects/projects.py | 8 +++----
src/codex/types/project_list_params.py | 6 ++---
tests/api_resources/test_projects.py | 28 +++++++-----------------
4 files changed, 16 insertions(+), 28 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index aa1535bd..42e47500 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,3 +1,3 @@
configured_endpoints: 42
-openapi_spec_hash: 71ff1de391293cdfb6dcb761ed89210d
+openapi_spec_hash: 684572da9b97ec2c9acf3ea698c7ce12
config_hash: 2d88a0a41f5faca603ff2789a116d988
diff --git a/src/codex/resources/projects/projects.py b/src/codex/resources/projects/projects.py
index 47639a5a..76237fb6 100644
--- a/src/codex/resources/projects/projects.py
+++ b/src/codex/resources/projects/projects.py
@@ -208,11 +208,11 @@ def update(
def list(
self,
*,
- organization_id: str,
include_entry_counts: bool | NotGiven = NOT_GIVEN,
limit: int | NotGiven = NOT_GIVEN,
offset: int | NotGiven = NOT_GIVEN,
order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN,
+ organization_id: str | NotGiven = NOT_GIVEN,
query: Optional[str] | NotGiven = NOT_GIVEN,
sort: Literal["created_at", "updated_at"] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -243,11 +243,11 @@ def list(
timeout=timeout,
query=maybe_transform(
{
- "organization_id": organization_id,
"include_entry_counts": include_entry_counts,
"limit": limit,
"offset": offset,
"order": order,
+ "organization_id": organization_id,
"query": query,
"sort": sort,
},
@@ -513,11 +513,11 @@ async def update(
async def list(
self,
*,
- organization_id: str,
include_entry_counts: bool | NotGiven = NOT_GIVEN,
limit: int | NotGiven = NOT_GIVEN,
offset: int | NotGiven = NOT_GIVEN,
order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN,
+ organization_id: str | NotGiven = NOT_GIVEN,
query: Optional[str] | NotGiven = NOT_GIVEN,
sort: Literal["created_at", "updated_at"] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -548,11 +548,11 @@ async def list(
timeout=timeout,
query=await async_maybe_transform(
{
- "organization_id": organization_id,
"include_entry_counts": include_entry_counts,
"limit": limit,
"offset": offset,
"order": order,
+ "organization_id": organization_id,
"query": query,
"sort": sort,
},
diff --git a/src/codex/types/project_list_params.py b/src/codex/types/project_list_params.py
index c2589598..0ab3b84b 100644
--- a/src/codex/types/project_list_params.py
+++ b/src/codex/types/project_list_params.py
@@ -3,14 +3,12 @@
from __future__ import annotations
from typing import Optional
-from typing_extensions import Literal, Required, TypedDict
+from typing_extensions import Literal, TypedDict
__all__ = ["ProjectListParams"]
class ProjectListParams(TypedDict, total=False):
- organization_id: Required[str]
-
include_entry_counts: bool
limit: int
@@ -19,6 +17,8 @@ class ProjectListParams(TypedDict, total=False):
order: Literal["asc", "desc"]
+ organization_id: str
+
query: Optional[str]
sort: Literal["created_at", "updated_at"]
diff --git a/tests/api_resources/test_projects.py b/tests/api_resources/test_projects.py
index bfa657d2..d71d0e19 100644
--- a/tests/api_resources/test_projects.py
+++ b/tests/api_resources/test_projects.py
@@ -194,20 +194,18 @@ def test_path_params_update(self, client: Codex) -> None:
@pytest.mark.skip()
@parametrize
def test_method_list(self, client: Codex) -> None:
- project = client.projects.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
+ project = client.projects.list()
assert_matches_type(ProjectListResponse, project, path=["response"])
@pytest.mark.skip()
@parametrize
def test_method_list_with_all_params(self, client: Codex) -> None:
project = client.projects.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
include_entry_counts=True,
limit=0,
offset=0,
order="asc",
+ organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
query="query",
sort="created_at",
)
@@ -216,9 +214,7 @@ def test_method_list_with_all_params(self, client: Codex) -> None:
@pytest.mark.skip()
@parametrize
def test_raw_response_list(self, client: Codex) -> None:
- response = client.projects.with_raw_response.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
+ response = client.projects.with_raw_response.list()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -228,9 +224,7 @@ def test_raw_response_list(self, client: Codex) -> None:
@pytest.mark.skip()
@parametrize
def test_streaming_response_list(self, client: Codex) -> None:
- with client.projects.with_streaming_response.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
+ with client.projects.with_streaming_response.list() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -542,20 +536,18 @@ async def test_path_params_update(self, async_client: AsyncCodex) -> None:
@pytest.mark.skip()
@parametrize
async def test_method_list(self, async_client: AsyncCodex) -> None:
- project = await async_client.projects.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
+ project = await async_client.projects.list()
assert_matches_type(ProjectListResponse, project, path=["response"])
@pytest.mark.skip()
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncCodex) -> None:
project = await async_client.projects.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
include_entry_counts=True,
limit=0,
offset=0,
order="asc",
+ organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
query="query",
sort="created_at",
)
@@ -564,9 +556,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncCodex) -> No
@pytest.mark.skip()
@parametrize
async def test_raw_response_list(self, async_client: AsyncCodex) -> None:
- response = await async_client.projects.with_raw_response.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
+ response = await async_client.projects.with_raw_response.list()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -576,9 +566,7 @@ async def test_raw_response_list(self, async_client: AsyncCodex) -> None:
@pytest.mark.skip()
@parametrize
async def test_streaming_response_list(self, async_client: AsyncCodex) -> None:
- async with async_client.projects.with_streaming_response.list(
- organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
+ async with async_client.projects.with_streaming_response.list() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
From bcc2ed013f1e88ac4f97bdb0dc7e47364276e182 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 23 Apr 2025 02:25:10 +0000
Subject: [PATCH 10/17] chore(ci): add timeout thresholds for CI jobs
---
.github/workflows/ci.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e8b72361..1e4dab9d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,6 +10,7 @@ on:
jobs:
lint:
+ timeout-minutes: 10
name: lint
runs-on: ubuntu-latest
steps:
From bcf89d29f760b4eb2e9b7534034adfc28582ea03 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 23 Apr 2025 02:25:42 +0000
Subject: [PATCH 11/17] chore(internal): import reformatting
---
src/codex/_client.py | 5 +----
src/codex/resources/projects/access_keys.py | 6 +-----
src/codex/resources/projects/entries.py | 6 +-----
src/codex/resources/projects/projects.py | 5 +----
src/codex/resources/tlm.py | 5 +----
src/codex/resources/users/users.py | 5 +----
6 files changed, 6 insertions(+), 26 deletions(-)
diff --git a/src/codex/_client.py b/src/codex/_client.py
index 2641513f..7464f6dc 100644
--- a/src/codex/_client.py
+++ b/src/codex/_client.py
@@ -20,10 +20,7 @@
ProxiesTypes,
RequestOptions,
)
-from ._utils import (
- is_given,
- get_async_library,
-)
+from ._utils import is_given, get_async_library
from ._version import __version__
from .resources import tlm, health
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
diff --git a/src/codex/resources/projects/access_keys.py b/src/codex/resources/projects/access_keys.py
index 61987399..15190030 100644
--- a/src/codex/resources/projects/access_keys.py
+++ b/src/codex/resources/projects/access_keys.py
@@ -8,11 +8,7 @@
import httpx
from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven
-from ..._utils import (
- maybe_transform,
- strip_not_given,
- async_maybe_transform,
-)
+from ..._utils import maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
diff --git a/src/codex/resources/projects/entries.py b/src/codex/resources/projects/entries.py
index f88e2243..a9e690b9 100644
--- a/src/codex/resources/projects/entries.py
+++ b/src/codex/resources/projects/entries.py
@@ -7,11 +7,7 @@
import httpx
from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven
-from ..._utils import (
- maybe_transform,
- strip_not_given,
- async_maybe_transform,
-)
+from ..._utils import maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
diff --git a/src/codex/resources/projects/projects.py b/src/codex/resources/projects/projects.py
index 76237fb6..95bcd3a3 100644
--- a/src/codex/resources/projects/projects.py
+++ b/src/codex/resources/projects/projects.py
@@ -17,10 +17,7 @@
AsyncEntriesResourceWithStreamingResponse,
)
from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven
-from ..._utils import (
- maybe_transform,
- async_maybe_transform,
-)
+from ..._utils import maybe_transform, async_maybe_transform
from .clusters import (
ClustersResource,
AsyncClustersResource,
diff --git a/src/codex/resources/tlm.py b/src/codex/resources/tlm.py
index c6585d0d..78f97e2e 100644
--- a/src/codex/resources/tlm.py
+++ b/src/codex/resources/tlm.py
@@ -9,10 +9,7 @@
from ..types import tlm_score_params, tlm_prompt_params
from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from .._utils import (
- maybe_transform,
- async_maybe_transform,
-)
+from .._utils import maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import (
diff --git a/src/codex/resources/users/users.py b/src/codex/resources/users/users.py
index c276f950..d207a96d 100644
--- a/src/codex/resources/users/users.py
+++ b/src/codex/resources/users/users.py
@@ -9,10 +9,7 @@
from ...types import user_activate_account_params
from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from ..._utils import (
- maybe_transform,
- async_maybe_transform,
-)
+from ..._utils import maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
From 2e9cf89c75d12ed44ace20998aa7245a8b241922 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 23 Apr 2025 02:27:01 +0000
Subject: [PATCH 12/17] chore(internal): fix list file params
---
src/codex/_utils/_utils.py | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/codex/_utils/_utils.py b/src/codex/_utils/_utils.py
index e5811bba..ea3cf3f2 100644
--- a/src/codex/_utils/_utils.py
+++ b/src/codex/_utils/_utils.py
@@ -72,8 +72,16 @@ def _extract_items(
from .._files import assert_is_file_content
# We have exhausted the path, return the entry we found.
- assert_is_file_content(obj, key=flattened_key)
assert flattened_key is not None
+
+ if is_list(obj):
+ files: list[tuple[str, FileTypes]] = []
+ for entry in obj:
+ assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "")
+ files.append((flattened_key + "[]", cast(FileTypes, entry)))
+ return files
+
+ assert_is_file_content(obj, key=flattened_key)
return [(flattened_key, cast(FileTypes, obj))]
index += 1
From 2f3a24a40294e21f2e3d9e9464796750ff25b51e Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 23 Apr 2025 02:27:27 +0000
Subject: [PATCH 13/17] chore(internal): refactor retries to not use recursion
---
src/codex/_base_client.py | 414 ++++++++++++++++----------------------
1 file changed, 175 insertions(+), 239 deletions(-)
diff --git a/src/codex/_base_client.py b/src/codex/_base_client.py
index 69be76db..0bf6ca7f 100644
--- a/src/codex/_base_client.py
+++ b/src/codex/_base_client.py
@@ -437,8 +437,7 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0
headers = httpx.Headers(headers_dict)
idempotency_header = self._idempotency_header
- if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers:
- options.idempotency_key = options.idempotency_key or self._idempotency_key()
+ if idempotency_header and options.idempotency_key and idempotency_header not in headers:
headers[idempotency_header] = options.idempotency_key
# Don't set these headers if they were already set or removed by the caller. We check
@@ -903,7 +902,6 @@ def request(
self,
cast_to: Type[ResponseT],
options: FinalRequestOptions,
- remaining_retries: Optional[int] = None,
*,
stream: Literal[True],
stream_cls: Type[_StreamT],
@@ -914,7 +912,6 @@ def request(
self,
cast_to: Type[ResponseT],
options: FinalRequestOptions,
- remaining_retries: Optional[int] = None,
*,
stream: Literal[False] = False,
) -> ResponseT: ...
@@ -924,7 +921,6 @@ def request(
self,
cast_to: Type[ResponseT],
options: FinalRequestOptions,
- remaining_retries: Optional[int] = None,
*,
stream: bool = False,
stream_cls: Type[_StreamT] | None = None,
@@ -934,125 +930,109 @@ def request(
self,
cast_to: Type[ResponseT],
options: FinalRequestOptions,
- remaining_retries: Optional[int] = None,
*,
stream: bool = False,
stream_cls: type[_StreamT] | None = None,
) -> ResponseT | _StreamT:
- if remaining_retries is not None:
- retries_taken = options.get_max_retries(self.max_retries) - remaining_retries
- else:
- retries_taken = 0
-
- return self._request(
- cast_to=cast_to,
- options=options,
- stream=stream,
- stream_cls=stream_cls,
- retries_taken=retries_taken,
- )
+ cast_to = self._maybe_override_cast_to(cast_to, options)
- def _request(
- self,
- *,
- cast_to: Type[ResponseT],
- options: FinalRequestOptions,
- retries_taken: int,
- stream: bool,
- stream_cls: type[_StreamT] | None,
- ) -> ResponseT | _StreamT:
# create a copy of the options we were given so that if the
# options are mutated later & we then retry, the retries are
# given the original options
input_options = model_copy(options)
-
- cast_to = self._maybe_override_cast_to(cast_to, options)
- options = self._prepare_options(options)
-
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
- request = self._build_request(options, retries_taken=retries_taken)
- self._prepare_request(request)
-
- if options.idempotency_key:
+ if input_options.idempotency_key is None and input_options.method.lower() != "get":
# ensure the idempotency key is reused between requests
- input_options.idempotency_key = options.idempotency_key
+ input_options.idempotency_key = self._idempotency_key()
- kwargs: HttpxSendArgs = {}
- if self.custom_auth is not None:
- kwargs["auth"] = self.custom_auth
+ response: httpx.Response | None = None
+ max_retries = input_options.get_max_retries(self.max_retries)
- log.debug("Sending HTTP Request: %s %s", request.method, request.url)
+ retries_taken = 0
+ for retries_taken in range(max_retries + 1):
+ options = model_copy(input_options)
+ options = self._prepare_options(options)
- try:
- response = self._client.send(
- request,
- stream=stream or self._should_stream_response_body(request=request),
- **kwargs,
- )
- except httpx.TimeoutException as err:
- log.debug("Encountered httpx.TimeoutException", exc_info=True)
+ remaining_retries = max_retries - retries_taken
+ request = self._build_request(options, retries_taken=retries_taken)
+ self._prepare_request(request)
- if remaining_retries > 0:
- return self._retry_request(
- input_options,
- cast_to,
- retries_taken=retries_taken,
- stream=stream,
- stream_cls=stream_cls,
- response_headers=None,
- )
+ kwargs: HttpxSendArgs = {}
+ if self.custom_auth is not None:
+ kwargs["auth"] = self.custom_auth
- log.debug("Raising timeout error")
- raise APITimeoutError(request=request) from err
- except Exception as err:
- log.debug("Encountered Exception", exc_info=True)
+ log.debug("Sending HTTP Request: %s %s", request.method, request.url)
- if remaining_retries > 0:
- return self._retry_request(
- input_options,
- cast_to,
- retries_taken=retries_taken,
- stream=stream,
- stream_cls=stream_cls,
- response_headers=None,
+ response = None
+ try:
+ response = self._client.send(
+ request,
+ stream=stream or self._should_stream_response_body(request=request),
+ **kwargs,
)
+ except httpx.TimeoutException as err:
+ log.debug("Encountered httpx.TimeoutException", exc_info=True)
+
+ if remaining_retries > 0:
+ self._sleep_for_retry(
+ retries_taken=retries_taken,
+ max_retries=max_retries,
+ options=input_options,
+ response=None,
+ )
+ continue
+
+ log.debug("Raising timeout error")
+ raise APITimeoutError(request=request) from err
+ except Exception as err:
+ log.debug("Encountered Exception", exc_info=True)
+
+ if remaining_retries > 0:
+ self._sleep_for_retry(
+ retries_taken=retries_taken,
+ max_retries=max_retries,
+ options=input_options,
+ response=None,
+ )
+ continue
+
+ log.debug("Raising connection error")
+ raise APIConnectionError(request=request) from err
+
+ log.debug(
+ 'HTTP Response: %s %s "%i %s" %s',
+ request.method,
+ request.url,
+ response.status_code,
+ response.reason_phrase,
+ response.headers,
+ )
- log.debug("Raising connection error")
- raise APIConnectionError(request=request) from err
-
- log.debug(
- 'HTTP Response: %s %s "%i %s" %s',
- request.method,
- request.url,
- response.status_code,
- response.reason_phrase,
- response.headers,
- )
+ try:
+ response.raise_for_status()
+ except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
+ log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
+
+ if remaining_retries > 0 and self._should_retry(err.response):
+ err.response.close()
+ self._sleep_for_retry(
+ retries_taken=retries_taken,
+ max_retries=max_retries,
+ options=input_options,
+ response=response,
+ )
+ continue
- try:
- response.raise_for_status()
- except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
- log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
-
- if remaining_retries > 0 and self._should_retry(err.response):
- err.response.close()
- return self._retry_request(
- input_options,
- cast_to,
- retries_taken=retries_taken,
- response_headers=err.response.headers,
- stream=stream,
- stream_cls=stream_cls,
- )
+ # If the response is streamed then we need to explicitly read the response
+ # to completion before attempting to access the response text.
+ if not err.response.is_closed:
+ err.response.read()
- # If the response is streamed then we need to explicitly read the response
- # to completion before attempting to access the response text.
- if not err.response.is_closed:
- err.response.read()
+ log.debug("Re-raising status error")
+ raise self._make_status_error_from_response(err.response) from None
- log.debug("Re-raising status error")
- raise self._make_status_error_from_response(err.response) from None
+ break
+ assert response is not None, "could not resolve response (should never happen)"
return self._process_response(
cast_to=cast_to,
options=options,
@@ -1062,37 +1042,20 @@ def _request(
retries_taken=retries_taken,
)
- def _retry_request(
- self,
- options: FinalRequestOptions,
- cast_to: Type[ResponseT],
- *,
- retries_taken: int,
- response_headers: httpx.Headers | None,
- stream: bool,
- stream_cls: type[_StreamT] | None,
- ) -> ResponseT | _StreamT:
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
+ def _sleep_for_retry(
+ self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None
+ ) -> None:
+ remaining_retries = max_retries - retries_taken
if remaining_retries == 1:
log.debug("1 retry left")
else:
log.debug("%i retries left", remaining_retries)
- timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers)
+ timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
log.info("Retrying request to %s in %f seconds", options.url, timeout)
- # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a
- # different thread if necessary.
time.sleep(timeout)
- return self._request(
- options=options,
- cast_to=cast_to,
- retries_taken=retries_taken + 1,
- stream=stream,
- stream_cls=stream_cls,
- )
-
def _process_response(
self,
*,
@@ -1436,7 +1399,6 @@ async def request(
options: FinalRequestOptions,
*,
stream: Literal[False] = False,
- remaining_retries: Optional[int] = None,
) -> ResponseT: ...
@overload
@@ -1447,7 +1409,6 @@ async def request(
*,
stream: Literal[True],
stream_cls: type[_AsyncStreamT],
- remaining_retries: Optional[int] = None,
) -> _AsyncStreamT: ...
@overload
@@ -1458,7 +1419,6 @@ async def request(
*,
stream: bool,
stream_cls: type[_AsyncStreamT] | None = None,
- remaining_retries: Optional[int] = None,
) -> ResponseT | _AsyncStreamT: ...
async def request(
@@ -1468,120 +1428,111 @@ async def request(
*,
stream: bool = False,
stream_cls: type[_AsyncStreamT] | None = None,
- remaining_retries: Optional[int] = None,
- ) -> ResponseT | _AsyncStreamT:
- if remaining_retries is not None:
- retries_taken = options.get_max_retries(self.max_retries) - remaining_retries
- else:
- retries_taken = 0
-
- return await self._request(
- cast_to=cast_to,
- options=options,
- stream=stream,
- stream_cls=stream_cls,
- retries_taken=retries_taken,
- )
-
- async def _request(
- self,
- cast_to: Type[ResponseT],
- options: FinalRequestOptions,
- *,
- stream: bool,
- stream_cls: type[_AsyncStreamT] | None,
- retries_taken: int,
) -> ResponseT | _AsyncStreamT:
if self._platform is None:
# `get_platform` can make blocking IO calls so we
# execute it earlier while we are in an async context
self._platform = await asyncify(get_platform)()
+ cast_to = self._maybe_override_cast_to(cast_to, options)
+
# create a copy of the options we were given so that if the
# options are mutated later & we then retry, the retries are
# given the original options
input_options = model_copy(options)
-
- cast_to = self._maybe_override_cast_to(cast_to, options)
- options = await self._prepare_options(options)
-
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
- request = self._build_request(options, retries_taken=retries_taken)
- await self._prepare_request(request)
-
- if options.idempotency_key:
+ if input_options.idempotency_key is None and input_options.method.lower() != "get":
# ensure the idempotency key is reused between requests
- input_options.idempotency_key = options.idempotency_key
+ input_options.idempotency_key = self._idempotency_key()
- kwargs: HttpxSendArgs = {}
- if self.custom_auth is not None:
- kwargs["auth"] = self.custom_auth
+ response: httpx.Response | None = None
+ max_retries = input_options.get_max_retries(self.max_retries)
- try:
- response = await self._client.send(
- request,
- stream=stream or self._should_stream_response_body(request=request),
- **kwargs,
- )
- except httpx.TimeoutException as err:
- log.debug("Encountered httpx.TimeoutException", exc_info=True)
+ retries_taken = 0
+ for retries_taken in range(max_retries + 1):
+ options = model_copy(input_options)
+ options = await self._prepare_options(options)
- if remaining_retries > 0:
- return await self._retry_request(
- input_options,
- cast_to,
- retries_taken=retries_taken,
- stream=stream,
- stream_cls=stream_cls,
- response_headers=None,
- )
+ remaining_retries = max_retries - retries_taken
+ request = self._build_request(options, retries_taken=retries_taken)
+ await self._prepare_request(request)
- log.debug("Raising timeout error")
- raise APITimeoutError(request=request) from err
- except Exception as err:
- log.debug("Encountered Exception", exc_info=True)
+ kwargs: HttpxSendArgs = {}
+ if self.custom_auth is not None:
+ kwargs["auth"] = self.custom_auth
- if remaining_retries > 0:
- return await self._retry_request(
- input_options,
- cast_to,
- retries_taken=retries_taken,
- stream=stream,
- stream_cls=stream_cls,
- response_headers=None,
- )
+ log.debug("Sending HTTP Request: %s %s", request.method, request.url)
- log.debug("Raising connection error")
- raise APIConnectionError(request=request) from err
+ response = None
+ try:
+ response = await self._client.send(
+ request,
+ stream=stream or self._should_stream_response_body(request=request),
+ **kwargs,
+ )
+ except httpx.TimeoutException as err:
+ log.debug("Encountered httpx.TimeoutException", exc_info=True)
+
+ if remaining_retries > 0:
+ await self._sleep_for_retry(
+ retries_taken=retries_taken,
+ max_retries=max_retries,
+ options=input_options,
+ response=None,
+ )
+ continue
+
+ log.debug("Raising timeout error")
+ raise APITimeoutError(request=request) from err
+ except Exception as err:
+ log.debug("Encountered Exception", exc_info=True)
+
+ if remaining_retries > 0:
+ await self._sleep_for_retry(
+ retries_taken=retries_taken,
+ max_retries=max_retries,
+ options=input_options,
+ response=None,
+ )
+ continue
+
+ log.debug("Raising connection error")
+ raise APIConnectionError(request=request) from err
+
+ log.debug(
+ 'HTTP Response: %s %s "%i %s" %s',
+ request.method,
+ request.url,
+ response.status_code,
+ response.reason_phrase,
+ response.headers,
+ )
- log.debug(
- 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase
- )
+ try:
+ response.raise_for_status()
+ except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
+ log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
+
+ if remaining_retries > 0 and self._should_retry(err.response):
+ await err.response.aclose()
+ await self._sleep_for_retry(
+ retries_taken=retries_taken,
+ max_retries=max_retries,
+ options=input_options,
+ response=response,
+ )
+ continue
- try:
- response.raise_for_status()
- except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
- log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
-
- if remaining_retries > 0 and self._should_retry(err.response):
- await err.response.aclose()
- return await self._retry_request(
- input_options,
- cast_to,
- retries_taken=retries_taken,
- response_headers=err.response.headers,
- stream=stream,
- stream_cls=stream_cls,
- )
+ # If the response is streamed then we need to explicitly read the response
+ # to completion before attempting to access the response text.
+ if not err.response.is_closed:
+ await err.response.aread()
- # If the response is streamed then we need to explicitly read the response
- # to completion before attempting to access the response text.
- if not err.response.is_closed:
- await err.response.aread()
+ log.debug("Re-raising status error")
+ raise self._make_status_error_from_response(err.response) from None
- log.debug("Re-raising status error")
- raise self._make_status_error_from_response(err.response) from None
+ break
+ assert response is not None, "could not resolve response (should never happen)"
return await self._process_response(
cast_to=cast_to,
options=options,
@@ -1591,35 +1542,20 @@ async def _request(
retries_taken=retries_taken,
)
- async def _retry_request(
- self,
- options: FinalRequestOptions,
- cast_to: Type[ResponseT],
- *,
- retries_taken: int,
- response_headers: httpx.Headers | None,
- stream: bool,
- stream_cls: type[_AsyncStreamT] | None,
- ) -> ResponseT | _AsyncStreamT:
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
+ async def _sleep_for_retry(
+ self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None
+ ) -> None:
+ remaining_retries = max_retries - retries_taken
if remaining_retries == 1:
log.debug("1 retry left")
else:
log.debug("%i retries left", remaining_retries)
- timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers)
+ timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
log.info("Retrying request to %s in %f seconds", options.url, timeout)
await anyio.sleep(timeout)
- return await self._request(
- options=options,
- cast_to=cast_to,
- retries_taken=retries_taken + 1,
- stream=stream,
- stream_cls=stream_cls,
- )
-
async def _process_response(
self,
*,
From 2db34b6bcbd0b62c6474146d50c259244f13f84f Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 23 Apr 2025 02:28:00 +0000
Subject: [PATCH 14/17] fix(pydantic v1): more robust ModelField.annotation
check
---
src/codex/_models.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/codex/_models.py b/src/codex/_models.py
index 58b9263e..798956f1 100644
--- a/src/codex/_models.py
+++ b/src/codex/_models.py
@@ -626,8 +626,8 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
# Note: if one variant defines an alias then they all should
discriminator_alias = field_info.alias
- if field_info.annotation and is_literal_type(field_info.annotation):
- for entry in get_args(field_info.annotation):
+ if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation):
+ for entry in get_args(annotation):
if isinstance(entry, str):
mapping[entry] = variant
From 9dec3acb85c55372a1fa629197bd459562980382 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 23 Apr 2025 14:16:17 +0000
Subject: [PATCH 15/17] codegen metadata
---
.stats.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index 42e47500..185dd58f 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,3 +1,3 @@
configured_endpoints: 42
-openapi_spec_hash: 684572da9b97ec2c9acf3ea698c7ce12
+openapi_spec_hash: 62b629dd5b215c1eebc57e0c6039eea7
config_hash: 2d88a0a41f5faca603ff2789a116d988
From ae79a63eb48beb6c4c72fd99e39c8de37915e028 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 23 Apr 2025 15:53:25 +0000
Subject: [PATCH 16/17] feat(api): add proj analytics endpoint
---
.stats.yml | 4 +-
api.md | 2 +
src/codex/resources/projects/projects.py | 126 +++++++++++++++++-
src/codex/types/__init__.py | 2 +
.../project_retrieve_analytics_params.py | 18 +++
.../project_retrieve_analytics_response.py | 43 ++++++
tests/api_resources/test_projects.py | 107 +++++++++++++++
7 files changed, 299 insertions(+), 3 deletions(-)
create mode 100644 src/codex/types/project_retrieve_analytics_params.py
create mode 100644 src/codex/types/project_retrieve_analytics_response.py
diff --git a/.stats.yml b/.stats.yml
index 185dd58f..230d2377 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,3 +1,3 @@
-configured_endpoints: 42
+configured_endpoints: 43
openapi_spec_hash: 62b629dd5b215c1eebc57e0c6039eea7
-config_hash: 2d88a0a41f5faca603ff2789a116d988
+config_hash: 5e459b33c53ffa6e554087a779bdb790
diff --git a/api.md b/api.md
index 617cdd24..f240632f 100644
--- a/api.md
+++ b/api.md
@@ -141,6 +141,7 @@ from codex.types import (
ProjectListResponse,
ProjectExportResponse,
ProjectIncrementQueriesResponse,
+ ProjectRetrieveAnalyticsResponse,
)
```
@@ -153,6 +154,7 @@ Methods:
- client.projects.delete(project_id) -> None
- client.projects.export(project_id) -> object
- client.projects.increment_queries(project_id) -> object
+- client.projects.retrieve_analytics(project_id, \*\*params) -> ProjectRetrieveAnalyticsResponse
## AccessKeys
diff --git a/src/codex/resources/projects/projects.py b/src/codex/resources/projects/projects.py
index 95bcd3a3..6ac036c0 100644
--- a/src/codex/resources/projects/projects.py
+++ b/src/codex/resources/projects/projects.py
@@ -7,7 +7,12 @@
import httpx
-from ...types import project_list_params, project_create_params, project_update_params
+from ...types import (
+ project_list_params,
+ project_create_params,
+ project_update_params,
+ project_retrieve_analytics_params,
+)
from .entries import (
EntriesResource,
AsyncEntriesResource,
@@ -46,6 +51,7 @@
from ...types.project_list_response import ProjectListResponse
from ...types.project_return_schema import ProjectReturnSchema
from ...types.project_retrieve_response import ProjectRetrieveResponse
+from ...types.project_retrieve_analytics_response import ProjectRetrieveAnalyticsResponse
__all__ = ["ProjectsResource", "AsyncProjectsResource"]
@@ -354,6 +360,59 @@ def increment_queries(
cast_to=object,
)
+ def retrieve_analytics(
+ self,
+ project_id: str,
+ *,
+ end: int | NotGiven = NOT_GIVEN,
+ sme_limit: int | NotGiven = NOT_GIVEN,
+ start: int | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> ProjectRetrieveAnalyticsResponse:
+ """
+ Get Project Analytics Route
+
+ Args:
+ end: End timestamp in seconds since epoch
+
+ sme_limit: Limit the number of top SME contributors to return.
+
+ start: Start timestamp in seconds since epoch
+
+ 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(
+ f"/api/projects/{project_id}/analytics/",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "end": end,
+ "sme_limit": sme_limit,
+ "start": start,
+ },
+ project_retrieve_analytics_params.ProjectRetrieveAnalyticsParams,
+ ),
+ ),
+ cast_to=ProjectRetrieveAnalyticsResponse,
+ )
+
class AsyncProjectsResource(AsyncAPIResource):
@cached_property
@@ -659,6 +718,59 @@ async def increment_queries(
cast_to=object,
)
+ async def retrieve_analytics(
+ self,
+ project_id: str,
+ *,
+ end: int | NotGiven = NOT_GIVEN,
+ sme_limit: int | NotGiven = NOT_GIVEN,
+ start: int | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> ProjectRetrieveAnalyticsResponse:
+ """
+ Get Project Analytics Route
+
+ Args:
+ end: End timestamp in seconds since epoch
+
+ sme_limit: Limit the number of top SME contributors to return.
+
+ start: Start timestamp in seconds since epoch
+
+ 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(
+ f"/api/projects/{project_id}/analytics/",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "end": end,
+ "sme_limit": sme_limit,
+ "start": start,
+ },
+ project_retrieve_analytics_params.ProjectRetrieveAnalyticsParams,
+ ),
+ ),
+ cast_to=ProjectRetrieveAnalyticsResponse,
+ )
+
class ProjectsResourceWithRawResponse:
def __init__(self, projects: ProjectsResource) -> None:
@@ -685,6 +797,9 @@ def __init__(self, projects: ProjectsResource) -> None:
self.increment_queries = to_raw_response_wrapper(
projects.increment_queries,
)
+ self.retrieve_analytics = to_raw_response_wrapper(
+ projects.retrieve_analytics,
+ )
@cached_property
def access_keys(self) -> AccessKeysResourceWithRawResponse:
@@ -724,6 +839,9 @@ def __init__(self, projects: AsyncProjectsResource) -> None:
self.increment_queries = async_to_raw_response_wrapper(
projects.increment_queries,
)
+ self.retrieve_analytics = async_to_raw_response_wrapper(
+ projects.retrieve_analytics,
+ )
@cached_property
def access_keys(self) -> AsyncAccessKeysResourceWithRawResponse:
@@ -763,6 +881,9 @@ def __init__(self, projects: ProjectsResource) -> None:
self.increment_queries = to_streamed_response_wrapper(
projects.increment_queries,
)
+ self.retrieve_analytics = to_streamed_response_wrapper(
+ projects.retrieve_analytics,
+ )
@cached_property
def access_keys(self) -> AccessKeysResourceWithStreamingResponse:
@@ -802,6 +923,9 @@ def __init__(self, projects: AsyncProjectsResource) -> None:
self.increment_queries = async_to_streamed_response_wrapper(
projects.increment_queries,
)
+ self.retrieve_analytics = async_to_streamed_response_wrapper(
+ projects.retrieve_analytics,
+ )
@cached_property
def access_keys(self) -> AsyncAccessKeysResourceWithStreamingResponse:
diff --git a/src/codex/types/__init__.py b/src/codex/types/__init__.py
index 008c4a67..53d1ab6b 100644
--- a/src/codex/types/__init__.py
+++ b/src/codex/types/__init__.py
@@ -15,7 +15,9 @@
from .project_retrieve_response import ProjectRetrieveResponse as ProjectRetrieveResponse
from .organization_schema_public import OrganizationSchemaPublic as OrganizationSchemaPublic
from .user_activate_account_params import UserActivateAccountParams as UserActivateAccountParams
+from .project_retrieve_analytics_params import ProjectRetrieveAnalyticsParams as ProjectRetrieveAnalyticsParams
from .organization_list_members_response import OrganizationListMembersResponse as OrganizationListMembersResponse
+from .project_retrieve_analytics_response import ProjectRetrieveAnalyticsResponse as ProjectRetrieveAnalyticsResponse
from .organization_retrieve_permissions_response import (
OrganizationRetrievePermissionsResponse as OrganizationRetrievePermissionsResponse,
)
diff --git a/src/codex/types/project_retrieve_analytics_params.py b/src/codex/types/project_retrieve_analytics_params.py
new file mode 100644
index 00000000..aadb4159
--- /dev/null
+++ b/src/codex/types/project_retrieve_analytics_params.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["ProjectRetrieveAnalyticsParams"]
+
+
+class ProjectRetrieveAnalyticsParams(TypedDict, total=False):
+ end: int
+ """End timestamp in seconds since epoch"""
+
+ sme_limit: int
+ """Limit the number of top SME contributors to return."""
+
+ start: int
+ """Start timestamp in seconds since epoch"""
diff --git a/src/codex/types/project_retrieve_analytics_response.py b/src/codex/types/project_retrieve_analytics_response.py
new file mode 100644
index 00000000..f17975ec
--- /dev/null
+++ b/src/codex/types/project_retrieve_analytics_response.py
@@ -0,0 +1,43 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List
+
+from .._models import BaseModel
+
+__all__ = [
+ "ProjectRetrieveAnalyticsResponse",
+ "AnswersPublished",
+ "AnswersPublishedAnswersByAuthor",
+ "BadResponses",
+ "Queries",
+]
+
+
+class AnswersPublishedAnswersByAuthor(BaseModel):
+ answers_published: int
+
+ name: str
+
+ user_id: str
+
+
+class AnswersPublished(BaseModel):
+ answers_by_author: List[AnswersPublishedAnswersByAuthor]
+
+
+class BadResponses(BaseModel):
+ responses_by_type: Dict[str, int]
+
+ total: int
+
+
+class Queries(BaseModel):
+ total: int
+
+
+class ProjectRetrieveAnalyticsResponse(BaseModel):
+ answers_published: AnswersPublished
+
+ bad_responses: BadResponses
+
+ queries: Queries
diff --git a/tests/api_resources/test_projects.py b/tests/api_resources/test_projects.py
index d71d0e19..3a8f0ec8 100644
--- a/tests/api_resources/test_projects.py
+++ b/tests/api_resources/test_projects.py
@@ -12,6 +12,7 @@
ProjectListResponse,
ProjectReturnSchema,
ProjectRetrieveResponse,
+ ProjectRetrieveAnalyticsResponse,
)
from tests.utils import assert_matches_type
@@ -359,6 +360,59 @@ def test_path_params_increment_queries(self, client: Codex) -> None:
"",
)
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_retrieve_analytics(self, client: Codex) -> None:
+ project = client.projects.retrieve_analytics(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(ProjectRetrieveAnalyticsResponse, project, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_retrieve_analytics_with_all_params(self, client: Codex) -> None:
+ project = client.projects.retrieve_analytics(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ end=0,
+ sme_limit=1,
+ start=0,
+ )
+ assert_matches_type(ProjectRetrieveAnalyticsResponse, project, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_retrieve_analytics(self, client: Codex) -> None:
+ response = client.projects.with_raw_response.retrieve_analytics(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ project = response.parse()
+ assert_matches_type(ProjectRetrieveAnalyticsResponse, project, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_streaming_response_retrieve_analytics(self, client: Codex) -> None:
+ with client.projects.with_streaming_response.retrieve_analytics(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ project = response.parse()
+ assert_matches_type(ProjectRetrieveAnalyticsResponse, project, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_path_params_retrieve_analytics(self, client: Codex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ client.projects.with_raw_response.retrieve_analytics(
+ project_id="",
+ )
+
class TestAsyncProjects:
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
@@ -700,3 +754,56 @@ async def test_path_params_increment_queries(self, async_client: AsyncCodex) ->
await async_client.projects.with_raw_response.increment_queries(
"",
)
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_retrieve_analytics(self, async_client: AsyncCodex) -> None:
+ project = await async_client.projects.retrieve_analytics(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(ProjectRetrieveAnalyticsResponse, project, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_retrieve_analytics_with_all_params(self, async_client: AsyncCodex) -> None:
+ project = await async_client.projects.retrieve_analytics(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ end=0,
+ sme_limit=1,
+ start=0,
+ )
+ assert_matches_type(ProjectRetrieveAnalyticsResponse, project, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_retrieve_analytics(self, async_client: AsyncCodex) -> None:
+ response = await async_client.projects.with_raw_response.retrieve_analytics(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ project = await response.parse()
+ assert_matches_type(ProjectRetrieveAnalyticsResponse, project, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_streaming_response_retrieve_analytics(self, async_client: AsyncCodex) -> None:
+ async with async_client.projects.with_streaming_response.retrieve_analytics(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ project = await response.parse()
+ assert_matches_type(ProjectRetrieveAnalyticsResponse, project, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_path_params_retrieve_analytics(self, async_client: AsyncCodex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ await async_client.projects.with_raw_response.retrieve_analytics(
+ project_id="",
+ )
From f6fecb22eb94625d5784c4480428261c9f152ecb Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 23 Apr 2025 15:53:43 +0000
Subject: [PATCH 17/17] release: 0.1.0-alpha.17
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 30 ++++++++++++++++++++++++++++++
pyproject.toml | 2 +-
src/codex/_version.py | 2 +-
4 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 7e56fe29..e2f2c074 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.1.0-alpha.16"
+ ".": "0.1.0-alpha.17"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 658e0649..3e462359 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,35 @@
# Changelog
+## 0.1.0-alpha.17 (2025-04-23)
+
+Full Changelog: [v0.1.0-alpha.16...v0.1.0-alpha.17](https://github.com/cleanlab/codex-python/compare/v0.1.0-alpha.16...v0.1.0-alpha.17)
+
+### Features
+
+* **api:** add proj analytics endpoint ([ae79a63](https://github.com/cleanlab/codex-python/commit/ae79a63eb48beb6c4c72fd99e39c8de37915e028))
+* **api:** add project increment_queries and other recent endpoints ([5bf31f7](https://github.com/cleanlab/codex-python/commit/5bf31f74b6008c9bda7b4ec290b1216a51ef9d1f))
+* **api:** add project increment_queries and other recent endpoints ([e81cc3d](https://github.com/cleanlab/codex-python/commit/e81cc3d451c7c8d163c0b80d983140506a0adb66))
+* **api:** add project increment_queries and other recent endpoints ([b038fbe](https://github.com/cleanlab/codex-python/commit/b038fbe3e90f3096b0913256db9e31ca52cd4001))
+* **api:** api update ([ed0b337](https://github.com/cleanlab/codex-python/commit/ed0b337167c713bebd54c21fe587ee05c57a90d4))
+* **api:** api update ([3f85757](https://github.com/cleanlab/codex-python/commit/3f857575176d200a639880dfa7e60e3a9949750c))
+* **api:** api update ([4f39e46](https://github.com/cleanlab/codex-python/commit/4f39e46167b88a0c7198bdd8ad7d31bf118d485c))
+
+
+### Bug Fixes
+
+* **pydantic v1:** more robust ModelField.annotation check ([2db34b6](https://github.com/cleanlab/codex-python/commit/2db34b6bcbd0b62c6474146d50c259244f13f84f))
+
+
+### Chores
+
+* **ci:** add timeout thresholds for CI jobs ([bcc2ed0](https://github.com/cleanlab/codex-python/commit/bcc2ed013f1e88ac4f97bdb0dc7e47364276e182))
+* **internal:** fix list file params ([2e9cf89](https://github.com/cleanlab/codex-python/commit/2e9cf89c75d12ed44ace20998aa7245a8b241922))
+* **internal:** import reformatting ([bcf89d2](https://github.com/cleanlab/codex-python/commit/bcf89d29f760b4eb2e9b7534034adfc28582ea03))
+* **internal:** refactor retries to not use recursion ([2f3a24a](https://github.com/cleanlab/codex-python/commit/2f3a24a40294e21f2e3d9e9464796750ff25b51e))
+* **internal:** update models test ([444cd03](https://github.com/cleanlab/codex-python/commit/444cd038fc03a545fc2da0a6e5ab14ea84102408))
+* **internal:** version bump ([03cd933](https://github.com/cleanlab/codex-python/commit/03cd93334de3db9beea06ae0f62bcb0ab61466a4))
+* **internal:** version bump ([88821c7](https://github.com/cleanlab/codex-python/commit/88821c710de290b37a6dbc9a81ef0a1cc396db62))
+
## 0.1.0-alpha.16 (2025-04-18)
Full Changelog: [v0.1.0-alpha.15...v0.1.0-alpha.16](https://github.com/cleanlab/codex-python/compare/v0.1.0-alpha.15...v0.1.0-alpha.16)
diff --git a/pyproject.toml b/pyproject.toml
index b98b79a6..130b32e5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "codex-sdk"
-version = "0.1.0-alpha.16"
+version = "0.1.0-alpha.17"
description = "Internal SDK used within cleanlab-codex package. Refer to https://pypi.org/project/cleanlab-codex/ instead."
dynamic = ["readme"]
license = "MIT"
diff --git a/src/codex/_version.py b/src/codex/_version.py
index b99310c3..264d49e0 100644
--- a/src/codex/_version.py
+++ b/src/codex/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "codex"
-__version__ = "0.1.0-alpha.16" # x-release-please-version
+__version__ = "0.1.0-alpha.17" # x-release-please-version