fix(cli): wire AffectUpdate + AwaitingLlmFirstToken into --send presenter (v0.14.1)
The CLI presenter at cli.py:201 carries its own isinstance check on the Event union (mirroring the TUI presenter's same pattern). v0.11.0 + v0.14.0 added AffectUpdate + AwaitingLlmFirstToken to the wire layer but only updated the TUI presenter, leaving the CLI presenter stuck on the pre-v0.11.0 event vocabulary. Effect: `ratatoskr --send` crashes with AssertionError on any v0.28.0+ server emitting either of those events. Persona-enabled agents (affect_update fires on every qualifying turn) and slow-first-token turns (awaiting_llm_first_token heartbeats fire at 5s intervals) are both reliably broken. Surfaced while running a wire-trace smoke test against a Gemma4-based Tier 3 agent. Patch-bump per SemVer discipline: corrects drift on the just-shipped surface (v0.11.0 / v0.14.0 wire layer); no public signature change, no new behavior, existing callers don't care — the bug fix lets them keep working against current servers. Routing additions in cli.py: - AffectUpdate: stderr line with status + (for current) dominant_emotion - AwaitingLlmFirstToken: stderr line with turn_id + elapsed (seconds)
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "ratatoskr"
|
name = "ratatoskr"
|
||||||
version = "0.14.0"
|
version = "0.14.1"
|
||||||
description = "Worldtree Conversation API debug TUI — multi-pane observability dashboard"
|
description = "Worldtree Conversation API debug TUI — multi-pane observability dashboard"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import httpx
|
|||||||
|
|
||||||
from ratatoskr.sessions import AgentNotFound, SessionApiFailed, create_session
|
from ratatoskr.sessions import AgentNotFound, SessionApiFailed, create_session
|
||||||
from ratatoskr.sse_client import (
|
from ratatoskr.sse_client import (
|
||||||
|
AffectUpdate,
|
||||||
|
AwaitingLlmFirstToken,
|
||||||
CancelAlreadyCompleted,
|
CancelAlreadyCompleted,
|
||||||
CancelFailed,
|
CancelFailed,
|
||||||
Cancelled,
|
Cancelled,
|
||||||
@@ -205,6 +207,7 @@ class CliPresenterState:
|
|||||||
(
|
(
|
||||||
WorkerPhase, Thinking, Text, TextBoundary,
|
WorkerPhase, Thinking, Text, TextBoundary,
|
||||||
ToolStart, ToolResult, Done, Error, Cancelled,
|
ToolStart, ToolResult, Done, Error, Cancelled,
|
||||||
|
AffectUpdate, AwaitingLlmFirstToken,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
# Thinking events accumulate into the open run.
|
# Thinking events accumulate into the open run.
|
||||||
@@ -275,6 +278,28 @@ class CliPresenterState:
|
|||||||
f". text_boundary: kind={event.kind} char_offset={event.char_offset}\n"
|
f". text_boundary: kind={event.kind} char_offset={event.char_offset}\n"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
if isinstance(event, AffectUpdate):
|
||||||
|
# Worldtree #204 / v0.28.0. CLI surface is debug telemetry —
|
||||||
|
# one line to stderr with status + (for current) dominant_emotion.
|
||||||
|
if event.snapshot is not None:
|
||||||
|
dom = event.snapshot.get("dominant_emotion")
|
||||||
|
stderr.write(
|
||||||
|
f". affect_update: status={event.status} turn_id={event.turn_id} "
|
||||||
|
f"dominant_emotion={dom!r}\n"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
stderr.write(
|
||||||
|
f". affect_update: status={event.status} turn_id={event.turn_id}\n"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if isinstance(event, AwaitingLlmFirstToken):
|
||||||
|
# Worldtree #201 / v0.29.0. Heartbeat during BuildingPrompt →
|
||||||
|
# CallingLLM gap. Stderr surface, one line per heartbeat.
|
||||||
|
secs = event.elapsed_ms_since_building_prompt / 1000.0
|
||||||
|
stderr.write(
|
||||||
|
f". awaiting_llm_first_token: turn_id={event.turn_id} elapsed={secs:.1f}s\n"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
async def _cancel_and_log(
|
async def _cancel_and_log(
|
||||||
|
|||||||
Reference in New Issue
Block a user