Skip to content

[FEAT] Add MiniMax LLM adapter#2166

Merged
chandrasekharan-zipstack merged 14 commits into
Zipstack:mainfrom
octo-patch:octo/20260712-provider-add-recvozNE1fKYRA-clean
Jul 20, 2026
Merged

[FEAT] Add MiniMax LLM adapter#2166
chandrasekharan-zipstack merged 14 commits into
Zipstack:mainfrom
octo-patch:octo/20260712-provider-add-recvozNE1fKYRA-clean

Conversation

@octo-patch

@octo-patch octo-patch commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

What

  • Adds a branded MiniMax LLM adapter for the OpenAI- and Anthropic-compatible chat APIs.
  • Supports MiniMax-M3 and MiniMax-M2.7 across global and China endpoints.
  • Exposes official context, modality, thinking, service-tier, pricing, and endpoint configuration.

Why

  • MiniMax users need a first-class adapter with correct protocol routing, request parameters, model limits, and regional configuration.

How

  • Uses native minimax/ routing for OpenAI-compatible bases and anthropic/ routing for bases ending in /anthropic.
  • Defaults to the global OpenAI-compatible base while documenting all four supported regional and protocol bases in the adapter schema.
  • Keeps the model field free-text, preserves protocol-native thinking defaults unless the user overrides them, and forwards service_tier.
  • Enforces the official temperature range and always-on thinking for MiniMax-M2.x models.
  • Uses the standard LiteLLM cost path. MiniMax-M3 pricing is provided by the pinned dependency, while MiniMax-M2.7 remains unpriced until upstream support is available.
  • Provides validated total context windows as internal adapter metadata and removes that metadata before provider requests.
  • Uses the official MiniMax icon asset.

Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)

  • No existing request or billing contract changes. The shared context lookup accepts optional validated adapter metadata, with focused regression coverage.

Database Migrations

  • None.

Env Config

  • None. Users provide their MiniMax API key and can override the API base for region or protocol selection.

Relevant Docs

Related Issues or PRs

  • None.

Dependencies Versions

  • No dependency changes in this PR. The current base pins litellm==1.90.3.

Notes on Testing

  • Branded adapter test module: 58 passed.
  • Shared LLM compatibility, sampling, provider ID, and usage tests: 147 passed.
  • Real SDK and LiteLLM request construction verified both regional OpenAI-compatible /v1/chat/completions paths and both Anthropic-compatible /anthropic/v1/messages paths.
  • Repository pre-commit hooks, Python compilation, diff whitespace, and public-content scans passed.

Screenshots

  • Not applicable.

Checklist

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds MiniMax LLM adapter support with provider-aware model routing, thinking and service-tier validation, adapter metadata, configuration schema, exports, and branded adapter tests.

Changes

MiniMax adapter integration

Layer / File(s) Summary
MiniMax parameter validation
unstract/sdk1/src/unstract/sdk1/adapters/base1.py
Adds MiniMax endpoint constants, provider-prefix selection, model normalization, thinking and service-tier validation, and cost metadata.
Adapter export and configuration contract
unstract/sdk1/src/unstract/sdk1/adapters/llm1/minimax.py, unstract/sdk1/src/unstract/sdk1/adapters/llm1/__init__.py, unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json
Defines and exports MiniMaxLLMAdapter and adds its configuration schema.
MiniMax integration coverage
unstract/sdk1/tests/test_branded_openai_adapters.py
Tests registration, endpoint fallback, provider routing, cost handling, temperature limits, service tiers, thinking behavior, and schema metadata.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AdapterConfiguration
  participant MiniMaxLLMAdapter
  participant MiniMaxLLMParameters
  AdapterConfiguration->>MiniMaxLLMAdapter: provide api_key, model, and options
  MiniMaxLLMAdapter->>MiniMaxLLMParameters: validate configuration
  MiniMaxLLMParameters-->>MiniMaxLLMAdapter: return routed model and validated parameters
Loading

Suggested reviewers: pk-zipstack, jaseemjaskp, chandrasekharan-zipstack

🚥 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.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding a MiniMax LLM adapter.
Description check ✅ Passed The description follows the template well and fills all required sections with relevant MiniMax details.
✨ 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.

@octo-patch octo-patch mentioned this pull request Jul 12, 2026
1 task
@greptile-apps

greptile-apps Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a first-class MiniMax LLM adapter. The main changes are:

  • OpenAI- and Anthropic-compatible routing for global and China endpoints.
  • MiniMax model, thinking, temperature, and service-tier validation.
  • Adapter-specific context windows and LiteLLM cost-model lookup.
  • MiniMax schema, registration, icon, and focused tests.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the updated code.
  • The MiniMax routing and context-window fixes include focused tests.

Important Files Changed

