From 5848aac2eb02a789c20951056a7b802985d8b2ab Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 19 Nov 2025 03:17:41 +0000
Subject: [PATCH 1/3] feat(api): api update
---
.stats.yml | 2 +-
.../types/projects/query_log_list_by_group_response.py | 6 ++++++
src/codex/types/projects/query_log_list_groups_response.py | 6 ++++++
src/codex/types/projects/query_log_list_response.py | 6 ++++++
src/codex/types/projects/query_log_retrieve_response.py | 6 ++++++
.../projects/remediation_list_resolved_logs_response.py | 6 ++++++
6 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index 49a4e3c..36d8a0f 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,3 +1,3 @@
configured_endpoints: 65
-openapi_spec_hash: d273ca5158facc1251efa0a5f9e723c5
+openapi_spec_hash: 9018ebfb2a9e1afa87058b3a4bd41b0b
config_hash: cd9208a2204f43e0aa5ab35ac85ef90d
diff --git a/src/codex/types/projects/query_log_list_by_group_response.py b/src/codex/types/projects/query_log_list_by_group_response.py
index ed1a32d..1a89baa 100644
--- a/src/codex/types/projects/query_log_list_by_group_response.py
+++ b/src/codex/types/projects/query_log_list_by_group_response.py
@@ -419,6 +419,9 @@ class QueryLogsByGroupQueryLog(BaseModel):
applied_expert_answer_id: Optional[str] = None
"""ID of the expert answer that was applied to the query."""
+ applied_expert_review_id: Optional[str] = None
+ """ID of the expert review that was applied to the query."""
+
context: Optional[List[QueryLogsByGroupQueryLogContext]] = None
"""RAG context used for the query"""
@@ -479,6 +482,9 @@ class QueryLogsByGroupQueryLog(BaseModel):
expert_review_explanation: Optional[str] = None
"""Expert explanation when marked as bad"""
+ expert_review_id: Optional[str] = None
+ """ID of the expert review that was created for the query."""
+
expert_review_status: Optional[Literal["good", "bad"]] = None
"""Expert review status: 'good' or 'bad'"""
diff --git a/src/codex/types/projects/query_log_list_groups_response.py b/src/codex/types/projects/query_log_list_groups_response.py
index d20eb8c..fe70223 100644
--- a/src/codex/types/projects/query_log_list_groups_response.py
+++ b/src/codex/types/projects/query_log_list_groups_response.py
@@ -414,6 +414,9 @@ class QueryLogListGroupsResponse(BaseModel):
applied_expert_answer_id: Optional[str] = None
"""ID of the expert answer that was applied to the query."""
+ applied_expert_review_id: Optional[str] = None
+ """ID of the expert review that was applied to the query."""
+
context: Optional[List[Context]] = None
"""RAG context used for the query"""
@@ -474,6 +477,9 @@ class QueryLogListGroupsResponse(BaseModel):
expert_review_explanation: Optional[str] = None
"""Expert explanation when marked as bad"""
+ expert_review_id: Optional[str] = None
+ """ID of the expert review that was created for the query."""
+
expert_review_status: Optional[Literal["good", "bad"]] = None
"""Expert review status: 'good' or 'bad'"""
diff --git a/src/codex/types/projects/query_log_list_response.py b/src/codex/types/projects/query_log_list_response.py
index 9d8d198..dc7768f 100644
--- a/src/codex/types/projects/query_log_list_response.py
+++ b/src/codex/types/projects/query_log_list_response.py
@@ -402,6 +402,9 @@ class QueryLogListResponse(BaseModel):
applied_expert_answer_id: Optional[str] = None
"""ID of the expert answer that was applied to the query."""
+ applied_expert_review_id: Optional[str] = None
+ """ID of the expert review that was applied to the query."""
+
context: Optional[List[Context]] = None
"""RAG context used for the query"""
@@ -462,6 +465,9 @@ class QueryLogListResponse(BaseModel):
expert_review_explanation: Optional[str] = None
"""Expert explanation when marked as bad"""
+ expert_review_id: Optional[str] = None
+ """ID of the expert review that was created for the query."""
+
expert_review_status: Optional[Literal["good", "bad"]] = None
"""Expert review status: 'good' or 'bad'"""
diff --git a/src/codex/types/projects/query_log_retrieve_response.py b/src/codex/types/projects/query_log_retrieve_response.py
index 3943325..db91943 100644
--- a/src/codex/types/projects/query_log_retrieve_response.py
+++ b/src/codex/types/projects/query_log_retrieve_response.py
@@ -409,6 +409,9 @@ class QueryLogRetrieveResponse(BaseModel):
applied_expert_answer_id: Optional[str] = None
"""ID of the expert answer that was applied to the query."""
+ applied_expert_review_id: Optional[str] = None
+ """ID of the expert review that was applied to the query."""
+
context: Optional[List[Context]] = None
"""RAG context used for the query"""
@@ -469,6 +472,9 @@ class QueryLogRetrieveResponse(BaseModel):
expert_review_explanation: Optional[str] = None
"""Expert explanation when marked as bad"""
+ expert_review_id: Optional[str] = None
+ """ID of the expert review that was created for the query."""
+
expert_review_status: Optional[Literal["good", "bad"]] = None
"""Expert review status: 'good' or 'bad'"""
diff --git a/src/codex/types/projects/remediation_list_resolved_logs_response.py b/src/codex/types/projects/remediation_list_resolved_logs_response.py
index e586b14..9f1b77b 100644
--- a/src/codex/types/projects/remediation_list_resolved_logs_response.py
+++ b/src/codex/types/projects/remediation_list_resolved_logs_response.py
@@ -409,6 +409,9 @@ class QueryLog(BaseModel):
applied_expert_answer_id: Optional[str] = None
"""ID of the expert answer that was applied to the query."""
+ applied_expert_review_id: Optional[str] = None
+ """ID of the expert review that was applied to the query."""
+
context: Optional[List[QueryLogContext]] = None
"""RAG context used for the query"""
@@ -469,6 +472,9 @@ class QueryLog(BaseModel):
expert_review_explanation: Optional[str] = None
"""Expert explanation when marked as bad"""
+ expert_review_id: Optional[str] = None
+ """ID of the expert review that was created for the query."""
+
expert_review_status: Optional[Literal["good", "bad"]] = None
"""Expert review status: 'good' or 'bad'"""
From cda6f911fe678318779d23de9ecc77fb15594fa1 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 19 Nov 2025 19:07:15 +0000
Subject: [PATCH 2/3] feat(api): add expert review endpoints
---
.stats.yml | 4 +-
api.md | 21 +
src/codex/pagination.py | 62 ++
.../projects/remediations/__init__.py | 14 +
.../projects/remediations/expert_reviews.py | 682 ++++++++++++++++++
.../projects/remediations/remediations.py | 32 +
.../types/projects/remediations/__init__.py | 8 +
.../expert_review_create_params.py | 21 +
.../expert_review_create_response.py | 29 +
.../expert_review_delete_params.py | 14 +
.../remediations/expert_review_edit_params.py | 16 +
.../expert_review_edit_response.py | 29 +
.../remediations/expert_review_list_params.py | 41 ++
.../expert_review_list_response.py | 35 +
.../expert_review_retrieve_response.py | 35 +
.../remediations/test_expert_reviews.py | 631 ++++++++++++++++
16 files changed, 1672 insertions(+), 2 deletions(-)
create mode 100644 src/codex/resources/projects/remediations/expert_reviews.py
create mode 100644 src/codex/types/projects/remediations/expert_review_create_params.py
create mode 100644 src/codex/types/projects/remediations/expert_review_create_response.py
create mode 100644 src/codex/types/projects/remediations/expert_review_delete_params.py
create mode 100644 src/codex/types/projects/remediations/expert_review_edit_params.py
create mode 100644 src/codex/types/projects/remediations/expert_review_edit_response.py
create mode 100644 src/codex/types/projects/remediations/expert_review_list_params.py
create mode 100644 src/codex/types/projects/remediations/expert_review_list_response.py
create mode 100644 src/codex/types/projects/remediations/expert_review_retrieve_response.py
create mode 100644 tests/api_resources/projects/remediations/test_expert_reviews.py
diff --git a/.stats.yml b/.stats.yml
index 36d8a0f..c021c17 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,3 +1,3 @@
-configured_endpoints: 65
+configured_endpoints: 70
openapi_spec_hash: 9018ebfb2a9e1afa87058b3a4bd41b0b
-config_hash: cd9208a2204f43e0aa5ab35ac85ef90d
+config_hash: aad16f20fed13ac50211fc1d0e2ea621
diff --git a/api.md b/api.md
index 6e8c053..4ee7964 100644
--- a/api.md
+++ b/api.md
@@ -284,3 +284,24 @@ Methods:
- client.projects.remediations.expert_answers.pause(expert_answer_id, \*, project_id) -> ExpertAnswerPauseResponse
- client.projects.remediations.expert_answers.publish(expert_answer_id, \*, project_id) -> ExpertAnswerPublishResponse
- client.projects.remediations.expert_answers.unpause(expert_answer_id, \*, project_id) -> ExpertAnswerUnpauseResponse
+
+### ExpertReviews
+
+Types:
+
+```python
+from codex.types.projects.remediations import (
+ ExpertReviewCreateResponse,
+ ExpertReviewRetrieveResponse,
+ ExpertReviewListResponse,
+ ExpertReviewEditResponse,
+)
+```
+
+Methods:
+
+- client.projects.remediations.expert_reviews.create(project_id, \*\*params) -> ExpertReviewCreateResponse
+- client.projects.remediations.expert_reviews.retrieve(expert_review_id, \*, project_id) -> ExpertReviewRetrieveResponse
+- client.projects.remediations.expert_reviews.list(project_id, \*\*params) -> SyncOffsetPageExpertReviews[ExpertReviewListResponse]
+- client.projects.remediations.expert_reviews.delete(expert_review_id, \*, project_id, \*\*params) -> None
+- client.projects.remediations.expert_reviews.edit(expert_review_id, \*, project_id, \*\*params) -> ExpertReviewEditResponse
diff --git a/src/codex/pagination.py b/src/codex/pagination.py
index e5721eb..d44ce47 100644
--- a/src/codex/pagination.py
+++ b/src/codex/pagination.py
@@ -24,6 +24,8 @@
"AsyncOffsetPageQueryLogsByGroup",
"SyncOffsetPageExpertAnswers",
"AsyncOffsetPageExpertAnswers",
+ "SyncOffsetPageExpertReviews",
+ "AsyncOffsetPageExpertReviews",
]
_BaseModelT = TypeVar("_BaseModelT", bound=BaseModel)
@@ -451,3 +453,63 @@ def next_page_info(self) -> Optional[PageInfo]:
return PageInfo(params={"offset": current_count})
return None
+
+
+class SyncOffsetPageExpertReviews(BaseSyncPage[_T], BasePage[_T], Generic[_T]):
+ expert_reviews: List[_T]
+ total_count: Optional[int] = None
+
+ @override
+ def _get_page_items(self) -> List[_T]:
+ expert_reviews = self.expert_reviews
+ if not expert_reviews:
+ return []
+ return expert_reviews
+
+ @override
+ def next_page_info(self) -> Optional[PageInfo]:
+ offset = self._options.params.get("offset") or 0
+ if not isinstance(offset, int):
+ raise ValueError(f'Expected "offset" param to be an integer but got {offset}')
+
+ length = len(self._get_page_items())
+ current_count = offset + length
+
+ total_count = self.total_count
+ if total_count is None:
+ return None
+
+ if current_count < total_count:
+ return PageInfo(params={"offset": current_count})
+
+ return None
+
+
+class AsyncOffsetPageExpertReviews(BaseAsyncPage[_T], BasePage[_T], Generic[_T]):
+ expert_reviews: List[_T]
+ total_count: Optional[int] = None
+
+ @override
+ def _get_page_items(self) -> List[_T]:
+ expert_reviews = self.expert_reviews
+ if not expert_reviews:
+ return []
+ return expert_reviews
+
+ @override
+ def next_page_info(self) -> Optional[PageInfo]:
+ offset = self._options.params.get("offset") or 0
+ if not isinstance(offset, int):
+ raise ValueError(f'Expected "offset" param to be an integer but got {offset}')
+
+ length = len(self._get_page_items())
+ current_count = offset + length
+
+ total_count = self.total_count
+ if total_count is None:
+ return None
+
+ if current_count < total_count:
+ return PageInfo(params={"offset": current_count})
+
+ return None
diff --git a/src/codex/resources/projects/remediations/__init__.py b/src/codex/resources/projects/remediations/__init__.py
index 3a7c4f9..6c71aad 100644
--- a/src/codex/resources/projects/remediations/__init__.py
+++ b/src/codex/resources/projects/remediations/__init__.py
@@ -16,6 +16,14 @@
ExpertAnswersResourceWithStreamingResponse,
AsyncExpertAnswersResourceWithStreamingResponse,
)
+from .expert_reviews import (
+ ExpertReviewsResource,
+ AsyncExpertReviewsResource,
+ ExpertReviewsResourceWithRawResponse,
+ AsyncExpertReviewsResourceWithRawResponse,
+ ExpertReviewsResourceWithStreamingResponse,
+ AsyncExpertReviewsResourceWithStreamingResponse,
+)
__all__ = [
"ExpertAnswersResource",
@@ -24,6 +32,12 @@
"AsyncExpertAnswersResourceWithRawResponse",
"ExpertAnswersResourceWithStreamingResponse",
"AsyncExpertAnswersResourceWithStreamingResponse",
+ "ExpertReviewsResource",
+ "AsyncExpertReviewsResource",
+ "ExpertReviewsResourceWithRawResponse",
+ "AsyncExpertReviewsResourceWithRawResponse",
+ "ExpertReviewsResourceWithStreamingResponse",
+ "AsyncExpertReviewsResourceWithStreamingResponse",
"RemediationsResource",
"AsyncRemediationsResource",
"RemediationsResourceWithRawResponse",
diff --git a/src/codex/resources/projects/remediations/expert_reviews.py b/src/codex/resources/projects/remediations/expert_reviews.py
new file mode 100644
index 0000000..1d0db1b
--- /dev/null
+++ b/src/codex/resources/projects/remediations/expert_reviews.py
@@ -0,0 +1,682 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List, Union, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+import httpx
+
+from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from ...._utils import maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ....pagination import SyncOffsetPageExpertReviews, AsyncOffsetPageExpertReviews
+from ...._base_client import AsyncPaginator, make_request_options
+from ....types.projects.remediations import (
+ expert_review_edit_params,
+ expert_review_list_params,
+ expert_review_create_params,
+ expert_review_delete_params,
+)
+from ....types.projects.remediations.expert_review_edit_response import ExpertReviewEditResponse
+from ....types.projects.remediations.expert_review_list_response import ExpertReviewListResponse
+from ....types.projects.remediations.expert_review_create_response import ExpertReviewCreateResponse
+from ....types.projects.remediations.expert_review_retrieve_response import ExpertReviewRetrieveResponse
+
+__all__ = ["ExpertReviewsResource", "AsyncExpertReviewsResource"]
+
+
+class ExpertReviewsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ExpertReviewsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cleanlab/codex-python#accessing-raw-response-data-eg-headers
+ """
+ return ExpertReviewsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ExpertReviewsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cleanlab/codex-python#with_streaming_response
+ """
+ return ExpertReviewsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ project_id: str,
+ *,
+ original_query_log_id: str,
+ review_status: Literal["good", "bad"],
+ generate_guidance: bool | Omit = omit,
+ explanation: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ExpertReviewCreateResponse:
+ """
+ Create Expert Review Route
+
+ Args:
+ original_query_log_id: ID of the original query log
+
+ review_status: Expert review status - 'good' or 'bad'
+
+ explanation: Optional explanation for expert review
+
+ 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}/expert_reviews/",
+ body=maybe_transform(
+ {
+ "original_query_log_id": original_query_log_id,
+ "review_status": review_status,
+ "explanation": explanation,
+ },
+ expert_review_create_params.ExpertReviewCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {"generate_guidance": generate_guidance}, expert_review_create_params.ExpertReviewCreateParams
+ ),
+ ),
+ cast_to=ExpertReviewCreateResponse,
+ )
+
+ def retrieve(
+ self,
+ expert_review_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,
+ ) -> ExpertReviewRetrieveResponse:
+ """
+ Get Expert Review Route
+
+ 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 expert_review_id:
+ raise ValueError(f"Expected a non-empty value for `expert_review_id` but received {expert_review_id!r}")
+ return self._get(
+ f"/api/projects/{project_id}/expert_reviews/{expert_review_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ExpertReviewRetrieveResponse,
+ )
+
+ def list(
+ self,
+ project_id: str,
+ *,
+ created_at_end: Union[str, datetime, None] | Omit = omit,
+ created_at_start: Union[str, datetime, None] | Omit = omit,
+ last_edited_at_end: Union[str, datetime, None] | Omit = omit,
+ last_edited_at_start: Union[str, datetime, None] | Omit = omit,
+ last_edited_by: Optional[str] | Omit = omit,
+ limit: int | Omit = omit,
+ offset: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ review_status: Optional[List[Literal["good", "bad"]]] | Omit = omit,
+ sort: Optional[Literal["created_at", "last_edited_at", "resolved_logs_count"]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SyncOffsetPageExpertReviews[ExpertReviewListResponse]:
+ """
+ List Expert Reviews Route
+
+ Args:
+ created_at_end: Filter expert reviews created at or before this timestamp
+
+ created_at_start: Filter expert reviews created at or after this timestamp
+
+ last_edited_at_end: Filter expert reviews last edited at or before this timestamp
+
+ last_edited_at_start: Filter expert reviews last edited at or after this timestamp
+
+ last_edited_by: Filter expert reviews last edited by user ID
+
+ order: Sort order
+
+ review_status: Filter expert reviews by review status
+
+ sort: Sort expert reviews by field
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not project_id:
+ raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}")
+ return self._get_api_list(
+ f"/api/projects/{project_id}/expert_reviews/",
+ page=SyncOffsetPageExpertReviews[ExpertReviewListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "created_at_end": created_at_end,
+ "created_at_start": created_at_start,
+ "last_edited_at_end": last_edited_at_end,
+ "last_edited_at_start": last_edited_at_start,
+ "last_edited_by": last_edited_by,
+ "limit": limit,
+ "offset": offset,
+ "order": order,
+ "review_status": review_status,
+ "sort": sort,
+ },
+ expert_review_list_params.ExpertReviewListParams,
+ ),
+ ),
+ model=ExpertReviewListResponse,
+ )
+
+ def delete(
+ self,
+ expert_review_id: str,
+ *,
+ project_id: str,
+ original_query_log_id: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Delete Expert Review Route
+
+ 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 expert_review_id:
+ raise ValueError(f"Expected a non-empty value for `expert_review_id` but received {expert_review_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ f"/api/projects/{project_id}/expert_reviews/{expert_review_id}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {"original_query_log_id": original_query_log_id},
+ expert_review_delete_params.ExpertReviewDeleteParams,
+ ),
+ ),
+ cast_to=NoneType,
+ )
+
+ def edit(
+ self,
+ expert_review_id: str,
+ *,
+ project_id: str,
+ explanation: Optional[str] | Omit = omit,
+ review_status: Optional[Literal["good", "bad"]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ExpertReviewEditResponse:
+ """
+ Update Expert Review Route
+
+ 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 expert_review_id:
+ raise ValueError(f"Expected a non-empty value for `expert_review_id` but received {expert_review_id!r}")
+ return self._patch(
+ f"/api/projects/{project_id}/expert_reviews/{expert_review_id}",
+ body=maybe_transform(
+ {
+ "explanation": explanation,
+ "review_status": review_status,
+ },
+ expert_review_edit_params.ExpertReviewEditParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ExpertReviewEditResponse,
+ )
+
+
+class AsyncExpertReviewsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncExpertReviewsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cleanlab/codex-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncExpertReviewsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncExpertReviewsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cleanlab/codex-python#with_streaming_response
+ """
+ return AsyncExpertReviewsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ project_id: str,
+ *,
+ original_query_log_id: str,
+ review_status: Literal["good", "bad"],
+ generate_guidance: bool | Omit = omit,
+ explanation: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ExpertReviewCreateResponse:
+ """
+ Create Expert Review Route
+
+ Args:
+ original_query_log_id: ID of the original query log
+
+ review_status: Expert review status - 'good' or 'bad'
+
+ explanation: Optional explanation for expert review
+
+ 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}/expert_reviews/",
+ body=await async_maybe_transform(
+ {
+ "original_query_log_id": original_query_log_id,
+ "review_status": review_status,
+ "explanation": explanation,
+ },
+ expert_review_create_params.ExpertReviewCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"generate_guidance": generate_guidance}, expert_review_create_params.ExpertReviewCreateParams
+ ),
+ ),
+ cast_to=ExpertReviewCreateResponse,
+ )
+
+ async def retrieve(
+ self,
+ expert_review_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,
+ ) -> ExpertReviewRetrieveResponse:
+ """
+ Get Expert Review Route
+
+ 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 expert_review_id:
+ raise ValueError(f"Expected a non-empty value for `expert_review_id` but received {expert_review_id!r}")
+ return await self._get(
+ f"/api/projects/{project_id}/expert_reviews/{expert_review_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ExpertReviewRetrieveResponse,
+ )
+
+ def list(
+ self,
+ project_id: str,
+ *,
+ created_at_end: Union[str, datetime, None] | Omit = omit,
+ created_at_start: Union[str, datetime, None] | Omit = omit,
+ last_edited_at_end: Union[str, datetime, None] | Omit = omit,
+ last_edited_at_start: Union[str, datetime, None] | Omit = omit,
+ last_edited_by: Optional[str] | Omit = omit,
+ limit: int | Omit = omit,
+ offset: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ review_status: Optional[List[Literal["good", "bad"]]] | Omit = omit,
+ sort: Optional[Literal["created_at", "last_edited_at", "resolved_logs_count"]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[ExpertReviewListResponse, AsyncOffsetPageExpertReviews[ExpertReviewListResponse]]:
+ """
+ List Expert Reviews Route
+
+ Args:
+ created_at_end: Filter expert reviews created at or before this timestamp
+
+ created_at_start: Filter expert reviews created at or after this timestamp
+
+ last_edited_at_end: Filter expert reviews last edited at or before this timestamp
+
+ last_edited_at_start: Filter expert reviews last edited at or after this timestamp
+
+ last_edited_by: Filter expert reviews last edited by user ID
+
+ order: Sort order
+
+ review_status: Filter expert reviews by review status
+
+ sort: Sort expert reviews by field
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not project_id:
+ raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}")
+ return self._get_api_list(
+ f"/api/projects/{project_id}/expert_reviews/",
+ page=AsyncOffsetPageExpertReviews[ExpertReviewListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "created_at_end": created_at_end,
+ "created_at_start": created_at_start,
+ "last_edited_at_end": last_edited_at_end,
+ "last_edited_at_start": last_edited_at_start,
+ "last_edited_by": last_edited_by,
+ "limit": limit,
+ "offset": offset,
+ "order": order,
+ "review_status": review_status,
+ "sort": sort,
+ },
+ expert_review_list_params.ExpertReviewListParams,
+ ),
+ ),
+ model=ExpertReviewListResponse,
+ )
+
+ async def delete(
+ self,
+ expert_review_id: str,
+ *,
+ project_id: str,
+ original_query_log_id: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Delete Expert Review Route
+
+ 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 expert_review_id:
+ raise ValueError(f"Expected a non-empty value for `expert_review_id` but received {expert_review_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ f"/api/projects/{project_id}/expert_reviews/{expert_review_id}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"original_query_log_id": original_query_log_id},
+ expert_review_delete_params.ExpertReviewDeleteParams,
+ ),
+ ),
+ cast_to=NoneType,
+ )
+
+ async def edit(
+ self,
+ expert_review_id: str,
+ *,
+ project_id: str,
+ explanation: Optional[str] | Omit = omit,
+ review_status: Optional[Literal["good", "bad"]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ExpertReviewEditResponse:
+ """
+ Update Expert Review Route
+
+ 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 expert_review_id:
+ raise ValueError(f"Expected a non-empty value for `expert_review_id` but received {expert_review_id!r}")
+ return await self._patch(
+ f"/api/projects/{project_id}/expert_reviews/{expert_review_id}",
+ body=await async_maybe_transform(
+ {
+ "explanation": explanation,
+ "review_status": review_status,
+ },
+ expert_review_edit_params.ExpertReviewEditParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ExpertReviewEditResponse,
+ )
+
+
+class ExpertReviewsResourceWithRawResponse:
+ def __init__(self, expert_reviews: ExpertReviewsResource) -> None:
+ self._expert_reviews = expert_reviews
+
+ self.create = to_raw_response_wrapper(
+ expert_reviews.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ expert_reviews.retrieve,
+ )
+ self.list = to_raw_response_wrapper(
+ expert_reviews.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ expert_reviews.delete,
+ )
+ self.edit = to_raw_response_wrapper(
+ expert_reviews.edit,
+ )
+
+
+class AsyncExpertReviewsResourceWithRawResponse:
+ def __init__(self, expert_reviews: AsyncExpertReviewsResource) -> None:
+ self._expert_reviews = expert_reviews
+
+ self.create = async_to_raw_response_wrapper(
+ expert_reviews.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ expert_reviews.retrieve,
+ )
+ self.list = async_to_raw_response_wrapper(
+ expert_reviews.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ expert_reviews.delete,
+ )
+ self.edit = async_to_raw_response_wrapper(
+ expert_reviews.edit,
+ )
+
+
+class ExpertReviewsResourceWithStreamingResponse:
+ def __init__(self, expert_reviews: ExpertReviewsResource) -> None:
+ self._expert_reviews = expert_reviews
+
+ self.create = to_streamed_response_wrapper(
+ expert_reviews.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ expert_reviews.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ expert_reviews.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ expert_reviews.delete,
+ )
+ self.edit = to_streamed_response_wrapper(
+ expert_reviews.edit,
+ )
+
+
+class AsyncExpertReviewsResourceWithStreamingResponse:
+ def __init__(self, expert_reviews: AsyncExpertReviewsResource) -> None:
+ self._expert_reviews = expert_reviews
+
+ self.create = async_to_streamed_response_wrapper(
+ expert_reviews.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ expert_reviews.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ expert_reviews.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ expert_reviews.delete,
+ )
+ self.edit = async_to_streamed_response_wrapper(
+ expert_reviews.edit,
+ )
diff --git a/src/codex/resources/projects/remediations/remediations.py b/src/codex/resources/projects/remediations/remediations.py
index 23873af..f02cba8 100644
--- a/src/codex/resources/projects/remediations/remediations.py
+++ b/src/codex/resources/projects/remediations/remediations.py
@@ -28,6 +28,14 @@
ExpertAnswersResourceWithStreamingResponse,
AsyncExpertAnswersResourceWithStreamingResponse,
)
+from .expert_reviews import (
+ ExpertReviewsResource,
+ AsyncExpertReviewsResource,
+ ExpertReviewsResourceWithRawResponse,
+ AsyncExpertReviewsResourceWithRawResponse,
+ ExpertReviewsResourceWithStreamingResponse,
+ AsyncExpertReviewsResourceWithStreamingResponse,
+)
from ...._base_client import AsyncPaginator, make_request_options
from ....types.projects import (
remediation_list_params,
@@ -54,6 +62,10 @@ class RemediationsResource(SyncAPIResource):
def expert_answers(self) -> ExpertAnswersResource:
return ExpertAnswersResource(self._client)
+ @cached_property
+ def expert_reviews(self) -> ExpertReviewsResource:
+ return ExpertReviewsResource(self._client)
+
@cached_property
def with_raw_response(self) -> RemediationsResourceWithRawResponse:
"""
@@ -539,6 +551,10 @@ class AsyncRemediationsResource(AsyncAPIResource):
def expert_answers(self) -> AsyncExpertAnswersResource:
return AsyncExpertAnswersResource(self._client)
+ @cached_property
+ def expert_reviews(self) -> AsyncExpertReviewsResource:
+ return AsyncExpertReviewsResource(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncRemediationsResourceWithRawResponse:
"""
@@ -1085,6 +1101,10 @@ def __init__(self, remediations: RemediationsResource) -> None:
def expert_answers(self) -> ExpertAnswersResourceWithRawResponse:
return ExpertAnswersResourceWithRawResponse(self._remediations.expert_answers)
+ @cached_property
+ def expert_reviews(self) -> ExpertReviewsResourceWithRawResponse:
+ return ExpertReviewsResourceWithRawResponse(self._remediations.expert_reviews)
+
class AsyncRemediationsResourceWithRawResponse:
def __init__(self, remediations: AsyncRemediationsResource) -> None:
@@ -1150,6 +1170,10 @@ def __init__(self, remediations: AsyncRemediationsResource) -> None:
def expert_answers(self) -> AsyncExpertAnswersResourceWithRawResponse:
return AsyncExpertAnswersResourceWithRawResponse(self._remediations.expert_answers)
+ @cached_property
+ def expert_reviews(self) -> AsyncExpertReviewsResourceWithRawResponse:
+ return AsyncExpertReviewsResourceWithRawResponse(self._remediations.expert_reviews)
+
class RemediationsResourceWithStreamingResponse:
def __init__(self, remediations: RemediationsResource) -> None:
@@ -1215,6 +1239,10 @@ def __init__(self, remediations: RemediationsResource) -> None:
def expert_answers(self) -> ExpertAnswersResourceWithStreamingResponse:
return ExpertAnswersResourceWithStreamingResponse(self._remediations.expert_answers)
+ @cached_property
+ def expert_reviews(self) -> ExpertReviewsResourceWithStreamingResponse:
+ return ExpertReviewsResourceWithStreamingResponse(self._remediations.expert_reviews)
+
class AsyncRemediationsResourceWithStreamingResponse:
def __init__(self, remediations: AsyncRemediationsResource) -> None:
@@ -1279,3 +1307,7 @@ def __init__(self, remediations: AsyncRemediationsResource) -> None:
@cached_property
def expert_answers(self) -> AsyncExpertAnswersResourceWithStreamingResponse:
return AsyncExpertAnswersResourceWithStreamingResponse(self._remediations.expert_answers)
+
+ @cached_property
+ def expert_reviews(self) -> AsyncExpertReviewsResourceWithStreamingResponse:
+ return AsyncExpertReviewsResourceWithStreamingResponse(self._remediations.expert_reviews)
diff --git a/src/codex/types/projects/remediations/__init__.py b/src/codex/types/projects/remediations/__init__.py
index da14220..520c597 100644
--- a/src/codex/types/projects/remediations/__init__.py
+++ b/src/codex/types/projects/remediations/__init__.py
@@ -3,13 +3,21 @@
from __future__ import annotations
from .expert_answer_list_params import ExpertAnswerListParams as ExpertAnswerListParams
+from .expert_review_edit_params import ExpertReviewEditParams as ExpertReviewEditParams
+from .expert_review_list_params import ExpertReviewListParams as ExpertReviewListParams
from .expert_answer_create_params import ExpertAnswerCreateParams as ExpertAnswerCreateParams
from .expert_answer_list_response import ExpertAnswerListResponse as ExpertAnswerListResponse
+from .expert_review_create_params import ExpertReviewCreateParams as ExpertReviewCreateParams
+from .expert_review_delete_params import ExpertReviewDeleteParams as ExpertReviewDeleteParams
+from .expert_review_edit_response import ExpertReviewEditResponse as ExpertReviewEditResponse
+from .expert_review_list_response import ExpertReviewListResponse as ExpertReviewListResponse
from .expert_answer_pause_response import ExpertAnswerPauseResponse as ExpertAnswerPauseResponse
from .expert_answer_create_response import ExpertAnswerCreateResponse as ExpertAnswerCreateResponse
+from .expert_review_create_response import ExpertReviewCreateResponse as ExpertReviewCreateResponse
from .expert_answer_publish_response import ExpertAnswerPublishResponse as ExpertAnswerPublishResponse
from .expert_answer_unpause_response import ExpertAnswerUnpauseResponse as ExpertAnswerUnpauseResponse
from .expert_answer_retrieve_response import ExpertAnswerRetrieveResponse as ExpertAnswerRetrieveResponse
+from .expert_review_retrieve_response import ExpertReviewRetrieveResponse as ExpertReviewRetrieveResponse
from .expert_answer_edit_answer_params import ExpertAnswerEditAnswerParams as ExpertAnswerEditAnswerParams
from .expert_answer_edit_answer_response import ExpertAnswerEditAnswerResponse as ExpertAnswerEditAnswerResponse
from .expert_answer_edit_draft_answer_params import (
diff --git a/src/codex/types/projects/remediations/expert_review_create_params.py b/src/codex/types/projects/remediations/expert_review_create_params.py
new file mode 100644
index 0000000..c871bd8
--- /dev/null
+++ b/src/codex/types/projects/remediations/expert_review_create_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 import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ExpertReviewCreateParams"]
+
+
+class ExpertReviewCreateParams(TypedDict, total=False):
+ original_query_log_id: Required[str]
+ """ID of the original query log"""
+
+ review_status: Required[Literal["good", "bad"]]
+ """Expert review status - 'good' or 'bad'"""
+
+ generate_guidance: bool
+
+ explanation: Optional[str]
+ """Optional explanation for expert review"""
diff --git a/src/codex/types/projects/remediations/expert_review_create_response.py b/src/codex/types/projects/remediations/expert_review_create_response.py
new file mode 100644
index 0000000..154d8e0
--- /dev/null
+++ b/src/codex/types/projects/remediations/expert_review_create_response.py
@@ -0,0 +1,29 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["ExpertReviewCreateResponse"]
+
+
+class ExpertReviewCreateResponse(BaseModel):
+ id: str
+
+ created_at: datetime
+
+ last_edited_at: datetime
+
+ last_edited_by: Optional[str] = None
+
+ project_id: str
+
+ review_status: Literal["good", "bad"]
+ """Expert review status - 'good' or 'bad'"""
+
+ deleted_at: Optional[datetime] = None
+
+ explanation: Optional[str] = None
+ """Optional explanation for expert review"""
diff --git a/src/codex/types/projects/remediations/expert_review_delete_params.py b/src/codex/types/projects/remediations/expert_review_delete_params.py
new file mode 100644
index 0000000..440bf07
--- /dev/null
+++ b/src/codex/types/projects/remediations/expert_review_delete_params.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Required, TypedDict
+
+__all__ = ["ExpertReviewDeleteParams"]
+
+
+class ExpertReviewDeleteParams(TypedDict, total=False):
+ project_id: Required[str]
+
+ original_query_log_id: Optional[str]
diff --git a/src/codex/types/projects/remediations/expert_review_edit_params.py b/src/codex/types/projects/remediations/expert_review_edit_params.py
new file mode 100644
index 0000000..d0b48b8
--- /dev/null
+++ b/src/codex/types/projects/remediations/expert_review_edit_params.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ExpertReviewEditParams"]
+
+
+class ExpertReviewEditParams(TypedDict, total=False):
+ project_id: Required[str]
+
+ explanation: Optional[str]
+
+ review_status: Optional[Literal["good", "bad"]]
diff --git a/src/codex/types/projects/remediations/expert_review_edit_response.py b/src/codex/types/projects/remediations/expert_review_edit_response.py
new file mode 100644
index 0000000..1c3d845
--- /dev/null
+++ b/src/codex/types/projects/remediations/expert_review_edit_response.py
@@ -0,0 +1,29 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["ExpertReviewEditResponse"]
+
+
+class ExpertReviewEditResponse(BaseModel):
+ id: str
+
+ created_at: datetime
+
+ last_edited_at: datetime
+
+ last_edited_by: Optional[str] = None
+
+ project_id: str
+
+ review_status: Literal["good", "bad"]
+ """Expert review status - 'good' or 'bad'"""
+
+ deleted_at: Optional[datetime] = None
+
+ explanation: Optional[str] = None
+ """Optional explanation for expert review"""
diff --git a/src/codex/types/projects/remediations/expert_review_list_params.py b/src/codex/types/projects/remediations/expert_review_list_params.py
new file mode 100644
index 0000000..b316b2f
--- /dev/null
+++ b/src/codex/types/projects/remediations/expert_review_list_params.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List, Union, Optional
+from datetime import datetime
+from typing_extensions import Literal, Annotated, TypedDict
+
+from ...._utils import PropertyInfo
+
+__all__ = ["ExpertReviewListParams"]
+
+
+class ExpertReviewListParams(TypedDict, total=False):
+ created_at_end: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")]
+ """Filter expert reviews created at or before this timestamp"""
+
+ created_at_start: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")]
+ """Filter expert reviews created at or after this timestamp"""
+
+ last_edited_at_end: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")]
+ """Filter expert reviews last edited at or before this timestamp"""
+
+ last_edited_at_start: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")]
+ """Filter expert reviews last edited at or after this timestamp"""
+
+ last_edited_by: Optional[str]
+ """Filter expert reviews last edited by user ID"""
+
+ limit: int
+
+ offset: int
+
+ order: Literal["asc", "desc"]
+ """Sort order"""
+
+ review_status: Optional[List[Literal["good", "bad"]]]
+ """Filter expert reviews by review status"""
+
+ sort: Optional[Literal["created_at", "last_edited_at", "resolved_logs_count"]]
+ """Sort expert reviews by field"""
diff --git a/src/codex/types/projects/remediations/expert_review_list_response.py b/src/codex/types/projects/remediations/expert_review_list_response.py
new file mode 100644
index 0000000..99d26ab
--- /dev/null
+++ b/src/codex/types/projects/remediations/expert_review_list_response.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["ExpertReviewListResponse"]
+
+
+class ExpertReviewListResponse(BaseModel):
+ id: str
+
+ created_at: datetime
+
+ evaluated_response: Optional[str] = None
+
+ last_edited_at: datetime
+
+ last_edited_by: Optional[str] = None
+
+ project_id: str
+
+ query: str
+
+ resolved_logs_count: int
+
+ review_status: Literal["good", "bad"]
+ """Expert review status - 'good' or 'bad'"""
+
+ deleted_at: Optional[datetime] = None
+
+ explanation: Optional[str] = None
+ """Optional explanation for expert review"""
diff --git a/src/codex/types/projects/remediations/expert_review_retrieve_response.py b/src/codex/types/projects/remediations/expert_review_retrieve_response.py
new file mode 100644
index 0000000..9cb0da6
--- /dev/null
+++ b/src/codex/types/projects/remediations/expert_review_retrieve_response.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["ExpertReviewRetrieveResponse"]
+
+
+class ExpertReviewRetrieveResponse(BaseModel):
+ id: str
+
+ created_at: datetime
+
+ evaluated_response: Optional[str] = None
+
+ last_edited_at: datetime
+
+ last_edited_by: Optional[str] = None
+
+ project_id: str
+
+ query: str
+
+ resolved_logs_count: int
+
+ review_status: Literal["good", "bad"]
+ """Expert review status - 'good' or 'bad'"""
+
+ deleted_at: Optional[datetime] = None
+
+ explanation: Optional[str] = None
+ """Optional explanation for expert review"""
diff --git a/tests/api_resources/projects/remediations/test_expert_reviews.py b/tests/api_resources/projects/remediations/test_expert_reviews.py
new file mode 100644
index 0000000..f65b0fb
--- /dev/null
+++ b/tests/api_resources/projects/remediations/test_expert_reviews.py
@@ -0,0 +1,631 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from codex import Codex, AsyncCodex
+from tests.utils import assert_matches_type
+from codex._utils import parse_datetime
+from codex.pagination import SyncOffsetPageExpertReviews, AsyncOffsetPageExpertReviews
+from codex.types.projects.remediations import (
+ ExpertReviewEditResponse,
+ ExpertReviewListResponse,
+ ExpertReviewCreateResponse,
+ ExpertReviewRetrieveResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestExpertReviews:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_create(self, client: Codex) -> None:
+ expert_review = client.projects.remediations.expert_reviews.create(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ original_query_log_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ review_status="good",
+ )
+ assert_matches_type(ExpertReviewCreateResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: Codex) -> None:
+ expert_review = client.projects.remediations.expert_reviews.create(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ original_query_log_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ review_status="good",
+ generate_guidance=True,
+ explanation="explanation",
+ )
+ assert_matches_type(ExpertReviewCreateResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: Codex) -> None:
+ response = client.projects.remediations.expert_reviews.with_raw_response.create(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ original_query_log_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ review_status="good",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ expert_review = response.parse()
+ assert_matches_type(ExpertReviewCreateResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: Codex) -> None:
+ with client.projects.remediations.expert_reviews.with_streaming_response.create(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ original_query_log_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ review_status="good",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ expert_review = response.parse()
+ assert_matches_type(ExpertReviewCreateResponse, expert_review, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_create(self, client: Codex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ client.projects.remediations.expert_reviews.with_raw_response.create(
+ project_id="",
+ original_query_log_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ review_status="good",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Codex) -> None:
+ expert_review = client.projects.remediations.expert_reviews.retrieve(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(ExpertReviewRetrieveResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Codex) -> None:
+ response = client.projects.remediations.expert_reviews.with_raw_response.retrieve(
+ expert_review_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"
+ expert_review = response.parse()
+ assert_matches_type(ExpertReviewRetrieveResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Codex) -> None:
+ with client.projects.remediations.expert_reviews.with_streaming_response.retrieve(
+ expert_review_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"
+
+ expert_review = response.parse()
+ assert_matches_type(ExpertReviewRetrieveResponse, expert_review, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_retrieve(self, client: Codex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ client.projects.remediations.expert_reviews.with_raw_response.retrieve(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `expert_review_id` but received ''"):
+ client.projects.remediations.expert_reviews.with_raw_response.retrieve(
+ expert_review_id="",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Codex) -> None:
+ expert_review = client.projects.remediations.expert_reviews.list(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(SyncOffsetPageExpertReviews[ExpertReviewListResponse], expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Codex) -> None:
+ expert_review = client.projects.remediations.expert_reviews.list(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ created_at_end=parse_datetime("2019-12-27T18:11:19.117Z"),
+ created_at_start=parse_datetime("2019-12-27T18:11:19.117Z"),
+ last_edited_at_end=parse_datetime("2019-12-27T18:11:19.117Z"),
+ last_edited_at_start=parse_datetime("2019-12-27T18:11:19.117Z"),
+ last_edited_by="last_edited_by",
+ limit=1,
+ offset=0,
+ order="asc",
+ review_status=["good"],
+ sort="created_at",
+ )
+ assert_matches_type(SyncOffsetPageExpertReviews[ExpertReviewListResponse], expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Codex) -> None:
+ response = client.projects.remediations.expert_reviews.with_raw_response.list(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ expert_review = response.parse()
+ assert_matches_type(SyncOffsetPageExpertReviews[ExpertReviewListResponse], expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Codex) -> None:
+ with client.projects.remediations.expert_reviews.with_streaming_response.list(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ expert_review = response.parse()
+ assert_matches_type(SyncOffsetPageExpertReviews[ExpertReviewListResponse], expert_review, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_list(self, client: Codex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ client.projects.remediations.expert_reviews.with_raw_response.list(
+ project_id="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_delete(self, client: Codex) -> None:
+ expert_review = client.projects.remediations.expert_reviews.delete(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert expert_review is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_delete_with_all_params(self, client: Codex) -> None:
+ expert_review = client.projects.remediations.expert_reviews.delete(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ original_query_log_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert expert_review is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_delete(self, client: Codex) -> None:
+ response = client.projects.remediations.expert_reviews.with_raw_response.delete(
+ expert_review_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"
+ expert_review = response.parse()
+ assert expert_review is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_delete(self, client: Codex) -> None:
+ with client.projects.remediations.expert_reviews.with_streaming_response.delete(
+ expert_review_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"
+
+ expert_review = response.parse()
+ assert expert_review is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_delete(self, client: Codex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ client.projects.remediations.expert_reviews.with_raw_response.delete(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `expert_review_id` but received ''"):
+ client.projects.remediations.expert_reviews.with_raw_response.delete(
+ expert_review_id="",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_edit(self, client: Codex) -> None:
+ expert_review = client.projects.remediations.expert_reviews.edit(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(ExpertReviewEditResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_edit_with_all_params(self, client: Codex) -> None:
+ expert_review = client.projects.remediations.expert_reviews.edit(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ explanation="explanation",
+ review_status="good",
+ )
+ assert_matches_type(ExpertReviewEditResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_edit(self, client: Codex) -> None:
+ response = client.projects.remediations.expert_reviews.with_raw_response.edit(
+ expert_review_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"
+ expert_review = response.parse()
+ assert_matches_type(ExpertReviewEditResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_edit(self, client: Codex) -> None:
+ with client.projects.remediations.expert_reviews.with_streaming_response.edit(
+ expert_review_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"
+
+ expert_review = response.parse()
+ assert_matches_type(ExpertReviewEditResponse, expert_review, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_edit(self, client: Codex) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"):
+ client.projects.remediations.expert_reviews.with_raw_response.edit(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `expert_review_id` but received ''"):
+ client.projects.remediations.expert_reviews.with_raw_response.edit(
+ expert_review_id="",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+
+class TestAsyncExpertReviews:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncCodex) -> None:
+ expert_review = await async_client.projects.remediations.expert_reviews.create(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ original_query_log_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ review_status="good",
+ )
+ assert_matches_type(ExpertReviewCreateResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncCodex) -> None:
+ expert_review = await async_client.projects.remediations.expert_reviews.create(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ original_query_log_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ review_status="good",
+ generate_guidance=True,
+ explanation="explanation",
+ )
+ assert_matches_type(ExpertReviewCreateResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncCodex) -> None:
+ response = await async_client.projects.remediations.expert_reviews.with_raw_response.create(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ original_query_log_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ review_status="good",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ expert_review = await response.parse()
+ assert_matches_type(ExpertReviewCreateResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncCodex) -> None:
+ async with async_client.projects.remediations.expert_reviews.with_streaming_response.create(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ original_query_log_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ review_status="good",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ expert_review = await response.parse()
+ assert_matches_type(ExpertReviewCreateResponse, expert_review, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_create(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.remediations.expert_reviews.with_raw_response.create(
+ project_id="",
+ original_query_log_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ review_status="good",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncCodex) -> None:
+ expert_review = await async_client.projects.remediations.expert_reviews.retrieve(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(ExpertReviewRetrieveResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncCodex) -> None:
+ response = await async_client.projects.remediations.expert_reviews.with_raw_response.retrieve(
+ expert_review_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"
+ expert_review = await response.parse()
+ assert_matches_type(ExpertReviewRetrieveResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncCodex) -> None:
+ async with async_client.projects.remediations.expert_reviews.with_streaming_response.retrieve(
+ expert_review_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"
+
+ expert_review = await response.parse()
+ assert_matches_type(ExpertReviewRetrieveResponse, expert_review, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve(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.remediations.expert_reviews.with_raw_response.retrieve(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `expert_review_id` but received ''"):
+ await async_client.projects.remediations.expert_reviews.with_raw_response.retrieve(
+ expert_review_id="",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncCodex) -> None:
+ expert_review = await async_client.projects.remediations.expert_reviews.list(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(AsyncOffsetPageExpertReviews[ExpertReviewListResponse], expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncCodex) -> None:
+ expert_review = await async_client.projects.remediations.expert_reviews.list(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ created_at_end=parse_datetime("2019-12-27T18:11:19.117Z"),
+ created_at_start=parse_datetime("2019-12-27T18:11:19.117Z"),
+ last_edited_at_end=parse_datetime("2019-12-27T18:11:19.117Z"),
+ last_edited_at_start=parse_datetime("2019-12-27T18:11:19.117Z"),
+ last_edited_by="last_edited_by",
+ limit=1,
+ offset=0,
+ order="asc",
+ review_status=["good"],
+ sort="created_at",
+ )
+ assert_matches_type(AsyncOffsetPageExpertReviews[ExpertReviewListResponse], expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncCodex) -> None:
+ response = await async_client.projects.remediations.expert_reviews.with_raw_response.list(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ expert_review = await response.parse()
+ assert_matches_type(AsyncOffsetPageExpertReviews[ExpertReviewListResponse], expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncCodex) -> None:
+ async with async_client.projects.remediations.expert_reviews.with_streaming_response.list(
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ expert_review = await response.parse()
+ assert_matches_type(
+ AsyncOffsetPageExpertReviews[ExpertReviewListResponse], expert_review, path=["response"]
+ )
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_list(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.remediations.expert_reviews.with_raw_response.list(
+ project_id="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncCodex) -> None:
+ expert_review = await async_client.projects.remediations.expert_reviews.delete(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert expert_review is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_delete_with_all_params(self, async_client: AsyncCodex) -> None:
+ expert_review = await async_client.projects.remediations.expert_reviews.delete(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ original_query_log_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert expert_review is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncCodex) -> None:
+ response = await async_client.projects.remediations.expert_reviews.with_raw_response.delete(
+ expert_review_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"
+ expert_review = await response.parse()
+ assert expert_review is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncCodex) -> None:
+ async with async_client.projects.remediations.expert_reviews.with_streaming_response.delete(
+ expert_review_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"
+
+ expert_review = await response.parse()
+ assert expert_review is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_delete(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.remediations.expert_reviews.with_raw_response.delete(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `expert_review_id` but received ''"):
+ await async_client.projects.remediations.expert_reviews.with_raw_response.delete(
+ expert_review_id="",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_edit(self, async_client: AsyncCodex) -> None:
+ expert_review = await async_client.projects.remediations.expert_reviews.edit(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(ExpertReviewEditResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_edit_with_all_params(self, async_client: AsyncCodex) -> None:
+ expert_review = await async_client.projects.remediations.expert_reviews.edit(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ explanation="explanation",
+ review_status="good",
+ )
+ assert_matches_type(ExpertReviewEditResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_edit(self, async_client: AsyncCodex) -> None:
+ response = await async_client.projects.remediations.expert_reviews.with_raw_response.edit(
+ expert_review_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"
+ expert_review = await response.parse()
+ assert_matches_type(ExpertReviewEditResponse, expert_review, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_edit(self, async_client: AsyncCodex) -> None:
+ async with async_client.projects.remediations.expert_reviews.with_streaming_response.edit(
+ expert_review_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"
+
+ expert_review = await response.parse()
+ assert_matches_type(ExpertReviewEditResponse, expert_review, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_edit(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.remediations.expert_reviews.with_raw_response.edit(
+ expert_review_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `expert_review_id` but received ''"):
+ await async_client.projects.remediations.expert_reviews.with_raw_response.edit(
+ expert_review_id="",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
From f74f2840241812da3d7afeb4b50123aed69056fc Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 19 Nov 2025 19:07:33 +0000
Subject: [PATCH 3/3] release: 0.1.0-alpha.34
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 9 +++++++++
pyproject.toml | 2 +-
src/codex/_version.py | 2 +-
4 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index ff4f9a5..36b2aff 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.1.0-alpha.33"
+ ".": "0.1.0-alpha.34"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 306f329..7782cb1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## 0.1.0-alpha.34 (2025-11-19)
+
+Full Changelog: [v0.1.0-alpha.33...v0.1.0-alpha.34](https://github.com/cleanlab/codex-python/compare/v0.1.0-alpha.33...v0.1.0-alpha.34)
+
+### Features
+
+* **api:** add expert review endpoints ([cda6f91](https://github.com/cleanlab/codex-python/commit/cda6f911fe678318779d23de9ecc77fb15594fa1))
+* **api:** api update ([5848aac](https://github.com/cleanlab/codex-python/commit/5848aac2eb02a789c20951056a7b802985d8b2ab))
+
## 0.1.0-alpha.33 (2025-11-18)
Full Changelog: [v0.1.0-alpha.32...v0.1.0-alpha.33](https://github.com/cleanlab/codex-python/compare/v0.1.0-alpha.32...v0.1.0-alpha.33)
diff --git a/pyproject.toml b/pyproject.toml
index efd2e79..5fb2418 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "codex-sdk"
-version = "0.1.0-alpha.33"
+version = "0.1.0-alpha.34"
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 45fdfee..c2ea81e 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.33" # x-release-please-version
+__version__ = "0.1.0-alpha.34" # x-release-please-version