Skip to content

feat: add MiniMax provider support#1277

Closed
octo-patch wants to merge 2 commits intocoleam00:devfrom
octo-patch:feature/add-minimax-provider
Closed

feat: add MiniMax provider support#1277
octo-patch wants to merge 2 commits intocoleam00:devfrom
octo-patch:feature/add-minimax-provider

Conversation

@octo-patch
Copy link
Copy Markdown

@octo-patch octo-patch commented Apr 17, 2026

Summary

Adds MiniMax as a built-in AI agent provider for Archon, enabling users to use cost-effective MiniMax models alongside Claude and Codex.

Changes

  • packages/providers/src/minimax/provider.tsMiniMaxProvider implementing IAgentProvider via MiniMax's OpenAI-compatible streaming API
  • packages/providers/src/minimax/capabilities.ts — Provider capability flags (env injection enabled; no session resume, MCP, hooks, or tool restrictions)
  • packages/providers/src/minimax/config.ts — Config parser for model and baseURL assistant defaults
  • packages/providers/src/registry.ts — Registers minimax as a built-in provider with MiniMax-* model compatibility
  • packages/providers/src/types.ts — Adds MiniMaxProviderDefaults interface
  • packages/providers/src/index.ts — Exports MiniMaxProvider and parseMiniMaxConfig
  • packages/providers/package.json — Adds minimax subpath exports and includes minimax tests
  • packages/providers/src/minimax/provider.test.ts — 23 unit tests covering config parsing, capabilities, streaming, error handling, and model selection
  • packages/providers/src/registry.test.ts — Updates existing registry tests to account for the new built-in provider

Supported Models

Model Description
MiniMax-M2.7 Default — Peak Performance. Ultimate Value.
MiniMax-M2.7-highspeed Same performance, faster and more agile

Configuration

Set MINIMAX_API_KEY in your environment. Optionally configure defaults in .archon/config.yaml:

assistants:
  minimax:
    model: MiniMax-M2.7
    baseURL: https://api.minimax.io/v1  # overseas default

Use in a workflow:

nodes:
  - id: analyze
    provider: minimax
    model: MiniMax-M2.7
    prompt: "Analyze the following code..."

API Reference

Notes

  • Temperature is fixed at 1.0 (MiniMax requires (0.0, 1.0])
  • Session resume is not supported (stateless chat completions)
  • MCP, hooks, skills, and tool restrictions are not supported

Summary by CodeRabbit

  • New Features

    • Added support for MiniMax AI provider with capabilities for text, image, video, speech, and music generation.
    • Added mmx-cli skill documentation with installation command.
  • Tests

    • Added comprehensive test suite for MiniMax provider including configuration validation and query handling.

octo-patch added 2 commits April 14, 2026 11:13
- Add MiniMax chat model provider using OpenAI-compatible API
- Support MiniMax-M2.7 (default) and MiniMax-M2.7-highspeed models
- Add MINIMAX_API_KEY environment variable support
- Add unit tests for provider, config, and capabilities
- Register MiniMax as a built-in provider in the registry
- Export MiniMax provider and config from @archon/providers
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

📝 Walkthrough

Walkthrough

This pull request introduces a new MiniMax provider to the providers package. It includes the complete provider implementation with configuration parsing, capability definitions, and comprehensive tests, along with integration into the package's public exports and built-in registry.

Changes

Cohort / File(s) Summary
Documentation
packages/docs-web/src/content/docs/guides/skills.md
Added entry for mmx-cli skill to the Popular Skills table with install command and description of MiniMax AI capabilities.
Core Provider Implementation
packages/providers/src/minimax/provider.ts, packages/providers/src/minimax/config.ts, packages/providers/src/minimax/capabilities.ts
Introduced complete MiniMax provider: provider class implementing streaming SSE parsing for OpenAI-compatible Chat Completions endpoint, configuration parser for model and baseURL, and provider capability flags object defining feature support.
Package Configuration & Exports
packages/providers/package.json, packages/providers/src/index.ts
Added package export entries mapping MiniMax provider and config modules; added re-exports of MiniMaxProvider, parseMiniMaxConfig, and MiniMaxProviderDefaults to main index; updated test script to include MiniMax provider tests.
Type Definitions
packages/providers/src/types.ts
Added MiniMaxProviderDefaults interface with optional model and baseURL fields for provider configuration.
Registry Integration
packages/providers/src/registry.ts
Registered MiniMax as a built-in provider with model compatibility check for models starting with 'MiniMax-'.
Tests
packages/providers/src/minimax/provider.test.ts, packages/providers/src/registry.test.ts
Added comprehensive test suite covering config parsing, capability validation, and provider behavior including streaming, error handling, auth, and token usage; updated registry tests to verify MiniMax provider registration and model compatibility.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client / Archon
    participant Provider as MiniMaxProvider
    participant API as MiniMax API
    participant Stream as SSE Stream

    Client->>Provider: sendQuery(prompt, options)
    Provider->>Provider: Parse config & resolve baseURL, model
    Provider->>Provider: Build request body with streaming enabled
    Provider->>API: POST /chat/completions (stream: true)
    API-->>Stream: Return streaming response
    Provider->>Stream: Read response.body (SSE lines)
    loop Parse SSE chunks
        Stream-->>Provider: "data: {delta: {content: '...'}}"
        Provider->>Provider: Parse JSON, extract delta
        Provider-->>Client: Yield MessageChunk with token delta
    end
    Stream-->>Provider: "data: {result: {usage: {...}}}"
    Provider->>Provider: Accumulate token usage
    Provider-->>Client: Yield result chunk with token counts
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Wirasm

🐰 Hops around excitedly

MiniMax joins the crew, streaming dreams so bright,
SSE whispers, tokens parsed just right,
Config flows smooth, from Moscow to here,
The rabbit cheers—new providers this year! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is comprehensive but deviates from the required template structure; it lacks formal sections like Summary bullets (Problem/Why/What changed/Scope), UX Journey diagrams, Architecture diagrams, Validation Evidence, Security Impact, Compatibility, Human Verification, Side Effects, and Rollback Plan. Restructure the description to follow the template format: add Summary bullets, Before/After UX and Architecture diagrams, Label Snapshot metadata, Validation Evidence, Security Impact analysis, Compatibility statement, Human Verification details, Side Effects/Blast Radius, and Rollback Plan sections.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add MiniMax provider support' is concise, clear, and accurately summarizes the main change—adding MiniMax as a new provider to the system.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (6)
packages/providers/src/minimax/config.ts (1)

18-30: Consider validating baseURL shape.

baseURL is accepted as-is as long as it's a string. A malformed value (e.g., missing scheme, or a path without host) will only surface later as an opaque fetch failure. A minimal new URL(raw.baseURL) check with an early throw (per guideline "Prefer throwing early with a clear error for unsupported or unsafe states") would yield a much clearer error at config-load time. Non-blocking.

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

In `@packages/providers/src/minimax/config.ts` around lines 18 - 30,
parseMiniMaxConfig currently accepts any string for the baseURL which can lead
to opaque fetch failures later; update parseMiniMaxConfig to validate
raw.baseURL by attempting to construct a new URL(raw.baseURL) and if that
throws, rethrow a clear, early error (e.g., "Invalid MiniMax baseURL: <value>")
so callers fail fast and get a helpful message; locate the logic in function
parseMiniMaxConfig and perform the new URL check before assigning
result.baseURL.
packages/providers/package.json (1)

21-21: Test script is getting unwieldy; consider a glob.

Chaining seven bun test invocations with && is brittle (one added test file requires editing this line). Consider bun test with a directory, or moving to a test runner config. Not blocking.

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

In `@packages/providers/package.json` at line 21, The package.json "test" script
is brittle because it chains many explicit bun test calls; replace the hardcoded
chain (the "test" script value) with a single glob or directory invocation (e.g.
run bun test against src or a pattern like src/**/*.test.ts) so new tests run
automatically without editing the script; update the "test" script entry to use
that glob/directory form instead of the long && chain.
packages/providers/src/registry.ts (1)

106-108: Update the docstring.

The comment still says "Register built-in providers (Claude, Codex)." — add MiniMax.