Filename Overview
unstract/sdk1/src/unstract/sdk1/adapters/base1.py Adds MiniMax routing, request validation, cost-model selection, and context-window metadata.
unstract/sdk1/src/unstract/sdk1/adapters/llm1/init.py Registers and exports the MiniMax adapter.
unstract/sdk1/src/unstract/sdk1/adapters/llm1/minimax.py Defines the branded MiniMax adapter and its metadata.
unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json Defines MiniMax credentials, endpoint, model, thinking, and service-tier settings.
unstract/sdk1/src/unstract/sdk1/llm.py Uses validated adapter metadata for context sizing and removes internal metadata before requests.
unstract/sdk1/tests/test_branded_openai_adapters.py Covers MiniMax registration, routing, validation, pricing, schema, and context windows.

Reviews (11): Last reviewed commit: "Merge branch 'main' into octo/20260712-p..." | Re-trigger Greptile

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

Actionable comments posted: 1

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

Inline comments:
In `@unstract/sdk1/src/unstract/sdk1/adapters/base1.py`:
- Line 539: Update the MiniMax subclass’s temperature declaration to retain the
base class’s Pydantic Field validation, including default 0.1 and ge=0/le=2
constraints, rather than redeclaring it as an unconstrained scalar. Preserve the
existing nullable float type.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 288ec4d5-fe33-44a6-b15c-b218f17ee909

📥 Commits

Reviewing files that changed from the base of the PR and between 09b6834 and 1e10ef8.

⛔ Files ignored due to path filters (1)
  • frontend/public/icons/adapter-icons/MiniMax.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • unstract/sdk1/src/unstract/sdk1/adapters/base1.py
  • unstract/sdk1/src/unstract/sdk1/adapters/llm1/__init__.py
  • unstract/sdk1/src/unstract/sdk1/adapters/llm1/minimax.py
  • unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json
  • unstract/sdk1/tests/test_branded_openai_adapters.py

Comment thread unstract/sdk1/src/unstract/sdk1/adapters/base1.py Outdated

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

Caution

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

⚠️ Outside diff range comments (1)
unstract/sdk1/src/unstract/sdk1/adapters/base1.py (1)

564-583: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

"Thinking always enabled for MiniMax-M2.7" is only partially enforced.

The validation blocks explicitly disabling thinking for minimax-m2* models (line 579-583), but if neither enable_thinking nor thinking is present in adapter_metadata at all, thinking stays at the class default (None) and no thinking param is sent — relying entirely on the JSON-schema UI default (enable_thinking: true) and/or the provider's own default to actually enable it. Any caller that invokes MiniMaxLLMParameters.validate()/LLM.__init__ directly (bypassing the schema-populated defaults — e.g. persisted adapter configs created before this default existed, or non-UI programmatic callers) would silently omit thinking for M2.7, which doesn't guarantee "always enabled" as stated in the PR objectives.

Consider enforcing the default at the validation layer itself, e.g.:

💡 Proposed fix to force adaptive thinking as the default for the M2 family
         thinking = adapter_metadata.get("thinking")
+        if thinking is None and model_id.lower().startswith("minimax-m2"):
+            thinking = {"type": "adaptive"}
+            adapter_metadata["thinking"] = thinking
         if thinking is not None:
🤖 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 `@unstract/sdk1/src/unstract/sdk1/adapters/base1.py` around lines 564 - 583,
Update the adapter metadata validation around the existing thinking handling so
minimax-m2* models default to {"type": "adaptive"} when neither enable_thinking
nor thinking is provided. Preserve explicit disabled-thinking rejection and
existing validation for supplied values, ensuring direct validation and LLM
initialization always send adaptive thinking for MiniMax-M2 models.
🤖 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.

Outside diff comments:
In `@unstract/sdk1/src/unstract/sdk1/adapters/base1.py`:
- Around line 564-583: Update the adapter metadata validation around the
existing thinking handling so minimax-m2* models default to {"type": "adaptive"}
when neither enable_thinking nor thinking is provided. Preserve explicit
disabled-thinking rejection and existing validation for supplied values,
ensuring direct validation and LLM initialization always send adaptive thinking
for MiniMax-M2 models.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 476807d6-7add-498f-a968-25dda55fe2c3

📥 Commits

Reviewing files that changed from the base of the PR and between 7c04f32 and b0a8525.

📒 Files selected for processing (6)
  • unstract/sdk1/src/unstract/sdk1/adapters/base1.py
  • unstract/sdk1/src/unstract/sdk1/adapters/llm1/minimax.py
  • unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json
  • unstract/sdk1/src/unstract/sdk1/llm.py
  • unstract/sdk1/tests/test_branded_openai_adapters.py
  • unstract/sdk1/tests/test_openai_compatible_adapter.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json

