From 8b69a2b3610cd8829c4a0242260e970e42875f2a Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Thu, 30 Jul 2026 05:04:46 +0000 Subject: [PATCH 1/2] feat(ask-code): add MiniMax-M3 to the inline code Q&A catalog while retaining M2.7 Introduce a MiniMax model and endpoint catalog so the inline code Q&A provider is no longer pinned to a single model or a single endpoint. M3 is represented with its 1M-token context window, image and video input and adaptive/disabled thinking, while M2.7 is retained as a 204K text-only always-on model. Both the global and China regional endpoints and their protocol base URLs are exposed. The provider now derives its request URL and default model from the catalog. --- README.md | 2 +- electron/ipc/ask-code-minimax.test.ts | 2 +- electron/ipc/ask-code-minimax.ts | 9 ++- electron/ipc/minimax-catalog.test.ts | 85 ++++++++++++++++++++ electron/ipc/minimax-catalog.ts | 110 ++++++++++++++++++++++++++ src/components/SettingsDialog.tsx | 4 +- 6 files changed, 206 insertions(+), 6 deletions(-) create mode 100644 electron/ipc/minimax-catalog.test.ts create mode 100644 electron/ipc/minimax-catalog.ts diff --git a/README.md b/README.md index be457922..7346525b 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ - **See every session in one place** — switch context without losing momentum. - **Control everything keyboard-first** — every action has a shortcut, mouse optional. - **Monitor progress from your phone** — scan a QR code, watch agents work over Wi-Fi or Tailscale. -- **Ask about code with any LLM** — the inline code Q&A feature supports [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (default) or [MiniMax](https://www.minimax.io/) M2.7 (204K context) — configurable in Settings. +- **Ask about code with any LLM** — the inline code Q&A feature supports [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (default) or [MiniMax](https://www.minimax.io/) M3 (1M context, image and video input) with M2.7 also supported — configurable in Settings.
How does it compare? diff --git a/electron/ipc/ask-code-minimax.test.ts b/electron/ipc/ask-code-minimax.test.ts index 8a73d51a..4232b9f0 100644 --- a/electron/ipc/ask-code-minimax.test.ts +++ b/electron/ipc/ask-code-minimax.test.ts @@ -164,7 +164,7 @@ describe('askAboutCodeMinimax', () => { ); }); - it('uses MiniMax-M2.7 model', async () => { + it('uses the default MiniMax model from the catalog', async () => { const { win, messages } = makeMockWin(); mockFetch.mockResolvedValueOnce(makeStreamResponse('data: [DONE]\n\n')); diff --git a/electron/ipc/ask-code-minimax.ts b/electron/ipc/ask-code-minimax.ts index 0f262296..0d382928 100644 --- a/electron/ipc/ask-code-minimax.ts +++ b/electron/ipc/ask-code-minimax.ts @@ -8,6 +8,11 @@ import { assertCanStart, assertPromptWithinLimit, } from './request-registry.js'; +import { + DEFAULT_MINIMAX_MODEL_ID, + DEFAULT_MINIMAX_REGION, + minimaxChatCompletionsUrl, +} from './minimax-catalog.js'; interface MinimaxAskCodeRequest { requestId: string; @@ -15,8 +20,8 @@ interface MinimaxAskCodeRequest { prompt: string; } -const MINIMAX_API_URL = 'https://api.minimax.io/v1/chat/completions'; -export const MINIMAX_MODEL = 'MiniMax-M2.7'; +const MINIMAX_API_URL = minimaxChatCompletionsUrl(DEFAULT_MINIMAX_REGION); +export const MINIMAX_MODEL = DEFAULT_MINIMAX_MODEL_ID; const activeRequests = new RequestRegistry({ maxConcurrent: ASK_CODE_MAX_CONCURRENT, diff --git a/electron/ipc/minimax-catalog.test.ts b/electron/ipc/minimax-catalog.test.ts new file mode 100644 index 00000000..532183d2 --- /dev/null +++ b/electron/ipc/minimax-catalog.test.ts @@ -0,0 +1,85 @@ +import { describe, it, expect } from 'vitest'; +import { + DEFAULT_MINIMAX_MODEL_ID, + DEFAULT_MINIMAX_REGION, + MINIMAX_ENDPOINTS, + MINIMAX_MODELS, + getMinimaxEndpoint, + getMinimaxModel, + minimaxBaseUrl, + minimaxChatCompletionsUrl, + minimaxModelIds, +} from './minimax-catalog.js'; + +describe('minimax catalog models', () => { + it('retains M2.7 alongside M3', () => { + expect(minimaxModelIds()).toEqual(['MiniMax-M3', 'MiniMax-M2.7']); + }); + + it('defaults to M3', () => { + expect(DEFAULT_MINIMAX_MODEL_ID).toBe('MiniMax-M3'); + expect(getMinimaxModel(DEFAULT_MINIMAX_MODEL_ID)).toBeDefined(); + }); + + it('represents M3 with a 1M context window, image/video input and adaptive/disabled thinking', () => { + const m3 = getMinimaxModel('MiniMax-M3'); + expect(m3).toBeDefined(); + expect(m3?.contextWindow).toBe(1_000_000); + expect(m3?.inputModalities).toEqual(['text', 'image', 'video']); + expect(m3?.thinking).toEqual(['adaptive', 'disabled']); + }); + + it('represents M2.7 as a 204K text-only always-on model', () => { + const m27 = getMinimaxModel('MiniMax-M2.7'); + expect(m27).toBeDefined(); + expect(m27?.contextWindow).toBe(204_800); + expect(m27?.inputModalities).toEqual(['text']); + expect(m27?.thinking).toEqual(['always_on']); + }); + + it('does not clone the M2.7 profile onto M3', () => { + const m3 = getMinimaxModel('MiniMax-M3'); + const m27 = getMinimaxModel('MiniMax-M2.7'); + expect(m3?.contextWindow).not.toBe(m27?.contextWindow); + expect(m3?.inputModalities).not.toEqual(m27?.inputModalities); + }); + + it('returns undefined for unknown models', () => { + expect(getMinimaxModel('nope')).toBeUndefined(); + }); + + it('exposes exactly the catalogued models', () => { + expect(MINIMAX_MODELS).toHaveLength(2); + }); +}); + +describe('minimax catalog endpoints', () => { + it('exposes the global and China regions', () => { + expect(MINIMAX_ENDPOINTS.map((e) => e.region)).toEqual(['global_en', 'cn_zh']); + }); + + it('exposes both protocol base URLs for the global region', () => { + expect(minimaxBaseUrl('global_en', 'openai')).toBe('https://api.minimax.io/v1'); + expect(minimaxBaseUrl('global_en', 'anthropic')).toBe('https://api.minimax.io/anthropic'); + }); + + it('exposes both protocol base URLs for the China region', () => { + expect(minimaxBaseUrl('cn_zh', 'openai')).toBe('https://api.minimaxi.com/v1'); + expect(minimaxBaseUrl('cn_zh', 'anthropic')).toBe('https://api.minimaxi.com/anthropic'); + }); + + it('defaults to the global region', () => { + expect(DEFAULT_MINIMAX_REGION).toBe('global_en'); + }); + + it('builds the chat completions URL from the region base URL', () => { + expect(minimaxChatCompletionsUrl('global_en')).toBe( + 'https://api.minimax.io/v1/chat/completions', + ); + expect(minimaxChatCompletionsUrl('cn_zh')).toBe('https://api.minimaxi.com/v1/chat/completions'); + }); + + it('falls back to the global endpoint for the default region', () => { + expect(getMinimaxEndpoint(DEFAULT_MINIMAX_REGION).region).toBe('global_en'); + }); +}); diff --git a/electron/ipc/minimax-catalog.ts b/electron/ipc/minimax-catalog.ts new file mode 100644 index 00000000..6d59315a --- /dev/null +++ b/electron/ipc/minimax-catalog.ts @@ -0,0 +1,110 @@ +/** + * Catalog of the MiniMax models and regional API endpoints used by the inline + * "Ask about Code" provider. + * + * Centralizing this metadata keeps the provider from being pinned to a single + * model or a single endpoint: each model carries its own context window, input + * modalities and thinking modes, and each region exposes both of its + * supported protocol base URLs. + */ + +/** Regions where the MiniMax platform is reachable. */ +export type MinimaxRegion = 'global_en' | 'cn_zh'; + +/** Wire protocols each MiniMax endpoint speaks. */ +export type MinimaxProtocol = 'openai' | 'anthropic'; + +/** Input modalities a MiniMax model can accept. */ +export type MinimaxInputModality = 'text' | 'image' | 'video'; + +/** Thinking modes a MiniMax model supports. */ +export type MinimaxThinkingMode = 'adaptive' | 'disabled' | 'always_on'; + +export interface MinimaxModel { + /** Model identifier sent as the `model` field on the wire. */ + id: string; + /** Maximum number of tokens the model accepts in a single request. */ + contextWindow: number; + /** Input modalities the model understands. */ + inputModalities: MinimaxInputModality[]; + /** Thinking modes the model exposes. */ + thinking: MinimaxThinkingMode[]; +} + +export interface MinimaxEndpoint { + region: MinimaxRegion; + /** Base URL for the `openai` protocol; chat completions live under `${openaiBaseUrl}/chat/completions`. */ + openaiBaseUrl: string; + /** Base URL for the `anthropic` protocol. */ + anthropicBaseUrl: string; + /** Root of the platform documentation for this region. */ + docsRoot: string; +} + +/** + * Supported models, newest first. M3 carries a 1M-token context window and + * accepts image and video input with adaptive or disabled thinking; M2.7 is + * retained as a 204K-token text-only model with always-on thinking. + */ +export const MINIMAX_MODELS: MinimaxModel[] = [ + { + id: 'MiniMax-M3', + contextWindow: 1_000_000, + inputModalities: ['text', 'image', 'video'], + thinking: ['adaptive', 'disabled'], + }, + { + id: 'MiniMax-M2.7', + contextWindow: 204_800, + inputModalities: ['text'], + thinking: ['always_on'], + }, +]; + +/** Regional endpoints and the protocol base URLs each one exposes. */ +export const MINIMAX_ENDPOINTS: MinimaxEndpoint[] = [ + { + region: 'global_en', + openaiBaseUrl: 'https://api.minimax.io/v1', + anthropicBaseUrl: 'https://api.minimax.io/anthropic', + docsRoot: 'https://platform.minimax.io/docs', + }, + { + region: 'cn_zh', + openaiBaseUrl: 'https://api.minimaxi.com/v1', + anthropicBaseUrl: 'https://api.minimaxi.com/anthropic', + docsRoot: 'https://platform.minimaxi.com/docs', + }, +]; + +/** Default model used by the inline code Q&A provider. */ +export const DEFAULT_MINIMAX_MODEL_ID = 'MiniMax-M3'; + +/** Default region used by the inline code Q&A provider. */ +export const DEFAULT_MINIMAX_REGION: MinimaxRegion = 'global_en'; + +/** Model ids in the catalog, newest first. */ +export function minimaxModelIds(): string[] { + return MINIMAX_MODELS.map((model) => model.id); +} + +/** Looks up a model by id, or `undefined` when it is not in the catalog. */ +export function getMinimaxModel(id: string): MinimaxModel | undefined { + return MINIMAX_MODELS.find((model) => model.id === id); +} + +/** Returns the endpoint for a region, falling back to the first (global) one. */ +export function getMinimaxEndpoint(region: MinimaxRegion): MinimaxEndpoint { + return MINIMAX_ENDPOINTS.find((endpoint) => endpoint.region === region) ?? MINIMAX_ENDPOINTS[0]; +} + +/** Returns the base URL a region exposes for the given protocol. */ +export function minimaxBaseUrl(region: MinimaxRegion, protocol: MinimaxProtocol): string { + const endpoint = getMinimaxEndpoint(region); + return protocol === 'anthropic' ? endpoint.anthropicBaseUrl : endpoint.openaiBaseUrl; +} + +/** Builds the chat completions URL for a region. */ +export function minimaxChatCompletionsUrl(region: MinimaxRegion): string { + return `${minimaxBaseUrl(region, 'openai')}/chat/completions`; +} diff --git a/src/components/SettingsDialog.tsx b/src/components/SettingsDialog.tsx index 7b2f246f..bce4caf6 100644 --- a/src/components/SettingsDialog.tsx +++ b/src/components/SettingsDialog.tsx @@ -590,7 +590,7 @@ export function SettingsDialog(props: SettingsDialogProps) { }} > - + @@ -625,7 +625,7 @@ export function SettingsDialog(props: SettingsDialogProps) { {store.askCodeProvider === 'minimax' - ? 'Uses MiniMax M2.7 (204K context) via the OpenAI-compatible API — no Claude Code CLI required.' + ? 'Uses MiniMax M3 (1M context; text, image and video input), with M2.7 also supported — no local CLI required.' : 'Uses the claude CLI to answer questions about selected code. Requires Claude Code to be installed.'} From c8f6f2ff3d53d749b71c5c80b3d988ee7b669a2e Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Tue, 11 Aug 2026 19:34:07 +0800 Subject: [PATCH 2/2] fix(ask-code): wire MiniMax model selection --- README.md | 2 +- electron/ipc/ask-code-minimax.test.ts | 25 ++++++++++++++--- electron/ipc/ask-code-minimax.ts | 8 ++++-- electron/ipc/ask-code.ts | 5 ++-- electron/ipc/minimax-catalog.test.ts | 26 +++++++++--------- electron/ipc/minimax-catalog.ts | 39 ++++++++++++++++++++++----- electron/ipc/register.ts | 2 ++ src/components/AskCodeCard.tsx | 1 + src/components/SettingsDialog.tsx | 37 +++++++++++++++++++++++-- src/store/autosave.test.ts | 9 +++++++ src/store/autosave.ts | 2 ++ src/store/core.ts | 1 + src/store/persistence.test.ts | 32 ++++++++++++++++++++++ src/store/persistence.ts | 3 +++ src/store/store.ts | 1 + src/store/types.ts | 4 +++ src/store/ui.ts | 6 ++++- 17 files changed, 172 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 7346525b..fc427b74 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ - **See every session in one place** — switch context without losing momentum. - **Control everything keyboard-first** — every action has a shortcut, mouse optional. - **Monitor progress from your phone** — scan a QR code, watch agents work over Wi-Fi or Tailscale. -- **Ask about code with any LLM** — the inline code Q&A feature supports [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (default) or [MiniMax](https://www.minimax.io/) M3 (1M context, image and video input) with M2.7 also supported — configurable in Settings. +- **Ask about code with any LLM** — the inline code Q&A feature supports [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (default) or [MiniMax](https://www.minimax.io/) M3 and M2.7 for text-only code questions — configurable in Settings.
How does it compare? diff --git a/electron/ipc/ask-code-minimax.test.ts b/electron/ipc/ask-code-minimax.test.ts index 4232b9f0..d4b28811 100644 --- a/electron/ipc/ask-code-minimax.test.ts +++ b/electron/ipc/ask-code-minimax.test.ts @@ -7,7 +7,6 @@ vi.stubGlobal('fetch', mockFetch); import { askAboutCodeMinimax, cancelAskAboutCodeMinimax, - MINIMAX_MODEL, setMinimaxApiKey, } from './ask-code-minimax.js'; @@ -164,7 +163,7 @@ describe('askAboutCodeMinimax', () => { ); }); - it('uses the default MiniMax model from the catalog', async () => { + it('uses MiniMax-M3 by default', async () => { const { win, messages } = makeMockWin(); mockFetch.mockResolvedValueOnce(makeStreamResponse('data: [DONE]\n\n')); @@ -180,7 +179,27 @@ describe('askAboutCodeMinimax', () => { const body = JSON.parse((mockFetch.mock.calls[0][1] as RequestInit).body as string) as { model: string; }; - expect(body.model).toBe(MINIMAX_MODEL); + expect(body.model).toBe('MiniMax-M3'); + }); + + it('uses the selected MiniMax model', async () => { + const { win, messages } = makeMockWin(); + + mockFetch.mockResolvedValueOnce(makeStreamResponse('data: [DONE]\n\n')); + + askAboutCodeMinimax(win, { + requestId: 'r6-model', + channelId: 'ch6-model', + prompt: 'Test', + modelId: 'MiniMax-M2.7', + }); + + await waitForDone(messages); + + const body = JSON.parse((mockFetch.mock.calls[0][1] as RequestInit).body as string) as { + model: string; + }; + expect(body.model).toBe('MiniMax-M2.7'); }); it('uses temperature in MiniMax allowed range (0, 1]', async () => { diff --git a/electron/ipc/ask-code-minimax.ts b/electron/ipc/ask-code-minimax.ts index 0d382928..8347319a 100644 --- a/electron/ipc/ask-code-minimax.ts +++ b/electron/ipc/ask-code-minimax.ts @@ -11,6 +11,7 @@ import { import { DEFAULT_MINIMAX_MODEL_ID, DEFAULT_MINIMAX_REGION, + getMinimaxModel, minimaxChatCompletionsUrl, } from './minimax-catalog.js'; @@ -18,6 +19,7 @@ interface MinimaxAskCodeRequest { requestId: string; channelId: string; prompt: string; + modelId?: string; } const MINIMAX_API_URL = minimaxChatCompletionsUrl(DEFAULT_MINIMAX_REGION); @@ -36,8 +38,10 @@ export function setMinimaxApiKey(key: string): void { } export function askAboutCodeMinimax(win: BrowserWindow, args: MinimaxAskCodeRequest): void { - const { requestId, channelId, prompt } = args; + const { requestId, channelId, prompt, modelId } = args; const apiKey = storedApiKey; + const model = + getMinimaxModel(modelId ?? DEFAULT_MINIMAX_MODEL_ID)?.id ?? DEFAULT_MINIMAX_MODEL_ID; if (!apiKey) { throw new Error('MiniMax API key is not set. Please configure it in Settings.'); @@ -67,7 +71,7 @@ export function askAboutCodeMinimax(win: BrowserWindow, args: MinimaxAskCodeRequ Authorization: `Bearer ${apiKey}`, }, body: JSON.stringify({ - model: MINIMAX_MODEL, + model, messages: [ { role: 'system', diff --git a/electron/ipc/ask-code.ts b/electron/ipc/ask-code.ts index c6b1e867..2cb85a45 100644 --- a/electron/ipc/ask-code.ts +++ b/electron/ipc/ask-code.ts @@ -23,6 +23,7 @@ interface AskCodeRequest { prompt: string; cwd: string; provider?: AskCodeProvider; + model?: string; } const activeRequests = new RequestRegistry({ @@ -31,12 +32,12 @@ const activeRequests = new RequestRegistry({ }); export function askAboutCode(win: BrowserWindow, args: AskCodeRequest): void { - const { requestId, channelId, prompt, cwd, provider } = args; + const { requestId, channelId, prompt, cwd, provider, model } = args; // Route to MiniMax backend when configured if (provider === 'minimax') { activeRequests.cancel(requestId); - askAboutCodeMinimax(win, { requestId, channelId, prompt }); + askAboutCodeMinimax(win, { requestId, channelId, prompt, modelId: model }); return; } diff --git a/electron/ipc/minimax-catalog.test.ts b/electron/ipc/minimax-catalog.test.ts index 532183d2..57eaa032 100644 --- a/electron/ipc/minimax-catalog.test.ts +++ b/electron/ipc/minimax-catalog.test.ts @@ -3,7 +3,6 @@ import { DEFAULT_MINIMAX_MODEL_ID, DEFAULT_MINIMAX_REGION, MINIMAX_ENDPOINTS, - MINIMAX_MODELS, getMinimaxEndpoint, getMinimaxModel, minimaxBaseUrl, @@ -25,6 +24,12 @@ describe('minimax catalog models', () => { const m3 = getMinimaxModel('MiniMax-M3'); expect(m3).toBeDefined(); expect(m3?.contextWindow).toBe(1_000_000); + expect(m3?.pricingUsdPerMillionTokens).toEqual({ + input: 0.6, + output: 2.4, + cacheRead: 0.12, + cacheWrite: null, + }); expect(m3?.inputModalities).toEqual(['text', 'image', 'video']); expect(m3?.thinking).toEqual(['adaptive', 'disabled']); }); @@ -33,24 +38,19 @@ describe('minimax catalog models', () => { const m27 = getMinimaxModel('MiniMax-M2.7'); expect(m27).toBeDefined(); expect(m27?.contextWindow).toBe(204_800); + expect(m27?.pricingUsdPerMillionTokens).toEqual({ + input: 0.3, + output: 1.2, + cacheRead: 0.06, + cacheWrite: 0.375, + }); expect(m27?.inputModalities).toEqual(['text']); expect(m27?.thinking).toEqual(['always_on']); }); - it('does not clone the M2.7 profile onto M3', () => { - const m3 = getMinimaxModel('MiniMax-M3'); - const m27 = getMinimaxModel('MiniMax-M2.7'); - expect(m3?.contextWindow).not.toBe(m27?.contextWindow); - expect(m3?.inputModalities).not.toEqual(m27?.inputModalities); - }); - it('returns undefined for unknown models', () => { expect(getMinimaxModel('nope')).toBeUndefined(); }); - - it('exposes exactly the catalogued models', () => { - expect(MINIMAX_MODELS).toHaveLength(2); - }); }); describe('minimax catalog endpoints', () => { @@ -79,7 +79,7 @@ describe('minimax catalog endpoints', () => { expect(minimaxChatCompletionsUrl('cn_zh')).toBe('https://api.minimaxi.com/v1/chat/completions'); }); - it('falls back to the global endpoint for the default region', () => { + it('resolves the default global endpoint', () => { expect(getMinimaxEndpoint(DEFAULT_MINIMAX_REGION).region).toBe('global_en'); }); }); diff --git a/electron/ipc/minimax-catalog.ts b/electron/ipc/minimax-catalog.ts index 6d59315a..b0610969 100644 --- a/electron/ipc/minimax-catalog.ts +++ b/electron/ipc/minimax-catalog.ts @@ -2,10 +2,8 @@ * Catalog of the MiniMax models and regional API endpoints used by the inline * "Ask about Code" provider. * - * Centralizing this metadata keeps the provider from being pinned to a single - * model or a single endpoint: each model carries its own context window, input - * modalities and thinking modes, and each region exposes both of its - * supported protocol base URLs. + * Each model carries its context window, pricing, input modalities and + * thinking modes, and each region exposes both supported protocol base URLs. */ /** Regions where the MiniMax platform is reachable. */ @@ -20,11 +18,22 @@ export type MinimaxInputModality = 'text' | 'image' | 'video'; /** Thinking modes a MiniMax model supports. */ export type MinimaxThinkingMode = 'adaptive' | 'disabled' | 'always_on'; +export type MinimaxModelId = 'MiniMax-M3' | 'MiniMax-M2.7'; + +export interface MinimaxPricing { + input: number; + output: number; + cacheRead: number; + cacheWrite: number | null; +} + export interface MinimaxModel { /** Model identifier sent as the `model` field on the wire. */ - id: string; + id: MinimaxModelId; /** Maximum number of tokens the model accepts in a single request. */ contextWindow: number; + /** USD per million tokens. */ + pricingUsdPerMillionTokens: MinimaxPricing; /** Input modalities the model understands. */ inputModalities: MinimaxInputModality[]; /** Thinking modes the model exposes. */ @@ -50,12 +59,24 @@ export const MINIMAX_MODELS: MinimaxModel[] = [ { id: 'MiniMax-M3', contextWindow: 1_000_000, + pricingUsdPerMillionTokens: { + input: 0.6, + output: 2.4, + cacheRead: 0.12, + cacheWrite: null, + }, inputModalities: ['text', 'image', 'video'], thinking: ['adaptive', 'disabled'], }, { id: 'MiniMax-M2.7', contextWindow: 204_800, + pricingUsdPerMillionTokens: { + input: 0.3, + output: 1.2, + cacheRead: 0.06, + cacheWrite: 0.375, + }, inputModalities: ['text'], thinking: ['always_on'], }, @@ -77,6 +98,10 @@ export const MINIMAX_ENDPOINTS: MinimaxEndpoint[] = [ }, ]; +const MINIMAX_ENDPOINT_BY_REGION: Record = Object.fromEntries( + MINIMAX_ENDPOINTS.map((endpoint) => [endpoint.region, endpoint]), +) as Record; + /** Default model used by the inline code Q&A provider. */ export const DEFAULT_MINIMAX_MODEL_ID = 'MiniMax-M3'; @@ -93,9 +118,9 @@ export function getMinimaxModel(id: string): MinimaxModel | undefined { return MINIMAX_MODELS.find((model) => model.id === id); } -/** Returns the endpoint for a region, falling back to the first (global) one. */ +/** Returns the endpoint for a supported region. */ export function getMinimaxEndpoint(region: MinimaxRegion): MinimaxEndpoint { - return MINIMAX_ENDPOINTS.find((endpoint) => endpoint.region === region) ?? MINIMAX_ENDPOINTS[0]; + return MINIMAX_ENDPOINT_BY_REGION[region]; } /** Returns the base URL a region exposes for the given protocol. */ diff --git a/electron/ipc/register.ts b/electron/ipc/register.ts index aae2d88a..16ee631e 100644 --- a/electron/ipc/register.ts +++ b/electron/ipc/register.ts @@ -893,12 +893,14 @@ export function registerAllHandlers(win: BrowserWindow): void { validatePath(args.cwd, 'cwd'); const provider: string | undefined = typeof args.provider === 'string' ? args.provider : undefined; + const model: string | undefined = typeof args.model === 'string' ? args.model : undefined; askAboutCode(win, { requestId: args.requestId, channelId: args.onOutput.__CHANNEL_ID__, prompt: args.prompt, cwd: args.cwd, provider: provider === 'minimax' ? 'minimax' : 'claude', + model, }); }); diff --git a/src/components/AskCodeCard.tsx b/src/components/AskCodeCard.tsx index 4a952e1b..af69b089 100644 --- a/src/components/AskCodeCard.tsx +++ b/src/components/AskCodeCard.tsx @@ -64,6 +64,7 @@ export function AskCodeCard(props: AskCodeCardProps) { cwd: props.worktreePath, onOutput: channel, provider: store.askCodeProvider, + model: store.minimaxModel, }).catch((err: unknown) => { setError(errMessage(err)); setLoading(false); diff --git a/src/components/SettingsDialog.tsx b/src/components/SettingsDialog.tsx index bce4caf6..1ae6cf65 100644 --- a/src/components/SettingsDialog.tsx +++ b/src/components/SettingsDialog.tsx @@ -28,6 +28,7 @@ import { setDockerImage, setShareDockerAgentAuth, setAskCodeProvider, + setMinimaxModel, setMinimaxApiKey, setAppearanceMode, setLightTheme, @@ -590,10 +591,42 @@ export function SettingsDialog(props: SettingsDialogProps) { }} > - + +