Skip to content

fix(model-picker): eliminate O(n²) catalog rebuild lag in /model - #2078

Merged
kevincodex1 merged 9 commits into
Twigpine:mainfrom
keyarr:fix/model-picker-lag
Aug 6, 2026
Merged

kevincodex1 merged 9 commits into
Twigpine:mainfrom
keyarr:fix/model-picker-lag

Conversation

@keyarr

@keyarr keyarr commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes the severe lag in the /model picker when a provider exposes many models (100+). Closes #2077.

Root cause — two O(n²) hot spots compounding per keystroke:

  • getModelOptions() was O(n²) for catalog-backed routes: optionMatchesModel ran a full catalog scan (getRouteCatalogModelOption) inside an options.some(...) loop, and getCatalogOptionValue re-filtered the whole catalog per entry. A single call measured ~43ms on the 277-entry Fireworks catalog.
  • The picker rebuilt the full options list on every render/focus change: the effort/display path calls isGenuineSwitchProfileValue → getModelOptions() on every keystroke, even for ordinary model ids that can never be cross-profile switch entries.

Changes:

  • hasOptionValue hoists the catalog lookup out of the per-option loop.
  • Duplicate api-names precomputed once into a Set (getDuplicateCatalogApiNames).
  • isGenuineSwitchProfileValue short-circuits for values that don't start with the __switch_profile__: prefix (semantics preserved, including the documented literal-prefixed-id edge case).

Measured: getModelOptions() on the 100+ entry NVIDIA NIM catalog went from 43.5ms → 2.0ms (~22x), and no option-list rebuild happens per keystroke anymore.

Impact

  • user-facing: /model arrow-key navigation is responsive again with large model catalogs (NVIDIA NIM, OpenRouter, big OpenAI-compatible endpoints).
  • developer/maintainer: no API changes; behavior identical, only complexity reduced.

Testing

  • bun run build
  • bun run smoke
  • bun run check (2 pre-existing flaky failures also present on main without this change)
  • bun run typecheck
  • focused tests: bun test src/utils/model/ src/commands/model/ src/components/ModelPicker.test.tsx (236 pass)

Notes

  • provider/model path tested: NVIDIA NIM catalog (100+ entries) + 100-model discovery cache via micro-benchmark.
  • follow-up: none.

Summary by CodeRabbit

  • Bug Fixes

    • Improved model selection when provider catalogs contain duplicate or normalized API names.
    • Preserved custom, scoped, aliased, and saved model selections more reliably.
    • Prevented ordinary custom model IDs from being misinterpreted as profile switches.
    • Improved validation for malformed or unsupported profile-switch values.
  • Tests

    • Added comprehensive coverage for model catalog deduplication, custom models, aliases, cached options, and profile switching.

@coderabbitai

coderabbitai Bot commented Jul 31, 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: dbfe6431-2dc5-4b4b-bd43-daeddcf181a1

📥 Commits

Reviewing files that changed from the base of the PR and between a69458c and f3ad04c.

📒 Files selected for processing (2)
  • src/components/ModelPicker.switchProfile.test.ts
  • src/utils/model/modelOptions.catalogDedup.test.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: smoke-and-tests (22)
  • GitHub Check: smoke-and-tests (24.11.x)
  • GitHub Check: typecheck
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

TypeScript code in this repository must use strict mode and ESM imports.

