Skip to content

Commit 1927cdf

Browse files
release: 0.12.0 (#22)
* feat(api): api update * fix: ensure file data are only sent as 1 parameter * feat: Add parent_run_id filter to List runs endpoint * feat(api): api update * feat(api): api update * feat: Inject auth secrets via ambient agent config. * feat(api): api update * feat(api): api update * feat: Add system prompt to resolve-prompt for harnesses. * feat: Add trigger URL to task source. * feat(api): api update * feat: Add worker_host to AgentListSource in OpenAPI spec * chore: update SDK settings * feat(api): api update * feat(api): api update * codegen metadata * feat: Update public API and graphql to support creating multiple service accounts and API keys * perf(client): optimize file structure copying in multipart requests * feat(api): api update * feat(api): api update * codegen metadata * codegen metadata * feat: Add API support for public access on oz session link * feat(api): api update * feat(api): api update * chore(internal): more robust bootstrap script * feat: Add Gemini as a third-party Oz harness. * codegen metadata * feat: implement server-side cloud-to-cloud handoff for agent follow-ups (REMOTE-1290) * feat: Accept and persist executor on schedule and integration create/update APIs * feat(api): api update * fix: use correct field name format for multipart file arrays * feat(api): api update * feat: support setting headers via env * feat: Make conversation/session redirects public * feat: Extend service account API to support skills + secrets * feat(api): api update * codegen metadata * feat: [REMOTE-1481] [3/N] Add executor filter to run list API * feat(api): api update * feat: Add Codex as a third-party Oz harness. * feat(api): api update * feat: Memory data model scaffolding. * codegen metadata * feat(api): api update * codegen metadata * feat(api): api update * feat: [APP-3106] Specs: Explicit QueryMode for cloud agent run creation * codegen metadata * codegen metadata * feat(api): api update * feat: Apply service-account secrets at run creation * chore(internal): reformat pyproject.toml * feat(api): api update * feat(api): api update * codegen metadata * codegen metadata * codegen metadata * codegen metadata * feat: Surface platform credits in the public API * feat: [REMOTE-1538] Support base model on named agents * feat(api): api update * feat: Include agent UID in scheduled agent API responses * release: 0.12.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com>
1 parent 2d9cdd0 commit 1927cdf

51 files changed

Lines changed: 2855 additions & 165 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "0.11.0"
2+
".": "0.12.0"
33
}

.stats.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
configured_endpoints: 14
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/warp-bnavetta%2Fwarp-api-8f9c749573846b07a55a3131b66456f0a592838c6bfc986ab30948df66cd6f11.yml
3-
openapi_spec_hash: 59f1ac98ad6cf13b12c59196bcecffd7
4-
config_hash: 60052b2c1c0862014416821aba875574
1+
configured_endpoints: 22
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/warp-bnavetta/warp-api-1fecc5f5d6ee664d804b81bd1aa6eec4d3f170ffa788d214fead4f7e95ab9d4e.yml
3+
openapi_spec_hash: 82990b03bd5a93e45bfc79db56ae7fc0
4+
config_hash: f52e7636f248f25c4ea0b086e7326816

CHANGELOG.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,74 @@
11
# Changelog
22

