Stream pi's thinking as an ephemeral live preview #19

Closed
opened 2026-07-05 07:26:38 +08:00 by weiwen · 1 comment
Owner

What to build

Pi already emits its reasoning ("thinking") over the RPC event stream, interleaved with the answer in each cumulative message_update. Evie currently parses thinking and throws it away, so the user stares at a bare typing indicator during long turns.

Stream thinking as an ephemeral preview in the live Telegram message: while the current assistant message has no answer text yet, show the latest thinking prefixed with 💭. The instant answer text begins arriving, the preview flips to the answer. The final delivered message is answer-only — the thinking is never persisted into chat history.

The preview must be bounded to a single Telegram message: keep the tail of the thinking (drop the head) so it can never paginate into multiple messages and leave orphaned stale messages behind when the shorter answer lands. The preview string is pre-formatted on the pi side and pushed through the existing string snapshot channel, so the Telegram rendering layer stays unaware of the thinking/answer distinction. The HTTP interface (which discards streamed snapshots) is unaffected.

Note: evie does not set pi's thinking level, so whether thinking events appear depends on pi's default for the configured model. Verify empirically that a preview actually renders; if pi defaults thinking off, that is a separate follow-up (out of scope here).

Acceptance criteria

  • During a turn where pi thinks before answering, the live Telegram message shows a 💭-prefixed thinking preview
  • Once answer text starts streaming, the message switches to the answer; the final message contains only the answer (no thinking)
  • A long thinking stream never produces more than one Telegram message and leaves no orphaned messages after the answer renders
  • Snapshot channel type is unchanged; Telegram rendering layer and HTTP path are untouched behaviorally
  • Tests cover: thinking-only update emits a 💭 preview, preview flips to plain answer text when text arrives, tail-bounding truncates over-long thinking
  • cargo build, cargo test, cargo clippy are clean

Blocked by

  • #18 (both modify the pi response-read loop; serialize to avoid conflicts)
## What to build Pi already emits its reasoning ("thinking") over the RPC event stream, interleaved with the answer in each cumulative `message_update`. Evie currently parses thinking and throws it away, so the user stares at a bare typing indicator during long turns. Stream thinking as an **ephemeral preview** in the live Telegram message: while the current assistant message has no answer text yet, show the latest thinking prefixed with `💭`. The instant answer text begins arriving, the preview flips to the answer. The final delivered message is answer-only — the thinking is never persisted into chat history. The preview must be bounded to a single Telegram message: keep the tail of the thinking (drop the head) so it can never paginate into multiple messages and leave orphaned stale messages behind when the shorter answer lands. The preview string is pre-formatted on the pi side and pushed through the existing string snapshot channel, so the Telegram rendering layer stays unaware of the thinking/answer distinction. The HTTP interface (which discards streamed snapshots) is unaffected. Note: evie does not set pi's thinking level, so whether thinking events appear depends on pi's default for the configured model. Verify empirically that a preview actually renders; if pi defaults thinking off, that is a separate follow-up (out of scope here). ## Acceptance criteria - [ ] During a turn where pi thinks before answering, the live Telegram message shows a `💭`-prefixed thinking preview - [ ] Once answer text starts streaming, the message switches to the answer; the final message contains only the answer (no thinking) - [ ] A long thinking stream never produces more than one Telegram message and leaves no orphaned messages after the answer renders - [ ] Snapshot channel type is unchanged; Telegram rendering layer and HTTP path are untouched behaviorally - [ ] Tests cover: thinking-only update emits a `💭` preview, preview flips to plain answer text when text arrives, tail-bounding truncates over-long thinking - [ ] `cargo build`, `cargo test`, `cargo clippy` are clean ## Blocked by - #18 (both modify the pi response-read loop; serialize to avoid conflicts)
Author
Owner

Implemented in commit fbef1fd on branch sandcastle/issue-19.

Changes:

  • Added assistant_thinking() to extract thinking blocks from message_update events (mirrors assistant_text())
  • Added thinking_preview() that tail-bounds raw thinking text to 3800 Unicode chars and prefixes with 💭
  • Updated read_agent_response to emit thinking preview when a message_update has thinking but no text; text takes priority so the preview flips the instant answer text arrives
  • 3 new tests: thinking-only emits preview, preview flips to answer, tail-bounding truncates over-long thinking

No Rust toolchain in sandbox — CI must validate. Logic is a straightforward extension of the existing snapshot pattern.

Implemented in commit fbef1fd on branch sandcastle/issue-19. Changes: - Added `assistant_thinking()` to extract thinking blocks from `message_update` events (mirrors `assistant_text()`) - Added `thinking_preview()` that tail-bounds raw thinking text to 3800 Unicode chars and prefixes with 💭 - Updated `read_agent_response` to emit thinking preview when a `message_update` has thinking but no text; text takes priority so the preview flips the instant answer text arrives - 3 new tests: thinking-only emits preview, preview flips to answer, tail-bounding truncates over-long thinking No Rust toolchain in sandbox — CI must validate. Logic is a straightforward extension of the existing snapshot pattern.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
weiwen/evie#19
No description provided.