**/*.{ts,tsx}: Add or update tests when a TypeScript or TSX change affects behavior.
Run the relevant TypeScript validation checks for changed code, including bun run typecheck and, when applicable, bun run typecheck:type-tests.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/components/ModelPicker.switchProfile.test.ts
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*: Preserve existing repository patterns unless intentionally refactoring them.
Keep changes small, readable, and focused; avoid broad rewrites or unrelated cleanup.
Do not reformat unrelated files, and keep comments useful and concise.
Update documentation when setup, commands, or user-facing behavior changes.
Review AI-generated changes for correctness, style consistency, unnecessary noise, and adherence to project architecture before submitting them.
Provider changes must follow the documented integration patterns in docs/integrations/overview.md and the focused guides under docs/integrations/how-to/.
When changing provider behavior, avoid breaking third-party providers and test the exact provider/model path changed when possible.
Provider pull requests must explicitly identify affected providers, limitations, and follow-up work.
Run the narrowest meaningful validation command for the touched area, and ensure relevant CI checks pass before merging.
Use bun install to install dependencies and the repository's Bun scripts for building, testing, smoke testing, and development.
Dependency changes require a concrete project benefit such as a bug fix, security issue, or approved feature; preference alone is insufficient.
Do not change the project's language, core runtime, or dependency stack, or introduce a new runtime, without prior maintainer agreement.
Keep each pull request focused on one issue or clearly scoped improvement and avoid bundling unrelated fixes, features, or refactors.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/components/ModelPicker.switchProfile.test.ts

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/components/ModelPicker.switchProfile.test.ts
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}

⚙️ CodeRabbit configuration file

{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}

⚙️ CodeRabbit configuration file

{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/components/ModelPicker.switchProfile.test.ts
🔇 Additional comments (3)
src/utils/model/modelOptions.catalogDedup.test.ts (2)

38-38: LGTM!

Also applies to: 72-76, 162-162, 179-179, 322-356, 375-390


375-390: 📐 Maintainability & Code Quality

Confirm required Bun validation before merge.

The supplied context has no CI logs or command output. Run bun run typecheck, the focused tests, build, smoke, and check commands. Run bun run typecheck:type-tests if that script applies. Report the exact commands and results.

Sources: Coding guidelines, Path instructions

src/components/ModelPicker.switchProfile.test.ts (1)

98-99: LGTM!


📝 Walkthrough

Walkthrough

The change adds prefix validation for switch-profile values and reuses catalog duplicate detection and route lookup state. New tests cover profile switching, malformed values, custom IDs, aliases, scoped caches, and large catalogs.

Changes

Model option flow

Layer / File(s) Summary
Catalog option normalization and lookup reuse
src/utils/model/modelOptions.ts, src/utils/model/modelOptions.catalogDedup.test.ts
Catalog duplicate API names are precomputed. Shared route-catalog context is reused for option conversion, canonicalization, and model presence checks. Tests cover aliases, custom models, scoped caches, and large catalogs.
Switch-profile value validation
src/components/ModelPicker.tsx, src/components/ModelPicker.switchProfile.test.ts
isGenuineSwitchProfileValue rejects non-prefixed values before option resolution. Tests cover genuine switches, malformed and unbacked values, ordinary IDs, and prefixed custom IDs.

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

Possibly related issues

  • Gitlawb/openclaude#2077 — The issue directly covers the prefix validation and catalog deduplication changes.

Suggested reviewers: jatmn

🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, scoped to the model picker, and accurately describes the catalog performance fix.
Description check ✅ Passed The description includes the required summary, impact, testing, and notes sections with relevant details and completed test results.
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.
Risk Surface Disclosed ✅ Passed The diff touches route-catalog and profile model selection, and the PR calls out unchanged routing behavior and reports no routing or profile-selection blockers.
No Hidden Policy Change ✅ Passed The diff changes only model-option lookup reuse and switch-prefix validation; no routing defaults, permissions, telemetry, network calls, credentials, or trust-policy logic changed.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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/utils/model/modelOptions.ts`:
- Around line 941-966: The catalog option changes around
getDuplicateCatalogApiNames and getCatalogOptionValue require focused regression
tests in src/utils/model/modelOptions.ts:941-966 covering case/whitespace
duplicates, direct/ID/alias matching, insertion paths, ordinary model IDs, and
large catalogs. Add focused switch-profile detection tests for
src/components/ModelPicker.tsx:499-509 covering ordinary IDs, valid
switch-profile values, and invalid prefixed values; no direct implementation
change is required at either site beyond adding or updating the corresponding
tests.
- Around line 1020-1042: Update getModelOptions and hasOptionValue to compute
the route-catalog lookup context, including duplicate API-name Map/Set data,
once per options build and pass it through all additional-model and
catalog-entry checks. Avoid calling getRouteCatalogModelOption or rebuilding
duplicate context for each option, while preserving existing matching behavior.
Verify performance with a large provider catalog and many additional options as
required by AGENTS.md.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2648f991-323f-41ac-83da-1167d0f7261c

📥 Commits

Reviewing files that changed from the base of the PR and between b3735be and 42685fb.

📒 Files selected for processing (2)
  • src/components/ModelPicker.tsx
  • src/utils/model/modelOptions.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: smoke-and-tests (24.11.x)
  • GitHub Check: smoke-and-tests (22)
  • GitHub Check: typecheck
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

TypeScript code in this repository must use strict mode and ESM imports.

**/*.{ts,tsx}: Add or update tests when a TypeScript or TSX change affects behavior.
Run the relevant TypeScript validation checks for changed code, including bun run typecheck and, when applicable, bun run typecheck:type-tests.

Files:

  • src/utils/model/modelOptions.ts
  • src/components/ModelPicker.tsx
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*: Preserve existing repository patterns unless intentionally refactoring them.
Keep changes small, readable, and focused; avoid broad rewrites or unrelated cleanup.
Do not reformat unrelated files, and keep comments useful and concise.
Update documentation when setup, commands, or user-facing behavior changes.
Review AI-generated changes for correctness, style consistency, unnecessary noise, and adherence to project architecture before submitting them.
Provider changes must follow the documented integration patterns in docs/integrations/overview.md and the focused guides under docs/integrations/how-to/.
When changing provider behavior, avoid breaking third-party providers and test the exact provider/model path changed when possible.
Provider pull requests must explicitly identify affected providers, limitations, and follow-up work.
Run the narrowest meaningful validation command for the touched area, and ensure relevant CI checks pass before merging.
Use bun install to install dependencies and the repository's Bun scripts for building, testing, smoke testing, and development.
Dependency changes require a concrete project benefit such as a bug fix, security issue, or approved feature; preference alone is insufficient.
Do not change the project's language, core runtime, or dependency stack, or introduce a new runtime, without prior maintainer agreement.
Keep each pull request focused on one issue or clearly scoped improvement and avoid bundling unrelated fixes, features, or refactors.