@chandrasekharan-zipstack

Copy link
Copy Markdown
Contributor

Thanks for the rework — routing through BaseChatCompletionParameters with the native minimax/ (and anthropic/) prefixes is the right call, and the reasoning / service_tier passthrough looks good.

One structural request on the cost side: could we drop the bespoke pricing engine (_ModelSpec / _TokenRates / _MODEL_SPECS, the static calculate_usage_cost, the local register_model call, and the calculate_usage_cost reflection hook added in llm.py) and rely on litellm's standard cost path, like every other adapter?

The only reason that engine is needed today is that our pinned litellm==1.85.1 has no minimax/MiniMax-M3 entry in its cost map at all, so M3 silently bills $0. That's now fixed upstream — litellm 1.90.0 added M3 with its _above_512k_tokens long-context tier. I've raised #2169 to bump litellm to 1.90.3, which prices M3 correctly (including the >512k rate switch) straight through the normal cost_per_token path. Verified on the bump branch:

minimax/MiniMax-M3: base $0.03 (<512k, @3e-7); $0.60 for a 1M-token prompt (@6e-7 above-512k)

Once #2169 lands, the standard path covers M3 with zero local pricing code — which avoids the drift/maintenance of a MiniMax-only cost table and keeps this adapter consistent with the rest.

Two follow-ups:

  • MiniMax-M2.7 is the one model litellm still doesn't price under the native minimax/ prefix (only sambanova/ and fireworks_ai/ variants exist upstream). I've filed an issue to add it: Add "minimax/MiniMax-M2.7" in "model_prices_and_context_window.json" BerriAI/litellm#33058. Until that lands, M2.7 bills $0 — acceptable as a known transient gap rather than a reason to keep a local engine.
  • Model field: every other LLM adapter (openai, anthropic, bedrock, openrouter, mistral, …) uses a free-text model string rather than an enum dropdown. Suggest matching that here — it's consistent and lets users select newer MiniMax models (M2.1 / M2.5 / lightning, and future releases) without a code change each time.

Happy to help coordinate sequencing with #2169.

@chandrasekharan-zipstack

Copy link
Copy Markdown
Contributor

@octo-patch TLDR from this comment

@octo-patch

Copy link
Copy Markdown
Contributor Author

Thanks for the review. I removed the adapter-specific model registration, pricing calculator, and shared billing hook; the adapter now uses the standard LiteLLM cost path from the merged dependency update, while keeping the model field free-text. I also added protocol-specific temperature validation and MiniMax-M2.x thinking coverage, and ran 25 focused tests plus four endpoint request captures.

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

Caution

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

⚠️ Outside diff range comments (1)
unstract/sdk1/src/unstract/sdk1/adapters/base1.py (1)

622-624: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix literal "None" coercion for missing models.

If a caller explicitly sets {"model": None} in adapter_metadata, adapter_metadata.get("model", "") evaluates to None. The subsequent str(None) call coerces it into the literal string "None". Because "None" is truthy, it bypasses the not model check and sends a malformed model ID to the upstream API.

Update the extraction to safely handle None values, mirroring the robust pattern used in the Mistral adapter below.

🐛 Proposed fix
     `@staticmethod`
     def validate_model(adapter_metadata: dict[str, "Any"]) -> str:
-        model = str(adapter_metadata.get("model", "")).strip()
+        raw_model = adapter_metadata.get("model")
+        model = str(raw_model).strip() if raw_model is not None else ""
         if not model:
             raise ValueError("model is required for the MiniMax adapter.")
🤖 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 `@unstract/sdk1/src/unstract/sdk1/adapters/base1.py` around lines 622 - 624,
Update the model extraction in the MiniMax adapter to handle an explicit None as
missing before string conversion, matching the robust handling used by the
Mistral adapter. Ensure None, empty, and whitespace-only values still trigger
the existing “model is required” ValueError, while valid model identifiers
remain stripped and accepted.
🧹 Nitpick comments (1)
unstract/sdk1/src/unstract/sdk1/adapters/base1.py (1)

591-604: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Future-proof the MiniMax-M2 prefix check.

The model_id.lower().startswith("minimax-m2") check correctly identifies current models like MiniMax-M2.7, but it will also unintentionally match future models like MiniMax-M20 or MiniMax-M200.

Consider tightening the check (e.g., matching "minimax-m2.", "minimax-m2-", or using a regex boundary) to ensure future model families don't accidentally inherit M2-specific thinking constraints.

🤖 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 `@unstract/sdk1/src/unstract/sdk1/adapters/base1.py` around lines 591 - 604,
Update both MiniMax-M2 checks in the thinking logic to require a model-family
boundary after “minimax-m2” (such as a dot, hyphen, or equivalent regex
boundary), so models like MiniMax-M20 and MiniMax-M200 do not match. Preserve
automatic adaptive thinking and the disabled-thinking validation for valid
MiniMax-M2 variants.
🤖 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.

