soothe-client-python 0.9.8__py3-none-any.whl → 0.9.9__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.
@@ -23,7 +23,7 @@ from soothe_client.stream_terminal import (
23
23
  is_turn_end_custom_data,
24
24
  is_turn_progress_chunk,
25
25
  )
26
- from soothe_client.turn_boundary import frame_seq, frame_turn_id
26
+ from soothe_client.turn_boundary import frame_seq, frame_turn_id, parse_turn_generation
27
27
  from soothe_client.websocket import WebSocketClient
28
28
 
29
29
  logger = logging.getLogger(__name__)
@@ -392,13 +392,19 @@ class DaemonSession:
392
392
  if ev_seq is not None and last_end_seq > 0 and ev_seq <= last_end_seq:
393
393
  continue
394
394
 
395
- # IG-659 phases 1–2: drop mismatched turn_id once bound.
395
+ # Drop mismatched turn_id once bound — except status=running,
396
+ # which may rebind to a newer generation (pre-admit early
397
+ # running can briefly carry a stale or empty turn_id).
396
398
  ev_turn_id = frame_turn_id(event)
399
+ is_running_status = (
400
+ event_type == "status" and str(event.get("state") or "") == "running"
401
+ )
397
402
  if (
398
403
  expected_turn_id
399
404
  and ev_turn_id
400
405
  and ev_turn_id != expected_turn_id
401
406
  and event_type in {"event", "status"}
407
+ and not is_running_status
402
408
  ):
