zoowork 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zoowork-0.1.0/.github/workflows/ci.yml +29 -0
- zoowork-0.1.0/.github/workflows/release.yml +42 -0
- zoowork-0.1.0/.gitignore +12 -0
- zoowork-0.1.0/AGENTS.md +13 -0
- zoowork-0.1.0/CHANGELOG.md +8 -0
- zoowork-0.1.0/LICENSE +21 -0
- zoowork-0.1.0/PKG-INFO +139 -0
- zoowork-0.1.0/README.md +107 -0
- zoowork-0.1.0/e2e/README.md +21 -0
- zoowork-0.1.0/e2e/smoke.py +55 -0
- zoowork-0.1.0/examples/quickstart.py +31 -0
- zoowork-0.1.0/pyproject.toml +61 -0
- zoowork-0.1.0/src/zoowork/__init__.py +48 -0
- zoowork-0.1.0/src/zoowork/_version.py +1 -0
- zoowork-0.1.0/src/zoowork/client.py +1133 -0
- zoowork-0.1.0/src/zoowork/events.py +189 -0
- zoowork-0.1.0/src/zoowork/py.typed +1 -0
- zoowork-0.1.0/src/zoowork/sse.py +63 -0
- zoowork-0.1.0/tests/test_client.py +179 -0
- zoowork-0.1.0/tests/test_events.py +91 -0
- zoowork-0.1.0/tests/test_sse.py +35 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.10", "3.14"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
with:
|
|
19
|
+
persist-credentials: false
|
|
20
|
+
- uses: actions/setup-python@v6
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
cache: pip
|
|
24
|
+
- run: python -m pip install -e '.[dev]'
|
|
25
|
+
- run: python -m pytest
|
|
26
|
+
- run: python -m ruff check .
|
|
27
|
+
- run: python -m mypy src
|
|
28
|
+
- run: python -m build
|
|
29
|
+
- run: python -m twine check dist/*
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v6
|
|
15
|
+
with:
|
|
16
|
+
persist-credentials: false
|
|
17
|
+
- uses: actions/setup-python@v6
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.x"
|
|
20
|
+
cache: pip
|
|
21
|
+
- run: python -m pip install build
|
|
22
|
+
- run: python -m build
|
|
23
|
+
- uses: actions/upload-artifact@v5
|
|
24
|
+
with:
|
|
25
|
+
name: python-package-distributions
|
|
26
|
+
path: dist/
|
|
27
|
+
|
|
28
|
+
publish:
|
|
29
|
+
needs: build
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
environment:
|
|
32
|
+
name: pypi
|
|
33
|
+
url: https://pypi.org/project/zoowork/
|
|
34
|
+
permissions:
|
|
35
|
+
contents: read
|
|
36
|
+
id-token: write
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/download-artifact@v6
|
|
39
|
+
with:
|
|
40
|
+
name: python-package-distributions
|
|
41
|
+
path: dist/
|
|
42
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
zoowork-0.1.0/.gitignore
ADDED
zoowork-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# ZooWork Python SDK
|
|
2
|
+
|
|
3
|
+
This repository is the official Python client for the public ZooWork Managed Agents API.
|
|
4
|
+
|
|
5
|
+
- Keep the runtime surface aligned with `zoowork-sdk-typescript`; API behavior comes from the public gateway contract, not Python-specific invention.
|
|
6
|
+
- Public methods use Python `snake_case`; request and response field names remain the API's original spelling.
|
|
7
|
+
- Treat identifiers and unknown response fields as opaque. Preserve server error codes verbatim in `ZooworkError`.
|
|
8
|
+
- Never put API keys, staging credentials, private payloads, or recorded customer data in source, fixtures, logs, or documentation.
|
|
9
|
+
- Unit tests and CI are offline. Live staging checks belong under `e2e/` and must be run explicitly before publication.
|
|
10
|
+
- The package is Python 3.10+ and uses `httpx` for async HTTP and streaming. Avoid adding runtime dependencies without a concrete public-SDK need.
|
|
11
|
+
- Run `python -m pytest`, `python -m ruff check .`, `python -m mypy src`, and `python -m build` before committing.
|
|
12
|
+
- Publishing is performed by `.github/workflows/release.yml` through PyPI Trusted Publishing. Do not add a long-lived PyPI token.
|
|
13
|
+
- Do not bump the package version or publish unless the user explicitly requests a release.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Initial Python SDK for ZooWork Managed Agents.
|
|
6
|
+
- Async HTTP client, paginated agent listing, durable event history and SSE streaming.
|
|
7
|
+
- Agent, channel, skill, session, approval, artifact, schedule, wake, exec and Environment operations.
|
|
8
|
+
- PyPI Trusted Publishing workflow.
|
zoowork-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SerendipityOneInc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
zoowork-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: zoowork
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for the ZooWork Managed Agents API
|
|
5
|
+
Project-URL: Documentation, https://github.com/SerendipityOneInc/zoowork-agents-docs
|
|
6
|
+
Project-URL: Repository, https://github.com/SerendipityOneInc/zoowork-sdk-python
|
|
7
|
+
Project-URL: Issues, https://github.com/SerendipityOneInc/zoowork-sdk-python/issues
|
|
8
|
+
Author: SerendipityOneInc
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,llm,sdk,sse,streaming,zoowork
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: httpx<1,>=0.27
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
26
|
+
Requires-Dist: mypy>=1.13; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=8.3; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
30
|
+
Requires-Dist: twine>=6; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# zoowork
|
|
34
|
+
|
|
35
|
+
Official Python SDK for the [ZooWork Managed Agents API](https://github.com/SerendipityOneInc/zoowork-agents-docs). Developer Preview.
|
|
36
|
+
|
|
37
|
+
The client is asynchronous, typed, and built on `httpx`. Request and response fields retain the public API's wire spelling, while methods use Python `snake_case`.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install zoowork
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quickstart
|
|
44
|
+
|
|
45
|
+
Create an organization API key (`zct_...`) in ZooWork under **Settings → API Keys** and keep it on your server. It authenticates as the organization, not as one end user.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import asyncio
|
|
49
|
+
import os
|
|
50
|
+
|
|
51
|
+
from zoowork import assistant_text, create_zoowork_client, is_run_finished
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def main() -> None:
|
|
55
|
+
async with create_zoowork_client(os.environ["ZOOWORK_API_KEY"]) as client:
|
|
56
|
+
models = await client.list_models()
|
|
57
|
+
agent = await client.create_agent(
|
|
58
|
+
{"name": "research-agent", "model": {"primary": models[0]["model"]}}
|
|
59
|
+
)
|
|
60
|
+
agent_id = agent["agent_id"]
|
|
61
|
+
|
|
62
|
+
await client.start_agent(agent_id)
|
|
63
|
+
await client.wait_until_running(agent_id)
|
|
64
|
+
session = await client.create_session(
|
|
65
|
+
agent_id,
|
|
66
|
+
{"initial_events": [{"type": "user.message", "content": "What can you do?"}]},
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
async for event in client.stream_events(agent_id, session["session_id"]):
|
|
70
|
+
print(assistant_text(event), end="", flush=True)
|
|
71
|
+
if is_run_finished(event):
|
|
72
|
+
break
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
asyncio.run(main())
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Set `ZOOWORK_API_KEY` and call `create_zoowork_client()` with no argument if you prefer. The client uses the production API by default; `ZOOWORK_BASE_URL` or `base_url=` selects another deployment.
|
|
79
|
+
|
|
80
|
+
## Pagination
|
|
81
|
+
|
|
82
|
+
`list_agents()` returns one `AgentPage`. Iterate the page to continue lazily through every remaining page while retaining the original filters:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
page = await client.list_agents(labels={"project": "research"})
|
|
86
|
+
print(page.data, page.total, page.next_page)
|
|
87
|
+
|
|
88
|
+
async for agent in page:
|
|
89
|
+
print(agent["agent_id"])
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Use `await page.get_next_page()` for manual navigation or `client.iter_agents()` when you do not need the first page's metadata.
|
|
93
|
+
|
|
94
|
+
## Durable events
|
|
95
|
+
|
|
96
|
+
The event stream is session-scoped and does not close when one turn ends. Break on `is_run_finished(event)`. Save `event.cursor` after consuming an event and pass it back as `cursor=` when reconnecting.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
async for event in client.stream_events(agent_id, session_id, cursor=last_cursor):
|
|
100
|
+
if event.cursor is not None:
|
|
101
|
+
last_cursor = event.cursor
|
|
102
|
+
if is_run_finished(event):
|
|
103
|
+
break
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`list_events()` reads one durable page. `list_all_events()` follows cursor pagination, with a safe fallback for older deployments.
|
|
107
|
+
|
|
108
|
+
## API surface
|
|
109
|
+
|
|
110
|
+
The runtime client follows the TypeScript SDK's public capabilities:
|
|
111
|
+
|
|
112
|
+
- agents, lifecycle, models and paginated listing;
|
|
113
|
+
- channels and guided Feishu/WeCom/WeChat setup;
|
|
114
|
+
- skill upload, versioning and agent attachment;
|
|
115
|
+
- sessions, events and SSE streaming;
|
|
116
|
+
- approvals, artifacts and system prompts;
|
|
117
|
+
- schedules, wake and sandbox exec;
|
|
118
|
+
- Environments and immutable Environment versions.
|
|
119
|
+
|
|
120
|
+
Methods return dictionaries containing the API response unchanged unless the SDK must normalize pagination or events. Unknown fields are intentionally preserved.
|
|
121
|
+
|
|
122
|
+
## Errors
|
|
123
|
+
|
|
124
|
+
Every non-successful response raises `ZooworkError`. Match `error.type` or `error.status`, never the human-readable message. Diagnostic fields include `content_type`, `body_snippet`, `cf_ray`, `request_id`, and `retryable`.
|
|
125
|
+
|
|
126
|
+
## Development
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
python -m venv .venv
|
|
130
|
+
. .venv/bin/activate
|
|
131
|
+
python -m pip install -e '.[dev]'
|
|
132
|
+
python -m pytest
|
|
133
|
+
python -m ruff check .
|
|
134
|
+
python -m mypy src
|
|
135
|
+
python -m build
|
|
136
|
+
python -m twine check dist/*
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Unit tests are offline. The live staging procedure is documented in [`e2e/README.md`](e2e/README.md) and is never run by CI.
|
zoowork-0.1.0/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# zoowork
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the [ZooWork Managed Agents API](https://github.com/SerendipityOneInc/zoowork-agents-docs). Developer Preview.
|
|
4
|
+
|
|
5
|
+
The client is asynchronous, typed, and built on `httpx`. Request and response fields retain the public API's wire spelling, while methods use Python `snake_case`.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install zoowork
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
Create an organization API key (`zct_...`) in ZooWork under **Settings → API Keys** and keep it on your server. It authenticates as the organization, not as one end user.
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import asyncio
|
|
17
|
+
import os
|
|
18
|
+
|
|
19
|
+
from zoowork import assistant_text, create_zoowork_client, is_run_finished
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def main() -> None:
|
|
23
|
+
async with create_zoowork_client(os.environ["ZOOWORK_API_KEY"]) as client:
|
|
24
|
+
models = await client.list_models()
|
|
25
|
+
agent = await client.create_agent(
|
|
26
|
+
{"name": "research-agent", "model": {"primary": models[0]["model"]}}
|
|
27
|
+
)
|
|
28
|
+
agent_id = agent["agent_id"]
|
|
29
|
+
|
|
30
|
+
await client.start_agent(agent_id)
|
|
31
|
+
await client.wait_until_running(agent_id)
|
|
32
|
+
session = await client.create_session(
|
|
33
|
+
agent_id,
|
|
34
|
+
{"initial_events": [{"type": "user.message", "content": "What can you do?"}]},
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
async for event in client.stream_events(agent_id, session["session_id"]):
|
|
38
|
+
print(assistant_text(event), end="", flush=True)
|
|
39
|
+
if is_run_finished(event):
|
|
40
|
+
break
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
asyncio.run(main())
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Set `ZOOWORK_API_KEY` and call `create_zoowork_client()` with no argument if you prefer. The client uses the production API by default; `ZOOWORK_BASE_URL` or `base_url=` selects another deployment.
|
|
47
|
+
|
|
48
|
+
## Pagination
|
|
49
|
+
|
|
50
|
+
`list_agents()` returns one `AgentPage`. Iterate the page to continue lazily through every remaining page while retaining the original filters:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
page = await client.list_agents(labels={"project": "research"})
|
|
54
|
+
print(page.data, page.total, page.next_page)
|
|
55
|
+
|
|
56
|
+
async for agent in page:
|
|
57
|
+
print(agent["agent_id"])
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Use `await page.get_next_page()` for manual navigation or `client.iter_agents()` when you do not need the first page's metadata.
|
|
61
|
+
|
|
62
|
+
## Durable events
|
|
63
|
+
|
|
64
|
+
The event stream is session-scoped and does not close when one turn ends. Break on `is_run_finished(event)`. Save `event.cursor` after consuming an event and pass it back as `cursor=` when reconnecting.
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
async for event in client.stream_events(agent_id, session_id, cursor=last_cursor):
|
|
68
|
+
if event.cursor is not None:
|
|
69
|
+
last_cursor = event.cursor
|
|
70
|
+
if is_run_finished(event):
|
|
71
|
+
break
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`list_events()` reads one durable page. `list_all_events()` follows cursor pagination, with a safe fallback for older deployments.
|
|
75
|
+
|
|
76
|
+
## API surface
|
|
77
|
+
|
|
78
|
+
The runtime client follows the TypeScript SDK's public capabilities:
|
|
79
|
+
|
|
80
|
+
- agents, lifecycle, models and paginated listing;
|
|
81
|
+
- channels and guided Feishu/WeCom/WeChat setup;
|
|
82
|
+
- skill upload, versioning and agent attachment;
|
|
83
|
+
- sessions, events and SSE streaming;
|
|
84
|
+
- approvals, artifacts and system prompts;
|
|
85
|
+
- schedules, wake and sandbox exec;
|
|
86
|
+
- Environments and immutable Environment versions.
|
|
87
|
+
|
|
88
|
+
Methods return dictionaries containing the API response unchanged unless the SDK must normalize pagination or events. Unknown fields are intentionally preserved.
|
|
89
|
+
|
|
90
|
+
## Errors
|
|
91
|
+
|
|
92
|
+
Every non-successful response raises `ZooworkError`. Match `error.type` or `error.status`, never the human-readable message. Diagnostic fields include `content_type`, `body_snippet`, `cf_ray`, `request_id`, and `retryable`.
|
|
93
|
+
|
|
94
|
+
## Development
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
python -m venv .venv
|
|
98
|
+
. .venv/bin/activate
|
|
99
|
+
python -m pip install -e '.[dev]'
|
|
100
|
+
python -m pytest
|
|
101
|
+
python -m ruff check .
|
|
102
|
+
python -m mypy src
|
|
103
|
+
python -m build
|
|
104
|
+
python -m twine check dist/*
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Unit tests are offline. The live staging procedure is documented in [`e2e/README.md`](e2e/README.md) and is never run by CI.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Staging E2E
|
|
2
|
+
|
|
3
|
+
This check installs the candidate package and exercises one real Agent and Session against an explicitly selected staging deployment. It may create a billable model turn.
|
|
4
|
+
|
|
5
|
+
Do not put the staging key in the repository, shell history, CI or test output. Read it without echoing, install the built wheel into an isolated environment, and run the smoke test only when a release candidate is ready:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python -m build
|
|
9
|
+
candidate_venv="$(mktemp -d)/venv"
|
|
10
|
+
python -m venv "$candidate_venv"
|
|
11
|
+
"$candidate_venv/bin/python" -m pip install dist/zoowork-*.whl
|
|
12
|
+
read -r -s ZOOWORK_API_KEY
|
|
13
|
+
export ZOOWORK_API_KEY
|
|
14
|
+
export ZOOWORK_BASE_URL=https://staging.example/service/v1
|
|
15
|
+
"$candidate_venv/bin/python" e2e/smoke.py
|
|
16
|
+
unset ZOOWORK_API_KEY
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`ZOOWORK_BASE_URL` is required so this release check cannot silently target production. The script creates an Agent, waits on `status.desired_state`, sends one message, waits for `run.finished`, verifies assistant text, and best-effort deletes the Agent in `finally`.
|
|
20
|
+
|
|
21
|
+
Passing this check does not publish the package. Publish the exact wheel that was tested; rebuild or source changes invalidate the result.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import os
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
from zoowork import assistant_text, create_zoowork_client, is_run_finished, run_outcome
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def main() -> None:
|
|
11
|
+
api_key = os.getenv("ZOOWORK_API_KEY")
|
|
12
|
+
base_url = os.getenv("ZOOWORK_BASE_URL")
|
|
13
|
+
if not api_key or not base_url:
|
|
14
|
+
raise SystemExit("Set ZOOWORK_API_KEY and an explicit staging ZOOWORK_BASE_URL")
|
|
15
|
+
|
|
16
|
+
async with create_zoowork_client(api_key, base_url=base_url) as client:
|
|
17
|
+
models = await client.list_models()
|
|
18
|
+
if not models or not isinstance(models[0].get("model"), str):
|
|
19
|
+
raise RuntimeError("Staging returned no usable model")
|
|
20
|
+
agent = await client.create_agent(
|
|
21
|
+
{
|
|
22
|
+
"name": f"python-sdk-e2e-{uuid.uuid4().hex[:10]}",
|
|
23
|
+
"model": {"primary": models[0]["model"]},
|
|
24
|
+
"labels": {"purpose": "python-sdk-release-e2e"},
|
|
25
|
+
},
|
|
26
|
+
idempotency_key=str(uuid.uuid4()),
|
|
27
|
+
)
|
|
28
|
+
agent_id = str(agent["agent_id"])
|
|
29
|
+
try:
|
|
30
|
+
await client.start_agent(agent_id)
|
|
31
|
+
await client.wait_until_running(agent_id)
|
|
32
|
+
session = await client.create_session(
|
|
33
|
+
agent_id,
|
|
34
|
+
{
|
|
35
|
+
"initial_events": [
|
|
36
|
+
{"type": "user.message", "content": "Reply with exactly: python sdk ok"}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
idempotency_key=str(uuid.uuid4()),
|
|
40
|
+
)
|
|
41
|
+
text = ""
|
|
42
|
+
outcome: str | None = None
|
|
43
|
+
async for event in client.stream_events(agent_id, str(session["session_id"])):
|
|
44
|
+
text += assistant_text(event)
|
|
45
|
+
if is_run_finished(event):
|
|
46
|
+
outcome = run_outcome(event)
|
|
47
|
+
break
|
|
48
|
+
if outcome != "succeeded" or "python sdk ok" not in text.lower():
|
|
49
|
+
raise RuntimeError(f"Unexpected turn result: outcome={outcome!r}, text={text!r}")
|
|
50
|
+
finally:
|
|
51
|
+
await client.delete_agent(agent_id)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
if __name__ == "__main__":
|
|
55
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from zoowork import assistant_text, create_zoowork_client, is_run_finished
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def main() -> None:
|
|
7
|
+
async with create_zoowork_client() as client:
|
|
8
|
+
models = await client.list_models()
|
|
9
|
+
if not models:
|
|
10
|
+
raise RuntimeError("This deployment returned no models")
|
|
11
|
+
agent = await client.create_agent(
|
|
12
|
+
{"name": "python-sdk-example", "model": {"primary": models[0]["model"]}}
|
|
13
|
+
)
|
|
14
|
+
agent_id = agent["agent_id"]
|
|
15
|
+
try:
|
|
16
|
+
await client.start_agent(agent_id)
|
|
17
|
+
await client.wait_until_running(agent_id)
|
|
18
|
+
session = await client.create_session(
|
|
19
|
+
agent_id,
|
|
20
|
+
{"initial_events": [{"type": "user.message", "content": "Say hello."}]},
|
|
21
|
+
)
|
|
22
|
+
async for event in client.stream_events(agent_id, session["session_id"]):
|
|
23
|
+
print(assistant_text(event), end="", flush=True)
|
|
24
|
+
if is_run_finished(event):
|
|
25
|
+
break
|
|
26
|
+
finally:
|
|
27
|
+
await client.delete_agent(agent_id)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "zoowork"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official Python SDK for the ZooWork Managed Agents API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "SerendipityOneInc" }]
|
|
13
|
+
keywords = ["zoowork", "agents", "sdk", "llm", "sse", "streaming"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
dependencies = ["httpx>=0.27,<1"]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = [
|
|
30
|
+
"build>=1.2",
|
|
31
|
+
"mypy>=1.13",
|
|
32
|
+
"pytest>=8.3",
|
|
33
|
+
"pytest-asyncio>=0.24",
|
|
34
|
+
"ruff>=0.8",
|
|
35
|
+
"twine>=6",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Documentation = "https://github.com/SerendipityOneInc/zoowork-agents-docs"
|
|
40
|
+
Repository = "https://github.com/SerendipityOneInc/zoowork-sdk-python"
|
|
41
|
+
Issues = "https://github.com/SerendipityOneInc/zoowork-sdk-python/issues"
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.wheel]
|
|
44
|
+
packages = ["src/zoowork"]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
addopts = "-q"
|
|
48
|
+
asyncio_mode = "auto"
|
|
49
|
+
testpaths = ["tests"]
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
line-length = 100
|
|
53
|
+
target-version = "py310"
|
|
54
|
+
|
|
55
|
+
[tool.ruff.lint]
|
|
56
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
|
|
57
|
+
|
|
58
|
+
[tool.mypy]
|
|
59
|
+
python_version = "3.10"
|
|
60
|
+
strict = true
|
|
61
|
+
packages = ["zoowork"]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Official Python SDK for ZooWork Managed Agents."""
|
|
2
|
+
|
|
3
|
+
from ._version import __version__
|
|
4
|
+
from .client import (
|
|
5
|
+
DEFAULT_BASE_URL,
|
|
6
|
+
AgentPage,
|
|
7
|
+
JsonObject,
|
|
8
|
+
ZooworkClient,
|
|
9
|
+
ZooworkError,
|
|
10
|
+
create_zoowork_client,
|
|
11
|
+
)
|
|
12
|
+
from .events import (
|
|
13
|
+
PUBLIC_INPUT_EVENT_TYPES,
|
|
14
|
+
SESSION_EVENT_TYPES,
|
|
15
|
+
SessionEvent,
|
|
16
|
+
ToolCall,
|
|
17
|
+
assistant_text,
|
|
18
|
+
is_run_finished,
|
|
19
|
+
message_text,
|
|
20
|
+
normalize_event,
|
|
21
|
+
run_outcome,
|
|
22
|
+
thinking_text,
|
|
23
|
+
tool_call,
|
|
24
|
+
)
|
|
25
|
+
from .sse import SSEMessage, parse_sse
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"DEFAULT_BASE_URL",
|
|
29
|
+
"PUBLIC_INPUT_EVENT_TYPES",
|
|
30
|
+
"SESSION_EVENT_TYPES",
|
|
31
|
+
"AgentPage",
|
|
32
|
+
"JsonObject",
|
|
33
|
+
"SSEMessage",
|
|
34
|
+
"SessionEvent",
|
|
35
|
+
"ToolCall",
|
|
36
|
+
"ZooworkClient",
|
|
37
|
+
"ZooworkError",
|
|
38
|
+
"__version__",
|
|
39
|
+
"assistant_text",
|
|
40
|
+
"create_zoowork_client",
|
|
41
|
+
"is_run_finished",
|
|
42
|
+
"message_text",
|
|
43
|
+
"normalize_event",
|
|
44
|
+
"parse_sse",
|
|
45
|
+
"run_outcome",
|
|
46
|
+
"thinking_text",
|
|
47
|
+
"tool_call",
|
|
48
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|