statewire 0.3.3__tar.gz → 0.4.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.
- {statewire-0.3.3 → statewire-0.4.0}/PKG-INFO +1 -1
- {statewire-0.3.3 → statewire-0.4.0}/pyproject.toml +1 -1
- {statewire-0.3.3 → statewire-0.4.0}/src/statewire/assistant_transport.py +15 -6
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_assistant_transport.py +15 -10
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_assistant_transport_client.py +2 -2
- {statewire-0.3.3 → statewire-0.4.0}/.gitignore +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/README.md +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/examples/__init__.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/examples/demo_app.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/src/statewire/__init__.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/src/statewire/api.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/src/statewire/assistant_transport_client.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/src/statewire/client.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/src/statewire/langgraph.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/src/statewire/ops.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/src/statewire/state.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/client_helpers.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/statewire_helpers.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_assistant_transport_facade.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_authorize.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_client.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_client_ws.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_commands.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_context.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_langgraph.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_lifespan.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_meta.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_state_proxy.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_statewire_hostable.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_stream.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_writer_lease.py +0 -0
- {statewire-0.3.3 → statewire-0.4.0}/tests/test_ws.py +0 -0
|
@@ -17,8 +17,9 @@ the legacy commands, which are normalized to the canonical ``run/*`` dialect
|
|
|
17
17
|
(a host-registered handler of the same legacy name always wins and receives
|
|
18
18
|
the command whole):
|
|
19
19
|
|
|
20
|
-
- ``add-message`` becomes ``run/steer`` with ``{"message": <wire message
|
|
21
|
-
|
|
20
|
+
- ``add-message`` becomes ``run/steer`` with ``{"message": <wire message>,
|
|
21
|
+
"anchorMessageId": <body parentId>}`` (body ``parentId`` null or absent →
|
|
22
|
+
``null``): a fresh ``legacy_`` id is synthesized, image parts become
|
|
22
23
|
``{"type": "file", "mediaType": "image/*", "url"}`` parts, and command-level
|
|
23
24
|
anchors are dropped. A command-level non-null ``sourceId`` makes it
|
|
24
25
|
``run/edit`` with that ``sourceId`` directly; otherwise, when the body
|
|
@@ -31,8 +32,8 @@ the command whole):
|
|
|
31
32
|
unanswered tool-call request in ``state["inputRequests"]`` (None rejects),
|
|
32
33
|
and a ``modelContent`` field rejects (unsupported in ``run/input``).
|
|
33
34
|
- A body with a ``parentId`` and no commands is the legacy reload: it becomes
|
|
34
|
-
``run/reload`` with ``{"sourceId": <child
|
|
35
|
-
(None rejects).
|
|
35
|
+
``run/reload`` with ``{"sourceId": <child>, "messageId": <fresh legacy_
|
|
36
|
+
id>}`` via ``get_message_child_id`` (None rejects).
|
|
36
37
|
|
|
37
38
|
A host without the target ``run/*`` handler rejects the batch via the
|
|
38
39
|
ordinary unknown-command path. The response streams state changes as legacy
|
|
@@ -464,7 +465,10 @@ class AssistantTransport(Statewire):
|
|
|
464
465
|
"method": "run/edit",
|
|
465
466
|
"params": [{"sourceId": source_id, "message": message}],
|
|
466
467
|
}
|
|
467
|
-
return {
|
|
468
|
+
return {
|
|
469
|
+
"method": "run/steer",
|
|
470
|
+
"params": [{"message": message, "anchorMessageId": parent_id}],
|
|
471
|
+
}
|
|
468
472
|
if kind == "add-tool-result" and "add-tool-result" not in registered:
|
|
469
473
|
return {
|
|
470
474
|
"method": "run/input",
|
|
@@ -494,7 +498,12 @@ class AssistantTransport(Statewire):
|
|
|
494
498
|
source_id = await self.get_message_child_id(parent_id)
|
|
495
499
|
if source_id is None:
|
|
496
500
|
raise _RunError(f"reload: no child message for parentId {parent_id!r}")
|
|
497
|
-
return {
|
|
501
|
+
return {
|
|
502
|
+
"method": "run/reload",
|
|
503
|
+
"params": [
|
|
504
|
+
{"sourceId": source_id, "messageId": f"legacy_{uuid4().hex}"}
|
|
505
|
+
],
|
|
506
|
+
}
|
|
498
507
|
|
|
499
508
|
async def _assistant_transport_dispatch(self, method: str, identity: Any) -> None:
|
|
500
509
|
client_id = "at-" + secrets.token_urlsafe(9)
|
|
@@ -27,7 +27,7 @@ class Harness(AssistantTransport):
|
|
|
27
27
|
self.state = {"messages": [], "text": "", "meta": {"title": None}}
|
|
28
28
|
yield
|
|
29
29
|
|
|
30
|
-
@command("
|
|
30
|
+
@command("demo/echo")
|
|
31
31
|
async def add_message(self, cmd):
|
|
32
32
|
self.seen.append(cmd)
|
|
33
33
|
self.state["messages"].append(cmd["message"])
|
|
@@ -120,7 +120,7 @@ async def test_data_stream_response_streams_snapshot_then_ops_until_idle():
|
|
|
120
120
|
{
|
|
121
121
|
"commands": [
|
|
122
122
|
{
|
|
123
|
-
"type": "
|
|
123
|
+
"type": "demo/echo",
|
|
124
124
|
"message": {
|
|
125
125
|
"role": "user",
|
|
126
126
|
"parts": [{"type": "text", "text": "hi"}],
|
|
@@ -229,7 +229,7 @@ async def test_splice_and_remove_fall_back_to_parent_set():
|
|
|
229
229
|
{
|
|
230
230
|
"commands": [
|
|
231
231
|
{
|
|
232
|
-
"type": "
|
|
232
|
+
"type": "demo/echo",
|
|
233
233
|
"message": {
|
|
234
234
|
"role": "user",
|
|
235
235
|
"parts": [{"type": "text", "text": "hi"}],
|
|
@@ -266,7 +266,7 @@ async def test_assistant_transport_sse_mode():
|
|
|
266
266
|
{
|
|
267
267
|
"commands": [
|
|
268
268
|
{
|
|
269
|
-
"type": "
|
|
269
|
+
"type": "demo/echo",
|
|
270
270
|
"message": {
|
|
271
271
|
"role": "user",
|
|
272
272
|
"parts": [{"type": "text", "text": "hi"}],
|
|
@@ -428,7 +428,7 @@ async def test_resume_after_completed_run_replays_with_completed_header():
|
|
|
428
428
|
{
|
|
429
429
|
"commands": [
|
|
430
430
|
{
|
|
431
|
-
"type": "
|
|
431
|
+
"type": "demo/echo",
|
|
432
432
|
"message": {
|
|
433
433
|
"role": "user",
|
|
434
434
|
"parts": [{"type": "text", "text": "hi"}],
|
|
@@ -453,7 +453,7 @@ async def test_resume_rejects_commands_and_malformed_bodies():
|
|
|
453
453
|
async with statewire_client(Harness) as (app, client):
|
|
454
454
|
response = await client.post(
|
|
455
455
|
"/threads/t1/assistant-transport/api/resume",
|
|
456
|
-
json={"commands": [{"type": "
|
|
456
|
+
json={"commands": [{"type": "demo/echo"}]},
|
|
457
457
|
)
|
|
458
458
|
assert response.status_code == 400
|
|
459
459
|
assert (
|
|
@@ -840,7 +840,8 @@ async def test_add_message_is_normalized_to_run_steer():
|
|
|
840
840
|
assert response.status_code == 200
|
|
841
841
|
instance = (await app.state.pinned_host.directory.get("t1")).instance
|
|
842
842
|
[(params, caller_id)] = instance.steered
|
|
843
|
-
assert set(params) == {"message"}
|
|
843
|
+
assert set(params) == {"message", "anchorMessageId"}
|
|
844
|
+
assert params["anchorMessageId"] is None
|
|
844
845
|
message = params["message"]
|
|
845
846
|
assert message["id"].startswith("legacy_")
|
|
846
847
|
assert message["role"] == "user"
|
|
@@ -1094,7 +1095,9 @@ async def test_parent_id_without_commands_is_normalized_to_run_reload():
|
|
|
1094
1095
|
frames = data_stream_frames(response.text)
|
|
1095
1096
|
assert all(prefix == "aui-state" for prefix, _ in frames)
|
|
1096
1097
|
instance = (await app.state.pinned_host.directory.get("t1")).instance
|
|
1097
|
-
|
|
1098
|
+
[params] = instance.reloaded
|
|
1099
|
+
assert params["sourceId"] == "m2"
|
|
1100
|
+
assert params["messageId"].startswith("legacy_")
|
|
1098
1101
|
|
|
1099
1102
|
|
|
1100
1103
|
async def test_reload_without_a_resolvable_child_is_an_error_frame():
|
|
@@ -1184,7 +1187,8 @@ async def test_add_message_with_a_null_source_id_and_no_parent_is_run_steer():
|
|
|
1184
1187
|
instance = (await app.state.pinned_host.directory.get("t1")).instance
|
|
1185
1188
|
assert instance.edited == []
|
|
1186
1189
|
[(params, _)] = instance.steered
|
|
1187
|
-
assert set(params) == {"message"}
|
|
1190
|
+
assert set(params) == {"message", "anchorMessageId"}
|
|
1191
|
+
assert params["anchorMessageId"] is None
|
|
1188
1192
|
|
|
1189
1193
|
|
|
1190
1194
|
async def test_add_message_with_an_unresolvable_parent_falls_back_to_run_steer():
|
|
@@ -1209,4 +1213,5 @@ async def test_add_message_with_an_unresolvable_parent_falls_back_to_run_steer()
|
|
|
1209
1213
|
instance = (await app.state.pinned_host.directory.get("t1")).instance
|
|
1210
1214
|
assert instance.edited == []
|
|
1211
1215
|
[(params, _)] = instance.steered
|
|
1212
|
-
assert set(params) == {"message"}
|
|
1216
|
+
assert set(params) == {"message", "anchorMessageId"}
|
|
1217
|
+
assert params["anchorMessageId"] == "unknown"
|
|
@@ -124,11 +124,11 @@ async def test_client_posts_the_legacy_body_and_applies_state_ops():
|
|
|
124
124
|
body={"system": "be nice"},
|
|
125
125
|
on_chunk=seen.append,
|
|
126
126
|
)
|
|
127
|
-
assert await client.send([{"type": "
|
|
127
|
+
assert await client.send([{"type": "demo/echo", "message": "hi"}]) is True
|
|
128
128
|
assert client.state == {"text": "hello"}
|
|
129
129
|
assert received == [
|
|
130
130
|
{
|
|
131
|
-
"commands": [{"type": "
|
|
131
|
+
"commands": [{"type": "demo/echo", "message": "hi"}],
|
|
132
132
|
"state": {"text": ""},
|
|
133
133
|
"threadId": "t1",
|
|
134
134
|
"system": "be nice",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|