perf: eliminate response.clone() memory doubling and cache lazy tool getters#1478
perf: eliminate response.clone() memory doubling and cache lazy tool getters#1478skyhighbg22-jpg wants to merge 5 commits into
Conversation
jatmn
left a comment
There was a problem hiding this comment.
I found issues that need to be addressed before this is ready.
Findings
-
[P2] Preserve the response body when usage parsing fails
src/services/api/openaiShim.ts:2733
This now reads the liveResponsebody beforeJSON.parsesucceeds, but thecatchleaves the original consumed response in place. If an OpenAI-compatible provider returns a successful non-streaming response with invalid JSON or an unexpected non-JSON body, the later caller path atresponse.json()/response.text()now fails withBody already usedor loses the provider's original body, whereas the oldresponse.clone().json()failure left the original response readable for normal conversion/error handling. Please recreate theResponseimmediately aftertext()succeeds, or otherwise only consume a clone/fallback buffer so downstream parsing can still read the body when usage extraction fails. -
[P3] De-duplicate this scope with the older open PR
src/services/api/openaiShim.ts:2733
This PR repeats the sameopenaiShim.tsresponse-body optimization and the samesrc/tools.tslazy getter caching that are already present in the older open #1473 from the same branch family, while #1473 also carries adjacent fixes in the same files. Keeping both PRs open makes reviewers resolve the same bug and tool-registry changes twice and risks merging one copy without the other PR's follow-up corrections. Please close/supersede one PR or remove the overlapping hunks so there is a single source of truth for these changes.
2587608 to
632203c
Compare
jatmn
left a comment
There was a problem hiding this comment.
Thanks for the update. I rechecked the changed paths and found an issue that still needs to be addressed.
Findings
- [P2] Preserve the response body when usage parsing fails
src/services/api/openaiShim.ts:2733
The liveResponsebody is still consumed before usage parsing has safely completed, and the replacementResponseis only created afterJSON.parse(bodyText)succeeds. If a provider returns a successful non-streaming response with invalid JSON or a 2xx non-JSON body, the catch swallows the parse failure and returns the original consumed response, so the downstream conversion/error path later hitsBody already usedinstead of reading the provider body. Please recreate the response immediately afterawait response.text()succeeds, before parsing usage, or keep the usage probe on a clone/fallback buffer so parse failures leave the response readable.
…getters openaiShim.ts: - Replace response.clone() with response.text() + JSON.parse() + new Response() for non-streaming usage extraction — avoids doubling memory for large responses tools.ts: - Cache getSendMessageTool(), getTeamCreateTool/DeleteTool(), getPowerShellTool() results in local IIFEs — avoids double-invocation of lazy require() getters
632203c to
fc2f084
Compare
jatmn
left a comment
There was a problem hiding this comment.
Thanks for the update. I rechecked the changed paths and found one issue that still needs to be addressed.
Findings
- [P3] Align the PR description with the current scope
src/main.tsx:1280
The PR description still says this change replaces the synchronousreadFileSyncpath for--system-prompt-file/--append-system-prompt-file, but the current diff only changessrc/services/api/openaiShim.tsandsrc/tools.ts;src/main.tsxstill importsreadFileSyncand uses it for both prompt-file flags. That makes the review target and claimed behavior drift from the actual implementation. Please either include the async file-read change, or remove that row and summary claim so the PR description matches the code being merged.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📜 Recent review details🧰 Additional context used📓 Path-based instructions (13)src/**/*.{ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
{src/commands/**/*.ts,src/services/**/*.ts,src/entrypoints/**/*.ts}📄 CodeRabbit inference engine (AGENTS.md)
Files:
{src/services/**/*.ts,src/utils/**/*.ts}📄 CodeRabbit inference engine (AGENTS.md)
Files:
{src/integrations/**/*.ts,src/services/**/*.ts}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{ts,tsx,js,jsx,py,json,md}📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*.test.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*.test.{ts,tsx,js}📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*.{ts,tsx,js,jsx,py}📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*.{ts,tsx}📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*⚙️ CodeRabbit configuration file
Files:
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}⚙️ CodeRabbit configuration file
Files:
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}⚙️ CodeRabbit configuration file
Files:
**⚙️ CodeRabbit configuration file
Files:
🔇 Additional comments (1)
📝 WalkthroughWalkthroughThe PR hardens non-streaming response handling in the OpenAI shim to preserve body content and routing metadata even when JSON parsing fails, adds two regression tests to validate error recovery and metadata preservation, and optimizes tool lazy-loading to eliminate redundant require() invocations. ChangesResponse Resilience and Tool Loading Optimization
Estimated Code Review Effort🎯 2 (Simple) | ⏱️ ~12 minutes Suggested Reviewers
🚥 Pre-merge checks | ✅ 6 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (6 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/services/api/openaiShim.ts`:
- Around line 2733-2745: In _doOpenAIRequest, when params.stream is false, avoid
replacing the original fetch response (which loses response.url) — instead call
response.clone(), await clone.text() to get bodyText, parse JSON from that clone
to extract tokensIn/tokensOut (e.g.,
data.usage?.prompt_tokens/completion_tokens), and leave the original response
object intact so OpenAIShimMessages.create() can inspect response.url to choose
the correct non-stream branch; only recreate a Response for other code paths
that truly need it, but do not overwrite response in the non-stream success
path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9f0b14e4-fec7-4b75-8030-be51d25a2a5c
📒 Files selected for processing (3)
src/services/api/openaiShim.test.tssrc/services/api/openaiShim.tssrc/tools.ts
jatmn
left a comment
There was a problem hiding this comment.
Thanks for the update. I rechecked the changed paths and found issues that still need to be addressed.
Findings
-
[P2] Complete CodeRabbit's request to preserve response routing metadata
src/services/api/openaiShim.ts:2737
CodeRabbit's review thread is marked resolved, but the current patch still replaces the fetch response withnew Response(bodyText, ...). That new response does not preserveresponse.url, whileOpenAIShimMessages.create()usesresponse.urlto choose the/responses,/messages, and Gemini non-stream conversion paths. Successful non-streaming requests to descriptor routes such as OpenCode/messagesor Gemini endpoint paths can therefore fall through to the generic OpenAI converter and return the wrong message shape. Please complete that review request by keeping the original response metadata available, or by routing from explicit request metadata instead of the recreated response URL. -
[P2] Fix the added regression test before merging
src/services/api/openaiShim.test.ts:4043
The newnon-streaming: preserves response body when usage parsing failstest currently fails:bun test src/services/api/openaiShim.test.tsreportsExpected: > 1, Received: 1forparseCalls. That leaves the focused shim test file red on this branch, so the test needs to be corrected or rewritten to assert the intended readable-body failure mode without relying on a secondJSON.parsecall that does not occur in the current runtime path.
…ession test Addresses PR review feedback (two P2s). 1. Preserve response.url and response.type when recreating the Response after reading the body for usage extraction. new Response(bodyText) drops url to empty string, which broke create()'s /responses, /messages, and Gemini routing — descriptor routes (OpenCode /messages, Gemini /models/gemini-*) fell through to the generic OpenAI converter and returned the wrong message shape. Restore the original metadata via Object.defineProperty (shadowing the read-only prototype getter), guarded by try/catch for runtime safety. 2. Fix the flaky 'preserves response body when usage parsing fails' test. The original mock threw on the first global JSON.parse call and asserted parseCalls > 1, but Bun's native Response.json() does not go through JS-level JSON.parse, so parseCalls stayed at 1 and the assertion failed. Rewrite to scope the failure to the response body text and assert usageParseFailed + content correctness instead, which works in both Bun (native Response.json) and Node (undici). 3. Add 'preserves response.url routing metadata after body read' test that pins an Anthropic-shaped body behind a /messages URL — fails without the url fix (content becomes []), passes with it.
jatmn
left a comment
There was a problem hiding this comment.
Thanks for the update. I rechecked the previously discussed paths and do not see any remaining actionable issues from my side.
@kevincodex1 LGTM
Summary
Performance optimizations — eliminates memory doubling from response cloning and caches lazy tool getters.
Optimizations
src/services/api/openaiShim.tsresponse.clone()withresponse.text()→ recreateResponseimmediately → thenJSON.parse()for usage. Response body is preserved even if JSON.parse fails.src/tools.tsrequire()getter results in local IIFEs — avoids double-invocation ofgetSendMessageTool,getTeamCreateTool/DeleteTool,getPowerShellToolTesting
bun run build✓Summary by CodeRabbit
response.url) is preserved.