Skip to content

fix(router-provider): fetch model metadata before context management decisions - #1053

Open
JamesRobert20 wants to merge 5 commits into
mainfrom
fix/router-provider-context-window
Open

fix(router-provider): fetch model metadata before context management decisions#1053
JamesRobert20 wants to merge 5 commits into
mainfrom
fix/router-provider-context-window

Conversation

@JamesRobert20

@JamesRobert20 JamesRobert20 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Problem

Router providers that are auth-scoped (zoo-gateway, kimi-code) skip the model cache entirely. When a fresh handler is constructed (on provider switch or profile change), getModel() falls back to hardcoded default model info because the real model list has not been fetched from the API yet.

Context management in Task.ts calls getModel() before createMessage() (where fetchModel() normally runs). This means condensing and truncation decisions use the default context window (200k for zoo-gateway) instead of the actual model's context window.

If the user selects a model with a larger context window through the gateway (e.g. Opus 4.8 at 1M tokens), the system incorrectly calculates context usage against 200k, triggering premature condensation.

Fix

  • Add ensureModelFetched() to RouterProvider that calls fetchModel() once when the instance model map is empty. Subsequent calls are no-ops.
  • Add ensureModelFetched as an optional method on the ApiHandler interface so Task.ts can call it.
  • Call await this.api.ensureModelFetched?.() in Task.ts before both context management paths (attemptApiRequest and handleContextWindowExceededError).

Static providers (Anthropic, Gemini, Bedrock, etc.) are unaffected since they don't implement the method. Non-auth-scoped router providers (OpenRouter, Vercel AI Gateway) already have cache hits via getModelsFromCache() so the fetch is skipped after the first call.

Summary by CodeRabbit

  • New Features
    • Model metadata (context window and pricing) can now be prefetched automatically via an optional API capability.
    • Router-based model loading is now concurrency-safe and memoized to avoid duplicate requests.
  • Bug Fixes
    • Context-limit handling and token management now use freshly fetched model metadata instead of defaults.
    • If metadata fetching fails, requests continue using safe fallback behavior.
    • Updated model details (e.g., context window) correctly override prior default values.
  • Tests
    • Expanded coverage for cache short-circuiting, recovery after failures, and concurrency/deduplication.

…decisions

Router providers (zoo-gateway, kimi-code) that are auth-scoped skip the
model cache entirely. On a fresh handler instance getModel() falls back
to hardcoded defaults (e.g. 200k context window) because the real model
list has not been fetched yet. Context management runs before
createMessage() which is where fetchModel() normally happens, so
condensing/truncation decisions use the wrong context window.

Add ensureModelFetched() to RouterProvider that fetches once when the
instance model map is empty. Call it in Task before context management
so getModel() returns accurate metadata from the API.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cd0bc5b3-b80b-4eb8-859c-328fc0d600c6

📥 Commits

Reviewing files that changed from the base of the PR and between c7ead3e and 1264874.

📒 Files selected for processing (1)
  • src/core/task/__tests__/Task.spec.ts

📝 Walkthrough

Walkthrough

ApiHandler now optionally supports lazy model fetching. RouterProvider caches and deduplicates remote model metadata requests, while task flows safely fetch metadata before using model-derived values.

Changes

Model Metadata Fetching

Layer / File(s) Summary
Provider model fetching
src/api/index.ts, src/api/providers/router-provider.ts, src/api/providers/__tests__/zoo-gateway.spec.ts
The API contract and RouterProvider add memoized model fetching with concurrent-call deduplication, retry recovery, and tests for cache behavior and fetched context windows.
Task model-dependent flows
src/core/task/Task.ts, src/core/task/__tests__/Task.spec.ts
Task request and context-window paths use a failure-tolerant model-fetch helper, with tests covering invocation, missing handlers, logging, and continued streaming.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Task
  participant ApiHandler
  participant RouterProvider
  Task->>ApiHandler: ensureModelFetched()
  ApiHandler->>RouterProvider: ensureModelFetched()
  RouterProvider->>RouterProvider: fetchModel() when models are empty
  RouterProvider-->>Task: model metadata available
Loading

Possibly related PRs

  • Zoo-Code-Org/Zoo-Code#344: Adds the Zoo Gateway model discovery and fetching behavior covered by the updated provider tests.

Suggested labels: awaiting-review

Suggested reviewers: edelauna

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the problem and fix, but it omits required template sections like the linked issue, test procedure, and checklist. Add the required sections from the template: Related GitHub Issue, Test Procedure, Pre-Submission Checklist, and any applicable notes.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: prefetching router model metadata before context decisions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/router-provider-context-window

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.

