Skip to content

fix(notes): stream LLM responses and time out only on a stall - #717

Merged
goosewobbler merged 3 commits into
mainfrom
fix/notes-llm-timeouts
Oct 23, 2026
Merged

goosewobbler merged 3 commits into
mainfrom
fix/notes-llm-timeouts

Conversation

@goosewobbler

@goosewobbler goosewobbler commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Description

  • Every LLM provider now streams its response, and llm.options.timeout is an idle timeout: a request fails only when nothing arrives for that long, so a long response that keeps streaming is never cut off. Before, the timeout capped the whole response, so a large chunk with PR context hit it on all three retries (about 6.5 minutes on chore: release v0.42.0 #614's run) and came back ungrouped.
  • Ollama parses the NDJSON stream. An error object mid-stream, or a stream that ends without done, fails retryably.
  • OpenAI, openai-compatible and Anthropic stream through their SDKs, and fail retryably when the stream ends without a finish or stop reason.
  • One place for it: BaseLLMProvider.withIdleTimeout runs each request under the idle timer, turns an abort into the timeout error (whatever the SDK then threw), and wraps other errors as <provider> error: …. The OpenAI, Anthropic and openai-compatible prefixes change from … API error: to that form.
  • Retries are unchanged: a timeout now means a stall, and a stall is what retries are for.
  • Live test: entries now carry PR bodies at the 2 KB cap (the first chunk is about 66 KB of entries), and calls run at the production timeout. A stall fails the live test instead of being skipped as an outage.
  • The timeout description in the schema and docs is updated, and the troubleshooting entry is rewritten.

Related Issue(s)

None; seen in #614's notes run.

Type of Change

  • feat — New feature
  • fix — Bug fix
  • docs — Documentation change
  • refactor — Code refactoring
  • test — Adding or updating tests
  • chore — Maintenance, dependencies, tooling
  • Breaking change — Requires major version bump

Scope

  • @releasekit/version
  • @releasekit/notes
  • @releasekit/publish
  • @releasekit/release
  • @releasekit/config
  • @releasekit/core
  • CI / workflows
  • Root / monorepo

Checklist

  • Lint passes (pnpm lint)
  • Tests pass (pnpm test)
  • Typecheck passes (pnpm typecheck)
  • Documentation updated (if applicable)
  • Breaking changes noted in description

Testing Notes

  • base.spec.ts tests the timer once: a request that keeps resetting never times out; a quiet one aborts as a retryable timeout with the abort as its cause; an abort wins over whatever the request then throws; other errors are wrapped with their cause; the timer is cleared; and bad timeouts (0, sub-millisecond, NaN, past the timer ceiling) are clamped. Making reset a no-op, dropping the clamp, or letting an LLMError win over the abort each fails it.
  • Each provider tests that a slow but steady stream doesn't time out (its wiring of reset) and its own missing finish marker. Ollama also covers NDJSON split across reads, including multi-byte characters, and an error object mid-stream.
  • The live test needs OLLAMA_API_KEY, so it runs in CI's Live Tests job on this PR. It passed on the earlier commits.
  • The notes (486) and config unit suites, typecheck, schema:check and docs:check pass.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no new actionable issues remain.

Summary

All four LLM providers now stream responses and use an idle timeout instead of a total request deadline.

  • LLM providers stream complete answers and time out only when they stall.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Start provider request] --> B[Start idle timer]
  B --> C{Response activity}
  C -->|Data arrives| D[Reset idle timer]
  D --> C
  C -->|Response completes| E[Return result and clear timer]
  C -->|Timer expires| F[Abort request]
  F --> G[Return retryable timeout and clear timer]
Loading

Reviews (3) · Last reviewed commit: "refactor(notes): share the idle timeout ..." · Reviewed by Greptile

The notes timeout capped the whole request, so a large chunk whose
structured response took over 60s to generate timed out on every retry
and came back ungrouped. Every provider now streams its response and
aborts only when nothing arrives for the timeout.

- ollama: stream NDJSON; an error object mid-stream, or a stream that
  ends without done, fails retryably
- openai, openai-compatible, anthropic: stream through the SDKs; a stream
  that ends without a finish/stop reason fails retryably
- the timeout error chains its abort, so the live test fails on a stall
  instead of skipping it as an outage
- live test: entries carry PR bodies at the 2 KB cap, at production timeouts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Standing release PR: #614 · v0.42.0 queued (4 packages) · open 82d 22h 42m · ✅ ready to merge

Release Preview — v0.41.2 → v0.41.3

Note: Labels on this PR are advisory in standing-pr mode. Bumps come from conventional commits in the standing PR; override by editing labels on the standing PR itself. Add release:immediate to bypass the standing PR and release this PR directly.

These changes will be added to the release PR (#614) when merged:

Changelog

monorepo 0.41.2 → 0.41.3

Changed

  • share the idle timeout and error handling in the base provider (notes)

Fixed

  • give a cut-off Ollama stream a cause (notes)
  • stream LLM responses and time out only on a stall (notes)

All packages → 0.41.3 (sync versioning)

  • @releasekit/version
  • @releasekit/notes
  • @releasekit/publish
  • @releasekit/release

After merge — predicted release

No version escalation — this PR's changes will be included in the queued release without affecting the projected versions.

Package Standing PR This PR After merge
monorepo 0.42.0 0.41.3 0.42.0

Updated automatically by ReleaseKit

Comment thread packages/notes/src/llm/ollama.ts Resolved
github-actions Bot and others added 2 commits October 23, 2026 17:31
A retryable error with no cause reads as an outage the provider classified,
so the live test skipped a stream that closed without `done` instead of
failing. The cause names the last line received, which also helps debug a
real cut-off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e provider

Each provider repeated the same timer setup, abort-to-timeout mapping and
error wrapping. `withIdleTimeout` now does it once and is tested once, in
base.spec; each provider keeps a test that a steady stream doesn't time out
and one for its missing finish marker. The live test's entries keep their
2 KB PR bodies from a shorter template.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@goosewobbler
goosewobbler merged commit 72927b4 into main Oct 23, 2026
36 checks passed
@goosewobbler
goosewobbler deleted the fix/notes-llm-timeouts branch October 23, 2026 16:57
@github-actions github-actions Bot mentioned this pull request Oct 23, 2026
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.

1 participant