Files:

  • src/utils/model/modelOptions.ts
  • src/components/ModelPicker.tsx

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/utils/model/modelOptions.ts
  • src/components/ModelPicker.tsx
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}

⚙️ CodeRabbit configuration file

{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.

Files:

  • src/utils/model/modelOptions.ts
src/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Use React + Ink for terminal UI components under src/components/.

Files:

  • src/components/ModelPicker.tsx
🔇 Additional comments (2)
src/utils/model/modelOptions.ts (1)

977-979: LGTM!

src/components/ModelPicker.tsx (1)

14-14: LGTM!

Comment thread src/utils/model/modelOptions.ts
Comment thread src/utils/model/modelOptions.ts Outdated

@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/components/ModelPicker.switchProfile.test.ts`:
- Around line 196-201: Update the test helper that imports the nonce-suffixed
model-options module to return that mocked module instance, then reuse its
getModelOptions result in the assertion near the switchOption lookup. Ensure the
import uses the same specifier instance already bound to ModelPicker, rather
than performing a second dynamic import with a different nonce and separator.

In `@src/utils/model/modelOptions.catalogDedup.test.ts`:
- Around line 56-70: Fix both fresh-import helpers so their process-wide mocks
are gated and fall through to real implementations when inactive. In
src/utils/model/modelOptions.catalogDedup.test.ts lines 56-70, gate the
./providers.js mock, spread realModelProviders, reset its flag in beforeEach and
afterEach, and remove the ./model.js mock if the nonce import is sufficient; in
src/components/ModelPicker.switchProfile.test.ts lines 80-90, gate the
../utils/model/modelOptions.js mock on activeProfilesOverride so it resolves to
the real module when null.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 465fde48-13f9-428a-b3d9-53979d0a10ce

📥 Commits

Reviewing files that changed from the base of the PR and between 1938738 and 8faf6a4.

📒 Files selected for processing (3)
  • src/components/ModelPicker.switchProfile.test.ts
  • src/components/ModelPicker.tsx
  • src/utils/model/modelOptions.catalogDedup.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: typecheck
  • GitHub Check: smoke-and-tests (22)
  • GitHub Check: smoke-and-tests (24.11.x)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

TypeScript code in this repository must use strict mode and ESM imports.

**/*.{ts,tsx}: Add or update tests when a TypeScript or TSX change affects behavior.
Run the relevant TypeScript validation checks for changed code, including bun run typecheck and, when applicable, bun run typecheck:type-tests.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/components/ModelPicker.switchProfile.test.ts
  • src/components/ModelPicker.tsx
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*: Preserve existing repository patterns unless intentionally refactoring them.
Keep changes small, readable, and focused; avoid broad rewrites or unrelated cleanup.
Do not reformat unrelated files, and keep comments useful and concise.
Update documentation when setup, commands, or user-facing behavior changes.
Review AI-generated changes for correctness, style consistency, unnecessary noise, and adherence to project architecture before submitting them.
Provider changes must follow the documented integration patterns in docs/integrations/overview.md and the focused guides under docs/integrations/how-to/.
When changing provider behavior, avoid breaking third-party providers and test the exact provider/model path changed when possible.
Provider pull requests must explicitly identify affected providers, limitations, and follow-up work.
Run the narrowest meaningful validation command for the touched area, and ensure relevant CI checks pass before merging.
Use bun install to install dependencies and the repository's Bun scripts for building, testing, smoke testing, and development.
Dependency changes require a concrete project benefit such as a bug fix, security issue, or approved feature; preference alone is insufficient.
Do not change the project's language, core runtime, or dependency stack, or introduce a new runtime, without prior maintainer agreement.
Keep each pull request focused on one issue or clearly scoped improvement and avoid bundling unrelated fixes, features, or refactors.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/components/ModelPicker.switchProfile.test.ts
  • src/components/ModelPicker.tsx

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/components/ModelPicker.switchProfile.test.ts
  • src/components/ModelPicker.tsx
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}

⚙️ CodeRabbit configuration file

{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}

⚙️ CodeRabbit configuration file

{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/components/ModelPicker.switchProfile.test.ts
src/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Use React + Ink for terminal UI components under src/components/.

Files:

  • src/components/ModelPicker.tsx
🪛 ast-grep (0.45.0)
src/utils/model/modelOptions.catalogDedup.test.ts

[warning] 65-65: Do not use Math.random() to generate security-sensitive values such as tokens, secrets, passwords, API keys, salts, nonces, OTPs, or session IDs. Math.random() is not cryptographically secure and is predictable. Use crypto.randomBytes()/crypto.randomUUID() (Node) or crypto.getRandomValues() (Web Crypto) instead.
Context: Math.random()
Note: [CWE-330] Use of Insufficiently Random Values.

(insecure-random-security-token-typescript)

src/components/ModelPicker.switchProfile.test.ts

[warning] 83-83: Do not use Math.random() to generate security-sensitive values such as tokens, secrets, passwords, API keys, salts, nonces, OTPs, or session IDs. Math.random() is not cryptographically secure and is predictable. Use crypto.randomBytes()/crypto.randomUUID() (Node) or crypto.getRandomValues() (Web Crypto) instead.
Context: Math.random()
Note: [CWE-330] Use of Insufficiently Random Values.

(insecure-random-security-token-typescript)

🔇 Additional comments (4)
src/utils/model/modelOptions.catalogDedup.test.ts (2)

116-145: LGTM!


280-308: LGTM!

src/components/ModelPicker.tsx (1)

506-511: LGTM!

src/components/ModelPicker.switchProfile.test.ts (1)

235-246: LGTM!

Comment thread src/components/ModelPicker.switchProfile.test.ts Outdated
Comment thread src/utils/model/modelOptions.catalogDedup.test.ts

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/ModelPicker.switchProfile.test.ts (1)

82-97: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the nonce separator consistently when importing modelOptions.js.

The test at line 204 still imports ../utils/model/modelOptions.js?switchProfile-...; this bypasses importFreshModelPicker’s mock.module(... 'modelOptions.js' ...) and can load a separate module instance. Use the same query-param nonce style.

🤖 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/components/ModelPicker.switchProfile.test.ts` around lines 82 - 97,
Update the direct modelOptions.js import in the test around the switch-profile
case to use the same `?switchProfile=${nonce}` query-parameter format as
`importFreshModelPicker`. Keep the nonce-based cache busting intact so the
import resolves through the existing mock.module setup instead of creating a
separate module instance.
🤖 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.

Outside diff comments:
In `@src/components/ModelPicker.switchProfile.test.ts`:
- Around line 82-97: Update the direct modelOptions.js import in the test around
the switch-profile case to use the same `?switchProfile=${nonce}`
query-parameter format as `importFreshModelPicker`. Keep the nonce-based cache
busting intact so the import resolves through the existing mock.module setup
instead of creating a separate module instance.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b0271e54-fb3f-4659-b057-f867824a8eca

📥 Commits

Reviewing files that changed from the base of the PR and between 8faf6a4 and c5f5831.

📒 Files selected for processing (2)
  • src/components/ModelPicker.switchProfile.test.ts
  • src/utils/model/modelOptions.catalogDedup.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: typecheck
  • GitHub Check: smoke-and-tests (24.11.x)
  • GitHub Check: smoke-and-tests (22)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

TypeScript code in this repository must use strict mode and ESM imports.

**/*.{ts,tsx}: Add or update tests when a TypeScript or TSX change affects behavior.
Run the relevant TypeScript validation checks for changed code, including bun run typecheck and, when applicable, bun run typecheck:type-tests.

Files:

  • src/components/ModelPicker.switchProfile.test.ts
  • src/utils/model/modelOptions.catalogDedup.test.ts
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*: Preserve existing repository patterns unless intentionally refactoring them.
Keep changes small, readable, and focused; avoid broad rewrites or unrelated cleanup.
Do not reformat unrelated files, and keep comments useful and concise.
Update documentation when setup, commands, or user-facing behavior changes.
Review AI-generated changes for correctness, style consistency, unnecessary noise, and adherence to project architecture before submitting them.
Provider changes must follow the documented integration patterns in docs/integrations/overview.md and the focused guides under docs/integrations/how-to/.
When changing provider behavior, avoid breaking third-party providers and test the exact provider/model path changed when possible.
Provider pull requests must explicitly identify affected providers, limitations, and follow-up work.
Run the narrowest meaningful validation command for the touched area, and ensure relevant CI checks pass before merging.
Use bun install to install dependencies and the repository's Bun scripts for building, testing, smoke testing, and development.
Dependency changes require a concrete project benefit such as a bug fix, security issue, or approved feature; preference alone is insufficient.
Do not change the project's language, core runtime, or dependency stack, or introduce a new runtime, without prior maintainer agreement.
Keep each pull request focused on one issue or clearly scoped improvement and avoid bundling unrelated fixes, features, or refactors.

Files:

  • src/components/ModelPicker.switchProfile.test.ts
  • src/utils/model/modelOptions.catalogDedup.test.ts

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/components/ModelPicker.switchProfile.test.ts
  • src/utils/model/modelOptions.catalogDedup.test.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}

⚙️ CodeRabbit configuration file

{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.

Files:

  • src/components/ModelPicker.switchProfile.test.ts
  • src/utils/model/modelOptions.catalogDedup.test.ts
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}

⚙️ CodeRabbit configuration file

{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
🔇 Additional comments (2)
src/utils/model/modelOptions.catalogDedup.test.ts (1)

14-14: LGTM!

Also applies to: 24-24, 33-63, 96-108, 147-147, 162-162

src/components/ModelPicker.switchProfile.test.ts (1)

11-11: LGTM!

Also applies to: 20-20, 82-97

@keyarr

keyarr commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

@keyarr

keyarr commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

Risk surface assessment:

This PR touches provider-route catalog lookup and switch-profile value
validation.

The catalog change only reuses lookup context within a single
getModelOptions() build and preserves the existing matching and fallback
semantics. It does not change provider selection, routing defaults,
credentials, network behavior, or fallback expansion.

The switch-profile change only short-circuits ordinary model IDs that cannot
represent switch-profile entries. Valid prefixed values continue to be
resolved and validated against the generated options.

Focused regression tests cover catalog aliases, duplicate API names,
additional-option insertion paths, large catalogs, ordinary model IDs, valid
switch-profile values, and invalid prefixed values.

Based on those constraints and the passing test suite, no remaining routing or
profile-selection blocker is known.

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I found issues that need to be addressed before this is ready.

Findings

  • [P2] Remove the duplicate release repair from this PR
    web/src/data/releases.ts:20
    This is a hand-maintained release record, not output from the release bot. The same baseline repair is already tracked by open maintainer PR #2075, which adds the 0.27.0 entry and its regression coverage. Leaving this separate change here creates a merge conflict if #2075 lands first; landing this PR first instead supersedes the dedicated repair with a divergent, untested release record. Drop this hunk and rebase on the canonical repair (or explicitly coordinate which PR supersedes it).

  • [P2] Stop leaking the fresh model.js module into later test suites
    src/utils/model/modelOptions.catalogDedup.test.ts:89
    mock.module('./model.js', ...) is process-wide and mock.restore() does not unregister module mocks, but this mock has no active-test gate or real-module fallback. After this suite runs, later imports of the canonical ./model.js receive the last nonce-suffixed test instance, making module identity/state depend on test order. Remove the override if it is unnecessary, or gate it and fall through to a snapshot of the real module when no catalog test is active.

@keyarr
keyarr force-pushed the fix/model-picker-lag branch from 894629a to 1eb46a6 Compare August 2, 2026 11:00

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/utils/model/modelOptions.ts (1)

1030-1036: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Non-blocking: the per-lookup catalog scan is still O(entries) and runs several times per build.

findRouteCatalogOption scans context.entries with .find on every call. getModelOptions calls it once per scoped additional option (Line 1109) and again through hasOptionValue (Line 1115), so a build with m additional options costs 2·m·n normalizations on an n-entry catalog. The shared context removed the duplicate-set rebuild, but not this scan.

Build a normalized lookup Map once inside getRouteCatalogContext and reuse it. Keep first-entry-wins insertion so the resolved entry matches the current .find result, and keep the apiName → id → alias precedence.

⚡ Sketch: precompute the normalized index in the context
 type RouteCatalogContext = {
   entries: ReturnType<typeof getCatalogEntriesForRoute>
   duplicateApiNames: Set<string>
+  entriesByNormalizedKey: Map<string, ReturnType<typeof getCatalogEntriesForRoute>[number]>
 }
   const entries = getCatalogEntriesForRoute(routeId)
+  // apiName, then id, then aliases — first entry wins, matching the
+  // precedence and ordering of the previous linear `.find`.
+  const entriesByNormalizedKey = new Map<string, (typeof entries)[number]>()
+  for (const keyOf of [
+    (e: (typeof entries)[number]) => [e.apiName],
+    (e: (typeof entries)[number]) => [e.id],
+    (e: (typeof entries)[number]) => e.aliases ?? [],
+  ]) {
+    for (const entry of entries) {
+      for (const raw of keyOf(entry)) {
+        const key = normalizeRouteModelOptionKey(raw)
+        if (key && !entriesByNormalizedKey.has(key)) {
+          entriesByNormalizedKey.set(key, entry)
+        }
+      }
+    }
+  }
   return {
     entries,
     duplicateApiNames: getDuplicateCatalogApiNames(entries),
+    entriesByNormalizedKey,
   }
-  const catalogEntry = context.entries.find(entry =>
-    normalizeRouteModelOptionKey(entry.apiName) === normalizedValue ||
-    normalizeRouteModelOptionKey(entry.id) === normalizedValue ||
-    (entry.aliases ?? []).some(
-      alias => normalizeRouteModelOptionKey(alias) === normalizedValue,
-    ),
-  )
+  const catalogEntry = context.entriesByNormalizedKey.get(normalizedValue)

Note the precedence difference: the current .find is entry-major (an early entry's alias beats a later entry's apiName), the sketch above is field-major. Pick the one that matches intent and add a test for the collision case.

🤖 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/utils/model/modelOptions.ts` around lines 1030 - 1036, Update
getRouteCatalogContext to build and retain a normalized lookup Map once,
inserting each entry’s apiName, id, and aliases in entry-major order with
first-entry-wins semantics. Refactor findRouteCatalogOption to resolve through
this Map instead of scanning context.entries, preserving the existing
normalization and apiName → id → alias checks for each entry and adding coverage
for an early alias versus a later apiName collision.
🤖 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/components/ModelPicker.switchProfile.test.ts`:
- Around line 167-173: Update the ordinary-model test around
isGenuineSwitchProfileValue to track calls to getModelOptions through the gated
mock binding used by ModelPicker, then assert it is not called for an ordinary
id. Preserve the existing false-result assertions while explicitly verifying the
prefix short-circuit.

In `@src/utils/model/modelOptions.catalogDedup.test.ts`:
- Around line 272-303: Remove the overwritten OPENAI_BASE_URL and OPENAI_MODEL
environment assignments from the test setup around getRouteCatalogModelOptions,
while preserving OPENAI_API_KEY if needed by the exercised path. Keep the test
focused on the activeCacheScopeOverride and the helper’s own environment
behavior.

---

Outside diff comments:
In `@src/utils/model/modelOptions.ts`:
- Around line 1030-1036: Update getRouteCatalogContext to build and retain a
normalized lookup Map once, inserting each entry’s apiName, id, and aliases in
entry-major order with first-entry-wins semantics. Refactor
findRouteCatalogOption to resolve through this Map instead of scanning
context.entries, preserving the existing normalization and apiName → id → alias
checks for each entry and adding coverage for an early alias versus a later
apiName collision.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 08413e7a-673c-499c-9c59-6c7f084a9db7

📥 Commits

Reviewing files that changed from the base of the PR and between 894629a and 1eb46a6.

📒 Files selected for processing (5)
  • src/components/ModelPicker.switchProfile.test.ts
  • src/components/ModelPicker.tsx
  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/utils/model/modelOptions.ts
  • web/src/data/releases.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: smoke-and-tests (22)
  • GitHub Check: smoke-and-tests (24.11.x)
  • GitHub Check: typecheck
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

TypeScript code in this repository must use strict mode and ESM imports.

**/*.{ts,tsx}: Add or update tests when a TypeScript or TSX change affects behavior.
Run the relevant TypeScript validation checks for changed code, including bun run typecheck and, when applicable, bun run typecheck:type-tests.

Files:

  • src/components/ModelPicker.tsx
  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/components/ModelPicker.switchProfile.test.ts
  • web/src/data/releases.ts
  • src/utils/model/modelOptions.ts
src/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Use React + Ink for terminal UI components under src/components/.

Files:

  • src/components/ModelPicker.tsx
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*: Preserve existing repository patterns unless intentionally refactoring them.
Keep changes small, readable, and focused; avoid broad rewrites or unrelated cleanup.
Do not reformat unrelated files, and keep comments useful and concise.
Update documentation when setup, commands, or user-facing behavior changes.
Review AI-generated changes for correctness, style consistency, unnecessary noise, and adherence to project architecture before submitting them.
Provider changes must follow the documented integration patterns in docs/integrations/overview.md and the focused guides under docs/integrations/how-to/.
When changing provider behavior, avoid breaking third-party providers and test the exact provider/model path changed when possible.
Provider pull requests must explicitly identify affected providers, limitations, and follow-up work.
Run the narrowest meaningful validation command for the touched area, and ensure relevant CI checks pass before merging.
Use bun install to install dependencies and the repository's Bun scripts for building, testing, smoke testing, and development.
Dependency changes require a concrete project benefit such as a bug fix, security issue, or approved feature; preference alone is insufficient.
Do not change the project's language, core runtime, or dependency stack, or introduce a new runtime, without prior maintainer agreement.
Keep each pull request focused on one issue or clearly scoped improvement and avoid bundling unrelated fixes, features, or refactors.

Files:

  • src/components/ModelPicker.tsx
  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/components/ModelPicker.switchProfile.test.ts
  • web/src/data/releases.ts
  • src/utils/model/modelOptions.ts

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/components/ModelPicker.tsx
  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/components/ModelPicker.switchProfile.test.ts
  • web/src/data/releases.ts
  • src/utils/model/modelOptions.ts
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}

