Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
.prism.log
.vscode
_dev

__pycache__
Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.1.0-alpha.23"
".": "0.1.0-alpha.24"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
configured_endpoints: 55
openapi_spec_hash: 922886934580d0b2addcb6e26ada0e09
config_hash: 8f6e5c3b064cbb77569a6bf654954a56
configured_endpoints: 54
openapi_spec_hash: 49989625bf633c5fdb3e11140f788f2d
config_hash: 930284cfa37f835d949c8a1b124f4807
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"python.analysis.importFormat": "relative",
}
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# Changelog

## 0.1.0-alpha.24 (2025-07-28)

Full Changelog: [v0.1.0-alpha.23...v0.1.0-alpha.24](https://github.com/cleanlab/codex-python/compare/v0.1.0-alpha.23...v0.1.0-alpha.24)

### Features

* **api:** api update ([ce6d89f](https://github.com/cleanlab/codex-python/commit/ce6d89f3c885765b21c6ba43b1b7b9a1ebf8a61e))
* **api:** api update ([1a06cfc](https://github.com/cleanlab/codex-python/commit/1a06cfc7c19943ac468b2ec9f2787215363cf77e))
* **api:** api update ([2ee8095](https://github.com/cleanlab/codex-python/commit/2ee809593ddb15c4de776a2048883287ec5c0cdb))
* **api:** api update ([6992031](https://github.com/cleanlab/codex-python/commit/6992031e6aa610031f24d818040050b0fc185c34))
* **api:** api update ([7e7caf9](https://github.com/cleanlab/codex-python/commit/7e7caf9a3ad214c5df3686122e4f26b850dcb8b0))
* **api:** api update ([0a33c47](https://github.com/cleanlab/codex-python/commit/0a33c4710d4890d17ddd973ba4a2ed183e45e4c7))
* **api:** api update ([575d190](https://github.com/cleanlab/codex-python/commit/575d1901319984fea901ce216323a5259e17f98c))
* **api:** api update ([f55f4b7](https://github.com/cleanlab/codex-python/commit/f55f4b768f8c1d00bdf61e56b0a7227c8424c5b6))
* **api:** api update ([b956ce0](https://github.com/cleanlab/codex-python/commit/b956ce083ef3c507a7649577724f337a562c427a))
* **api:** remove deprecated endpoint increment_queries ([6b52a98](https://github.com/cleanlab/codex-python/commit/6b52a985af9df1b6618d0685fafee2bae7e98566))


### Bug Fixes

* **client:** don't send Content-Type header on GET requests ([4732aae](https://github.com/cleanlab/codex-python/commit/4732aaeb03872abffb4e13df6dd1994711bd4268))
* **parsing:** correctly handle nested discriminated unions ([b374589](https://github.com/cleanlab/codex-python/commit/b374589baf01ca1236cf0823305e6bca037cf12b))
* **parsing:** ignore empty metadata ([1cdf391](https://github.com/cleanlab/codex-python/commit/1cdf391742b196d5a723307e8c202a69e00b371d))
* **parsing:** parse extra field types ([3c74ca0](https://github.com/cleanlab/codex-python/commit/3c74ca0f1a913bed65cc4c6580dda25a07a90b74))


### Chores

* **internal:** bump pinned h11 dep ([7ce51e9](https://github.com/cleanlab/codex-python/commit/7ce51e93023f66f3e343e379fc1930ddba335e9b))
* **package:** mark python 3.13 as supported ([5cba949](https://github.com/cleanlab/codex-python/commit/5cba94956fff8ca4de99426a20e5c67f0ce6a2ac))
* **project:** add settings file for vscode ([00df8ec](https://github.com/cleanlab/codex-python/commit/00df8ec35d44e5bdc6e68661a92d9d21905222c7))
* **readme:** fix version rendering on pypi ([d05336d](https://github.com/cleanlab/codex-python/commit/d05336d89f5a49b09d7b1f85e7cb3ed74035157a))

## 0.1.0-alpha.23 (2025-07-07)

Full Changelog: [v0.1.0-alpha.22...v0.1.0-alpha.23](https://github.com/cleanlab/codex-python/compare/v0.1.0-alpha.22...v0.1.0-alpha.23)
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Codex SDK

[![PyPI version](<https://img.shields.io/pypi/v/codex-sdk.svg?label=pypi%20(stable)>)](https://pypi.org/project/codex-sdk/)
<!-- prettier-ignore -->
[![PyPI version](https://img.shields.io/pypi/v/codex-sdk.svg?label=pypi%20(stable))](https://pypi.org/project/codex-sdk/)

This library is not meant to be used directly. Refer to https://pypi.org/project/cleanlab-codex/ instead.
3 changes: 1 addition & 2 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ Methods:
- <code title="get /api/projects/">client.projects.<a href="./src/codex/resources/projects/projects.py">list</a>(\*\*<a href="src/codex/types/project_list_params.py">params</a>) -> <a href="./src/codex/types/project_list_response.py">ProjectListResponse</a></code>
- <code title="delete /api/projects/{project_id}">client.projects.<a href="./src/codex/resources/projects/projects.py">delete</a>(project_id) -> None</code>
- <code title="get /api/projects/{project_id}/export">client.projects.<a href="./src/codex/resources/projects/projects.py">export</a>(project_id) -> object</code>
- <code title="post /api/projects/{project_id}/increment_queries">client.projects.<a href="./src/codex/resources/projects/projects.py">increment_queries</a>(project_id, \*\*<a href="src/codex/types/project_increment_queries_params.py">params</a>) -> object</code>
- <code title="post /api/projects/{project_id}/notifications">client.projects.<a href="./src/codex/resources/projects/projects.py">invite_sme</a>(project_id, \*\*<a href="src/codex/types/project_invite_sme_params.py">params</a>) -> <a href="./src/codex/types/project_invite_sme_response.py">ProjectInviteSmeResponse</a></code>
- <code title="get /api/projects/{project_id}/analytics/">client.projects.<a href="./src/codex/resources/projects/projects.py">retrieve_analytics</a>(project_id, \*\*<a href="src/codex/types/project_retrieve_analytics_params.py">params</a>) -> <a href="./src/codex/types/project_retrieve_analytics_response.py">ProjectRetrieveAnalyticsResponse</a></code>
- <code title="post /api/projects/{project_id}/validate">client.projects.<a href="./src/codex/resources/projects/projects.py">validate</a>(project_id, \*\*<a href="src/codex/types/project_validate_params.py">params</a>) -> <a href="./src/codex/types/project_validate_response.py">ProjectValidateResponse</a></code>
Expand Down Expand Up @@ -192,7 +191,7 @@ Methods:

- <code title="post /api/projects/{project_id}/evals">client.projects.evals.<a href="./src/codex/resources/projects/evals.py">create</a>(project_id, \*\*<a href="src/codex/types/projects/eval_create_params.py">params</a>) -> <a href="./src/codex/types/project_return_schema.py">ProjectReturnSchema</a></code>
- <code title="put /api/projects/{project_id}/evals/{eval_key}">client.projects.evals.<a href="./src/codex/resources/projects/evals.py">update</a>(path_eval_key, \*, project_id, \*\*<a href="src/codex/types/projects/eval_update_params.py">params</a>) -> <a href="./src/codex/types/project_return_schema.py">ProjectReturnSchema</a></code>
- <code title="get /api/projects/{project_id}/evals">client.projects.evals.<a href="./src/codex/resources/projects/evals.py">list</a>(project_id) -> <a href="./src/codex/types/projects/eval_list_response.py">EvalListResponse</a></code>
- <code title="get /api/projects/{project_id}/evals">client.projects.evals.<a href="./src/codex/resources/projects/evals.py">list</a>(project_id, \*\*<a href="src/codex/types/projects/eval_list_params.py">params</a>) -> <a href="./src/codex/types/projects/eval_list_response.py">EvalListResponse</a></code>
- <code title="delete /api/projects/{project_id}/evals/{eval_key}">client.projects.evals.<a href="./src/codex/resources/projects/evals.py">delete</a>(eval_key, \*, project_id) -> <a href="./src/codex/types/project_return_schema.py">ProjectReturnSchema</a></code>

## QueryLogs
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "codex-sdk"
version = "0.1.0-alpha.23"
version = "0.1.0-alpha.24"
description = "Internal SDK used within cleanlab-codex package. Refer to https://pypi.org/project/cleanlab-codex/ instead."
dynamic = ["readme"]
license = "MIT"
Expand All @@ -24,6 +24,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Operating System :: OS Independent",
"Operating System :: POSIX",
"Operating System :: MacOS",
Expand All @@ -38,7 +39,7 @@ Homepage = "https://github.com/cleanlab/codex-python"
Repository = "https://github.com/cleanlab/codex-python"

[project.optional-dependencies]
aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"]
aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.8"]

[tool.rye]
managed = true
Expand Down
4 changes: 2 additions & 2 deletions requirements-dev.lock
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ filelock==3.12.4
frozenlist==1.6.2
# via aiohttp
# via aiosignal
h11==0.14.0
h11==0.16.0
# via httpcore
httpcore==1.0.2
httpcore==1.0.9
# via httpx
httpx==0.28.1
# via codex-sdk
Expand Down
4 changes: 2 additions & 2 deletions requirements.lock
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ exceptiongroup==1.2.2
frozenlist==1.6.2
# via aiohttp
# via aiosignal
h11==0.14.0
h11==0.16.0
# via httpcore
httpcore==1.0.2
httpcore==1.0.9
# via httpx
httpx==0.28.1
# via codex-sdk
Expand Down
11 changes: 9 additions & 2 deletions src/codex/_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,15 @@ def _build_request(
# work around https://github.com/encode/httpx/discussions/2880
kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")}

is_body_allowed = options.method.lower() != "get"

if is_body_allowed:
kwargs["json"] = json_data if is_given(json_data) else None
kwargs["files"] = files
else:
headers.pop("Content-Type", None)
kwargs.pop("data", None)

# TODO: report this error to httpx
return self._client.build_request( # pyright: ignore[reportUnknownMemberType]
headers=headers,
Expand All @@ -540,8 +549,6 @@ def _build_request(
# so that passing a `TypedDict` doesn't cause an error.
# https://github.com/microsoft/pyright/issues/3526#event-6715453066
params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None,
json=json_data if is_given(json_data) else None,
files=files,
**kwargs,
)

Expand Down
38 changes: 31 additions & 7 deletions src/codex/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import os
import inspect
from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast
from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast
from datetime import date, datetime
from typing_extensions import (
List,
Unpack,
Literal,
ClassVar,
Expand Down Expand Up @@ -207,14 +208,18 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride]
else:
fields_values[name] = field_get_default(field)

extra_field_type = _get_extra_fields_type(__cls)

_extra = {}
for key, value in values.items():
if key not in model_fields:
parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value

if PYDANTIC_V2:
_extra[key] = value
_extra[key] = parsed
else:
_fields_set.add(key)
fields_values[key] = value
fields_values[key] = parsed

object.__setattr__(m, "__dict__", fields_values)

Expand Down Expand Up @@ -366,7 +371,24 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object:
if type_ is None:
raise RuntimeError(f"Unexpected field type is None for {key}")

return construct_type(value=value, type_=type_)
return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None))


def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None:
if not PYDANTIC_V2:
# TODO
return None

schema = cls.__pydantic_core_schema__
if schema["type"] == "model":
fields = schema["schema"]
if fields["type"] == "model-fields":
extras = fields.get("extras_schema")
if extras and "cls" in extras:
# mypy can't narrow the type
return extras["cls"] # type: ignore[no-any-return]

return None


def is_basemodel(type_: type) -> bool:
Expand Down Expand Up @@ -420,7 +442,7 @@ def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T:
return cast(_T, construct_type(value=value, type_=type_))


def construct_type(*, value: object, type_: object) -> object:
def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object:
"""Loose coercion to the expected type with construction of nested values.

If the given value does not match the expected type then it is returned as-is.
Expand All @@ -438,8 +460,10 @@ def construct_type(*, value: object, type_: object) -> object:
type_ = type_.__value__ # type: ignore[unreachable]

# unwrap `Annotated[T, ...]` -> `T`
if is_annotated_type(type_):
meta: tuple[Any, ...] = get_args(type_)[1:]
if metadata is not None and len(metadata) > 0:
meta: tuple[Any, ...] = tuple(metadata)
elif is_annotated_type(type_):
meta = get_args(type_)[1:]
type_ = extract_type_arg(type_, 0)
else:
meta = tuple()
Expand Down
2 changes: 1 addition & 1 deletion src/codex/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "codex"
__version__ = "0.1.0-alpha.23" # x-release-please-version
__version__ = "0.1.0-alpha.24" # x-release-please-version
38 changes: 33 additions & 5 deletions src/codex/resources/projects/evals.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
async_to_streamed_response_wrapper,
)
from ..._base_client import make_request_options
from ...types.projects import eval_create_params, eval_update_params
from ...types.projects import eval_list_params, eval_create_params, eval_update_params
from ...types.project_return_schema import ProjectReturnSchema
from ...types.projects.eval_list_response import EvalListResponse

Expand Down Expand Up @@ -324,6 +324,9 @@ def list(
self,
project_id: str,
*,
guardrails_only: bool | NotGiven = NOT_GIVEN,
limit: Optional[int] | NotGiven = NOT_GIVEN,
offset: 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,
Expand All @@ -332,7 +335,7 @@ def list(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> EvalListResponse:
"""
Get the evaluations config for a project.
Get the evaluations config for a project with optional pagination.

Args:
extra_headers: Send extra headers
Expand All @@ -348,7 +351,18 @@ def list(
return self._get(
f"/api/projects/{project_id}/evals",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
query=maybe_transform(
{
"guardrails_only": guardrails_only,
"limit": limit,
"offset": offset,
},
eval_list_params.EvalListParams,
),
),
cast_to=EvalListResponse,
)
Expand Down Expand Up @@ -689,6 +703,9 @@ async def list(
self,
project_id: str,
*,
guardrails_only: bool | NotGiven = NOT_GIVEN,
limit: Optional[int] | NotGiven = NOT_GIVEN,
offset: 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,
Expand All @@ -697,7 +714,7 @@ async def list(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> EvalListResponse:
"""
Get the evaluations config for a project.
Get the evaluations config for a project with optional pagination.

Args:
extra_headers: Send extra headers
Expand All @@ -713,7 +730,18 @@ async def list(
return await self._get(
f"/api/projects/{project_id}/evals",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
query=await async_maybe_transform(
{
"guardrails_only": guardrails_only,
"limit": limit,
"offset": offset,
},
eval_list_params.EvalListParams,
),
),
cast_to=EvalListResponse,
)
Expand Down
Loading