403
409
  logger.debug(
404
410
  "Ignoring mismatched turn_id frame type=%s got=%s expected=%s",
@@ -426,8 +432,18 @@ class DaemonSession:
426
432
  progress_seen = True
427
433
  status_turn = frame_turn_id(event)
428
434
  if status_turn:
429
- expected_turn_id = status_turn
430
- self._expected_turn_id = status_turn
435
+ new_gen = parse_turn_generation(status_turn)
436
+ old_gen = parse_turn_generation(expected_turn_id)
437
+ if expected_turn_id is None or (
438
+ new_gen is not None and (old_gen is None or new_gen >= old_gen)
439
+ ):
440
+ if expected_turn_id and status_turn != expected_turn_id:
441
+ # Successor turn admitted while this reader
442
+ # still held a stale/early binding.
443
+ stream_payload_seen = False
444
+ turn_progress_seen = False
445
+ expected_turn_id = status_turn
446
+ self._expected_turn_id = status_turn
431
447
  elif query_started and state == "stopped":
432
448
  self.last_turn_end_state = state
433
449
  if ev_seq is not None:
@@ -18,6 +18,19 @@ def format_turn_id(loop_id: str, generation: int) -> str:
18
18
  return f"{lid}:{gen}"
19
19
 
20
20
 
21
+ def parse_turn_generation(turn_id: str | None) -> int | None:
22
+ """Extract generation int from ``turn_id``, or None if malformed."""
23
+ raw = str(turn_id or "").strip()
24
+ if not raw or ":" not in raw:
25
+ return None
26
+ suffix = raw.rsplit(":", 1)[-1]
27
+ try:
28
+ gen = int(suffix)
29
+ except ValueError:
30
+ return None
31
+ return gen if gen > 0 else None
32
+
33
+
21
34
  def frame_turn_id(frame: dict[str, Any] | None) -> str | None:
22
35
  """Return ``turn_id`` from a status/event frame or nested custom data."""
23
36
  if not isinstance(frame, dict):
@@ -51,4 +64,5 @@ __all__ = [
51
64
  "format_turn_id",
52
65
  "frame_seq",
53
66
  "frame_turn_id",
67
+ "parse_turn_generation",
54
68
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soothe-client-python
3
- Version: 0.9.8
3
+ Version: 0.9.9
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
@@ -7,7 +7,7 @@ soothe_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  soothe_client/schemas.py,sha256=FDhx2qwKOrJMqxjwFSDVi3SnYihFmZiNWD97ZktLmLc,1689
8
8
  soothe_client/session.py,sha256=Z-j_8sgcWaH5lJbPUDRdmMCgR_E81sCm0u5UFhLAEI8,7887
9
9
  soothe_client/stream_terminal.py,sha256=CD7CMNXHx8klFXmrT0kZWBwtJOTdsjhhF-2PaaHP0M8,3515
10
- soothe_client/turn_boundary.py,sha256=lsCgPh7CbRIgorignVUVS-1DfxQiLMfP7PcuuDEyRUs,1530
10
+ soothe_client/turn_boundary.py,sha256=FLBL5Pz47lVhVlPuqjvO4YjUtq2gIccXIsqsV2N-s-4,1937
11
11
  soothe_client/websocket.py,sha256=UmHRYNmx8brDYr983fO1tKQIFWHKfF7Wc6DbQRlYBTA,64716
12
12
  soothe_client/ws_command_client.py,sha256=Clp9YT6WDa5COTfus8FmxGksozmJbiXFctN6c36z-RI,23605
13
13
  soothe_client/appkit/__init__.py,sha256=lloR8K0FARCaBE1n9Rr-B28qUMS0Fd180OUNqiPDnCE,3651
@@ -15,7 +15,7 @@ soothe_client/appkit/attachments.py,sha256=hD2FsXeR8TqO3hzIYT49DUedsv9r2yFEuIYDB
15
15
  soothe_client/appkit/broadcaster.py,sha256=wcWQUOy7HjFgNaXDk1Ec71CGi7mZmb-zXZZbuOon9OY,3529
16
16
  soothe_client/appkit/chunk_filter.py,sha256=UoC4kbS_65UNEJIjbyufqxGBLwvamwvQ26BqZ_gkDTg,3134
17
17
  soothe_client/appkit/classifier.py,sha256=DH04CtdYyJV7Eoft1i7CR-vVvLBhH9n4Wl_CypFzi5M,18767
18
- soothe_client/appkit/daemon_session.py,sha256=OUDMo6z8ENojF85kKMstGp4EH-EawAAOD9aL1eVvrPs,30969
18
+ soothe_client/appkit/daemon_session.py,sha256=AQQLKF7fHkrbm1aBVUdcAALoboN3B0BQuBjGZnFjkC0,32113
19
19
  soothe_client/appkit/events.py,sha256=p7uZmSx_kYdiR7DQY84rvgyHE6RQ6Orbq2KT-WCNsiM,2290
20
20
  soothe_client/appkit/managed_client.py,sha256=QZhXA-zbx2v8IJpU_YFyUcwHRmNEziEGR6euPSvadfY,7183
21
21
  soothe_client/appkit/observability.py,sha256=9maW5oyEK0PZBjU7x8tHupoRBvAxApkHFQavRzkr7SM,650
@@ -25,7 +25,7 @@ soothe_client/appkit/session_store.py,sha256=uJNMZ33bq3F_qggdSD2ku4CN4aMxbMWywb5
25
25
  soothe_client/appkit/thinking_step.py,sha256=dB1L1ERPkisLh2y3KIgd4f6EmN8GmF5B0h6lae9tt18,4558
26
26
  soothe_client/appkit/turn.py,sha256=gtQyz3gGw8nkg8tfzpGZzNgRmJ48vJnj2Ll95W_BX4w,12479
27
27
  soothe_client/appkit/turn_runner.py,sha256=Svy-LAgy6p506zEHHnKKPay2rzxgLORoAawf6S66dKQ,20753
28
- soothe_client_python-0.9.8.dist-info/METADATA,sha256=x7OQyn9aZv7geK0PZ9WMovHx8WOvkEgoE8zdBfn-8kM,2857
29
- soothe_client_python-0.9.8.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
30
- soothe_client_python-0.9.8.dist-info/licenses/LICENSE,sha256=WbXjnHkSCPG_f4TfOdS_mGrbkQAYykAdfIqVDj9O-IY,1068
31
- soothe_client_python-0.9.8.dist-info/RECORD,,
28
+ soothe_client_python-0.9.9.dist-info/METADATA,sha256=lIhxg_Ah3XN-p3xj8XBgvBqb5Km13KcSabhPn5a8wBc,2857
29
+ soothe_client_python-0.9.9.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
30
+ soothe_client_python-0.9.9.dist-info/licenses/LICENSE,sha256=WbXjnHkSCPG_f4TfOdS_mGrbkQAYykAdfIqVDj9O-IY,1068
31
+ soothe_client_python-0.9.9.dist-info/RECORD,,