agent-supervisor 0.2.0__tar.gz → 0.3.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.
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/Cargo.lock +69 -1
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/Cargo.toml +2 -1
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/PKG-INFO +9 -10
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/README.md +8 -9
- agent_supervisor-0.3.0/agent_supervisor/__init__.py +165 -0
- agent_supervisor-0.3.0/agent_supervisor/_lifecycle.py +248 -0
- agent_supervisor-0.3.0/agent_supervisor/_run_context.py +11 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/agent_supervisor/agent_run.py +24 -19
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/agent_supervisor/google_adk.py +5 -4
- agent_supervisor-0.3.0/agent_supervisor/langgraph.py +18 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/agent_supervisor/microsoft.py +35 -31
- agent_supervisor-0.3.0/agent_supervisor/openai.py +112 -0
- agent_supervisor-0.3.0/agent_supervisor/pydantic_ai.py +54 -0
- agent_supervisor-0.3.0/dist/agent_supervisor-0.3.0-cp39-abi3-manylinux_2_39_x86_64.whl +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/api-boundaries.md +42 -30
- agent_supervisor-0.3.0/docs/api-reference.md +229 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/index.md +16 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/openshell.md +1 -1
- agent_supervisor-0.3.0/docs/python-api.md +855 -0
- agent_supervisor-0.3.0/docs/quickstart.md +309 -0
- agent_supervisor-0.3.0/docs/releases/0.3.0.md +78 -0
- agent_supervisor-0.3.0/docs/rust-api.md +735 -0
- agent_supervisor-0.3.0/docs/supervisor-lifecycle.md +130 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/wasm-backend.md +1 -1
- agent_supervisor-0.3.0/examples/README.md +51 -0
- agent_supervisor-0.3.0/examples/python/supervisor_background_jobs.py +55 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/mkdocs.yml +12 -10
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/pyproject.toml +1 -1
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/python/Cargo.lock +99 -2
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/python/Cargo.toml +1 -1
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/python/README.md +8 -9
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/python/src/lib.rs +440 -313
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/python/src/mcp_stdio.rs +2 -0
- agent_supervisor-0.3.0/python/tests/test_api.py +581 -0
- agent_supervisor-0.3.0/python/tests/test_framework_adapters.py +386 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/python/tests/test_microsoft.py +52 -16
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/python/tests/test_openai.py +53 -23
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/agent_run.rs +181 -64
- agent_supervisor-0.3.0/src/event_log.rs +669 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/events.rs +62 -25
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/lib.rs +7 -2
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/process_control.rs +6 -1
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/supervisor.rs +1393 -26
- agent_supervisor-0.2.0/agent_supervisor/__init__.py +0 -145
- agent_supervisor-0.2.0/agent_supervisor/_lifecycle.py +0 -161
- agent_supervisor-0.2.0/agent_supervisor/_run_scope.py +0 -11
- agent_supervisor-0.2.0/agent_supervisor/langgraph.py +0 -15
- agent_supervisor-0.2.0/agent_supervisor/openai.py +0 -103
- agent_supervisor-0.2.0/agent_supervisor/pydantic_ai.py +0 -13
- agent_supervisor-0.2.0/dist/agent_supervisor-0.2.0-cp39-abi3-manylinux_2_39_x86_64.whl +0 -0
- agent_supervisor-0.2.0/docs/api-reference.md +0 -156
- agent_supervisor-0.2.0/docs/python-api.md +0 -508
- agent_supervisor-0.2.0/docs/quickstart.md +0 -122
- agent_supervisor-0.2.0/docs/rust-api.md +0 -349
- agent_supervisor-0.2.0/docs/supervisor-lifecycle.md +0 -20
- agent_supervisor-0.2.0/examples/README.md +0 -16
- agent_supervisor-0.2.0/python/tests/test_api.py +0 -378
- agent_supervisor-0.2.0/python/tests/test_framework_adapters.py +0 -234
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/.github/workflows/ci.yml +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/.github/workflows/docs.yml +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/.github/workflows/publish-python.yml +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/.gitignore +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/LICENSE +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/agent_supervisor/fastmcp.py +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/agent_supervisor/mcp.py +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/javascripts/mermaid-init.js +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/mcp.md +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/network-host-mode.md +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/process-groups.md +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/releases/0.1.0.md +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/releases/0.1.1.md +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/releases/0.1.2.md +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/releases/0.1.3.md +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/releases/0.2.0.md +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/security-model.md +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/stylesheets/extra.css +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/docs/testing.md +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/examples/python/official_mcp_server.py +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/examples/python/prefect_fastmcp_server.py +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/python/tests/fixtures/official_mcp_server.py +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/python/tests/fixtures/prefect_mcp_server.py +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/python/tests/test_mcp_adapters.py +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/python/tests/test_supervision_settings.py +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/environment.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/error.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/filesystem.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/group.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/mcp/policy.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/mcp/relay.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/mcp.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/network.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/pipeline.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/policy.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/probes.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/pty.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/runner.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/wasm.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/src/windows_filesystem.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/tests/filesystem_unsupported.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/tests/fixtures/mcp_stdio_fixture.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/tests/linux_filesystem.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/tests/loom_supervisor.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/tests/macos_filesystem.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/tests/macos_smoke.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/tests/mcp_stdio.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/tests/resource_limits_properties.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/tests/windows_filesystem.rs +0 -0
- {agent_supervisor-0.2.0 → agent_supervisor-0.3.0}/tests/windows_smoke.rs +0 -0
|
@@ -4,7 +4,7 @@ version = 4
|
|
|
4
4
|
|
|
5
5
|
[[package]]
|
|
6
6
|
name = "agent-supervisor"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
dependencies = [
|
|
9
9
|
"futures",
|
|
10
10
|
"landlock",
|
|
@@ -19,6 +19,7 @@ dependencies = [
|
|
|
19
19
|
"thiserror 1.0.69",
|
|
20
20
|
"tokio",
|
|
21
21
|
"tokio-util",
|
|
22
|
+
"uuid",
|
|
22
23
|
"wasmi",
|
|
23
24
|
"wat",
|
|
24
25
|
"windows-sys",
|
|
@@ -340,6 +341,17 @@ version = "1.0.18"
|
|
|
340
341
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
341
342
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
342
343
|
|
|
344
|
+
[[package]]
|
|
345
|
+
name = "js-sys"
|
|
346
|
+
version = "0.3.107"
|
|
347
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
348
|
+
checksum = "8dc09d2154098bbf4ef54eab6b117a3c24cbbffe94046b16ee6d6d4510584d3a"
|
|
349
|
+
dependencies = [
|
|
350
|
+
"cfg-if",
|
|
351
|
+
"futures-util",
|
|
352
|
+
"wasm-bindgen",
|
|
353
|
+
]
|
|
354
|
+
|
|
343
355
|
[[package]]
|
|
344
356
|
name = "landlock"
|
|
345
357
|
version = "0.4.7"
|
|
@@ -984,6 +996,18 @@ version = "0.2.3"
|
|
|
984
996
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
985
997
|
checksum = "ca718471876992059f251e33aafc334346ada08042b7661dd1fe313a9bced557"
|
|
986
998
|
|
|
999
|
+
[[package]]
|
|
1000
|
+
name = "uuid"
|
|
1001
|
+
version = "1.29.0"
|
|
1002
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1003
|
+
checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514"
|
|
1004
|
+
dependencies = [
|
|
1005
|
+
"getrandom 0.4.3",
|
|
1006
|
+
"js-sys",
|
|
1007
|
+
"serde_core",
|
|
1008
|
+
"wasm-bindgen",
|
|
1009
|
+
]
|
|
1010
|
+
|
|
987
1011
|
[[package]]
|
|
988
1012
|
name = "valuable"
|
|
989
1013
|
version = "0.1.1"
|
|
@@ -1014,6 +1038,50 @@ dependencies = [
|
|
|
1014
1038
|
"wit-bindgen",
|
|
1015
1039
|
]
|
|
1016
1040
|
|
|
1041
|
+
[[package]]
|
|
1042
|
+
name = "wasm-bindgen"
|
|
1043
|
+
version = "0.2.130"
|
|
1044
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1045
|
+
checksum = "a0181c14ddd6c7b8b0b0132a5f1ea2f32be0d893cd3ea97b6e3b28886b8a3d1c"
|
|
1046
|
+
dependencies = [
|
|
1047
|
+
"cfg-if",
|
|
1048
|
+
"rustversion",
|
|
1049
|
+
"wasm-bindgen-macro",
|
|
1050
|
+
"wasm-bindgen-shared",
|
|
1051
|
+
]
|
|
1052
|
+
|
|
1053
|
+
[[package]]
|
|
1054
|
+
name = "wasm-bindgen-macro"
|
|
1055
|
+
version = "0.2.130"
|
|
1056
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1057
|
+
checksum = "6e931a02a39abdbf66966fc35902b5ed58344750a70885bed4ebb3d8eaf28ed9"
|
|
1058
|
+
dependencies = [
|
|
1059
|
+
"quote",
|
|
1060
|
+
"wasm-bindgen-macro-support",
|
|
1061
|
+
]
|
|
1062
|
+
|
|
1063
|
+
[[package]]
|
|
1064
|
+
name = "wasm-bindgen-macro-support"
|
|
1065
|
+
version = "0.2.130"
|
|
1066
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1067
|
+
checksum = "2ca4b54280e455bd831e33a6b8e734baf432fe63fbc35c42ac14ce67a043a53a"
|
|
1068
|
+
dependencies = [
|
|
1069
|
+
"bumpalo",
|
|
1070
|
+
"proc-macro2",
|
|
1071
|
+
"quote",
|
|
1072
|
+
"syn 3.0.5",
|
|
1073
|
+
"wasm-bindgen-shared",
|
|
1074
|
+
]
|
|
1075
|
+
|
|
1076
|
+
[[package]]
|
|
1077
|
+
name = "wasm-bindgen-shared"
|
|
1078
|
+
version = "0.2.130"
|
|
1079
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1080
|
+
checksum = "3b1f9c3798725016c6ee5f2f57869ec0831e8bbc0ab0310a86e787e97f41389e"
|
|
1081
|
+
dependencies = [
|
|
1082
|
+
"unicode-ident",
|
|
1083
|
+
]
|
|
1084
|
+
|
|
1017
1085
|
[[package]]
|
|
1018
1086
|
name = "wasm-encoder"
|
|
1019
1087
|
version = "0.262.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "agent-supervisor"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
license = "Apache-2.0"
|
|
6
6
|
description = "Policy-driven process isolation and supervision for RooAGI runtimes"
|
|
@@ -19,6 +19,7 @@ tokio-util = "0.7"
|
|
|
19
19
|
portable-pty = "0.9"
|
|
20
20
|
wasmi = { version = "2", optional = true }
|
|
21
21
|
serde_json = "1"
|
|
22
|
+
uuid = { version = "1", features = ["v4", "serde"] }
|
|
22
23
|
|
|
23
24
|
[features]
|
|
24
25
|
default = []
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-supervisor
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
6
6
|
Classifier: Operating System :: MacOS
|
|
@@ -31,11 +31,11 @@ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
|
31
31
|
|
|
32
32
|
Python bindings for the `agent-supervisor` native process supervisor.
|
|
33
33
|
|
|
34
|
-
Version 0.
|
|
35
|
-
streaming
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
Version 0.3.0 source provides bounded one-shot execution, output-event
|
|
35
|
+
streaming, manager-owned background jobs, supervisor-scoped process IDs, and
|
|
36
|
+
agent-run lifecycle management through the native Linux, macOS, and Windows
|
|
37
|
+
backends. The Rust crate's optional WASM runner is not exposed by the Python
|
|
38
|
+
binding.
|
|
39
39
|
|
|
40
40
|
Install the published package with `python -m pip install agent-supervisor`.
|
|
41
41
|
To use the current repository source, build it from this checkout:
|
|
@@ -55,9 +55,8 @@ The API and supported platform behavior are documented in
|
|
|
55
55
|
optional stdio adapters for the official MCP Python SDK and Prefect FastMCP; see
|
|
56
56
|
[`../docs/mcp.md`](../docs/mcp.md) for complete server examples and installation steps.
|
|
57
57
|
|
|
58
|
-
The 0.
|
|
59
|
-
|
|
60
|
-
published PyPI package is 0.1.2 and does not include these adapters yet. From a
|
|
58
|
+
The 0.3.0 source includes optional run adapters for OpenAI Agents SDK,
|
|
59
|
+
Microsoft Agent Framework, LangGraph, PydanticAI, and Google ADK. From a
|
|
61
60
|
checkout, install an adapter with its matching extra, for example:
|
|
62
61
|
|
|
63
62
|
```bash
|
|
@@ -66,7 +65,7 @@ python -m pip install -e '.[langgraph]'
|
|
|
66
65
|
```
|
|
67
66
|
|
|
68
67
|
Use `openai`, `microsoft`, `pydantic-ai`, or `google-adk` for the other
|
|
69
|
-
adapters. They share the Rust run
|
|
68
|
+
adapters. They share the Rust agent run,
|
|
70
69
|
apply the same default deadline and cleanup behavior, and clean up local tool
|
|
71
70
|
processes started through Agent Supervisor. Agent code remains in your app
|
|
72
71
|
process. See the
|
|
@@ -20,7 +20,7 @@ application remains responsible for identity, authentication, authorization,
|
|
|
20
20
|
and orchestration. Enforcement depends on the operating system and selected
|
|
21
21
|
policy; inspect the execution result and platform capabilities.
|
|
22
22
|
|
|
23
|
-
The current version in this checkout is **0.
|
|
23
|
+
The current version in this checkout is **0.3.0**.
|
|
24
24
|
|
|
25
25
|
## Who it is for
|
|
26
26
|
|
|
@@ -178,7 +178,7 @@ Add the crate:
|
|
|
178
178
|
|
|
179
179
|
```toml
|
|
180
180
|
[dependencies]
|
|
181
|
-
agent-supervisor = "0.
|
|
181
|
+
agent-supervisor = "0.3.0"
|
|
182
182
|
```
|
|
183
183
|
|
|
184
184
|
|
|
@@ -217,16 +217,14 @@ and [using agent SDKs](docs/api-boundaries.md) for the integration example.
|
|
|
217
217
|
|
|
218
218
|
## Python bindings
|
|
219
219
|
|
|
220
|
-
|
|
221
|
-
wheels are available for Apple Silicon macOS, x86-64 Windows, and x86-64 Linux
|
|
222
|
-
with glibc 2.39 or newer:
|
|
220
|
+
Install the Python package from PyPI:
|
|
223
221
|
|
|
224
222
|
```bash
|
|
225
|
-
python -m pip install agent-supervisor
|
|
223
|
+
python -m pip install agent-supervisor
|
|
226
224
|
```
|
|
227
225
|
|
|
228
|
-
|
|
229
|
-
|
|
226
|
+
To use the current 0.3.0 source from this checkout, build it with maturin; the
|
|
227
|
+
build steps are in [`python/README.md`](python/README.md).
|
|
230
228
|
|
|
231
229
|
See the [Python API guide](docs/python-api.md) for capture, streaming,
|
|
232
230
|
cancellation, and platform behavior.
|
|
@@ -282,7 +280,8 @@ behavior, and release notes:
|
|
|
282
280
|
|
|
283
281
|
## Release
|
|
284
282
|
|
|
285
|
-
See the [0.
|
|
283
|
+
See the [0.3.0 release notes](docs/releases/0.3.0.md) for manager-owned jobs,
|
|
284
|
+
supervisor-scoped process IDs, and store/resume behavior; the [0.2.0 release notes](docs/releases/0.2.0.md) cover agent-run scopes and
|
|
286
285
|
MCP integrations, the [0.1.3 notes](docs/releases/0.1.3.md) for the optional WASM
|
|
287
286
|
runner, the [0.1.2 notes](docs/releases/0.1.2.md) for Python bindings, and the
|
|
288
287
|
[0.1.1 notes](docs/releases/0.1.1.md) for the separate OpenShell developer
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Python bindings for agent-supervisor."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from ._native import (
|
|
6
|
+
CancellationToken,
|
|
7
|
+
Command,
|
|
8
|
+
OutputEvent,
|
|
9
|
+
Policy,
|
|
10
|
+
Process,
|
|
11
|
+
RunResult,
|
|
12
|
+
SupervisorProcessId,
|
|
13
|
+
SupervisorError,
|
|
14
|
+
SupervisorEvent,
|
|
15
|
+
SupervisorHandle,
|
|
16
|
+
platform_capabilities,
|
|
17
|
+
run as _run_native,
|
|
18
|
+
)
|
|
19
|
+
from .agent_run import AgentRun, AgentRunPolicy
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Supervisor:
|
|
23
|
+
"""Own agent runs and local child processes for the application.
|
|
24
|
+
|
|
25
|
+
Use ``start_agent_run`` to create a deadline and cleanup boundary for one
|
|
26
|
+
framework invocation. The supervisor also owns background jobs that can
|
|
27
|
+
continue between agent runs.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
self._native = SupervisorHandle()
|
|
32
|
+
|
|
33
|
+
def start_agent_run(self, policy=None):
|
|
34
|
+
"""Create one agent run owned by this supervisor."""
|
|
35
|
+
if policy is None:
|
|
36
|
+
policy = AgentRunPolicy()
|
|
37
|
+
if not isinstance(policy, AgentRunPolicy):
|
|
38
|
+
raise TypeError("policy must be an AgentRunPolicy")
|
|
39
|
+
native = self._native.start_agent_run(policy.timeout, policy.shutdown_grace)
|
|
40
|
+
return AgentRun(native, self, policy)
|
|
41
|
+
|
|
42
|
+
def active_agent_runs(self):
|
|
43
|
+
"""Return the number of agent runs currently owned by this supervisor."""
|
|
44
|
+
return self._native.active_agent_runs()
|
|
45
|
+
|
|
46
|
+
def active_processes(self):
|
|
47
|
+
"""Return the number of child processes registered with this supervisor."""
|
|
48
|
+
return self._native.active_processes()
|
|
49
|
+
|
|
50
|
+
async def start(self, command, *, policy=None):
|
|
51
|
+
"""Start a persistent child and return its interactive Process handle."""
|
|
52
|
+
run = _current_run_context()
|
|
53
|
+
if run is not None:
|
|
54
|
+
return await run.start(command, policy=policy)
|
|
55
|
+
return await self._native.start(command, policy=policy)
|
|
56
|
+
|
|
57
|
+
def stream(self, *, run=None, process=None):
|
|
58
|
+
"""Watch this supervisor's work as one ordered stream of events.
|
|
59
|
+
|
|
60
|
+
``async for event in supervisor.stream(...)``. This only observes:
|
|
61
|
+
launch work with :func:`run`, :meth:`start`, :meth:`start_background`,
|
|
62
|
+
or a framework adapter. Stopping the watcher stops nothing, and a slow
|
|
63
|
+
watcher never slows a process.
|
|
64
|
+
|
|
65
|
+
- ``stream()`` watches everything, until the supervisor is dropped.
|
|
66
|
+
- ``stream(run=run)`` watches one agent run, including its framework
|
|
67
|
+
events, and ends when the run ends.
|
|
68
|
+
- ``stream(process=process_id)`` watches one process or background job
|
|
69
|
+
and ends when it exits.
|
|
70
|
+
|
|
71
|
+
A watcher starts with the oldest retained event, so opening one just
|
|
72
|
+
after starting work does not miss it.
|
|
73
|
+
"""
|
|
74
|
+
native_run = run._native if run is not None else None
|
|
75
|
+
return self._native.stream(run=native_run, process=process)
|
|
76
|
+
|
|
77
|
+
async def start_background(self, command, *, policy=None):
|
|
78
|
+
"""Start a manager-owned job and return its stable SupervisorProcessId.
|
|
79
|
+
|
|
80
|
+
Persistent jobs are stopped explicitly with ``shutdown_process`` or
|
|
81
|
+
``shutdown_all``; ``Command.timeout`` does not end a background job.
|
|
82
|
+
The command's stdout and stderr byte limits still apply.
|
|
83
|
+
"""
|
|
84
|
+
return await self._native.start_background(command, policy=policy)
|
|
85
|
+
|
|
86
|
+
def process_status(self, process_id):
|
|
87
|
+
"""Return ``running``, ``completed``, ``failed``, or ``None`` if unknown."""
|
|
88
|
+
return self._native.process_status(process_id)
|
|
89
|
+
|
|
90
|
+
def process_result(self, process_id):
|
|
91
|
+
"""Return a completed RunResult snapshot, or ``None`` while active/unknown."""
|
|
92
|
+
return self._native.process_result(process_id)
|
|
93
|
+
|
|
94
|
+
async def wait_process(self, process_id):
|
|
95
|
+
"""Wait for a background job; return ``None`` if its ID is unknown."""
|
|
96
|
+
return await self._native.wait_process(process_id)
|
|
97
|
+
|
|
98
|
+
def take_process_result(self, process_id):
|
|
99
|
+
"""Remove and return a completed result, or ``None`` if not completed."""
|
|
100
|
+
return self._native.take_process_result(process_id)
|
|
101
|
+
|
|
102
|
+
def forget_process(self, process_id):
|
|
103
|
+
"""Forget a completed/failed result. Active jobs cannot be forgotten."""
|
|
104
|
+
return self._native.forget_process(process_id)
|
|
105
|
+
|
|
106
|
+
async def write_process_stdin(self, process_id, data):
|
|
107
|
+
"""Write bytes to a managed job's stdin."""
|
|
108
|
+
return await self._native.write_process_stdin(process_id, data)
|
|
109
|
+
|
|
110
|
+
async def close_process_stdin(self, process_id):
|
|
111
|
+
"""Close a managed job's stdin and deliver EOF."""
|
|
112
|
+
return await self._native.close_process_stdin(process_id)
|
|
113
|
+
|
|
114
|
+
async def shutdown_process(self, process_id, *, grace=5.0):
|
|
115
|
+
"""Stop one process tree; return the stop request outcome string."""
|
|
116
|
+
return await self._native.shutdown_process(process_id, grace=grace)
|
|
117
|
+
|
|
118
|
+
async def shutdown_all(self, *, grace=5.0):
|
|
119
|
+
"""Stop all jobs and return ``[(process_id, RunResult), ...]`` results."""
|
|
120
|
+
return await self._native.shutdown_all(grace=grace)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _current_run_context():
|
|
124
|
+
from ._run_context import current_run
|
|
125
|
+
return current_run()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
async def run(command, *, policy=None, input=None, cancellation=None):
|
|
129
|
+
"""Run one command, collecting bounded stdout and stderr bytes."""
|
|
130
|
+
token = cancellation if cancellation is not None else CancellationToken()
|
|
131
|
+
run = _current_run_context()
|
|
132
|
+
operation = asyncio.ensure_future(
|
|
133
|
+
run.run_command(command, policy=policy, input=input) if run is not None
|
|
134
|
+
else _run_native(command, policy=policy, input=input, cancellation=token)
|
|
135
|
+
)
|
|
136
|
+
try:
|
|
137
|
+
return await asyncio.shield(operation)
|
|
138
|
+
except asyncio.CancelledError:
|
|
139
|
+
if run is not None:
|
|
140
|
+
await run.cancel()
|
|
141
|
+
else:
|
|
142
|
+
token.cancel()
|
|
143
|
+
try:
|
|
144
|
+
await asyncio.shield(operation)
|
|
145
|
+
except SupervisorError:
|
|
146
|
+
pass
|
|
147
|
+
raise
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
__all__ = [
|
|
151
|
+
"CancellationToken",
|
|
152
|
+
"AgentRunPolicy",
|
|
153
|
+
"AgentRun",
|
|
154
|
+
"Command",
|
|
155
|
+
"OutputEvent",
|
|
156
|
+
"Policy",
|
|
157
|
+
"Process",
|
|
158
|
+
"RunResult",
|
|
159
|
+
"SupervisorProcessId",
|
|
160
|
+
"SupervisorError",
|
|
161
|
+
"SupervisorEvent",
|
|
162
|
+
"Supervisor",
|
|
163
|
+
"platform_capabilities",
|
|
164
|
+
"run",
|
|
165
|
+
]
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""Shared lifecycle helpers for optional agent framework adapters."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import inspect
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
from .agent_run import AgentRunPolicy
|
|
8
|
+
from ._run_context import active_run
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def supervision_settings(supervision):
|
|
12
|
+
if supervision is None:
|
|
13
|
+
return 300.0, 5.0
|
|
14
|
+
if not isinstance(supervision, dict):
|
|
15
|
+
raise TypeError("supervision must be a dict or None")
|
|
16
|
+
unknown = set(supervision) - {"timeout", "shutdown_grace"}
|
|
17
|
+
if unknown:
|
|
18
|
+
raise TypeError(f"unexpected supervision option: {sorted(unknown)[0]}")
|
|
19
|
+
timeout = supervision.get("timeout", 300.0)
|
|
20
|
+
grace = supervision.get("shutdown_grace", 5.0)
|
|
21
|
+
if (
|
|
22
|
+
not isinstance(timeout, (int, float))
|
|
23
|
+
or not math.isfinite(timeout)
|
|
24
|
+
or timeout <= 0
|
|
25
|
+
):
|
|
26
|
+
raise ValueError("timeout must be a finite positive number of seconds")
|
|
27
|
+
if not isinstance(grace, (int, float)) or not 0 <= grace <= 60:
|
|
28
|
+
raise ValueError("shutdown_grace must be between 0 and 60")
|
|
29
|
+
return float(timeout), float(grace)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def new_run(supervision, supervisor=None):
|
|
33
|
+
timeout, grace = supervision_settings(supervision)
|
|
34
|
+
if supervisor is None:
|
|
35
|
+
from . import Supervisor
|
|
36
|
+
|
|
37
|
+
supervisor = Supervisor()
|
|
38
|
+
return supervisor.start_agent_run(AgentRunPolicy(timeout, grace))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
async def invoke_run(operation, supervision=None, supervisor=None):
|
|
42
|
+
"""Call a framework operation within a run owned by ``supervisor``."""
|
|
43
|
+
run = new_run(supervision, supervisor)
|
|
44
|
+
try:
|
|
45
|
+
return await run.run(operation())
|
|
46
|
+
finally:
|
|
47
|
+
await asyncio.shield(run.cancel())
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
_DONE = object()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class AgentEventPump:
|
|
54
|
+
"""Read a framework's stream in the background.
|
|
55
|
+
|
|
56
|
+
Each event is published to watchers of the run and queued for the caller,
|
|
57
|
+
so watchers see agent events whether or not the caller reads the stream.
|
|
58
|
+
The queue is unbounded because dropping events would change what the caller
|
|
59
|
+
receives; the run's deadline limits how long it can grow.
|
|
60
|
+
|
|
61
|
+
``on_finish(exc, completed)`` runs once when reading stops, in this task.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(self, iterator, run, deadline, on_finish):
|
|
65
|
+
self._iterator = iterator
|
|
66
|
+
self._run = run
|
|
67
|
+
self._deadline = deadline
|
|
68
|
+
self._on_finish = on_finish
|
|
69
|
+
self._queue = asyncio.Queue()
|
|
70
|
+
self._stop_exc = None
|
|
71
|
+
self._task = asyncio.ensure_future(self._read())
|
|
72
|
+
|
|
73
|
+
async def _read(self):
|
|
74
|
+
token = active_run.set(self._run)
|
|
75
|
+
loop = asyncio.get_running_loop()
|
|
76
|
+
failure = None
|
|
77
|
+
completed = False
|
|
78
|
+
try:
|
|
79
|
+
while True:
|
|
80
|
+
remaining = self._deadline - loop.time()
|
|
81
|
+
if remaining <= 0:
|
|
82
|
+
raise asyncio.TimeoutError("agent run exceeded its deadline")
|
|
83
|
+
try:
|
|
84
|
+
event = await asyncio.wait_for(self._iterator.__anext__(), remaining)
|
|
85
|
+
except StopAsyncIteration:
|
|
86
|
+
completed = True
|
|
87
|
+
break
|
|
88
|
+
self._run.publish_agent_event(event)
|
|
89
|
+
self._queue.put_nowait(event)
|
|
90
|
+
except asyncio.CancelledError:
|
|
91
|
+
failure = self._stop_exc
|
|
92
|
+
except BaseException as exc:
|
|
93
|
+
failure = exc
|
|
94
|
+
finally:
|
|
95
|
+
active_run.reset(token)
|
|
96
|
+
try:
|
|
97
|
+
await self._on_finish(failure, completed)
|
|
98
|
+
except BaseException as exc:
|
|
99
|
+
failure = failure or exc
|
|
100
|
+
self._queue.put_nowait(_Failure(failure) if failure is not None else _DONE)
|
|
101
|
+
|
|
102
|
+
async def get(self):
|
|
103
|
+
"""Return the next event, or raise when the stream has ended."""
|
|
104
|
+
item = await self._queue.get()
|
|
105
|
+
if item is _DONE or isinstance(item, _Failure):
|
|
106
|
+
# Keep the end marker so later reads end the same way.
|
|
107
|
+
self._queue.put_nowait(item)
|
|
108
|
+
if item is _DONE:
|
|
109
|
+
raise StopAsyncIteration
|
|
110
|
+
raise item.exc
|
|
111
|
+
return item
|
|
112
|
+
|
|
113
|
+
async def wait(self):
|
|
114
|
+
"""Wait until the framework's stream has been fully read."""
|
|
115
|
+
await asyncio.gather(self._task, return_exceptions=True)
|
|
116
|
+
|
|
117
|
+
async def stop(self, exc=None):
|
|
118
|
+
"""Stop reading early. Cleanup runs in the reading task."""
|
|
119
|
+
if not self._task.done():
|
|
120
|
+
self._stop_exc = exc
|
|
121
|
+
self._task.cancel()
|
|
122
|
+
await self.wait()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class _Failure:
|
|
126
|
+
def __init__(self, exc):
|
|
127
|
+
self.exc = exc
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def run_iterator(iterator, supervision=None, on_close=None, supervisor=None):
|
|
131
|
+
"""Keep a native agent run alive while a framework's stream is read."""
|
|
132
|
+
timeout, grace = supervision_settings(supervision)
|
|
133
|
+
if supervisor is None:
|
|
134
|
+
from . import Supervisor
|
|
135
|
+
|
|
136
|
+
supervisor = Supervisor()
|
|
137
|
+
run = supervisor.start_agent_run(AgentRunPolicy(timeout, grace))
|
|
138
|
+
return AgentRunAsyncIterator(iterator, run, timeout, on_close)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def run_context_manager(open_context, supervision=None, supervisor=None):
|
|
142
|
+
"""Keep a run active across ``open_context(run)``'s context manager."""
|
|
143
|
+
timeout, grace = supervision_settings(supervision)
|
|
144
|
+
run = new_run({"timeout": timeout, "shutdown_grace": grace}, supervisor)
|
|
145
|
+
return AgentRunAsyncContextManager(open_context(run), run, timeout)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class AgentRunAsyncIterator:
|
|
149
|
+
def __init__(self, iterator, run, timeout, on_close=None):
|
|
150
|
+
self._iterator = iterator.__aiter__()
|
|
151
|
+
self._run = run
|
|
152
|
+
self.agent_run = run
|
|
153
|
+
self._on_close = on_close
|
|
154
|
+
self._closed = False
|
|
155
|
+
deadline = asyncio.get_running_loop().time() + timeout
|
|
156
|
+
self._pump = AgentEventPump(self._iterator, run, deadline, self._finish)
|
|
157
|
+
|
|
158
|
+
def __aiter__(self):
|
|
159
|
+
return self
|
|
160
|
+
|
|
161
|
+
async def __anext__(self):
|
|
162
|
+
try:
|
|
163
|
+
return await self._pump.get()
|
|
164
|
+
except StopAsyncIteration:
|
|
165
|
+
raise
|
|
166
|
+
except BaseException as exc:
|
|
167
|
+
await self.aclose(exc)
|
|
168
|
+
raise
|
|
169
|
+
|
|
170
|
+
async def aclose(self, exc=None):
|
|
171
|
+
await self._pump.stop(exc)
|
|
172
|
+
|
|
173
|
+
async def _finish(self, exc, completed):
|
|
174
|
+
if self._closed:
|
|
175
|
+
return
|
|
176
|
+
self._closed = True
|
|
177
|
+
token = active_run.set(self._run)
|
|
178
|
+
try:
|
|
179
|
+
if self._on_close is not None:
|
|
180
|
+
result = self._on_close(exc, completed)
|
|
181
|
+
if inspect.isawaitable(result):
|
|
182
|
+
await result
|
|
183
|
+
close = getattr(self._iterator, "aclose", None)
|
|
184
|
+
if close is not None:
|
|
185
|
+
await close()
|
|
186
|
+
finally:
|
|
187
|
+
await asyncio.shield(self._run.cancel())
|
|
188
|
+
active_run.reset(token)
|
|
189
|
+
|
|
190
|
+
async def __aenter__(self):
|
|
191
|
+
return self
|
|
192
|
+
|
|
193
|
+
async def __aexit__(self, exc_type, exc, traceback):
|
|
194
|
+
await self.aclose(exc)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class AgentRunAsyncContextManager:
|
|
198
|
+
"""Keep the run active across a native async context manager's body."""
|
|
199
|
+
|
|
200
|
+
def __init__(self, context_manager, run, timeout):
|
|
201
|
+
self._context_manager = context_manager
|
|
202
|
+
self._run = run
|
|
203
|
+
self.agent_run = run
|
|
204
|
+
self._timeout = timeout
|
|
205
|
+
self._token = None
|
|
206
|
+
self._timeout_context = None
|
|
207
|
+
self._entered = False
|
|
208
|
+
self._task = None
|
|
209
|
+
self._timer = None
|
|
210
|
+
self._expired = False
|
|
211
|
+
|
|
212
|
+
async def __aenter__(self):
|
|
213
|
+
self._token = active_run.set(self._run)
|
|
214
|
+
self._task = asyncio.current_task()
|
|
215
|
+
self._timer = asyncio.get_running_loop().call_later(
|
|
216
|
+
self._timeout, self._expire
|
|
217
|
+
)
|
|
218
|
+
try:
|
|
219
|
+
result = await self._context_manager.__aenter__()
|
|
220
|
+
self._entered = True
|
|
221
|
+
return result
|
|
222
|
+
except BaseException as exc:
|
|
223
|
+
await self._finish(type(exc), exc, exc.__traceback__)
|
|
224
|
+
raise
|
|
225
|
+
|
|
226
|
+
async def __aexit__(self, exc_type, exc, traceback):
|
|
227
|
+
return await self._finish(exc_type, exc, traceback)
|
|
228
|
+
|
|
229
|
+
async def _finish(self, exc_type, exc, traceback):
|
|
230
|
+
suppress = False
|
|
231
|
+
try:
|
|
232
|
+
if self._entered:
|
|
233
|
+
suppress = await self._context_manager.__aexit__(exc_type, exc, traceback)
|
|
234
|
+
finally:
|
|
235
|
+
if self._timer is not None:
|
|
236
|
+
self._timer.cancel()
|
|
237
|
+
if self._token is not None:
|
|
238
|
+
active_run.reset(self._token)
|
|
239
|
+
self._token = None
|
|
240
|
+
await asyncio.shield(self._run.cancel())
|
|
241
|
+
if self._expired and exc_type is asyncio.CancelledError:
|
|
242
|
+
raise asyncio.TimeoutError("agent run exceeded its deadline") from None
|
|
243
|
+
return suppress
|
|
244
|
+
|
|
245
|
+
def _expire(self):
|
|
246
|
+
self._expired = True
|
|
247
|
+
if self._task is not None:
|
|
248
|
+
self._task.cancel()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Context shared by framework adapters and supervised local tools."""
|
|
2
|
+
|
|
3
|
+
from contextvars import ContextVar
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
active_run = ContextVar("agent_supervisor_run_context", default=None)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def current_run():
|
|
10
|
+
"""Return the agent run active in this async context, if any."""
|
|
11
|
+
return active_run.get()
|