- * Register built-in providers (Claude, Codex). Idempotent — skips already-registered IDs.
+ * Register built-in providers (Claude, Codex, MiniMax). Idempotent — skips already-registered IDs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/providers/src/registry.ts` around lines 106 - 108, Update the
top-of-file/doc comment for the built-in provider registration to mention
MiniMax in addition to Claude and Codex; locate the documentation block above
the registerBuiltInProviders function (or the Register built-in providers
comment in registry.ts) and change the text "Register built-in providers
(Claude, Codex)." to include "MiniMax" so it reads something like "Register
built-in providers (Claude, Codex, MiniMax)."
packages/docs-web/src/content/docs/guides/skills.md (1)

171-171: Doc entry looks fine.

Minor: the surrounding guide is explicitly scoped to Claude (line 16 states "Claude only — Codex nodes will warn and ignore the skills field"). Since MiniMax capabilities set skills: false, this new row fits the "Popular Skills" list (skill installation is provider-agnostic on disk) but readers may expect the skill to be usable on MiniMax nodes. Consider a brief note that mmx-cli only takes effect on Claude nodes, or place MiniMax-related content in a MiniMax-specific guide.

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

In `@packages/docs-web/src/content/docs/guides/skills.md` at line 171, The
"Popular Skills" row for `mmx-cli` / `MiniMax-AI/cli/skill` could mislead
readers because the surrounding guide is Claude-only and MiniMax nodes set
`skills: false`; add a brief clarifying sentence near the table (or immediately
after the `mmx-cli` row) stating that `mmx-cli`/`MiniMax-AI/cli/skill`
installation is provider-agnostic on disk but the `skills` field is disabled for
MiniMax (i.e., `skills: false`), so the skill only takes effect on Claude nodes
— alternatively move this MiniMax-specific entry into a MiniMax-specific guide
to avoid confusion.
packages/providers/src/registry.test.ts (1)

271-277: Consider covering the codex/minimax overlap.

Codex's isModelCompatible returns true for any model that is not a Claude alias / claude-* / inherit, which includes MiniMax-M2.7. A test like expect(getRegistration('codex').isModelCompatible('MiniMax-M2.7')).toBe(false) would currently fail and surface the ambiguity flagged on registry.ts. Worth adding once that is resolved.

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

In `@packages/providers/src/registry.test.ts` around lines 271 - 277, The test
suite misses the Codex vs MiniMax ambiguity: add a test asserting
getRegistration('codex').isModelCompatible('MiniMax-M2.7') returns false, and
fix registry logic so Codex doesn't claim MiniMax-style names; either make
Codex's isModelCompatible explicitly exclude MiniMax patterns (e.g. names
matching /^MiniMax-/) or change the registration matching precedence in
registry.ts to check MiniMax (getRegistration('minimax') / its
isModelCompatible) before Codex (getRegistration('codex')) so MiniMax names are
claimed by MiniMax; update tests accordingly to include
expect(getRegistration('codex').isModelCompatible('MiniMax-M2.7')).toBe(false).
packages/providers/src/minimax/provider.test.ts (1)

130-130: Restore globalThis.fetch between tests.

globalThis.fetch is overwritten in every sendQuery test but never restored. Once this suite runs, the overridden fetch leaks into any subsequent test in the same bun process and into teardown code, which can produce cross-suite pollution and hard-to-diagnose failures.

Capture the original once and restore it in afterEach/afterAll:

♻️ Suggested refactor
-import { describe, test, expect, mock, beforeEach } from 'bun:test';
+import { describe, test, expect, mock, beforeEach, afterEach } from 'bun:test';
 ...
 describe('MiniMaxProvider', () => {
   let provider: MiniMaxProvider;
+  const originalFetch = globalThis.fetch;
+
+  afterEach(() => {
+    globalThis.fetch = originalFetch;
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/providers/src/minimax/provider.test.ts` at line 130, The tests
overwrite globalThis.fetch with mockFetch in the sendQuery suite but never
restore it; capture the original fetch before overriding (e.g., const
originalFetch = globalThis.fetch) and restore it in an afterEach or afterAll
hook to avoid leaking the mock into other suites—update the
packages/providers/src/minimax/provider.test.ts test file to save the original
globalThis.fetch before assigning mockFetch and call globalThis.fetch =
originalFetch in a teardown hook after each test (or after all) so sendQuery
tests clean up their mock.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/providers/src/index.ts`:
- Line 43: The package currently re-exports parseMiniMaxConfig and redeclares
MiniMaxProviderDefaults from ./minimax/config, creating a duplicate type; change
the export so MiniMaxProviderDefaults is re-exported from the single source of
truth (the providers' types module where MiniMaxProviderDefaults is declared)
instead of from minimax/config, and remove the redundant interface declaration
in minimax/config so parseMiniMaxConfig remains exported but the type is
imported/re-exported from the canonical types declaration (ensure the symbol
MiniMaxProviderDefaults is imported from the types module and re-exported
alongside parseMiniMaxConfig).

In `@packages/providers/src/minimax/config.ts`:
- Around line 5-12: Remove the local duplicate interface declaration for
MiniMaxProviderDefaults and instead import the canonical type from ../types
using a type-only import; then re-export it as MiniMaxConfig (e.g., import type
{ MiniMaxProviderDefaults } from '../types' and export type {
MiniMaxProviderDefaults as MiniMaxConfig }). Update the file to reference only
the imported MiniMaxProviderDefaults symbol (no local interface) and ensure you
use import type for the type-only import per guidelines.

In `@packages/providers/src/minimax/provider.test.ts`:
- Around line 5-7: The test currently calls mock.module('@archon/paths', ...)
which globally mutates the module registry; instead import the module locally
(e.g. import * as paths from '@archon/paths') and replace mock.module(...) with
spyOn(paths, 'createLogger').mockReturnValue(mockLogger), and ensure you restore
the spy in afterEach via (paths.createLogger as any).mockRestore() or
spy.mockRestore(); update any test setup referencing mock.module to use spyOn
and mockLogger so the mock is file-scoped and does not pollute other tests.

In `@packages/providers/src/minimax/provider.ts`:
- Around line 41-50: classifyError currently uses loose substring matching
(RATE_LIMIT_PATTERNS and AUTH_PATTERNS) that misclassifies errors; update
classifyError to (1) first try to parse the incoming message as JSON and inspect
structured fields like error.code, error.status, or statusCode to decide
'rate_limit' vs 'auth', and only fall back to string matching if parsing fails,
(2) tighten string matches by using whole-word/regex boundaries and replace
generic tokens like 'invalid' with specific phrases such as 'invalid api key' or
'invalid token', and (3) remove or regex-anchor raw numeric tokens (e.g.,
'1002','1039','1004') so they match only if they appear as standalone error
codes. Ensure these changes are applied inside the classifyError function and
reference RATE_LIMIT_PATTERNS and AUTH_PATTERNS for pattern updates.
- Line 264: The structured log events in provider.ts violate the
`{domain}.{action}_{state}` convention: rename `minimax.query_started`
(getLog().debug call) to `minimax.query_started`, then emit
`minimax.query_completed` after the successful streaming finish (after the
`yield*`/stream end in the query flow) and emit `minimax.query_failed` on the
terminal throw path (the throw around line 335); also rename other events so
they use `_failed` or explicit states instead of `_error`/bare actions —
specifically update `minimax.fetch_error` -> `minimax.fetch_failed`,
`minimax.http_error` -> `minimax.http_failed`, `minimax.stream_error` ->
`minimax.stream_failed`, `minimax.sse_parse_error` ->
`minimax.sse_parse_failed`, `minimax.empty_response` ->
`minimax.empty_response_failed` (or another explicit failed state), and
`minimax.retrying` -> `minimax.retrying_started`/`minimax.retrying_completed` as
appropriate; locate and change the getLog() calls in the streaming/query
functions (the getLog().debug/getLog().warn/getLog().error invocations) so each
`_started` has a paired `_completed` or `_failed` and keep message context
(model, attempt, baseURL).
- Around line 321-333: The current retry loop can re-deliver partial streamed
assistant chunks because yield* streamMiniMaxResponse(...) may emit data before
a stream error; change the retry logic to detect if any chunks were emitted and,
if so, do not retry—immediately rethrow the stream error instead of attempting a
new request. Concretely, introduce a local boolean (e.g., emittedAnyChunk) that
is set by a small wrapper/iterator around streamMiniMaxResponse when the first
chunk is yielded, then in the catch block check that flag: if emittedAnyChunk is
true, rethrow streamErr; otherwise continue with the existing retry/backoff
logic (use the existing variables attempt, MAX_RETRIES, retryBaseDelayMs,
lastError).

In `@packages/providers/src/registry.ts`:
- Around line 135-142: Codex's isModelCompatible currently claims any
non-Claude/non-'inherit' model which causes MiniMax-* names to match; update the
Codex provider's isModelCompatible predicate to explicitly exclude MiniMax
prefixes (e.g. return false if model.startsWith('MiniMax-')) or better replace
the predicate with an allow-list of recognized OpenAI prefixes (e.g. check
model.startsWith one of ['gpt-', 'text-davinci-', ...]) so MiniMaxProvider
(factory: () => new MiniMaxProvider(), isModelCompatible: (model) =>
model.startsWith('MiniMax-')) will be selected by inferProviderFromModel;
add/adjust a unit test for inferProviderFromModel to assert 'MiniMax-M2.7'
resolves to the 'minimax' provider.

---

Nitpick comments:
In `@packages/docs-web/src/content/docs/guides/skills.md`:
- Line 171: The "Popular Skills" row for `mmx-cli` / `MiniMax-AI/cli/skill`
could mislead readers because the surrounding guide is Claude-only and MiniMax
nodes set `skills: false`; add a brief clarifying sentence near the table (or
immediately after the `mmx-cli` row) stating that
`mmx-cli`/`MiniMax-AI/cli/skill` installation is provider-agnostic on disk but
the `skills` field is disabled for MiniMax (i.e., `skills: false`), so the skill
only takes effect on Claude nodes — alternatively move this MiniMax-specific
entry into a MiniMax-specific guide to avoid confusion.

In `@packages/providers/package.json`:
- Line 21: The package.json "test" script is brittle because it chains many
explicit bun test calls; replace the hardcoded chain (the "test" script value)
with a single glob or directory invocation (e.g. run bun test against src or a
pattern like src/**/*.test.ts) so new tests run automatically without editing
the script; update the "test" script entry to use that glob/directory form
instead of the long && chain.

In `@packages/providers/src/minimax/config.ts`:
- Around line 18-30: parseMiniMaxConfig currently accepts any string for the
baseURL which can lead to opaque fetch failures later; update parseMiniMaxConfig
to validate raw.baseURL by attempting to construct a new URL(raw.baseURL) and if
that throws, rethrow a clear, early error (e.g., "Invalid MiniMax baseURL:
<value>") so callers fail fast and get a helpful message; locate the logic in
function parseMiniMaxConfig and perform the new URL check before assigning
result.baseURL.

In `@packages/providers/src/minimax/provider.test.ts`:
- Line 130: The tests overwrite globalThis.fetch with mockFetch in the sendQuery
suite but never restore it; capture the original fetch before overriding (e.g.,
const originalFetch = globalThis.fetch) and restore it in an afterEach or
afterAll hook to avoid leaking the mock into other suites—update the
packages/providers/src/minimax/provider.test.ts test file to save the original
globalThis.fetch before assigning mockFetch and call globalThis.fetch =
originalFetch in a teardown hook after each test (or after all) so sendQuery
tests clean up their mock.

In `@packages/providers/src/registry.test.ts`:
- Around line 271-277: The test suite misses the Codex vs MiniMax ambiguity: add
a test asserting getRegistration('codex').isModelCompatible('MiniMax-M2.7')
returns false, and fix registry logic so Codex doesn't claim MiniMax-style
names; either make Codex's isModelCompatible explicitly exclude MiniMax patterns
(e.g. names matching /^MiniMax-/) or change the registration matching precedence
in registry.ts to check MiniMax (getRegistration('minimax') / its
isModelCompatible) before Codex (getRegistration('codex')) so MiniMax names are
claimed by MiniMax; update tests accordingly to include
expect(getRegistration('codex').isModelCompatible('MiniMax-M2.7')).toBe(false).

In `@packages/providers/src/registry.ts`:
- Around line 106-108: Update the top-of-file/doc comment for the built-in
provider registration to mention MiniMax in addition to Claude and Codex; locate
the documentation block above the registerBuiltInProviders function (or the
Register built-in providers comment in registry.ts) and change the text
"Register built-in providers (Claude, Codex)." to include "MiniMax" so it reads
something like "Register built-in providers (Claude, Codex, MiniMax)."
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d42feb65-0d04-4f52-81eb-0f9a83527c12

📥 Commits

Reviewing files that changed from the base of the PR and between d89bc76 and be03c0a.

📒 Files selected for processing (10)
  • packages/docs-web/src/content/docs/guides/skills.md
  • packages/providers/package.json
  • packages/providers/src/index.ts
  • packages/providers/src/minimax/capabilities.ts
  • packages/providers/src/minimax/config.ts
  • packages/providers/src/minimax/provider.test.ts
  • packages/providers/src/minimax/provider.ts
  • packages/providers/src/registry.test.ts
  • packages/providers/src/registry.ts
  • packages/providers/src/types.ts

// Config parsers
export { parseClaudeConfig, type ClaudeProviderDefaults } from './claude/config';
export { parseCodexConfig, type CodexProviderDefaults } from './codex/config';
export { parseMiniMaxConfig, type MiniMaxProviderDefaults } from './minimax/config';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Re-export source is inconsistent with the canonical type location.

MiniMaxProviderDefaults is re-exported here from ./minimax/config, but packages/providers/src/types.ts declares itself the "Single source of truth for provider-specific config shapes" and defines MiniMaxProviderDefaults at lines 30-35. The config module currently redeclares the interface rather than re-exporting from ./types, producing two structurally-identical types that can silently drift. See the comment on minimax/config.ts.

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

In `@packages/providers/src/index.ts` at line 43, The package currently re-exports
parseMiniMaxConfig and redeclares MiniMaxProviderDefaults from ./minimax/config,
creating a duplicate type; change the export so MiniMaxProviderDefaults is
re-exported from the single source of truth (the providers' types module where
MiniMaxProviderDefaults is declared) instead of from minimax/config, and remove
the redundant interface declaration in minimax/config so parseMiniMaxConfig
remains exported but the type is imported/re-exported from the canonical types
declaration (ensure the symbol MiniMaxProviderDefaults is imported from the
types module and re-exported alongside parseMiniMaxConfig).

Comment on lines +5 to +12
export interface MiniMaxProviderDefaults {
[key: string]: unknown;
model?: string;
baseURL?: string;
}

// Re-export so consumers can import the type from either location
export type { MiniMaxProviderDefaults as MiniMaxConfig };
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Duplicate interface declaration — re-export from ../types instead.

MiniMaxProviderDefaults is already the canonical definition in packages/providers/src/types.ts (lines 30-35), which that file explicitly identifies as the "Single source of truth for provider-specific config shapes." Redeclaring the same interface here creates two nominally distinct types that can drift (a field added in one location will not appear in the other, and @archon/core/config/config-types.ts — per the comment in types.ts — imports from ./types, not from here).

Align with the Claude/Codex pattern by re-exporting from ../types:

♻️ Proposed fix
-export interface MiniMaxProviderDefaults {
-  [key: string]: unknown;
-  model?: string;
-  baseURL?: string;
-}
-
-// Re-export so consumers can import the type from either location
-export type { MiniMaxProviderDefaults as MiniMaxConfig };
+export type { MiniMaxProviderDefaults } from '../types';
+export type { MiniMaxProviderDefaults as MiniMaxConfig } from '../types';

As per coding guidelines: "Always use import type for type-only imports and specific named imports for values".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface MiniMaxProviderDefaults {
[key: string]: unknown;
model?: string;
baseURL?: string;
}
// Re-export so consumers can import the type from either location
export type { MiniMaxProviderDefaults as MiniMaxConfig };
export type { MiniMaxProviderDefaults } from '../types';
export type { MiniMaxProviderDefaults as MiniMaxConfig } from '../types';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/providers/src/minimax/config.ts` around lines 5 - 12, Remove the
local duplicate interface declaration for MiniMaxProviderDefaults and instead
import the canonical type from ../types using a type-only import; then re-export
it as MiniMaxConfig (e.g., import type { MiniMaxProviderDefaults } from
'../types' and export type { MiniMaxProviderDefaults as MiniMaxConfig }). Update
the file to reference only the imported MiniMaxProviderDefaults symbol (no local
interface) and ensure you use import type for the type-only import per
guidelines.

