Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# API Keys - At least one is required
#
# IMPORTANT: Choose ONE approach:
# - Native APIs (Gemini/OpenAI/XAI) for direct access
# - Native APIs (Gemini/OpenAI/XAI/MiniMax) for direct access
# - DIAL for unified enterprise access
# - OpenRouter for unified cloud access
# Having multiple unified providers creates ambiguity about which serves each model.
Expand All @@ -29,6 +29,9 @@ AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
# Get your X.AI API key from: https://console.x.ai/
XAI_API_KEY=your_xai_api_key_here

# Get your MiniMax API key from: https://platform.minimaxi.com/
MINIMAX_API_KEY=your_minimax_api_key_here

# Get your DIAL API key and configure host URL
# DIAL provides unified access to multiple AI models through a single API
DIAL_API_KEY=your_dial_api_key_here
Expand Down Expand Up @@ -105,6 +108,20 @@ DEFAULT_THINKING_MODE_THINKDEEP=high
# - grok3 (shorthand for grok-3)
# - grokfast (shorthand for grok-3-fast)
#
#
# Supported MiniMax models:
# - MiniMax-M2.5 (196K context, frontier reasoning)
# - MiniMax-M2.5-highspeed (204K context, fast reasoning)
# - MiniMax-M2.1 (204K context, advanced reasoning)
# - MiniMax-M2.1-highspeed (204K context, fast reasoning)
# - MiniMax-M2 (204K context, capable reasoning)
# - m2.5 (shorthand for MiniMax-M2.5)
# - m2.5-fast (shorthand for MiniMax-M2.5-highspeed)
# - m2.1 (shorthand for MiniMax-M2.1)
# - m2.1-fast (shorthand for MiniMax-M2.1-highspeed)
# - m2 (shorthand for MiniMax-M2)
# - minimax (shorthand for MiniMax-M2.5)
#
# Supported DIAL models (when available in your DIAL deployment):
# - o3-2025-04-16 (200K context, latest O3 release)
# - o4-mini-2025-04-16 (200K context, latest O4 mini)
Expand Down Expand Up @@ -133,6 +150,8 @@ DEFAULT_THINKING_MODE_THINKDEEP=high
# OPENAI_ALLOWED_MODELS=o4-mini # Single model standardization
# GOOGLE_ALLOWED_MODELS=flash,pro # Allow both Gemini models
# XAI_ALLOWED_MODELS=grok,grok-3-fast # Allow both GROK variants
# MINIMAX_ALLOWED_MODELS=MiniMax-M2.5 # Only allow M2.5 model
# MINIMAX_ALLOWED_MODELS=m2.5,m2.5-fast # Allow M2.5 and M2.5 Highspeed
# DIAL_ALLOWED_MODELS=o3,o4-mini # Only allow O3/O4 models via DIAL
# DIAL_ALLOWED_MODELS=opus-4.1,sonnet-4.1 # Only Claude 4.1 models (without thinking)
# DIAL_ALLOWED_MODELS=opus-4.1-thinking,sonnet-4.1-thinking # Only Claude 4.1 with thinking mode
Expand All @@ -142,6 +161,7 @@ DEFAULT_THINKING_MODE_THINKDEEP=high
# OPENAI_ALLOWED_MODELS=
# GOOGLE_ALLOWED_MODELS=
# XAI_ALLOWED_MODELS=
# MINIMAX_ALLOWED_MODELS=
# DIAL_ALLOWED_MODELS=

