Skip to content

feat: add OpenRouter model selector and configurable fallback provider#2014

Open
luiggibcn wants to merge 34 commits into
AndyMik90:developfrom
luiggibcn:feat/openrouter-model-selector
Open

feat: add OpenRouter model selector and configurable fallback provider#2014
luiggibcn wants to merge 34 commits into
AndyMik90:developfrom
luiggibcn:feat/openrouter-model-selector

Conversation

@luiggibcn
Copy link
Copy Markdown

@luiggibcn luiggibcn commented Apr 25, 2026

Base Branch

  • This PR targets the `develop` branch (required for all feature/fix PRs)

Description

Adds a searchable combobox for selecting OpenRouter models from the live API, and replaces the fragile hardcoded slash-prefix list in `MODEL_PROVIDER_MAP` with a user-configurable fallback provider setting.

Also addresses:

Related Issue

Closes #1988
Ref #1950 (bundling fix already in develop via 35bb86d)

Type of Change

  • ✨ New feature

Area

  • Frontend
  • Fullstack

Commit Message Format

Follow conventional commits: `: `

Types: feat, fix, docs, style, refactor, test, chore

Example: `feat: add user authentication system`

AI Disclosure

  • This PR includes AI-generated code (Claude, Codex, Copilot, etc.)

Tool(s) used: Claude Code
Testing level:

  • Untested -- AI output not yet verified

  • Lightly tested -- ran the app / spot-checked key paths

  • Fully tested -- all tests pass, manually verified behavior

  • I understand what this PR does and how the underlying code works

Checklist

  • I've synced with `develop` branch
  • I've tested my changes locally
  • I've followed the code principles (SOLID, DRY, KISS)
  • My PR is small and focused (< 400 lines ideally)

Platform Testing Checklist

  • Windows tested (either on Windows or via CI)
  • macOS tested (either on macOS or via CI)
  • Linux tested (CI covers this)
  • Used centralized `platform/` module instead of direct `process.platform` checks
  • No hardcoded paths (used `findExecutable()` or platform abstractions)

CI/Testing Requirements

  • All CI checks pass on all platforms (Windows, macOS, Linux)
  • All existing tests pass
  • New features include test coverage
  • Bug fixes include regression tests

Screenshots

New Task — Model Selector

Before After
Static dropdown with only Claude models Searchable combobox with all OpenRouter models
Before - New Task After - New Task

Settings → Accounts

Before After
No fallback provider option New "Fallback Provider" selector at the top
Before - Accounts After - Accounts

Feature Toggle

  • N/A - Feature is complete and ready for all users

Breaking Changes

Breaking: No

Summary by CodeRabbit

Release Notes

  • New Features

    • Added configurable fallback provider for ambiguous model identifiers
    • Integrated OpenRouter model discovery and selection
    • Enhanced model picker UI with improved performance and dynamic loading
  • Bug Fixes

    • Fixed model detection to properly support non-Anthropic providers
    • Corrected AI message handling for non-Anthropic models to prevent compatibility errors
  • Chores

    • Updated localization strings for settings UI

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 25, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds OpenRouter model discovery and caching, a provider-aware ProviderModelCombobox with virtualized combobox rendering, configurable settings-driven fallback provider for slash-form model IDs, IPC/preload/type updates for OpenRouter listing, provider-detection/auth adjustment, and sanitization of assistant messages for non-Anthropic providers.

Changes

Cohort / File(s) Summary
Provider detection & auth
apps/desktop/src/main/agent/agent-manager.ts, apps/desktop/src/main/ai/providers/factory.ts, apps/desktop/src/main/ai/providers/__tests__/factory.test.ts
Detect provider from model IDs now accepts an optional fallbackProvider and reads a cached fallbackProviderId from settings; added resetFallbackProviderCache(); legacy auth resolution uses detected provider instead of hard-coded Anthropic; tests cover prefix vs slash vs settings-driven fallback.
OpenRouter IPC & main handler
apps/desktop/src/shared/constants/ipc.ts, apps/desktop/src/main/ipc-handlers/memory-handlers.ts, apps/desktop/src/shared/types/ipc.ts, apps/desktop/src/preload/api/project-api.ts
Added OPENROUTER_LIST_MODELS IPC channel, main handler that fetches OpenRouter /api/v1/models, typed ElectronAPI.listOpenRouterModels(), and preload ProjectAPI wiring to invoke it.
OpenRouter model hook & exports
apps/desktop/src/renderer/hooks/useOpenRouterModels.ts, apps/desktop/src/renderer/hooks/index.ts
New cached preload API preloadOpenRouterModels() and hook useOpenRouterModels() that normalizes, caches, and exposes OpenRouter model options and loading/error state; re-exported from hooks barrel.
Renderer API & mocks
apps/desktop/src/renderer/lib/browser-mock.ts, apps/desktop/src/shared/types/ipc.ts
Added mock listOpenRouterModels to browser mock and updated IPC typings for the new method.
ProviderModelCombobox UI & tests
apps/desktop/src/renderer/components/ui/ProviderModelCombobox.tsx, apps/desktop/src/renderer/components/ui/__tests__/ProviderModelCombobox.test.tsx
Introduced ProviderModelCombobox to render provider-aware model selection (static Select for known providers, dynamic Combobox for OpenRouter/Ollama/unknown), handles preload/abort and exposes expected props; added tests covering rendering and side effects.
Combobox virtualization & API
apps/desktop/src/renderer/components/ui/combobox.tsx
Replaced per-option refs with @tanstack/react-virtual virtualization, added allowCustomValue and isLoading props, extended filtering to include option value, adjusted keyboard navigation/aria and scroll behavior, and rebuilt options rendering with virtual rows.
Agent profile UI consolidation
apps/desktop/src/renderer/components/AgentProfileSelector.tsx
Removed inline provider/model fetching and per-phase custom-model UI; unified model selection to ProviderModelCombobox.
Settings UI & types
apps/desktop/src/shared/types/settings.ts, apps/desktop/src/renderer/components/settings/ProviderAccountsList.tsx
Added AppSettings.fallbackProviderId?: string; settings UI now exposes a Select to persist fallbackProviderId (defaults to openrouter).
Localization & model prefix tests
apps/desktop/src/main/ai/config/__tests__/types.test.ts, apps/desktop/src/shared/i18n/locales/en/settings.json, apps/desktop/src/shared/i18n/locales/fr/settings.json
Added tests for native model-prefix detection and ensured MODEL_PROVIDER_MAP keys are prefix-style; added English/French strings for fallback provider UI and small punctuation fixes.
Stream sanitization for non-Anthropic
apps/desktop/src/main/ai/session/runner.ts
Preprocesses initialMessages for non-Anthropic models to strip/convert reasoning/redacted-reasoning parts to avoid provider BadRequest errors; new helper implements filtering and fallback content.
Misc / config
apps/desktop/tsconfig.json
Set compilerOptions.ignoreDeprecations: "5.0".

