GCWing / GCWing/OpenBitFun

[Bug]: Incomplete assistant reply is persisted as `complete` when the model output stream ends prematurely

Offen
#1,980 0 Kommentare 0 Reaktionen 1 zugewiesene Person Beansprucht von @wsp1911 Auf GitHub ansehen
bug
Vorherrschende Sprache
Rust
Sterne
2.2k
Forks
229
Ø Merge
2 Std. 46 Min.
Gemergte PRs (30 T.)
577

Beschreibung

### Summary

An assistant reply stopped mid-generation (the text ends at the title of list item 4, with the markdown list left unclosed), but the session persistence layer recorded the turn as `finishReason=complete` / `status=completed` / `hasFinalResponse=true`. The UI shows no "reply may be incomplete" indication, so the user cannot distinguish an intentional stop from a transport/generation failure. This is a silent failure with the same observability gap as session freeze / review freeze issues.

**Expected behavior**: when the model output stream ends prematurely (provider-side interruption, timeout, connection drop, or max_tokens cut), the session pipeline should record a non-`complete` finish reason (`length` / `interrupted` / `error`), persist the turn as incomplete, and let the UI surface a hint (e.g. "reply may be incomplete") instead of silently marking it complete.

### Area

Agent runtime / core (session persistence and `finishReason` determination); AI provider / model adapter (premature stream termination).

### Reproduction or evidence

Hard to reproduce deterministically (it depends on the underlying model stream ending early), but this is a real occurrence with complete local evidence from the persisted turn data (no private data included):

1. **Phenomenon**: the assistant reply was cut mid-sentence — the text stops at `4. **压缩/总结(co`, right after the "能结构性解决" table and list items 1-3 of "解决不了的(方案边界)". Item 4 has a title only, no body; the markdown list is unclosed.

2. **Persisted evidence** (local `sessions//turns/turn-0004.json`):
- `textItems[0].content` is only **865 characters**, ending at `4. **压缩/总结(co`;
- the same modelRound's `thinkingItems` are **complete**, containing the full analysis for items 8, 9, 10 and the conclusion — proving the model finished reasoning and had a full answer ready, but the text stream terminated mid-generation;
- the turn records `finishReason=complete`, `status=completed`, `hasFinalResponse=true` — no truncation or error marker at all;
- `tokenUsage.outputTokens=1776` (including thinking); the text is only a fraction of that, so the output stream stopped far earlier than a normal completion point.

3. **Control group**: other turns in the same session with much longer replies (e.g. 4908 and 2787 characters) completed normally. This shows it is not "long replies always get cut"; it is an intermittent premature stream termination.

### Suggested fix

1. When the response stream ends early (missing stream-end frame, `finish_reason` = `length`/`interrupted`, or visibly unclosed text), mark the round as `interrupted`/`incomplete` instead of `complete`.
2. Keep `finishReason` semantics distinct: `complete` / `length` / `interrupted` / `error`, and retain the termination reason in the round state or `tokenUsage`.
3. Surface a "reply may be incomplete" hint in the UI and allow the user to regenerate or continue.
4. Optionally add a persistence-layer integrity check (e.g. warn when thinking is complete but the text is clearly truncated).

### Environment, if relevant

- BitFun v0.2.15 (2026-07-31 release), Windows desktop app, built-in personal-assistant session.
- Not tied to a specific model/provider; any provider stream that ends early during a long output can trigger this.

Beitragsleitfaden

Beitragsleitfaden öffnen

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.