Skip to content

refactor(gateway): separate streaming and plain request timeouts - #1590

Merged
steebchen merged 1 commit into
mainfrom
ai-request-timeout-80s
Feb 4, 2026
Merged

steebchen merged 1 commit into
mainfrom
ai-request-timeout-80s

Conversation

@steebchen

@steebchen steebchen commented Feb 4, 2026 •

Copy link
Copy Markdown
Member

Summary

Add distinct timeout configurations for streaming and non-streaming requests with clearer naming. Non-streaming requests now default to 80 seconds (AI_TIMEOUT_MS) while streaming requests use 4 minutes (AI_STREAMING_TIMEOUT_MS).

Changes

  • Renamed environment variables to clarify their purpose
  • Added separate timeout functions for streaming vs non-streaming requests
  • Non-streaming requests use shorter timeout (80s default) for faster failure detection
  • Updated tests to work with new timeout configuration

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Configuration Updates
    • Separated AI request timeout handling into distinct streaming and non-streaming configurations for improved request management.

Add distinct timeout configurations for streaming and non-streaming requests with clearer naming. Non-streaming requests now default to 80 seconds (AI_TIMEOUT_MS) while streaming requests use 4 minutes (AI_STREAMING_TIMEOUT_MS).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 4, 2026 16:17
@steebchen
steebchen enabled auto-merge February 4, 2026 16:17
@coderabbitai

coderabbitai Bot commented Feb 4, 2026 •

Copy link
Copy Markdown
Contributor

Walkthrough

The PR introduces separate timeout configurations for streaming and non-streaming AI requests. It replaces the unified AI_REQUEST_TIMEOUT_MS with AI_STREAMING_TIMEOUT_MS for streaming requests and adds AI_TIMEOUT_MS for non-streaming requests (default 80000 ms), with corresponding updates to the timeout-config module to support both timeout types and their respective signal creation helpers.

Changes

Cohort / File(s) Summary
Configuration
.env.example
Replaces AI_REQUEST_TIMEOUT_MS with AI_STREAMING_TIMEOUT_MS for streaming requests and adds AI_TIMEOUT_MS (default 80000 ms) for non-streaming requests with explanatory comments.
Test Setup
apps/gateway/src/api.spec.ts
Updates test fixtures to handle both AI_TIMEOUT_MS and AI_STREAMING_TIMEOUT_MS environment variables, replacing the previous unified AI_REQUEST_TIMEOUT_MS configuration.
Chat Streaming Logic
apps/gateway/src/chat/chat.ts
Imports and uses createStreamingCombinedSignal for streaming requests while retaining createCombinedSignal for non-streaming requests; adds comment noting the shorter default timeout (80s) for non-streaming paths.
Timeout Configuration
apps/gateway/src/lib/timeout-config.ts
Renames getAIRequestTimeoutMs() to getStreamingTimeoutMs(), adds getTimeoutMs() for non-streaming timeouts, introduces createStreamingTimeoutSignal() and createStreamingCombinedSignal() helpers, updates createTimeoutSignal() to use non-streaming timeout, and refactors exports to use AI_STREAMING_TIMEOUT_MS and AI_TIMEOUT_MS.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Chat as Chat Handler
    participant TimeoutConfig as Timeout Config
    participant Upstream

    rect rgba(100, 150, 200, 0.5)
    Note over Chat,Upstream: Streaming Request Path
    Client->>Chat: Stream request
    Chat->>TimeoutConfig: createStreamingCombinedSignal()
    TimeoutConfig->>TimeoutConfig: getStreamingTimeoutMs() [AI_STREAMING_TIMEOUT_MS]
    TimeoutConfig->>Chat: AbortSignal (streaming timeout)
    Chat->>Upstream: fetch with streaming timeout signal
    Upstream-->>Chat: stream response
    end

    rect rgba(150, 100, 200, 0.5)
    Note over Chat,Upstream: Non-Streaming Request Path
    Client->>Chat: Regular request
    Chat->>TimeoutConfig: createCombinedSignal()
    TimeoutConfig->>TimeoutConfig: getTimeoutMs() [AI_TIMEOUT_MS, default 80s]
    TimeoutConfig->>Chat: AbortSignal (non-streaming timeout)
    Chat->>Upstream: fetch with non-streaming timeout signal
    Upstream-->>Chat: response
    end