Comment on lines +5 to +7
mock.module('@archon/paths', () => ({
createLogger: mock(() => mockLogger),
}));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid mock.module('@archon/paths', ...); use spyOn(paths, 'createLogger') instead.

@archon/paths is a shared module likely mocked by other test files in this package with different shapes. mock.module() mutates the global module registry for the entire bun test process, so whichever test file runs first wins and the rest silently get the wrong logger — leading to flaky, order-dependent failures.

Switch to spyOn() (which restores cleanly via mockRestore()), or isolate this file in a separate bun test invocation in package.json.

♻️ Suggested refactor
-import { describe, test, expect, mock, beforeEach } from 'bun:test';
+import { describe, test, expect, mock, spyOn, beforeEach, afterAll } from 'bun:test';
+import * as paths from '@archon/paths';
 import { createMockLogger } from '../test/mocks/logger';

 const mockLogger = createMockLogger();
-mock.module('@archon/paths', () => ({
-  createLogger: mock(() => mockLogger),
-}));
+const createLoggerSpy = spyOn(paths, 'createLogger').mockReturnValue(mockLogger);
+afterAll(() => createLoggerSpy.mockRestore());

As per coding guidelines: "Never use mock.module() for modules that other test files also mock with different implementations; use spyOn() instead ... use separate bun test invocations in package.json for conflicting mocks to prevent process-global pollution".

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