@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.75000% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/core/task/Task.ts 85.71% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 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/api/providers/router-provider.ts`:
- Around line 64-68: Make ensureModelFetched() single-flight by storing and
reusing an in-flight fetchModel() promise, clearing it after completion while
preserving the empty-model check. Update ZooGatewayHandler.createMessage() and
completePrompt() to call ensureModelFetched() followed by getModel(), replacing
their unconditional fetchModel() calls so auth-scoped providers avoid duplicate
metadata requests.

In `@src/core/task/Task.ts`:
- Around line 4046-4047: Move the optional ensureModelFetched call before the
cachedStreamingModel snapshot and before getSystemPrompt-dependent request
setup. Reuse the initialized model returned by this.api.getModel() for both
cachedStreamingModel and modelInfo, ensuring fresh router providers have
metadata available even when contextTokens is falsy.
🪄 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: bc932c48-2b90-434b-b92a-9aef4da2165e

📥 Commits

Reviewing files that changed from the base of the PR and between dcaa3cb and 101b490.

📒 Files selected for processing (4)
  • src/api/index.ts
  • src/api/providers/__tests__/zoo-gateway.spec.ts
  • src/api/providers/router-provider.ts
  • src/core/task/Task.ts

Comment thread src/api/providers/router-provider.ts
Comment thread src/core/task/Task.ts Outdated
…ll site

Make ensureModelFetched single-flight so concurrent callers share a
single in-flight fetch instead of firing duplicates. Move the call site
before the cachedStreamingModel snapshot so the model info is accurate
from the start of the streaming session, not just for context management.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot 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.

♻️ Duplicate comments (1)
src/core/task/Task.ts (1)

4048-4049: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Move model fetching before getSystemPrompt() and remove the contextTokens guard.

This is the same still-valid issue from the previous review. Direct attemptApiRequest() callers compute getSystemPrompt() before fetching metadata, and requests with falsy contextTokens skip fetching entirely, leaving router-provider defaults in model-dependent logic. Fetch at the start of attemptApiRequest() before getSystemPrompt().

🤖 Prompt for 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.

In `@src/core/task/Task.ts` around lines 4048 - 4049, Update attemptApiRequest to
call ensureModelFetched at its start, before getSystemPrompt is computed, and
remove the contextTokens guard that currently allows fetching to be skipped.
Preserve the existing model metadata usage through getModel().info after the
unconditional fetch.
🤖 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.

Duplicate comments:
In `@src/core/task/Task.ts`:
- Around line 4048-4049: Update attemptApiRequest to call ensureModelFetched at
its start, before getSystemPrompt is computed, and remove the contextTokens
guard that currently allows fetching to be skipped. Preserve the existing model
metadata usage through getModel().info after the unconditional fetch.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d25401b-9da0-459f-9f39-79608468aa3a

📥 Commits

Reviewing files that changed from the base of the PR and between 101b490 and 47e0732.

📒 Files selected for processing (3)
  • src/api/providers/__tests__/zoo-gateway.spec.ts
  • src/api/providers/router-provider.ts
  • src/core/task/Task.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/api/providers/tests/zoo-gateway.spec.ts
  • src/api/providers/router-provider.ts

@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Jul 29, 2026
Comment thread src/core/task/Task.ts Outdated

await this.diffViewProvider.reset()

await this.api.ensureModelFetched?.()

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.

The litellm/deepseek/moonshot fetchers re-throw, so a failure here would reject into the catch at L3743, which return trues and ends the task silently — the comment there assumes only attemptApiRequest can throw. Pre-change, the same failure surfaced via the createMessage retry path. Would catching and logging here be safer, letting getModel() fall back to defaults?

Comment thread src/api/providers/router-provider.ts Outdated

private modelFetchPromise?: Promise<void>

async ensureModelFetched(): Promise<void> {

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.

For auth-scoped providers, does the first request now fetch the model list twice — once here and again unconditionally in createMessage (zoo-gateway.ts L184)? getModels bypasses both caches for auth-scoped providers (modelCache.ts L265), so the second call is another network round trip. Worth making fetchModel() single-flight (or short-circuiting when this.models is populated) so both callers share one fetch?

})
})

describe("ensureModelFetched", () => {

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.

Is the rejection path covered anywhere? If getModels rejects once and the finally cleanup in router-provider.ts (L71-74) ever regresses, the stored rejected promise would poison every subsequent call. A reject-then-resolve test here would lock that in.

@github-actions github-actions Bot added awaiting-author PR is waiting for the author to address requested changes and removed awaiting-review PR changes are ready and waiting for maintainer re-review labels Jul 29, 2026
…ouble-fetch

Make fetchModel single-flight and short-circuit once models are loaded so
auth-scoped providers do not hit the models endpoint twice per request.
Catch ensureModelFetched failures in Task via safeEnsureModelFetched so a
metadata fetch error falls back to defaults instead of ending the task.
Add reject-then-recover coverage and Task tests for the new call sites.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions github-actions Bot removed the awaiting-author PR is waiting for the author to address requested changes label Jul 30, 2026
JamesRobert20 and others added 2 commits July 30, 2026 05:10
vi.spyOn on the private method fails check-types; route it through the
existing test access cast like the other private helpers.

Co-authored-by: Cursor <cursoragent@cursor.com>
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