# Optional: Custom model configuration file path
Expand Down
121 changes: 121 additions & 0 deletions conf/minimax_models.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
{
"_README": {
"description": "Model metadata for MiniMax API access.",
"documentation": "https://github.com/BeehiveInnovations/pal-mcp-server/blob/main/docs/custom_models.md",
"usage": "Models listed here are exposed directly through the MiniMax provider. Aliases are case-insensitive.",
"field_notes": "Matches providers/shared/model_capabilities.py.",
"field_descriptions": {
"model_name": "The model identifier (e.g., 'MiniMax-M2.5', 'MiniMax-M2')",
"aliases": "Array of short names users can type instead of the full model name",
"context_window": "Total number of tokens the model can process (input + output combined)",
"max_output_tokens": "Maximum number of tokens the model can generate in a single response",
"supports_extended_thinking": "Whether the model supports extended reasoning tokens (MiniMax uses reasoning_split instead)",
"supports_json_mode": "Whether the model can guarantee valid JSON output",
"supports_function_calling": "Whether the model supports function/tool calling",
"supports_images": "Whether the model can process images/visual input",
"supports_temperature": "Whether the model accepts temperature parameter in API calls",
"description": "Human-readable description of the model",
"intelligence_score": "1-20 human rating used as the primary signal for auto-mode model ordering"
}
},
"models": [
{
"model_name": "MiniMax-M2.5",
"friendly_name": "MiniMax (M2.5)",
"aliases": [
"m2.5",
"minimax",
"minimax-m2.5"
],
"intelligence_score": 16,
"description": "MiniMax M2.5 (196K context) - Frontier reasoning model with tool calling",
"context_window": 196608,
"max_output_tokens": 196608,
"supports_extended_thinking": false,
"supports_system_prompts": true,
"supports_streaming": true,
"supports_function_calling": true,
"supports_json_mode": true,
"supports_images": false,
"supports_temperature": true
},
{
"model_name": "MiniMax-M2.5-highspeed",
"friendly_name": "MiniMax (M2.5 Highspeed)",
"aliases": [
"m2.5-fast",
"m2.5-highspeed",
"minimax-m2.5-fast"
],
"intelligence_score": 15,
"description": "MiniMax M2.5 Highspeed (204K context) - Fast reasoning model",
"context_window": 204800,
"max_output_tokens": 100000,
"supports_extended_thinking": false,
"supports_system_prompts": true,
"supports_streaming": true,
"supports_function_calling": true,
"supports_json_mode": true,
"supports_images": false,
"supports_temperature": true
},
{
"model_name": "MiniMax-M2.1",
"friendly_name": "MiniMax (M2.1)",
"aliases": [
"m2.1",
"minimax-m2.1"
],
"intelligence_score": 15,
"description": "MiniMax M2.1 (204K context) - Advanced reasoning model with tool calling",
"context_window": 204800,
"max_output_tokens": 100000,
"supports_extended_thinking": false,
"supports_system_prompts": true,
"supports_streaming": true,
"supports_function_calling": true,
"supports_json_mode": true,
"supports_images": false,
"supports_temperature": true
},
{
"model_name": "MiniMax-M2.1-highspeed",
"friendly_name": "MiniMax (M2.1 Highspeed)",
"aliases": [
"m2.1-fast",
"m2.1-highspeed",
"minimax-m2.1-fast"
],
"intelligence_score": 14,
"description": "MiniMax M2.1 Highspeed (204K context) - Fast reasoning model",
"context_window": 204800,
"max_output_tokens": 100000,
"supports_extended_thinking": false,
"supports_system_prompts": true,
"supports_streaming": true,
"supports_function_calling": true,
"supports_json_mode": true,
"supports_images": false,
"supports_temperature": true
},
{
"model_name": "MiniMax-M2",
"friendly_name": "MiniMax (M2)",
"aliases": [
"m2",
"minimax-m2"
],
"intelligence_score": 13,
"description": "MiniMax M2 (204K context) - Capable reasoning model with tool calling",
"context_window": 204800,
"max_output_tokens": 100000,
"supports_extended_thinking": false,
"supports_system_prompts": true,
"supports_streaming": true,
"supports_function_calling": true,
"supports_json_mode": true,
"supports_images": false,
"supports_temperature": true
}
]
}
2 changes: 2 additions & 0 deletions providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .azure_openai import AzureOpenAIProvider
from .base import ModelProvider
from .gemini import GeminiModelProvider
from .minimax import MiniMaxModelProvider
from .openai import OpenAIModelProvider
from .openai_compatible import OpenAICompatibleProvider
from .openrouter import OpenRouterProvider
Expand All @@ -16,6 +17,7 @@
"ModelProviderRegistry",
"AzureOpenAIProvider",
"GeminiModelProvider",
"MiniMaxModelProvider",
"OpenAIModelProvider",
"OpenAICompatibleProvider",
"OpenRouterProvider",
Expand Down
86 changes: 86 additions & 0 deletions providers/minimax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""MiniMax model provider implementation."""

import logging
from typing import TYPE_CHECKING, ClassVar, Optional

if TYPE_CHECKING:
from tools.models import ToolModelCategory

from .openai_compatible import OpenAICompatibleProvider
from .registries.minimax import MiniMaxModelRegistry
from .registry_provider_mixin import RegistryBackedProviderMixin
from .shared import ModelCapabilities, ProviderType

logger = logging.getLogger(__name__)


class MiniMaxModelProvider(RegistryBackedProviderMixin, OpenAICompatibleProvider):
"""Integration for MiniMax models exposed over an OpenAI-style API.

