awp-python 0.1.0a2__tar.gz → 0.1.0a4__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.
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/CHANGELOG.md +18 -0
- awp_python-0.1.0a4/PKG-INFO +130 -0
- awp_python-0.1.0a4/README.md +109 -0
- awp_python-0.1.0a4/conformance/README.md +53 -0
- awp_python-0.1.0a4/conformance/core-agent-lockstep.json +1496 -0
- awp_python-0.1.0a4/conformance/core-agent-streaming.json +1498 -0
- awp_python-0.1.0a4/conformance/frames.json +4 -0
- awp_python-0.1.0a4/conformance/manifest-lockstep.json +157 -0
- awp_python-0.1.0a4/conformance/manifest-streaming.json +160 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/pyproject.toml +5 -6
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/__init__.py +1 -1
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/action-status.schema.json +1 -1
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/action-submit-result.schema.json +1 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/approval-respond.schema.json +4 -4
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/common.schema.json +3 -3
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/error.schema.json +1 -1
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/frame-inline.schema.json +1 -1
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/obs-report.schema.json +1 -1
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/safety-policy.schema.json +1 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-ready.schema.json +2 -2
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-state.schema.json +1 -1
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/world-event.schema.json +1 -1
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/aio.py +45 -10
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/client.py +62 -10
- awp_python-0.1.0a4/src/awp/demo.py +157 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/frames.py +4 -0
- awp_python-0.1.0a4/tests/test_aio.py +190 -0
- awp_python-0.1.0a4/tests/test_client.py +31 -0
- awp_python-0.1.0a4/tests/test_evidence.py +43 -0
- awp_python-0.1.0a4/tests/world.py +348 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/uv.lock +1 -1
- awp_python-0.1.0a2/PKG-INFO +0 -129
- awp_python-0.1.0a2/README.md +0 -108
- awp_python-0.1.0a2/conformance/README.md +0 -63
- awp_python-0.1.0a2/conformance/core-world-lockstep.json +0 -1655
- awp_python-0.1.0a2/conformance/core-world-streaming.json +0 -1657
- awp_python-0.1.0a2/scripts/check_traces.py +0 -57
- awp_python-0.1.0a2/src/awp_sim/__init__.py +0 -8
- awp_python-0.1.0a2/src/awp_sim/__main__.py +0 -5
- awp_python-0.1.0a2/src/awp_sim/arm.py +0 -177
- awp_python-0.1.0a2/src/awp_sim/audit.py +0 -138
- awp_python-0.1.0a2/src/awp_sim/cli.py +0 -238
- awp_python-0.1.0a2/src/awp_sim/config.py +0 -225
- awp_python-0.1.0a2/src/awp_sim/demo.py +0 -85
- awp_python-0.1.0a2/src/awp_sim/loopback.py +0 -222
- awp_python-0.1.0a2/src/awp_sim/py.typed +0 -0
- awp_python-0.1.0a2/src/awp_sim/recorder.py +0 -72
- awp_python-0.1.0a2/src/awp_sim/replay.py +0 -134
- awp_python-0.1.0a2/src/awp_sim/scenarios.py +0 -427
- awp_python-0.1.0a2/src/awp_sim/server.py +0 -336
- awp_python-0.1.0a2/src/awp_sim/session.py +0 -153
- awp_python-0.1.0a2/src/awp_sim/world.py +0 -1638
- awp_python-0.1.0a2/tests/helpers.py +0 -62
- awp_python-0.1.0a2/tests/test_audit.py +0 -62
- awp_python-0.1.0a2/tests/test_cli.py +0 -26
- awp_python-0.1.0a2/tests/test_e2e.py +0 -118
- awp_python-0.1.0a2/tests/test_evidence.py +0 -181
- awp_python-0.1.0a2/tests/test_features.py +0 -297
- awp_python-0.1.0a2/tests/test_regressions.py +0 -243
- awp_python-0.1.0a2/tests/test_scenarios.py +0 -33
- awp_python-0.1.0a2/tests/test_streams.py +0 -139
- awp_python-0.1.0a2/tests/test_world.py +0 -479
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/.gitignore +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/.gitmodules +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/.python-version +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/LICENSE +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/scripts/sync_spec.py +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/lifecycle.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/action-cancel-result.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/action-ref.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/action-schema.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/action-submit.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/agent-manifest.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/approval-requested.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/embodiment.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/empty-result.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/frame-tree.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/observation-channel.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/ping-result.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/ping.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/profiles/gui-actions.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/reset-result.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/reset.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/restore.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-open.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-resume.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-telemetry.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-transfer-result.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-transfer.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/snapshot-result.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/subscribe-result.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/subscribe.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/task-update.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/tick-result.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/tick.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/unsubscribe.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/_spec/schemas/world-manifest.schema.json +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/clock.py +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/errors.py +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/jsonrpc.py +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/lifecycle.py +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/py.typed +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/src/awp/schema.py +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/tests/__init__.py +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/tests/conftest.py +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/tests/test_client_traces.py +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/tests/test_frames.py +0 -0
- {awp_python-0.1.0a2 → awp_python-0.1.0a4}/tests/test_schema.py +0 -0
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0a4
|
|
4
|
+
|
|
5
|
+
Targets specification revision `0.1-draft.9`.
|
|
6
|
+
|
|
7
|
+
- The reference world moves to its own package, [awp-sim](https://github.com/Hyperduality/awp-sim), and awp-python is the agent SDK alone.
|
|
8
|
+
- `awp-sim demo` becomes `awp-demo` (`awp.demo`). It reads `$AWP_URL` and `$AWP_TOKEN` when its flags are absent.
|
|
9
|
+
- Tests run against a scripted world over WebSockets instead of awp-sim, and CI runs awp-conformance against `awp-demo` in both time models.
|
|
10
|
+
|
|
11
|
+
## 0.1.0a3
|
|
12
|
+
|
|
13
|
+
Targets specification revision `0.1-draft.9`.
|
|
14
|
+
|
|
15
|
+
- `awp_sim`: standing approvals (AWP-APR-004), declared as `safety_policy.standing_approvals` with the approval feature; a submission within a grant is admitted without approval and its result names the grant's `approval_id`.
|
|
16
|
+
- `awp`: `AsyncClient.advance` returns once every subscribed per-tick channel holds a frame of the new tick, which on a stream connection may follow the result (AWP-TIM-003). A `session.resume` answered `AWP_SESSION_UNKNOWN` closes the session and forgets its actions (AWP-SES-008). `respond_approval` takes `standing`.
|
|
17
|
+
- `awp-sim demo` resumes after a lost connection, or opens a new session if the world no longer holds it.
|
|
18
|
+
- `awp_sim`: an integer beyond 2^53-1 closes the session with reason `protocol_error` and the connection with code 1002 (AWP-CTL-009); a malformed frame on a stream connection closes it with code 1002 and `AWP_MALFORMED` (AWP-DAT-010); after a lockstep resumption every per-tick channel restarts with a resync keyframe at the current tick (AWP-TIM-009); after a reset the fresh frames precede the result (AWP-PRM-006).
|
|
19
|
+
- `awp`: an integer beyond 2^53-1 ends the session with `session.close` and close code 1002; a malformed stream frame is dropped and its stream connection closed and re-established; frames with `resync` but not `keyframe` are malformed. While a lost stream connection is down, `AsyncClient.command` raises instead of sending inline (AWP-TRN-010). `ClientConnection.receive_frame` raises for a malformed frame, and `delivery()` reports each channel's frames and gaps.
|
|
20
|
+
|
|
3
21
|
## 0.1.0a2
|
|
4
22
|
|
|
5
23
|
Targets specification revision `0.1-draft.8`.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: awp-python
|
|
3
|
+
Version: 0.1.0a4
|
|
4
|
+
Summary: Agent World Protocol agent SDK for Python.
|
|
5
|
+
Project-URL: Specification, https://www.agentworldprotocol.com
|
|
6
|
+
Project-URL: Source, https://github.com/Hyperduality/awp-python
|
|
7
|
+
Author: Hyperduality
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: jsonschema>=4.23
|
|
19
|
+
Requires-Dist: websockets>=13.0
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# awp-python
|
|
23
|
+
|
|
24
|
+
The Python agent SDK for the [Agent World Protocol](https://www.agentworldprotocol.com). `ClientConnection` is the agent side of AWP as a sans-IO state machine, and `awp.aio.AsyncClient` drives it over a WebSocket.
|
|
25
|
+
|
|
26
|
+
It targets specification revision **`0.1-draft.9`**, pinned as the `spec/` submodule. This is an alpha, so the API will change along with the draft. The reference world, [awp-sim](https://github.com/Hyperduality/awp-sim), is a separate package built on this one.
|
|
27
|
+
|
|
28
|
+
## Status
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
In both time models, the demo agent built on the client is **Core Agent: AWP-conformant against 0.1-draft.9**. [awp-conformance](https://github.com/Hyperduality/awp-conformance) reports no failure and nothing untested, and [`conformance/`](conformance/README.md) holds the reports and the evidence for their manual rows. CI runs the suite against the demo agent on every change.
|
|
33
|
+
|
|
34
|
+
| Implemented | Not implemented |
|
|
35
|
+
|---|---|
|
|
36
|
+
| Lockstep and streaming sessions; `world.tick` advances that wait for every per-tick channel | Multi-bind (`embodiments`) |
|
|
37
|
+
| Inline and `ws` stream bindings; binary frame codec (every spec vector) | Other stream bindings (`webrtc`, `webtransport`, `shm`, `grpc`) |
|
|
38
|
+
| Action lifecycle checked against the spec's transition table; idempotent resubmission | |
|
|
39
|
+
| Heartbeats, clock synchronization, receiver reports, resumption with replay and acknowledgement | |
|
|
40
|
+
| Canonical schema validation in receiver and sender forms | |
|
|
41
|
+
| Task, approval and standing approvals, transfer, reset, snapshots, and command channels | |
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install --pre awp-python
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Requires Python 3.11+.
|
|
50
|
+
|
|
51
|
+
## Using the client
|
|
52
|
+
|
|
53
|
+
With asyncio:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from awp import ClientConnection
|
|
57
|
+
from awp.aio import AsyncClient
|
|
58
|
+
|
|
59
|
+
conn = ClientConnection({"name": "my-agent", "version": "0.1.0", "vendor": "me"}, ["proprio/json"])
|
|
60
|
+
async with AsyncClient(conn, "ws://127.0.0.1:8710") as client:
|
|
61
|
+
await client.initialize()
|
|
62
|
+
await client.open_session("streaming", embodiment="arm_01", subscribe=["proprio"])
|
|
63
|
+
await client.wait_for(lambda e: "proprio" in client.latest)
|
|
64
|
+
record = await client.submit(
|
|
65
|
+
"move_to_pose",
|
|
66
|
+
{"pose": {"frame": "base", "p_m": [0.3, 0.2, 0.5], "q": [0, 0, 0, 1]}},
|
|
67
|
+
basis=client.latest["proprio"].frame, # the observation this intent rests on
|
|
68
|
+
valid_for_ms=200,
|
|
69
|
+
)
|
|
70
|
+
print((await client.wait_terminal(record.action_id)).state)
|
|
71
|
+
await client.close_session()
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Without asyncio, feed `ClientConnection` the decoded messages and send whatever it queues:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
events = conn.receive(message) # typed events: ActionUpdated, FrameReceived, ...
|
|
78
|
+
for out in conn.outgoing(): # messages to send, in order
|
|
79
|
+
transport.send(json.dumps(out))
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The connection:
|
|
83
|
+
|
|
84
|
+
- tracks the action lifecycle against the spec's transition table;
|
|
85
|
+
- deduplicates replayed statuses;
|
|
86
|
+
- keeps the clock offset from heartbeats;
|
|
87
|
+
- reports any violation by the world as a `ProtocolViolation` event.
|
|
88
|
+
|
|
89
|
+
## The demo agent
|
|
90
|
+
|
|
91
|
+
`awp-demo` is a scripted agent for awp-sim's arm. It moves, cancels a move, and triggers an envelope refusal. It rides out a lost connection by resuming the session, or by opening a new one if the world no longer holds it.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pip install --pre awp-sim
|
|
95
|
+
awp-sim serve & # streaming world on ws://127.0.0.1:8710
|
|
96
|
+
awp-demo # or --url, --token ($AWP_URL, $AWP_TOKEN)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Layout
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
src/awp/ client protocol layer (sans-IO), asyncio adapter, frame codec, schemas, demo agent
|
|
103
|
+
src/awp/_spec/ schemas and lifecycle table bundled from spec/ (scripts/sync_spec.py)
|
|
104
|
+
tests/ unit tests, and a scripted world that serves over WebSockets
|
|
105
|
+
conformance/ conformance reports, the manifests they were run against, and evidence
|
|
106
|
+
spec/ agent-world-protocol, pinned at spec-v0.1-draft.9
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Development
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
git clone --recurse-submodules https://github.com/Hyperduality/awp-python
|
|
113
|
+
cd awp-python
|
|
114
|
+
uv sync
|
|
115
|
+
uv run ruff check && uv run ruff format --check
|
|
116
|
+
uv run mypy
|
|
117
|
+
uv run pytest --cov
|
|
118
|
+
uv run python scripts/sync_spec.py --check
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
To move to a new draft revision:
|
|
122
|
+
|
|
123
|
+
1. Check out its tag in `spec/`.
|
|
124
|
+
2. Run `scripts/sync_spec.py`.
|
|
125
|
+
3. Update `SPEC_REVISION` in `src/awp/__init__.py`.
|
|
126
|
+
4. Fix whatever the tests report.
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
Apache-2.0. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# awp-python
|
|
2
|
+
|
|
3
|
+
The Python agent SDK for the [Agent World Protocol](https://www.agentworldprotocol.com). `ClientConnection` is the agent side of AWP as a sans-IO state machine, and `awp.aio.AsyncClient` drives it over a WebSocket.
|
|
4
|
+
|
|
5
|
+
It targets specification revision **`0.1-draft.9`**, pinned as the `spec/` submodule. This is an alpha, so the API will change along with the draft. The reference world, [awp-sim](https://github.com/Hyperduality/awp-sim), is a separate package built on this one.
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
In both time models, the demo agent built on the client is **Core Agent: AWP-conformant against 0.1-draft.9**. [awp-conformance](https://github.com/Hyperduality/awp-conformance) reports no failure and nothing untested, and [`conformance/`](conformance/README.md) holds the reports and the evidence for their manual rows. CI runs the suite against the demo agent on every change.
|
|
12
|
+
|
|
13
|
+
| Implemented | Not implemented |
|
|
14
|
+
|---|---|
|
|
15
|
+
| Lockstep and streaming sessions; `world.tick` advances that wait for every per-tick channel | Multi-bind (`embodiments`) |
|
|
16
|
+
| Inline and `ws` stream bindings; binary frame codec (every spec vector) | Other stream bindings (`webrtc`, `webtransport`, `shm`, `grpc`) |
|
|
17
|
+
| Action lifecycle checked against the spec's transition table; idempotent resubmission | |
|
|
18
|
+
| Heartbeats, clock synchronization, receiver reports, resumption with replay and acknowledgement | |
|
|
19
|
+
| Canonical schema validation in receiver and sender forms | |
|
|
20
|
+
| Task, approval and standing approvals, transfer, reset, snapshots, and command channels | |
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install --pre awp-python
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Requires Python 3.11+.
|
|
29
|
+
|
|
30
|
+
## Using the client
|
|
31
|
+
|
|
32
|
+
With asyncio:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from awp import ClientConnection
|
|
36
|
+
from awp.aio import AsyncClient
|
|
37
|
+
|
|
38
|
+
conn = ClientConnection({"name": "my-agent", "version": "0.1.0", "vendor": "me"}, ["proprio/json"])
|
|
39
|
+
async with AsyncClient(conn, "ws://127.0.0.1:8710") as client:
|
|
40
|
+
await client.initialize()
|
|
41
|
+
await client.open_session("streaming", embodiment="arm_01", subscribe=["proprio"])
|
|
42
|
+
await client.wait_for(lambda e: "proprio" in client.latest)
|
|
43
|
+
record = await client.submit(
|
|
44
|
+
"move_to_pose",
|
|
45
|
+
{"pose": {"frame": "base", "p_m": [0.3, 0.2, 0.5], "q": [0, 0, 0, 1]}},
|
|
46
|
+
basis=client.latest["proprio"].frame, # the observation this intent rests on
|
|
47
|
+
valid_for_ms=200,
|
|
48
|
+
)
|
|
49
|
+
print((await client.wait_terminal(record.action_id)).state)
|
|
50
|
+
await client.close_session()
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Without asyncio, feed `ClientConnection` the decoded messages and send whatever it queues:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
events = conn.receive(message) # typed events: ActionUpdated, FrameReceived, ...
|
|
57
|
+
for out in conn.outgoing(): # messages to send, in order
|
|
58
|
+
transport.send(json.dumps(out))
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The connection:
|
|
62
|
+
|
|
63
|
+
- tracks the action lifecycle against the spec's transition table;
|
|
64
|
+
- deduplicates replayed statuses;
|
|
65
|
+
- keeps the clock offset from heartbeats;
|
|
66
|
+
- reports any violation by the world as a `ProtocolViolation` event.
|
|
67
|
+
|
|
68
|
+
## The demo agent
|
|
69
|
+
|
|
70
|
+
`awp-demo` is a scripted agent for awp-sim's arm. It moves, cancels a move, and triggers an envelope refusal. It rides out a lost connection by resuming the session, or by opening a new one if the world no longer holds it.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install --pre awp-sim
|
|
74
|
+
awp-sim serve & # streaming world on ws://127.0.0.1:8710
|
|
75
|
+
awp-demo # or --url, --token ($AWP_URL, $AWP_TOKEN)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Layout
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
src/awp/ client protocol layer (sans-IO), asyncio adapter, frame codec, schemas, demo agent
|
|
82
|
+
src/awp/_spec/ schemas and lifecycle table bundled from spec/ (scripts/sync_spec.py)
|
|
83
|
+
tests/ unit tests, and a scripted world that serves over WebSockets
|
|
84
|
+
conformance/ conformance reports, the manifests they were run against, and evidence
|
|
85
|
+
spec/ agent-world-protocol, pinned at spec-v0.1-draft.9
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Development
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git clone --recurse-submodules https://github.com/Hyperduality/awp-python
|
|
92
|
+
cd awp-python
|
|
93
|
+
uv sync
|
|
94
|
+
uv run ruff check && uv run ruff format --check
|
|
95
|
+
uv run mypy
|
|
96
|
+
uv run pytest --cov
|
|
97
|
+
uv run python scripts/sync_spec.py --check
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
To move to a new draft revision:
|
|
101
|
+
|
|
102
|
+
1. Check out its tag in `spec/`.
|
|
103
|
+
2. Run `scripts/sync_spec.py`.
|
|
104
|
+
3. Update `SPEC_REVISION` in `src/awp/__init__.py`.
|
|
105
|
+
4. Fix whatever the tests report.
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
Apache-2.0. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Conformance
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
| Class | Configuration | Claim | Report |
|
|
6
|
+
|---|---|---|---|
|
|
7
|
+
| Core Agent | `awp-demo`, [`manifest-lockstep.json`](manifest-lockstep.json) | Core Agent (lockstep): AWP-conformant against 0.1-draft.9 (awp-conformance 0.1.0a3) | [`core-agent-lockstep.json`](core-agent-lockstep.json) |
|
|
8
|
+
| Core Agent | `awp-demo`, [`manifest-streaming.json`](manifest-streaming.json) | Core Agent (streaming): AWP-conformant against 0.1-draft.9 (awp-conformance 0.1.0a3) | [`core-agent-streaming.json`](core-agent-streaming.json) |
|
|
9
|
+
|
|
10
|
+
The reports come from awp-conformance 0.1.0a3 run against awp-python 0.1.0a4. Neither has a failure or anything untested. The evidence for their `manual` rows is below (AWP-CNF-005).
|
|
11
|
+
|
|
12
|
+
## Reproduce
|
|
13
|
+
|
|
14
|
+
The suite serves each manifest from its harness world and runs the demo agent against it. The manifests are awp-sim's (`awp-sim manifest --mode lockstep`, `--mode streaming`). [`frames.json`](frames.json) gives the harness one sample payload per channel.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install --pre awp-python awp-conformance
|
|
18
|
+
awp-conformance agent --manifest conformance/manifest-lockstep.json --frames conformance/frames.json \
|
|
19
|
+
--mode lockstep --out report/ -- awp-demo --url '{url}' --token '{token}'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
For streaming, use `manifest-streaming.json` with `--mode streaming`. CI runs both on every change. The tests cited below run in CI too:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uv run pytest tests/test_frames.py tests/test_evidence.py
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Manual evidence
|
|
29
|
+
|
|
30
|
+
### AWP-DAT-001, AWP-DAT-009: loss accounting at the receiver (lockstep)
|
|
31
|
+
|
|
32
|
+
In streaming, the suite reads the agent's loss accounting from its `obs.report`. Lockstep sessions send no `obs.report`, so these rows are `manual` there.
|
|
33
|
+
|
|
34
|
+
- `test_the_receiver_counts_seq_gaps_as_loss_but_not_the_gap_before_a_resync` (`tests/test_evidence.py`) runs in each time model:
|
|
35
|
+
- on one channel, frames that skip two `seq` values count two missing frames in `ClientConnection.delivery()`;
|
|
36
|
+
- on another, a resync frame after a gap of five counts none.
|
|
37
|
+
- The client holds no delta state. Each frame reaches the application whole, with its `keyframe` and `resync` flags.
|
|
38
|
+
|
|
39
|
+
### AWP-DAT-008: frame test vectors
|
|
40
|
+
|
|
41
|
+
`test_vectors` (`tests/test_frames.py`) runs `awp.frames` over every vector in `schemas/test-vectors/frames.json` of the `spec/` submodule, which is pinned at `spec-v0.1-draft.9`:
|
|
42
|
+
|
|
43
|
+
- the 10 valid vectors decode to their listed fields;
|
|
44
|
+
- the 7 marked `expect_error` are rejected with their listed error.
|
|
45
|
+
|
|
46
|
+
`test_roundtrip_without_vendor` re-encodes the 8 valid vectors that have no unknown extension or reserved bits, and each matches byte for byte.
|
|
47
|
+
|
|
48
|
+
### AWP-VER-009: the draft revision is named
|
|
49
|
+
|
|
50
|
+
- The [README](../README.md) and the [Python SDK page](https://www.agentworldprotocol.com/sdks/python) name `0.1-draft.9`.
|
|
51
|
+
- `awp.SPEC_REVISION` is `"0.1-draft.9"`.
|
|
52
|
+
- The `spec/` submodule is pinned at the tag `spec-v0.1-draft.9`.
|
|
53
|
+
- Each report records `"specification": "0.1-draft.9"`, and its claim names the revision.
|