Sequence Diagram(s)

sequenceDiagram
  participant UI as Renderer UI
  participant Combobox as ProviderModelCombobox
  participant Hook as useOpenRouterModels
  participant Preload as Preload (electronAPI)
  participant Main as Main Process
  participant OR as OpenRouter API

  UI->>Combobox: mount / user opens picker
  Combobox->>Hook: preloadOpenRouterModels() (if provider == openrouter)
  Hook->>Preload: window.electronAPI.listOpenRouterModels()
  Preload->>Main: ipcRenderer.invoke(OPENROUTER_LIST_MODELS)
  Main->>OR: HTTP GET /api/v1/models
  OR-->>Main: return models [{id,name},...]
  Main-->>Preload: IPCResult { success: true, data:{models} }
  Preload-->>Hook: models
  Hook-->>Combobox: cached options + isLoading/isError
  Combobox-->>UI: render options (virtualized)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

area/frontend, size/XL

Poem

🐰 I nibble keys and cache the trees,
I preload models with nimble ease,
Combobox hops on virtual rows,
I guard the slash where fallback goes—
Hooray, the settings saved with breeze!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: adding an OpenRouter model selector and implementing a configurable fallback provider feature.
Linked Issues check ✅ Passed The PR fully addresses #1988 by implementing sanitizeReasoningFromMessages() in session/runner.ts to strip reasoning/redacted-reasoning content parts before sending to non-Claude providers, preventing BadRequestError when switching providers mid-session.
Out of Scope Changes check ✅ Passed All changes are directly related to the PR objectives: OpenRouter integration (combobox, model listing, fallback provider UI/settings), thinking-blocks compatibility, and supporting infrastructure (IPC, types, i18n, tests). No unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate 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 and usage tips.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

🎉 Thanks for your first PR!

A maintainer will review it soon. Please make sure:

  • Your branch is synced with develop
  • CI checks pass
  • You've followed our contribution guide

Welcome to the Auto Claude community!

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a more flexible model selection system, specifically adding support for slash-format model IDs (e.g., 'provider/model') and a configurable fallback provider. Key changes include the implementation of a virtualized ProviderModelCombobox component that supports dynamic fetching for OpenRouter and freeform input, as well as updates to the provider detection logic to utilize application settings. Review feedback identifies a regression where Ollama models are no longer dynamically listed in the new selector, potential performance issues due to synchronous disk I/O when retrieving settings, and a recommendation for more robust string handling in the OpenRouter model hook.

Comment thread apps/desktop/src/renderer/components/ui/ProviderModelCombobox.tsx Outdated
Comment thread apps/desktop/src/renderer/hooks/useOpenRouterModels.ts Outdated
Comment thread apps/desktop/src/main/ai/providers/factory.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

Caution

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

⚠️ Outside diff range comments (3)
apps/desktop/src/main/agent/agent-manager.ts (1)

189-195: 🧹 Nitpick | 🔵 Trivial

Semantically incorrect context passed to resolveAuth for non-Anthropic providers.

In the legacy fallback path (lines 189-195), when a slash-form requestedModel like "openai/gpt-4o" is used, detectProviderFromModel returns a non-Anthropic provider (typically OpenRouter), yet configDir is still sourced from the active Claude profile. Passing an Anthropic-specific configDir for a non-Anthropic provider is semantically inconsistent, even though configDir is not currently consumed for non-Anthropic providers (the OAuth resolver guards against this).

To improve clarity, either:

  • Null out configDir when the resolved provider is not 'anthropic'
  • Or skip passing configDir in this fallback path and let each resolver default to undefined
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/agent/agent-manager.ts` around lines 189 - 195, The
legacy fallback currently grabs configDir from
getClaudeProfileManager()->activeProfile and passes it into resolveAuth even
when detectProviderFromModel(requestedModel) yields a non-'anthropic' provider;
update the block so configDir is only passed when provider === 'anthropic'
(e.g., set const configDir = provider === 'anthropic' ? activeProfile?.configDir
: undefined or omit configDir from the resolveAuth call for non-anthropic
providers), leaving detectProviderFromModel and resolveAuth usage unchanged.
apps/desktop/src/main/ai/providers/factory.ts (1)

234-252: ⚠️ Potential issue | 🟡 Minor

JSDoc for detectProviderFromModel is now attached to getConfiguredFallbackProvider.

The block at lines 234–240 (Detects the provider for a model ID based on its prefix... @parammodelId...@returns ...) was originally documenting detectProviderFromModel, but the new getConfiguredFallbackProvider function was inserted directly under it. The JSDoc now describes a function that takes no modelId and returns a hardcoded SupportedProvider. Move the JSDoc to immediately precede detectProviderFromModel (line 254) and add a brief doc for getConfiguredFallbackProvider.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/ai/providers/factory.ts` around lines 234 - 252, The
JSDoc block currently above getConfiguredFallbackProvider belongs to
detectProviderFromModel; move that existing JSDoc so it immediately precedes
detectProviderFromModel (the function named detectProviderFromModel) and then
add a short, accurate JSDoc above getConfiguredFallbackProvider describing that
it reads settings to return the configured fallback provider or defaults to
SupportedProvider.OpenRouter, including mention that it may swallow errors; keep
both comments concise and aligned with each function signature.
apps/desktop/src/renderer/components/AgentProfileSelector.tsx (1)

