Skip to content
Merged
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
3 changes: 2 additions & 1 deletion .github/workflows/tests-integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
steps:
- name: Set expected providers for integration tests
id: set_providers
run: echo "expected_providers=anthropic,bedrock,cerebras,cohere,databricks,deepseek,fireworks,gemini,groq,huggingface,inception,llama,mistral,moonshot,nebius,openai,openrouter,perplexity,portkey,together,sambanova,voyage,xai,zai" >> $GITHUB_OUTPUT
run: echo "expected_providers=anthropic,bedrock,cerebras,cohere,databricks,deepseek,fireworks,gemini,groq,huggingface,inception,llama,mistral,moonshot,nebius,openai,openrouter,perplexity,portkey,together,sambanova,voyage,xai,zai,minimax" >> $GITHUB_OUTPUT

- name: Set expected providers for local integration tests
id: set_local_providers
Expand Down Expand Up @@ -79,6 +79,7 @@ jobs:
VOYAGE_API_KEY: ${{ secrets.VOYAGE_API_KEY }}
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
EXPECTED_PROVIDERS: ${{ needs.expected-providers.outputs.providers }}
INCLUDE_LOCAL_PROVIDERS: "false"
run: |
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ dependencies = [
[project.optional-dependencies]

all = [
"any-llm-sdk[mistral,anthropic,huggingface,gemini,vertexai,cohere,cerebras,fireworks,groq,bedrock,azure,azureopenai,watsonx,together,sambanova,ollama,moonshot,nebius,xai,databricks,deepseek,inception,openai,openrouter,portkey,lmstudio,llama,voyage,perplexity,llamafile,llamacpp,sagemaker,gateway,zai]"
"any-llm-sdk[mistral,anthropic,huggingface,gemini,vertexai,cohere,cerebras,fireworks,groq,bedrock,azure,azureopenai,watsonx,together,sambanova,ollama,moonshot,nebius,xai,databricks,deepseek,inception,openai,openrouter,portkey,lmstudio,llama,voyage,perplexity,llamafile,llamacpp,sagemaker,gateway,zai,minimax]"
]

perplexity = []
Expand Down Expand Up @@ -105,6 +105,7 @@ openai = []
openrouter = []
portkey = []
sambanova = []
minimax = []
gateway = []
zai = []

Expand Down
1 change: 1 addition & 0 deletions src/any_llm/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class LLMProvider(StrEnum):
WATSONX = "watsonx"
XAI = "xai"
PERPLEXITY = "perplexity"
MINIMAX = "minimax"
ZAI = "zai"
GATEWAY = "gateway"

Expand Down
3 changes: 3 additions & 0 deletions src/any_llm/providers/minimax/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .minimax import MinimaxProvider

__all__ = ["MinimaxProvider"]
65 changes: 65 additions & 0 deletions src/any_llm/providers/minimax/minimax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from collections.abc import AsyncIterator
from typing import Any

from openai._streaming import AsyncStream
from openai.types.chat.chat_completion import ChatCompletion as OpenAIChatCompletion
from openai.types.chat.chat_completion_chunk import ChatCompletionChunk as OpenAIChatCompletionChunk

from any_llm.exceptions import UnsupportedParameterError
from any_llm.providers.openai.base import BaseOpenAIProvider
from any_llm.types.completion import ChatCompletion, ChatCompletionChunk, CompletionParams, Reasoning
from any_llm.utils.reasoning import process_streaming_reasoning_chunks


class MinimaxProvider(BaseOpenAIProvider):
API_BASE = "https://api.minimax.io/v1"
ENV_API_KEY_NAME = "MINIMAX_API_KEY"
PROVIDER_NAME = "minimax"
PROVIDER_DOCUMENTATION_URL = "https://www.minimax.io/platform_overview"

SUPPORTS_COMPLETION_PDF = False
SUPPORTS_COMPLETION_REASONING = True
SUPPORTS_COMPLETION_IMAGE = False
SUPPORTS_LIST_MODELS = False
SUPPORTS_EMBEDDING = False

def _convert_completion_response_async(
self, response: OpenAIChatCompletion | AsyncStream[OpenAIChatCompletionChunk]
) -> ChatCompletion | AsyncIterator[ChatCompletionChunk]:
"""Convert an OpenAI completion response with streaming reasoning support."""
if isinstance(response, OpenAIChatCompletion):
return self._convert_completion_response(response)

async def chunk_iterator() -> AsyncIterator[ChatCompletionChunk]:
async for chunk in response:
yield self._convert_completion_chunk_response(chunk)

def get_content(chunk: ChatCompletionChunk) -> str | None:
return chunk.choices[0].delta.content if len(chunk.choices) > 0 else None

def set_content(chunk: ChatCompletionChunk, content: str | None) -> ChatCompletionChunk:
chunk.choices[0].delta.content = content
return chunk

def set_reasoning(chunk: ChatCompletionChunk, reasoning: str) -> ChatCompletionChunk:
chunk.choices[0].delta.reasoning = Reasoning(content=reasoning)
return chunk

return process_streaming_reasoning_chunks(
chunk_iterator(),
get_content=get_content,
set_content=set_content,
set_reasoning=set_reasoning,
)

@staticmethod
def _convert_completion_params(params: CompletionParams, **kwargs: Any) -> dict[str, Any]:
# response_format is supported in the z.ai SDK, but the SDK doesn't yet have an async client
# so we can't use it in any-llm
if params.response_format is not None:
param = "response_format"
raise UnsupportedParameterError(param, "minimax")
# Copy of the logic from the base implementation because you can't use super() in a static method
converted_params = params.model_dump(exclude_none=True, exclude={"model_id", "messages"})
converted_params.update(kwargs)
return converted_params
2 changes: 2 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def provider_reasoning_model_map() -> dict[LLMProvider, str]:
LLMProvider.SAMBANOVA: "DeepSeek-R1-Distill-Llama-70B",
LLMProvider.TOGETHER: "OpenAI/gpt-oss-20B",
LLMProvider.PORTKEY: "@anthropic/claude-3-7-sonnet-latest",
LLMProvider.MINIMAX: "MiniMax-M2",
LLMProvider.ZAI: "glm-4.5-flash",
}

Expand Down Expand Up @@ -76,6 +77,7 @@ def provider_model_map() -> dict[LLMProvider, str]:
LLMProvider.PERPLEXITY: "sonar",
LLMProvider.OPENROUTER: "meta-llama/llama-3.3-8b-instruct:free",
LLMProvider.LLAMACPP: "N/A",
LLMProvider.MINIMAX: "MiniMax-M2",
LLMProvider.ZAI: "glm-4-32b-0414-128k",
}

Expand Down
Loading