Skip to content

feat: add MiniMax M2.7 as configurable backend for Ask about Code#53

Open
octo-patch wants to merge 2 commits intojohannesjo:mainfrom
octo-patch:feature/add-minimax-provider
Open

feat: add MiniMax M2.7 as configurable backend for Ask about Code#53
octo-patch wants to merge 2 commits intojohannesjo:mainfrom
octo-patch:feature/add-minimax-provider

Conversation

@octo-patch
Copy link
Copy Markdown

Summary

  • Adds MiniMax M2.7 (204K context) as an alternative LLM provider for the Ask about Code inline Q&A feature
  • Users can switch between Claude Code (default) and MiniMax in the Settings → Ask about Code section
  • When MiniMax is selected, the app calls the MiniMax API directly via its OpenAI-compatible endpoint — no claude CLI required
  • API key is stored in app state and persisted across restarts

Changes

File What changed
electron/ipc/ask-code-minimax.ts New MiniMax streaming backend: SSE parser, abort-signal handling, Bearer auth
electron/ipc/ask-code-minimax.test.ts 12 unit tests (chunks, errors, cancel, auth header, model, temperature, stream flag)
electron/ipc/ask-code.ts Route to MiniMax or Claude based on provider arg
electron/ipc/register.ts Forward provider / minimaxApiKey from IPC call args
src/store/{types,core,ui,store,persistence}.ts New askCodeProvider + minimaxApiKey store fields, persisted to state.json
src/components/SettingsDialog.tsx Provider dropdown + conditional API-key input
src/components/AskCodeCard.tsx Pass provider + apiKey through IPC
README.md Mention MiniMax as a supported Ask-about-Code provider

Test plan

  • Run npm test — 12 new passing tests, all existing tests still pass
  • Open Settings (Ctrl+,) → see new "Ask about Code" section with provider dropdown
  • Select MiniMax, enter a valid MINIMAX_API_KEY, select code in a task, ask a question — response streams in
  • Switch back to Claude Code — Claude CLI is used as before
  • Restart the app — provider and key are persisted

The "Ask about Code" inline Q&A feature now supports MiniMax as an
alternative to the Claude Code CLI. Users can switch providers in
Settings and supply a MINIMAX_API_KEY to use MiniMax M2.7 (204K
context) without needing claude installed.

- electron/ipc/ask-code-minimax.ts: streaming MiniMax backend via
  OpenAI-compatible API, with abort-signal handling and SSE parsing
- electron/ipc/ask-code.ts: route to MiniMax or Claude based on
  provider arg passed through IPC
- electron/ipc/register.ts: forward provider/minimaxApiKey from IPC args
- src/store/{types,core,ui,store,persistence}.ts: persist askCodeProvider
  and minimaxApiKey settings
- src/components/SettingsDialog.tsx: provider selector + API key input
- src/components/AskCodeCard.tsx: pass provider/apiKey through IPC
- README.md: mention MiniMax as supported Ask-about-Code provider
- electron/ipc/ask-code-minimax.test.ts: 12 unit tests
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds MiniMax M2.7 as an alternate backend for the app’s Ask about Code feature, allowing users to switch providers in Settings and stream responses via MiniMax’s OpenAI-compatible API.

Changes:

  • Added a new Electron MiniMax streaming backend with SSE parsing, cancellation, and basic request limits.
  • Routed ask-about-code IPC calls to either Claude CLI or MiniMax based on a new persisted askCodeProvider setting.
  • Added Settings UI for provider selection and MiniMax API key entry, and threaded these values through the IPC call chain.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