281-318: ⚠️ Potential issue | 🟠 Major

Extend ProviderModelCombobox to fetch Ollama installed models.

Currently, when provider === 'ollama', the combobox renders without a model list since ALL_AVAILABLE_MODELS contains no Ollama entries. Users must type model names manually instead of selecting from installed models. The IPC infrastructure (window.electronAPI.listOllamaModels) exists and is already in use by OllamaModelManager and MultiProviderModelSelect; apply the same pattern here so Ollama users in agent profile configuration get the same model discovery as in settings.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/components/AgentProfileSelector.tsx` around lines
281 - 318, ProviderModelCombobox currently doesn't load Ollama-installed models,
so update ProviderModelCombobox to detect when props.provider === 'ollama' and
asynchronously fetch installed models using the existing IPC call
(window.electronAPI.listOllamaModels) or reuse the pattern in
OllamaModelManager/MultiProviderModelSelect; merge the returned model names into
the component's options (alongside ALL_AVAILABLE_MODELS entries), manage local
state for the dynamic options, handle loading/error states and cleanup on
unmount, and ensure the fetched list is used for both the agent profile usages
in AgentProfileSelector and the custom model combobox so Ollama users can select
installed models instead of typing them.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/main/ai/config/__tests__/types.test.ts`:
- Around line 68-71: The test silently passes when no matching prefix is found;
remove the conditional short-circuit and assert the prefix exists first, e.g.
replace the `if` with an assertion like `expect(prefix).toBeDefined()` and then
assert `MODEL_PROVIDER_MAP[prefix]` equals 'anthropic' so the test fails when
the `claude-` mapping is missing (referencing the MODEL_PROVIDER_MAP lookup and
the test case that computes `prefix` for 'claude-sonnet-4-6').

In `@apps/desktop/src/main/ai/providers/__tests__/factory.test.ts`:
- Around line 169-185: Add tests that exercise getConfiguredFallbackProvider by
mocking readSettingsFile from ../../settings-utils: call detectProviderFromModel
without passing the explicit fallbackProvider so it uses
getConfiguredFallbackProvider; assert that when readSettingsFile returns a
settings object with a valid fallbackProviderId the returned provider matches
it, when readSettingsFile returns an invalid id the provider defaults to
'openrouter', and when readSettingsFile throws the helper also defaults to
'openrouter'. Use the same model inputs as existing tests and reference
detectProviderFromModel and getConfiguredFallbackProvider when locating code to
update.
- Around line 170-184: The tests pass raw strings as the fallback provider;
replace those string literals with the SupportedProvider enum to ensure
type-safety and resilience to renames—update calls to detectProviderFromModel in
this file (e.g., the three it() blocks) to use SupportedProvider.OPENROUTER,
SupportedProvider.ANTHROPIC, SupportedProvider.OPENAI (or the matching enum
members) instead of 'openrouter', 'anthropic', 'openai' so the test signature
matches the function and uses the enum everywhere.

In `@apps/desktop/src/main/ai/providers/factory.ts`:
- Around line 244-247: The code silently ignores an invalid
settings.fallbackProviderId by returning the default provider; update the check
around settings?.fallbackProviderId and SupportedProvider so that when an id
exists but is not in Object.values(SupportedProvider) you record the
misconfiguration (use Sentry: add a breadcrumb or Sentry.captureMessage/error
with the invalid id and context; avoid console.log) and then fall back to the
default (e.g., OpenRouter) as before; reference the variables/settings:
settings, fallbackProviderId, SupportedProvider and the return path that casts
id as SupportedProvider so you can locate and modify that branch.
- Around line 241-252: getConfiguredFallbackProvider performs a synchronous disk
read and is invoked from detectProviderFromModel when model IDs use
slash-format; avoid this by either (A) memoizing the fallback value inside
getConfiguredFallbackProvider so it reads settings once and returns cached value
thereafter, or (B) thread the already-loaded fallbackProvider from higher-level
resolver code (e.g., resolveAuth) into detectProviderFromModel so
detectProviderFromModel no longer calls getConfiguredFallbackProvider
synchronously; update detectProviderFromModel signature to accept an optional
fallbackProvider and change callers to pass the preloaded value where available.

In `@apps/desktop/src/renderer/components/settings/ProviderAccountsList.tsx`:
- Around line 204-224: The visible label for the fallback Select is a plain <p>
and not programmatically associated with the Select, so screen readers won’t
announce it; update the Select markup by giving the SelectTrigger a stable id
(e.g. "fallback-provider-trigger") and add aria-labelledby on the Select (or the
SelectTrigger depending on your Select API) pointing to that id, or replace the
<p> label with the existing Label component and reference its id; update the
Select/SelectTrigger usage around fallbackProviderId and
handleFallbackProviderChange and ensure PROVIDER_REGISTRY mapping remains
unchanged.
- Around line 196-200: handleFallbackProviderChange currently awaits
saveSettings but swallows errors so users get no feedback; update the function
to wrap the saveSettings call in try/catch, and on error call the same
destructive toast used elsewhere (e.g., accounts.toast.settingsUpdateFailed with
tryAgain) to inform the user and preserve UI state. Locate
handleFallbackProviderChange and modify it to catch exceptions from
saveSettings, call the toast on failure, and keep the fallbackProviderId logic
unchanged.

