rio 0.4.0__py3-none-any.whl
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.
- rio-0.4.0.dist-info/METADATA +90 -0
- rio-0.4.0.dist-info/RECORD +140 -0
- rio-0.4.0.dist-info/WHEEL +4 -0
- rio-0.4.0.dist-info/entry_points.txt +2 -0
- rio-0.4.0.dist-info/licenses/LICENSE +21 -0
- rio-0.4.0.dist-info/licenses/NOTICE +37 -0
- rio_agent/__init__.py +47 -0
- rio_agent/errors.py +29 -0
- rio_agent/events.py +95 -0
- rio_agent/harness.py +125 -0
- rio_agent/loop.py +199 -0
- rio_agent/observation.py +24 -0
- rio_agent/prompt.py +111 -0
- rio_agent/py.typed +0 -0
- rio_agent/skill.py +27 -0
- rio_agent/state.py +50 -0
- rio_ai/__init__.py +81 -0
- rio_ai/_provider_events.py +93 -0
- rio_ai/anthropic.py +771 -0
- rio_ai/content.py +45 -0
- rio_ai/env.py +158 -0
- rio_ai/events.py +37 -0
- rio_ai/fake.py +41 -0
- rio_ai/google.py +522 -0
- rio_ai/http.py +81 -0
- rio_ai/http_errors.py +65 -0
- rio_ai/messages.py +286 -0
- rio_ai/mistral.py +525 -0
- rio_ai/model_limits.py +48 -0
- rio_ai/openai_cache.py +20 -0
- rio_ai/openai_codex.py +1080 -0
- rio_ai/openai_compatible.py +1387 -0
- rio_ai/provider.py +37 -0
- rio_ai/provider_events.py +107 -0
- rio_ai/py.typed +0 -0
- rio_ai/retry.py +62 -0
- rio_ai/stream.py +212 -0
- rio_ai/tool_call_ids.py +24 -0
- rio_ai/tools.py +118 -0
- rio_ai/types.py +8 -0
- rio_coding/__init__.py +306 -0
- rio_coding/__main__.py +5 -0
- rio_coding/auth_commands.py +72 -0
- rio_coding/branch_summary.py +79 -0
- rio_coding/built_in_extensions.py +114 -0
- rio_coding/catalog_loader.py +763 -0
- rio_coding/cli.py +667 -0
- rio_coding/coding_skill.py +129 -0
- rio_coding/commands.py +956 -0
- rio_coding/context.py +95 -0
- rio_coding/credentials.py +260 -0
- rio_coding/data/catalog.toml +6882 -0
- rio_coding/data/docs/README.md +14 -0
- rio_coding/data/docs/architecture.md +105 -0
- rio_coding/data/docs/cli.md +42 -0
- rio_coding/data/docs/extensions.md +38 -0
- rio_coding/data/docs/local-inference.md +9 -0
- rio_coding/data/docs/models.md +23 -0
- rio_coding/data/docs/security.md +13 -0
- rio_coding/data/docs/skills.md +32 -0
- rio_coding/data/examples/offline_session.py +47 -0
- rio_coding/data/models-dev-catalog.json +15937 -0
- rio_coding/data/release-notes/releases.json +16 -0
- rio_coding/diagnostics.py +183 -0
- rio_coding/events.py +121 -0
- rio_coding/extension_installer.py +200 -0
- rio_coding/extensions/__init__.py +235 -0
- rio_coding/extensions/api.py +1173 -0
- rio_coding/extensions/builtins/__init__.py +1 -0
- rio_coding/extensions/builtins/llama_cpp/__init__.py +87 -0
- rio_coding/extensions/builtins/llama_cpp/huggingface.py +217 -0
- rio_coding/extensions/builtins/llama_cpp/router.py +315 -0
- rio_coding/extensions/builtins/llama_cpp/service.py +1763 -0
- rio_coding/extensions/builtins/llama_cpp/state.py +411 -0
- rio_coding/extensions/loader.py +405 -0
- rio_coding/extensions/provider_registry.py +650 -0
- rio_coding/extensions/providers.py +527 -0
- rio_coding/extensions/runtime.py +1282 -0
- rio_coding/extensions/startup.py +65 -0
- rio_coding/frontend_session.py +216 -0
- rio_coding/image_processing.py +253 -0
- rio_coding/local_backends.py +1299 -0
- rio_coding/models_dev.py +335 -0
- rio_coding/models_dev_store.py +207 -0
- rio_coding/oauth.py +528 -0
- rio_coding/oauth_anthropic.py +288 -0
- rio_coding/oauth_device.py +109 -0
- rio_coding/oauth_github_copilot.py +285 -0
- rio_coding/oauth_registry.py +55 -0
- rio_coding/oauth_types.py +127 -0
- rio_coding/paths.py +150 -0
- rio_coding/project_trust.py +703 -0
- rio_coding/prompt_templates.py +285 -0
- rio_coding/provider_catalog.py +119 -0
- rio_coding/provider_config.py +2617 -0
- rio_coding/provider_runtime.py +534 -0
- rio_coding/py.typed +0 -0
- rio_coding/reload.py +31 -0
- rio_coding/rendering/__init__.py +28 -0
- rio_coding/rendering/base.py +45 -0
- rio_coding/rendering/json.py +72 -0
- rio_coding/rendering/plain.py +103 -0
- rio_coding/rendering/steps.py +116 -0
- rio_coding/resources.py +376 -0
- rio_coding/rpc.py +596 -0
- rio_coding/self_docs.py +23 -0
- rio_coding/session.py +795 -0
- rio_coding/session_export.py +634 -0
- rio_coding/session_manager.py +383 -0
- rio_coding/session_preparation.py +85 -0
- rio_coding/session_runner.py +486 -0
- rio_coding/session_stats.py +99 -0
- rio_coding/session_store/__init__.py +51 -0
- rio_coding/session_store/entries.py +186 -0
- rio_coding/session_store/jsonl.py +40 -0
- rio_coding/session_store/storage.py +203 -0
- rio_coding/session_store/tree.py +90 -0
- rio_coding/session_usage.py +207 -0
- rio_coding/shell_config.py +74 -0
- rio_coding/skills.py +254 -0
- rio_coding/step_footprint.py +148 -0
- rio_coding/system_prompt.py +308 -0
- rio_coding/thinking.py +93 -0
- rio_coding/tools.py +1376 -0
- rio_coding/update_check.py +379 -0
- rio_coding/updater.py +387 -0
- rio_coding/version.py +16 -0
- rio_tui/__init__.py +6 -0
- rio_tui/__main__.py +3 -0
- rio_tui/app.py +219 -0
- rio_tui/app.tcss +74 -0
- rio_tui/bridge.py +219 -0
- rio_tui/dialogs.py +267 -0
- rio_tui/session.py +510 -0
- rio_tui/settings.py +48 -0
- rio_tui/widgets/__init__.py +1 -0
- rio_tui/widgets/conversation.py +134 -0
- rio_tui/widgets/prompt.py +192 -0
- rio_tui/widgets/sidebar.py +55 -0
- rio_tui/widgets/terminal.py +191 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: rio
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: rio_ai: a ported multi-provider LLM streaming SDK. rio_agent: a SKILL.state long-horizon agent runtime (arXiv:2608.26263). rio_coding: a SKILL.state coding agent.
|
|
5
|
+
Project-URL: Repository, https://github.com/soasme/rio
|
|
6
|
+
Author-email: Ju Lin <soasme@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
License-File: NOTICE
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Requires-Dist: anyio>=4.0
|
|
12
|
+
Requires-Dist: httpx[socks]>=0.27
|
|
13
|
+
Requires-Dist: packaging>=24.0
|
|
14
|
+
Requires-Dist: pillow>=11.0
|
|
15
|
+
Requires-Dist: pydantic>=2.11
|
|
16
|
+
Requires-Dist: pygments>=2.18
|
|
17
|
+
Requires-Dist: pyte>=0.8.2
|
|
18
|
+
Requires-Dist: rich>=13.0
|
|
19
|
+
Requires-Dist: textual-diff-view>=0.1.5
|
|
20
|
+
Requires-Dist: textual>=8.2.8
|
|
21
|
+
Requires-Dist: typer>=0.12
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# rio
|
|
25
|
+
|
|
26
|
+
Four packages:
|
|
27
|
+
|
|
28
|
+
- **`rio_ai`** -- a multi-provider LLM streaming SDK (Anthropic, Google
|
|
29
|
+
Gemini, Mistral, OpenAI Codex, OpenAI-compatible), ported from
|
|
30
|
+
[huggingface/tau](https://github.com/huggingface/tau)'s `tau_ai` module.
|
|
31
|
+
The message/tool/type vocabulary `tau_ai` depends on (`tau_agent.messages`,
|
|
32
|
+
`tau_agent.tools`, `tau_agent.types`, `tau_agent.provider`,
|
|
33
|
+
`tau_agent.provider_events` upstream) is folded in here too, so `rio_ai`
|
|
34
|
+
is self-contained. See [`NOTICE`](NOTICE) for the upstream MIT license.
|
|
35
|
+
- **`rio_agent`** -- a from-scratch runtime implementing
|
|
36
|
+
[*SKILL.state: Scalable Long-Horizon Agent Skills*](https://arxiv.org/abs/2608.26263)
|
|
37
|
+
(Badhe, Tiwari & Chung; EMNLP), built on `rio_ai`.
|
|
38
|
+
- **`rio_coding`** -- the coding backend (CLI, tools, skills, extensions), built on `rio_agent`.
|
|
39
|
+
- **`rio_tui`** -- the terminal application, wired to `rio_coding`. It owns
|
|
40
|
+
session tabs, the prompt editor, shell, file navigation, diffs, and settings.
|
|
41
|
+
|
|
42
|
+
## Coding agent
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv sync
|
|
46
|
+
uv run rio setup --provider local --base-url http://localhost:8080/v1 --model my-model
|
|
47
|
+
uv run rio # interactive terminal interface
|
|
48
|
+
uv run rio -p "Inspect this project and explain its entry point"
|
|
49
|
+
uv run rio -p --mode json "Add a regression test for the parser"
|
|
50
|
+
uv run rio --mode rpc # JSONL commands on stdin
|
|
51
|
+
uv run rio sessions
|
|
52
|
+
uv run rio --session SESSION_ID
|
|
53
|
+
uv run rio export SESSION_ID --format html
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Choose an existing provider with `--provider NAME --model MODEL`; `rio providers`
|
|
57
|
+
lists the configured catalog. Credentials may come from the provider's environment
|
|
58
|
+
variable or Rio's credential store (`rio login PROVIDER`; use `--method api-key`
|
|
59
|
+
for an API key). A successful login remembers the provider for future launches.
|
|
60
|
+
API keys and OAuth tokens are stored in `~/.rio/credentials.json` with owner-only
|
|
61
|
+
permissions; the file is unencrypted. OAuth tokens refresh automatically.
|
|
62
|
+
Use `rio logout PROVIDER` to remove saved credentials. Configuration and journals
|
|
63
|
+
live under `~/.rio`.
|
|
64
|
+
Use `--approve` to allow ambient project instructions and extensions for a run;
|
|
65
|
+
project trust controls resource loading, not what shell commands can access.
|
|
66
|
+
|
|
67
|
+
The coding tools are `read`, `write`, `edit`, `bash`, and `respond`. Each step
|
|
68
|
+
updates structured state and executes one action. Sessions store snapshots for
|
|
69
|
+
resume and checkpoint restoration; they never replay a transcript into the model.
|
|
70
|
+
HTML exports show steps, final state, and estimated token footprints.
|
|
71
|
+
|
|
72
|
+
See the [installed documentation](src/rio_coding/data/docs/README.md) for
|
|
73
|
+
providers, skills, extensions, RPC, and the state-based runtime. The
|
|
74
|
+
[offline example](src/rio_coding/data/examples/offline_session.py) runs a complete
|
|
75
|
+
session with a fake provider and requires no credentials.
|
|
76
|
+
|
|
77
|
+
## Development
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
uv sync
|
|
81
|
+
uv run pytest
|
|
82
|
+
uv run ruff check .
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Tests use scripted providers (`rio_ai.FakeProvider`) and mocked HTTP; no live
|
|
86
|
+
provider credentials are required. Headless Textual tests exercise terminal
|
|
87
|
+
interaction, and tool integration tests work in temporary directories. See in
|
|
88
|
+
particular `tests/test_rio_agent_loop.py::test_prompt_footprint_is_bounded_across_steps`,
|
|
89
|
+
which asserts that per-step prompt size stays constant across many steps
|
|
90
|
+
rather than growing -- a direct runtime check of the paper's core claim.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
rio_agent/__init__.py,sha256=DC9rZDoQ4qI1gUamg1RbB9dZHtfSsiajm4F-6mxv18Y,1629
|
|
2
|
+
rio_agent/errors.py,sha256=Zep_eWy0u4dTAY2vy534Uzl51pSsvFfwLvRWz2ioLJ8,939
|
|
3
|
+
rio_agent/events.py,sha256=fYez8Yqf5Q_xQ1M7GR0WZxVfeNdRgj3NgyRCadf6aWo,2017
|
|
4
|
+
rio_agent/harness.py,sha256=J9XVfofIIH5hvXHCLVkW0uyzTUuTKTraBLhn4gHpgmo,4105
|
|
5
|
+
rio_agent/loop.py,sha256=ypgEm49I-5j15NjgSfbBy6T31AB9zL859_wnTftBQmI,8489
|
|
6
|
+
rio_agent/observation.py,sha256=XDWkY80XYvAaOtBkmTaaGlNUsh_2G1-ioR2WVL9ZnaY,887
|
|
7
|
+
rio_agent/prompt.py,sha256=93v8gJyLMj7dYkoFnReFnAFhQW0h8DKZG678jfyS5XE,4288
|
|
8
|
+
rio_agent/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
rio_agent/skill.py,sha256=Ia_5duVHTSDAVIQO53oQsFKtr34g49-IUISSjonaRsQ,889
|
|
10
|
+
rio_agent/state.py,sha256=DKTMV_Pffa7Jdh_MKA6BOyYlRASnxvmRr-weiuAJLQk,1873
|
|
11
|
+
rio_ai/__init__.py,sha256=9KCNoV61_DmwbDkL7gftewJKEGST82iqjGGLH04Kr4I,2468
|
|
12
|
+
rio_ai/_provider_events.py,sha256=qx9pdFfL6u6WXR5_xYccSX_C5SkZVTcUeA6ysqMxJP8,2321
|
|
13
|
+
rio_ai/anthropic.py,sha256=5A7gSNLImEjWTKAsx8zMXIEDILH6sA0PWiLRvG7x_Bo,32553
|
|
14
|
+
rio_ai/content.py,sha256=HIZ_NT61L7mX9nYY7sLuO5nA7fjUGFiXdAbH1mnc-zc,1683
|
|
15
|
+
rio_ai/env.py,sha256=sSgw6mEuiEefWovVg7022jj8rAuUSgMXMXP8vDIpsv8,6218
|
|
16
|
+
rio_ai/events.py,sha256=kKHwffbfjb4yXIPE1iGvcMnTKN1yPQ1zPKGLKI180zw,823
|
|
17
|
+
rio_ai/fake.py,sha256=x0mU14Q0q9pXaVvnYmsDSwmcjOioy1DAF9GQpBHHRK4,1417
|
|
18
|
+
rio_ai/google.py,sha256=e9huzlaXV1INGErp2nPcdnXHiXKP-5UCUqyphPBoXAI,20609
|
|
19
|
+
rio_ai/http.py,sha256=9BvofnxhZqnp3a8lxKblRY6nfrvhz_4PAtDkATYCkOc,2387
|
|
20
|
+
rio_ai/http_errors.py,sha256=kuSGGwkSCi9XLUFHAuR2xjOAXJPJbPmbeYanIiJ7I8Y,2020
|
|
21
|
+
rio_ai/messages.py,sha256=1jCeqn8yovwMfVopZaJQUtKSuzvWIVEPpdaQaW_VDTo,8419
|
|
22
|
+
rio_ai/mistral.py,sha256=1emOQywqR_CrL-oKXIrN1CWRgo4XNLKHx6_r75QS-Gw,19487
|
|
23
|
+
rio_ai/model_limits.py,sha256=zxC4CbA6lrPhvmbkx_fp8gmJCSOUEzDLvizY-dNZtVk,2077
|
|
24
|
+
rio_ai/openai_cache.py,sha256=jpyLf2jJndg3OVNdgJX0dTuOQU4zmqIlv9jdXQBynzE,628
|
|
25
|
+
rio_ai/openai_codex.py,sha256=RwUFVzA4_FverridAD6kYb6mnpAtsfRg2EbU_PbsErM,40286
|
|
26
|
+
rio_ai/openai_compatible.py,sha256=tJAhUdiuCXOEVL1PM2udYVExGLsYfkaSM6ksAXVHeHM,52499
|
|
27
|
+
rio_ai/provider.py,sha256=pbyT8k6EYy5m6K7OmIBInqnv1XYfF6wU4aKYxwx_YLM,1062
|
|
28
|
+
rio_ai/provider_events.py,sha256=1DlMfBAp5xIWCVnBEABQ4rXQj2d3YQOrBhs0nW188RQ,2438
|
|
29
|
+
rio_ai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
+
rio_ai/retry.py,sha256=b2hYU0k4bh73ihsUH-RK1gW_ZauSPH-FrsrTH3jz-ac,1885
|
|
31
|
+
rio_ai/stream.py,sha256=KT5yVDfxZjlWfP2l4Cq7n3UH2jtqnWSOIGbZjv2pu-0,8454
|
|
32
|
+
rio_ai/tool_call_ids.py,sha256=7-faY63oTCtTmdUBEQ9z3o9m0BREsIphCsEnCBWa0NI,928
|
|
33
|
+
rio_ai/tools.py,sha256=GAZ6Em0ptvFyTmHDnYgF-GIUudqtPiqEgxIIgkF89pY,3549
|
|
34
|
+
rio_ai/types.py,sha256=UOFf9T_uMjFMRvFsYS8RJvKPjIFMwHP2WeJFGtYsEZQ,333
|
|
35
|
+
rio_coding/__init__.py,sha256=mkf3UE_wR3xk0Z-mbJCwJYDUItJIEXI23mV6JJuoBkA,8100
|
|
36
|
+
rio_coding/__main__.py,sha256=DPbgfyZSWPIiTwG070jOOV_np2jNi4XEsU3_a8JKgyQ,84
|
|
37
|
+
rio_coding/auth_commands.py,sha256=He4znCCEZmVM4bce43OU0T3t335C2GOEoLy6_Fy2_5k,2680
|
|
38
|
+
rio_coding/branch_summary.py,sha256=hyPBzP08fnuNiD0mfMddJVeu7RkSk_fBK9G0nE0ITp4,3103
|
|
39
|
+
rio_coding/built_in_extensions.py,sha256=5u8v81a0YjdZveUORIcD-uzuPkIV6F1OdCmqI5vXyCo,4075
|
|
40
|
+
rio_coding/catalog_loader.py,sha256=38qVMwtuFryGX7Ja45s42dEdA5vrStksW9WMZqF58s0,29530
|
|
41
|
+
rio_coding/cli.py,sha256=MRY537lBB-OmjTMtTvspAmAkZr8iFjk2MQ6LtS1IjT4,25325
|
|
42
|
+
rio_coding/coding_skill.py,sha256=Xkqn8pCGnUH42kYoaZeBQh9-yO9GRVWh0A3d4gQPifk,4309
|
|
43
|
+
rio_coding/commands.py,sha256=kTSBK0fQfZ_BwyDQDorfyYaNf1zXiThkQw1wagygI3Q,33331
|
|
44
|
+
rio_coding/context.py,sha256=aRb8NrQxSEXBmtC92u3_Y5Zq9nPSnIgL37THHSAZIgE,3267
|
|
45
|
+
rio_coding/credentials.py,sha256=5uGiUN--TB5Iw25E0jbzLSz7Tt_oUJcRrUuPq_WbcXc,9616
|
|
46
|
+
rio_coding/diagnostics.py,sha256=-q9tIW1eWr905V6Gd9FVsByotFc0-6mrxTuYYsyajwk,6239
|
|
47
|
+
rio_coding/events.py,sha256=scdrsJWHnSv_FbzjtD5FIvZYdNJS_eVeyzBSzbSy-G0,3465
|
|
48
|
+
rio_coding/extension_installer.py,sha256=cDiqICrlBSFldqBL1LcoXR1uod-wyb6j01HQ40tPgc4,7438
|
|
49
|
+
rio_coding/frontend_session.py,sha256=EwYsfJ-hs_MGAj1UF-DmmFzjtqdlkP_RmtMm1RmPpPU,9398
|
|
50
|
+
rio_coding/image_processing.py,sha256=3172j-fRSjvZfUVNa_Q6ZmoJKRERdZS8FjeRFnwhnyg,9260
|
|
51
|
+
rio_coding/local_backends.py,sha256=12ENUomVFRz6XjKVJVchqjiF84T3YgZdYBFBPzHQcJY,48797
|
|
52
|
+
rio_coding/models_dev.py,sha256=gL-rI0h1OL7q2ZSFXdPKpIhWsWeqktn397UMWeZbotA,13111
|
|
53
|
+
rio_coding/models_dev_store.py,sha256=DAejqmZRIb6xrsMr36a7G0qMrHuG1DXvDOChdxWaSzI,7187
|
|
54
|
+
rio_coding/oauth.py,sha256=bwfElAQWKsW31PsNjnowkVigPomSgXG0V6Qqh-OKzkI,17462
|
|
55
|
+
rio_coding/oauth_anthropic.py,sha256=YDLsyih0I0U41-o5XhiFKTkbhyoFtinDL0L0ygajJ2Y,10160
|
|
56
|
+
rio_coding/oauth_device.py,sha256=D9P5FPDGak8KCF7cbDp20xWdr8FydAb_38a5hVqKY9c,3502
|
|
57
|
+
rio_coding/oauth_github_copilot.py,sha256=M_vMnKHG-CK7qiGaYDbJZhaRZTVtx8U0sqAAgeB_CGw,10757
|
|
58
|
+
rio_coding/oauth_registry.py,sha256=6zuyX6fZWEJ6FE5y_dYbGuiDcWYFzRYBVD2yifIcFl8,1998
|
|
59
|
+
rio_coding/oauth_types.py,sha256=O1ZuCslKoEJvUi9WJlzZbax7iQ32FJo7wFT7Cz-nZMk,3556
|
|
60
|
+
rio_coding/paths.py,sha256=tP--CPMsBGsuHHDxwe1cmS89dO6FM_mX6pQKPSMWm_Q,5284
|
|
61
|
+
rio_coding/project_trust.py,sha256=cceOvdafJ35w8Bxx9Qat5nZPLWo2DcnwWsy_odChTZY,27518
|
|
62
|
+
rio_coding/prompt_templates.py,sha256=5Wog4enRWnjdG2PxXH8Yfl4hKW-40wBdrTFBRuxOAc8,10227
|
|
63
|
+
rio_coding/provider_catalog.py,sha256=RLTXavhKv5Mi-8AAM5oUYhEuIMbgH1fJVD6F5UAvLIk,4030
|
|
64
|
+
rio_coding/provider_config.py,sha256=aw9uM5Qiz3-MxvUnyTqPASy1_wm4bYa2ZBT4lqytpvc,104036
|
|
65
|
+
rio_coding/provider_runtime.py,sha256=SNt9oCeCv6xCPD24wErnNdEoHrcsRhHwlI2gFk0OaVs,22248
|
|
66
|
+
rio_coding/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
|
+
rio_coding/reload.py,sha256=5MJ033lyL-jqHBAfEo1iR2c6y6qHrPJ2d07_tvEtJLM,789
|
|
68
|
+
rio_coding/resources.py,sha256=l6aREEeAi6Umd5cY_7pz4zjwrOOT5J5UKZWzAWo7Mlg,12297
|
|
69
|
+
rio_coding/rpc.py,sha256=h4rmBFkafQ6W5Yy9mHJyyByE-YQ1IYT-ZR4__cBJBn0,22723
|
|
70
|
+
rio_coding/self_docs.py,sha256=jWy3exeyLABuCtobEf-nqsg5z_UnQrSpXfQk38xGWjs,613
|
|
71
|
+
rio_coding/session.py,sha256=shgawEyN58mr175B1K1Io2vN3fHVfBUYb_0V-RTPiIM,29819
|
|
72
|
+
rio_coding/session_export.py,sha256=-NM3ZnbPpy9yobFnlVMLBV6vWpzQyqrBLgjR4QpumYo,21888
|
|
73
|
+
rio_coding/session_manager.py,sha256=PDTEYFHKaYAiTsrfhC_WdDX0gCKWVVVUf-bcKrlq-K4,14320
|
|
74
|
+
rio_coding/session_preparation.py,sha256=MXYLfNgfUgyloGCwgOT9Q5_CWea1qQ36wxJExOjTgdk,2774
|
|
75
|
+
rio_coding/session_runner.py,sha256=8THfX1xPzEKK0TJJ14lJq4kdRsluV_uiAvaF7fgqCoo,19211
|
|
76
|
+
rio_coding/session_stats.py,sha256=RdfyxWp2bYmXwH9ROibMwRwJXh8lBpBdmeFr3r1dQTQ,3818
|
|
77
|
+
rio_coding/session_usage.py,sha256=nKS-OqL5Ex30QcXST1W0r6vtrOb5RtfF1OqTA01Sxfg,7166
|
|
78
|
+
rio_coding/shell_config.py,sha256=pu32bvupMZzLxd5OaQlg50ePzjML1vfJWCPanJpevUM,2843
|
|
79
|
+
rio_coding/skills.py,sha256=yeWNZhfZVvFch5d4sEs0ZCNkomsCOr6UkrF2Fkno6mo,8959
|
|
80
|
+
rio_coding/step_footprint.py,sha256=FO35WoocS0nDF5QbWb9m6hJpdgYvBQISiUoOV_k6MC8,5534
|
|
81
|
+
rio_coding/system_prompt.py,sha256=bii7CgiDEmnhvnZseO2TFLPvSO90ux25j-k1zfKS_kw,13357
|
|
82
|
+
rio_coding/thinking.py,sha256=aknZSnW7JthyGMP8i2GPy0bNbD799w6FyvCUaVoak8w,3044
|
|
83
|
+
rio_coding/tools.py,sha256=EapKdE-tGdj8GDxJpAbVo7v7WB1G5dtx45cRIcj8HZg,51952
|
|
84
|
+
rio_coding/update_check.py,sha256=CIrkv_p1GH5XxISHlZdTlkpKB8V_eKrw-hYyEVsu5pc,13075
|
|
85
|
+
rio_coding/updater.py,sha256=Ud0rwdAhmu20ReQYekA-RVOn84bFgeO7Jy4o5xfpFeg,13491
|
|
86
|
+
rio_coding/version.py,sha256=_jG2JtOXwfNuug0a0wfc4zKUvquFYUnDFH6FqijYfO4,409
|
|
87
|
+
rio_coding/data/catalog.toml,sha256=WdFGv6xpyPkV0bqlre0CPK2aDf7fL3__YF6DXm4vgIo,233789
|
|
88
|
+
rio_coding/data/models-dev-catalog.json,sha256=TMjIGXnQqIzO4W-SJZ19DMWmhwttCKOZG9gTxOeJA4Y,403853
|
|
89
|
+
rio_coding/data/docs/README.md,sha256=UgaiPH8_leXVd-4CNe1cZycQTIe5qsiwDzLxbwSryns,781
|
|
90
|
+
rio_coding/data/docs/architecture.md,sha256=yDrfpfTA1iw-DCbCd7rVenaY7qKCITEt9Rx4Tyh_vw8,5843
|
|
91
|
+
rio_coding/data/docs/cli.md,sha256=9m5ZbnDLTJUvZah8lxxAY-sylb46oj2sNe9Fs5_r1yg,2637
|
|
92
|
+
rio_coding/data/docs/extensions.md,sha256=td-Dpd-SvdePi3vN0YRvAtx4op6rl2M9TQS81QUUhjk,2436
|
|
93
|
+
rio_coding/data/docs/local-inference.md,sha256=4vGaelExDf3e2GgA7ddNZ2xV99ClCm4LYEB0OKsrg7s,970
|
|
94
|
+
rio_coding/data/docs/models.md,sha256=NBuNVt1STerRmcIx-5PLpXleSmCSWoRBwSeRaJZRNN4,2187
|
|
95
|
+
rio_coding/data/docs/security.md,sha256=nBiIyaDphF4bEUxn08mAWlkcNWSRLMJI2K5ItCeP1bw,1452
|
|
96
|
+
rio_coding/data/docs/skills.md,sha256=lgBUjiJ7w8J7iogVmIRaAGa-zCKYNuNZWTUM4dBRDhQ,2142
|
|
97
|
+
rio_coding/data/examples/offline_session.py,sha256=wlhHKNueupqg8rh0MwqS6891IqsEVMbmzqJmm1jJpwk,1478
|
|
98
|
+
rio_coding/data/release-notes/releases.json,sha256=EpVsjjsE8s9rJaAdznyx38p3pAOHMRj9eBcAkk2lLXY,1086
|
|
99
|
+
rio_coding/extensions/__init__.py,sha256=Wksp3cLcH3uao4sxK8t7IOqrNfFt-sl8aG5-mgWnwjM,5756
|
|
100
|
+
rio_coding/extensions/api.py,sha256=SAWymZX8EPRCT3hkV-sKycqLK8QGDkH4E6uC8yW98jc,42069
|
|
101
|
+
rio_coding/extensions/loader.py,sha256=0FNF6Umx_UjICuxflW7BitUTtjvyhqZTNJZGnMXPQIA,14221
|
|
102
|
+
rio_coding/extensions/provider_registry.py,sha256=yW0hnvp8u39EIBU2zy7Zo2P3-4DkHdXZ64hM2uq5HMk,25950
|
|
103
|
+
rio_coding/extensions/providers.py,sha256=WKyuiMgtLSDzF_a3xMxpKvZRVZ8OaWxZAusdA6hE2d0,21826
|
|
104
|
+
rio_coding/extensions/runtime.py,sha256=gNY_-HCY4wWN8m3irMc2diCI95Uj-9rOf-sY6QceN2E,51672
|
|
105
|
+
rio_coding/extensions/startup.py,sha256=hWlRdqfobmNjDQL72owXCgP_-LFuGIUoCnszoN5CLYs,2438
|
|
106
|
+
rio_coding/extensions/builtins/__init__.py,sha256=8RwaQsjE6IUr8qeUxamm_oYp-xeV-Tddp4_Ncn4BSBk,41
|
|
107
|
+
rio_coding/extensions/builtins/llama_cpp/__init__.py,sha256=_RdD-RmlLjjwP5So428wdg3LbI4H-BMuSF9w5CfDiI4,2555
|
|
108
|
+
rio_coding/extensions/builtins/llama_cpp/huggingface.py,sha256=oem3mZgAzgPWvlrLxtXsI64vLWlgfyGMohxtRe3ADZQ,7511
|
|
109
|
+
rio_coding/extensions/builtins/llama_cpp/router.py,sha256=QgEsdFKYc82Nf9LaZmtWxnE35E-gmdeCMUws5WuVjTA,10639
|
|
110
|
+
rio_coding/extensions/builtins/llama_cpp/service.py,sha256=8BPgxNcixo189EqWsvxdFc1mM9WGuesRF5H5vAFN8TU,71014
|
|
111
|
+
rio_coding/extensions/builtins/llama_cpp/state.py,sha256=w4wklCBOB4ujGWhflmy6E0dhM8wbWPpgx07LfnK85QY,17333
|
|
112
|
+
rio_coding/rendering/__init__.py,sha256=oibCD3w0YTu3--f2r2AwiKJ8Cc1WysgemHQHI1rIUJc,877
|
|
113
|
+
rio_coding/rendering/base.py,sha256=g7Ag16orXE4OPAYCs8LR3vfvdFwhVImI_QMAEU0LTqs,1802
|
|
114
|
+
rio_coding/rendering/json.py,sha256=ImYpMA8y9nJjkIAPpI1ZoQRo05e6iSFX3_gynJiyGlw,2654
|
|
115
|
+
rio_coding/rendering/plain.py,sha256=_FSOaAbtJpP2fhHje2K4lcIIIzZE_uMQN4qnLRFs5LA,3909
|
|
116
|
+
rio_coding/rendering/steps.py,sha256=lV4gBepQ2r42O3EXh3RhZLgg0-BOA7xZySN8o6BTYaE,4539
|
|
117
|
+
rio_coding/session_store/__init__.py,sha256=wvn1HrS29bvJUsl_yRqyod4COzfgyP4sGJQOHlgN0Z4,1308
|
|
118
|
+
rio_coding/session_store/entries.py,sha256=2xqd5VdCmqGWMCgoEhX_l3PoySyF7xrUFWKjjltNhSE,5564
|
|
119
|
+
rio_coding/session_store/jsonl.py,sha256=PXilexk8J9d4Da92yECZTesHG51t-HMd43iqcrgewAo,1431
|
|
120
|
+
rio_coding/session_store/storage.py,sha256=w2zBo7DBt2v8CF_11S00296imoSYODWBeVP7QX8lZ2k,7344
|
|
121
|
+
rio_coding/session_store/tree.py,sha256=nbZ4nNVbehV52VVT0HKovc3mxTm3FsEEXLDK4_RasJg,3265
|
|
122
|
+
rio_tui/__init__.py,sha256=GURtvbciFTnpf1TBIalK8hT7j3hvKP-V4-2okKYylIQ,191
|
|
123
|
+
rio_tui/__main__.py,sha256=Z-FokKnWybtUCIyaU_-1RyMwMN2E1jZqcON1RtBmVC8,38
|
|
124
|
+
rio_tui/app.py,sha256=SqK9NCINBFGEY8y_HTVid705GAIX1v_JwN3EBwmwZfc,8360
|
|
125
|
+
rio_tui/app.tcss,sha256=ZYamNhJ2yii75DItVUYbiLJnG8RbyStNAtvvVKeV8Uw,4085
|
|
126
|
+
rio_tui/bridge.py,sha256=s0m6miRd_zSY5nxVzr3d_97qun2b2TPcsWBAuTCLYu0,7653
|
|
127
|
+
rio_tui/dialogs.py,sha256=a57UVyWOhqykBnO-J4i30NOO8yzvo5uFEMMLVSeGpAU,9298
|
|
128
|
+
rio_tui/session.py,sha256=TQMNDyQnCz1UDizCHTqCusohaUVT76-aGQiF7NJcxoM,21705
|
|
129
|
+
rio_tui/settings.py,sha256=CXmn7SN0ZmtQumz74EiLoGx2D1VI7HfU9yTq1G9RhyI,1533
|
|
130
|
+
rio_tui/widgets/__init__.py,sha256=PsSHMD7Gm75fDIplUpoPWa0WVacHQIgzgtFoX0LajZE,40
|
|
131
|
+
rio_tui/widgets/conversation.py,sha256=ZwvsLJs9E51Rho503xOLzJzw1bpRbVjOGTMuzW-B5lQ,4549
|
|
132
|
+
rio_tui/widgets/prompt.py,sha256=nVss9GFaEypfOIt4gfVHZojU32on316lFpr9ngGJ5Q8,6566
|
|
133
|
+
rio_tui/widgets/sidebar.py,sha256=NKL5pGMt6QHrDjv0BXVyhr9eBsMA7DM9X2R0kpcPb1Y,1943
|
|
134
|
+
rio_tui/widgets/terminal.py,sha256=Ip0uKtdsWrLQphncVJigsFVeZeGQYTno6rM-5VIXKoU,6447
|
|
135
|
+
rio-0.4.0.dist-info/METADATA,sha256=SY1fIe9X7VEMc2Z8Bt6u4L8EQsR_iI-DNLZ733ANtZo,4108
|
|
136
|
+
rio-0.4.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
137
|
+
rio-0.4.0.dist-info/entry_points.txt,sha256=oGugo6Dvgb9fDiTC3zoUNQWZ_GRnkcUoCl51W00FoAU,43
|
|
138
|
+
rio-0.4.0.dist-info/licenses/LICENSE,sha256=-PLaKGZFrtCpEyu6v0yJCWrR95hROnRQ3eEh0Fq2_Wg,1063
|
|
139
|
+
rio-0.4.0.dist-info/licenses/NOTICE,sha256=lQVrQEsQH8CumOXq48YYUSXAMAMOz0CdqKjxT7rmYk4,1856
|
|
140
|
+
rio-0.4.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ju Lin
|
|
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.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
rio_ai (src/rio_ai/) is a port of huggingface/tau's `tau_ai` module, plus the
|
|
2
|
+
`tau_agent` message/tool/type vocabulary that `tau_ai` depends on
|
|
3
|
+
(`messages.py`, `tools.py`, `types.py`, `provider.py`, `provider_events.py`).
|
|
4
|
+
|
|
5
|
+
rio_coding (src/rio_coding/) is a port of huggingface/tau's `tau_coding`
|
|
6
|
+
module, retargeted from `tau_agent`'s append-only conversational harness onto
|
|
7
|
+
`rio_agent`'s SKILL.state runtime. Modules whose purpose was serving a growing
|
|
8
|
+
transcript -- context compaction, transcript rendering and export, and the
|
|
9
|
+
message-history session log -- were redesigned around execution state rather
|
|
10
|
+
than carried over. `rio_coding/session_store/` derives from `tau_agent.session`.
|
|
11
|
+
|
|
12
|
+
Upstream project: https://github.com/huggingface/tau
|
|
13
|
+
Upstream license: MIT, reproduced below.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
MIT License
|
|
18
|
+
|
|
19
|
+
Copyright (c) 2026 Alejandro AO
|
|
20
|
+
|
|
21
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
22
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
23
|
+
in the Software without restriction, including without limitation the rights
|
|
24
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
25
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
26
|
+
furnished to do so, subject to the following conditions:
|
|
27
|
+
|
|
28
|
+
The above copyright notice and this permission notice shall be included in all
|
|
29
|
+
copies or substantial portions of the Software.
|
|
30
|
+
|
|
31
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
32
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
33
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
34
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
35
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
36
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
37
|
+
SOFTWARE.
|
rio_agent/__init__.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""rio_agent: a SKILL.state long-horizon agent runtime.
|
|
2
|
+
|
|
3
|
+
Fixed skill instructions, a structured mutable execution state, and the
|
|
4
|
+
latest observation -- an action's result, a user message, or both -- are the
|
|
5
|
+
only inputs to each step. The runtime discards the reasoning behind each
|
|
6
|
+
step once it commits a valid state update, so per-step prompt size stays
|
|
7
|
+
fixed instead of growing with the number of steps already taken.
|
|
8
|
+
|
|
9
|
+
Exposes a loop interface shaped like `rio_ai`'s ported tau_agent-style
|
|
10
|
+
`AgentHarness`/`run_agent_loop` (a stateful harness plus a bare async-
|
|
11
|
+
generator loop function, both emitting a typed event stream), but backed
|
|
12
|
+
internally by execution state rather than an append-only transcript.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# ruff: noqa: F401 - this module intentionally defines the public facade
|
|
16
|
+
|
|
17
|
+
from rio_agent.errors import (
|
|
18
|
+
ActionNotFoundError,
|
|
19
|
+
ProviderResponseError,
|
|
20
|
+
RetriesExhaustedError,
|
|
21
|
+
StateValidationError,
|
|
22
|
+
)
|
|
23
|
+
from rio_agent.events import (
|
|
24
|
+
ActionEndEvent,
|
|
25
|
+
ActionStartEvent,
|
|
26
|
+
ReasoningDiscardedEvent,
|
|
27
|
+
RunEndEvent,
|
|
28
|
+
RunStartEvent,
|
|
29
|
+
SkillEvent,
|
|
30
|
+
StateUpdateEvent,
|
|
31
|
+
StepEndEvent,
|
|
32
|
+
StepStartEvent,
|
|
33
|
+
ValidationErrorEvent,
|
|
34
|
+
)
|
|
35
|
+
from rio_agent.harness import (
|
|
36
|
+
EventListener,
|
|
37
|
+
Harness,
|
|
38
|
+
HarnessCancellationToken,
|
|
39
|
+
HarnessConfig,
|
|
40
|
+
)
|
|
41
|
+
from rio_agent.loop import run_skill_loop
|
|
42
|
+
from rio_agent.observation import HarnessObservation
|
|
43
|
+
from rio_agent.prompt import STEP_TOOL_NAME, build_step_messages, skill_step_tool
|
|
44
|
+
from rio_agent.skill import HarnessSpec
|
|
45
|
+
from rio_agent.state import apply_state_delta, validate_state_delta
|
|
46
|
+
|
|
47
|
+
__all__ = [name for name in globals() if not name.startswith("_")]
|
rio_agent/errors.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Errors raised while validating and committing SKILL.state step output."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class StateValidationError(Exception):
|
|
7
|
+
"""A proposed state_delta failed deterministic runtime validation.
|
|
8
|
+
|
|
9
|
+
Validation happens in the runtime, not the model, so a malformed patch
|
|
10
|
+
never corrupts persistent state -- it triggers a rollback-retry cycle
|
|
11
|
+
instead (see `RetriesExhaustedError`).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ActionNotFoundError(Exception):
|
|
16
|
+
"""The model proposed an action that isn't declared on the skill."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RetriesExhaustedError(Exception):
|
|
20
|
+
"""The rollback-retry cycle exceeded `max_retries` without a valid step."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ProviderResponseError(Exception):
|
|
24
|
+
"""The provider failed to produce an assistant message.
|
|
25
|
+
|
|
26
|
+
Distinct from a malformed step: the model never got to propose one, so
|
|
27
|
+
there is nothing to roll back and nothing a retry of the same prompt would
|
|
28
|
+
correct.
|
|
29
|
+
"""
|
rio_agent/events.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Events emitted by `rio_agent.loop.run_skill_loop`.
|
|
2
|
+
|
|
3
|
+
Shaped like `rio_ai`'s ported tau_agent-style agent events (a flat stream of
|
|
4
|
+
small, typed records a UI or logger can subscribe to) but describing SKILL.
|
|
5
|
+
state's step lifecycle instead of an append-only conversation turn.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
from rio_agent.observation import HarnessObservation
|
|
13
|
+
from rio_ai.tools import AgentToolResult
|
|
14
|
+
from rio_ai.types import JSONObject
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, slots=True)
|
|
18
|
+
class RunStartEvent:
|
|
19
|
+
skill: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True, slots=True)
|
|
23
|
+
class StepStartEvent:
|
|
24
|
+
step: int
|
|
25
|
+
state: JSONObject
|
|
26
|
+
observation: HarnessObservation
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True, slots=True)
|
|
30
|
+
class ReasoningDiscardedEvent:
|
|
31
|
+
"""The model's reasoning for this step. Surfaced once for observability, then never sent back
|
|
32
|
+
to the model.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
step: int
|
|
36
|
+
reasoning: str
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True, slots=True)
|
|
40
|
+
class ValidationErrorEvent:
|
|
41
|
+
"""A proposed state update or action failed runtime validation; a rollback-retry follows."""
|
|
42
|
+
|
|
43
|
+
step: int
|
|
44
|
+
attempt: int
|
|
45
|
+
error: str
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True, slots=True)
|
|
49
|
+
class StateUpdateEvent:
|
|
50
|
+
"""The proposed state update was validated and committed to form the new state."""
|
|
51
|
+
|
|
52
|
+
step: int
|
|
53
|
+
delta: JSONObject
|
|
54
|
+
state: JSONObject
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(frozen=True, slots=True)
|
|
58
|
+
class ActionStartEvent:
|
|
59
|
+
step: int
|
|
60
|
+
name: str
|
|
61
|
+
arguments: JSONObject
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass(frozen=True, slots=True)
|
|
65
|
+
class ActionEndEvent:
|
|
66
|
+
step: int
|
|
67
|
+
name: str
|
|
68
|
+
result: AgentToolResult
|
|
69
|
+
is_error: bool
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass(frozen=True, slots=True)
|
|
73
|
+
class StepEndEvent:
|
|
74
|
+
step: int
|
|
75
|
+
state: JSONObject
|
|
76
|
+
terminated: bool
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass(frozen=True, slots=True)
|
|
80
|
+
class RunEndEvent:
|
|
81
|
+
steps: int
|
|
82
|
+
state: JSONObject
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
type SkillEvent = (
|
|
86
|
+
RunStartEvent
|
|
87
|
+
| StepStartEvent
|
|
88
|
+
| ReasoningDiscardedEvent
|
|
89
|
+
| ValidationErrorEvent
|
|
90
|
+
| StateUpdateEvent
|
|
91
|
+
| ActionStartEvent
|
|
92
|
+
| ActionEndEvent
|
|
93
|
+
| StepEndEvent
|
|
94
|
+
| RunEndEvent
|
|
95
|
+
)
|
rio_agent/harness.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Harness: a reusable stateful runtime around `run_skill_loop`.
|
|
2
|
+
|
|
3
|
+
Deliberately mirrors the public shape of `rio_ai`'s ported tau_agent-style
|
|
4
|
+
`AgentHarness` -- construct with a config, `subscribe()` an event listener,
|
|
5
|
+
call `run()`/`prompt()` to drive it, `cancel()` to stop it -- so callers
|
|
6
|
+
already familiar with that loop interface can pick this one up directly.
|
|
7
|
+
The difference is entirely internal: instead of holding a growing message
|
|
8
|
+
transcript, this harness holds only the current execution state and
|
|
9
|
+
replays `run_skill_loop`'s fixed-size per-step prompt.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from collections.abc import AsyncIterator, Awaitable, Callable
|
|
15
|
+
from contextlib import suppress
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from inspect import isawaitable
|
|
18
|
+
|
|
19
|
+
from rio_agent.events import RunEndEvent, SkillEvent, StateUpdateEvent
|
|
20
|
+
from rio_agent.loop import run_skill_loop
|
|
21
|
+
from rio_agent.observation import HarnessObservation
|
|
22
|
+
from rio_agent.skill import HarnessSpec
|
|
23
|
+
from rio_ai.provider import ModelProvider
|
|
24
|
+
from rio_ai.types import JSONObject, JSONValue
|
|
25
|
+
|
|
26
|
+
EventListener = Callable[[SkillEvent], Awaitable[None] | None]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(slots=True)
|
|
30
|
+
class HarnessConfig:
|
|
31
|
+
provider: ModelProvider
|
|
32
|
+
model: str
|
|
33
|
+
skill: HarnessSpec
|
|
34
|
+
max_steps: int | None = None
|
|
35
|
+
max_retries: int = 2
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class HarnessCancellationToken:
|
|
39
|
+
def __init__(self) -> None:
|
|
40
|
+
self._cancelled = False
|
|
41
|
+
|
|
42
|
+
def cancel(self) -> None:
|
|
43
|
+
self._cancelled = True
|
|
44
|
+
|
|
45
|
+
def is_cancelled(self) -> bool:
|
|
46
|
+
return self._cancelled
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Harness:
|
|
50
|
+
"""Reusable stateful long-horizon agent runtime built on SKILL.state."""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
config: HarnessConfig,
|
|
55
|
+
*,
|
|
56
|
+
state: JSONObject | None = None,
|
|
57
|
+
) -> None:
|
|
58
|
+
self._config = config
|
|
59
|
+
self._state: JSONObject = dict(state if state is not None else config.skill.initial_state)
|
|
60
|
+
self._listeners: list[EventListener] = []
|
|
61
|
+
self._current_signal: HarnessCancellationToken | None = None
|
|
62
|
+
self._running = False
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def state(self) -> dict[str, JSONValue]:
|
|
66
|
+
return dict(self._state)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def config(self) -> HarnessConfig:
|
|
70
|
+
return self._config
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def is_running(self) -> bool:
|
|
74
|
+
return self._running
|
|
75
|
+
|
|
76
|
+
def subscribe(self, listener: EventListener) -> Callable[[], None]:
|
|
77
|
+
self._listeners.append(listener)
|
|
78
|
+
|
|
79
|
+
def unsubscribe() -> None:
|
|
80
|
+
with suppress(ValueError):
|
|
81
|
+
self._listeners.remove(listener)
|
|
82
|
+
|
|
83
|
+
return unsubscribe
|
|
84
|
+
|
|
85
|
+
def cancel(self) -> None:
|
|
86
|
+
if self._current_signal is not None:
|
|
87
|
+
self._current_signal.cancel()
|
|
88
|
+
|
|
89
|
+
def run(self, observation: HarnessObservation) -> AsyncIterator[SkillEvent]:
|
|
90
|
+
self._ensure_not_running()
|
|
91
|
+
self._running = True
|
|
92
|
+
return self._run(observation)
|
|
93
|
+
|
|
94
|
+
async def _run(self, observation: HarnessObservation) -> AsyncIterator[SkillEvent]:
|
|
95
|
+
signal = HarnessCancellationToken()
|
|
96
|
+
self._current_signal = signal
|
|
97
|
+
try:
|
|
98
|
+
async for event in run_skill_loop(
|
|
99
|
+
provider=self._config.provider,
|
|
100
|
+
model=self._config.model,
|
|
101
|
+
skill=self._config.skill,
|
|
102
|
+
observation=observation,
|
|
103
|
+
state=self._state,
|
|
104
|
+
max_steps=self._config.max_steps,
|
|
105
|
+
max_retries=self._config.max_retries,
|
|
106
|
+
signal=signal,
|
|
107
|
+
):
|
|
108
|
+
if isinstance(event, (StateUpdateEvent, RunEndEvent)):
|
|
109
|
+
self._state = dict(event.state)
|
|
110
|
+
await self._notify(event)
|
|
111
|
+
yield event
|
|
112
|
+
finally:
|
|
113
|
+
if self._current_signal is signal:
|
|
114
|
+
self._current_signal = None
|
|
115
|
+
self._running = False
|
|
116
|
+
|
|
117
|
+
async def _notify(self, event: SkillEvent) -> None:
|
|
118
|
+
for listener in list(self._listeners):
|
|
119
|
+
result = listener(event)
|
|
120
|
+
if isawaitable(result):
|
|
121
|
+
await result
|
|
122
|
+
|
|
123
|
+
def _ensure_not_running(self) -> None:
|
|
124
|
+
if self._running:
|
|
125
|
+
raise RuntimeError("Harness is already running")
|