-
Notifications
You must be signed in to change notification settings - Fork 633
Add MiniMax LLM adapter #2156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add MiniMax LLM adapter #2156
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| from typing import Any | ||
|
|
||
| from unstract.sdk1.adapters.base1 import BaseAdapter, MiniMaxLLMParameters | ||
| from unstract.sdk1.adapters.enums import AdapterTypes | ||
|
|
||
| DESCRIPTION = ( | ||
| "Adapter for MiniMax's OpenAI-compatible API. " | ||
| "Supply a model name and your MiniMax API key; the endpoint is preconfigured." | ||
| ) | ||
|
|
||
|
|
||
| class MiniMaxLLMAdapter(MiniMaxLLMParameters, BaseAdapter): | ||
| @staticmethod | ||
| def get_id() -> str: | ||
| return "minimax|4f0e4241-2430-4921-81bf-8b2c6040d8d2" | ||
|
|
||
| @staticmethod | ||
| def get_metadata() -> dict[str, Any]: | ||
| return { | ||
| "name": "MiniMax", | ||
| "version": "1.0.0", | ||
| "adapter": MiniMaxLLMAdapter, | ||
| "description": DESCRIPTION, | ||
| "is_active": True, | ||
| } | ||
|
|
||
| @staticmethod | ||
| def get_name() -> str: | ||
| return "MiniMax" | ||
|
|
||
| @staticmethod | ||
| def get_description() -> str: | ||
| return DESCRIPTION | ||
|
|
||
| @staticmethod | ||
| def get_provider() -> str: | ||
| return "minimax" | ||
|
|
||
| @staticmethod | ||
| def get_icon() -> str: | ||
| return "/icons/adapter-icons/MiniMax.png" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The shipped
Both 29953 bytes. Rendering it confirms it's the OpenRouter fork-arrow mark. Users would see OpenRouter's branding on the MiniMax card in the adapter picker. Needs the real MiniMax logo. |
||
|
|
||
| @staticmethod | ||
| def get_adapter_type() -> AdapterTypes: | ||
| return AdapterTypes.LLM | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| { | ||
| "title": "MiniMax", | ||
| "type": "object", | ||
| "required": [ | ||
| "adapter_name", | ||
| "api_key", | ||
| "model" | ||
| ], | ||
| "properties": { | ||
| "adapter_name": { | ||
| "type": "string", | ||
| "title": "Name", | ||
| "default": "", | ||
| "description": "Provide a unique name for this adapter instance. Example: minimax-1" | ||
| }, | ||
| "api_key": { | ||
| "type": "string", | ||
| "title": "API Key", | ||
| "format": "password", | ||
| "description": "Your MiniMax API key from [platform.minimax.io](https://platform.minimax.io)." | ||
| }, | ||
| "model": { | ||
| "type": "string", | ||
| "title": "Model", | ||
| "default": "MiniMax-M3", | ||
| "description": "The MiniMax model name. Example: MiniMax-M3" | ||
| }, | ||
| "api_base": { | ||
| "type": "string", | ||
| "format": "url", | ||
| "title": "API Base", | ||
| "default": "https://api.minimax.io/v1", | ||
| "description": "MiniMax OpenAI-compatible endpoint. Pre-filled with the default; change only if MiniMax moves the base URL or you use a regional endpoint." | ||
| }, | ||
| "max_tokens": { | ||
| "type": "number", | ||
| "minimum": 0, | ||
| "multipleOf": 1, | ||
| "title": "Maximum Output Tokens", | ||
| "default": 4096, | ||
| "description": "Maximum number of output tokens to limit LLM replies. Leave it empty to use the provider default." | ||
| }, | ||
| "max_retries": { | ||
| "type": "number", | ||
| "minimum": 0, | ||
| "multipleOf": 1, | ||
| "title": "Max Retries", | ||
| "default": 5, | ||
| "description": "The maximum number of times to retry a request if it fails." | ||
| }, | ||
| "timeout": { | ||
| "type": "number", | ||
| "minimum": 0, | ||
| "multipleOf": 1, | ||
| "title": "Timeout", | ||
| "default": 900, | ||
| "description": "Timeout in seconds." | ||
| }, | ||
| "enable_reasoning": { | ||
| "type": "boolean", | ||
| "title": "Enable Reasoning", | ||
| "default": false, | ||
| "description": "Toggle on for reasoning models to route reasoning_effort via the OpenAI-compatible request body." | ||
| } | ||
|
Comment on lines
+59
to
+64
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The adapter routes reasoning through Prompt To Fix With AIThis is a comment left during a code review.
Path: unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json
Line: 59-64
Comment:
**`enable_reasoning` toggle silently has no effect on MiniMax**
The adapter routes reasoning through `OpenAICompatibleLLMParameters.validate()`, which sends `reasoning_effort` in `extra_body`. MiniMax's OpenAI-compatible API does not recognise `reasoning_effort`; it exposes deep thinking via a `thinking: {type: "adaptive"}` field instead ([MiniMax docs](https://platform.minimax.io/docs/guides/text-generation)). A user who turns this toggle on will have their `temperature` stripped from the request (potentially changing generation behaviour) while MiniMax silently ignores the unknown `reasoning_effort` key — no actual reasoning is activated. Is there a plan to map `enable_reasoning` → `thinking: {type: "adaptive"}` in MiniMax's request body, or should the `enable_reasoning` section be removed from `minimax.json` until that mapping is implemented?
How can I resolve this? If you propose a fix, please make it concise.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirmed — this is a real issue, not just a doc mismatch. The reasoning path in Severity is bounded to the opt-in case: it only fires if a user flips the toggle. It won't auto-enable, since Fix: dropping the |
||
| }, | ||
| "allOf": [ | ||
| { | ||
| "if": { | ||
| "properties": { | ||
| "enable_reasoning": { | ||
| "const": true | ||
| } | ||
| }, | ||
| "required": [ | ||
| "enable_reasoning" | ||
| ] | ||
| }, | ||
| "then": { | ||
| "properties": { | ||
| "reasoning_effort": { | ||
| "type": "string", | ||
| "enum": [ | ||
| "low", | ||
| "medium", | ||
| "high" | ||
| ], | ||
| "default": "medium", | ||
| "title": "Reasoning Effort", | ||
| "description": "Sets the reasoning strength when Enable Reasoning is on." | ||
| } | ||
| }, | ||
| "required": [ | ||
| "reasoning_effort" | ||
| ] | ||
| } | ||
| }, | ||
| { | ||
| "if": { | ||
| "properties": { | ||
| "enable_reasoning": { | ||
| "const": false | ||
| } | ||
| }, | ||
| "required": [ | ||
| "enable_reasoning" | ||
| ] | ||
| }, | ||
| "then": { | ||
| "properties": {} | ||
| } | ||
| } | ||
| ] | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Routing through
custom_openai/silently zeroes out cost tracking for MiniMax._validate_branded_openai_compatibledelegates toOpenAICompatibleLLMParameters.validate(), whosevalidate_model()unconditionally prependscustom_openai/.cost_modelis only populated when the endpoint is OpenAI's own (base1.py:483), so it stays unset here, andaudit.py:98ends up callingcost_per_token(model="custom_openai/MiniMax-M3")— which raises, gets swallowed by the bareexcept, and recordscost_in_dollars = 0.0atdebuglevel.LiteLLM already ships a native MiniMax provider, and it prices M3:
So every MiniMax token would bill as $0 in usage tracking, silently.
This is why
OpenRouterLLMParametersextendsBaseChatCompletionParametersinstead ofOpenAICompatibleLLMParameters— its own docstring says so: "Routed through LiteLLM's nativeopenrouter/provider so per-token costs resolve and reasoning params map without provider-specific workarounds." MiniMax should follow OpenRouter's template, not NVIDIA Build's.Worth noting NVIDIA Build's
custom_openai/routing is correct for NVIDIA — LiteLLM prices zeronvidia_nim/chat models (3 entries, all rerankers, all$0.0), so there's nothing to forfeit there. That reason doesn't carry over to MiniMax.Bonus:
MinimaxChatConfig.get_api_base()already returnshttps://api.minimax.io/v1, so going native lets you drop the hardcoded_MINIMAX_API_BASEconstant entirely.