Publishes capability metadata for the officially supported deployments and
maps tool-category preferences to the appropriate MiniMax model.
"""

FRIENDLY_NAME = "MiniMax"

REGISTRY_CLASS = MiniMaxModelRegistry
MODEL_CAPABILITIES: ClassVar[dict[str, ModelCapabilities]] = {}

# Canonical model identifiers used for category routing.
PRIMARY_MODEL = "MiniMax-M2.5"
FAST_MODEL = "MiniMax-M2.5-highspeed"

def __init__(self, api_key: str, **kwargs):
"""Initialize MiniMax provider with API key."""
# Set MiniMax base URL
kwargs.setdefault("base_url", "https://api.minimax.io/v1")
self._ensure_registry()
super().__init__(api_key, **kwargs)
self._invalidate_capability_cache()

def get_provider_type(self) -> ProviderType:
"""Get the provider type."""
return ProviderType.MINIMAX

def get_preferred_model(self, category: "ToolModelCategory", allowed_models: list[str]) -> Optional[str]:
"""Get MiniMax's preferred model for a given category from allowed models.

Args:
category: The tool category requiring a model
allowed_models: Pre-filtered list of models allowed by restrictions

Returns:
Preferred model name or None
"""
from tools.models import ToolModelCategory

if not allowed_models:
return None

if category == ToolModelCategory.EXTENDED_REASONING:
# Prefer M2.5 for advanced reasoning tasks
if self.PRIMARY_MODEL in allowed_models:
return self.PRIMARY_MODEL
if self.FAST_MODEL in allowed_models:
return self.FAST_MODEL
return allowed_models[0]

elif category == ToolModelCategory.FAST_RESPONSE:
# Prefer M2.5-highspeed for speed
if self.FAST_MODEL in allowed_models:
return self.FAST_MODEL
if self.PRIMARY_MODEL in allowed_models:
return self.PRIMARY_MODEL
return allowed_models[0]

else: # BALANCED or default
# Prefer M2.5 for balanced use
if self.PRIMARY_MODEL in allowed_models:
return self.PRIMARY_MODEL
if self.FAST_MODEL in allowed_models:
return self.FAST_MODEL
return allowed_models[0]
Comment on lines +45 to +82
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.

medium

The logic for selecting a preferred model is a bit repetitive across the different categories. The EXTENDED_REASONING and BALANCED categories have identical logic, and the FAST_RESPONSE category just swaps the preference order. You can simplify this by defining the preference order in a list and then iterating through it to make the code more concise and maintainable.

    def get_preferred_model(self, category: "ToolModelCategory", allowed_models: list[str]) -> Optional[str]:
        """Get MiniMax's preferred model for a given category from allowed models.

        Args:
            category: The tool category requiring a model
            allowed_models: Pre-filtered list of models allowed by restrictions

        Returns:
            Preferred model name or None
        """
        from tools.models import ToolModelCategory

        if not allowed_models:
            return None

        if category == ToolModelCategory.FAST_RESPONSE:
            preference_order = [self.FAST_MODEL, self.PRIMARY_MODEL]
        else:  # BALANCED, EXTENDED_REASONING, or default
            preference_order = [self.PRIMARY_MODEL, self.FAST_MODEL]

        for model in preference_order:
            if model in allowed_models:
                return model

        return allowed_models[0]



# Load registry data at import time
MiniMaxModelProvider._ensure_registry()
2 changes: 2 additions & 0 deletions providers/registries/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .custom import CustomEndpointModelRegistry
from .dial import DialModelRegistry
from .gemini import GeminiModelRegistry
from .minimax import MiniMaxModelRegistry
from .openai import OpenAIModelRegistry
from .openrouter import OpenRouterModelRegistry
from .xai import XAIModelRegistry
Expand All @@ -13,6 +14,7 @@
"CustomEndpointModelRegistry",
"DialModelRegistry",
"GeminiModelRegistry",
"MiniMaxModelRegistry",
"OpenAIModelRegistry",
"OpenRouterModelRegistry",
"XAIModelRegistry",
Expand Down
19 changes: 19 additions & 0 deletions providers/registries/minimax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Registry loader for MiniMax model capabilities."""

from __future__ import annotations

from ..shared import ProviderType
from .base import CapabilityModelRegistry


class MiniMaxModelRegistry(CapabilityModelRegistry):
"""Capability registry backed by ``conf/minimax_models.json``."""

def __init__(self, config_path: str | None = None) -> None:
super().__init__(
env_var_name="MINIMAX_MODELS_CONFIG_PATH",
default_filename="minimax_models.json",
provider=ProviderType.MINIMAX,
friendly_prefix="MiniMax ({model})",
config_path=config_path,
)
2 changes: 2 additions & 0 deletions providers/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class ModelProviderRegistry:
ProviderType.OPENAI, # Direct OpenAI access
ProviderType.AZURE, # Azure-hosted OpenAI deployments
ProviderType.XAI, # Direct X.AI GROK access
ProviderType.MINIMAX, # Direct MiniMax access
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Apply MiniMax allowlist before fallback model selection

By adding ProviderType.MINIMAX to PROVIDER_PRIORITY_ORDER, auto-mode can now prefer MiniMax models in get_preferred_fallback_model, but MINIMAX_ALLOWED_MODELS is not wired into ModelRestrictionService.ENV_VARS (utils/model_restrictions.py), so registry-level filtering still treats all MiniMax models as allowed. In deployments that restrict MiniMax to a subset (for example only m2), the registry can still select MiniMax-M2.5, and the request then fails later when provider-level checks reject that model, instead of selecting an actually allowed model up front.

Useful? React with 👍 / 👎.

ProviderType.DIAL, # DIAL unified API access
ProviderType.CUSTOM, # Local/self-hosted models
ProviderType.OPENROUTER, # Catch-all for cloud models
Expand Down Expand Up @@ -336,6 +337,7 @@ def _get_api_key_for_provider(cls, provider_type: ProviderType) -> Optional[str]
ProviderType.OPENAI: "OPENAI_API_KEY",
ProviderType.AZURE: "AZURE_OPENAI_API_KEY",
ProviderType.XAI: "XAI_API_KEY",
ProviderType.MINIMAX: "MINIMAX_API_KEY",
ProviderType.OPENROUTER: "OPENROUTER_API_KEY",
ProviderType.CUSTOM: "CUSTOM_API_KEY", # Can be empty for providers that don't need auth
ProviderType.DIAL: "DIAL_API_KEY",
Expand Down
1 change: 1 addition & 0 deletions providers/shared/provider_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class ProviderType(Enum):
OPENAI = "openai"
AZURE = "azure"
XAI = "xai"
MINIMAX = "minimax"
OPENROUTER = "openrouter"
CUSTOM = "custom"
DIAL = "dial"
Loading