Skip to content

feat(ai-llm-proxy): make upstream LLM request timeout configurable - #530

Merged
LukasHirt merged 1 commit into
mainfrom
feat/ai-llm-proxy-configurable-timeout
Jul 27, 2026
Merged

feat(ai-llm-proxy): make upstream LLM request timeout configurable#530
LukasHirt merged 1 commit into
mainfrom
feat/ai-llm-proxy-configurable-timeout

Conversation

@LukasHirt

Copy link
Copy Markdown
Collaborator

Summary

  • The ai-llm-proxy had a hardcoded 60s timeout on the upstream LLM request, which is too short for some slower LLMs/models.
  • Added LLM_TIMEOUT_MS env var (default 60000) so operators can tune it per deployment, following the existing config pattern (LLM_MAX_TOKENS, MAX_BODY_BYTES, RATE_LIMIT_RPM).
  • Documented the new variable in packages/web-app-chat-with-file/README.md.

Test plan

  • pnpm --filter ai-llm-proxy test:unit — 25/25 passing
  • Verified default behavior unchanged (defaults to 60000ms) when LLM_TIMEOUT_MS is unset

The 60s timeout on the upstream LLM request was hardcoded, which is too
short for some slower LLMs/models. Add LLM_TIMEOUT_MS (default 60000)
so operators can tune it per deployment.

Signed-off-by: Lukas Hirt <info@hirt.cz>
@LukasHirt
LukasHirt requested a review from a team as a code owner July 27, 2026 10:20
@kw-security

kw-security commented Jul 27, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@LukasHirt
LukasHirt requested a review from dj4oC July 27, 2026 10:27
@LukasHirt
LukasHirt merged commit bd2e009 into main Jul 27, 2026
34 checks passed
@LukasHirt
LukasHirt deleted the feat/ai-llm-proxy-configurable-timeout branch July 27, 2026 12:32
LukasHirt added a commit that referenced this pull request Jul 27, 2026
…ceiling

The proxy's upstream LLM timeout will become configurable via LLM_TIMEOUT_MS
in a separate PR (#530), with a default of 60s but no fixed upper bound. A
hardcoded 90s client-side timeout could again fire before a larger
admin-configured proxy timeout, recreating the mid-flight cancellation issue
this branch fixes. Raise every client composable's AbortSignal.timeout from
90_000 to 300_000 (5 minutes) so it acts purely as a generous outer bound
against a hung network or dead proxy process, rather than an attempt to
closely track the proxy's own (separately configurable) timeout.

Signed-off-by: Lukas Hirt <info@hirt.cz>
LukasHirt added a commit that referenced this pull request Jul 27, 2026
…ceiling

The proxy's upstream LLM timeout will become configurable via LLM_TIMEOUT_MS
in a separate PR (#530), with a default of 60s but no fixed upper bound. A
hardcoded 90s client-side timeout could again fire before a larger
admin-configured proxy timeout, recreating the mid-flight cancellation issue
this branch fixes. Raise every client composable's AbortSignal.timeout from
90_000 to 300_000 (5 minutes) so it acts purely as a generous outer bound
against a hung network or dead proxy process, rather than an attempt to
closely track the proxy's own (separately configurable) timeout.

Signed-off-by: Lukas Hirt <info@hirt.cz>
LukasHirt added a commit that referenced this pull request Jul 29, 2026
…ng LLM cost (#535)

* fix(ai-llm-proxy): abort upstream fetch when client disconnects

The proxy never listened for the client connection closing, so an
AbortSignal.timeout firing client-side, a closed tab, or a dropped
connection left the outbound LLM fetch running for up to its own
60s timeout — burning LLM cost for a response nobody could receive,
then attempting to write to an already-dead response.

Wire an AbortController into handleRequest that aborts on req's
'close' event and is combined with the existing upstream timeout via
AbortSignal.any(), so either condition stops the outbound fetch.
Guard sendJson and the final response write against a disconnected
client, and attach a no-op res error listener so a late write-after-
close can never crash the process with an unhandled 'error' event.

Add unit tests proving the outbound fetch signal aborts on client
disconnect and that no write/crash happens afterwards.

Signed-off-by: Lukas Hirt <info@hirt.cz>

* fix(web-app-ai-*): align client LLM request timeouts above proxy timeout

Every ai-llm-proxy caller set its own AbortSignal.timeout with wildly
inconsistent values (30s or 60s), while the proxy's own upstream fetch
allows up to 60s plus unaccounted latency for OIDC discovery/userinfo
validation. Callers using the 30s value could abort mid-flight while
the proxy was still legitimately working, cancelling a request that
would otherwise have succeeded.

Raise and unify every client-side timeout that calls the ai-llm-proxy
to 90s — comfortably above the proxy's 60s upstream timeout plus
margin — across web-app-ai-data-insights-sidebar, web-app-ai-doc-summary,
web-app-ai-folder-brief-sidebar, web-app-ai-folder-readme-generator,
web-app-ai-image-alt-text-sidebar (main call only; the separate 10s
vision-capability probe is intentionally untouched), web-app-ai-multi-
doc-synthesizer, web-app-ai-quick-draft-creator, web-app-ai-sensitive-
data-scanner, web-app-ai-smart-collections-nav, web-app-ai-smart-file-
tagger-qa, web-app-chat-with-file, and web-app-version-changelog.
(web-app-ai-smart-collections-nav's useRecentFiles.ts REQUEST_TIMEOUT_MS
is a WebDAV REPORT/getFileContents timeout, not an LLM proxy call, and
is left untouched.)

Also add TimeoutError-specific error handling to the two composables
that were missing it entirely: web-app-ai-sensitive-data-scanner's
useLlm/useScanner and web-app-ai-quick-draft-creator's useLLM (which
required wrapping the fetch call itself in try/catch, since a timeout
abort rejects before there's a Response to check .ok on).

Signed-off-by: Lukas Hirt <info@hirt.cz>

* fix(web-app-ai-*): raise client LLM timeout to a generous safety-net ceiling

The proxy's upstream LLM timeout will become configurable via LLM_TIMEOUT_MS
in a separate PR (#530), with a default of 60s but no fixed upper bound. A
hardcoded 90s client-side timeout could again fire before a larger
admin-configured proxy timeout, recreating the mid-flight cancellation issue
this branch fixes. Raise every client composable's AbortSignal.timeout from
90_000 to 300_000 (5 minutes) so it acts purely as a generous outer bound
against a hung network or dead proxy process, rather than an attempt to
closely track the proxy's own (separately configurable) timeout.

Signed-off-by: Lukas Hirt <info@hirt.cz>

---------

Signed-off-by: Lukas Hirt <info@hirt.cz>
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.

3 participants