fix(chat): surface empty upstream model completions instead of silence

✅ merged · #379 · open-legal-products/mike ← duncanmcqueen/mike · opened 16d ago by duncanmcqueen · merged 14d ago by willchen96 · +42 across 2 files · ↗ on GitHub

From the PR description

Summary

When an upstream provider ends a chat stream cleanly but produces zero content (observed intermittently via OpenRouter), the client currently renders nothing - the composer just sits there looking hung. This PR emits an explicit, safe-to-display error event instead:

The model returned an empty response. Try again, or pick a different model.

What changed

  • routes/chat.ts: after runLLMStream resolves, if fullText is empty/whitespace and no events were produced, emit the retry-guidance error event followed by [DONE].
  • Turns that produced tool artifacts (documents, ask-inputs flows, etc.) keep their existing completion path - the guard only fires when nothing visible was generated.

Why

An empty 200-complete stream is indistinguishable from a hang to the user, and the underlying trigger is provider-side (retrying or switching models works). Surfacing it turns a dead-end into actionable guidance.

Testing performed

  • New integration test in chat.routes.test.ts: empty runLLMStream result → SSE contains the error event with safe_to_display: true and terminates with [DONE].
  • Full backend suite on Node 22: 726 passed / 25 skipped.

Our analysis

Surface empty chat-stream failures — read the full analysis →

Think the analysis missed something the PR description covers?

Capture this PR into my fork

Download a Markdown prompt that tells Claude how to port every commit in this PR into your working tree. Run it via claude -p < capture-pull-379.md from inside the repo you want the changes in.

⬇ Download capture-pull-379.md