Outside diff comments:
In `@unstract/sdk1/src/unstract/sdk1/adapters/base1.py`:
- Around line 622-624: Update the model extraction in the MiniMax adapter to
handle an explicit None as missing before string conversion, matching the robust
handling used by the Mistral adapter. Ensure None, empty, and whitespace-only
values still trigger the existing “model is required” ValueError, while valid
model identifiers remain stripped and accepted.

---

Nitpick comments:
In `@unstract/sdk1/src/unstract/sdk1/adapters/base1.py`:
- Around line 591-604: Update both MiniMax-M2 checks in the thinking logic to
require a model-family boundary after “minimax-m2” (such as a dot, hyphen, or
equivalent regex boundary), so models like MiniMax-M20 and MiniMax-M200 do not
match. Preserve automatic adaptive thinking and the disabled-thinking validation
for valid MiniMax-M2 variants.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 77326b44-0569-40ac-8939-f863ea0f799e

📥 Commits

Reviewing files that changed from the base of the PR and between b0a8525 and 12c516a.

📒 Files selected for processing (4)
  • unstract/sdk1/src/unstract/sdk1/adapters/base1.py
  • unstract/sdk1/src/unstract/sdk1/adapters/llm1/minimax.py
  • unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json
  • unstract/sdk1/tests/test_branded_openai_adapters.py
💤 Files with no reviewable changes (1)
  • unstract/sdk1/src/unstract/sdk1/adapters/llm1/minimax.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json

@octo-patch

Copy link
Copy Markdown
Contributor Author

Thanks for the review. I pushed a follow-up that rejects null or blank model values, tightens the MiniMax-M2 family boundary, and aligns both compatible protocols with the official 0-2 temperature range. I ran 29 focused tests, the repository pre-commit hooks, and four regional and protocol request captures.

@octo-patch

octo-patch commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Updated the branch to follow the standard LiteLLM cost path by removing local model registration, priority cost multiplication, and shared cache billing changes. The adapter retains protocol-native thinking defaults and uses validated internal context-window metadata that is stripped before requests. I ran 58 focused tests, 147 shared LLM tests, repository pre-commit hooks, Python compilation, and four regional and protocol request captures.

octo-patch and others added 5 commits July 16, 2026 02:11
…icing

The MiniMax adapter's JSON schema descriptions embedded per-token prices,
per-model context windows and the four regional endpoint URLs. Every other
LLM adapter keeps these one line long and links to the provider's docs, and
the adapter-ops skill template does the same.

Provider-owned facts go stale: MiniMax's pricing page currently advertises a
"permanent 50% discount", the model description named 2 of the 8 documented
chat models, and the model field is free text.

Keep the facts the adapter owns and links out for the rest:

- model: link to MiniMax's model list and pricing, keep the examples.
- api_base: state the routing rule (a path ending in /anthropic selects the
  Anthropic-compatible protocol), which is not discoverable from MiniMax's
  docs. Drop the URL list.
- service_tier: drop the 1.5x multiplier and note that cost tracking records
  standard-tier rates, since LiteLLM has no priority pricing for minimax.
- enable_thinking: M2.x, not just M2.7, keeps thinking enabled -- matches
  _is_minimax_m2_model().
- max_tokens: match the wording used by the other adapters.

The schema test asserted on the description prose, which pinned the copy in
place; the four endpoints it checked are already covered by the protocol
routing tests. Replace it with a guard that fails if a price is ever pasted
back into a description.
_MINIMAX_CONTEXT_WINDOWS hardcoded MiniMax-M3 and MiniMax-M2.7 and let every
other model fall through to LiteLLM. That is backwards: LiteLLM already has
the right window for M3, while it reports 1,000,000 for MiniMax-M2.1 and
MiniMax-M2.5 against a documented 204,800.

The model field is free text, so selecting M2.1 or M2.5 sized prompts around
a ~5x overstated window and the request would fail upstream.

MiniMax documents 204,800 for the whole M2.x family, so derive it from the
existing _is_minimax_m2_model() boundary instead of listing each model. This
also covers the -highspeed and -lightning variants. Unknown models still fall
through to LiteLLM, so future models keep working without a change here.
@chandrasekharan-zipstack chandrasekharan-zipstack changed the title Add MiniMax LLM adapter [FEAT] Add MiniMax LLM adapter Jul 20, 2026
@sonarqubecloud

Copy link
Copy Markdown

@chandrasekharan-zipstack
chandrasekharan-zipstack merged commit 4eb8811 into Zipstack:main Jul 20, 2026
12 checks passed
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