3+
## 0.12.0 (2026-05-07)
4+
5+
Full Changelog: [v0.11.0...v0.12.0](https://github.com/warpdotdev/oz-sdk-python/compare/v0.11.0...v0.12.0)
6+
7+
### Features
8+
9+
* [APP-3106] Specs: Explicit QueryMode for cloud agent run creation ([1d4e049](https://github.com/warpdotdev/oz-sdk-python/commit/1d4e049e5c64fdfe1f8eff8dbf7bc8256ed98182))
10+
* [REMOTE-1481] [3/N] Add executor filter to run list API ([2dbc229](https://github.com/warpdotdev/oz-sdk-python/commit/2dbc22949c6c9475a31e74cba5eadecb018277f3))
11+
* [REMOTE-1538] Support base model on named agents ([7634c23](https://github.com/warpdotdev/oz-sdk-python/commit/7634c231906d3fb53c9576b39fac1fa60f694790))
12+
* Accept and persist executor on schedule and integration create/update APIs ([a8500aa](https://github.com/warpdotdev/oz-sdk-python/commit/a8500aa0b3e74221cd931b01d7e7569ad191d1f3))
13+
* Add API support for public access on oz session link ([df940e0](https://github.com/warpdotdev/oz-sdk-python/commit/df940e09b23d2cc3d314407a8826f722b9428dca))
14+
* Add Codex as a third-party Oz harness. ([898e18f](https://github.com/warpdotdev/oz-sdk-python/commit/898e18f8e26a2064d3bb3bdda83c08adeb91088d))
15+
* Add Gemini as a third-party Oz harness. ([0128722](https://github.com/warpdotdev/oz-sdk-python/commit/0128722383f87f6d5a73fd0ecafc4206a3013342))
16+
* Add parent_run_id filter to List runs endpoint ([33cf04c](https://github.com/warpdotdev/oz-sdk-python/commit/33cf04c0777d53059f8a3c73b925b468276aba2a))
17+
* Add system prompt to resolve-prompt for harnesses. ([2b7afab](https://github.com/warpdotdev/oz-sdk-python/commit/2b7afabf4a54764ab162ac32f4dbe7fe4274ba72))
18+
* Add trigger URL to task source. ([9662dbe](https://github.com/warpdotdev/oz-sdk-python/commit/9662dbe10cc5392271b3e56c35cb8ef95ec80eed))
19+
* Add worker_host to AgentListSource in OpenAPI spec ([ec0982c](https://github.com/warpdotdev/oz-sdk-python/commit/ec0982ce786b2380ab89aa7298b28998c6eade46))
20+
* **api:** api update ([88de4e2](https://github.com/warpdotdev/oz-sdk-python/commit/88de4e2765d70de416658f0ce0ef42585763efc9))
21+
* **api:** api update ([4a0d4fc](https://github.com/warpdotdev/oz-sdk-python/commit/4a0d4fc0711f3fe0d3168c3f5f7ec2176d5b0010))
22+
* **api:** api update ([654da59](https://github.com/warpdotdev/oz-sdk-python/commit/654da597a10a63048c5a34ca67c9d6684c72b688))
23+
* **api:** api update ([86d389c](https://github.com/warpdotdev/oz-sdk-python/commit/86d389c610fb2b89d7fcd17ebfd06610ef2b0b90))
24+
* **api:** api update ([15d68a1](https://github.com/warpdotdev/oz-sdk-python/commit/15d68a1db8178fd9cb5fae456ca6f341b60fd1e7))
25+
* **api:** api update ([54f5cdc](https://github.com/warpdotdev/oz-sdk-python/commit/54f5cdc819015611f7214b8ac6632df1cebf857d))
26+
* **api:** api update ([e290d3b](https://github.com/warpdotdev/oz-sdk-python/commit/e290d3b8cd2e8bd10a713e41992e282b704c624e))
27+
* **api:** api update ([35565ad](https://github.com/warpdotdev/oz-sdk-python/commit/35565ada13a8a16f9117b3ca0c12279d1708d184))
28+
* **api:** api update ([9cf2d03](https://github.com/warpdotdev/oz-sdk-python/commit/9cf2d0352242be5c7075fe971561cc36aaaf6e77))
29+
* **api:** api update ([ed871aa](https://github.com/warpdotdev/oz-sdk-python/commit/ed871aac657265138112d98be757a0de48796e02))
30+
* **api:** api update ([a44c455](https://github.com/warpdotdev/oz-sdk-python/commit/a44c455cb949e69b05f33f38571b40d9c0caed36))
31+
* **api:** api update ([e24ba64](https://github.com/warpdotdev/oz-sdk-python/commit/e24ba644c9d3624019f240fd43f51079adf7d8d4))
32+
* **api:** api update ([e5aea21](https://github.com/warpdotdev/oz-sdk-python/commit/e5aea2192017b1758d7b32d1e855015ce29e93ca))
33+
* **api:** api update ([f94b38b](https://github.com/warpdotdev/oz-sdk-python/commit/f94b38b2ffaf344a198e89ad7ace1d7b8e93c300))
34+
* **api:** api update ([7f419fa](https://github.com/warpdotdev/oz-sdk-python/commit/7f419faa01908e1b4a613ec2bdd708aed1a05871))
35+
* **api:** api update ([116f06e](https://github.com/warpdotdev/oz-sdk-python/commit/116f06e5c63e8d015e7dfea47fc22ede53ebb84e))
36+
* **api:** api update ([66c8521](https://github.com/warpdotdev/oz-sdk-python/commit/66c852194f355cedca2b34b944c398e796f064ab))
37+
* **api:** api update ([ebd2c55](https://github.com/warpdotdev/oz-sdk-python/commit/ebd2c5520128722d88285aea9dd0caf3242d9a31))
38+
* **api:** api update ([1ffc53b](https://github.com/warpdotdev/oz-sdk-python/commit/1ffc53b4d69eeb8060faac1903f1ba25f7a4c443))
39+
* **api:** api update ([cf28804](https://github.com/warpdotdev/oz-sdk-python/commit/cf288045d369ed927db45ac5e5ebd994ccebb547))
40+
* **api:** api update ([a6d82f1](https://github.com/warpdotdev/oz-sdk-python/commit/a6d82f1a71ad8e0ff5f10c43cc81938babc1444e))
41+
* **api:** api update ([b9872f2](https://github.com/warpdotdev/oz-sdk-python/commit/b9872f2642191ef6e1d4d5d3542a08f9e2784b9e))
42+
* **api:** api update ([0ef9c8f](https://github.com/warpdotdev/oz-sdk-python/commit/0ef9c8f483269d203a3edc7b5805f48f4263eb2f))
43+
* Apply service-account secrets at run creation ([db0e98c](https://github.com/warpdotdev/oz-sdk-python/commit/db0e98ca667b0cfe5b8ab182db5b1d4a19584113))
44+
* Extend service account API to support skills + secrets ([caf9c45](https://github.com/warpdotdev/oz-sdk-python/commit/caf9c4539ad1718ed194619ebf26425217265706))
45+
* implement server-side cloud-to-cloud handoff for agent follow-ups (REMOTE-1290) ([eb873a6](https://github.com/warpdotdev/oz-sdk-python/commit/eb873a6092aa1792ca6c23f5d27a7ee66c225ea0))
46+
* Include agent UID in scheduled agent API responses ([1714226](https://github.com/warpdotdev/oz-sdk-python/commit/1714226a85760699faacebd23cc1ea3a6bc7f0b9))
47+
* Inject auth secrets via ambient agent config. ([6032a0c](https://github.com/warpdotdev/oz-sdk-python/commit/6032a0ccfe2ce669ce1f815ad853b912d4ca575e))
48+
* Make conversation/session redirects public ([4c23bb6](https://github.com/warpdotdev/oz-sdk-python/commit/4c23bb6ebec32b5022a1731284d3e07b81a9f5ae))
49+
* Memory data model scaffolding. ([4ccbe03](https://github.com/warpdotdev/oz-sdk-python/commit/4ccbe0371e7bf02c454a14bc8bc2d412dcebf290))
50+
* support setting headers via env ([d3b4755](https://github.com/warpdotdev/oz-sdk-python/commit/d3b4755c7447d915a4e11be043b21b859f6c028d))
51+
* Surface platform credits in the public API ([d54b3d5](https://github.com/warpdotdev/oz-sdk-python/commit/d54b3d55760fa68853130d026dfdd88a7e226c80))
52+
* Update public API and graphql to support creating multiple service accounts and API keys ([8d537bc](https://github.com/warpdotdev/oz-sdk-python/commit/8d537bcbe75b344427bd04e5406ab066769f580a))
53+
54+
55+
### Bug Fixes
56+
57+
* ensure file data are only sent as 1 parameter ([6720ea9](https://github.com/warpdotdev/oz-sdk-python/commit/6720ea9331a945cf2d030e20b8851a223697aab3))
58+
* use correct field name format for multipart file arrays ([f785ff2](https://github.com/warpdotdev/oz-sdk-python/commit/f785ff2bafbd319574974549a73c21643cdb5cc5))
59+
60+
61+
### Performance Improvements
62+
63+
* **client:** optimize file structure copying in multipart requests ([b8e42cc](https://github.com/warpdotdev/oz-sdk-python/commit/b8e42cc08805eef08a3462088ba0e32cf79dac8c))
64+
65+
66+
### Chores
67+
68+
* **internal:** more robust bootstrap script ([27c80e9](https://github.com/warpdotdev/oz-sdk-python/commit/27c80e953bc19de56ea730b0c2170aa8ffbc495a))
69+
* **internal:** reformat pyproject.toml ([fd17af1](https://github.com/warpdotdev/oz-sdk-python/commit/fd17af10c72ed943e4023a4e9e071ac7b8db08f7))
70+
* update SDK settings ([f2dd099](https://github.com/warpdotdev/oz-sdk-python/commit/f2dd099128bf4f98e304778556d55e80a4fd219c))
71+
372
## 0.11.0 (2026-04-09)
473

574
Full Changelog: [v0.10.1...v0.11.0](https://github.com/warpdotdev/oz-sdk-python/compare/v0.10.1...v0.11.0)

api.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ from oz_agent_sdk.types import (
77
AgentSkill,
88
AmbientAgentConfig,
99
AwsProviderConfig,
10+
CloudEnvironment,
1011
CloudEnvironmentConfig,
1112
Error,
1213
ErrorCode,
@@ -16,6 +17,7 @@ from oz_agent_sdk.types import (
1617
UserProfile,
1718
AgentListResponse,
1819
AgentGetArtifactResponse,
20+
AgentListEnvironmentsResponse,
1921
AgentRunResponse,
2022
)
2123
```
@@ -24,6 +26,7 @@ Methods:
2426

2527
- <code title="get /agent">client.agent.<a href="./src/oz_agent_sdk/resources/agent/agent.py">list</a>(\*\*<a href="src/oz_agent_sdk/types/agent_list_params.py">params</a>) -> <a href="./src/oz_agent_sdk/types/agent_list_response.py">AgentListResponse</a></code>
2628
- <code title="get /agent/artifacts/{artifactUid}">client.agent.<a href="./src/oz_agent_sdk/resources/agent/agent.py">get_artifact</a>(artifact_uid) -> <a href="./src/oz_agent_sdk/types/agent_get_artifact_response.py">AgentGetArtifactResponse</a></code>
29+
- <code title="get /agent/environments">client.agent.<a href="./src/oz_agent_sdk/resources/agent/agent.py">list_environments</a>(\*\*<a href="src/oz_agent_sdk/types/agent_list_environments_params.py">params</a>) -> <a href="./src/oz_agent_sdk/types/agent_list_environments_response.py">AgentListEnvironmentsResponse</a></code>
2730
- <code title="post /agent/runs">client.agent.<a href="./src/oz_agent_sdk/resources/agent/agent.py">run</a>(\*\*<a href="src/oz_agent_sdk/types/agent_run_params.py">params</a>) -> <a href="./src/oz_agent_sdk/types/agent_run_response.py">AgentRunResponse</a></code>
2831

2932
## Runs
@@ -37,6 +40,7 @@ from oz_agent_sdk.types.agent import (
3740
RunSourceType,
3841
RunState,
3942
RunCancelResponse,
43+
RunListHandoffAttachmentsResponse,
4044
)
4145
```
4246

@@ -45,6 +49,8 @@ Methods:
4549
- <code title="get /agent/runs/{runId}">client.agent.runs.<a href="./src/oz_agent_sdk/resources/agent/runs.py">retrieve</a>(run_id) -> <a href="./src/oz_agent_sdk/types/agent/run_item.py">RunItem</a></code>
4650
- <code title="get /agent/runs">client.agent.runs.<a href="./src/oz_agent_sdk/resources/agent/runs.py">list</a>(\*\*<a href="src/oz_agent_sdk/types/agent/run_list_params.py">params</a>) -> <a href="./src/oz_agent_sdk/types/agent/run_item.py">SyncRunsCursorPage[RunItem]</a></code>
4751
- <code title="post /agent/runs/{runId}/cancel">client.agent.runs.<a href="./src/oz_agent_sdk/resources/agent/runs.py">cancel</a>(run_id) -> str</code>
52+
- <code title="get /agent/runs/{runId}/handoff/attachments">client.agent.runs.<a href="./src/oz_agent_sdk/resources/agent/runs.py">list_handoff_attachments</a>(run_id) -> <a href="./src/oz_agent_sdk/types/agent/run_list_handoff_attachments_response.py">RunListHandoffAttachmentsResponse</a></code>
53+
- <code title="post /agent/runs/{runId}/followups">client.agent.runs.<a href="./src/oz_agent_sdk/resources/agent/runs.py">submit_followup</a>(run_id, \*\*<a href="src/oz_agent_sdk/types/agent/run_submit_followup_params.py">params</a>) -> object</code>
4854

4955
## Schedules
5056

@@ -69,6 +75,26 @@ Methods:
6975
- <code title="post /agent/schedules/{scheduleId}/pause">client.agent.schedules.<a href="./src/oz_agent_sdk/resources/agent/schedules.py">pause</a>(schedule_id) -> <a href="./src/oz_agent_sdk/types/agent/scheduled_agent_item.py">ScheduledAgentItem</a></code>
7076
- <code title="post /agent/schedules/{scheduleId}/resume">client.agent.schedules.<a href="./src/oz_agent_sdk/resources/agent/schedules.py">resume</a>(schedule_id) -> <a href="./src/oz_agent_sdk/types/agent/scheduled_agent_item.py">ScheduledAgentItem</a></code>
7177

78+
## Agent
79+
80+
Types:
81+
82+
```python
83+
from oz_agent_sdk.types.agent import (
84+
AgentResponse,
85+
CreateAgentRequest,
86+
ListAgentIdentitiesResponse,
87+
UpdateAgentRequest,
88+
)
89+
```
90+
91+
Methods:
92+
93+
- <code title="post /agent/identities">client.agent.agent.<a href="./src/oz_agent_sdk/resources/agent/agent_.py">create</a>(\*\*<a href="src/oz_agent_sdk/types/agent/agent_create_params.py">params</a>) -> <a href="./src/oz_agent_sdk/types/agent/agent_response.py">AgentResponse</a></code>
94+
- <code title="put /agent/identities/{uid}">client.agent.agent.<a href="./src/oz_agent_sdk/resources/agent/agent_.py">update</a>(uid, \*\*<a href="src/oz_agent_sdk/types/agent/agent_update_params.py">params</a>) -> <a href="./src/oz_agent_sdk/types/agent/agent_response.py">AgentResponse</a></code>
95+
- <code title="get /agent/identities">client.agent.agent.<a href="./src/oz_agent_sdk/resources/agent/agent_.py">list</a>() -> <a href="./src/oz_agent_sdk/types/agent/list_agent_identities_response.py">ListAgentIdentitiesResponse</a></code>
96+
- <code title="delete /agent/identities/{uid}">client.agent.agent.<a href="./src/oz_agent_sdk/resources/agent/agent_.py">delete</a>(uid) -> None</code>
97+
7298
## Sessions
7399

74100
Types:
@@ -80,3 +106,15 @@ from oz_agent_sdk.types.agent import SessionCheckRedirectResponse
80106
Methods:
81107

82108
- <code title="get /agent/sessions/{sessionUuid}/redirect">client.agent.sessions.<a href="./src/oz_agent_sdk/resources/agent/sessions.py">check_redirect</a>(session_uuid) -> <a href="./src/oz_agent_sdk/types/agent/session_check_redirect_response.py">SessionCheckRedirectResponse</a></code>
109+
110+
## Conversations
111+
112+
Types:
113+
114+
```python
115+
from oz_agent_sdk.types.agent import ConversationCheckRedirectResponse
116+
```
117+
118+
Methods:
119+
120+
- <code title="get /agent/conversations/{conversationId}/redirect">client.agent.conversations.<a href="./src/oz_agent_sdk/resources/agent/conversations.py">check_redirect</a>(conversation_id) -> <a href="./src/oz_agent_sdk/types/agent/conversation_check_redirect_response.py">ConversationCheckRedirectResponse</a></code>

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "oz-agent-sdk"
3-
version = "0.11.0"
3+
version = "0.12.0"
44
description = "The official Python library for the oz-api API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"
@@ -154,7 +154,7 @@ show_error_codes = true
154154
#
155155
# We also exclude our `tests` as mypy doesn't always infer
156156
# types correctly and Pyright will still catch any type errors.
157-
exclude = ['src/oz_agent_sdk/_files.py', '_dev/.*.py', 'tests/.*']
157+
exclude = ["src/oz_agent_sdk/_files.py", "_dev/.*.py", "tests/.*"]
158158

159159
strict_equality = true
160160
implicit_reexport = true

scripts/bootstrap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ set -e
44

55
cd "$(dirname "$0")/.."
66

7-
if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then
7+
if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then
88
brew bundle check >/dev/null 2>&1 || {
99
echo -n "==> Install Homebrew dependencies? (y/N): "
1010
read -r response

src/oz_agent_sdk/_client.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
RequestOptions,
2020
not_given,
2121
)
22-
from ._utils import is_given, get_async_library
22+
from ._utils import (
23+
is_given,
24+
is_mapping_t,
25+
get_async_library,
26+
)
2327
from ._compat import cached_property
2428
from ._models import SecurityOptions
2529
from ._version import __version__
@@ -82,6 +86,15 @@ def __init__(
8286
if base_url is None:
8387
base_url = f"https://app.warp.dev/api/v1"
8488

89+
custom_headers_env = os.environ.get("OZ_API_CUSTOM_HEADERS")
90+
if custom_headers_env is not None:
91+
parsed: dict[str, str] = {}
92+
for line in custom_headers_env.split("\n"):
93+
colon = line.find(":")
94+
if colon >= 0:
95+
parsed[line[:colon].strip()] = line[colon + 1 :].strip()
96+
default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})}
97+
8598
super().__init__(
8699
version=__version__,
87100
base_url=base_url,
@@ -262,6 +275,15 @@ def __init__(
262275
if base_url is None:
263276
base_url = f"https://app.warp.dev/api/v1"
264277

278+
custom_headers_env = os.environ.get("OZ_API_CUSTOM_HEADERS")
279+
if custom_headers_env is not None:
280+
parsed: dict[str, str] = {}
281+
for line in custom_headers_env.split("\n"):
282+
colon = line.find(":")
283+
if colon >= 0:
284+
parsed[line[:colon].strip()] = line[colon + 1 :].strip()
285+
default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})}
286+
265287
super().__init__(
266288
version=__version__,
267289
base_url=base_url,

src/oz_agent_sdk/_files.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import io
44
import os
55
import pathlib
6-
from typing import overload
7-
from typing_extensions import TypeGuard
6+
from typing import Sequence, cast, overload
7+
from typing_extensions import TypeVar, TypeGuard
88

99
import anyio
1010

@@ -17,7 +17,9 @@
1717
HttpxFileContent,
1818
HttpxRequestFiles,
1919
)
20-
from ._utils import is_tuple_t, is_mapping_t, is_sequence_t
20+
from ._utils import is_list, is_mapping, is_tuple_t, is_mapping_t, is_sequence_t
21+
22+
_T = TypeVar("_T")
2123

2224

2325
def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]:
@@ -121,3 +123,51 @@ async def async_read_file_content(file: FileContent) -> HttpxFileContent:
121123
return await anyio.Path(file).read_bytes()
122124

123125
return file
126+
127+
128+
def deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]]) -> _T:
129+
"""Copy only the containers along the given paths.
130+
131+
Used to guard against mutation by extract_files without copying the entire structure.
132+
Only dicts and lists that lie on a path are copied; everything else
133+
is returned by reference.
134+
135+
For example, given paths=[["foo", "files", "file"]] and the structure:
136+
{
137+
"foo": {
138+
"bar": {"baz": {}},
139+
"files": {"file": <content>}
140+
}
141+
}
142+
The root dict, "foo", and "files" are copied (they lie on the path).
143+
"bar" and "baz" are returned by reference (off the path).
144+
"""
145+
return _deepcopy_with_paths(item, paths, 0)
146+
147+
148+
def _deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]], index: int) -> _T:
149+
if not paths:
150+
return item
151+
if is_mapping(item):
152+
key_to_paths: dict[str, list[Sequence[str]]] = {}
153+
for path in paths:
154+
if index < len(path):
155+
key_to_paths.setdefault(path[index], []).append(path)
156+
157+
# if no path continues through this mapping, it won't be mutated and copying it is redundant
158+
if not key_to_paths:
159+
return item
160+
161+
result = dict(item)
162+
for key, subpaths in key_to_paths.items():
163+
if key in result:
164+
result[key] = _deepcopy_with_paths(result[key], subpaths, index + 1)
165+
return cast(_T, result)
166+
if is_list(item):
167+
array_paths = [path for path in paths if index < len(path) and path[index] == "<array>"]
168+
169+
# if no path expects a list here, nothing will be mutated inside it - return by reference
170+
if not array_paths:
171+
return cast(_T, item)
172+
return cast(_T, [_deepcopy_with_paths(entry, array_paths, index + 1) for entry in item])
173+
return item

src/oz_agent_sdk/_qs.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@
22

33
from typing import Any, List, Tuple, Union, Mapping, TypeVar
44
from urllib.parse import parse_qs, urlencode
5-
from typing_extensions import Literal, get_args
5+
from typing_extensions import get_args
66

7-
from ._types import NotGiven, not_given
7+
from ._types import NotGiven, ArrayFormat, NestedFormat, not_given
88
from ._utils import flatten
99

1010
_T = TypeVar("_T")
1111

12-
13-
ArrayFormat = Literal["comma", "repeat", "indices", "brackets"]
14-
NestedFormat = Literal["dots", "brackets"]
15-
1612
PrimitiveData = Union[str, int, float, bool, None]
1713
# this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"]
1814
# https://github.com/microsoft/pyright/issues/3555

src/oz_agent_sdk/_types.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
ModelT = TypeVar("ModelT", bound=pydantic.BaseModel)
4848
_T = TypeVar("_T")
4949

50+
ArrayFormat = Literal["comma", "repeat", "indices", "brackets"]
51+
NestedFormat = Literal["dots", "brackets"]
52+
5053

5154
# Approximates httpx internal ProxiesTypes and RequestFiles types
5255
# while adding support for `PathLike` instances

0 commit comments

Comments
 (0)