Loading

Possibly Related PRs

  • fix: handle fetch timeouts #1125: Modifies apps/gateway/src/chat/chat.ts to add fetch-error handling alongside the main PR's streaming/non-streaming timeout signal separation for upstream request handling.
  • fix(gateway): use 504 for upstream timeouts #1578: Updates gateway upstream timeout error handling to distinguish timeout (504) from fetch errors (502), complementing the main PR's distinct streaming vs non-streaming timeout configuration.
  • feat: add configurable upstream AI request timeouts #1547: Directly refactors the timeout-config subsystem, replacing the earlier AI_REQUEST_TIMEOUT_MS implementation with separate AI_STREAMING_TIMEOUT_MS and AI_TIMEOUT_MS paths and introducing the new streaming timeout signal helpers.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: separating streaming and plain request timeouts across the gateway module, which is reflected throughout all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ai-request-timeout-80s

Important

Action Needed: IP Allowlist Update

If your organization protects your Git platform with IP whitelisting, please add the new CodeRabbit IP address to your allowlist:

  • ✨ 136.113.208.247/32 (new)
  • 34.170.211.100/32
  • 35.222.179.152/32

Reviews will stop working after February 8, 2026 if the new IP is not added to your allowlist.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This pull request refactors the timeout configuration to differentiate between streaming and non-streaming AI requests with more explicit naming conventions. Streaming requests continue to use a 4-minute default timeout, while non-streaming requests now use a shorter 80-second default timeout for faster failure detection.

Changes:

  • Renamed environment variables from AI_REQUEST_TIMEOUT_MS to separate AI_STREAMING_TIMEOUT_MS and AI_TIMEOUT_MS
  • Added separate timeout functions and signal creation functions for streaming vs non-streaming requests
  • Updated the gateway chat handler to use the appropriate timeout based on request type
  • Updated tests to handle both timeout environment variables

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
apps/gateway/src/lib/timeout-config.ts Added separate timeout functions for streaming (getStreamingTimeoutMs()) and non-streaming (getTimeoutMs()) requests, along with corresponding signal creation functions
apps/gateway/src/chat/chat.ts Updated to import and use createStreamingCombinedSignal() for streaming requests and createCombinedSignal() for non-streaming requests
apps/gateway/src/api.spec.ts Updated test setup to save and restore both AI_TIMEOUT_MS and AI_STREAMING_TIMEOUT_MS environment variables
.env.example Updated documentation to describe both AI_STREAMING_TIMEOUT_MS and AI_TIMEOUT_MS with clear explanations of their purposes

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