electron/ipc/ask-code-minimax.ts Implements MiniMax streaming backend (fetch + SSE parsing + abort/timeout).
electron/ipc/ask-code-minimax.test.ts Adds unit tests for MiniMax streaming, headers, errors, and cancellation.
electron/ipc/ask-code.ts Routes requests to MiniMax when selected; keeps Claude CLI path as default.
electron/ipc/register.ts Extends IPC handler to accept/validate provider and minimaxApiKey.
src/store/core.ts Adds default store fields for askCodeProvider and minimaxApiKey.
src/store/types.ts Extends persisted/in-memory state typings for new provider + key fields.
src/store/ui.ts Adds setters for provider and API key.
src/store/store.ts Re-exports new UI setters from the store module.
src/store/persistence.ts Persists/loads provider selection and MiniMax API key.
src/components/SettingsDialog.tsx Adds provider dropdown and conditional MiniMax API key input.
src/components/AskCodeCard.tsx Passes provider + API key into the IPC AskAboutCode call.
README.md Documents MiniMax as a supported Ask-about-Code provider.

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

editorCommand: store.editorCommand || undefined,
dockerImage: store.dockerImage !== 'parallel-code-agent:latest' ? store.dockerImage : undefined,
askCodeProvider: store.askCodeProvider !== 'claude' ? store.askCodeProvider : undefined,
minimaxApiKey: store.minimaxApiKey || undefined,
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

minimaxApiKey is being persisted into state.json (and therefore also into the .bak backup) in plaintext. This is a high-risk secret-storage pattern for an API key; consider storing it via an OS credential store / Electron secure storage (e.g., safeStorage + platform keychain) or avoid persisting it by default (or only persist when provider === 'minimax' and add an explicit “forget key” action).

Suggested change
minimaxApiKey: store.minimaxApiKey || undefined,
// Do not persist API keys in plaintext application state files.

Copilot uses AI. Check for mistakes.
const { win, messages } = makeMockWin();

// Simulate a slow response that never closes
let rejectReader!: (err: unknown) => void;
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

rejectReader is declared but never used, which will fail the repo’s @typescript-eslint/no-unused-vars lint rule. Remove it (or use it) to keep npm run lint / npm run check passing.

Suggested change
let rejectReader!: (err: unknown) => void;

Copilot uses AI. Check for mistakes.
Comment on lines 127 to 132
inactiveColumnOpacity?: number;
editorCommand?: string;
dockerImage?: string;
askCodeProvider?: string;
minimaxApiKey?: string;
customAgents?: AgentDef[];
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

PersistedState.askCodeProvider is typed as string, but the in-memory store restricts this to 'claude' | 'minimax'. Narrowing the persisted type to the same union (and validating to that) will make the persistence layer safer and prevent accidental misuse elsewhere.

Copilot uses AI. Check for mistakes.
@johannesjo
Copy link
Copy Markdown
Owner

Review findings (new issues not covered by Copilot)

1. API key flows through IPC and lives in renderer store (medium severity)

In AskCodeCard.tsx, store.minimaxApiKey is read from the SolidJS renderer store and passed directly as an IPC argument on every query:

minimaxApiKey: store.minimaxApiKey,

This means:

  • The key lives in plaintext renderer-process memory (accessible to any compromised renderer script or DevTools).
  • The key is serialised into every IPC AskAboutCode message, so it appears in Electron's IPC log when devTools IPC logging is enabled.

Copilot's comment covered plaintext persistence to state.json; this is the complementary concern about the in-flight IPC surface. A safer architecture is to keep the key only in the main process (store it there after the user types it once via a dedicated set_minimax_api_key IPC call, ideally via safeStorage) and never send it back to the renderer or forward it per-request.


2. User-initiated cancel sends exitCode: 1 (semantic bug, low severity)

In ask-code-minimax.ts, both the timeout path and the abort path emit { type: 'done', exitCode: 1 }. For a user-initiated cancel the AbortError is correctly suppressed from the error message, but the exitCode: 1 still fires. If any caller treats exit code 1 as a failure (e.g. showing a red error state), users will see a spurious failure indicator after every cancel. Consider a distinct exitCode: 2 or a type: 'cancelled' message for graceful cancellation.


3. cancelAskAboutCode unconditionally calls cancelAskAboutCodeMinimax (minor, architectural concern)

export function cancelAskAboutCode(requestId: string): void {
+  cancelAskAboutCodeMinimax(requestId);  // always called, even for claude provider
  const proc = activeRequests.get(requestId);
  ...
}

Today this is a no-op for claude requests (the ID won't exist in the minimax map), but it couples the cancel dispatcher to every provider unconditionally. If a third provider is added the same pattern will continue to diverge. A cleaner approach is to store the active provider per requestId and dispatch cancel accordingly.


4. No max_tokens cap in the request body (medium severity)

The MiniMax request body has no max_tokens field. M2.7 has a 204K context window — without a cap every response can consume up to the full output token budget. For a code Q&A use-case this risks unexpected cost and latency spikes. Adding a reasonable cap (e.g. max_tokens: 2048) would bound both.


Summary: Issues 1 and 4 are the most actionable before merge. Issue 2 is a UX correctness bug. Issue 3 is a code-quality concern but not a blocker.

@johannesjo
Copy link
Copy Markdown
Owner

Thank you very much! <3

Copy link
Copy Markdown
Owner

@johannesjo johannesjo left a comment

Choose a reason for hiding this comment

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

Three issues to address before merge:

  1. API key exposure — the MiniMax API key is held in plaintext renderer memory and transmitted in the IPC payload on every query. Consider keeping it in the main process only, reading it there from persisted state.
  2. No max_tokens cap — without a limit, a single query can produce an unbounded response, leading to unexpected latency and cost.
  3. Cancel exit code — user-initiated cancellation emits exitCode: 1 (treated as an error); it should emit a neutral/cancelled signal instead.

Additionally, cancelAskAboutCode calls the MiniMax canceller regardless of which provider is active — it should be guarded by a provider check. Details in the review comment.

- Move API key to main process only: add SetMinimaxApiKey IPC channel,
  store key in main-process memory, remove from renderer store and
  per-request IPC payload, stop persisting to state.json
- Add max_tokens: 2048 cap to MiniMax request body
- Fix cancel exit code: emit exitCode 0 with cancelled flag instead of
  exitCode 1 for user-initiated cancellation
- Guard cancelAskAboutCode by provider: track active provider per request,
  only call cancelAskAboutCodeMinimax when provider is minimax
- Narrow PersistedState.askCodeProvider type to 'claude' | 'minimax'
- Remove unused rejectReader variable in ask-code-minimax.test.ts

Co-Authored-By: Octopus <liyuan851277048@icloud.com>
@octo-patch
Copy link
Copy Markdown
Author

Thanks for the detailed review! All four issues plus the Copilot feedback have been addressed in commit dfb2e00:

1. API key exposure (medium):

  • Added a dedicated SetMinimaxApiKey IPC channel (channels.ts, preload.cjs)
  • API key is now stored only in main-process memory (ask-code-minimax.ts: storedApiKey + setMinimaxApiKey())
  • Removed apiKey from MinimaxAskCodeRequest interface — askAboutCodeMinimax() reads from main-process state
  • Removed minimaxApiKey from AskCodeRequest in ask-code.ts and from the IPC args in register.ts
  • AskCodeCard.tsx no longer sends minimaxApiKey in the IPC payload
  • persistence.ts no longer persists the API key to state.json
  • SettingsDialog.tsx input sends the key to main process via IPC on input; no longer binds to store value
  • Removed minimaxApiKey from AppStore interface and store defaults

2. No max_tokens cap (medium):

  • Added max_tokens: 2048 to the MiniMax request body in ask-code-minimax.ts

3. Cancel exit code (low):

  • Abort/cancel path now emits { type: 'done', exitCode: 0, cancelled: true } instead of exitCode: 1
  • Both the stream-abort path (.then branch) and the AbortError catch path use exitCode: 0

4. cancelAskAboutCode provider guard:

  • Added activeProviders map in ask-code.ts to track which provider each request uses
  • cancelAskAboutCode() now checks the provider and only calls cancelAskAboutCodeMinimax() when provider === 'minimax'

Copilot feedback:

  • Removed unused rejectReader variable in ask-code-minimax.test.ts
  • Narrowed PersistedState.askCodeProvider from string to 'claude' | 'minimax'

All 12 unit tests pass. TypeScript typecheck passes with no errors.

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.

3 participants