In `@packages/providers/src/minimax/provider.test.ts` around lines 5 - 7, The test
currently calls mock.module('@archon/paths', ...) which globally mutates the
module registry; instead import the module locally (e.g. import * as paths from
'@archon/paths') and replace mock.module(...) with spyOn(paths,
'createLogger').mockReturnValue(mockLogger), and ensure you restore the spy in
afterEach via (paths.createLogger as any).mockRestore() or spy.mockRestore();
update any test setup referencing mock.module to use spyOn and mockLogger so the
mock is file-scoped and does not pollute other tests.

Comment on lines +41 to +50
const RATE_LIMIT_PATTERNS = ['rate limit', 'too many requests', '429', '1002', '1039'];
const AUTH_PATTERNS = ['unauthorized', 'authentication', 'invalid', '401', '403', '1004'];

/** Classify MiniMax error for retry decisions */
function classifyError(message: string): 'rate_limit' | 'auth' | 'unknown' {
const m = message.toLowerCase();
if (RATE_LIMIT_PATTERNS.some(p => m.includes(p))) return 'rate_limit';
if (AUTH_PATTERNS.some(p => m.includes(p))) return 'auth';
return 'unknown';
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

classifyError substring patterns are too loose and can misroute retryable vs. fatal errors.

  • AUTH_PATTERNS includes the bare word 'invalid', so errors like "invalid request body", "invalid model", or any 400 validation response will be classified as auth and throw immediately — even when a retry or a clearer error would be appropriate.
  • RATE_LIMIT_PATTERNS / AUTH_PATTERNS include raw numeric tokens ('1002', '1039', '1004') that can also match inside unrelated substrings (timestamps, request IDs, byte counts in an error body).

Consider matching on word boundaries or structured fields (e.g. parse JSON error body and inspect an explicit code/status field) and tightening 'invalid' to 'invalid api key' / 'invalid token'.

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

In `@packages/providers/src/minimax/provider.ts` around lines 41 - 50,
classifyError currently uses loose substring matching (RATE_LIMIT_PATTERNS and
AUTH_PATTERNS) that misclassifies errors; update classifyError to (1) first try
to parse the incoming message as JSON and inspect structured fields like
error.code, error.status, or statusCode to decide 'rate_limit' vs 'auth', and
only fall back to string matching if parsing fails, (2) tighten string matches
by using whole-word/regex boundaries and replace generic tokens like 'invalid'
with specific phrases such as 'invalid api key' or 'invalid token', and (3)
remove or regex-anchor raw numeric tokens (e.g., '1002','1039','1004') so they
match only if they appear as standalone error codes. Ensure these changes are
applied inside the classifyError function and reference RATE_LIMIT_PATTERNS and
AUTH_PATTERNS for pattern updates.

throw new Error('Query aborted');
}