* Creates an AbortSignal that will abort after the plain (non-streaming) request timeout.
* Can be combined with other signals (e.g., client cancellation) using AbortSignal.any().
*/
export function createTimeoutSignal(): AbortSignal {

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

The function name createTimeoutSignal() is too generic and doesn't clearly indicate it's for non-streaming requests. This creates ambiguity with createStreamingTimeoutSignal(). Consider renaming to createNonStreamingTimeoutSignal() or createPlainTimeoutSignal() to maintain naming consistency and clarity throughout the codebase.

Copilot uses AI. Check for mistakes.
* Combines a plain (non-streaming) timeout signal with an optional cancellation signal.
* Uses the shorter timeout (default 80s) for non-streaming requests.
*/
export function createCombinedSignal(

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

The function name createCombinedSignal() is ambiguous since there's also createStreamingCombinedSignal(). The naming pattern is inconsistent - this function handles non-streaming requests but doesn't indicate that in its name. Consider renaming to createNonStreamingCombinedSignal() or createPlainCombinedSignal() to maintain clear distinction and naming consistency with the streaming variant.

Copilot uses AI. Check for mistakes.
export const GATEWAY_TIMEOUT_MS = getGatewayTimeoutMs();
export const AI_REQUEST_TIMEOUT_MS = getAIRequestTimeoutMs();
export const AI_STREAMING_TIMEOUT_MS = getStreamingTimeoutMs();
export const AI_TIMEOUT_MS = getTimeoutMs();

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

The export name AI_TIMEOUT_MS is ambiguous compared to AI_STREAMING_TIMEOUT_MS. It's not immediately clear that this constant is for non-streaming requests. While marked as legacy, maintaining clear naming even in backwards compatibility exports helps prevent confusion. Consider renaming to AI_PLAIN_TIMEOUT_MS or AI_NONSTREAMING_TIMEOUT_MS for consistency.

Suggested change
export const AI_TIMEOUT_MS = getTimeoutMs();
export const AI_PLAIN_TIMEOUT_MS = getTimeoutMs();
// Legacy alias for non-streaming timeout; prefer AI_PLAIN_TIMEOUT_MS in new code.
export const AI_TIMEOUT_MS = AI_PLAIN_TIMEOUT_MS;

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +44
* Default: 80 seconds (80000ms)
*/
export function getTimeoutMs(): number {
const envValue = Number(process.env.AI_TIMEOUT_MS);
if (envValue > 0) {
return envValue;
}
// Default: 80 seconds for non-streaming requests
return 80000;

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

The non-streaming timeout validation is missing logic to ensure it stays shorter than the gateway timeout. Unlike getStreamingTimeoutMs() which uses Math.min(240000, getGatewayTimeoutMs() * 0.8) to cap the timeout relative to the gateway timeout, this function doesn't validate against GATEWAY_TIMEOUT_MS. If a user sets AI_TIMEOUT_MS to a value exceeding the gateway timeout, the request will always be terminated by the gateway timeout rather than this AI timeout, potentially leading to unexpected behavior. Consider adding validation similar to the streaming timeout to ensure this value is always less than the gateway timeout.

Suggested change
* Default: 80 seconds (80000ms)
*/
export function getTimeoutMs(): number {
const envValue = Number(process.env.AI_TIMEOUT_MS);
if (envValue > 0) {
return envValue;
}
// Default: 80 seconds for non-streaming requests
return 80000;
* Default: 80 seconds (80000ms) or 80% of gateway timeout, whichever is smaller
*/
export function getTimeoutMs(): number {
const envValue = Number(process.env.AI_TIMEOUT_MS);
if (envValue > 0) {
// Ensure configured timeout does not exceed a safe fraction of the gateway timeout
return Math.min(envValue, getGatewayTimeoutMs() * 0.8);
}
// Default: 80 seconds or 80% of gateway timeout, whichever is smaller
return Math.min(80000, getGatewayTimeoutMs() * 0.8);

Copilot uses AI. Check for mistakes.
* from incremental responses and long waits are usually indicative of issues.
* Default: 80 seconds (80000ms)
*/
export function getTimeoutMs(): number {

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

The function name getTimeoutMs() is too generic and ambiguous. It doesn't clearly indicate that it's specifically for non-streaming/plain requests. Consider renaming to getNonStreamingTimeoutMs() or getPlainRequestTimeoutMs() to match the clarity of getStreamingTimeoutMs() and make the distinction between the two timeout types immediately obvious.

Copilot uses AI. Check for mistakes.
@steebchen
steebchen added this pull request to the merge queue Feb 4, 2026
Merged via the queue into main with commit 1118b6c Feb 4, 2026
19 checks passed
@steebchen
steebchen deleted the ai-request-timeout-80s branch February 4, 2026 16:28
@coderabbitai coderabbitai Bot mentioned this pull request Feb 10, 2026
2 tasks done
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