⚙️ CodeRabbit configuration file

{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/utils/model/modelOptions.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}

⚙️ CodeRabbit configuration file

{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.

Files:

  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/components/ModelPicker.switchProfile.test.ts
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When changing the web application, run bun run web:typecheck and bun run web:build.

Files:

  • web/src/data/releases.ts
web/**

⚙️ CodeRabbit configuration file

web/**: Review browser extension changes for content-script isolation, message validation, cross-origin assumptions, permission surfaces, and failures that could leak prompts or credentials.

Files:

  • web/src/data/releases.ts
🪛 ast-grep (0.45.0)
src/utils/model/modelOptions.catalogDedup.test.ts

[warning] 93-93: Do not use Math.random() to generate security-sensitive values such as tokens, secrets, passwords, API keys, salts, nonces, OTPs, or session IDs. Math.random() is not cryptographically secure and is predictable. Use crypto.randomBytes()/crypto.randomUUID() (Node) or crypto.getRandomValues() (Web Crypto) instead.
Context: Math.random()
Note: [CWE-330] Use of Insufficiently Random Values.

(insecure-random-security-token-typescript)

src/components/ModelPicker.switchProfile.test.ts

[warning] 85-85: Do not use Math.random() to generate security-sensitive values such as tokens, secrets, passwords, API keys, salts, nonces, OTPs, or session IDs. Math.random() is not cryptographically secure and is predictable. Use crypto.randomBytes()/crypto.randomUUID() (Node) or crypto.getRandomValues() (Web Crypto) instead.
Context: Math.random()
Note: [CWE-330] Use of Insufficiently Random Values.

(insecure-random-security-token-typescript)

🔇 Additional comments (8)
web/src/data/releases.ts (2)

20-31: LGTM!


20-31: 📐 Maintainability & Code Quality

Confirm the required web checks.

Because web/src/data/releases.ts changed, confirm that bun run web:typecheck and bun run web:build passed.

Source: Path instructions

src/utils/model/modelOptions.ts (2)

992-1015: LGTM!

Also applies to: 1078-1090


1056-1069: 🗄️ Data Integrity & Integration

Check the removed optionMatchesModel implementation before merging.

hasOptionValue now uses strict equality against both option values and the first matched catalog value. If the old comparison normalized case/whitespace or matched a [1m] suffix, existing persisted custom models can fall through and be appended again. Use the original call-site behavior to decide whether the strict comparison is safe.

src/utils/model/modelOptions.catalogDedup.test.ts (2)

39-102: LGTM!

Also applies to: 149-182


317-345: LGTM!

src/components/ModelPicker.switchProfile.test.ts (1)

30-98: LGTM!

Also applies to: 132-165

src/components/ModelPicker.tsx (1)

506-511: 🎯 Functional Correctness

No change needed: switch marker construction is consistent.

Production model options set switchToProfileId only in getInactiveProviderProfileOptions, and that path always writes value: encodeSwitchProfileValue(...). Existing tests cover bare values with the prefix but no marker.

Comment thread src/components/ModelPicker.switchProfile.test.ts
Comment thread src/utils/model/modelOptions.catalogDedup.test.ts
@keyarr
keyarr requested a review from jatmn August 2, 2026 14:04
keyarr added 8 commits August 3, 2026 15:34
getModelOptions() ran an O(n²) optionMatchesModel loop (catalog scan per
option) plus an O(n²) duplicate-apiName filter, costing ~43ms per call on
catalogs with hundreds of models (e.g. Fireworks' ~280 entries). The
picker also rebuilt the full options list on every keystroke via
isGenuineSwitchProfileValue, so arrow-key navigation lagged badly.

- hoist catalog lookup out of the per-option loop (hasOptionValue)
- precompute duplicate apiNames into a Set
- short-circuit isGenuineSwitchProfileValue for non-switch values

getModelOptions(): 43.5ms -> 2.0ms on a 277-entry catalog
…ns checks

getRouteCatalogModelOption re-resolved the active route, fetched the
catalog entries, and rebuilt the duplicate-apiName set on every call —
getModelOptions() invoked it up to 3x per build (env custom model, each
scoped additional option, active custom model + fallback), so large
catalogs (Fireworks ~280 entries) paid O(n) context rebuilds repeatedly.

Build the RouteCatalogContext lazily once per options build and pass it
through findRouteCatalogOption/hasOptionValue. Behavior unchanged; the
catalog-miss path measures 6.1ms -> 4.1ms on a 277-entry catalog.
…dup helper

OPENAI_BASE_URL and OPENAI_MODEL assigned in the scoped-cache test were
immediately overwritten by getRouteCatalogModelOptions, so they never
affected the exercised path. Remove the dead assignments; keep
OPENAI_API_KEY to preserve the helper's auth path.
isGenuineSwitchProfileValue short-circuits on the switch-profile prefix,
skipping the getModelOptions() rebuild for ordinary model ids. Track the
gated getModelOptions binding ModelPicker captures (opt-in call-through
mock in importFreshModelPicker) and assert it is never invoked for
non-prefixed ids, alongside the existing false-result assertions.
@keyarr
keyarr force-pushed the fix/model-picker-lag branch from 0272a35 to a69458c Compare August 3, 2026 19:02
@keyarr

keyarr commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Both review findings are addressed:

  1. Duplicate release entry removed — rebased onto upstream main (tip 63fda83d, the 0.27.0 entry from fix(web): add v0.27.0 changelog entry and clarify release-data ownership #2075); the standalone web/src/data/releases.ts commit was dropped, so the PR no longer touches that file.

  2. Mock leakage fixed — modelOptions.catalogDedup.test.ts now gates the process-wide mock.module('./model.js') behind activeModelOverride with fallback to the real module snapshot, registered once, and reset in beforeEach/afterEach (commits 1a270d37/0262777f). Same pattern applied in the switch-profile suite.

Branch rebased and pushed (0272a354...a69458c2), all CI green.

@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: 3

🤖 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/components/ModelPicker.switchProfile.test.ts`:
- Around line 96-104: Update the tracked spy setup in the test around
getModelOptionsSpy so its mock delegates to the nonce-fresh modelOptions module
instance imported for gatedModelOptionsModule, rather than realModelOptions.
Keep the existing conditional module replacement intact and ensure tracked calls
use the fresh instance’s provider, auth, and providerProfiles bindings without
leaking global or configuration state.

In `@src/utils/model/modelOptions.catalogDedup.test.ts`:
- Around line 69-73: Update the catalog mock around getCatalogEntriesForRoute to
increment a catalogFetchCount on each fetch, reset that counter in beforeEach,
and assert the expected small fixed fetch bound in the large-catalog
options-build test. Keep the existing option-value assertions while verifying
the shared RouteCatalogContext eliminates per-lookup rescans.

In `@src/utils/model/modelOptions.ts`:
- Around line 1056-1069: Add regression coverage for hasOptionValue through
getModelOptions using a cached additional option whose option.value is the
catalog alias while the lookup uses the canonical value. Assert the canonical
lookup matches the alias-backed option and does not append a duplicate model,
specifically exercising the catalogOption.value comparison 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: afe01071-a602-497b-a424-403d8d327584

📥 Commits

Reviewing files that changed from the base of the PR and between 0272a35 and a69458c.

📒 Files selected for processing (4)
  • src/components/ModelPicker.switchProfile.test.ts
  • src/components/ModelPicker.tsx
  • src/utils/model/modelOptions.catalogDedup.test.ts
  • src/utils/model/modelOptions.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: smoke-and-tests (22)
  • GitHub Check: typecheck
  • GitHub Check: smoke-and-tests (24.11.x)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

TypeScript code in this repository must use strict mode and ESM imports.

**/*.{ts,tsx}: Add or update tests when a TypeScript or TSX change affects behavior.
Run the relevant TypeScript validation checks for changed code, including bun run typecheck and, when applicable, bun run typecheck:type-tests.

Files:

  • src/components/ModelPicker.tsx
  • src/utils/model/modelOptions.ts
  • src/components/ModelPicker.switchProfile.test.ts
  • src/utils/model/modelOptions.catalogDedup.test.ts
src/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Use React + Ink for terminal UI components under src/components/.

Files:

  • src/components/ModelPicker.tsx
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*: Preserve existing repository patterns unless intentionally refactoring them.
Keep changes small, readable, and focused; avoid broad rewrites or unrelated cleanup.
Do not reformat unrelated files, and keep comments useful and concise.
Update documentation when setup, commands, or user-facing behavior changes.
Review AI-generated changes for correctness, style consistency, unnecessary noise, and adherence to project architecture before submitting them.
Provider changes must follow the documented integration patterns in docs/integrations/overview.md and the focused guides under docs/integrations/how-to/.
When changing provider behavior, avoid breaking third-party providers and test the exact provider/model path changed when possible.
Provider pull requests must explicitly identify affected providers, limitations, and follow-up work.
Run the narrowest meaningful validation command for the touched area, and ensure relevant CI checks pass before merging.
Use bun install to install dependencies and the repository's Bun scripts for building, testing, smoke testing, and development.
Dependency changes require a concrete project benefit such as a bug fix, security issue, or approved feature; preference alone is insufficient.
Do not change the project's language, core runtime, or dependency stack, or introduce a new runtime, without prior maintainer agreement.
Keep each pull request focused on one issue or clearly scoped improvement and avoid bundling unrelated fixes, features, or refactors.

Files:

  • src/components/ModelPicker.tsx
  • src/utils/model/modelOptions.ts
  • src/components/ModelPicker.switchProfile.test.ts
  • src/utils/model/modelOptions.catalogDedup.test.ts

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/components/ModelPicker.tsx
  • src/utils/model/modelOptions.ts
  • src/components/ModelPicker.switchProfile.test.ts
  • src/utils/model/modelOptions.catalogDedup.test.ts
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}

