soothe-client-python 1.0.9__tar.gz → 1.0.11__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.
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/PKG-INFO +1 -1
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/daemon_session.py +3 -8
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/command_client.py +53 -38
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/protocol_params.py +11 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/.gitignore +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/LICENSE +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/README.md +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/pyproject.toml +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/__init__.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/__init__.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/attachments.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/broadcaster.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/card_projection.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/chunk_filter.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/classifier.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/events.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/loop_session_store.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/managed_client.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/observability.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/pool.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/query_gate.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/thinking_step.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/turn.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/turn_boundary.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/turn_runner.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/errors.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/helpers.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/intent_hints.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/py.typed +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/schemas.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/session.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/stream_terminal.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/turn_boundary.py +0 -0
- {soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/websocket.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: soothe-client-python
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.11
|
|
4
4
|
Summary: WebSocket client + appkit for soothe-daemon (Python)
|
|
5
5
|
Project-URL: Homepage, https://github.com/mirasoth/soothe-client-python
|
|
6
6
|
Project-URL: Documentation, https://soothe.readthedocs.io
|
|
@@ -450,9 +450,7 @@ class DaemonSession:
|
|
|
450
450
|
# pre-admit early running can omit turn_id). Idle/stopped
|
|
451
451
|
# fall through to their gates (cancel idle may omit turn_id).
|
|
452
452
|
ev_turn_id = frame_turn_id(event)
|
|
453
|
-
status_state = (
|
|
454
|
-
str(event.get("state") or "") if event_type == "status" else ""
|
|
455
|
-
)
|
|
453
|
+
status_state = str(event.get("state") or "") if event_type == "status" else ""
|
|
456
454
|
is_running_status = status_state == "running"
|
|
457
455
|
is_terminal_status = status_state in {"idle", "stopped"}
|
|
458
456
|
if (
|
|
@@ -463,8 +461,7 @@ class DaemonSession:
|
|
|
463
461
|
and not turn_ids_match(expected_turn_id, ev_turn_id)
|
|
464
462
|
):
|
|
465
463
|
logger.debug(
|
|
466
|
-
"Ignoring unbound/mismatched turn_id frame type=%s "
|
|
467
|
-
"got=%s expected=%s",
|
|
464
|
+
"Ignoring unbound/mismatched turn_id frame type=%s got=%s expected=%s",
|
|
468
465
|
event_type,
|
|
469
466
|
(ev_turn_id or "<absent>")[-24:],
|
|
470
467
|
expected_turn_id[-24:],
|
|
@@ -518,9 +515,7 @@ class DaemonSession:
|
|
|
518
515
|
self._expected_turn_id = status_turn
|
|
519
516
|
elif query_started and state == "stopped":
|
|
520
517
|
stop_turn = frame_turn_id(event)
|
|
521
|
-
if expected_turn_id and not turn_ids_match(
|
|
522
|
-
expected_turn_id, stop_turn
|
|
523
|
-
):
|
|
518
|
+
if expected_turn_id and not turn_ids_match(expected_turn_id, stop_turn):
|
|
524
519
|
continue
|
|
525
520
|
self.last_turn_end_state = state
|
|
526
521
|
if ev_seq is not None:
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/command_client.py
RENAMED
|
@@ -132,34 +132,6 @@ async def _perform_handshake(ws: Any, *, timeout: float) -> None:
|
|
|
132
132
|
raise RuntimeError(f"Daemon state is {state}")
|
|
133
133
|
|
|
134
134
|
|
|
135
|
-
# Command message types
|
|
136
|
-
_AUTOPilot_COMMANDS = {
|
|
137
|
-
"status": "autopilot_status",
|
|
138
|
-
"submit": "autopilot_submit",
|
|
139
|
-
"list_goals": "autopilot_list_goals",
|
|
140
|
-
"get_goal": "autopilot_get_goal",
|
|
141
|
-
"cancel_goal": "autopilot_cancel_goal",
|
|
142
|
-
"cancel_all": "autopilot_cancel_all",
|
|
143
|
-
"wake": "autopilot_wake",
|
|
144
|
-
"dream": "autopilot_dream",
|
|
145
|
-
"resume": "autopilot_resume",
|
|
146
|
-
"list_jobs": "autopilot_list_jobs",
|
|
147
|
-
"get_job": "autopilot_get_job",
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
_CRON_COMMANDS = {
|
|
151
|
-
"add": "cron_add",
|
|
152
|
-
"list": "cron_list",
|
|
153
|
-
"list_jobs": "cron_list",
|
|
154
|
-
"show": "cron_show",
|
|
155
|
-
"cancel": "cron_cancel",
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
_MEMORY_COMMANDS = {
|
|
159
|
-
"stats": "memory_stats",
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
135
|
class AsyncCommandClient:
|
|
164
136
|
"""Async client for one-shot daemon RPCs (jobs, autopilot, cron).
|
|
165
137
|
|
|
@@ -186,7 +158,11 @@ class AsyncCommandClient:
|
|
|
186
158
|
self._timeout = timeout
|
|
187
159
|
|
|
188
160
|
async def _send_command(
|
|
189
|
-
self,
|
|
161
|
+
self,
|
|
162
|
+
command_type: str,
|
|
163
|
+
payload: dict[str, Any] | None = None,
|
|
164
|
+
*,
|
|
165
|
+
timeout: float | None = None,
|
|
190
166
|
) -> dict[str, Any]:
|
|
191
167
|
"""Send a protocol-1 request envelope and wait for the response.
|
|
192
168
|
|
|
@@ -199,6 +175,8 @@ class AsyncCommandClient:
|
|
|
199
175
|
Args:
|
|
200
176
|
command_type: RPC method name (e.g. ``"autopilot_status"``).
|
|
201
177
|
payload: Structured parameters object for the method.
|
|
178
|
+
timeout: Optional per-call override (seconds). Defaults to the
|
|
179
|
+
client constructor timeout.
|
|
202
180
|
|
|
203
181
|
Returns:
|
|
204
182
|
The ``result`` dict from the matching ``response`` envelope.
|
|
@@ -209,6 +187,7 @@ class AsyncCommandClient:
|
|
|
209
187
|
"""
|
|
210
188
|
import websockets
|
|
211
189
|
|
|
190
|
+
cmd_timeout = self._timeout if timeout is None else timeout
|
|
212
191
|
req_id = str(uuid4())
|
|
213
192
|
envelope = WireEnvelope(
|
|
214
193
|
type=MessageType.REQUEST.value,
|
|
@@ -218,15 +197,15 @@ class AsyncCommandClient:
|
|
|
218
197
|
)
|
|
219
198
|
|
|
220
199
|
try:
|
|
221
|
-
async with websockets.connect(self._ws_url, open_timeout=
|
|
222
|
-
await _perform_handshake(ws, timeout=
|
|
200
|
+
async with websockets.connect(self._ws_url, open_timeout=cmd_timeout) as ws:
|
|
201
|
+
await _perform_handshake(ws, timeout=cmd_timeout)
|
|
223
202
|
|
|
224
203
|
# Send the protocol-1 request envelope.
|
|
225
204
|
await ws.send(encode_envelope(envelope))
|
|
226
205
|
|
|
227
206
|
# Wait for the matching response/error envelope.
|
|
228
207
|
while True:
|
|
229
|
-
response_str = await asyncio.wait_for(ws.recv(), timeout=
|
|
208
|
+
response_str = await asyncio.wait_for(ws.recv(), timeout=cmd_timeout)
|
|
230
209
|
response = decode_envelope(response_str)
|
|
231
210
|
if not isinstance(response, dict):
|
|
232
211
|
raise RuntimeError(f"Unexpected response: {response_str!r}")
|
|
@@ -255,7 +234,7 @@ class AsyncCommandClient:
|
|
|
255
234
|
continue
|
|
256
235
|
|
|
257
236
|
except TimeoutError:
|
|
258
|
-
raise RuntimeError(f"Command timeout after {
|
|
237
|
+
raise RuntimeError(f"Command timeout after {cmd_timeout}s") from None
|
|
259
238
|
except websockets.exceptions.ConnectionClosedError as exc:
|
|
260
239
|
raise RuntimeError(f"WebSocket connection closed: {exc}") from exc
|
|
261
240
|
except RuntimeError:
|
|
@@ -270,13 +249,24 @@ class AsyncCommandClient:
|
|
|
270
249
|
return await self._send_command("autopilot_status")
|
|
271
250
|
|
|
272
251
|
async def autopilot_submit(
|
|
273
|
-
self,
|
|
252
|
+
self,
|
|
253
|
+
description: str,
|
|
254
|
+
*,
|
|
255
|
+
priority: int = 50,
|
|
256
|
+
workspace: str | None = None,
|
|
257
|
+
rail_id: str | None = None,
|
|
274
258
|
) -> dict[str, Any]:
|
|
275
|
-
"""Submit a new autopilot goal (returns ``goal_id``).
|
|
259
|
+
"""Submit a new autopilot goal (returns ``goal_id``).
|
|
260
|
+
|
|
261
|
+
Uses a 120s RPC timeout so placement analysis can finish before the
|
|
262
|
+
client gives up (intake may call the verifier LLM).
|
|
263
|
+
"""
|
|
276
264
|
payload: dict[str, Any] = {"description": description, "priority": priority}
|
|
277
265
|
if workspace:
|
|
278
266
|
payload["workspace"] = workspace
|
|
279
|
-
|
|
267
|
+
if rail_id:
|
|
268
|
+
payload["rail_id"] = rail_id
|
|
269
|
+
return await self._send_command("autopilot_submit", payload, timeout=120.0)
|
|
280
270
|
|
|
281
271
|
async def autopilot_list_goals(self) -> dict[str, Any]:
|
|
282
272
|
"""List all goals (including non-root children)."""
|
|
@@ -318,6 +308,14 @@ class AsyncCommandClient:
|
|
|
318
308
|
"""Get a root job with DAG snapshot. Prefer ``job_status`` / ``job_dag``."""
|
|
319
309
|
return await self._send_command("autopilot_get_job", {"job_id": job_id})
|
|
320
310
|
|
|
311
|
+
async def autopilot_top(self, *, include_terminal: bool = False) -> dict[str, Any]:
|
|
312
|
+
"""Jobs → goals → loops snapshot for CLI ``top`` (IG-679 / IG-688).
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
include_terminal: When true, include terminal goals and jobs.
|
|
316
|
+
"""
|
|
317
|
+
return await self._send_command("autopilot_top", {"include_terminal": include_terminal})
|
|
318
|
+
|
|
321
319
|
async def autopilot_subscribe(self) -> dict[str, Any]:
|
|
322
320
|
"""Subscribe this connection to autopilot worker events."""
|
|
323
321
|
return await self._send_command("autopilot_subscribe")
|
|
@@ -442,13 +440,23 @@ class CommandClient:
|
|
|
442
440
|
return cast(dict[str, Any], self._run_async(self._client.autopilot_status()))
|
|
443
441
|
|
|
444
442
|
def autopilot_submit(
|
|
445
|
-
self,
|
|
443
|
+
self,
|
|
444
|
+
description: str,
|
|
445
|
+
*,
|
|
446
|
+
priority: int = 50,
|
|
447
|
+
workspace: str | None = None,
|
|
448
|
+
rail_id: str | None = None,
|
|
446
449
|
) -> dict[str, Any]:
|
|
447
450
|
"""Submit a new autopilot goal."""
|
|
448
451
|
return cast(
|
|
449
452
|
dict[str, Any],
|
|
450
453
|
self._run_async(
|
|
451
|
-
self._client.autopilot_submit(
|
|
454
|
+
self._client.autopilot_submit(
|
|
455
|
+
description,
|
|
456
|
+
priority=priority,
|
|
457
|
+
workspace=workspace,
|
|
458
|
+
rail_id=rail_id,
|
|
459
|
+
)
|
|
452
460
|
),
|
|
453
461
|
)
|
|
454
462
|
|
|
@@ -488,6 +496,13 @@ class CommandClient:
|
|
|
488
496
|
"""Get a root job with DAG snapshot."""
|
|
489
497
|
return cast(dict[str, Any], self._run_async(self._client.autopilot_get_job(job_id)))
|
|
490
498
|
|
|
499
|
+
def autopilot_top(self, *, include_terminal: bool = False) -> dict[str, Any]:
|
|
500
|
+
"""Jobs → goals → loops snapshot for CLI ``top``."""
|
|
501
|
+
return cast(
|
|
502
|
+
dict[str, Any],
|
|
503
|
+
self._run_async(self._client.autopilot_top(include_terminal=include_terminal)),
|
|
504
|
+
)
|
|
505
|
+
|
|
491
506
|
def autopilot_subscribe(self) -> dict[str, Any]:
|
|
492
507
|
"""Subscribe to autopilot worker events."""
|
|
493
508
|
return cast(dict[str, Any], self._run_async(self._client.autopilot_subscribe()))
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/protocol_params.py
RENAMED
|
@@ -38,6 +38,7 @@ __all__ = [
|
|
|
38
38
|
"AutopilotResumeParams",
|
|
39
39
|
"AutopilotListJobsParams",
|
|
40
40
|
"AutopilotGetJobParams",
|
|
41
|
+
"AutopilotTopParams",
|
|
41
42
|
# Job RPC params
|
|
42
43
|
"JobCreateParams",
|
|
43
44
|
"JobStatusParams",
|
|
@@ -401,6 +402,16 @@ class AutopilotGetJobParams(ParamsBase):
|
|
|
401
402
|
job_id: str = Field(..., min_length=1)
|
|
402
403
|
|
|
403
404
|
|
|
405
|
+
class AutopilotTopParams(ParamsBase):
|
|
406
|
+
"""Params for ``method=autopilot_top`` (IG-679 / IG-688).
|
|
407
|
+
|
|
408
|
+
Attributes:
|
|
409
|
+
include_terminal: Include terminal goals/jobs when true (default false).
|
|
410
|
+
"""
|
|
411
|
+
|
|
412
|
+
include_terminal: bool = False
|
|
413
|
+
|
|
414
|
+
|
|
404
415
|
# ---------------------------------------------------------------------------
|
|
405
416
|
# Job RPC params
|
|
406
417
|
# ---------------------------------------------------------------------------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/__init__.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/attachments.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/broadcaster.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/chunk_filter.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/classifier.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/events.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/observability.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/query_gate.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/thinking_step.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/turn_boundary.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/appkit/turn_runner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/intent_hints.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/stream_terminal.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.9 → soothe_client_python-1.0.11}/src/soothe_client/turn_boundary.py
RENAMED
|
File without changes
|
|
File without changes
|