In
`@apps/desktop/src/renderer/components/ui/__tests__/ProviderModelCombobox.test.tsx`:
- Around line 46-52: The test title and comment are inconsistent: when
ProviderModelCombobox is rendered with provider={undefined} it falls back to
AVAILABLE_MODELS and renders the static <Select> branch, not a freeform
combobox; update the test description and any inline comment to reflect that it
asserts the static Select branch (e.g., "renders a Select when provider is
undefined (falls back to AVAILABLE_MODELS)"), while keeping the render call to
ProviderModelCombobox and the existing assertion using
screen.getByRole('combobox') intact; refer to ProviderModelCombobox and
AVAILABLE_MODELS when renaming the test to avoid confusion.

In `@apps/desktop/src/renderer/components/ui/combobox.tsx`:
- Around line 67-78: The focusedIndex state (referenced as focusedIndex and
setFocusedIndex) can become stale when the user edits the search string (search)
and filteredOptions changes, so add a useEffect that watches trimmedSearch or
filteredOptions and resets focusedIndex to a safe value (e.g., 0 or -1) whenever
the filteredOptions result changes; update the logic used when opening (existing
open handling) to remain compatible with this reset so keyboard navigation
(Arrow keys/Enter) always targets the currently visible options.
- Around line 252-353: The component renders hardcoded user strings "Loading…"
and "Use" inside the Combobox rendering block (see isLoading branch and the
custom entry block) which must be internationalized; add two new props to the
Combobox component (e.g., loadingMessage and customValuePrefix) and use those
instead of the literals (falling back to sensible defaults), update all call
sites such as ProviderModelCombobox to pass translated values via
t('common:loading','Loading…') and t('common:combobox.use','Use'), and add the
corresponding keys to both en/*.json and fr/*.json translation files so
translations are available.

In `@apps/desktop/src/renderer/components/ui/ProviderModelCombobox.tsx`:
- Around line 39-43: ProviderModelCombobox currently calls useOpenRouterModels()
unconditionally which registers a subscriber on every mount; extract the
OpenRouter-specific logic into a small wrapper component (e.g.,
OpenRouterModelOptions) that calls useOpenRouterModels() and returns { options,
isLoading } and only render that wrapper when isOpenRouter is true; keep
preloadOpenRouterModels() call in the existing useEffect (or move it into the
wrapper if appropriate), and replace the original const { options:
openRouterOptions, isLoading: openRouterLoading } = useOpenRouterModels(); with
a conditional render/prop that uses the wrapper so hooks are only invoked in the
OpenRouter branch and other ProviderModelCombobox mounts won’t subscribe.
- Line 60: In ProviderModelCombobox replace the template-string class
composition on SelectTrigger (currently `${sizeClass} ${className ?? ''}`) with
the project's cn() helper to merge classes correctly (e.g. cn(sizeClass,
className)) so Tailwind merge and clsx behavior are preserved; update the same
pattern where SelectContent/other SelectTrigger usages appear (the similar
occurrence around the later Select instance at the other spot) to use cn() as
well, referencing the SelectTrigger component and the sizeClass and className
props to locate the changes.
- Line 35: ProviderModelCombobox.tsx uses the i18n keys "select.model",
"search.models", and "empty.models" via useTranslation in the 'common' namespace
but those keys are missing; add these keys to both en/common.json and
fr/common.json (under the "select", "search", and "empty" objects) with
appropriate translations so the calls in ProviderModelCombobox.tsx resolve
correctly for English and French users.

In `@apps/desktop/src/renderer/hooks/useOpenRouterModels.ts`:
- Around line 48-72: Add a refresh path to useOpenRouterModels so consumers can
clear error/stale cache and retry: implement a refresh() that sets cacheState =
'idle', clears cachedOptions = null, and then calls preloadOpenRouterModels()
(or the underlying fetchModels()) to re-run the load; expose refresh in the hook
return value alongside options/isLoading/isError and ensure
subscribers/rerendering behavior remains the same so UI updates after clearing
and reloading.
- Around line 14-41: The fetchModels function currently swallows errors in the
bare catch and duplicates the inline model type; update fetchModels to catch
(err) and log the error (e.g., using console.error or a logger) before setting
cacheState = 'error' and calling notify(), and extract the repeated inline type
({ id: string; name: string }) into a named type (e.g., OpenRouterModel) used in
the sort/map operations; reference the async function fetchModels, cacheState,
cachedOptions and the call window.electronAPI.listOpenRouterModels when making
these changes.

In `@apps/desktop/src/shared/types/settings.ts`:
- Around line 293-294: The fallbackProviderId field is currently a plain string;
tighten its type to the runtime-validated provider union by changing
fallbackProviderId?: string to fallbackProviderId?: BuiltinProvider (or the
SupportedProvider union) and update imports if needed; ensure places that
write/read this setting (e.g., ProviderAccountsList.tsx) use the new type and
that getConfiguredFallbackProvider()’s validation remains compatible with the
updated type.

---

Outside diff comments:
In `@apps/desktop/src/main/agent/agent-manager.ts`:
- Around line 189-195: The legacy fallback currently grabs configDir from
getClaudeProfileManager()->activeProfile and passes it into resolveAuth even
when detectProviderFromModel(requestedModel) yields a non-'anthropic' provider;
update the block so configDir is only passed when provider === 'anthropic'
(e.g., set const configDir = provider === 'anthropic' ? activeProfile?.configDir
: undefined or omit configDir from the resolveAuth call for non-anthropic
providers), leaving detectProviderFromModel and resolveAuth usage unchanged.

In `@apps/desktop/src/main/ai/providers/factory.ts`:
- Around line 234-252: The JSDoc block currently above
getConfiguredFallbackProvider belongs to detectProviderFromModel; move that
existing JSDoc so it immediately precedes detectProviderFromModel (the function
named detectProviderFromModel) and then add a short, accurate JSDoc above
getConfiguredFallbackProvider describing that it reads settings to return the
configured fallback provider or defaults to SupportedProvider.OpenRouter,
including mention that it may swallow errors; keep both comments concise and
aligned with each function signature.

In `@apps/desktop/src/renderer/components/AgentProfileSelector.tsx`:
- Around line 281-318: ProviderModelCombobox currently doesn't load
Ollama-installed models, so update ProviderModelCombobox to detect when
props.provider === 'ollama' and asynchronously fetch installed models using the
existing IPC call (window.electronAPI.listOllamaModels) or reuse the pattern in
OllamaModelManager/MultiProviderModelSelect; merge the returned model names into
the component's options (alongside ALL_AVAILABLE_MODELS entries), manage local
state for the dynamic options, handle loading/error states and cleanup on
unmount, and ensure the fetched list is used for both the agent profile usages
in AgentProfileSelector and the custom model combobox so Ollama users can select
installed models instead of typing them.
🪄 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

Run ID: 6b16ed81-144b-4903-ac22-044f2a3a8aaa

📥 Commits

Reviewing files that changed from the base of the PR and between cba7a02 and 7ed8140.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
📒 Files selected for processing (19)
  • apps/desktop/src/main/agent/agent-manager.ts
  • apps/desktop/src/main/ai/config/__tests__/types.test.ts
  • apps/desktop/src/main/ai/providers/__tests__/factory.test.ts
  • apps/desktop/src/main/ai/providers/factory.ts
  • apps/desktop/src/preload/api/project-api.ts
  • apps/desktop/src/renderer/components/AgentProfileSelector.tsx
  • apps/desktop/src/renderer/components/settings/ProviderAccountsList.tsx
  • apps/desktop/src/renderer/components/ui/ProviderModelCombobox.tsx
  • apps/desktop/src/renderer/components/ui/__tests__/ProviderModelCombobox.test.tsx
  • apps/desktop/src/renderer/components/ui/combobox.tsx
  • apps/desktop/src/renderer/hooks/index.ts
  • apps/desktop/src/renderer/hooks/useOpenRouterModels.ts
  • apps/desktop/src/renderer/lib/browser-mock.ts
  • apps/desktop/src/shared/constants/ipc.ts
  • apps/desktop/src/shared/i18n/locales/en/settings.json
  • apps/desktop/src/shared/i18n/locales/fr/settings.json
  • apps/desktop/src/shared/types/ipc.ts
  • apps/desktop/src/shared/types/settings.ts
  • apps/desktop/tsconfig.json

Comment thread apps/desktop/src/main/ai/config/__tests__/types.test.ts
Comment thread apps/desktop/src/main/ai/providers/__tests__/factory.test.ts
Comment thread apps/desktop/src/main/ai/providers/__tests__/factory.test.ts
Comment thread apps/desktop/src/main/ai/providers/factory.ts
Comment thread apps/desktop/src/main/ai/providers/factory.ts Outdated
Comment thread apps/desktop/src/renderer/components/ui/ProviderModelCombobox.tsx Outdated
Comment thread apps/desktop/src/renderer/components/ui/ProviderModelCombobox.tsx Outdated
Comment thread apps/desktop/src/renderer/hooks/useOpenRouterModels.ts
Comment thread apps/desktop/src/renderer/hooks/useOpenRouterModels.ts
Comment thread apps/desktop/src/shared/types/settings.ts Outdated
@luiggibcn
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
apps/desktop/src/main/ai/providers/factory.ts (1)

247-256: ⚠️ Potential issue | 🟡 Minor

Invalid fallbackProviderId and read failures are still silently swallowed.

Both the catch block (line 254-256) and the id validation branch (line 250) silently fall through to OpenRouter without surfacing the misconfiguration. This was previously raised; flagging again because the current implementation does not record the invalid id or the read failure. Per coding guidelines, use Sentry (e.g., Sentry.captureMessage / breadcrumb) so misconfigurations and transient read errors are observable in production.

As per coding guidelines: "Use Sentry for error tracking in production; reserve console.log for development only."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/ai/providers/factory.ts` around lines 247 - 256, The
code swallows both settings read failures and invalid fallbackProviderId values;
update the try/catch around readSettingsFile() and the validation branch for id
to report issues to Sentry (e.g., call Sentry.captureMessage or add a
breadcrumb) with a clear message including the raw id and the error, then fall
back to the default as before; specifically, in the catch for readSettingsFile()
call Sentry.captureMessage("Failed to read settings file", { extra: { error } })
(or similar) and in the branch where id is present but not in
Object.values(SupportedProvider) call Sentry.captureMessage("Invalid
fallbackProviderId", { extra: { fallbackProviderId: id } }) before falling
through, while still using _fallbackProviderCache when id is valid.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/main/ai/providers/factory.ts`:
- Around line 244-259: The settings:save handler fails to invalidate the
process-lifetime fallback provider cache, so getConfiguredFallbackProvider() can
return a stale value (or the default cached after a transient readSettingsFile()
error); fix it by invoking resetFallbackProviderCache() inside the settings:save
handler whenever the update includes fallbackProviderId (mirror how
resetMemoryService() is called for memory-related updates), ensuring settings
saves triggered from saveSettings() / ProviderAccountsList.tsx clear the cached
value so subsequent calls to getConfiguredFallbackProvider() will read the new
setting.
- Around line 275-277: detectProviderFromModel currently treats any modelId
containing '/' as a fallback provider (returning fallbackProvider ??
getConfiguredFallbackProvider()), which breaks Ollama matching for slash-named
local models; update the detection so that the slash-format fallback is only
applied when intended by callers—either by adding an optional flag (e.g.,
allowSlashFallback = true) to detectProviderFromModel and have
resolveAuthFromQueue call it with allowSlashFallback = false, or by changing
resolveAuthFromQueue to detect only explicit native prefixes (claude-, gpt-,
etc.) instead of relying on detectProviderFromModel's slash behavior; ensure
symbols involved are detectProviderFromModel, resolveAuthFromQueue,
fallbackProvider, and getConfiguredFallbackProvider so Ollama (slash-named)
models resolve to undefined/inconclusive in the resolver branch.

In `@apps/desktop/src/renderer/components/ui/ProviderModelCombobox.tsx`:
- Line 5: Replace the relative import of useOpenRouterModels and
preloadOpenRouterModels with the project path-alias import; in
ProviderModelCombobox.tsx update the import that currently references
'../../hooks/useOpenRouterModels' to use the alias '@hooks/useOpenRouterModels'
so the symbols useOpenRouterModels and preloadOpenRouterModels are imported via
the tsconfig path alias.

---

Duplicate comments:
In `@apps/desktop/src/main/ai/providers/factory.ts`:
- Around line 247-256: The code swallows both settings read failures and invalid
fallbackProviderId values; update the try/catch around readSettingsFile() and
the validation branch for id to report issues to Sentry (e.g., call
Sentry.captureMessage or add a breadcrumb) with a clear message including the
raw id and the error, then fall back to the default as before; specifically, in
the catch for readSettingsFile() call Sentry.captureMessage("Failed to read
settings file", { extra: { error } }) (or similar) and in the branch where id is
present but not in Object.values(SupportedProvider) call
Sentry.captureMessage("Invalid fallbackProviderId", { extra: {
fallbackProviderId: id } }) before falling through, while still using
_fallbackProviderCache when id is valid.
🪄 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

Run ID: 678e0a88-5fe9-4d06-92cd-2eebc29218b4

📥 Commits

Reviewing files that changed from the base of the PR and between 7ed8140 and a9d4357.

📒 Files selected for processing (3)
  • apps/desktop/src/main/agent/agent-manager.ts
  • apps/desktop/src/main/ai/providers/factory.ts
  • apps/desktop/src/renderer/components/ui/ProviderModelCombobox.tsx

Comment thread apps/desktop/src/main/ai/providers/factory.ts Outdated
Comment thread apps/desktop/src/main/ai/providers/factory.ts
Comment thread apps/desktop/src/renderer/components/ui/ProviderModelCombobox.tsx Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
apps/desktop/src/main/ai/providers/__tests__/factory.test.ts (1)

175-219: 🧹 Nitpick | 🔵 Trivial

Optional: reset the fallback cache at suite scope to harden against future test pollution.

_fallbackProviderCache is module-level state populated by the preceding getConfiguredFallbackProvider describe. Current tests in this block all pass an explicit fallbackProvider, so cache contents are bypassed via ??. However, any future test added here that omits the explicit fallback would silently inherit cached state from a prior describe and become order-dependent. Consider hoisting resetFallbackProviderCache() (and mockReadSettingsFile.mockReset()) into a top-level beforeEach for the file.

♻️ Proposed harden-up
+beforeEach(() => {
+  resetFallbackProviderCache();
+  mockReadSettingsFile.mockReset();
+});
+
 describe('createProvider', () => {

…and the per-describe beforeEach at lines 149-152 can then be removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/ai/providers/__tests__/factory.test.ts` around lines
175 - 219, Tests rely on module-level _fallbackProviderCache populated by
getConfiguredFallbackProvider; to avoid order-dependent failures, add a
top-level beforeEach in the test file that calls resetFallbackProviderCache()
and mockReadSettingsFile.mockReset() so every test starts with a clean cache and
mock state, and remove the now-redundant per-describe beforeEach blocks that
reset the same state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/desktop/src/renderer/components/ui/__tests__/ProviderModelCombobox.test.tsx`:
- Around line 87-88: Remove the redundant global stub and ensure proper test
isolation: delete the vi.stubGlobal('electronAPI', ...) call and rely only on
Object.defineProperty(window, 'electronAPI', { value: { listOllamaModels },
writable: true }); or, if you prefer stubbing globals, call
vi.unstubAllGlobals() in an afterEach to restore state (or enable
unstubAllGlobals in config). Also make the assertion robust by awaiting the
async effect update with waitFor(() =>
expect(listOllamaModels).toHaveBeenCalled()) after render to avoid React act
warnings related to the component's IIFE effect and subsequent setOllamaLoading
state update.

---

Outside diff comments:
In `@apps/desktop/src/main/ai/providers/__tests__/factory.test.ts`:
- Around line 175-219: Tests rely on module-level _fallbackProviderCache
populated by getConfiguredFallbackProvider; to avoid order-dependent failures,
add a top-level beforeEach in the test file that calls
resetFallbackProviderCache() and mockReadSettingsFile.mockReset() so every test
starts with a clean cache and mock state, and remove the now-redundant
per-describe beforeEach blocks that reset the same state.
🪄 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

Run ID: 1a4be53a-fa59-48c9-b67a-c31d75383afc

📥 Commits

Reviewing files that changed from the base of the PR and between a9d4357 and f0e6206.

📒 Files selected for processing (2)
  • apps/desktop/src/main/ai/providers/__tests__/factory.test.ts
  • apps/desktop/src/renderer/components/ui/__tests__/ProviderModelCombobox.test.tsx

Comment thread apps/desktop/src/renderer/components/ui/__tests__/ProviderModelCombobox.test.tsx Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/main/ipc-handlers/memory-handlers.ts`:
- Around line 577-597: The OpenRouter IPC handler
(IPC_CHANNELS.OPENROUTER_LIST_MODELS) currently registered inline in
memory-handlers.ts should be moved into a new domain module file (e.g.,
openrouter-handlers.ts) and exposed via an exported function
registerOpenRouterHandlers(); copy the ipcMain.handle(...) block that performs
the fetch/JSON parsing/error handling into that module, export
registerOpenRouterHandlers which registers the handler, then remove the original
ipcMain.handle(...) from memory-handlers.ts and call
registerOpenRouterHandlers() from the same initialization site where
registerMemoryHandlers() is invoked so the handler is registered alongside other
domain handlers.
- Around line 587-588: The parsing of the OpenRouter response blindly assumes
json.data is an array and then calls .map, which can throw if the shape is
different; update the handler that defines json and models so it validates the
shape (e.g., use Array.isArray(json.data)) before mapping — if json.data is not
an array, treat it as an empty array or throw a meaningful Error so the existing
catch produces a clear message; ensure you update the creation of models (the
variable named models) to use the guarded array instead of json.data directly.
- Around line 581-597: The handler registered for
IPC_CHANNELS.OPENROUTER_LIST_MODELS uses fetch without a timeout and can hang
indefinitely; update the async handler to use an AbortController with a bounded
timeout (create or reuse a timeout constant like OPENROUTER_TIMEOUT_MS or
OLLAMA_TIMEOUT_MS), pass controller.signal into
fetch('https://openrouter.ai/api/v1/models', { signal }), and clear the timeout
when complete; ensure the catch handles AbortError distinctly (or returns the
same failure shape) so the IPC call always resolves even on timeout.
🪄 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

Run ID: f47ab0b3-ba48-4acd-b150-d94ad3659467

📥 Commits

Reviewing files that changed from the base of the PR and between f0e6206 and 567bfdf.

📒 Files selected for processing (1)
  • apps/desktop/src/main/ipc-handlers/memory-handlers.ts

Comment thread apps/desktop/src/main/ipc-handlers/memory-handlers.ts Outdated
Comment thread apps/desktop/src/main/ipc-handlers/memory-handlers.ts Outdated
Comment thread apps/desktop/src/main/ipc-handlers/memory-handlers.ts Outdated
luiggibcn pushed a commit to luiggibcn/Aperant that referenced this pull request Apr 25, 2026
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
luiggibcn and others added 7 commits April 25, 2026 09:53
- Add ProviderModelCombobox component for selecting OpenRouter models with search
- Add useOpenRouterModels hook to fetch available models from OpenRouter API
- Add fallbackProviderId setting: user-configurable provider for ambiguous
  slash-format model IDs (e.g. "provider/model"), defaults to openrouter
- Remove hardcoded slash-prefix entries from MODEL_PROVIDER_MAP; detection
  is now generic via detectProviderFromModel() with user-controlled fallback
- Add fallback provider selector UI in the Accounts settings section
- Add i18n keys (en/fr) for the new fallback provider setting
- Update tests to reflect new MODEL_PROVIDER_MAP structure and slash routing

Closes AndyMik90#1950
Closes AndyMik90#1988

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Only pass configDir to resolveAuth when provider is anthropic
- Cache fallback provider read from disk (avoid sync I/O on every call)
- Export resetFallbackProviderCache() for settings change invalidation
- Restore Ollama dynamic model listing in ProviderModelCombobox

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Test getConfiguredFallbackProvider via detectProviderFromModel:
  valid provider id, invalid id, readSettingsFile throws, no id set
- Test ProviderModelCombobox Ollama: renders combobox, calls
  listOllamaModels on mount, skips call for non-Ollama providers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Registers the ipcMain handler for OPENROUTER_LIST_MODELS that fetches
the public model list from openrouter.ai/api/v1/models and returns it
to the renderer for the ProviderModelCombobox dynamic list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add sanitizeReasoningFromMessages() in session/runner.ts: strips
  reasoning/redacted-reasoning content parts from message history
  before sending to non-Claude providers, preventing BadRequestError
  when task switches from Claude (extended thinking) to Fireworks,
  Together, or any provider that rejects thinking blocks (AndyMik90#1988)
- Add JSDoc to useOpenRouterModels, fetchModels, notify functions
- Add JSDoc to AgentProfileSelector and ProviderAccountsList components

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Non-Claude provider: strips reasoning parts, preserves text parts
- Claude provider: passes reasoning parts through unchanged
- redacted-reasoning parts stripped for non-Claude providers
- String content messages unaffected regardless of provider

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@luiggibcn luiggibcn force-pushed the feat/openrouter-model-selector branch from 148b5a5 to cbd331a Compare April 25, 2026 07:53
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/main/ai/session/runner.ts`:
- Around line 662-671: The current messages.map transform replaces assistant
turns that only contain 'reasoning'/'redacted-reasoning' parts with { type:
'text', text: '' }, which some providers reject; update the logic in the
messages.map handler in runner.ts so that when (msg.role === 'assistant' &&
Array.isArray(msg.content)) and filtered.length === 0 you either (A) omit the
message entirely from the returned array (e.g., map -> filter pattern: return
null for that msg and then filter(Boolean)), or (B) replace it with a non-empty
placeholder text (e.g., { type: 'text', text: '[redacted]' }) to ensure
downstream providers accept it; adjust the return to use filtered when
filtered.length > 0 and apply the chosen omission/placeholder behavior
consistently.
- Around line 337-343: The Anthropic detection using isAnthropicModel =
modelId?.startsWith('claude-') incorrectly treats Claude models routed via
Bedrock/OpenRouter/Vertex as non-Anthropic and causes
sanitizeReasoningFromMessages to strip reasoning blocks; update session
detection to rely on a provider/capability flag instead of modelId prefix by
adding a resolvedProvider or supportsReasoningBlocks field to SessionConfig
(propagate it from the auth layer) and change the logic in runner.ts to check
that flag (replace references to isAnthropicModel/modelId with the new
SessionConfig.resolvedProvider or SessionConfig.supportsReasoningBlocks) so
reasoning/redacted-reasoning parts are preserved for supported providers and
avoid creating empty-text fallbacks that some providers may reject.
🪄 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

Run ID: 5efee793-d336-4058-95f2-646c8ef292ff

📥 Commits

Reviewing files that changed from the base of the PR and between 567bfdf and aceeb32.

⛔ Files ignored due to path filters (4)
  • apps/desktop/docs/screenshots/pr-2014/after_new_task.png is excluded by !**/*.png
  • apps/desktop/docs/screenshots/pr-2014/after_settings_accounts.png is excluded by !**/*.png
  • apps/desktop/docs/screenshots/pr-2014/before_new_task.png is excluded by !**/*.png
  • apps/desktop/docs/screenshots/pr-2014/before_settings_accounts.png is excluded by !**/*.png
📒 Files selected for processing (4)
  • apps/desktop/src/main/ai/session/runner.ts
  • apps/desktop/src/renderer/components/AgentProfileSelector.tsx
  • apps/desktop/src/renderer/components/settings/ProviderAccountsList.tsx
  • apps/desktop/src/renderer/hooks/useOpenRouterModels.ts

Comment thread apps/desktop/src/main/ai/session/runner.ts
Comment thread apps/desktop/src/main/ai/session/runner.ts
luiggibcn and others added 9 commits April 25, 2026 10:13
Some providers reject messages with empty content arrays. Replace the
empty text fallback with '[redacted]' when all content parts in an
assistant message are reasoning/redacted-reasoning blocks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move the stray JSDoc from above getConfiguredFallbackProvider to the
correct position above detectProviderFromModel. Add proper JSDoc to
getConfiguredFallbackProvider describing its caching behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace hardcoded 'Select model…', 'Search models…', 'No models found'
strings with proper i18n keys (common:modelCombobox.*) in en and fr.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Change fallbackProviderId from string to BuiltinProvider in Settings type
- Wrap saveSettings call in try/catch with destructive toast on failure
- Add settings:errors.saveFailed i18n key (en/fr)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…romModel tests

- Replace raw string literals with SupportedProvider enum values
- Add beforeEach that resets fallback provider cache and settings mock
  to prevent cross-suite contamination from module-level cache state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract the OPENROUTER_LIST_MODELS handler from memory-handlers.ts into
its own registerOpenRouterHandlers() function in openrouter-handlers.ts.
Register it alongside other handlers in ipc-handlers/index.ts.
Add OpenRouterModel interface for the parsed API response shape.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exposes a refresh() function that clears the module-level cache and
retries the fetch — useful when the user wants to retry after a network
error without reloading the app.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace template string class concatenation with cn(sizeClass, className)
for consistent Tailwind class merging across the component.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The conditional if(prefix) silently passed when no matching prefix was
found, masking regressions. Replace with explicit toBeDefined() assertion
so the test fails loudly if the claude- mapping is ever removed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@luiggibcn
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 25, 2026

Only users with a collaborator, contributor, member, or owner role can interact with CodeRabbit.

luiggibcn and others added 16 commits April 25, 2026 12:56
Guard json.data with Array.isArray before calling .map to avoid a
confusing TypeError if OpenRouter returns an unexpected envelope.
Filter out entries with a missing id field for extra safety.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dels

- Surface the underlying error in the catch block via console.error
  instead of silently swallowing it
- Guard against empty providerSlug (e.g. model ID starts with '/')
  by falling back to 'Other' group label

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Log a console.warn when fallbackProviderId is set to an unknown
SupportedProvider value, and when readSettingsFile throws, instead of
silently ignoring both cases. Falls back to openrouter in both paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add id="fallback-provider-label" to the <p> label and
aria-labelledby on the SelectTrigger so screen readers announce
the correct label when the control receives focus.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename 'renders a combobox when provider is undefined (freeform)' to
  accurately reflect that undefined falls back to the anthropic static
  Select, not a freeform Combobox
- Remove vi.stubGlobal('electronAPI', ...) which set globalThis.electronAPI
  instead of window.electronAPI; the Object.defineProperty call below it
  was already setting the correct target

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevents the IPC handler from hanging indefinitely if the openrouter.ai
API is slow or unresponsive. Uses AbortController + clearTimeout in
finally to avoid timer leaks on both success and error paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ox wrapper

Extract OpenRouterCombobox so useOpenRouterModels() is only called when
provider === 'openrouter', preventing unnecessary subscriber registrations
on Anthropic/Ollama/unknown phase selectors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…anslation internally

Keeps the generic UI primitive free of i18n concerns — consistent with
how emptyMessage, placeholder and searchPlaceholder are already handled
as caller-provided strings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…st resolution

vitest had @/ → src/ while tsconfig has @/* → src/renderer/*. Aligning
them allows @/hooks/useOpenRouterModels to resolve correctly in tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
factory.ts statically imported settings-utils, which imports { app } from
'electron'. Worker threads don't have access to app, so module evaluation
failed with "does not provide an export named 'app'" whenever a task moved
to in_progress and spawned a worker.

Replace the static import with an injectable _settingsReader (default no-op,
safe in any runtime). The main process wires it up via configureSettingsReader
at startup; worker threads keep the no-op because they already receive the
provider resolved in workerData.

Also:
- FactorySettings type instead of Record<string,unknown> — catches typos at
  compile time and removes the 'as string' cast
- isMainThread canary: warns if the main process forgets to call
  configureSettingsReader before the slash-format path is exercised
- Tests: global beforeEach resets cache + reader before every test in the
  file; describe-level beforeEach only injects the mock it needs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_fallbackProviderCache was never reset after the user changed
fallbackProviderId in the UI — the new value only took effect after
restarting the app. Call resetFallbackProviderCache() in the SETTINGS_SAVE
handler whenever fallbackProviderId is part of the incoming payload.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread apps/desktop/src/main/ai/providers/__tests__/factory.test.ts Fixed
luiggibcn and others added 2 commits April 27, 2026 14:55
Replace hardcoded "Use" string with a customValueLabel prop and pass
the translated value from ProviderModelCombobox.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.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.

Thinking blocks compatibility for non-Claude providers

3 participants