getLog().debug({ model, attempt, baseURL }, 'minimax.query_started');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Structured log event names don't follow the required {domain}.{action}_{state} convention.

  • minimax.query_started (line 264) has no paired minimax.query_completed / minimax.query_failed on the success or terminal-failure paths.
  • minimax.fetch_error, minimax.http_error, minimax.stream_error, minimax.sse_parse_error, minimax.empty_response, minimax.retrying use _error / bare-action names instead of _failed / explicit states.

Rename to follow the convention and add the completion/failure pair for query_started (e.g., emit minimax.query_completed after successful yield* and minimax.query_failed on the terminal throw at line 335).

As per coding guidelines: "Logging must use Pino with structured events: format {domain}.{action}_{state} ... always pair _started with _completed or _failed".

Also applies to: 284-284, 303-303, 315-315, 327-327

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

In `@packages/providers/src/minimax/provider.ts` at line 264, The structured log
events in provider.ts violate the `{domain}.{action}_{state}` convention: rename
`minimax.query_started` (getLog().debug call) to `minimax.query_started`, then
emit `minimax.query_completed` after the successful streaming finish (after the
`yield*`/stream end in the query flow) and emit `minimax.query_failed` on the
terminal throw path (the throw around line 335); also rename other events so
they use `_failed` or explicit states instead of `_error`/bare actions —
specifically update `minimax.fetch_error` -> `minimax.fetch_failed`,
`minimax.http_error` -> `minimax.http_failed`, `minimax.stream_error` ->
`minimax.stream_failed`, `minimax.sse_parse_error` ->
`minimax.sse_parse_failed`, `minimax.empty_response` ->
`minimax.empty_response_failed` (or another explicit failed state), and
`minimax.retrying` -> `minimax.retrying_started`/`minimax.retrying_completed` as
appropriate; locate and change the getLog() calls in the streaming/query
functions (the getLog().debug/getLog().warn/getLog().error invocations) so each
`_started` has a paired `_completed` or `_failed` and keep message context
(model, attempt, baseURL).

