Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
134 commits
Select commit Hold shift + click to select a range
97c2432
codegen metadata
stainless-app[bot] Jan 26, 2026
cca7a2d
codegen metadata
stainless-app[bot] Jan 26, 2026
080210a
codegen metadata
stainless-app[bot] Jan 26, 2026
f49e642
codegen metadata
stainless-app[bot] Jan 27, 2026
10bea95
codegen metadata
stainless-app[bot] Jan 27, 2026
9668d4d
codegen metadata
stainless-app[bot] Jan 27, 2026
320d85e
codegen metadata
stainless-app[bot] Jan 27, 2026
078ed6c
codegen metadata
stainless-app[bot] Jan 27, 2026
fa092b1
codegen metadata
stainless-app[bot] Jan 27, 2026
7994ae3
codegen metadata
stainless-app[bot] Jan 27, 2026
573aeea
codegen metadata
stainless-app[bot] Jan 27, 2026
090106a
codegen metadata
stainless-app[bot] Jan 27, 2026
0113d15
codegen metadata
stainless-app[bot] Jan 27, 2026
960ab60
codegen metadata
stainless-app[bot] Jan 27, 2026
89a585a
codegen metadata
stainless-app[bot] Jan 27, 2026
f1a1770
codegen metadata
stainless-app[bot] Jan 27, 2026
78d9491
codegen metadata
stainless-app[bot] Jan 27, 2026
b7c0802
codegen metadata
stainless-app[bot] Jan 28, 2026
dd62a4a
codegen metadata
stainless-app[bot] Jan 28, 2026
5f5aa69
codegen metadata
stainless-app[bot] Jan 28, 2026
57e1a4a
codegen metadata
stainless-app[bot] Jan 28, 2026
3e37c30
codegen metadata
stainless-app[bot] Jan 28, 2026
a85b237
codegen metadata
stainless-app[bot] Jan 28, 2026
7f36478
codegen metadata
stainless-app[bot] Jan 28, 2026
4d05311
codegen metadata
stainless-app[bot] Jan 28, 2026
5f50b63
codegen metadata
stainless-app[bot] Jan 28, 2026
c6cb35f
codegen metadata
stainless-app[bot] Jan 28, 2026
0d55a8f
codegen metadata
stainless-app[bot] Jan 28, 2026
3601afb
codegen metadata
stainless-app[bot] Jan 28, 2026
2689fd4
codegen metadata
stainless-app[bot] Jan 28, 2026
b6238e6
codegen metadata
stainless-app[bot] Jan 28, 2026
ba14bb6
codegen metadata
stainless-app[bot] Jan 28, 2026
7367681
codegen metadata
stainless-app[bot] Jan 28, 2026
5093063
codegen metadata
stainless-app[bot] Jan 28, 2026
7409e56
codegen metadata
stainless-app[bot] Jan 28, 2026
abc8296
codegen metadata
stainless-app[bot] Jan 29, 2026
bd5d956
codegen metadata
stainless-app[bot] Jan 29, 2026
61ab672
codegen metadata
stainless-app[bot] Jan 29, 2026
53ab30e
codegen metadata
stainless-app[bot] Jan 29, 2026
cfa07b7
codegen metadata
stainless-app[bot] Jan 29, 2026
f962a56
codegen metadata
stainless-app[bot] Jan 29, 2026
a0720ab
feat(client): add custom JSON encoder for extended type support
stainless-app[bot] Jan 29, 2026
30bcfe5
codegen metadata
stainless-app[bot] Jan 29, 2026
e0451f1
codegen metadata
stainless-app[bot] Jan 29, 2026
b2f6f0b
codegen metadata
stainless-app[bot] Jan 29, 2026
9a9d5cc
codegen metadata
stainless-app[bot] Jan 29, 2026
bf31bc6
codegen metadata
stainless-app[bot] Jan 30, 2026
76c9c9d
codegen metadata
stainless-app[bot] Jan 30, 2026
77c43df
codegen metadata
stainless-app[bot] Jan 30, 2026
30fa188
codegen metadata
stainless-app[bot] Jan 30, 2026
8668bfe
codegen metadata
stainless-app[bot] Jan 30, 2026
abca0f7
codegen metadata
stainless-app[bot] Jan 30, 2026
17222ba
codegen metadata
stainless-app[bot] Jan 30, 2026
c47145a
codegen metadata
stainless-app[bot] Jan 30, 2026
1563bb9
codegen metadata
stainless-app[bot] Jan 30, 2026
34d7f11
codegen metadata
stainless-app[bot] Jan 30, 2026
fa047a1
codegen metadata
stainless-app[bot] Jan 30, 2026
81c3cfd
codegen metadata
stainless-app[bot] Jan 31, 2026
910a093
codegen metadata
stainless-app[bot] Jan 31, 2026
853c097
codegen metadata
stainless-app[bot] Jan 31, 2026
02f4934
codegen metadata
stainless-app[bot] Jan 31, 2026
e5695f2
codegen metadata
stainless-app[bot] Jan 31, 2026
b9f3999
codegen metadata
stainless-app[bot] Jan 31, 2026
f1ee9a5
codegen metadata
stainless-app[bot] Jan 31, 2026
4038161
codegen metadata
stainless-app[bot] Jan 31, 2026
1863d47
codegen metadata
stainless-app[bot] Jan 31, 2026
bc1724e
codegen metadata
stainless-app[bot] Feb 1, 2026
b1c8b08
codegen metadata
stainless-app[bot] Feb 1, 2026
63aca1f
codegen metadata
stainless-app[bot] Feb 1, 2026
da17c1e
codegen metadata
stainless-app[bot] Feb 1, 2026
f376221
codegen metadata
stainless-app[bot] Feb 1, 2026
ec7f6b8
codegen metadata
stainless-app[bot] Feb 1, 2026
184449c
codegen metadata
stainless-app[bot] Feb 1, 2026
3cf5762
codegen metadata
stainless-app[bot] Feb 1, 2026
ff34d02
codegen metadata
stainless-app[bot] Feb 1, 2026
81a15c7
codegen metadata
stainless-app[bot] Feb 1, 2026
d7f414e
codegen metadata
stainless-app[bot] Feb 1, 2026
910ad6f
codegen metadata
stainless-app[bot] Feb 1, 2026
9e317f2
codegen metadata
stainless-app[bot] Feb 1, 2026
e0296ba
codegen metadata
stainless-app[bot] Feb 1, 2026
aef9809
codegen metadata
stainless-app[bot] Feb 2, 2026
9e2dc02
codegen metadata
stainless-app[bot] Feb 2, 2026
e40698b
codegen metadata
stainless-app[bot] Feb 2, 2026
ab84c42
codegen metadata
stainless-app[bot] Feb 2, 2026
ab07651
codegen metadata
stainless-app[bot] Feb 2, 2026
d7fed48
codegen metadata
stainless-app[bot] Feb 2, 2026
0a8ce56
codegen metadata
stainless-app[bot] Feb 2, 2026
931ab2d
codegen metadata
stainless-app[bot] Feb 2, 2026
6845c0a
codegen metadata
stainless-app[bot] Feb 2, 2026
6cbbf75
codegen metadata
stainless-app[bot] Feb 2, 2026
7e4ebf5
codegen metadata
stainless-app[bot] Feb 2, 2026
c2736a8
codegen metadata
stainless-app[bot] Feb 2, 2026
9bc7183
codegen metadata
stainless-app[bot] Feb 2, 2026
d0a7023
codegen metadata
stainless-app[bot] Feb 2, 2026
77f4206
codegen metadata
stainless-app[bot] Feb 3, 2026
a22ea19
codegen metadata
stainless-app[bot] Feb 3, 2026
036b6f6
codegen metadata
stainless-app[bot] Feb 3, 2026
77f684f
codegen metadata
stainless-app[bot] Feb 3, 2026
b1e1415
codegen metadata
stainless-app[bot] Feb 3, 2026
3190b19
codegen metadata
stainless-app[bot] Feb 3, 2026
173807e
codegen metadata
stainless-app[bot] Feb 3, 2026
0f66d2c
codegen metadata
stainless-app[bot] Feb 3, 2026
9cd175e
codegen metadata
stainless-app[bot] Feb 3, 2026
2f5659c
codegen metadata
stainless-app[bot] Feb 3, 2026
ce0eb4e
codegen metadata
stainless-app[bot] Feb 3, 2026
3538af3
codegen metadata
stainless-app[bot] Feb 3, 2026
975707c
codegen metadata
stainless-app[bot] Feb 4, 2026
0e15951
codegen metadata
stainless-app[bot] Feb 4, 2026
a4e4568
codegen metadata
stainless-app[bot] Feb 4, 2026
1df3e97
codegen metadata
stainless-app[bot] Feb 4, 2026
8524451
codegen metadata
stainless-app[bot] Feb 4, 2026
e4108a0
codegen metadata
stainless-app[bot] Feb 4, 2026
18302d1
codegen metadata
stainless-app[bot] Feb 4, 2026
db81b94
codegen metadata
stainless-app[bot] Feb 4, 2026
6385631
codegen metadata
stainless-app[bot] Feb 4, 2026
44d9246
codegen metadata
stainless-app[bot] Feb 4, 2026
515312c
codegen metadata
stainless-app[bot] Feb 4, 2026
1ceffcf
codegen metadata
stainless-app[bot] Feb 4, 2026
8d24124
codegen metadata
stainless-app[bot] Feb 4, 2026
60f7563
codegen metadata
stainless-app[bot] Feb 4, 2026
2404e43
codegen metadata
stainless-app[bot] Feb 4, 2026
fcce6da
codegen metadata
stainless-app[bot] Feb 4, 2026
9cb6ee5
codegen metadata
stainless-app[bot] Feb 5, 2026
5869135
codegen metadata
stainless-app[bot] Feb 5, 2026
d0b98cc
codegen metadata
stainless-app[bot] Feb 5, 2026
dbc84c5
codegen metadata
stainless-app[bot] Feb 5, 2026
22de4cb
codegen metadata
stainless-app[bot] Feb 5, 2026
db296f8
codegen metadata
stainless-app[bot] Feb 5, 2026
bad8072
codegen metadata
stainless-app[bot] Feb 5, 2026
0764ea8
codegen metadata
stainless-app[bot] Feb 5, 2026
57138f3
codegen metadata
stainless-app[bot] Feb 5, 2026
f5d560a
codegen metadata
stainless-app[bot] Feb 5, 2026
f4791a4
codegen metadata
stainless-app[bot] Feb 5, 2026
e89cb28
release: 0.9.2
stainless-app[bot] Feb 6, 2026
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
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.9.1"
".": "0.9.2"
}
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 0.9.2 (2026-02-06)

Full Changelog: [v0.9.1...v0.9.2](https://github.com/scaleapi/scale-agentex-python/compare/v0.9.1...v0.9.2)

### Features

* **client:** add custom JSON encoder for extended type support ([a0720ab](https://github.com/scaleapi/scale-agentex-python/commit/a0720abb088583ce4b596e464f7483a4be728e29))


### Bug Fixes

* add litellm retry with exponential backoff for rate limit errors ([ccdb24a](https://github.com/scaleapi/scale-agentex-python/commit/ccdb24a08607298f8dafd748ee9e7fe8ba13d5fe))

## 0.9.1 (2026-01-26)

Full Changelog: [v0.9.0...v0.9.1](https://github.com/scaleapi/scale-agentex-python/compare/v0.9.0...v0.9.1)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "agentex-sdk"
version = "0.9.1"
version = "0.9.2"
description = "The official Python library for the agentex API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
7 changes: 5 additions & 2 deletions src/agentex/_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
APIConnectionError,
APIResponseValidationError,
)
from ._utils._json import openapi_dumps

log: logging.Logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -554,8 +555,10 @@ def _build_request(
kwargs["content"] = options.content
elif isinstance(json_data, bytes):
kwargs["content"] = json_data
else:
kwargs["json"] = json_data if is_given(json_data) else None
elif not files:
# Don't set content when JSON is sent as multipart/form-data,
# since httpx's content param overrides other body arguments
kwargs["content"] = openapi_dumps(json_data) if is_given(json_data) and json_data is not None else None
kwargs["files"] = files
else:
headers.pop("Content-Type", None)
Expand Down
6 changes: 3 additions & 3 deletions src/agentex/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def model_dump(
exclude_defaults: bool = False,
warnings: bool = True,
mode: Literal["json", "python"] = "python",
by_alias: bool | None = None,
) -> dict[str, Any]:
if (not PYDANTIC_V1) or hasattr(model, "model_dump"):
return model.model_dump(
Expand All @@ -148,13 +149,12 @@ def model_dump(
exclude_defaults=exclude_defaults,
# warnings are not supported in Pydantic v1
warnings=True if PYDANTIC_V1 else warnings,
by_alias=by_alias,
)
return cast(
"dict[str, Any]",
model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
exclude=exclude,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias)
),
)

Expand Down
35 changes: 35 additions & 0 deletions src/agentex/_utils/_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import json
from typing import Any
from datetime import datetime
from typing_extensions import override

import pydantic

from .._compat import model_dump


def openapi_dumps(obj: Any) -> bytes:
"""
Serialize an object to UTF-8 encoded JSON bytes.
Extends the standard json.dumps with support for additional types
commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc.
"""
return json.dumps(
obj,
cls=_CustomEncoder,
# Uses the same defaults as httpx's JSON serialization
ensure_ascii=False,
separators=(",", ":"),
allow_nan=False,
).encode()


class _CustomEncoder(json.JSONEncoder):
@override
def default(self, o: Any) -> Any:
if isinstance(o, datetime):
return o.isoformat()
if isinstance(o, pydantic.BaseModel):
return model_dump(o, exclude_unset=True, mode="json", by_alias=True)
return super().default(o)
2 changes: 1 addition & 1 deletion src/agentex/_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__ = "agentex"
__version__ = "0.9.1" # x-release-please-version
__version__ = "0.9.2" # x-release-please-version
126 changes: 126 additions & 0 deletions tests/test_utils/test_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from __future__ import annotations

import datetime
from typing import Union

import pydantic

from agentex import _compat
from agentex._utils._json import openapi_dumps


class TestOpenapiDumps:
def test_basic(self) -> None:
data = {"key": "value", "number": 42}
json_bytes = openapi_dumps(data)
assert json_bytes == b'{"key":"value","number":42}'

def test_datetime_serialization(self) -> None:
dt = datetime.datetime(2023, 1, 1, 12, 0, 0)
data = {"datetime": dt}
json_bytes = openapi_dumps(data)
assert json_bytes == b'{"datetime":"2023-01-01T12:00:00"}'

def test_pydantic_model_serialization(self) -> None:
class User(pydantic.BaseModel):
first_name: str
last_name: str
age: int

model_instance = User(first_name="John", last_name="Kramer", age=83)
data = {"model": model_instance}
json_bytes = openapi_dumps(data)
assert json_bytes == b'{"model":{"first_name":"John","last_name":"Kramer","age":83}}'

def test_pydantic_model_with_default_values(self) -> None:
class User(pydantic.BaseModel):
name: str
role: str = "user"
active: bool = True
score: int = 0

model_instance = User(name="Alice")
data = {"model": model_instance}
json_bytes = openapi_dumps(data)
assert json_bytes == b'{"model":{"name":"Alice"}}'

def test_pydantic_model_with_default_values_overridden(self) -> None:
class User(pydantic.BaseModel):
name: str
role: str = "user"
active: bool = True

model_instance = User(name="Bob", role="admin", active=False)
data = {"model": model_instance}
json_bytes = openapi_dumps(data)
assert json_bytes == b'{"model":{"name":"Bob","role":"admin","active":false}}'

def test_pydantic_model_with_alias(self) -> None:
class User(pydantic.BaseModel):
first_name: str = pydantic.Field(alias="firstName")
last_name: str = pydantic.Field(alias="lastName")

model_instance = User(firstName="John", lastName="Doe")
data = {"model": model_instance}
json_bytes = openapi_dumps(data)
assert json_bytes == b'{"model":{"firstName":"John","lastName":"Doe"}}'

def test_pydantic_model_with_alias_and_default(self) -> None:
class User(pydantic.BaseModel):
user_name: str = pydantic.Field(alias="userName")
user_role: str = pydantic.Field(default="member", alias="userRole")
is_active: bool = pydantic.Field(default=True, alias="isActive")

model_instance = User(userName="charlie")
data = {"model": model_instance}
json_bytes = openapi_dumps(data)
assert json_bytes == b'{"model":{"userName":"charlie"}}'

model_with_overrides = User(userName="diana", userRole="admin", isActive=False)
data = {"model": model_with_overrides}
json_bytes = openapi_dumps(data)
assert json_bytes == b'{"model":{"userName":"diana","userRole":"admin","isActive":false}}'

def test_pydantic_model_with_nested_models_and_defaults(self) -> None:
class Address(pydantic.BaseModel):
street: str
city: str = "Unknown"

class User(pydantic.BaseModel):
name: str
address: Address
verified: bool = False

if _compat.PYDANTIC_V1:
# to handle forward references in Pydantic v1
User.update_forward_refs(**locals()) # type: ignore[reportDeprecated]

address = Address(street="123 Main St")
user = User(name="Diana", address=address)
data = {"user": user}
json_bytes = openapi_dumps(data)
assert json_bytes == b'{"user":{"name":"Diana","address":{"street":"123 Main St"}}}'

address_with_city = Address(street="456 Oak Ave", city="Boston")
user_verified = User(name="Eve", address=address_with_city, verified=True)
data = {"user": user_verified}
json_bytes = openapi_dumps(data)
assert (
json_bytes == b'{"user":{"name":"Eve","address":{"street":"456 Oak Ave","city":"Boston"},"verified":true}}'
)

def test_pydantic_model_with_optional_fields(self) -> None:
class User(pydantic.BaseModel):
name: str
email: Union[str, None]
phone: Union[str, None]

model_with_none = User(name="Eve", email=None, phone=None)
data = {"model": model_with_none}
json_bytes = openapi_dumps(data)
assert json_bytes == b'{"model":{"name":"Eve","email":null,"phone":null}}'

model_with_values = User(name="Frank", email="frank@example.com", phone=None)
data = {"model": model_with_values}
json_bytes = openapi_dumps(data)
assert json_bytes == b'{"model":{"name":"Frank","email":"frank@example.com","phone":null}}'