Skip to content

⏱️ fix: Bound Remaining Outbound Calls With Timeout Budgets - #17468

Merged
berry-13 merged 8 commits into
devfrom
berry-13/devin-perf-timeouts
Oct 29, 2026
Merged

berry-13 merged 8 commits into
devfrom
berry-13/devin-perf-timeouts

Conversation

@berry-13

@berry-13 berry-13 commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Bounds the outbound HTTP calls from the 2026-10-19 Devin performance scan that #17263 did not cover. A stalled provider could hold a request, a tool call or startup indefinitely: the OpenWeather, Wolfram, Google Search, Traversaal, Tavily and Stable Diffusion tools, TTS and STT providers, RAG file deletes, Firebase download streams, the service key and remote config fetches, the RAG health check, Google admin refresh and Mailgun had no timeout.

They now reuse the #17263 timeouts budget helpers. Three keys join the timeouts block in librechat.yaml: toolsMs (60 s), speechMs (300 s) and imageGenerationMs (300 s). TTS requests also abort when the client disconnects (abortOnClose). Startup and auth fetches that run before app config exists use a fixed 10 s budget.

Type of change

  • Bug fix

Testing

Tested environments/configuration: Node 20 unit specs.

Automated tests:

  • New tests for the RAG delete budget, abortOnClose and toolRequestBudget; updated mocks in the key, rag, STT, TTS, speech SSRF and Tavily specs.
  • packages/api jest over utils, app, auth/googleRefresh, rag, streams, key and cloudfront: 73 suites, 1976 tests pass.
  • api jest over Files services, utils, tools and middleware: pass.
  • eslint, prettier, sort-imports clean; tsc --noEmit in packages/data-provider and packages/api clean.

Screenshots / recordings

No user-facing change.

Risk / compatibility

New finite defaults mean a tool, speech or image generation call that previously ran longer than its budget now fails with a timeout; the budgets are configurable. The Flux tool (tracked in berry-13#400), RAG /embed and the CloudFront body stream are not changed here.

Checklist

Copilot AI balanced review requested due to automatic review settings October 26, 2026 17:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T07:58:03.320226Z 51e131f New commits
🔒 Security Review ✅ Completed 2026-10-26T17:08:07.366110Z fb8ea60 PR opened

Security findings

Advisory findings (1)

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fb8ea603c8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread api/server/services/Files/Audio/TTSService.js Outdated
stream: true,
},
allowedAddresses,
requestOptions,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Create a fresh speech deadline for each TTS chunk

For input of at least 4096 characters, every chunk reuses this single requestOptions.signal, so speechMs limits the entire sequence rather than each provider request as documented. Later chunks are cancelled even when every individual request finishes within its budget. An isolated execution with a 60 ms budget and two 35 ms requests completed only the first chunk. Keep the disconnect signal shared, but create a new outbound budget for each ttsRequest, as streamAudio already does.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 2d4aa6b. createSpeechBudget returns a factory, so each TTS chunk takes a fresh speechMs deadline while the disconnect signal stays shared. Unit spec in timeouts.spec.ts.

Comment thread packages/api/src/files/rag.ts Outdated
accept: 'application/json',
},
data: [file.file_id],
...outboundRequestBudget(DEFAULT_OUTBOUND_TIMEOUTS_MS.ragMs, 'ragMs'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Honor the configured RAG budget for stored-file deletion

When an operator changes timeouts.ragMs, deletions through the local, S3, Azure, and Firebase storage paths still use this fixed 30-second deadline; only createVectorFileDeleter honors the configuration. Consequently, raising the documented budget cannot prevent these embedding-cleanup requests from timing out, and lowering it does not bound them as requested. Pass the configured budget from the storage callers into deleteRagFile, using the constant only as a fallback. This also satisfies the repository requirement that new timeouts remain configurable.

AGENTS.md reference: AGENTS.md:L91-L93

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 2d4aa6b. Local, S3 and owned-blob (Azure/Firebase) deleters pass req.config.timeouts into deleteRagFile; covered by rag.spec.ts and the stalled-rag-delete scenario.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: fb8ea603c8

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Comment thread packages/api/src/app/loader.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: de15dc804b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread api/server/utils/sendEmail.js Outdated
Comment on lines +44 to +45
timeout: SERVICE_REQUEST_TIMEOUT_MS,
signal: AbortSignal.timeout(SERVICE_REQUEST_TIMEOUT_MS),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Move Mailgun deadline construction into TypeScript

The Mailgun path still constructs its new timeout and abort lifecycle directly inside legacy CJS. This violates the explicit module-boundary rule in AGENTS.md, which applies to additions in existing /api files, not just new services. Move the deadline construction into packages/api and have this adapter consume the returned request options, as the speech and tool callers now do.

AGENTS.md reference: AGENTS.md:L78-L82

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a461964. sendEmail.js calls serviceRequestBudget() from packages/api, which the remote config loader shares.

stream: true,
},
allowedAddresses,
nextBudget(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Close the manual audio response when a later chunk times out

For manual TTS input of at least 4096 characters, if one chunk has already streamed and a subsequent provider request exceeds speechMs before returning headers, the new deadline rejects ttsRequest before pipeSegment can attach its stream cleanup. The catch then returns without ending or destroying the response because res.headersSent is already true, and the /manual route has no final response cleanup. An isolated execution of this method with a successful first chunk and a timed-out second chunk confirmed that the handler returns with the response neither ended nor destroyed. Destroy the partially written response on this timeout path so the client does not keep waiting indefinitely.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a461964. The manual TTS loop now destroys the response when a chunk fails after headers were sent; covered by a spec with a successful first chunk and a timed-out second.

@berry-13 berry-13 added 🐛 bug Something isn't working 🔧 fix Bug fixes and code patches labels Oct 27, 2026
@berry-13
berry-13 force-pushed the berry-13/devin-perf-timeouts branch 2 times, most recently from 9f14f9d to c06e33f Compare October 28, 2026 19:03
Add timeouts.toolsMs, speechMs and imageGenerationMs to the timeouts block and apply the
existing budgets to the RAG delete paths, built-in tool APIs, TTS/STT providers (TTS also
aborts when the client closes), Stable Diffusion, Firebase reads and Mailgun. Calls made
before app config exists (remote config, RAG health, service key URL, Google refresh) get a
fixed 10 second budget.
@berry-13
berry-13 force-pushed the berry-13/devin-perf-timeouts branch from c06e33f to 51e131f Compare October 29, 2026 07:56
@berry-13
berry-13 merged commit 5b8bb3c into dev Oct 29, 2026
50 checks passed
@berry-13
berry-13 deleted the berry-13/devin-perf-timeouts branch October 29, 2026 08:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 bug Something isn't working 🔧 fix Bug fixes and code patches 🛡️ security review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants