From 8ca7ff886773539906d2f214892409af7c77bc47 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 7 Nov 2025 17:47:38 +0000
Subject: [PATCH 01/23] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index d521f65..57791d3 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 29
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-0a15ddd7e03addf08468ff36ac294458f86a3a990277a71870e4bc293635bef9.yml
-openapi_spec_hash: 8640228f8a86e5dc464dfa2c8205a2a7
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-46ea61822976f3993310e2c139f133f450b489682d8df4c61b65c731edba8639.yml
+openapi_spec_hash: 8cd802f4d9cdfa000d35792175b3b203
config_hash: 70cdb57c982c578d1961657c07b8b397
From d68084f663f4cf682457a195918d95cce6584772 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 11 Nov 2025 04:49:51 +0000
Subject: [PATCH 02/23] chore(package): drop Python 3.8 support
---
README.md | 4 ++--
pyproject.toml | 5 ++---
src/arcadepy/_utils/_sync.py | 34 +++-------------------------------
3 files changed, 7 insertions(+), 36 deletions(-)
diff --git a/README.md b/README.md
index 0a1de1e..5589a18 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[)](https://pypi.org/project/arcadepy/)
-The Arcade Python library provides convenient access to the Arcade REST API from any Python 3.8+
+The Arcade Python library provides convenient access to the Arcade REST API from any Python 3.9+
application. The library includes type definitions for all request params and response fields,
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
@@ -416,7 +416,7 @@ print(arcadepy.__version__)
## Requirements
-Python 3.8 or higher.
+Python 3.9 or higher.
## Contributing
diff --git a/pyproject.toml b/pyproject.toml
index c588719..fa0ea6e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -15,11 +15,10 @@ dependencies = [
"distro>=1.7.0, <2",
"sniffio",
]
-requires-python = ">= 3.8"
+requires-python = ">= 3.9"
classifiers = [
"Typing :: Typed",
"Intended Audience :: Developers",
- "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
@@ -141,7 +140,7 @@ filterwarnings = [
# there are a couple of flags that are still disabled by
# default in strict mode as they are experimental and niche.
typeCheckingMode = "strict"
-pythonVersion = "3.8"
+pythonVersion = "3.9"
exclude = [
"_dev",
diff --git a/src/arcadepy/_utils/_sync.py b/src/arcadepy/_utils/_sync.py
index ad7ec71..f6027c1 100644
--- a/src/arcadepy/_utils/_sync.py
+++ b/src/arcadepy/_utils/_sync.py
@@ -1,10 +1,8 @@
from __future__ import annotations
-import sys
import asyncio
import functools
-import contextvars
-from typing import Any, TypeVar, Callable, Awaitable
+from typing import TypeVar, Callable, Awaitable
from typing_extensions import ParamSpec
import anyio
@@ -15,34 +13,11 @@
T_ParamSpec = ParamSpec("T_ParamSpec")
-if sys.version_info >= (3, 9):
- _asyncio_to_thread = asyncio.to_thread
-else:
- # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread
- # for Python 3.8 support
- async def _asyncio_to_thread(
- func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
- ) -> Any:
- """Asynchronously run function *func* in a separate thread.
-
- Any *args and **kwargs supplied for this function are directly passed
- to *func*. Also, the current :class:`contextvars.Context` is propagated,
- allowing context variables from the main thread to be accessed in the
- separate thread.
-
- Returns a coroutine that can be awaited to get the eventual result of *func*.
- """
- loop = asyncio.events.get_running_loop()
- ctx = contextvars.copy_context()
- func_call = functools.partial(ctx.run, func, *args, **kwargs)
- return await loop.run_in_executor(None, func_call)
-
-
async def to_thread(
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
) -> T_Retval:
if sniffio.current_async_library() == "asyncio":
- return await _asyncio_to_thread(func, *args, **kwargs)
+ return await asyncio.to_thread(func, *args, **kwargs)
return await anyio.to_thread.run_sync(
functools.partial(func, *args, **kwargs),
@@ -53,10 +28,7 @@ async def to_thread(
def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]:
"""
Take a blocking function and create an async one that receives the same
- positional and keyword arguments. For python version 3.9 and above, it uses
- asyncio.to_thread to run the function in a separate thread. For python version
- 3.8, it uses locally defined copy of the asyncio.to_thread function which was
- introduced in python 3.9.
+ positional and keyword arguments.
Usage:
From 4453f62ffe78f5bf6ec84c89566a2581ef381cca Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 11 Nov 2025 04:50:28 +0000
Subject: [PATCH 03/23] fix: compat with Python 3.14
---
src/arcadepy/_models.py | 11 ++++++++---
tests/test_models.py | 8 ++++----
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/src/arcadepy/_models.py b/src/arcadepy/_models.py
index 6a3cd1d..fcec2cf 100644
--- a/src/arcadepy/_models.py
+++ b/src/arcadepy/_models.py
@@ -2,6 +2,7 @@
import os
import inspect
+import weakref
from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast
from datetime import date, datetime
from typing_extensions import (
@@ -573,6 +574,9 @@ class CachedDiscriminatorType(Protocol):
__discriminator__: DiscriminatorDetails
+DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary()
+
+
class DiscriminatorDetails:
field_name: str
"""The name of the discriminator field in the variant class, e.g.
@@ -615,8 +619,9 @@ def __init__(
def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None:
- if isinstance(union, CachedDiscriminatorType):
- return union.__discriminator__
+ cached = DISCRIMINATOR_CACHE.get(union)
+ if cached is not None:
+ return cached
discriminator_field_name: str | None = None
@@ -669,7 +674,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
discriminator_field=discriminator_field_name,
discriminator_alias=discriminator_alias,
)
- cast(CachedDiscriminatorType, union).__discriminator__ = details
+ DISCRIMINATOR_CACHE.setdefault(union, details)
return details
diff --git a/tests/test_models.py b/tests/test_models.py
index 202c517..62c67d8 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -9,7 +9,7 @@
from arcadepy._utils import PropertyInfo
from arcadepy._compat import PYDANTIC_V1, parse_obj, model_dump, model_json
-from arcadepy._models import BaseModel, construct_type
+from arcadepy._models import DISCRIMINATOR_CACHE, BaseModel, construct_type
class BasicModel(BaseModel):
@@ -809,7 +809,7 @@ class B(BaseModel):
UnionType = cast(Any, Union[A, B])
- assert not hasattr(UnionType, "__discriminator__")
+ assert not DISCRIMINATOR_CACHE.get(UnionType)
m = construct_type(
value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")])
@@ -818,7 +818,7 @@ class B(BaseModel):
assert m.type == "b"
assert m.data == "foo" # type: ignore[comparison-overlap]
- discriminator = UnionType.__discriminator__
+ discriminator = DISCRIMINATOR_CACHE.get(UnionType)
assert discriminator is not None
m = construct_type(
@@ -830,7 +830,7 @@ class B(BaseModel):
# if the discriminator details object stays the same between invocations then
# we hit the cache
- assert UnionType.__discriminator__ is discriminator
+ assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator
@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1")
From d4527cf2a0ab18f31c0b75d6b452a09f47bd15f5 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 12 Nov 2025 04:33:03 +0000
Subject: [PATCH 04/23] fix(compat): update signatures of `model_dump` and
`model_dump_json` for Pydantic v1
---
src/arcadepy/_models.py | 41 +++++++++++++++++++++++++++++------------
1 file changed, 29 insertions(+), 12 deletions(-)
diff --git a/src/arcadepy/_models.py b/src/arcadepy/_models.py
index fcec2cf..ca9500b 100644
--- a/src/arcadepy/_models.py
+++ b/src/arcadepy/_models.py
@@ -257,15 +257,16 @@ def model_dump(
mode: Literal["json", "python"] | str = "python",
include: IncEx | None = None,
exclude: IncEx | None = None,
+ context: Any | None = None,
by_alias: bool | None = None,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
+ exclude_computed_fields: bool = False,
round_trip: bool = False,
warnings: bool | Literal["none", "warn", "error"] = True,
- context: dict[str, Any] | None = None,
- serialize_as_any: bool = False,
fallback: Callable[[Any], Any] | None = None,
+ serialize_as_any: bool = False,
) -> dict[str, Any]:
"""Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump
@@ -273,16 +274,24 @@ def model_dump(
Args:
mode: The mode in which `to_python` should run.
- If mode is 'json', the dictionary will only contain JSON serializable types.
- If mode is 'python', the dictionary may contain any Python objects.
- include: A list of fields to include in the output.
- exclude: A list of fields to exclude from the output.
+ If mode is 'json', the output will only contain JSON serializable types.
+ If mode is 'python', the output may contain non-JSON-serializable Python objects.
+ include: A set of fields to include in the output.
+ exclude: A set of fields to exclude from the output.
+ context: Additional context to pass to the serializer.
by_alias: Whether to use the field's alias in the dictionary key if defined.
- exclude_unset: Whether to exclude fields that are unset or None from the output.
- exclude_defaults: Whether to exclude fields that are set to their default value from the output.
- exclude_none: Whether to exclude fields that have a value of `None` from the output.
- round_trip: Whether to enable serialization and deserialization round-trip support.
- warnings: Whether to log warnings when invalid fields are encountered.
+ exclude_unset: Whether to exclude fields that have not been explicitly set.
+ exclude_defaults: Whether to exclude fields that are set to their default value.
+ exclude_none: Whether to exclude fields that have a value of `None`.
+ exclude_computed_fields: Whether to exclude computed fields.
+ While this can be useful for round-tripping, it is usually recommended to use the dedicated
+ `round_trip` parameter instead.
+ round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
+ warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,
+ "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
+ fallback: A function to call when an unknown value is encountered. If not provided,
+ a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
+ serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
Returns:
A dictionary representation of the model.
@@ -299,6 +308,8 @@ def model_dump(
raise ValueError("serialize_as_any is only supported in Pydantic v2")
if fallback is not None:
raise ValueError("fallback is only supported in Pydantic v2")
+ if exclude_computed_fields != False:
+ raise ValueError("exclude_computed_fields is only supported in Pydantic v2")
dumped = super().dict( # pyright: ignore[reportDeprecated]
include=include,
exclude=exclude,
@@ -315,15 +326,17 @@ def model_dump_json(
self,
*,
indent: int | None = None,
+ ensure_ascii: bool = False,
include: IncEx | None = None,
exclude: IncEx | None = None,
+ context: Any | None = None,
by_alias: bool | None = None,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
+ exclude_computed_fields: bool = False,
round_trip: bool = False,
warnings: bool | Literal["none", "warn", "error"] = True,
- context: dict[str, Any] | None = None,
fallback: Callable[[Any], Any] | None = None,
serialize_as_any: bool = False,
) -> str:
@@ -355,6 +368,10 @@ def model_dump_json(
raise ValueError("serialize_as_any is only supported in Pydantic v2")
if fallback is not None:
raise ValueError("fallback is only supported in Pydantic v2")
+ if ensure_ascii != False:
+ raise ValueError("ensure_ascii is only supported in Pydantic v2")
+ if exclude_computed_fields != False:
+ raise ValueError("exclude_computed_fields is only supported in Pydantic v2")
return super().json( # type: ignore[reportDeprecated]
indent=indent,
include=include,
From 8c6d5c5ace8884839590c11a0b72f9ea70731e0e Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sat, 22 Nov 2025 04:15:50 +0000
Subject: [PATCH 05/23] chore: add Python 3.14 classifier and testing
---
pyproject.toml | 1 +
1 file changed, 1 insertion(+)
diff --git a/pyproject.toml b/pyproject.toml
index fa0ea6e..864da50 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -24,6 +24,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
"Operating System :: OS Independent",
"Operating System :: POSIX",
"Operating System :: MacOS",
From e4be19ee70952d16361d55acfc082875bd926c01 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 28 Nov 2025 03:27:15 +0000
Subject: [PATCH 06/23] fix: ensure streams are always closed
---
src/arcadepy/_streaming.py | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/src/arcadepy/_streaming.py b/src/arcadepy/_streaming.py
index 6bf52e7..d539a62 100644
--- a/src/arcadepy/_streaming.py
+++ b/src/arcadepy/_streaming.py
@@ -54,11 +54,12 @@ def __stream__(self) -> Iterator[_T]:
process_data = self._client._process_response_data
iterator = self._iter_events()
- for sse in iterator:
- yield process_data(data=sse.json(), cast_to=cast_to, response=response)
-
- # As we might not fully consume the response stream, we need to close it explicitly
- response.close()
+ try:
+ for sse in iterator:
+ yield process_data(data=sse.json(), cast_to=cast_to, response=response)
+ finally:
+ # Ensure the response is closed even if the consumer doesn't read all data
+ response.close()
def __enter__(self) -> Self:
return self
@@ -117,11 +118,12 @@ async def __stream__(self) -> AsyncIterator[_T]:
process_data = self._client._process_response_data
iterator = self._iter_events()
- async for sse in iterator:
- yield process_data(data=sse.json(), cast_to=cast_to, response=response)
-
- # As we might not fully consume the response stream, we need to close it explicitly
- await response.aclose()
+ try:
+ async for sse in iterator:
+ yield process_data(data=sse.json(), cast_to=cast_to, response=response)
+ finally:
+ # Ensure the response is closed even if the consumer doesn't read all data
+ await response.aclose()
async def __aenter__(self) -> Self:
return self
From 0c9e3402e6714d2c1e4273ff5bd0e2dc00cce7b2 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 28 Nov 2025 03:28:21 +0000
Subject: [PATCH 07/23] chore(deps): mypy 1.18.1 has a regression, pin to 1.17
---
pyproject.toml | 2 +-
requirements-dev.lock | 4 +++-
requirements.lock | 8 ++++----
3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 864da50..4ca4f40 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -46,7 +46,7 @@ managed = true
# version pins are in requirements-dev.lock
dev-dependencies = [
"pyright==1.1.399",
- "mypy",
+ "mypy==1.17",
"respx",
"pytest",
"pytest-asyncio",
diff --git a/requirements-dev.lock b/requirements-dev.lock
index 3c2bd65..2b8f8f0 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -72,7 +72,7 @@ mdurl==0.1.2
multidict==6.4.4
# via aiohttp
# via yarl
-mypy==1.14.1
+mypy==1.17.0
mypy-extensions==1.0.0
# via mypy
nodeenv==1.8.0
@@ -81,6 +81,8 @@ nox==2023.4.22
packaging==23.2
# via nox
# via pytest
+pathspec==0.12.1
+ # via mypy
platformdirs==3.11.0
# via virtualenv
pluggy==1.5.0
diff --git a/requirements.lock b/requirements.lock
index b5a9719..e946d7e 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -55,21 +55,21 @@ multidict==6.4.4
propcache==0.3.1
# via aiohttp
# via yarl
-pydantic==2.11.9
+pydantic==2.12.5
# via arcadepy
-pydantic-core==2.33.2
+pydantic-core==2.41.5
# via pydantic
sniffio==1.3.0
# via anyio
# via arcadepy
-typing-extensions==4.12.2
+typing-extensions==4.15.0
# via anyio
# via arcadepy
# via multidict
# via pydantic
# via pydantic-core
# via typing-inspection
-typing-inspection==0.4.1
+typing-inspection==0.4.2
# via pydantic
yarl==1.20.0
# via aiohttp
From d0dae695284c4ee2873529f0e24034dc409e99e7 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 3 Dec 2025 05:36:26 +0000
Subject: [PATCH 08/23] chore: update lockfile
---
pyproject.toml | 14 +++---
requirements-dev.lock | 108 +++++++++++++++++++++++-------------------
requirements.lock | 31 ++++++------
3 files changed, 83 insertions(+), 70 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 4ca4f40..ce26232 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,14 +7,16 @@ license = "MIT"
authors = [
{ name = "Arcade", email = "dev@arcade.dev" },
]
+
dependencies = [
- "httpx>=0.23.0, <1",
- "pydantic>=1.9.0, <3",
- "typing-extensions>=4.10, <5",
- "anyio>=3.5.0, <5",
- "distro>=1.7.0, <2",
- "sniffio",
+ "httpx>=0.23.0, <1",
+ "pydantic>=1.9.0, <3",
+ "typing-extensions>=4.10, <5",
+ "anyio>=3.5.0, <5",
+ "distro>=1.7.0, <2",
+ "sniffio",
]
+
requires-python = ">= 3.9"
classifiers = [
"Typing :: Typed",
diff --git a/requirements-dev.lock b/requirements-dev.lock
index 2b8f8f0..cb9974b 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -12,40 +12,45 @@
-e file:.
aiohappyeyeballs==2.6.1
# via aiohttp
-aiohttp==3.12.8
+aiohttp==3.13.2
# via arcadepy
# via httpx-aiohttp
-aiosignal==1.3.2
+aiosignal==1.4.0
# via aiohttp
-annotated-types==0.6.0
+annotated-types==0.7.0
# via pydantic
-anyio==4.4.0
+anyio==4.12.0
# via arcadepy
# via httpx
-argcomplete==3.1.2
+argcomplete==3.6.3
# via nox
async-timeout==5.0.1
# via aiohttp
-attrs==25.3.0
+attrs==25.4.0
# via aiohttp
-certifi==2023.7.22
+ # via nox
+backports-asyncio-runner==1.2.0
+ # via pytest-asyncio
+certifi==2025.11.12
# via httpcore
# via httpx
-colorlog==6.7.0
+colorlog==6.10.1
+ # via nox
+dependency-groups==1.3.1
# via nox
-dirty-equals==0.6.0
-distlib==0.3.7
+dirty-equals==0.11
+distlib==0.4.0
# via virtualenv
-distro==1.8.0
+distro==1.9.0
# via arcadepy
-exceptiongroup==1.2.2
+exceptiongroup==1.3.1
# via anyio
# via pytest
-execnet==2.1.1
+execnet==2.1.2
# via pytest-xdist
-filelock==3.12.4
+filelock==3.19.1
# via virtualenv
-frozenlist==1.6.2
+frozenlist==1.8.0
# via aiohttp
# via aiosignal
h11==0.16.0
@@ -58,82 +63,87 @@ httpx==0.28.1
# via respx
httpx-aiohttp==0.1.9
# via arcadepy
-idna==3.4
+humanize==4.13.0
+ # via nox
+idna==3.11
# via anyio
# via httpx
# via yarl
-importlib-metadata==7.0.0
-iniconfig==2.0.0
+importlib-metadata==8.7.0
+iniconfig==2.1.0
# via pytest
markdown-it-py==3.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
-multidict==6.4.4
+multidict==6.7.0
# via aiohttp
# via yarl
mypy==1.17.0
-mypy-extensions==1.0.0
+mypy-extensions==1.1.0
# via mypy
-nodeenv==1.8.0
+nodeenv==1.9.1
# via pyright
-nox==2023.4.22
-packaging==23.2
+nox==2025.11.12
+packaging==25.0
+ # via dependency-groups
# via nox
# via pytest
pathspec==0.12.1
# via mypy
-platformdirs==3.11.0
+platformdirs==4.4.0
# via virtualenv
-pluggy==1.5.0
+pluggy==1.6.0
# via pytest
-propcache==0.3.1
+propcache==0.4.1
# via aiohttp
# via yarl
-pydantic==2.11.9
+pydantic==2.12.5
# via arcadepy
-pydantic-core==2.33.2
+pydantic-core==2.41.5
# via pydantic
-pygments==2.18.0
+pygments==2.19.2
+ # via pytest
# via rich
pyright==1.1.399
-pytest==8.3.3
+pytest==8.4.2
# via pytest-asyncio
# via pytest-xdist
-pytest-asyncio==0.24.0
-pytest-xdist==3.7.0
-python-dateutil==2.8.2
+pytest-asyncio==1.2.0
+pytest-xdist==3.8.0
+python-dateutil==2.9.0.post0
# via time-machine
-pytz==2023.3.post1
- # via dirty-equals
respx==0.22.0
-rich==13.7.1
-ruff==0.9.4
-setuptools==68.2.2
- # via nodeenv
-six==1.16.0
+rich==14.2.0
+ruff==0.14.7
+six==1.17.0
# via python-dateutil
-sniffio==1.3.0
- # via anyio
+sniffio==1.3.1
# via arcadepy
-time-machine==2.9.0
-tomli==2.0.2
+time-machine==2.19.0
+tomli==2.3.0
+ # via dependency-groups
# via mypy
+ # via nox
# via pytest
-typing-extensions==4.12.2
+typing-extensions==4.15.0
+ # via aiosignal
# via anyio
# via arcadepy
+ # via exceptiongroup
# via multidict
# via mypy
# via pydantic
# via pydantic-core
# via pyright
+ # via pytest-asyncio
# via typing-inspection
-typing-inspection==0.4.1
+ # via virtualenv
+typing-inspection==0.4.2
# via pydantic
-virtualenv==20.24.5
+virtualenv==20.35.4
# via nox
-yarl==1.20.0
+yarl==1.22.0
# via aiohttp
-zipp==3.17.0
+zipp==3.23.0
# via importlib-metadata
diff --git a/requirements.lock b/requirements.lock
index e946d7e..9c47af4 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -12,28 +12,28 @@
-e file:.
aiohappyeyeballs==2.6.1
# via aiohttp
-aiohttp==3.12.8
+aiohttp==3.13.2
# via arcadepy
# via httpx-aiohttp
-aiosignal==1.3.2
+aiosignal==1.4.0
# via aiohttp
-annotated-types==0.6.0
+annotated-types==0.7.0
# via pydantic
-anyio==4.4.0
+anyio==4.12.0
# via arcadepy
# via httpx
async-timeout==5.0.1
# via aiohttp
-attrs==25.3.0
+attrs==25.4.0
# via aiohttp
-certifi==2023.7.22
+certifi==2025.11.12
# via httpcore
# via httpx
-distro==1.8.0
+distro==1.9.0
# via arcadepy
-exceptiongroup==1.2.2
+exceptiongroup==1.3.1
# via anyio
-frozenlist==1.6.2
+frozenlist==1.8.0
# via aiohttp
# via aiosignal
h11==0.16.0
@@ -45,31 +45,32 @@ httpx==0.28.1
# via httpx-aiohttp
httpx-aiohttp==0.1.9
# via arcadepy
-idna==3.4
+idna==3.11
# via anyio
# via httpx
# via yarl
-multidict==6.4.4
+multidict==6.7.0
# via aiohttp
# via yarl
-propcache==0.3.1
+propcache==0.4.1
# via aiohttp
# via yarl
pydantic==2.12.5
# via arcadepy
pydantic-core==2.41.5
# via pydantic
-sniffio==1.3.0
- # via anyio
+sniffio==1.3.1
# via arcadepy
typing-extensions==4.15.0
+ # via aiosignal
# via anyio
# via arcadepy
+ # via exceptiongroup
# via multidict
# via pydantic
# via pydantic-core
# via typing-inspection
typing-inspection==0.4.2
# via pydantic
-yarl==1.20.0
+yarl==1.22.0
# via aiohttp
From de0cde1a836d5b1992d8cdc284c3decbfa8eb20b Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 3 Dec 2025 05:44:11 +0000
Subject: [PATCH 09/23] chore(docs): use environment variables for
authentication in code snippets
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 5589a18..80f0d66 100644
--- a/README.md
+++ b/README.md
@@ -87,6 +87,7 @@ pip install arcadepy[aiohttp]
Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
```python
+import os
import asyncio
from arcadepy import DefaultAioHttpClient
from arcadepy import AsyncArcade
@@ -94,7 +95,7 @@ from arcadepy import AsyncArcade
async def main() -> None:
async with AsyncArcade(
- api_key="My API Key",
+ api_key=os.environ.get("ARCADE_API_KEY"), # This is the default and can be omitted
http_client=DefaultAioHttpClient(),
) as client:
execute_tool_response = await client.tools.execute(
From 60e16d2e59496bcf64c28328e3a351925b82dccd Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 3 Dec 2025 23:27:57 +0000
Subject: [PATCH 10/23] feat(api): api update
---
.stats.yml | 4 ++--
src/arcadepy/resources/tools/formatted.py | 8 ++++++++
src/arcadepy/resources/tools/tools.py | 8 ++++++++
src/arcadepy/types/tool_list_params.py | 3 +++
src/arcadepy/types/tools/formatted_list_params.py | 3 +++
tests/api_resources/test_tools.py | 2 ++
tests/api_resources/tools/test_formatted.py | 2 ++
7 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 57791d3..104433a 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 29
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-46ea61822976f3993310e2c139f133f450b489682d8df4c61b65c731edba8639.yml
-openapi_spec_hash: 8cd802f4d9cdfa000d35792175b3b203
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-4dc4e58ef402ce5362e0a8988b3928a8bfa0d5ba847f7ad8b14226a0cf282f28.yml
+openapi_spec_hash: fb72b9121306240c419669a3d42e45f6
config_hash: 70cdb57c982c578d1961657c07b8b397
diff --git a/src/arcadepy/resources/tools/formatted.py b/src/arcadepy/resources/tools/formatted.py
index 4b85016..3705e20 100644
--- a/src/arcadepy/resources/tools/formatted.py
+++ b/src/arcadepy/resources/tools/formatted.py
@@ -45,6 +45,7 @@ def list(
self,
*,
format: str | Omit = omit,
+ include_all_versions: bool | Omit = omit,
limit: int | Omit = omit,
offset: int | Omit = omit,
toolkit: str | Omit = omit,
@@ -63,6 +64,8 @@ def list(
Args:
format: Provider format
+ include_all_versions: Include all versions of each tool
+
limit: Number of items to return (default: 25, max: 100)
offset: Offset from the start of the list (default: 0)
@@ -90,6 +93,7 @@ def list(
query=maybe_transform(
{
"format": format,
+ "include_all_versions": include_all_versions,
"limit": limit,
"offset": offset,
"toolkit": toolkit,
@@ -175,6 +179,7 @@ def list(
self,
*,
format: str | Omit = omit,
+ include_all_versions: bool | Omit = omit,
limit: int | Omit = omit,
offset: int | Omit = omit,
toolkit: str | Omit = omit,
@@ -193,6 +198,8 @@ def list(
Args:
format: Provider format
+ include_all_versions: Include all versions of each tool
+
limit: Number of items to return (default: 25, max: 100)
offset: Offset from the start of the list (default: 0)
@@ -220,6 +227,7 @@ def list(
query=maybe_transform(
{
"format": format,
+ "include_all_versions": include_all_versions,
"limit": limit,
"offset": offset,
"toolkit": toolkit,
diff --git a/src/arcadepy/resources/tools/tools.py b/src/arcadepy/resources/tools/tools.py
index 30aec45..e45a77c 100644
--- a/src/arcadepy/resources/tools/tools.py
+++ b/src/arcadepy/resources/tools/tools.py
@@ -74,6 +74,7 @@ def with_streaming_response(self) -> ToolsResourceWithStreamingResponse:
def list(
self,
*,
+ include_all_versions: bool | Omit = omit,
include_format: List[Literal["arcade", "openai", "anthropic"]] | Omit = omit,
limit: int | Omit = omit,
offset: int | Omit = omit,
@@ -91,6 +92,8 @@ def list(
toolkit
Args:
+ include_all_versions: Include all versions of each tool
+
include_format: Comma separated tool formats that will be included in the response.
limit: Number of items to return (default: 25, max: 100)
@@ -119,6 +122,7 @@ def list(
timeout=timeout,
query=maybe_transform(
{
+ "include_all_versions": include_all_versions,
"include_format": include_format,
"limit": limit,
"offset": offset,
@@ -319,6 +323,7 @@ def with_streaming_response(self) -> AsyncToolsResourceWithStreamingResponse:
def list(
self,
*,
+ include_all_versions: bool | Omit = omit,
include_format: List[Literal["arcade", "openai", "anthropic"]] | Omit = omit,
limit: int | Omit = omit,
offset: int | Omit = omit,
@@ -336,6 +341,8 @@ def list(
toolkit
Args:
+ include_all_versions: Include all versions of each tool
+
include_format: Comma separated tool formats that will be included in the response.
limit: Number of items to return (default: 25, max: 100)
@@ -364,6 +371,7 @@ def list(
timeout=timeout,
query=maybe_transform(
{
+ "include_all_versions": include_all_versions,
"include_format": include_format,
"limit": limit,
"offset": offset,
diff --git a/src/arcadepy/types/tool_list_params.py b/src/arcadepy/types/tool_list_params.py
index 6a68b48..7984944 100644
--- a/src/arcadepy/types/tool_list_params.py
+++ b/src/arcadepy/types/tool_list_params.py
@@ -9,6 +9,9 @@
class ToolListParams(TypedDict, total=False):
+ include_all_versions: bool
+ """Include all versions of each tool"""
+
include_format: List[Literal["arcade", "openai", "anthropic"]]
"""Comma separated tool formats that will be included in the response."""
diff --git a/src/arcadepy/types/tools/formatted_list_params.py b/src/arcadepy/types/tools/formatted_list_params.py
index e9e53fd..97790de 100644
--- a/src/arcadepy/types/tools/formatted_list_params.py
+++ b/src/arcadepy/types/tools/formatted_list_params.py
@@ -11,6 +11,9 @@ class FormattedListParams(TypedDict, total=False):
format: str
"""Provider format"""
+ include_all_versions: bool
+ """Include all versions of each tool"""
+
limit: int
"""Number of items to return (default: 25, max: 100)"""
diff --git a/tests/api_resources/test_tools.py b/tests/api_resources/test_tools.py
index d3d1cb6..364d216 100644
--- a/tests/api_resources/test_tools.py
+++ b/tests/api_resources/test_tools.py
@@ -30,6 +30,7 @@ def test_method_list(self, client: Arcade) -> None:
@parametrize
def test_method_list_with_all_params(self, client: Arcade) -> None:
tool = client.tools.list(
+ include_all_versions=True,
include_format=["arcade"],
limit=0,
offset=0,
@@ -203,6 +204,7 @@ async def test_method_list(self, async_client: AsyncArcade) -> None:
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncArcade) -> None:
tool = await async_client.tools.list(
+ include_all_versions=True,
include_format=["arcade"],
limit=0,
offset=0,
diff --git a/tests/api_resources/tools/test_formatted.py b/tests/api_resources/tools/test_formatted.py
index 0ab89e6..271a233 100644
--- a/tests/api_resources/tools/test_formatted.py
+++ b/tests/api_resources/tools/test_formatted.py
@@ -26,6 +26,7 @@ def test_method_list(self, client: Arcade) -> None:
def test_method_list_with_all_params(self, client: Arcade) -> None:
formatted = client.tools.formatted.list(
format="format",
+ include_all_versions=True,
limit=0,
offset=0,
toolkit="toolkit",
@@ -115,6 +116,7 @@ async def test_method_list(self, async_client: AsyncArcade) -> None:
async def test_method_list_with_all_params(self, async_client: AsyncArcade) -> None:
formatted = await async_client.tools.formatted.list(
format="format",
+ include_all_versions=True,
limit=0,
offset=0,
toolkit="toolkit",
From 6c633443c6c61f409e1092693a30b5e4e2e31edd Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 8 Dec 2025 15:38:48 +0000
Subject: [PATCH 11/23] codegen metadata
---
.stats.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index 104433a..4510020 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 29
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-4dc4e58ef402ce5362e0a8988b3928a8bfa0d5ba847f7ad8b14226a0cf282f28.yml
openapi_spec_hash: fb72b9121306240c419669a3d42e45f6
-config_hash: 70cdb57c982c578d1961657c07b8b397
+config_hash: f0d78fdab30e3346ae9b6804632ae8b6
From f98c6d3ccf44a38525f056bc52011fbdcc3183c1 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 8 Dec 2025 17:44:52 +0000
Subject: [PATCH 12/23] feat(api): api update
---
.stats.yml | 4 +-
api.md | 1 +
src/arcadepy/resources/admin/secrets.py | 101 ++++++++++++++++-
src/arcadepy/types/admin/__init__.py | 1 +
.../types/admin/secret_create_params.py | 13 +++
tests/api_resources/admin/test_secrets.py | 104 +++++++++++++++++-
6 files changed, 220 insertions(+), 4 deletions(-)
create mode 100644 src/arcadepy/types/admin/secret_create_params.py
diff --git a/.stats.yml b/.stats.yml
index 4510020..09786b4 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 29
+configured_endpoints: 30
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-4dc4e58ef402ce5362e0a8988b3928a8bfa0d5ba847f7ad8b14226a0cf282f28.yml
openapi_spec_hash: fb72b9121306240c419669a3d42e45f6
-config_hash: f0d78fdab30e3346ae9b6804632ae8b6
+config_hash: 7d1d98a4a1938a6884fa81ccfa3184c5
diff --git a/api.md b/api.md
index 55a932d..61b6e13 100644
--- a/api.md
+++ b/api.md
@@ -50,6 +50,7 @@ from arcadepy.types.admin import SecretResponse, SecretListResponse
Methods:
+- client.admin.secrets.create(secret_key, \*\*params) -> SecretResponse
- client.admin.secrets.list() -> SecretListResponse
- client.admin.secrets.delete(secret_id) -> None
diff --git a/src/arcadepy/resources/admin/secrets.py b/src/arcadepy/resources/admin/secrets.py
index ac839d9..b3d55f5 100644
--- a/src/arcadepy/resources/admin/secrets.py
+++ b/src/arcadepy/resources/admin/secrets.py
@@ -4,7 +4,8 @@
import httpx
-from ..._types import Body, Query, Headers, NoneType, NotGiven, not_given
+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 (
@@ -13,7 +14,9 @@
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
+from ...types.admin import secret_create_params
from ..._base_client import make_request_options
+from ...types.admin.secret_response import SecretResponse
from ...types.admin.secret_list_response import SecretListResponse
__all__ = ["SecretsResource", "AsyncSecretsResource"]
@@ -39,6 +42,48 @@ def with_streaming_response(self) -> SecretsResourceWithStreamingResponse:
"""
return SecretsResourceWithStreamingResponse(self)
+ def create(
+ self,
+ secret_key: str,
+ *,
+ value: str,
+ description: 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,
+ ) -> SecretResponse:
+ """
+ Create or update a secret
+
+ 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 secret_key:
+ raise ValueError(f"Expected a non-empty value for `secret_key` but received {secret_key!r}")
+ return self._post(
+ f"/v1/admin/secrets/{secret_key}",
+ body=maybe_transform(
+ {
+ "value": value,
+ "description": description,
+ },
+ secret_create_params.SecretCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=SecretResponse,
+ )
+
def list(
self,
*,
@@ -113,6 +158,48 @@ def with_streaming_response(self) -> AsyncSecretsResourceWithStreamingResponse:
"""
return AsyncSecretsResourceWithStreamingResponse(self)
+ async def create(
+ self,
+ secret_key: str,
+ *,
+ value: str,
+ description: 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,
+ ) -> SecretResponse:
+ """
+ Create or update a secret
+
+ 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 secret_key:
+ raise ValueError(f"Expected a non-empty value for `secret_key` but received {secret_key!r}")
+ return await self._post(
+ f"/v1/admin/secrets/{secret_key}",
+ body=await async_maybe_transform(
+ {
+ "value": value,
+ "description": description,
+ },
+ secret_create_params.SecretCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=SecretResponse,
+ )
+
async def list(
self,
*,
@@ -171,6 +258,9 @@ class SecretsResourceWithRawResponse:
def __init__(self, secrets: SecretsResource) -> None:
self._secrets = secrets
+ self.create = to_raw_response_wrapper(
+ secrets.create,
+ )
self.list = to_raw_response_wrapper(
secrets.list,
)
@@ -183,6 +273,9 @@ class AsyncSecretsResourceWithRawResponse:
def __init__(self, secrets: AsyncSecretsResource) -> None:
self._secrets = secrets
+ self.create = async_to_raw_response_wrapper(
+ secrets.create,
+ )
self.list = async_to_raw_response_wrapper(
secrets.list,
)
@@ -195,6 +288,9 @@ class SecretsResourceWithStreamingResponse:
def __init__(self, secrets: SecretsResource) -> None:
self._secrets = secrets
+ self.create = to_streamed_response_wrapper(
+ secrets.create,
+ )
self.list = to_streamed_response_wrapper(
secrets.list,
)
@@ -207,6 +303,9 @@ class AsyncSecretsResourceWithStreamingResponse:
def __init__(self, secrets: AsyncSecretsResource) -> None:
self._secrets = secrets
+ self.create = async_to_streamed_response_wrapper(
+ secrets.create,
+ )
self.list = async_to_streamed_response_wrapper(
secrets.list,
)
diff --git a/src/arcadepy/types/admin/__init__.py b/src/arcadepy/types/admin/__init__.py
index 04e75d9..637035f 100644
--- a/src/arcadepy/types/admin/__init__.py
+++ b/src/arcadepy/types/admin/__init__.py
@@ -3,6 +3,7 @@
from __future__ import annotations
from .secret_response import SecretResponse as SecretResponse
+from .secret_create_params import SecretCreateParams as SecretCreateParams
from .secret_list_response import SecretListResponse as SecretListResponse
from .auth_provider_response import AuthProviderResponse as AuthProviderResponse
from .user_connection_response import UserConnectionResponse as UserConnectionResponse
diff --git a/src/arcadepy/types/admin/secret_create_params.py b/src/arcadepy/types/admin/secret_create_params.py
new file mode 100644
index 0000000..2986cbf
--- /dev/null
+++ b/src/arcadepy/types/admin/secret_create_params.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["SecretCreateParams"]
+
+
+class SecretCreateParams(TypedDict, total=False):
+ value: Required[str]
+
+ description: str
diff --git a/tests/api_resources/admin/test_secrets.py b/tests/api_resources/admin/test_secrets.py
index 3afbce3..f5e71b6 100644
--- a/tests/api_resources/admin/test_secrets.py
+++ b/tests/api_resources/admin/test_secrets.py
@@ -9,7 +9,7 @@
from arcadepy import Arcade, AsyncArcade
from tests.utils import assert_matches_type
-from arcadepy.types.admin import SecretListResponse
+from arcadepy.types.admin import SecretResponse, SecretListResponse
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -17,6 +17,57 @@
class TestSecrets:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @parametrize
+ def test_method_create(self, client: Arcade) -> None:
+ secret = client.admin.secrets.create(
+ secret_key="secret_key",
+ value="value",
+ )
+ assert_matches_type(SecretResponse, secret, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: Arcade) -> None:
+ secret = client.admin.secrets.create(
+ secret_key="secret_key",
+ value="value",
+ description="description",
+ )
+ assert_matches_type(SecretResponse, secret, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Arcade) -> None:
+ response = client.admin.secrets.with_raw_response.create(
+ secret_key="secret_key",
+ value="value",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ secret = response.parse()
+ assert_matches_type(SecretResponse, secret, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Arcade) -> None:
+ with client.admin.secrets.with_streaming_response.create(
+ secret_key="secret_key",
+ value="value",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ secret = response.parse()
+ assert_matches_type(SecretResponse, secret, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: Arcade) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `secret_key` but received ''"):
+ client.admin.secrets.with_raw_response.create(
+ secret_key="",
+ value="value",
+ )
+
@parametrize
def test_method_list(self, client: Arcade) -> None:
secret = client.admin.secrets.list()
@@ -86,6 +137,57 @@ class TestAsyncSecrets:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
+ @parametrize
+ async def test_method_create(self, async_client: AsyncArcade) -> None:
+ secret = await async_client.admin.secrets.create(
+ secret_key="secret_key",
+ value="value",
+ )
+ assert_matches_type(SecretResponse, secret, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncArcade) -> None:
+ secret = await async_client.admin.secrets.create(
+ secret_key="secret_key",
+ value="value",
+ description="description",
+ )
+ assert_matches_type(SecretResponse, secret, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncArcade) -> None:
+ response = await async_client.admin.secrets.with_raw_response.create(
+ secret_key="secret_key",
+ value="value",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ secret = await response.parse()
+ assert_matches_type(SecretResponse, secret, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncArcade) -> None:
+ async with async_client.admin.secrets.with_streaming_response.create(
+ secret_key="secret_key",
+ value="value",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ secret = await response.parse()
+ assert_matches_type(SecretResponse, secret, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncArcade) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `secret_key` but received ''"):
+ await async_client.admin.secrets.with_raw_response.create(
+ secret_key="",
+ value="value",
+ )
+
@parametrize
async def test_method_list(self, async_client: AsyncArcade) -> None:
secret = await async_client.admin.secrets.list()
From efb9e39b8d4675e9c869b70f4b1366792ea519d7 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 9 Dec 2025 01:38:48 +0000
Subject: [PATCH 13/23] codegen metadata
---
.stats.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index 09786b4..7f6cedc 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 30
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-4dc4e58ef402ce5362e0a8988b3928a8bfa0d5ba847f7ad8b14226a0cf282f28.yml
openapi_spec_hash: fb72b9121306240c419669a3d42e45f6
-config_hash: 7d1d98a4a1938a6884fa81ccfa3184c5
+config_hash: b31a3f1bbe9abcc7bb144942d88ad1b6
From 6834686ece469c756682bb559f8bca5b5b316296 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 9 Dec 2025 04:58:20 +0000
Subject: [PATCH 14/23] fix(types): allow pyright to infer TypedDict types
within SequenceNotStr
---
src/arcadepy/_types.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/arcadepy/_types.py b/src/arcadepy/_types.py
index 39e9386..4e54c6d 100644
--- a/src/arcadepy/_types.py
+++ b/src/arcadepy/_types.py
@@ -243,6 +243,9 @@ class HttpxSendArgs(TypedDict, total=False):
if TYPE_CHECKING:
# This works because str.__contains__ does not accept object (either in typeshed or at runtime)
# https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285
+ #
+ # Note: index() and count() methods are intentionally omitted to allow pyright to properly
+ # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr.
class SequenceNotStr(Protocol[_T_co]):
@overload
def __getitem__(self, index: SupportsIndex, /) -> _T_co: ...
@@ -251,8 +254,6 @@ def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ...
def __contains__(self, value: object, /) -> bool: ...
def __len__(self) -> int: ...
def __iter__(self) -> Iterator[_T_co]: ...
- def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ...
- def count(self, value: Any, /) -> int: ...
def __reversed__(self) -> Iterator[_T_co]: ...
else:
# just point this to a normal `Sequence` at runtime to avoid having to special case
From d8347917cfbe05f70e64d6c01ede460e15e20896 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 9 Dec 2025 05:01:03 +0000
Subject: [PATCH 15/23] chore: add missing docstrings
---
src/arcadepy/types/chat/completion_create_params.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/arcadepy/types/chat/completion_create_params.py b/src/arcadepy/types/chat/completion_create_params.py
index 31608db..b9079d5 100644
--- a/src/arcadepy/types/chat/completion_create_params.py
+++ b/src/arcadepy/types/chat/completion_create_params.py
@@ -78,6 +78,8 @@ class ResponseFormat(TypedDict, total=False):
class StreamOptions(TypedDict, total=False):
+ """Options for streaming response. Only set this when you set stream: true."""
+
include_usage: bool
"""
If set, an additional chunk will be streamed before the data: [DONE] message.
From bc3886aeb649f605137cbec239ab768152f97d15 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sat, 13 Dec 2025 00:06:30 +0000
Subject: [PATCH 16/23] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 7f6cedc..59d6b2e 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 30
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-4dc4e58ef402ce5362e0a8988b3928a8bfa0d5ba847f7ad8b14226a0cf282f28.yml
-openapi_spec_hash: fb72b9121306240c419669a3d42e45f6
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-7ecb40b6650ff002eed02efd1b59c630abe1fb9eb70c9c916c15b115260e5003.yml
+openapi_spec_hash: 2e5c04d1a50afcd0bdbd9737cec227c9
config_hash: b31a3f1bbe9abcc7bb144942d88ad1b6
From eb32a7551debd52fdfc15f3e7948d35e48f20875 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 16 Dec 2025 04:31:44 +0000
Subject: [PATCH 17/23] chore(internal): add missing files argument to base
client
---
src/arcadepy/_base_client.py | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/arcadepy/_base_client.py b/src/arcadepy/_base_client.py
index 57cf33c..0fc752f 100644
--- a/src/arcadepy/_base_client.py
+++ b/src/arcadepy/_base_client.py
@@ -1247,9 +1247,12 @@ def patch(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ files: RequestFiles | None = None,
options: RequestOptions = {},
) -> ResponseT:
- opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options)
+ opts = FinalRequestOptions.construct(
+ method="patch", url=path, json_data=body, files=to_httpx_files(files), **options
+ )
return self.request(cast_to, opts)
def put(
@@ -1767,9 +1770,12 @@ async def patch(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ files: RequestFiles | None = None,
options: RequestOptions = {},
) -> ResponseT:
- opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options)
+ opts = FinalRequestOptions.construct(
+ method="patch", url=path, json_data=body, files=to_httpx_files(files), **options
+ )
return await self.request(cast_to, opts)
async def put(
From 12abf0c7c4493c0ff4e27db53679e65dde59eab9 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 17 Dec 2025 06:04:40 +0000
Subject: [PATCH 18/23] chore: speedup initial import
---
src/arcadepy/_client.py | 316 ++++++++++++++++++++++++++++++++--------
1 file changed, 253 insertions(+), 63 deletions(-)
diff --git a/src/arcadepy/_client.py b/src/arcadepy/_client.py
index 7ad9056..a261011 100644
--- a/src/arcadepy/_client.py
+++ b/src/arcadepy/_client.py
@@ -3,7 +3,7 @@
from __future__ import annotations
import os
-from typing import Any, Mapping
+from typing import TYPE_CHECKING, Any, Mapping
from typing_extensions import Self, override
import httpx
@@ -20,8 +20,8 @@
not_given,
)
from ._utils import is_given, get_async_library
+from ._compat import cached_property
from ._version import __version__
-from .resources import auth, health, workers
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
from ._exceptions import ArcadeError, APIStatusError
from ._base_client import (
@@ -29,23 +29,20 @@
SyncAPIClient,
AsyncAPIClient,
)
-from .resources.chat import chat
-from .resources.admin import admin
-from .resources.tools import tools
+
+if TYPE_CHECKING:
+ from .resources import auth, chat, admin, tools, health, workers
+ from .resources.auth import AuthResource, AsyncAuthResource
+ from .resources.health import HealthResource, AsyncHealthResource
+ from .resources.workers import WorkersResource, AsyncWorkersResource
+ from .resources.chat.chat import ChatResource, AsyncChatResource
+ from .resources.admin.admin import AdminResource, AsyncAdminResource
+ from .resources.tools.tools import ToolsResource, AsyncToolsResource
__all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Arcade", "AsyncArcade", "Client", "AsyncClient"]
class Arcade(SyncAPIClient):
- admin: admin.AdminResource
- auth: auth.AuthResource
- health: health.HealthResource
- chat: chat.ChatResource
- tools: tools.ToolsResource
- workers: workers.WorkersResource
- with_raw_response: ArcadeWithRawResponse
- with_streaming_response: ArcadeWithStreamedResponse
-
# client options
api_key: str
@@ -100,14 +97,49 @@ def __init__(
_strict_response_validation=_strict_response_validation,
)
- self.admin = admin.AdminResource(self)
- self.auth = auth.AuthResource(self)
- self.health = health.HealthResource(self)
- self.chat = chat.ChatResource(self)
- self.tools = tools.ToolsResource(self)
- self.workers = workers.WorkersResource(self)
- self.with_raw_response = ArcadeWithRawResponse(self)
- self.with_streaming_response = ArcadeWithStreamedResponse(self)
+ @cached_property
+ def admin(self) -> AdminResource:
+ from .resources.admin import AdminResource
+
+ return AdminResource(self)
+
+ @cached_property
+ def auth(self) -> AuthResource:
+ from .resources.auth import AuthResource
+
+ return AuthResource(self)
+
+ @cached_property
+ def health(self) -> HealthResource:
+ from .resources.health import HealthResource
+
+ return HealthResource(self)
+
+ @cached_property
+ def chat(self) -> ChatResource:
+ from .resources.chat import ChatResource
+
+ return ChatResource(self)
+
+ @cached_property
+ def tools(self) -> ToolsResource:
+ from .resources.tools import ToolsResource
+
+ return ToolsResource(self)
+
+ @cached_property
+ def workers(self) -> WorkersResource:
+ from .resources.workers import WorkersResource
+
+ return WorkersResource(self)
+
+ @cached_property
+ def with_raw_response(self) -> ArcadeWithRawResponse:
+ return ArcadeWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ArcadeWithStreamedResponse:
+ return ArcadeWithStreamedResponse(self)
@property
@override
@@ -215,15 +247,6 @@ def _make_status_error(
class AsyncArcade(AsyncAPIClient):
- admin: admin.AsyncAdminResource
- auth: auth.AsyncAuthResource
- health: health.AsyncHealthResource
- chat: chat.AsyncChatResource
- tools: tools.AsyncToolsResource
- workers: workers.AsyncWorkersResource
- with_raw_response: AsyncArcadeWithRawResponse
- with_streaming_response: AsyncArcadeWithStreamedResponse
-
# client options
api_key: str
@@ -278,14 +301,49 @@ def __init__(
_strict_response_validation=_strict_response_validation,
)
- self.admin = admin.AsyncAdminResource(self)
- self.auth = auth.AsyncAuthResource(self)
- self.health = health.AsyncHealthResource(self)
- self.chat = chat.AsyncChatResource(self)
- self.tools = tools.AsyncToolsResource(self)
- self.workers = workers.AsyncWorkersResource(self)
- self.with_raw_response = AsyncArcadeWithRawResponse(self)
- self.with_streaming_response = AsyncArcadeWithStreamedResponse(self)
+ @cached_property
+ def admin(self) -> AsyncAdminResource:
+ from .resources.admin import AsyncAdminResource
+
+ return AsyncAdminResource(self)
+
+ @cached_property
+ def auth(self) -> AsyncAuthResource:
+ from .resources.auth import AsyncAuthResource
+
+ return AsyncAuthResource(self)
+
+ @cached_property
+ def health(self) -> AsyncHealthResource:
+ from .resources.health import AsyncHealthResource
+
+ return AsyncHealthResource(self)
+
+ @cached_property
+ def chat(self) -> AsyncChatResource:
+ from .resources.chat import AsyncChatResource
+
+ return AsyncChatResource(self)
+
+ @cached_property
+ def tools(self) -> AsyncToolsResource:
+ from .resources.tools import AsyncToolsResource
+
+ return AsyncToolsResource(self)
+
+ @cached_property
+ def workers(self) -> AsyncWorkersResource:
+ from .resources.workers import AsyncWorkersResource
+
+ return AsyncWorkersResource(self)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncArcadeWithRawResponse:
+ return AsyncArcadeWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncArcadeWithStreamedResponse:
+ return AsyncArcadeWithStreamedResponse(self)
@property
@override
@@ -393,43 +451,175 @@ def _make_status_error(
class ArcadeWithRawResponse:
+ _client: Arcade
+
def __init__(self, client: Arcade) -> None:
- self.admin = admin.AdminResourceWithRawResponse(client.admin)
- self.auth = auth.AuthResourceWithRawResponse(client.auth)
- self.health = health.HealthResourceWithRawResponse(client.health)
- self.chat = chat.ChatResourceWithRawResponse(client.chat)
- self.tools = tools.ToolsResourceWithRawResponse(client.tools)
- self.workers = workers.WorkersResourceWithRawResponse(client.workers)
+ self._client = client
+
+ @cached_property
+ def admin(self) -> admin.AdminResourceWithRawResponse:
+ from .resources.admin import AdminResourceWithRawResponse
+
+ return AdminResourceWithRawResponse(self._client.admin)
+
+ @cached_property
+ def auth(self) -> auth.AuthResourceWithRawResponse:
+ from .resources.auth import AuthResourceWithRawResponse
+
+ return AuthResourceWithRawResponse(self._client.auth)
+
+ @cached_property
+ def health(self) -> health.HealthResourceWithRawResponse:
+ from .resources.health import HealthResourceWithRawResponse
+
+ return HealthResourceWithRawResponse(self._client.health)
+
+ @cached_property
+ def chat(self) -> chat.ChatResourceWithRawResponse:
+ from .resources.chat import ChatResourceWithRawResponse
+
+ return ChatResourceWithRawResponse(self._client.chat)
+
+ @cached_property
+ def tools(self) -> tools.ToolsResourceWithRawResponse:
+ from .resources.tools import ToolsResourceWithRawResponse
+
+ return ToolsResourceWithRawResponse(self._client.tools)
+
+ @cached_property
+ def workers(self) -> workers.WorkersResourceWithRawResponse:
+ from .resources.workers import WorkersResourceWithRawResponse
+
+ return WorkersResourceWithRawResponse(self._client.workers)
class AsyncArcadeWithRawResponse:
+ _client: AsyncArcade
+
def __init__(self, client: AsyncArcade) -> None:
- self.admin = admin.AsyncAdminResourceWithRawResponse(client.admin)
- self.auth = auth.AsyncAuthResourceWithRawResponse(client.auth)
- self.health = health.AsyncHealthResourceWithRawResponse(client.health)
- self.chat = chat.AsyncChatResourceWithRawResponse(client.chat)
- self.tools = tools.AsyncToolsResourceWithRawResponse(client.tools)
- self.workers = workers.AsyncWorkersResourceWithRawResponse(client.workers)
+ self._client = client
+
+ @cached_property
+ def admin(self) -> admin.AsyncAdminResourceWithRawResponse:
+ from .resources.admin import AsyncAdminResourceWithRawResponse
+
+ return AsyncAdminResourceWithRawResponse(self._client.admin)
+
+ @cached_property
+ def auth(self) -> auth.AsyncAuthResourceWithRawResponse:
+ from .resources.auth import AsyncAuthResourceWithRawResponse
+
+ return AsyncAuthResourceWithRawResponse(self._client.auth)
+
+ @cached_property
+ def health(self) -> health.AsyncHealthResourceWithRawResponse:
+ from .resources.health import AsyncHealthResourceWithRawResponse
+
+ return AsyncHealthResourceWithRawResponse(self._client.health)
+
+ @cached_property
+ def chat(self) -> chat.AsyncChatResourceWithRawResponse:
+ from .resources.chat import AsyncChatResourceWithRawResponse
+
+ return AsyncChatResourceWithRawResponse(self._client.chat)
+
+ @cached_property
+ def tools(self) -> tools.AsyncToolsResourceWithRawResponse:
+ from .resources.tools import AsyncToolsResourceWithRawResponse
+
+ return AsyncToolsResourceWithRawResponse(self._client.tools)
+
+ @cached_property
+ def workers(self) -> workers.AsyncWorkersResourceWithRawResponse:
+ from .resources.workers import AsyncWorkersResourceWithRawResponse
+
+ return AsyncWorkersResourceWithRawResponse(self._client.workers)
class ArcadeWithStreamedResponse:
+ _client: Arcade
+
def __init__(self, client: Arcade) -> None:
- self.admin = admin.AdminResourceWithStreamingResponse(client.admin)
- self.auth = auth.AuthResourceWithStreamingResponse(client.auth)
- self.health = health.HealthResourceWithStreamingResponse(client.health)
- self.chat = chat.ChatResourceWithStreamingResponse(client.chat)
- self.tools = tools.ToolsResourceWithStreamingResponse(client.tools)
- self.workers = workers.WorkersResourceWithStreamingResponse(client.workers)
+ self._client = client
+
+ @cached_property
+ def admin(self) -> admin.AdminResourceWithStreamingResponse:
+ from .resources.admin import AdminResourceWithStreamingResponse
+
+ return AdminResourceWithStreamingResponse(self._client.admin)
+
+ @cached_property
+ def auth(self) -> auth.AuthResourceWithStreamingResponse:
+ from .resources.auth import AuthResourceWithStreamingResponse
+
+ return AuthResourceWithStreamingResponse(self._client.auth)
+
+ @cached_property
+ def health(self) -> health.HealthResourceWithStreamingResponse:
+ from .resources.health import HealthResourceWithStreamingResponse
+
+ return HealthResourceWithStreamingResponse(self._client.health)
+
+ @cached_property
+ def chat(self) -> chat.ChatResourceWithStreamingResponse:
+ from .resources.chat import ChatResourceWithStreamingResponse
+
+ return ChatResourceWithStreamingResponse(self._client.chat)
+
+ @cached_property
+ def tools(self) -> tools.ToolsResourceWithStreamingResponse:
+ from .resources.tools import ToolsResourceWithStreamingResponse
+
+ return ToolsResourceWithStreamingResponse(self._client.tools)
+
+ @cached_property
+ def workers(self) -> workers.WorkersResourceWithStreamingResponse:
+ from .resources.workers import WorkersResourceWithStreamingResponse
+
+ return WorkersResourceWithStreamingResponse(self._client.workers)
class AsyncArcadeWithStreamedResponse:
+ _client: AsyncArcade
+
def __init__(self, client: AsyncArcade) -> None:
- self.admin = admin.AsyncAdminResourceWithStreamingResponse(client.admin)
- self.auth = auth.AsyncAuthResourceWithStreamingResponse(client.auth)
- self.health = health.AsyncHealthResourceWithStreamingResponse(client.health)
- self.chat = chat.AsyncChatResourceWithStreamingResponse(client.chat)
- self.tools = tools.AsyncToolsResourceWithStreamingResponse(client.tools)
- self.workers = workers.AsyncWorkersResourceWithStreamingResponse(client.workers)
+ self._client = client
+
+ @cached_property
+ def admin(self) -> admin.AsyncAdminResourceWithStreamingResponse:
+ from .resources.admin import AsyncAdminResourceWithStreamingResponse
+
+ return AsyncAdminResourceWithStreamingResponse(self._client.admin)
+
+ @cached_property
+ def auth(self) -> auth.AsyncAuthResourceWithStreamingResponse:
+ from .resources.auth import AsyncAuthResourceWithStreamingResponse
+
+ return AsyncAuthResourceWithStreamingResponse(self._client.auth)
+
+ @cached_property
+ def health(self) -> health.AsyncHealthResourceWithStreamingResponse:
+ from .resources.health import AsyncHealthResourceWithStreamingResponse
+
+ return AsyncHealthResourceWithStreamingResponse(self._client.health)
+
+ @cached_property
+ def chat(self) -> chat.AsyncChatResourceWithStreamingResponse:
+ from .resources.chat import AsyncChatResourceWithStreamingResponse
+
+ return AsyncChatResourceWithStreamingResponse(self._client.chat)
+
+ @cached_property
+ def tools(self) -> tools.AsyncToolsResourceWithStreamingResponse:
+ from .resources.tools import AsyncToolsResourceWithStreamingResponse
+
+ return AsyncToolsResourceWithStreamingResponse(self._client.tools)
+
+ @cached_property
+ def workers(self) -> workers.AsyncWorkersResourceWithStreamingResponse:
+ from .resources.workers import AsyncWorkersResourceWithStreamingResponse
+
+ return AsyncWorkersResourceWithStreamingResponse(self._client.workers)
Client = Arcade
From 75704b3bd2478de2815cbd90816c171a4017f6c7 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 17 Dec 2025 22:40:40 +0000
Subject: [PATCH 19/23] codegen metadata
---
.stats.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index 59d6b2e..6e0768f 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 30
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-7ecb40b6650ff002eed02efd1b59c630abe1fb9eb70c9c916c15b115260e5003.yml
openapi_spec_hash: 2e5c04d1a50afcd0bdbd9737cec227c9
-config_hash: b31a3f1bbe9abcc7bb144942d88ad1b6
+config_hash: f26ae67630e2fac8d08a018eefd1d2d9
From 74a7b771f42ef16b58544eb09df84f60e73db843 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 18 Dec 2025 01:01:24 +0000
Subject: [PATCH 20/23] codegen metadata
---
.stats.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index 6e0768f..9883e5e 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 30
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-7ecb40b6650ff002eed02efd1b59c630abe1fb9eb70c9c916c15b115260e5003.yml
openapi_spec_hash: 2e5c04d1a50afcd0bdbd9737cec227c9
-config_hash: f26ae67630e2fac8d08a018eefd1d2d9
+config_hash: 01e6bd1df0d14c729087edec4e6b6650
From 855d52c4817ecb2421b8467a0190f1222b1306de Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 18 Dec 2025 06:59:12 +0000
Subject: [PATCH 21/23] fix: use async_to_httpx_files in patch method
---
src/arcadepy/_base_client.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/arcadepy/_base_client.py b/src/arcadepy/_base_client.py
index 0fc752f..02030fd 100644
--- a/src/arcadepy/_base_client.py
+++ b/src/arcadepy/_base_client.py
@@ -1774,7 +1774,7 @@ async def patch(
options: RequestOptions = {},
) -> ResponseT:
opts = FinalRequestOptions.construct(
- method="patch", url=path, json_data=body, files=to_httpx_files(files), **options
+ method="patch", url=path, json_data=body, files=await async_to_httpx_files(files), **options
)
return await self.request(cast_to, opts)
From 461ab0bfb6285d29b773511c0350efcd1a011b7e Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 19 Dec 2025 06:04:48 +0000
Subject: [PATCH 22/23] chore(internal): add `--fix` argument to lint script
---
scripts/lint | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/scripts/lint b/scripts/lint
index 17c73a3..dc8016b 100755
--- a/scripts/lint
+++ b/scripts/lint
@@ -4,8 +4,13 @@ set -e
cd "$(dirname "$0")/.."
-echo "==> Running lints"
-rye run lint
+if [ "$1" = "--fix" ]; then
+ echo "==> Running lints with --fix"
+ rye run fix:ruff
+else
+ echo "==> Running lints"
+ rye run lint
+fi
echo "==> Making sure it imports"
rye run python -c 'import arcadepy'
From 555a8fa02fcb9fae0a1c3aa11bf9fc27526329e4 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 19 Dec 2025 06:05:06 +0000
Subject: [PATCH 23/23] release: 1.11.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 31 +++++++++++++++++++++++++++++++
pyproject.toml | 2 +-
src/arcadepy/_version.py | 2 +-
4 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index eb4e0db..caf1487 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "1.10.0"
+ ".": "1.11.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5938671..e0e5254 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,36 @@
# Changelog
+## 1.11.0 (2025-12-19)
+
+Full Changelog: [v1.10.0...v1.11.0](https://github.com/ArcadeAI/arcade-py/compare/v1.10.0...v1.11.0)
+
+### Features
+
+* **api:** api update ([f98c6d3](https://github.com/ArcadeAI/arcade-py/commit/f98c6d3ccf44a38525f056bc52011fbdcc3183c1))
+* **api:** api update ([60e16d2](https://github.com/ArcadeAI/arcade-py/commit/60e16d2e59496bcf64c28328e3a351925b82dccd))
+
+
+### Bug Fixes
+
+* compat with Python 3.14 ([4453f62](https://github.com/ArcadeAI/arcade-py/commit/4453f62ffe78f5bf6ec84c89566a2581ef381cca))
+* **compat:** update signatures of `model_dump` and `model_dump_json` for Pydantic v1 ([d4527cf](https://github.com/ArcadeAI/arcade-py/commit/d4527cf2a0ab18f31c0b75d6b452a09f47bd15f5))
+* ensure streams are always closed ([e4be19e](https://github.com/ArcadeAI/arcade-py/commit/e4be19ee70952d16361d55acfc082875bd926c01))
+* **types:** allow pyright to infer TypedDict types within SequenceNotStr ([6834686](https://github.com/ArcadeAI/arcade-py/commit/6834686ece469c756682bb559f8bca5b5b316296))
+* use async_to_httpx_files in patch method ([855d52c](https://github.com/ArcadeAI/arcade-py/commit/855d52c4817ecb2421b8467a0190f1222b1306de))
+
+
+### Chores
+
+* add missing docstrings ([d834791](https://github.com/ArcadeAI/arcade-py/commit/d8347917cfbe05f70e64d6c01ede460e15e20896))
+* add Python 3.14 classifier and testing ([8c6d5c5](https://github.com/ArcadeAI/arcade-py/commit/8c6d5c5ace8884839590c11a0b72f9ea70731e0e))
+* **deps:** mypy 1.18.1 has a regression, pin to 1.17 ([0c9e340](https://github.com/ArcadeAI/arcade-py/commit/0c9e3402e6714d2c1e4273ff5bd0e2dc00cce7b2))
+* **docs:** use environment variables for authentication in code snippets ([de0cde1](https://github.com/ArcadeAI/arcade-py/commit/de0cde1a836d5b1992d8cdc284c3decbfa8eb20b))
+* **internal:** add `--fix` argument to lint script ([461ab0b](https://github.com/ArcadeAI/arcade-py/commit/461ab0bfb6285d29b773511c0350efcd1a011b7e))
+* **internal:** add missing files argument to base client ([eb32a75](https://github.com/ArcadeAI/arcade-py/commit/eb32a7551debd52fdfc15f3e7948d35e48f20875))
+* **package:** drop Python 3.8 support ([d68084f](https://github.com/ArcadeAI/arcade-py/commit/d68084f663f4cf682457a195918d95cce6584772))
+* speedup initial import ([12abf0c](https://github.com/ArcadeAI/arcade-py/commit/12abf0c7c4493c0ff4e27db53679e65dde59eab9))
+* update lockfile ([d0dae69](https://github.com/ArcadeAI/arcade-py/commit/d0dae695284c4ee2873529f0e24034dc409e99e7))
+
## 1.10.0 (2025-11-06)
Full Changelog: [v1.9.0...v1.10.0](https://github.com/ArcadeAI/arcade-py/compare/v1.9.0...v1.10.0)
diff --git a/pyproject.toml b/pyproject.toml
index ce26232..2eed60c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "arcadepy"
-version = "1.10.0"
+version = "1.11.0"
description = "The official Python library for the Arcade API"
dynamic = ["readme"]
license = "MIT"
diff --git a/src/arcadepy/_version.py b/src/arcadepy/_version.py
index bb28e4b..3bd71df 100644
--- a/src/arcadepy/_version.py
+++ b/src/arcadepy/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "arcadepy"
-__version__ = "1.10.0" # x-release-please-version
+__version__ = "1.11.0" # x-release-please-version