⚙️ CodeRabbit configuration file

{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.

Files:

  • src/utils/model/modelOptions.ts
  • src/utils/model/modelOptions.catalogDedup.test.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}

⚙️ CodeRabbit configuration file

{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.

Files:

  • src/components/ModelPicker.switchProfile.test.ts
  • src/utils/model/modelOptions.catalogDedup.test.ts
🪛 ast-grep (0.45.0)
src/components/ModelPicker.switchProfile.test.ts

[warning] 88-88: Do not use Math.random() to generate security-sensitive values such as tokens, secrets, passwords, API keys, salts, nonces, OTPs, or session IDs. Math.random() is not cryptographically secure and is predictable. Use crypto.randomBytes()/crypto.randomUUID() (Node) or crypto.getRandomValues() (Web Crypto) instead.
Context: Math.random()
Note: [CWE-330] Use of Insufficiently Random Values.

(insecure-random-security-token-typescript)

src/utils/model/modelOptions.catalogDedup.test.ts

[warning] 93-93: Do not use Math.random() to generate security-sensitive values such as tokens, secrets, passwords, API keys, salts, nonces, OTPs, or session IDs. Math.random() is not cryptographically secure and is predictable. Use crypto.randomBytes()/crypto.randomUUID() (Node) or crypto.getRandomValues() (Web Crypto) instead.
Context: Math.random()
Note: [CWE-330] Use of Insufficiently Random Values.

(insecure-random-security-token-typescript)

🔇 Additional comments (5)
src/utils/model/modelOptions.ts (2)

941-962: LGTM!

Also applies to: 976-979


1078-1096: LGTM!

Also applies to: 1109-1115, 1133-1133, 1173-1176

src/utils/model/modelOptions.catalogDedup.test.ts (1)

93-116: LGTM!

Also applies to: 149-182, 184-313

src/components/ModelPicker.tsx (1)

14-14: LGTM!

Also applies to: 499-511

src/components/ModelPicker.switchProfile.test.ts (1)

30-75: LGTM!

Also applies to: 108-117, 151-184, 186-276

Comment thread src/components/ModelPicker.switchProfile.test.ts
Comment thread src/utils/model/modelOptions.catalogDedup.test.ts
Comment thread src/utils/model/modelOptions.ts

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@kevincodex1 kevincodex1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nice catch! thank you

@kevincodex1
kevincodex1 merged commit 248424f into Twigpine:main Aug 6, 2026
5 checks passed
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.

fix: /model picker lags badly with many provider models

3 participants