Comment on lines +321 to +333
try {
yield* streamMiniMaxResponse(response, requestOptions?.abortSignal);
return;
} catch (streamErr) {
const err = streamErr as Error;
if (err.message === 'Query aborted') throw err;
getLog().error({ err, attempt }, 'minimax.stream_error');
if (attempt >= MAX_RETRIES) throw err;
const delayMs = this.retryBaseDelayMs * Math.pow(2, attempt);
await new Promise(resolve => setTimeout(resolve, delayMs));
lastError = err;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Streaming retry can emit duplicate content to the consumer.

yield* streamMiniMaxResponse(...) may yield several {type:'assistant', content:...} chunks before it throws (e.g., mid-stream network drop). The outer loop then retries, opens a fresh request, and starts streaming the full response again — so the caller receives the earlier partial content twice, corrupting the assembled assistant message.

Either don't retry once streaming has started, or only retry if no chunks were emitted yet:

🔧 Suggested fix
       try {
-        yield* streamMiniMaxResponse(response, requestOptions?.abortSignal);
-        return;
+        let emitted = false;
+        for await (const chunk of streamMiniMaxResponse(response, requestOptions?.abortSignal)) {
+          emitted = true;
+          yield chunk;
+        }
+        return;
       } catch (streamErr) {
         const err = streamErr as Error;
         if (err.message === 'Query aborted') throw err;
         getLog().error({ err, attempt }, 'minimax.stream_error');
-        if (attempt >= MAX_RETRIES) throw err;
+        // Don't retry if we've already yielded chunks — the caller would see duplicates.
+        if (emitted || attempt >= MAX_RETRIES) throw err;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/providers/src/minimax/provider.ts` around lines 321 - 333, The
current retry loop can re-deliver partial streamed assistant chunks because
yield* streamMiniMaxResponse(...) may emit data before a stream error; change
the retry logic to detect if any chunks were emitted and, if so, do not
retry—immediately rethrow the stream error instead of attempting a new request.
Concretely, introduce a local boolean (e.g., emittedAnyChunk) that is set by a
small wrapper/iterator around streamMiniMaxResponse when the first chunk is
yielded, then in the catch block check that flag: if emittedAnyChunk is true,
rethrow streamErr; otherwise continue with the existing retry/backoff logic (use
the existing variables attempt, MAX_RETRIES, retryBaseDelayMs, lastError).

Comment on lines +135 to +142
{
id: 'minimax',
displayName: 'MiniMax',
factory: () => new MiniMaxProvider(),
capabilities: MINIMAX_CAPABILITIES,
isModelCompatible: (model: string): boolean => model.startsWith('MiniMax-'),
builtIn: true,
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find how isModelCompatible / provider inference from model name is used.
rg -nP -C3 'isModelCompatible\b'
rg -nP -C3 'inferProvider|providerFromModel|resolveProvider'

Repository: coleam00/Archon

Length of output: 31746


MiniMax models are incorrectly routed to Codex due to registration order and predicate overlap.

Codex's isModelCompatible predicate (line 127–131) accepts any model that is not a Claude alias or 'inherit'. A model like MiniMax-M2.7 matches this condition. When inferProviderFromModel (packages/workflows/src/model-validation.ts:15–20) iterates through registered providers in order, it returns the first built-in provider whose isModelCompatible returns true. Since Codex is registered before MiniMax, MiniMax-M2.7 models are inferred as 'codex' and will fail at runtime when sent to CodexProvider.

This affects any workflow or node that omits an explicit provider: field but specifies a MiniMax model. No test covers MiniMax inference, leaving this gap undetected.

Either:

  1. Exclude MiniMax-* from Codex's predicate (and ideally switch to an allow-list of recognized OpenAI prefixes), or
  2. Document that MiniMax workflows must use explicit provider: minimax (not rely on model-based inference).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/providers/src/registry.ts` around lines 135 - 142, Codex's
isModelCompatible currently claims any non-Claude/non-'inherit' model which
causes MiniMax-* names to match; update the Codex provider's isModelCompatible
predicate to explicitly exclude MiniMax prefixes (e.g. return false if
model.startsWith('MiniMax-')) or better replace the predicate with an allow-list
of recognized OpenAI prefixes (e.g. check model.startsWith one of ['gpt-',
'text-davinci-', ...]) so MiniMaxProvider (factory: () => new MiniMaxProvider(),
isModelCompatible: (model) => model.startsWith('MiniMax-')) will be selected by
inferProviderFromModel; add/adjust a unit test for inferProviderFromModel to
assert 'MiniMax-M2.7' resolves to the 'minimax' provider.

@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented Apr 19, 2026

Thanks for this contribution, but we will not add this

you can use minimax through claude code or through the PI provider

if you want to add provider support it must be added through a coding agent SDK

@Wirasm Wirasm closed this Apr 19, 2026
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