Skip to content

fix(chat): emit one earned terminal per turn - #903

Merged
WaylandYang merged 2 commits into
deeplethe:devfrom
Floating-Y:fix/issue-857-earned-terminal
Sep 24, 2026
Merged

WaylandYang merged 2 commits into
deeplethe:devfrom
Floating-Y:fix/issue-857-earned-terminal

Conversation

@Floating-Y

Copy link
Copy Markdown
Contributor

Addresses #857

This PR follows the direction proposed in #857: it uses eventsource-parser for client SSE framing and consolidates server terminal emission at the stream boundary.

The intent is to make done depend on a saved assistant message and to return one terminal event for each stream. I'd appreciate feedback on whether this matches the intended terminal contract, especially for fallback and reconnect paths.

Changes

  • Use eventsource-parser for client SSE framing.
  • Emit a terminal event when the assistant message is saved or a failure is known, with server-side handling to prevent duplicate terminals.
  • Reject empty fallback answers and report unexpected stream closure.
  • Add regression coverage for terminal events and SSE framing.

Validation

  • cargo fmt --all --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo build --workspace
  • cargo test -p utopia-server api::chat -- --test-threads=1 — 48 passed
  • cargo test --workspace --quiet -- --skip gbk_euc_h_pdf_extracts_chinese_text — passed
  • pnpm -C web test — 135 passed
  • pnpm -C web build

The skipped PDF test requires Poppler, which is unavailable on this Windows host; CI installs it.

@Floating-Y
Floating-Y force-pushed the fix/issue-857-earned-terminal branch from c789589 to c7cca9e Compare September 24, 2026 12:11

@WaylandYang WaylandYang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ProducerEvent split is the right shape for #857's step 3: every producer exit path I traced (size limit, model error, cancelled-then-answer, fallback, empty answer, save failure, success) now yields an outcome and the spawn loop emits exactly one terminal, with Closed-without-terminal correctly surfaced as an error given how live::emit freezes the snapshot. Two changes before merge:

  1. Drop commit b8fec3d2 (vector index drop/build lock in crates/utopia-store/src/vector_index.rs and its test) into its own PR. It is unrelated to #857 and undocumented here; it looks reasonable on its own and deserves its own description.
  2. The terminal table in chat_terminal_tests.rs lost its pending list and the injection-point section without gaining rows. The targeted tests do exist on dev (chat_persistence_tests.rs, chat_fallback_tests.rs, chat_registry_tests.rs), so either add the rows or name those tests in the comment so the table stays the place reviewers look.

Optional: make Progress a type that cannot hold a terminal event (today a done frame sent as progress is only caught at runtime), and put the saved message id on done so a client can verify the "earned" claim. The parser swap and its CR/CRLF tests look good.

Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>
Signed-off-by: Wayland Yang <wayland0916@gmail.com>
@WaylandYang

Copy link
Copy Markdown
Contributor

Applied the two review points as maintainer edits so this can land; your commit and sign-off are kept, mine is added.

  1. b8fec3d (vector index drop takes the build lock) is now its own PR, Take the build lock when dropping a vector index so a drop cannot deadlock a concurrent build #908, carried there as your commit with your sign-off. This branch is the chat change alone, rebased onto current dev.
  2. The terminal table's header again says where the paths that need fixture-level injection live: chat_persistence_tests.rs, chat_fallback_tests.rs and chat_registry_tests.rs, so the table stays the place a reviewer looks first.

Locally on a fresh database: the 48 chat tests pass, clippy is clean, and the web suite (135 tests) and build pass with the pinned eventsource-parser. Merging once CI agrees. Thanks for the ProducerEvent split; it is the right shape for #857.

@WaylandYang
WaylandYang force-pushed the fix/issue-857-earned-terminal branch from b8fec3d to 4dab0d6 Compare September 24, 2026 15:24

@WaylandYang WaylandYang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both review points applied: the vector-index commit is #908 on its own, and the terminal table names the targeted test files; 48 chat tests, clippy, and the web suite pass locally; CI green. Approving to clear the earlier request-changes.

@WaylandYang
WaylandYang merged commit f4ea6a8 into deeplethe:dev Sep 24, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants