Skip to content

Add Mistral AI API provider support#1013

Merged
PeterDaveHello merged 1 commit into
ChatGPTBox-dev:masterfrom
PeterDaveHello:add-mistral-provider
Jul 19, 2026
Merged

Add Mistral AI API provider support#1013
PeterDaveHello merged 1 commit into
ChatGPTBox-dev:masterfrom
PeterDaveHello:add-mistral-provider

Conversation

@PeterDaveHello

@PeterDaveHello PeterDaveHello commented Jul 17, 2026

Copy link
Copy Markdown
Member

Summary

  • Add Mistral AI as a built-in OpenAI-compatible API provider.
  • Add mistral-medium-latest, mistral-small-latest, and mistral-large-latest, with Mistral Medium enabled by default.
  • Wire API-key setup, request routing, legacy preset mapping, and focused provider coverage.

Validation

  • npm test (795 tests passed)
  • npm run lint
  • npm run build

Manual browser smoke testing was not run because a browser extension runtime is not available in this environment.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Mistral API models, configuration defaults, provider mappings, built-in provider registration, legacy model resolution, OpenAI-compatible routing, API-key setup, and adapter tests are added.

Changes

Mistral API Provider

Layer / File(s) Summary
Mistral configuration and provider mappings
src/config/index.mjs, src/config/openai-provider-mappings.mjs, tests/unit/config/config-predicates.test.mjs
Adds Mistral model groups, model metadata, API-key configuration, default activation, provider mappings, and model-detection tests.
Mistral provider registration and resolution
src/services/apis/provider-registry.mjs, tests/unit/services/apis/provider-registry.test.mjs
Registers the enabled Mistral chat-completions provider and resolves legacy Mistral model names to it.
OpenAI-compatible routing and API-key setup
src/background/index.mjs, src/popup/sections/GeneralPart.jsx, tests/unit/services/apis/thin-adapters.test.mjs
Routes Mistral sessions through the OpenAI-compatible API, adds the Mistral key setup URL, and validates the Mistral request endpoint and authorization header.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Session
  participant ProviderRegistry
  participant OpenAICompatibleAPI
  participant MistralAPI
  Session->>ProviderRegistry: identify Mistral model and provider
  ProviderRegistry->>OpenAICompatibleAPI: route session as compatible
  OpenAICompatibleAPI->>MistralAPI: send chat-completions request
  MistralAPI-->>OpenAICompatibleAPI: return response
Loading

Possibly related PRs

Suggested reviewers: josstorer

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding Mistral AI API provider support.
✨ 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.

@kilo-code-bot

kilo-code-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (8 files in current PR diff)
  • src/background/index.mjs
  • src/config/index.mjs
  • src/config/openai-provider-mappings.mjs
  • src/popup/sections/GeneralPart.jsx
  • src/services/apis/provider-registry.mjs
  • tests/unit/config/config-predicates.test.mjs
  • tests/unit/services/apis/provider-registry.test.mjs
  • tests/unit/services/apis/thin-adapters.test.mjs

Note: This review covers the authoritative current PR diff (PR head vs master). The earlier incremental base (3cdbfd2) was far behind master, so the large provider-registry/config refactor shown in the local 3cdbfd2..HEAD range is already present on master and is not part of the current PR. The current PR is a clean, additive Mistral AI provider feature with focused unit tests; no new issues were found in the changed lines. mistralApiKey is correctly wired into LEGACY_API_KEY_FIELD_BY_PROVIDER_ID and its derived LEGACY_SECRET_KEY_TO_PROVIDER_ID, so legacy secret migration populates providerSecrets['mistral'] as expected.

Previous Review Summaries (13 snapshots, latest commit 3cdbfd2)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 3cdbfd2)

Status: No Issues Found | Recommendation: Merge

Previous CRITICAL at src/services/apis/provider-registry.mjs:776 (undeclared normalizedProviderId in the if (!provider) fallback) is resolved at HEAD 3cdbfd2. normalizedProviderId is declared at line 698 inside the customApiModelKeys block that spans through the fallback, so all references (780, 782, 793, 794, 805, 807) are in scope; for non-custom sessions the block is skipped and the function-level fallback runs. Verified via grep of current HEAD.

Incremental changes (this review)

  • src/popup/sections/api-modes-provider-utils.mjs:441isProviderReferencedByApiModes now uses areProviderIdsEquivalent(apiMode?.providerId, normalizedProviderId) instead of an exact normalized match, so canonical IDs (e.g. proxy-v1 vs Proxy_V1) match while legacy/other IDs do not. Correct and improves provider-reference detection.
  • tests/unit/popup/api-modes-provider-utils.test.mjs — adds a regression test asserting the canonical-equivalence behavior with correct expectations.

No new issues found in the changed code.

Files Reviewed (2 files in incremental diff)
  • src/popup/sections/api-modes-provider-utils.mjs - 0 issues
  • tests/unit/popup/api-modes-provider-utils.test.mjs - 0 issues

Previous review (commit dbada74)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 0
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
src/services/apis/provider-registry.mjs 776 Removing the const normalizedProviderId = normalizeProviderId(providerId) declaration at the start of the if (!provider) fallback block leaves normalizedProviderId undeclared there. It is still referenced at lines 780, 782, 793, 794, 805, and 807. The only remaining declaration (line 698) is scoped inside the customApiModelKeys block, so for non-custom sessions this fallback throws a ReferenceError and breaks provider resolution. Restore the declaration before line 776.
Files Reviewed (1 file in incremental diff)
  • src/services/apis/provider-registry.mjs - 1 issue (incremental change since prior review at 0f93a82)

Previous review (commit 0f93a82)

Status: No Issues Found | Recommendation: Merge

Incremental Review Notes (since 6237294)

Incremental diff re-verified against current HEAD 0f93a82. Changed files since the prior review:

  • src/config/index.mjs - Adds malformed custom-mode provider-id recovery: when a mode's providerId raw value normalizes to empty (e.g. " !!! ") but a secret is stored under that raw id, the id is cleared and (when no mode-level key exists) the secret is recovered via promoteCustomModeApiKeyToProvider, then the orphaned raw-keyed secret is deleted at the end of migration. Covered by three new unit tests (recover, conflict-preserve, selected-after-listed).
  • src/popup/sections/import-data-cleanup.mjs - importsProviderState now triggers when EITHER customOpenAIProviders OR providerSecrets is present (was AND). Covered by provider-only and secret-only import tests.
  • src/services/apis/provider-registry.mjs - Adds hasNormalizedProviderIdMatch helper used for both enabled and disabled custom-provider matching in the diagnostic; additive and correct.
  • src/popup/sections/api-modes-provider-utils.mjs - Reformats the continue / else if control flow inside getReferencedCustomProviderIdsFromSessions; behavior is functionally equivalent to the prior version (previously reviewed with 0 issues).
  • New/extended unit tests in tests/unit/config/migrate-user-config.test.mjs, tests/unit/services/apis/provider-registry.test.mjs, tests/unit/popup/api-modes-provider-utils.test.mjs, tests/unit/popup/import-data-cleanup.test.mjs.

No new defects introduced; all changed logic is internally consistent and covered by tests. The malformed-secret deletion is guarded by a value-equality check against providerSecretSnapshot, so it will not delete a raw-keyed secret that diverged from the migrated value.

Files Reviewed (7 files in incremental diff)
  • src/config/index.mjs - 0 issues
  • src/popup/sections/import-data-cleanup.mjs - 0 issues
  • src/services/apis/provider-registry.mjs - 0 issues
  • src/popup/sections/api-modes-provider-utils.mjs - 0 issues
  • tests/unit/config/migrate-user-config.test.mjs - 0 issues
  • tests/unit/services/apis/provider-registry.test.mjs - 0 issues
  • tests/unit/popup/api-modes-provider-utils.test.mjs - 0 issues

Previous review (commit 6237294)

Status: No Issues Found | Recommendation: Merge

Incremental Review Notes (since f4c3e30)

Incremental diff re-verified against current HEAD 6237294. Only two files changed since the prior review:

  • src/popup/sections/api-modes-provider-utils.mjs
  • tests/unit/popup/api-modes-provider-utils.test.mjs

The change resolves the previously flagged qodo finding (disabled-provider reference handling). It now keeps a disabled custom provider referenced in getReferencedCustomProviderIdsFromSessions when the session's providerId is equivalent to that provider (lines 579-587), includes disabled providers in legacy-URL matching only when ambiguous ID matches exist (lines 600-602), and adds a dedicated recovery loop for disabled ambiguous-ID candidates (lines 613-622). The logic is internally consistent with the existing areProviderIdsEquivalent / getProvidersMatchingSessionProviderId / getProvidersMatchingLegacySessionUrl helpers and is covered by three new unit tests (equivalent-id, label-recovery alias, and shared-URL candidates) that exercise both enabled and disabled states. No new defects introduced; no regressions in surrounding logic.

The prior review's remaining bot/provider-scope findings (secret-slot collision at provider-registry.mjs:39, Mistral label localization at config/index.mjs:244, Mistral preset count at config/index.mjs:781, and providerId canonicalization at config/index.mjs:1555) were outside this incremental diff and remain resolved/addressed as of the prior review; they are not duplicated here.

Files Reviewed (2 files in incremental diff)
  • src/popup/sections/api-modes-provider-utils.mjs - 0 issues
  • tests/unit/popup/api-modes-provider-utils.test.mjs - 0 issues

Previous review (commit f4c3e30)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (16 files)
  • src/background/index.mjs
  • src/config/index.mjs
  • src/config/openai-provider-mappings.mjs
  • src/popup/sections/ApiModes.jsx
  • src/popup/sections/GeneralPart.jsx
  • src/popup/sections/api-modes-provider-utils.mjs
  • src/popup/sections/import-data-cleanup.mjs
  • src/services/apis/provider-registry.mjs
  • src/utils/model-name-convert.mjs
  • tests/unit/config/config-predicates.test.mjs
  • tests/unit/config/migrate-mistral-provider.test.mjs
  • tests/unit/popup/api-modes-provider-utils.test.mjs
  • tests/unit/popup/import-data-cleanup.test.mjs
  • tests/unit/services/apis/provider-registry.test.mjs
  • tests/unit/services/apis/thin-adapters.test.mjs
  • tests/unit/utils/model-name-convert.test.mjs

Incremental Review Notes (vs 0bed9c2a)

Incremental diff re-verified against current HEAD f4c3e30. The changed-code scope (Mistral provider registration, mistralApiModelKeys group/aliases, mistralApiKey secret mapping, legacy-provider-ID resolution, and the migrateUserConfig secret-collision handling) is unchanged in substance from the previously reviewed revision and introduces no new defects.

  • The previously raised P1 (secret slot collision for a pre-existing mistral custom provider, comment 3609085829) was fixed in d240a40; the migration now snapshots pre-existing provider secrets, moves a colliding custom-provider secret to its renamed ID, and clears the newly reserved mistral built-in slot unless a distinct built-in secret is present. The fix is confirmed in migrateUserConfig (src/config/index.mjs:1257-1366) and covered by tests/unit/config/migrate-mistral-provider.test.mjs.
  • The prior kilo-code-bot suggestion at src/config/index.mjs:781 referenced a PR description claiming "enable all three"; the current PR body states "enable Mistral Medium by default", which matches the actual code (only mistralMediumLatest is added to activeApiModes). No discrepancy remains.
  • Unlocalized Mistral labels follow the existing provider/model-label convention (other providers like Kimi.Moonshot/DeepSeek likewise have no locale entries); the author's rationale (comment 3608997643) is consistent with codebase behavior, so no change is required.
  • Remaining bot suggestions (qodo disabled-provider reference handling at src/popup/sections/api-modes-provider-utils.mjs:600, Copilot thin-adapter test placeholder) are pre-existing/low-severity and were not part of this PR's Mistral scope; not duplicated here.

Previous review (commit 0bed9c2)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (16 files)
  • src/background/index.mjs
  • src/config/index.mjs
  • src/config/openai-provider-mappings.mjs
  • src/popup/sections/ApiModes.jsx
  • src/popup/sections/GeneralPart.jsx
  • src/popup/sections/api-modes-provider-utils.mjs
  • src/popup/sections/import-data-cleanup.mjs
  • src/services/apis/provider-registry.mjs
  • src/utils/model-name-convert.mjs
  • tests/unit/config/config-predicates.test.mjs
  • tests/unit/config/migrate-mistral-provider.test.mjs
  • tests/unit/popup/api-modes-provider-utils.test.mjs
  • tests/unit/popup/import-data-cleanup.test.mjs
  • tests/unit/services/apis/provider-registry.test.mjs
  • tests/unit/services/apis/thin-adapters.test.mjs
  • tests/unit/utils/model-name-convert.test.mjs

Incremental review vs previous baseline 38932bf. The changed-code scope (Mistral provider registration, legacyProviderIds resolution, secret migration, and equivalence helpers) is unchanged from the previously reviewed revision and introduces no new defects. All active inline comments from other reviewers were re-verified against current HEAD 0bed9c2a: legacy-ID comparisons are consistently normalized (stored normalized, compared against normalized IDs), and the Mistral adapter test uses a distinct 'mistral-key' value. No new issues to report.

Previous review (commit 38932bf)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (15 files)
  • src/background/index.mjs - register isUsingMistralApiModel and mark Mistral as OpenAI-compatible
  • src/config/index.mjs - Mistral provider IDs, presets, secret migration + legacyProviderIds machinery
  • src/config/openai-provider-mappings.mjs - Mistral legacy key + group mapping
  • src/popup/sections/GeneralPart.jsx - Mistral API key setup URL
  • src/popup/sections/api-modes-provider-utils.mjs - legacy-provider-id resolution in references/labels
  • src/popup/sections/import-data-cleanup.mjs - rerun builtin provider-ID migration on legacy imports
  • src/services/apis/provider-registry.mjs - Mistral builtin template, legacy-ID request resolution
  • src/utils/model-name-convert.mjs - legacy-provider-id selection logic
  • tests/unit/config/config-predicates.test.mjs
  • tests/unit/config/migrate-mistral-provider.test.mjs
  • tests/unit/popup/api-modes-provider-utils.test.mjs
  • tests/unit/popup/import-data-cleanup.test.mjs
  • tests/unit/services/apis/provider-registry.test.mjs
  • tests/unit/services/apis/thin-adapters.test.mjs
  • tests/unit/utils/model-name-convert.test.mjs

Notes

Incremental review against HEAD 38932bf. The previous review at d240a40 (Mistral secret migration) remains resolved and is now extended by the legacyProviderIds mechanism that keeps renamed/colliding custom-provider sessions linked via explicit legacy IDs and session-URL disambiguation. New code paths in provider-registry.mjs, api-modes-provider-utils.mjs, and model-name-convert.mjs are covered by extensive new unit tests. No new issues found in changed lines. Existing active inline comments from other reviewers (localization convention at config/index.mjs:244, raw-vs-normalized legacy-ID matching at provider-registry.mjs:39/45, and the mistral collision secret handling) are addressed in code and by tests in this revision.

Previous review (commit e6d54e9)

Status: No Issues Found | Recommendation: Merge

All previously raised findings remain resolved, and the incremental changes in this revision (d240a40) introduce no new issues.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 0
Files Reviewed (4 files in incremental diff)
  • src/config/index.mjs - Mistral builtin provider ID reservation + one-time secret migration; completedBuiltinProviderIdMigrations tracking; session provider-id remap; import re-run hook
  • src/popup/sections/import-data-cleanup.mjs - Reset completedBuiltinProviderIdMigrations on legacy provider-state imports so migration re-runs
  • tests/unit/config/migrate-mistral-provider.test.mjs - New comprehensive migration test suite (9 cases)
  • tests/unit/popup/import-data-cleanup.test.mjs - New import-cleanup migration tests

Notes

The new migration logic for reserving the mistral builtin provider ID (previously a valid custom provider id) is carefully bounded: it uses providerSecretSnapshot so decisions are based on pre-mutation secrets, distinguishes raw (Mistral) vs normalized (mistral) secret slots, preserves a real builtin mistralApiKey when it differs from a colliding custom secret, and only clears the builtin slot/legacy field when the value actually belonged to the custom provider. Ambiguous multi-rename resolution matches by canonicalized chat-completions URL. The behavior is covered by an extensive new test suite that also asserts idempotency on re-run.

Previous review (commit d240a40)

Status: No Issues Found | Recommendation: Merge

All previously raised findings remain resolved, and the incremental changes in this revision (d240a40) introduce no new issues.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 0
Files Reviewed (4 files in incremental diff)
  • src/config/index.mjs - Mistral builtin provider ID reservation + one-time secret migration; completedBuiltinProviderIdMigrations tracking; session provider-id remap; import re-run hook
  • src/popup/sections/import-data-cleanup.mjs - Reset completedBuiltinProviderIdMigrations on legacy provider-state imports so migration re-runs
  • tests/unit/config/migrate-mistral-provider.test.mjs - New comprehensive migration test suite (9 cases)
  • tests/unit/popup/import-data-cleanup.test.mjs - New import-cleanup migration tests

Notes

The new migration logic for reserving the mistral builtin provider ID (previously a valid custom provider id) is carefully bounded: it uses providerSecretSnapshot so decisions are based on pre-mutation secrets, distinguishes raw (Mistral) vs normalized (mistral) secret slots, preserves a real builtin mistralApiKey when it differs from a colliding custom secret, and only clears the builtin slot/legacy field when the value actually belonged to the custom provider. Ambiguous multi-rename resolution matches by canonicalized chat-completions URL. The behavior is covered by an extensive new test suite that also asserts idempotency on re-run.

Previous review (commit cc6b85f)

Status: No Issues Found | Recommendation: Merge

All previously raised findings have been resolved in the current revision (32d17df):

  • The Mistral default-enablement mismatch is now consistent — only mistralMediumLatest is added to activeApiModes (config/index.mjs:776), matching the updated PR description and the defaultConfig enables only Mistral AI Medium latest test.
  • The resolveProviderIdFromLegacyModelName Mistral branch now uses braces (provider-registry.mjs:122-124), matching surrounding style.
  • The Mistral adapter test now uses a provider-specific expectedApiKey: 'mistral-key' (thin-adapters.test.mjs:307).
Files Reviewed (8 files)
  • src/background/index.mjs
  • src/config/index.mjs
  • src/config/openai-provider-mappings.mjs
  • src/popup/sections/GeneralPart.jsx
  • src/services/apis/provider-registry.mjs
  • tests/unit/config/config-predicates.test.mjs
  • tests/unit/services/apis/provider-registry.test.mjs
  • tests/unit/services/apis/thin-adapters.test.mjs

Previous review (commit 32d17df)

Status: No Issues Found | Recommendation: Merge

All previously raised findings have been resolved in the current revision (32d17df):

  • The Mistral default-enablement mismatch is now consistent — only mistralMediumLatest is added to activeApiModes (config/index.mjs:776), matching the updated PR description and the defaultConfig enables only Mistral AI Medium latest test.
  • The resolveProviderIdFromLegacyModelName Mistral branch now uses braces (provider-registry.mjs:122-124), matching surrounding style.
  • The Mistral adapter test now uses a provider-specific expectedApiKey: 'mistral-key' (thin-adapters.test.mjs:307).
Files Reviewed (8 files)
  • src/background/index.mjs
  • src/config/index.mjs
  • src/config/openai-provider-mappings.mjs
  • src/popup/sections/GeneralPart.jsx
  • src/services/apis/provider-registry.mjs
  • tests/unit/config/config-predicates.test.mjs
  • tests/unit/services/apis/provider-registry.test.mjs
  • tests/unit/services/apis/thin-adapters.test.mjs

Previous review (commit 41a3dfc)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
src/config/index.mjs 776 PR description claims all three Mistral presets are enabled by default, but only mistralMediumLatest is added to activeApiModes (test asserts only Medium is enabled). Either enable all three or correct the description.
Files Reviewed (8 files)
  • src/background/index.mjs - 0 issues
  • src/config/index.mjs - 1 issue
  • src/config/openai-provider-mappings.mjs - 0 issues
  • src/popup/sections/GeneralPart.jsx - 0 issues
  • src/services/apis/provider-registry.mjs - 0 new issues (1 pre-existing Copilot comment on brace style at line 123)
  • tests/unit/config/config-predicates.test.mjs - 0 issues
  • tests/unit/services/apis/provider-registry.test.mjs - 0 issues
  • tests/unit/services/apis/thin-adapters.test.mjs - 0 new issues (1 pre-existing Copilot comment on test apiKey at line 58)

Fix these issues in Kilo Cloud

Previous review (commit 888c567)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (8 files)
  • src/background/index.mjs
  • src/config/index.mjs
  • src/config/openai-provider-mappings.mjs
  • src/popup/sections/GeneralPart.jsx
  • src/services/apis/provider-registry.mjs
  • tests/unit/config/config-predicates.test.mjs
  • tests/unit/services/apis/provider-registry.test.mjs
  • tests/unit/services/apis/thin-adapters.test.mjs

Reviewed by hy3:free · Input: 88.7K · Output: 9.9K · Cached: 1.9M

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Add built-in Mistral AI OpenAI-compatible provider and model presets

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Register Mistral AI as a built-in OpenAI-compatible provider with default enablement.
• Add Mistral -latest model aliases and route requests via existing provider registry.
• Wire API-key mapping, setup link, and unit coverage for Mistral selection and routing.
Diagram

graph TD
  CFG["Config: models & secrets"] --> PR["Provider registry"]
  UI["Popup: API setup"] --> PR --> CORE["OpenAI-compatible core"] --> MS{{"Mistral API"}}
  BG["Background: session routing"] --> OA["OpenAI API adapter"] --> PR
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Rely on Custom OpenAI Provider configuration only
  • ➕ No built-in provider registry changes required
  • ➕ Users can point to any Mistral-compatible endpoint immediately
  • ➖ Worse UX (manual setup vs one-click provider)
  • ➖ Harder to ensure correct defaults, links, and migrations
  • ➖ Less consistent test coverage for a commonly used provider
2. Fetch available Mistral models dynamically (e.g., /models)
  • ➕ Avoids hard-coding even the -latest aliases
  • ➕ Can surface new models without extension updates
  • ➖ Adds runtime dependency and failure modes to the picker experience
  • ➖ Requires caching/versioning decisions and UI changes
  • ➖ More complex to test and maintain than stable -latest aliases

Recommendation: Keep the PR’s approach: treat Mistral as a first-class built-in provider and use Mistral’s stable -latest aliases. This preserves the existing OpenAI-compatible execution path, minimizes ongoing model-ID churn, and provides a clean UX (provider selection + setup link) with targeted unit coverage.

Files changed (8) +113 / -0

Enhancement (4) +44 / -0
index.mjsInclude Mistral in OpenAI-compatible session detection +2/-0

Include Mistral in OpenAI-compatible session detection

• Extends the OpenAI-compatible API session predicate to recognize Mistral API-mode sessions. Ensures requests for Mistral presets route through the shared OpenAI-compatible API execution flow.

src/background/index.mjs

index.mjsAdd Mistral model group, presets, default enablement, and predicate +30/-0

Add Mistral model group, presets, default enablement, and predicate

• Introduces 'mistralApiModelKeys', adds three 'mistral-*-latest' preset entries, and adds 'mistralApiKey' to the default config. Enables all three Mistral presets by default and exports 'isUsingMistralApiModel' for routing/predicate checks.

src/config/index.mjs

GeneralPart.jsxAdd Mistral API key setup link in provider UI +2/-0

Add Mistral API key setup link in provider UI

• Adds a provider-specific setup URL for Mistral ('https://console.mistral.ai/api-keys'). This improves discoverability when users select Mistral as the provider in API mode.

src/popup/sections/GeneralPart.jsx

provider-registry.mjsRegister Mistral as a built-in provider and support legacy preset resolution +10/-0

Register Mistral as a built-in provider and support legacy preset resolution

• Adds a built-in provider definition for Mistral (base URL 'https://api.mistral.ai/v1', chat completions path '/chat/completions'). Extends legacy model-name preset parsing to resolve Mistral provider IDs for 'mistral*Latest' presets and group keys.

src/services/apis/provider-registry.mjs

Tests (3) +67 / -0
config-predicates.test.mjsAdd unit tests for Mistral model predicates and defaults +31/-0

Add unit tests for Mistral model predicates and defaults

• Adds coverage for 'isUsingMistralApiModel', verifies exported key list usage, ensures no duplicate picker entries, and asserts Mistral presets are enabled in 'defaultConfig.activeApiModes'. Protects predicate and configuration regressions.

tests/unit/config/config-predicates.test.mjs

provider-registry.test.mjsTest provider resolution and request building for Mistral +29/-0

Test provider resolution and request building for Mistral

• Adds tests validating Mistral provider resolution from legacy preset keys and from apiMode group selection. Confirms the resolved request URL and api key selection are correct, and that raw model IDs are not misclassified as legacy presets.

tests/unit/services/apis/provider-registry.test.mjs

thin-adapters.test.mjsAdd thin-adapter coverage for Mistral OpenAI-compatible routing +7/-0

Add thin-adapter coverage for Mistral OpenAI-compatible routing

• Adds Mistral to the adapter test matrix to ensure the correct base URL and Authorization header are used. Confirms the compat layer produces expected streaming output for a Mistral apiMode selection.

tests/unit/services/apis/thin-adapters.test.mjs

Other (1) +2 / -0
openai-provider-mappings.mjsMap Mistral provider ID to legacy API key field and apiMode group +2/-0

Map Mistral provider ID to legacy API key field and apiMode group

• Adds 'mistral' to legacy API-key field mapping ('mistralApiKey') and maps 'mistralApiModelKeys' to provider id 'mistral'. This allows provider secret migration and consistent provider resolution from apiMode group names.

src/config/openai-provider-mappings.mjs

@qodo-code-review

qodo-code-review Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Malformed providerId persists ✓ Resolved 🐞 Bug ≡ Correctness
Description
migrateUserConfig() only rewrites customApiModes[].providerId (and selected custom
apiMode.providerId) when normalizeProviderId() returns a non-empty value, so a legacy/imported ID
like "!!!" remains in storage and no longer matches any normalized provider entry. This can cause
resolveOpenAICompatibleRequest() to fail provider lookup (return null) for those modes and can keep
secrets attached to an unusable providerId string.
Code

src/config/index.mjs[R1554-1555]

      customApiModesDirty = true
    }
Evidence
The migration normalizes providers to generated IDs when the normalized form is empty, but does not
similarly clear/repair custom mode providerId values that normalize to empty; provider lookup then
compares against normalized provider IDs and returns null when no match exists.

src/config/index.mjs[1077-1089]
src/config/index.mjs[1549-1564]
src/config/index.mjs[1641-1651]
src/services/apis/provider-registry.mjs[394-403]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`migrateUserConfig()` canonicalizes `providerId` only when `normalizeProviderId(raw)` is truthy. For malformed-but-non-empty legacy/imported IDs whose canonical form is empty (e.g. `"!!!"`), the migration leaves `providerId` unchanged, but custom providers are normalized to generated IDs (e.g. `custom-provider-1`). This breaks later provider resolution because provider lookup relies on `normalizeProviderId(providerId)` matching a real provider.

### Issue Context
- Custom providers are normalized with `preferredId = originalId || custom-provider-N`, where `originalId` can be empty.
- Custom API modes (and selected custom mode) only rewrite `providerId` when the canonicalized id is non-empty, leaving malformed IDs in place.

### How to fix
1. In the `customApiModes` migration loop:
  - Detect the case: `existingProviderIdRaw` is non-empty but `normalizeProviderId(existingProviderIdRaw)` is empty.
  - Set `apiMode.providerId = ''` (or delete it) so the existing downstream logic can recover it from `customUrl` (or fall back to `legacy-custom-default`).
  - Additionally, ensure `promoteCustomModeApiKeyToProvider()` refuses to promote into an invalid/un-normalizable provider id (to avoid writing secrets into unusable keys).
2. Apply the same handling for the selected custom mode (`migrated.apiMode`) providerId canonicalization.

### Fix Focus Areas
- src/config/index.mjs[1549-1623]
- src/config/index.mjs[1641-1651]
- src/config/index.mjs[1501-1524]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Disabled provider refs dropped ✓ Resolved 🐞 Bug ≡ Correctness
Description
getReferencedCustomProviderIdsFromSessions() intentionally excludes disabled providers (and
URL-based matches default to excluding disabled), so the Providers UI can allow deleting a provider
that saved conversations still rely on for their display label.
Code

src/popup/sections/api-modes-provider-utils.mjs[R564-590]

+      const matchedProviders = getProvidersMatchingSessionProviderId(providers, providerId, {
+        includeDisabled: true,
+      })
+      if (matchedProviders.length === 1) {
+        const matchedProviderId = normalizeText(matchedProviders[0]?.id)
+        if (
+          matchedProviders[0]?.enabled !== false &&
+          matchedProviderId &&
+          matchedProviderId !== 'legacy-custom-default'
+        ) {
+          referencedProviderIds.add(matchedProviderId)
        }
        continue
      }
+      if (matchedProviders.length > 1) {
+        ambiguousProviderIdMatches = matchedProviders
+      }
      if (!(Array.isArray(providers) ? providers : []).length) {
        referencedProviderIds.add(providerId)
        continue
      }
    }

-    const matchedByCustomUrl = getProvidersMatchingLegacySessionUrl(providers, session)
+    const recoveryProviders =
+      ambiguousProviderIdMatches.length > 0 ? ambiguousProviderIdMatches : providers
+    const matchedByCustomUrl = getProvidersMatchingLegacySessionUrl(recoveryProviders, session)
    if (matchedByCustomUrl.length > 0) {
Evidence
Provider deletion is gated by whether a provider ID appears in the set returned from
getReferencedCustomProviderIdsFromSessions(). That function currently drops disabled providers
from the set, while display logic explicitly resolves disabled providers for labels, so deletion can
remove a provider still needed for historical display names.

src/popup/sections/api-modes-provider-utils.mjs[553-599]
src/popup/sections/ApiModes.jsx[204-225]
src/popup/sections/api-modes-provider-utils.mjs[627-662]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`getReferencedCustomProviderIdsFromSessions()` is used to determine whether a provider delete action should be disabled. The function currently omits disabled providers from the returned referenced-ID set (even when the session uniquely matches that provider), and it also matches by legacy customUrl without `includeDisabled`, so disabled providers can be treated as unreferenced.

This creates an inconsistency with the display logic (`getApiModeDisplayLabel`) which intentionally keeps disabled providers displayable; deleting the provider then degrades historical conversation labels.

## Issue Context
- `ApiModes.jsx` uses `getReferencedCustomProviderIdsFromSessions()` to disable provider deletion when referenced by saved conversations.
- `getApiModeDisplayLabel()` explicitly matches providers with `{ includeDisabled: true }`, so disabled providers are still considered relevant for display.

## Fix Focus Areas
- src/popup/sections/api-modes-provider-utils.mjs[553-623]
- src/popup/sections/api-modes-provider-utils.mjs[496-551]

## What to change
1. In `getReferencedCustomProviderIdsFromSessions()`, when `matchedProviders.length === 1`, add the matched provider ID to `referencedProviderIds` even if `enabled === false`.
2. When calling `getProvidersMatchingLegacySessionUrl(...)` from `getReferencedCustomProviderIdsFromSessions()`, pass `{ includeDisabled: true }` so URL-based matching can also retain disabled-provider references.
3. If you want label-based recovery to also preserve disabled-provider references, update `getProviderIdsMatchingSessionLabel(...)` to not filter out disabled providers (or add an option similar to the other helpers and call it with `includeDisabled: true`).

## Acceptance criteria
- A saved conversation that references a disabled provider causes `sessionReferencedProviderIds` to include that provider ID.
- Provider deletion is disabled when the provider is referenced by saved conversations, regardless of `enabled` state.
- Existing ambiguity/“fail closed” behavior for collisions remains unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Provider ID collision ✓ Resolved 🐞 Bug ☼ Reliability
Description
Adding the builtin provider ID mistral can force an existing custom OpenAI-compatible provider
with the same ID to be auto-renamed during config migration, while persisted sessions keep the old
apiMode.providerId. Those sessions can then fail OpenAI-compatible request resolution (or route to
the wrong provider) because request routing relies on apiMode.providerId for custom modes.
Code

src/services/apis/provider-registry.mjs[R38-45]

+  {
+    id: 'mistral',
+    name: 'Mistral AI',
+    baseUrl: 'https://api.mistral.ai/v1',
+    chatCompletionsPath: '/chat/completions',
+    builtin: true,
+    enabled: true,
+  },
Evidence
The PR introduces a builtin provider with ID mistral and maps the Mistral API-mode group to that
provider ID. Custom-provider creation reserves builtin IDs from those mappings, meaning mistral
becomes a reserved/builtin ID going forward. Config migration enforces unique provider IDs (renaming
on collisions), but session storage migration only canonicalizes model fields and does not remap
session.apiMode.providerId. OpenAI-compatible routing resolves custom-mode providers primarily
from session.apiMode.providerId, so stale provider IDs can prevent resolving the intended provider
after a rename.

src/services/apis/provider-registry.mjs[11-46]
src/config/openai-provider-mappings.mjs[1-34]
src/popup/sections/ApiModes.jsx[333-336]
src/config/index.mjs[1014-1033]
src/config/index.mjs[1088-1126]
src/config/index.mjs[1364-1376]
src/services/local-session.mjs[71-79]
src/services/apis/provider-registry.mjs[350-365]
src/services/apis/provider-registry.mjs[658-805]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new builtin provider uses ID `mistral`. Older installs could already have a *custom* provider with ID `mistral` (it wasn’t reserved before), and config migration will rename that custom provider to maintain uniqueness. Persisted sessions (stored in `Browser.storage.local.sessions`) are only canonicalized for model fields and do not remap `session.apiMode.providerId`, so sessions referencing the old provider ID can no longer resolve to the intended custom provider.

## Issue Context
- Custom provider IDs are reserved from `OPENAI_COMPATIBLE_GROUP_TO_PROVIDER_ID` values when creating new providers.
- Config migration ensures unique custom provider IDs and can rename providers when a collision occurs.
- Session migration currently does not rewrite `apiMode.providerId` to follow these renames.

## Fix Focus Areas
- src/config/openai-provider-mappings.mjs[1-34]
- src/config/index.mjs[1014-1033]
- src/config/index.mjs[1088-1126]
- src/config/index.mjs[1364-1376]
- src/services/local-session.mjs[71-79]
- src/services/apis/provider-registry.mjs[350-365]
- src/services/apis/provider-registry.mjs[658-805]

## Implementation notes
1. When config migration detects provider ID renames (you already build `providerIdRenameLookup`), also migrate stored sessions:
  - Read `sessions` from `Browser.storage.local`.
  - For each session where `session.apiMode.groupName === 'customApiModelKeys'` and `session.apiMode.providerId` matches an `oldId` in the rename map, rewrite it to the `newId`.
  - Persist the updated sessions only if changes were made.
2. Add a unit test that simulates:
  - Pre-migration custom provider `{ id: 'mistral', ... }` and a session with `apiMode.providerId: 'mistral'`.
  - Post-migration: provider renamed (e.g. `mistral-2`) and session providerId updated accordingly.
3. Consider a defensive fallback in request resolution for custom modes: if `apiMode.providerId` doesn’t match any enabled provider but a provider matches by saved legacy URL or by session label, prefer that and (optionally) repair `apiMode.providerId` on save.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. Provider ID logic duplicated ✗ Dismissed 🐞 Bug ⚙ Maintainability ⭐ New
Description
Provider ID normalization/equivalence is re-implemented in multiple places (e.g., model-name-convert
vs popup provider utils vs config/provider-registry), so any future tweak risks inconsistent
behavior across selection, migration, and request routing paths.
Code

src/utils/model-name-convert.mjs[R3-24]

+function normalizeProviderId(value) {
+  return String(value || '')
+    .trim()
+    .toLowerCase()
+    .replace(/[^a-z0-9]+/g, '-')
+    .replace(/^-+|-+$/g, '')
+}
+
+function areProviderIdsEquivalent(firstProviderId, secondProviderId) {
+  const normalizedFirstProviderId = normalizeProviderId(firstProviderId)
+  const normalizedSecondProviderId = normalizeProviderId(secondProviderId)
+  if (!normalizedFirstProviderId || !normalizedSecondProviderId) {
+    return String(firstProviderId || '').trim() === String(secondProviderId || '').trim()
+  }
+  return normalizedFirstProviderId === normalizedSecondProviderId
+}
+
+function normalizeProviderEndpointUrl(value) {
+  return String(value || '')
+    .trim()
+    .replace(/\/+$/, '')
+}
Evidence
The PR adds/uses provider-id equivalence in multiple modules with effectively the same algorithm,
while other subsystems (config migration and provider registry) also define their own provider-id
normalizers; this is direct duplication that can drift over time.

src/utils/model-name-convert.mjs[1-24]
src/popup/sections/api-modes-provider-utils.mjs[17-39]
src/config/index.mjs[983-1018]
src/services/apis/provider-registry.mjs[148-158]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Provider ID canonicalization helpers (`normalizeProviderId`, `areProviderIdsEquivalent`, and URL normalization used for comparisons) are duplicated across modules. This increases long-term risk because future fixes (e.g., changing allowed characters, whitespace handling, case-folding, or legacy-id behavior) can be applied in one place and accidentally omitted in another.

### Issue Context
The PR introduces a new copy of the provider-ID helpers in `src/utils/model-name-convert.mjs`, while `src/popup/sections/api-modes-provider-utils.mjs` also defines its own provider-ID normalization and now exports `areProviderIdsEquivalent`. There are additional, similar normalizers in config migration and provider registry.

### Fix Focus Areas
- src/utils/model-name-convert.mjs[1-24]
- src/popup/sections/api-modes-provider-utils.mjs[17-39]
- src/config/index.mjs[983-1018]
- src/services/apis/provider-registry.mjs[148-158]

### Suggested fix
1. Create a shared utility module (e.g. `src/utils/provider-id.mjs`) exporting:
  - `normalizeProviderId(value)`
  - `areProviderIdsEquivalent(a, b)`
  - (optionally) `normalizeEndpointUrlForCompare(url)` if you want URL-compare behavior consistent too.
2. Replace local implementations in the files above with imports from the shared module.
3. Keep the existing behavior identical (including the “fallback to raw string compare when one side normalizes to empty” rule), so tests remain valid.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit d6f21e2

Results up to commit cc6b85f


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Remediation recommended
1. Provider ID collision ✓ Resolved 🐞 Bug ☼ Reliability
Description
Adding the builtin provider ID mistral can force an existing custom OpenAI-compatible provider
with the same ID to be auto-renamed during config migration, while persisted sessions keep the old
apiMode.providerId. Those sessions can then fail OpenAI-compatible request resolution (or route to
the wrong provider) because request routing relies on apiMode.providerId for custom modes.
Code

src/services/apis/provider-registry.mjs[R38-45]

+  {
+    id: 'mistral',
+    name: 'Mistral AI',
+    baseUrl: 'https://api.mistral.ai/v1',
+    chatCompletionsPath: '/chat/completions',
+    builtin: true,
+    enabled: true,
+  },
Evidence
The PR introduces a builtin provider with ID mistral and maps the Mistral API-mode group to that
provider ID. Custom-provider creation reserves builtin IDs from those mappings, meaning mistral
becomes a reserved/builtin ID going forward. Config migration enforces unique provider IDs (renaming
on collisions), but session storage migration only canonicalizes model fields and does not remap
session.apiMode.providerId. OpenAI-compatible routing resolves custom-mode providers primarily
from session.apiMode.providerId, so stale provider IDs can prevent resolving the intended provider
after a rename.

src/services/apis/provider-registry.mjs[11-46]
src/config/openai-provider-mappings.mjs[1-34]
src/popup/sections/ApiModes.jsx[333-336]
src/config/index.mjs[1014-1033]
src/config/index.mjs[1088-1126]
src/config/index.mjs[1364-1376]
src/services/local-session.mjs[71-79]
src/services/apis/provider-registry.mjs[350-365]
src/services/apis/provider-registry.mjs[658-805]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new builtin provider uses ID `mistral`. Older installs could already have a *custom* provider with ID `mistral` (it wasn’t reserved before), and config migration will rename that custom provider to maintain uniqueness. Persisted sessions (stored in `Browser.storage.local.sessions`) are only canonicalized for model fields and do not remap `session.apiMode.providerId`, so sessions referencing the old provider ID can no longer resolve to the intended custom provider.

## Issue Context
- Custom provider IDs are reserved from `OPENAI_COMPATIBLE_GROUP_TO_PROVIDER_ID` values when creating new providers.
- Config migration ensures unique custom provider IDs and can rename providers when a collision occurs.
- Session migration currently does not rewrite `apiMode.providerId` to follow these renames.

## Fix Focus Areas
- src/config/openai-provider-mappings.mjs[1-34]
- src/config/index.mjs[1014-1033]
- src/config/index.mjs[1088-1126]
- src/config/index.mjs[1364-1376]
- src/services/local-session.mjs[71-79]
- src/services/apis/provider-registry.mjs[350-365]
- src/services/apis/provider-registry.mjs[658-805]

## Implementation notes
1. When config migration detects provider ID renames (you already build `providerIdRenameLookup`), also migrate stored sessions:
  - Read `sessions` from `Browser.storage.local`.
  - For each session where `session.apiMode.groupName === 'customApiModelKeys'` and `session.apiMode.providerId` matches an `oldId` in the rename map, rewrite it to the `newId`.
  - Persist the updated sessions only if changes were made.
2. Add a unit test that simulates:
  - Pre-migration custom provider `{ id: 'mistral', ... }` and a session with `apiMode.providerId: 'mistral'`.
  - Post-migration: provider renamed (e.g. `mistral-2`) and session providerId updated accordingly.
3. Consider a defensive fallback in request resolution for custom modes: if `apiMode.providerId` doesn’t match any enabled provider but a provider matches by saved legacy URL or by session label, prefer that and (optionally) repair `apiMode.providerId` on save.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 38932bf


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Remediation recommended
1. Disabled provider refs dropped ✓ Resolved 🐞 Bug ≡ Correctness
Description
getReferencedCustomProviderIdsFromSessions() intentionally excludes disabled providers (and
URL-based matches default to excluding disabled), so the Providers UI can allow deleting a provider
that saved conversations still rely on for their display label.
Code

src/popup/sections/api-modes-provider-utils.mjs[R564-590]

+      const matchedProviders = getProvidersMatchingSessionProviderId(providers, providerId, {
+        includeDisabled: true,
+      })
+      if (matchedProviders.length === 1) {
+        const matchedProviderId = normalizeText(matchedProviders[0]?.id)
+        if (
+          matchedProviders[0]?.enabled !== false &&
+          matchedProviderId &&
+          matchedProviderId !== 'legacy-custom-default'
+        ) {
+          referencedProviderIds.add(matchedProviderId)
        }
        continue
      }
+      if (matchedProviders.length > 1) {
+        ambiguousProviderIdMatches = matchedProviders
+      }
      if (!(Array.isArray(providers) ? providers : []).length) {
        referencedProviderIds.add(providerId)
        continue
      }
    }

-    const matchedByCustomUrl = getProvidersMatchingLegacySessionUrl(providers, session)
+    const recoveryProviders =
+      ambiguousProviderIdMatches.length > 0 ? ambiguousProviderIdMatches : providers
+    const matchedByCustomUrl = getProvidersMatchingLegacySessionUrl(recoveryProviders, session)
    if (matchedByCustomUrl.length > 0) {
Evidence
Provider deletion is gated by whether a provider ID appears in the set returned from
getReferencedCustomProviderIdsFromSessions(). That function currently drops disabled providers
from the set, while display logic explicitly resolves disabled providers for labels, so deletion can
remove a provider still needed for historical display names.

src/popup/sections/api-modes-provider-utils.mjs[553-599]
src/popup/sections/ApiModes.jsx[204-225]
src/popup/sections/api-modes-provider-utils.mjs[627-662]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`getReferencedCustomProviderIdsFromSessions()` is used to determine whether a provider delete action should be disabled. The function currently omits disabled providers from the returned referenced-ID set (even when the session uniquely matches that provider), and it also matches by legacy customUrl without `includeDisabled`, so disabled providers can be treated as unreferenced.

This creates an inconsistency with the display logic (`getApiModeDisplayLabel`) which intentionally keeps disabled providers displayable; deleting the provider then degrades historical conversation labels.

## Issue Context
- `ApiModes.jsx` uses `getReferencedCustomProviderIdsFromSessions()` to disable provider deletion when referenced by saved conversations.
- `getApiModeDisplayLabel()` explicitly matches providers with `{ includeDisabled: true }`, so disabled providers are still considered relevant for display.

## Fix Focus Areas
- src/popup/sections/api-modes-provider-utils.mjs[553-623]
- src/popup/sections/api-modes-provider-utils.mjs[496-551]

## What to change
1. In `getReferencedCustomProviderIdsFromSessions()`, when `matchedProviders.length === 1`, add the matched provider ID to `referencedProviderIds` even if `enabled === false`.
2. When calling `getProvidersMatchingLegacySessionUrl(...)` from `getReferencedCustomProviderIdsFromSessions()`, pass `{ includeDisabled: true }` so URL-based matching can also retain disabled-provider references.
3. If you want label-based recovery to also preserve disabled-provider references, update `getProviderIdsMatchingSessionLabel(...)` to not filter out disabled providers (or add an option similar to the other helpers and call it with `includeDisabled: true`).

## Acceptance criteria
- A saved conversation that references a disabled provider causes `sessionReferencedProviderIds` to include that provider ID.
- Provider deletion is disabled when the provider is referenced by saved conversations, regardless of `enabled` state.
- Existing ambiguity/“fail closed” behavior for collisions remains unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit f4c3e30


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Remediation recommended
1. Malformed providerId persists ✓ Resolved 🐞 Bug ≡ Correctness
Description
migrateUserConfig() only rewrites customApiModes[].providerId (and selected custom
apiMode.providerId) when normalizeProviderId() returns a non-empty value, so a legacy/imported ID
like "!!!" remains in storage and no longer matches any normalized provider entry. This can cause
resolveOpenAICompatibleRequest() to fail provider lookup (return null) for those modes and can keep
secrets attached to an unusable providerId string.
Code

src/config/index.mjs[R1554-1555]

      customApiModesDirty = true
    }
Evidence
The migration normalizes providers to generated IDs when the normalized form is empty, but does not
similarly clear/repair custom mode providerId values that normalize to empty; provider lookup then
compares against normalized provider IDs and returns null when no match exists.

src/config/index.mjs[1077-1089]
src/config/index.mjs[1549-1564]
src/config/index.mjs[1641-1651]
src/services/apis/provider-registry.mjs[394-403]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`migrateUserConfig()` canonicalizes `providerId` only when `normalizeProviderId(raw)` is truthy. For malformed-but-non-empty legacy/imported IDs whose canonical form is empty (e.g. `"!!!"`), the migration leaves `providerId` unchanged, but custom providers are normalized to generated IDs (e.g. `custom-provider-1`). This breaks later provider resolution because provider lookup relies on `normalizeProviderId(providerId)` matching a real provider.

### Issue Context
- Custom providers are normalized with `preferredId = originalId || custom-provider-N`, where `originalId` can be empty.
- Custom API modes (and selected custom mode) only rewrite `providerId` when the canonicalized id is non-empty, leaving malformed IDs in place.

### How to fix
1. In the `customApiModes` migration loop:
  - Detect the case: `existingProviderIdRaw` is non-empty but `normalizeProviderId(existingProviderIdRaw)` is empty.
  - Set `apiMode.providerId = ''` (or delete it) so the existing downstream logic can recover it from `customUrl` (or fall back to `legacy-custom-default`).
  - Additionally, ensure `promoteCustomModeApiKeyToProvider()` refuses to promote into an invalid/un-normalizable provider id (to avoid writing secrets into unusable keys).
2. Apply the same handling for the selected custom mode (`migrated.apiMode`) providerId canonicalization.

### Fix Focus Areas
- src/config/index.mjs[1549-1623]
- src/config/index.mjs[1641-1651]
- src/config/index.mjs[1501-1524]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

@PeterDaveHello
PeterDaveHello requested a review from Copilot July 17, 2026 19:33
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
putComment timed out

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

An error occurred during the review process. Please try again later.

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds Mistral AI as a built-in OpenAI-compatible API provider within the extension’s provider registry/config system, including model aliases and default enablement so users can select it from the existing API mode UI.

Changes:

  • Register a new built-in provider (mistral) with base URL routing and legacy-session provider resolution.
  • Add three Mistral “-latest” model presets, group them under mistralApiModelKeys, and enable them by default.
  • Wire provider-key mapping/setup link behavior and add focused unit test coverage for provider resolution + request construction.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/unit/services/apis/thin-adapters.test.mjs Adds an adapter-case verifying Mistral base URL + auth header propagation through the OpenAI-compat layer.
tests/unit/services/apis/provider-registry.test.mjs Adds unit coverage for provider ID resolution and request URL/key resolution for Mistral.
tests/unit/config/config-predicates.test.mjs Adds predicate + default-config assertions for the new Mistral model group/keys.
src/services/apis/provider-registry.mjs Registers built-in Mistral provider and supports resolving it from legacy model/session shapes.
src/popup/sections/GeneralPart.jsx Adds the Mistral API key setup link for the popup UI.
src/config/openai-provider-mappings.mjs Maps Mistral provider ID to its legacy key field and to the OpenAI-compatible apiMode group.
src/config/index.mjs Defines mistralApiModelKeys, model entries/aliases, default config key field, default enablement, and predicate.
src/background/index.mjs Treats Mistral sessions as OpenAI-compatible for background request routing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/services/apis/provider-registry.mjs
Comment thread tests/unit/services/apis/thin-adapters.test.mjs
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment thread src/config/index.mjs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 32d17df1e4

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread src/config/index.mjs
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 32d17df

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc6b85fe50

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread src/services/apis/provider-registry.mjs
Comment thread src/services/apis/provider-registry.mjs
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit cc6b85f

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Comment thread src/config/index.mjs
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit f4c3e30

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 6237294

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.

Comment thread src/popup/sections/api-modes-provider-utils.mjs Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 0f93a82

Comment thread src/services/apis/provider-registry.mjs
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit dbada74

Comment thread src/utils/model-name-convert.mjs Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 3cdbfd2

Register Mistral AI as a built-in OpenAI-compatible chat provider and expose current flagship model presets.

Wire shared API-key storage, request routing, setup links, and focused coverage for the Mistral API endpoint.
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🧹 Nitpick comments (2)
tests/unit/services/apis/provider-registry.test.mjs (1)

44-47: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Exercise all legacy Mistral preset keys.

The test name covers Mistral preset keys, but only mistralMediumLatest is asserted. Add mistralSmallLatest and mistralLargeLatest checks so every newly supported legacy preset is verified.

🤖 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 `@tests/unit/services/apis/provider-registry.test.mjs` around lines 44 - 47,
Update the resolveProviderIdForSession test to cover all newly supported legacy
Mistral presets by adding assertions for mistralSmallLatest and
mistralLargeLatest, while preserving the existing mistralMediumLatest and
custom-key checks.
tests/unit/config/config-predicates.test.mjs (1)

228-234: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the apiMode branch of the predicate.

This test only supplies modelName, while isUsingMistralApiModel prioritizes apiMode when present. Add assertions using { apiMode: { groupName: 'mistralApiModelKeys', itemName: modelName } } so a regression in session routing cannot pass unnoticed.

🤖 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 `@tests/unit/config/config-predicates.test.mjs` around lines 228 - 234, Extend
the isUsingMistralApiModel test to cover inputs containing apiMode, using
groupName 'mistralApiModelKeys' and each modelName as itemName, and assert they
return true. Also assert a non-Mistral apiMode value returns false, preserving
the existing modelName coverage.
🤖 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.

Nitpick comments:
In `@tests/unit/config/config-predicates.test.mjs`:
- Around line 228-234: Extend the isUsingMistralApiModel test to cover inputs
containing apiMode, using groupName 'mistralApiModelKeys' and each modelName as
itemName, and assert they return true. Also assert a non-Mistral apiMode value
returns false, preserving the existing modelName coverage.

In `@tests/unit/services/apis/provider-registry.test.mjs`:
- Around line 44-47: Update the resolveProviderIdForSession test to cover all
newly supported legacy Mistral presets by adding assertions for
mistralSmallLatest and mistralLargeLatest, while preserving the existing
mistralMediumLatest and custom-key checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9613f7c2-dbc5-4dfb-8fa1-a319fc2f013e

📥 Commits

Reviewing files that changed from the base of the PR and between cc6b85f and d6f21e2.

📒 Files selected for processing (8)
  • src/background/index.mjs
  • src/config/index.mjs
  • src/config/openai-provider-mappings.mjs
  • src/popup/sections/GeneralPart.jsx
  • src/services/apis/provider-registry.mjs
  • tests/unit/config/config-predicates.test.mjs
  • tests/unit/services/apis/provider-registry.test.mjs
  • tests/unit/services/apis/thin-adapters.test.mjs
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/popup/sections/GeneralPart.jsx
  • tests/unit/services/apis/thin-adapters.test.mjs
  • src/config/openai-provider-mappings.mjs
  • src/services/apis/provider-registry.mjs

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit d6f21e2

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

@PeterDaveHello
PeterDaveHello merged commit 3ed3d67 into ChatGPTBox-dev:master Jul 19, 2026
4 checks passed
@PeterDaveHello
PeterDaveHello deleted the add-mistral-provider branch July 19, 2026 17:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants