mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-07-09 16:00:45 +00:00
Add MiniMax provider (#980)
This commit is contained in:
parent
7633337972
commit
081fcfcda6
23 changed files with 488 additions and 28 deletions
|
|
@ -26,6 +26,10 @@ KIMI_API_KEY=""
|
|||
WAFER_API_KEY=""
|
||||
|
||||
|
||||
# MiniMax Config (Anthropic-compatible Messages at api.minimax.io/anthropic/v1)
|
||||
MINIMAX_API_KEY=""
|
||||
|
||||
|
||||
# OpenCode Zen (opencode.ai/zen/v1) and OpenCode Go (opencode.ai/zen/go/v1) share OPENCODE_API_KEY
|
||||
OPENCODE_API_KEY=""
|
||||
|
||||
|
|
@ -69,7 +73,7 @@ OLLAMA_BASE_URL="http://localhost:11434"
|
|||
|
||||
# All Claude model requests are mapped to these models, plain model is fallback
|
||||
# Format: provider_type/model/name
|
||||
# Valid providers: "nvidia_nim" | "open_router" | "gemini" | "deepseek" | "mistral" | "mistral_codestral" | "opencode" | "opencode_go" | "wafer" | "kimi" | "cerebras" | "groq" | "fireworks" | "cloudflare" | "zai" | "lmstudio" | "llamacpp" | "ollama"
|
||||
# Valid providers: "nvidia_nim" | "open_router" | "gemini" | "deepseek" | "mistral" | "mistral_codestral" | "opencode" | "opencode_go" | "wafer" | "kimi" | "minimax" | "cerebras" | "groq" | "fireworks" | "cloudflare" | "zai" | "lmstudio" | "llamacpp" | "ollama"
|
||||
MODEL_OPUS=
|
||||
MODEL_SONNET=
|
||||
MODEL_HAIKU=
|
||||
|
|
@ -88,6 +92,7 @@ FCC_SMOKE_MODEL_LLAMACPP=
|
|||
FCC_SMOKE_MODEL_OLLAMA=
|
||||
FCC_SMOKE_MODEL_KIMI=
|
||||
FCC_SMOKE_MODEL_WAFER=
|
||||
FCC_SMOKE_MODEL_MINIMAX=
|
||||
FCC_SMOKE_MODEL_OPENCODE=
|
||||
FCC_SMOKE_MODEL_OPENCODE_GO=
|
||||
FCC_SMOKE_MODEL_ZAI=
|
||||
|
|
@ -121,6 +126,7 @@ LMSTUDIO_PROXY=""
|
|||
LLAMACPP_PROXY=""
|
||||
KIMI_PROXY=""
|
||||
WAFER_PROXY=""
|
||||
MINIMAX_PROXY=""
|
||||
OPENCODE_PROXY=""
|
||||
OPENCODE_GO_PROXY=""
|
||||
ZAI_PROXY=""
|
||||
|
|
|
|||
|
|
@ -359,6 +359,8 @@ into Anthropic usage fields for Claude-compatible clients. Cloudflare uses its
|
|||
account-scoped Workers AI OpenAI-compatible Chat Completions endpoint for
|
||||
`@cf/...` model IDs, while account ID composition, model search, and
|
||||
Cloudflare-specific reasoning deltas stay in the Cloudflare provider client.
|
||||
MiniMax uses its Anthropic-compatible Messages endpoint; its provider client owns
|
||||
the MiniMax-specific `adaptive` thinking request shape.
|
||||
NIM reasoning budget control is also treated as a provider-owned best-effort
|
||||
downgrade: if an upstream NIM deployment rejects explicit budget control, FCC
|
||||
retries without the budget while preserving thinking enablement.
|
||||
|
|
|
|||
30
README.md
30
README.md
|
|
@ -57,7 +57,7 @@ Free Claude Code routes Anthropic Messages API traffic from Claude Code (CLI and
|
|||
- Drop-in proxy for Claude Code's Anthropic API calls (`/v1/messages`, `/v1/models`).
|
||||
- Drop-in proxy for Codex via the OpenAI Responses API (`/v1/responses`).
|
||||
- `fcc-claude` and `fcc-codex` launchers that read the current Admin UI port and auth token each time they start.
|
||||
- 18 provider backends: NVIDIA NIM, OpenRouter, Google AI Studio (Gemini), DeepSeek, Mistral La Plateforme, Mistral Codestral, OpenCode Zen, OpenCode Go, Wafer, Kimi, Cerebras Inference, Groq, Fireworks AI, Cloudflare, Z.ai, LM Studio, llama.cpp, and Ollama.
|
||||
- 19 provider backends: NVIDIA NIM, OpenRouter, Google AI Studio (Gemini), DeepSeek, Mistral La Plateforme, Mistral Codestral, OpenCode Zen, OpenCode Go, Wafer, Kimi, MiniMax, Cerebras Inference, Groq, Fireworks AI, Cloudflare, Z.ai, LM Studio, llama.cpp, and Ollama.
|
||||
- Per-model routing for Claude Code: send Opus, Sonnet, Haiku, and fallback traffic to different providers.
|
||||
- Native Claude Code `/model` picker support through the proxy's `/v1/models` endpoint (see [Model Picker](#model-picker)).
|
||||
- Native Codex `/model` picker support when launched through `fcc-codex`, using a generated local model catalog.
|
||||
|
|
@ -276,7 +276,15 @@ This provider calls Kimi's **Anthropic-compatible** Messages API (`https://api.m
|
|||
|
||||
Browse models at [platform.moonshot.ai](https://platform.moonshot.ai).
|
||||
|
||||
### 11. [Cerebras Inference](https://inference-docs.cerebras.ai/quickstart)
|
||||
### 11. [MiniMax](https://platform.minimax.io/)
|
||||
|
||||
Get a key from [MiniMax](https://platform.minimax.io/user-center/basic-information/interface-key).
|
||||
|
||||
In the Admin UI, paste it into `MINIMAX_API_KEY`, then set `MODEL` to a MiniMax slug such as `minimax/MiniMax-M3`.
|
||||
|
||||
This provider calls MiniMax's **Anthropic-compatible** Messages API (`https://api.minimax.io/anthropic/v1/messages`). `MiniMax-M3` is the recommended default because MiniMax documents controllable Anthropic thinking for that model; other MiniMax models remain discoverable through the provider model list.
|
||||
|
||||
### 12. [Cerebras Inference](https://inference-docs.cerebras.ai/quickstart)
|
||||
|
||||
Sign up and create an API key in the [Cerebras Cloud Console](https://cloud.cerebras.ai) (see [Quickstart](https://inference-docs.cerebras.ai/quickstart)).
|
||||
|
||||
|
|
@ -284,7 +292,7 @@ In the Admin UI, set `CEREBRAS_API_KEY`, then route with `MODEL` such as `cerebr
|
|||
|
||||
Cerebras exposes an OpenAI-compatible API at `https://api.cerebras.ai/v1` ([OpenAI compatibility](https://inference-docs.cerebras.ai/resources/openai)). Non-standard request fields should go in `extra_body` when using the OpenAI client; see the same page. For reasoning models and parameters, see [Reasoning](https://inference-docs.cerebras.ai/capabilities/reasoning). This proxy follows other OpenAI-compat adapters for thinking via `reasoning_content` when Claude-style thinking is enabled.
|
||||
|
||||
### 12. [Groq](https://console.groq.com/)
|
||||
### 13. [Groq](https://console.groq.com/)
|
||||
|
||||
Get an API key at [console.groq.com/keys](https://console.groq.com/keys).
|
||||
|
||||
|
|
@ -296,7 +304,7 @@ Reasoning-heavy models expose extra knobs documented under [Groq reasoning](http
|
|||
|
||||
Browse models at [console.groq.com/docs/models](https://console.groq.com/docs/models).
|
||||
|
||||
### 13. [Fireworks AI](https://fireworks.ai/)
|
||||
### 14. [Fireworks AI](https://fireworks.ai/)
|
||||
|
||||
Get an API key at [fireworks.ai/account/api-keys](https://fireworks.ai/account/api-keys).
|
||||
|
||||
|
|
@ -306,7 +314,7 @@ Fireworks exposes an **Anthropic-compatible** Messages API at `https://api.firew
|
|||
|
||||
Browse models at [fireworks.ai/models](https://fireworks.ai/models).
|
||||
|
||||
### 14. [Cloudflare](https://developers.cloudflare.com/workers-ai/)
|
||||
### 15. [Cloudflare](https://developers.cloudflare.com/workers-ai/)
|
||||
|
||||
Create a Cloudflare API token and copy your account ID from the Cloudflare dashboard.
|
||||
|
||||
|
|
@ -314,7 +322,7 @@ In the Admin UI, set `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID`, then se
|
|||
|
||||
This provider calls Cloudflare's account-scoped **OpenAI-compatible** Chat Completions API at `https://api.cloudflare.com/client/v4/accounts/<account_id>/ai/v1/chat/completions`. Use literal Workers AI model IDs, including the `@cf/` prefix when the catalog model includes it.
|
||||
|
||||
### 15. [Z.ai](https://z.ai/)
|
||||
### 16. [Z.ai](https://z.ai/)
|
||||
|
||||
Get an API key at [Z.ai/manage-apikey/apikey-list](https://z.ai/manage-apikey/apikey-list).
|
||||
|
||||
|
|
@ -329,13 +337,13 @@ Popular examples:
|
|||
|
||||
Browse models at [Z.ai](https://z.ai).
|
||||
|
||||
### 16. [LM Studio](https://lmstudio.ai/)
|
||||
### 17. [LM Studio](https://lmstudio.ai/)
|
||||
|
||||
Start LM Studio's local server and load a model. In the Admin UI, keep or update `LM_STUDIO_BASE_URL`, then set `MODEL` to the model identifier shown by LM Studio, prefixed with `lmstudio/`.
|
||||
|
||||
Prefer models with tool-use support for Claude Code workflows.
|
||||
|
||||
### 17. [llama.cpp](https://github.com/ggml-org/llama.cpp)
|
||||
### 18. [llama.cpp](https://github.com/ggml-org/llama.cpp)
|
||||
|
||||
Start `llama-server` with an Anthropic-compatible `/v1/messages` endpoint and enough context for Claude Code requests.
|
||||
|
||||
|
|
@ -343,7 +351,7 @@ In the Admin UI, keep or update `LLAMACPP_BASE_URL`, then set `MODEL` to the loc
|
|||
|
||||
For local coding models, context size matters. If llama.cpp returns HTTP 400 for normal Claude Code requests, increase `--ctx-size` and verify the model/server build supports the requested features.
|
||||
|
||||
### 18. [Ollama](https://ollama.com/)
|
||||
### 19. [Ollama](https://ollama.com/)
|
||||
|
||||
Run Ollama and pull a model:
|
||||
|
||||
|
|
@ -356,7 +364,7 @@ In the Admin UI, keep or update `OLLAMA_BASE_URL`, then set `MODEL` to the same
|
|||
|
||||
`OLLAMA_BASE_URL` is the Ollama server root; do not append `/v1`. Example model slugs include `ollama/llama3.1` and `ollama/llama3.1:8b`.
|
||||
|
||||
### 18. Mix Providers By Model Tier
|
||||
### 20. Mix Providers By Model Tier
|
||||
|
||||
Each model tier can use a different provider by setting `MODEL_OPUS`, `MODEL_SONNET`, and `MODEL_HAIKU` in the Admin UI. Leave a tier blank to inherit `MODEL`. These tier overrides apply to Claude model names that contain `opus`, `sonnet`, or `haiku`. Codex uses the Admin `MODEL` default through `fcc-codex` unless a session requests a provider-prefixed slug directly.
|
||||
|
||||
|
|
@ -571,7 +579,7 @@ Important pieces:
|
|||
- `fcc-codex` registers a custom `fcc` provider that points Codex at the local proxy's `/v1/responses` endpoint.
|
||||
- Model routing resolves Claude model names to `MODEL_OPUS`, `MODEL_SONNET`, `MODEL_HAIKU`, or `MODEL`.
|
||||
- NIM, DeepSeek, OpenCode Zen, and OpenCode Go use OpenAI chat streaming translated into Anthropic SSE.
|
||||
- Wafer, OpenRouter, Kimi, Fireworks AI, Z.ai, LM Studio, llama.cpp, and Ollama use Anthropic Messages style transports where applicable (with provider-specific quirks and model-list URLs).
|
||||
- Wafer, OpenRouter, Kimi, MiniMax, Fireworks AI, Z.ai, LM Studio, llama.cpp, and Ollama use Anthropic Messages style transports where applicable (with provider-specific quirks and model-list URLs).
|
||||
- The proxy normalizes thinking blocks, tool calls, token usage metadata, and provider errors into the shape each client expects.
|
||||
- Request optimizations answer trivial Claude Code probes locally to save latency and quota.
|
||||
|
||||
|
|
|
|||
|
|
@ -543,6 +543,12 @@ _NON_PROVIDER_FIELDS: tuple[ConfigFieldSpec, ...] = (
|
|||
"smoke",
|
||||
advanced=True,
|
||||
),
|
||||
ConfigFieldSpec(
|
||||
"FCC_SMOKE_MODEL_MINIMAX",
|
||||
"Smoke MiniMax Model",
|
||||
"smoke",
|
||||
advanced=True,
|
||||
),
|
||||
ConfigFieldSpec(
|
||||
"FCC_SMOKE_MODEL_WAFER",
|
||||
"Smoke Wafer Model",
|
||||
|
|
|
|||
|
|
@ -40,6 +40,13 @@ _PROVIDER_FIELD_OVERRIDES: dict[str, dict[str, Any]] = {
|
|||
"label": "Fireworks API Key",
|
||||
"description": "Fireworks AI inference API key.",
|
||||
},
|
||||
"MINIMAX_API_KEY": {
|
||||
"label": "MiniMax API Key",
|
||||
"description": (
|
||||
"MiniMax API key for the Anthropic-compatible Messages API at "
|
||||
"api.minimax.io/anthropic/v1."
|
||||
),
|
||||
},
|
||||
"CLOUDFLARE_API_TOKEN": {
|
||||
"label": "Cloudflare API Token",
|
||||
"description": (
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ NVIDIA_NIM_DEFAULT_BASE = "https://integrate.api.nvidia.com/v1"
|
|||
# Moonshot Kimi Anthropic-compatible Messages API (POST …/messages).
|
||||
KIMI_DEFAULT_BASE = "https://api.moonshot.ai/anthropic/v1"
|
||||
WAFER_DEFAULT_BASE = "https://pass.wafer.ai/v1"
|
||||
MINIMAX_DEFAULT_BASE = "https://api.minimax.io/anthropic/v1"
|
||||
# DeepSeek Chat Completions API; cache usage is reported on this endpoint.
|
||||
DEEPSEEK_DEFAULT_BASE = "https://api.deepseek.com"
|
||||
FIREWORKS_DEFAULT_BASE = "https://api.fireworks.ai/inference/v1"
|
||||
|
|
@ -171,6 +172,24 @@ PROVIDER_CATALOG: dict[str, ProviderDescriptor] = {
|
|||
"native_anthropic",
|
||||
),
|
||||
),
|
||||
"minimax": ProviderDescriptor(
|
||||
provider_id="minimax",
|
||||
display_name="MiniMax",
|
||||
transport_type="anthropic_messages",
|
||||
credential_env="MINIMAX_API_KEY",
|
||||
credential_url="https://platform.minimax.io/user-center/basic-information/interface-key",
|
||||
credential_attr="minimax_api_key",
|
||||
default_base_url=MINIMAX_DEFAULT_BASE,
|
||||
proxy_attr="minimax_proxy",
|
||||
capabilities=(
|
||||
"chat",
|
||||
"streaming",
|
||||
"tools",
|
||||
"thinking",
|
||||
"native_anthropic",
|
||||
"rate_limit",
|
||||
),
|
||||
),
|
||||
"cerebras": ProviderDescriptor(
|
||||
provider_id="cerebras",
|
||||
display_name="Cerebras",
|
||||
|
|
@ -284,8 +303,9 @@ PROVIDER_CATALOG: dict[str, ProviderDescriptor] = {
|
|||
}
|
||||
|
||||
# Key order:
|
||||
# NVIDIA NIM first (README default), DeepSeek fourth, Wafer ninth / Kimi tenth; then cerebras /
|
||||
# groq / fireworks / Cloudflare overlap; remainder and locals last per project plan (
|
||||
# NVIDIA NIM first (README default), DeepSeek fourth, Wafer ninth / Kimi tenth;
|
||||
# MiniMax follows the native Anthropic remotes; then cerebras / groq / fireworks /
|
||||
# Cloudflare overlap; remainder and locals last per project plan (
|
||||
# github.com/cheahjs/free-llm-api-resources Free Providers TOC as rough guide beyond fixed slots).
|
||||
# ``SUPPORTED_PROVIDER_IDS`` inherits this insertion order for UI and error-message listing.
|
||||
SUPPORTED_PROVIDER_IDS: tuple[str, ...] = tuple(PROVIDER_CATALOG.keys())
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ class Settings(BaseSettings):
|
|||
# ==================== Wafer Config ====================
|
||||
wafer_api_key: str = Field(default="", validation_alias="WAFER_API_KEY")
|
||||
|
||||
# ==================== MiniMax Config ====================
|
||||
minimax_api_key: str = Field(default="", validation_alias="MINIMAX_API_KEY")
|
||||
|
||||
# ==================== OpenCode Zen / OpenCode Go ====================
|
||||
# Same key from opencode.ai/auth; zen uses prefix ``opencode/``, Go uses ``opencode_go/``.
|
||||
opencode_api_key: str = Field(default="", validation_alias="OPENCODE_API_KEY")
|
||||
|
|
@ -117,6 +120,7 @@ class Settings(BaseSettings):
|
|||
llamacpp_proxy: str = Field(default="", validation_alias="LLAMACPP_PROXY")
|
||||
kimi_proxy: str = Field(default="", validation_alias="KIMI_PROXY")
|
||||
wafer_proxy: str = Field(default="", validation_alias="WAFER_PROXY")
|
||||
minimax_proxy: str = Field(default="", validation_alias="MINIMAX_PROXY")
|
||||
opencode_proxy: str = Field(default="", validation_alias="OPENCODE_PROXY")
|
||||
opencode_go_proxy: str = Field(default="", validation_alias="OPENCODE_GO_PROXY")
|
||||
zai_proxy: str = Field(default="", validation_alias="ZAI_PROXY")
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from config.provider_catalog import (
|
|||
KIMI_DEFAULT_BASE,
|
||||
LLAMACPP_DEFAULT_BASE,
|
||||
LMSTUDIO_DEFAULT_BASE,
|
||||
MINIMAX_DEFAULT_BASE,
|
||||
MISTRAL_DEFAULT_BASE,
|
||||
NVIDIA_NIM_DEFAULT_BASE,
|
||||
OLLAMA_DEFAULT_BASE,
|
||||
|
|
@ -30,6 +31,7 @@ __all__ = (
|
|||
"KIMI_DEFAULT_BASE",
|
||||
"LLAMACPP_DEFAULT_BASE",
|
||||
"LMSTUDIO_DEFAULT_BASE",
|
||||
"MINIMAX_DEFAULT_BASE",
|
||||
"MISTRAL_DEFAULT_BASE",
|
||||
"NVIDIA_NIM_DEFAULT_BASE",
|
||||
"OLLAMA_DEFAULT_BASE",
|
||||
|
|
|
|||
10
providers/minimax/__init__.py
Normal file
10
providers/minimax/__init__.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
"""MiniMax provider exports."""
|
||||
|
||||
from providers.defaults import MINIMAX_DEFAULT_BASE
|
||||
|
||||
from .client import MiniMaxProvider
|
||||
|
||||
__all__ = [
|
||||
"MINIMAX_DEFAULT_BASE",
|
||||
"MiniMaxProvider",
|
||||
]
|
||||
63
providers/minimax/client.py
Normal file
63
providers/minimax/client.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
"""MiniMax provider implementation (Anthropic-compatible Messages API)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from providers.base import ProviderConfig
|
||||
from providers.defaults import MINIMAX_DEFAULT_BASE
|
||||
from providers.transports.anthropic_messages import (
|
||||
AnthropicMessagesTransport,
|
||||
NativeMessagesRequestPolicy,
|
||||
build_native_messages_request_body,
|
||||
)
|
||||
|
||||
_ANTHROPIC_VERSION = "2023-06-01"
|
||||
_REQUEST_POLICY = NativeMessagesRequestPolicy(provider_name="MINIMAX")
|
||||
|
||||
|
||||
class MiniMaxProvider(AnthropicMessagesTransport):
|
||||
"""MiniMax using Anthropic-compatible Messages at api.minimax.io/anthropic/v1."""
|
||||
|
||||
def __init__(self, config: ProviderConfig):
|
||||
super().__init__(
|
||||
config,
|
||||
provider_name="MINIMAX",
|
||||
default_base_url=MINIMAX_DEFAULT_BASE,
|
||||
)
|
||||
|
||||
def _build_request_body(
|
||||
self, request: Any, thinking_enabled: bool | None = None
|
||||
) -> dict:
|
||||
effective_thinking_enabled = self._is_thinking_enabled(
|
||||
request, thinking_enabled
|
||||
)
|
||||
return build_native_messages_request_body(
|
||||
request,
|
||||
thinking_enabled=effective_thinking_enabled,
|
||||
policy=_REQUEST_POLICY,
|
||||
postprocessors=(_apply_minimax_thinking_policy,),
|
||||
)
|
||||
|
||||
def _request_headers(self) -> dict[str, str]:
|
||||
return {
|
||||
"Accept": "text/event-stream",
|
||||
"Content-Type": "application/json",
|
||||
"x-api-key": self._api_key,
|
||||
"anthropic-version": _ANTHROPIC_VERSION,
|
||||
}
|
||||
|
||||
def _model_list_headers(self) -> dict[str, str]:
|
||||
return {
|
||||
"x-api-key": self._api_key,
|
||||
"anthropic-version": _ANTHROPIC_VERSION,
|
||||
}
|
||||
|
||||
|
||||
def _apply_minimax_thinking_policy(
|
||||
body: dict[str, Any], _request: Any, thinking_enabled: bool
|
||||
) -> None:
|
||||
"""Use MiniMax's documented Anthropic thinking control values."""
|
||||
body["thinking"] = (
|
||||
{"type": "adaptive"} if thinking_enabled else {"type": "disabled"}
|
||||
)
|
||||
|
|
@ -79,6 +79,12 @@ def _create_wafer(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|||
return WaferProvider(config)
|
||||
|
||||
|
||||
def _create_minimax(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
||||
from providers.minimax import MiniMaxProvider
|
||||
|
||||
return MiniMaxProvider(config)
|
||||
|
||||
|
||||
def _create_opencode(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
||||
from providers.opencode import OpenCodeProvider
|
||||
|
||||
|
|
@ -138,6 +144,7 @@ PROVIDER_FACTORIES: dict[str, ProviderFactory] = {
|
|||
"opencode_go": _create_opencode_go,
|
||||
"wafer": _create_wafer,
|
||||
"kimi": _create_kimi,
|
||||
"minimax": _create_minimax,
|
||||
"cerebras": _create_cerebras,
|
||||
"groq": _create_groq,
|
||||
"fireworks": _create_fireworks,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||
|
||||
[project]
|
||||
name = "free-claude-code"
|
||||
version = "2.4.4"
|
||||
version = "2.5.0"
|
||||
description = "Middleware between Claude Code CLI (Anthropic API) and NVIDIA NIM"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.14.0"
|
||||
|
|
|
|||
|
|
@ -129,7 +129,8 @@ uv run pytest smoke/product -n 0 -s --tb=short
|
|||
- `FCC_SMOKE_MODEL_NVIDIA_NIM`, `FCC_SMOKE_MODEL_OPEN_ROUTER`,
|
||||
`FCC_SMOKE_MODEL_MISTRAL`, `FCC_SMOKE_MODEL_MISTRAL_CODESTRAL`,
|
||||
`FCC_SMOKE_MODEL_DEEPSEEK`, `FCC_SMOKE_MODEL_KIMI`,
|
||||
`FCC_SMOKE_MODEL_WAFER`, `FCC_SMOKE_MODEL_OPENCODE`, `FCC_SMOKE_MODEL_OPENCODE_GO`,
|
||||
`FCC_SMOKE_MODEL_WAFER`, `FCC_SMOKE_MODEL_MINIMAX`,
|
||||
`FCC_SMOKE_MODEL_OPENCODE`, `FCC_SMOKE_MODEL_OPENCODE_GO`,
|
||||
`FCC_SMOKE_MODEL_ZAI`, `FCC_SMOKE_MODEL_FIREWORKS`, `FCC_SMOKE_MODEL_CLOUDFLARE`,
|
||||
`FCC_SMOKE_MODEL_GEMINI`, `FCC_SMOKE_MODEL_GROQ`, `FCC_SMOKE_MODEL_CEREBRAS`,
|
||||
`FCC_SMOKE_MODEL_LMSTUDIO`,
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ PROVIDER_SMOKE_DEFAULT_MODELS: dict[str, str] = {
|
|||
"llamacpp": "llamacpp/local-model",
|
||||
"ollama": "ollama/llama3.1",
|
||||
"wafer": "wafer/DeepSeek-V4-Pro",
|
||||
"minimax": "minimax/MiniMax-M3",
|
||||
"opencode": "opencode/gpt-5.3-codex",
|
||||
"opencode_go": "opencode_go/minimax-m2.7",
|
||||
"zai": "zai/glm-5.1",
|
||||
|
|
@ -245,6 +246,8 @@ class SmokeConfig:
|
|||
return bool(self.settings.ollama_base_url.strip())
|
||||
if provider == "wafer":
|
||||
return bool(self.settings.wafer_api_key.strip())
|
||||
if provider == "minimax":
|
||||
return bool(self.settings.minimax_api_key.strip())
|
||||
if provider == "fireworks":
|
||||
return bool(self.settings.fireworks_api_key.strip())
|
||||
if provider == "opencode":
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ def _make_mock_settings(**overrides):
|
|||
mock.codestral_api_key = "test_codestral_key"
|
||||
mock.deepseek_api_key = "test_deepseek_key"
|
||||
mock.wafer_api_key = "test_wafer_key"
|
||||
mock.minimax_api_key = "test_minimax_key"
|
||||
mock.opencode_api_key = "test_opencode_key"
|
||||
mock.zai_api_key = "test_zai_key"
|
||||
mock.lm_studio_base_url = "http://localhost:1234/v1"
|
||||
|
|
@ -47,6 +48,7 @@ def _make_mock_settings(**overrides):
|
|||
mock.kimi_proxy = ""
|
||||
mock.kimi_api_key = "test_kimi_key"
|
||||
mock.wafer_proxy = ""
|
||||
mock.minimax_proxy = ""
|
||||
mock.opencode_proxy = ""
|
||||
mock.opencode_go_proxy = ""
|
||||
mock.zai_proxy = ""
|
||||
|
|
|
|||
|
|
@ -124,6 +124,21 @@ def test_model_router_routes_wafer_provider_model_directly(settings):
|
|||
assert routed.resolved.provider_model_ref == "wafer/DeepSeek-V4-Pro"
|
||||
|
||||
|
||||
def test_model_router_routes_minimax_provider_model_directly(settings):
|
||||
routed = ModelRouter(settings).resolve_messages_request(
|
||||
MessagesRequest(
|
||||
model="minimax/MiniMax-M3",
|
||||
max_tokens=100,
|
||||
messages=[Message(role="user", content="hello")],
|
||||
)
|
||||
)
|
||||
|
||||
assert routed.request.model == "MiniMax-M3"
|
||||
assert routed.resolved.provider_id == "minimax"
|
||||
assert routed.resolved.provider_model == "MiniMax-M3"
|
||||
assert routed.resolved.provider_model_ref == "minimax/MiniMax-M3"
|
||||
|
||||
|
||||
def test_model_router_routes_gateway_encoded_provider_model_directly(settings):
|
||||
routed = ModelRouter(settings).resolve_messages_request(
|
||||
MessagesRequest(
|
||||
|
|
|
|||
|
|
@ -284,6 +284,16 @@ class TestSettings:
|
|||
settings = Settings()
|
||||
assert settings.wafer_api_key == "wafer-key"
|
||||
|
||||
def test_minimax_settings_from_env(self, monkeypatch):
|
||||
"""MiniMax key and proxy env vars load into settings."""
|
||||
from config.settings import Settings
|
||||
|
||||
monkeypatch.setenv("MINIMAX_API_KEY", "minimax-key")
|
||||
monkeypatch.setenv("MINIMAX_PROXY", "http://proxy.test:8080")
|
||||
settings = Settings()
|
||||
assert settings.minimax_api_key == "minimax-key"
|
||||
assert settings.minimax_proxy == "http://proxy.test:8080"
|
||||
|
||||
def test_cloudflare_settings_from_env(self, monkeypatch):
|
||||
"""Cloudflare token, account, and proxy env vars load into settings."""
|
||||
from config.settings import Settings
|
||||
|
|
@ -869,6 +879,7 @@ class TestPerModelMapping:
|
|||
assert parse_provider_type("llamacpp/model") == "llamacpp"
|
||||
assert parse_provider_type("ollama/llama3.1") == "ollama"
|
||||
assert parse_provider_type("wafer/DeepSeek-V4-Pro") == "wafer"
|
||||
assert parse_provider_type("minimax/MiniMax-M3") == "minimax"
|
||||
assert (
|
||||
parse_provider_type("cloudflare/@cf/moonshotai/kimi-k2.6") == "cloudflare"
|
||||
)
|
||||
|
|
@ -891,6 +902,7 @@ class TestPerModelMapping:
|
|||
assert parse_model_name("llamacpp/model") == "model"
|
||||
assert parse_model_name("ollama/llama3.1") == "llama3.1"
|
||||
assert parse_model_name("wafer/DeepSeek-V4-Pro") == "DeepSeek-V4-Pro"
|
||||
assert parse_model_name("minimax/MiniMax-M3") == "MiniMax-M3"
|
||||
assert (
|
||||
parse_model_name("cloudflare/@cf/moonshotai/kimi-k2.6")
|
||||
== "@cf/moonshotai/kimi-k2.6"
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from providers.groq import GroqProvider
|
|||
from providers.kimi import KimiProvider
|
||||
from providers.llamacpp import LlamaCppProvider
|
||||
from providers.lmstudio import LMStudioProvider
|
||||
from providers.minimax import MiniMaxProvider
|
||||
from providers.mistral import MistralProvider
|
||||
from providers.nvidia_nim import NvidiaNimProvider
|
||||
from providers.ollama import OllamaProvider
|
||||
|
|
@ -84,6 +85,7 @@ def test_provider_and_platform_registries_include_advertised_builtins() -> None:
|
|||
"mistral_codestral": CodestralProvider,
|
||||
"deepseek": DeepSeekProvider,
|
||||
"kimi": KimiProvider,
|
||||
"minimax": MiniMaxProvider,
|
||||
"fireworks": FireworksProvider,
|
||||
"cloudflare": CloudflareProvider,
|
||||
"lmstudio": LMStudioProvider,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ _EXPECTED_PROVIDER_ORDER: tuple[str, ...] = (
|
|||
"opencode_go",
|
||||
"wafer",
|
||||
"kimi",
|
||||
"minimax",
|
||||
"cerebras",
|
||||
"groq",
|
||||
"fireworks",
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ def _settings(**overrides):
|
|||
"deepseek_api_key": "",
|
||||
"kimi_api_key": "",
|
||||
"wafer_api_key": "",
|
||||
"minimax_api_key": "",
|
||||
"opencode_api_key": "",
|
||||
"zai_api_key": "",
|
||||
"gemini_api_key": "",
|
||||
|
|
@ -146,6 +147,22 @@ def test_wafer_provider_configuration_uses_api_key(monkeypatch) -> None:
|
|||
assert models[0].full_model == PROVIDER_SMOKE_DEFAULT_MODELS["wafer"]
|
||||
|
||||
|
||||
def test_minimax_provider_configuration_uses_api_key(monkeypatch) -> None:
|
||||
monkeypatch.delenv("FCC_SMOKE_MODEL_MINIMAX", raising=False)
|
||||
config = _smoke_config(
|
||||
settings=_settings(
|
||||
model="ollama/llama3.1",
|
||||
ollama_base_url="",
|
||||
minimax_api_key="minimax-key",
|
||||
)
|
||||
)
|
||||
|
||||
assert config.has_provider_configuration("minimax")
|
||||
models = config.provider_smoke_models()
|
||||
assert models[0].provider == "minimax"
|
||||
assert models[0].full_model == PROVIDER_SMOKE_DEFAULT_MODELS["minimax"]
|
||||
|
||||
|
||||
def test_cloudflare_provider_configuration_requires_token_and_account(
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
|
|
|
|||
255
tests/providers/test_minimax.py
Normal file
255
tests/providers/test_minimax.py
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
"""Tests for MiniMax native Anthropic Messages provider."""
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from api.models.anthropic import Message, MessagesRequest, Tool
|
||||
from core.anthropic.stream_contracts import parse_sse_text, thinking_content
|
||||
from providers.base import ProviderConfig
|
||||
from providers.minimax import MINIMAX_DEFAULT_BASE, MiniMaxProvider
|
||||
|
||||
|
||||
class FakeResponse:
|
||||
def __init__(self, *, lines=None):
|
||||
self.status_code = 200
|
||||
self._lines = lines or []
|
||||
self.is_closed = False
|
||||
self.headers = httpx.Headers()
|
||||
self.request = httpx.Request(
|
||||
"POST", "https://api.minimax.io/anthropic/v1/messages"
|
||||
)
|
||||
|
||||
async def aiter_lines(self):
|
||||
for line in self._lines:
|
||||
yield line
|
||||
|
||||
async def aclose(self):
|
||||
self.is_closed = True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def minimax_config():
|
||||
return ProviderConfig(
|
||||
api_key="test-minimax-key",
|
||||
base_url=MINIMAX_DEFAULT_BASE,
|
||||
rate_limit=10,
|
||||
rate_window=60,
|
||||
enable_thinking=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_rate_limiter():
|
||||
@asynccontextmanager
|
||||
async def _slot():
|
||||
yield
|
||||
|
||||
with patch(
|
||||
"providers.transports.anthropic_messages.transport.GlobalRateLimiter"
|
||||
) as mock:
|
||||
instance = mock.get_scoped_instance.return_value
|
||||
|
||||
async def _passthrough(fn, *args, **kwargs):
|
||||
return await fn(*args, **kwargs)
|
||||
|
||||
instance.execute_with_retry = AsyncMock(side_effect=_passthrough)
|
||||
instance.concurrency_slot.side_effect = _slot
|
||||
yield instance
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def minimax_provider(minimax_config):
|
||||
return MiniMaxProvider(minimax_config)
|
||||
|
||||
|
||||
def test_default_base_url():
|
||||
assert MINIMAX_DEFAULT_BASE == "https://api.minimax.io/anthropic/v1"
|
||||
|
||||
|
||||
def test_init_uses_default_base_url_and_strips_trailing_slash(minimax_config):
|
||||
config = minimax_config.model_copy(update={"base_url": f"{MINIMAX_DEFAULT_BASE}/"})
|
||||
|
||||
with patch("httpx.AsyncClient"):
|
||||
provider = MiniMaxProvider(config)
|
||||
|
||||
assert provider._api_key == "test-minimax-key"
|
||||
assert provider._base_url == MINIMAX_DEFAULT_BASE
|
||||
assert provider._provider_name == "MINIMAX"
|
||||
|
||||
|
||||
def test_headers_use_x_api_key(minimax_provider):
|
||||
headers = minimax_provider._request_headers()
|
||||
|
||||
assert headers["x-api-key"] == "test-minimax-key"
|
||||
assert headers["Accept"] == "text/event-stream"
|
||||
assert headers["Content-Type"] == "application/json"
|
||||
assert headers["anthropic-version"] == "2023-06-01"
|
||||
assert "Authorization" not in headers
|
||||
assert minimax_provider._model_list_headers() == {
|
||||
"x-api-key": "test-minimax-key",
|
||||
"anthropic-version": "2023-06-01",
|
||||
}
|
||||
|
||||
|
||||
def test_build_request_body_uses_adaptive_thinking_and_preserves_tools(
|
||||
minimax_provider,
|
||||
):
|
||||
request = MessagesRequest.model_validate(
|
||||
{
|
||||
"model": "MiniMax-M3",
|
||||
"messages": [Message(role="user", content="Hello")],
|
||||
"tools": [
|
||||
Tool(
|
||||
name="echo",
|
||||
description="Echo input",
|
||||
input_schema={"type": "object", "properties": {}},
|
||||
)
|
||||
],
|
||||
"thinking": {"type": "enabled", "budget_tokens": 2048},
|
||||
}
|
||||
)
|
||||
|
||||
body = minimax_provider._build_request_body(request)
|
||||
|
||||
assert body["model"] == "MiniMax-M3"
|
||||
assert body["tools"][0]["name"] == "echo"
|
||||
assert body["thinking"] == {"type": "adaptive"}
|
||||
assert body["stream"] is True
|
||||
|
||||
|
||||
def test_build_request_body_enables_adaptive_thinking_by_default(minimax_provider):
|
||||
request = MessagesRequest(
|
||||
model="MiniMax-M3",
|
||||
messages=[Message(role="user", content="Hello")],
|
||||
)
|
||||
|
||||
body = minimax_provider._build_request_body(request)
|
||||
|
||||
assert body["thinking"] == {"type": "adaptive"}
|
||||
|
||||
|
||||
def test_build_request_body_honors_no_thinking(minimax_provider):
|
||||
request = MessagesRequest(
|
||||
model="MiniMax-M3",
|
||||
messages=[Message(role="user", content="Hello")],
|
||||
)
|
||||
|
||||
body = minimax_provider._build_request_body(request, thinking_enabled=False)
|
||||
|
||||
assert body["thinking"] == {"type": "disabled"}
|
||||
|
||||
|
||||
def test_build_request_body_preserves_request_disabled_thinking(minimax_provider):
|
||||
request = MessagesRequest.model_validate(
|
||||
{
|
||||
"model": "MiniMax-M3",
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
"thinking": {"type": "disabled"},
|
||||
}
|
||||
)
|
||||
|
||||
body = minimax_provider._build_request_body(request, thinking_enabled=True)
|
||||
|
||||
assert body["thinking"] == {"type": "disabled"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_lists_models_from_anthropic_compatible_models_endpoint(
|
||||
minimax_provider,
|
||||
):
|
||||
with patch.object(
|
||||
minimax_provider._client,
|
||||
"get",
|
||||
new_callable=AsyncMock,
|
||||
return_value=httpx.Response(
|
||||
200,
|
||||
json={
|
||||
"data": [
|
||||
{"id": "MiniMax-M3", "type": "model"},
|
||||
{"id": "MiniMax-M2.7", "type": "model"},
|
||||
]
|
||||
},
|
||||
request=httpx.Request("GET", "https://api.minimax.io/anthropic/v1/models"),
|
||||
),
|
||||
) as mock_get:
|
||||
assert await minimax_provider.list_model_ids() == frozenset(
|
||||
{"MiniMax-M3", "MiniMax-M2.7"}
|
||||
)
|
||||
|
||||
mock_get.assert_awaited_once_with(
|
||||
"/models",
|
||||
headers={
|
||||
"x-api-key": "test-minimax-key",
|
||||
"anthropic-version": "2023-06-01",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stream_uses_messages_path_and_preserves_native_thinking(
|
||||
minimax_provider,
|
||||
):
|
||||
request = MessagesRequest(
|
||||
model="MiniMax-M3",
|
||||
messages=[Message(role="user", content="hi")],
|
||||
)
|
||||
response = FakeResponse(
|
||||
lines=[
|
||||
"event: message_start",
|
||||
'data: {"type":"message_start"}',
|
||||
"",
|
||||
"event: content_block_start",
|
||||
'data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}',
|
||||
"",
|
||||
"event: content_block_delta",
|
||||
'data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"plan"}}',
|
||||
"",
|
||||
"event: content_block_stop",
|
||||
'data: {"type":"content_block_stop","index":0}',
|
||||
"",
|
||||
"event: content_block_start",
|
||||
'data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}}',
|
||||
"",
|
||||
"event: content_block_delta",
|
||||
'data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"done"}}',
|
||||
"",
|
||||
"event: content_block_stop",
|
||||
'data: {"type":"content_block_stop","index":1}',
|
||||
"",
|
||||
"event: message_stop",
|
||||
'data: {"type":"message_stop"}',
|
||||
"",
|
||||
]
|
||||
)
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
minimax_provider._client, "build_request", return_value=MagicMock()
|
||||
) as mock_build,
|
||||
patch.object(
|
||||
minimax_provider._client,
|
||||
"send",
|
||||
new_callable=AsyncMock,
|
||||
return_value=response,
|
||||
),
|
||||
):
|
||||
events = [event async for event in minimax_provider.stream_response(request)]
|
||||
|
||||
parsed = parse_sse_text("".join(events))
|
||||
assert [event.event for event in parsed] == [
|
||||
"message_start",
|
||||
"content_block_start",
|
||||
"content_block_delta",
|
||||
"content_block_stop",
|
||||
"content_block_start",
|
||||
"content_block_delta",
|
||||
"content_block_stop",
|
||||
"message_stop",
|
||||
]
|
||||
assert thinking_content(parsed) == "plan"
|
||||
assert response.is_closed
|
||||
assert mock_build.call_args.args[:2] == ("POST", "/messages")
|
||||
assert mock_build.call_args.kwargs["headers"]["x-api-key"] == "test-minimax-key"
|
||||
|
|
@ -5,7 +5,11 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
|||
import pytest
|
||||
|
||||
from config.nim import NimSettings
|
||||
from config.provider_catalog import PROVIDER_CATALOG, ZAI_DEFAULT_BASE
|
||||
from config.provider_catalog import (
|
||||
MINIMAX_DEFAULT_BASE,
|
||||
PROVIDER_CATALOG,
|
||||
ZAI_DEFAULT_BASE,
|
||||
)
|
||||
from config.provider_ids import SUPPORTED_PROVIDER_IDS
|
||||
from providers.cerebras import CerebrasProvider
|
||||
from providers.cloudflare import CloudflareProvider
|
||||
|
|
@ -18,6 +22,7 @@ from providers.groq import GroqProvider
|
|||
from providers.kimi import KimiProvider
|
||||
from providers.llamacpp import LlamaCppProvider
|
||||
from providers.lmstudio import LMStudioProvider
|
||||
from providers.minimax import MiniMaxProvider
|
||||
from providers.mistral import MistralProvider
|
||||
from providers.nvidia_nim import NvidiaNimProvider
|
||||
from providers.ollama import OllamaProvider
|
||||
|
|
@ -40,6 +45,7 @@ def _make_settings(**overrides):
|
|||
mock.codestral_api_key = "test_codestral_key"
|
||||
mock.deepseek_api_key = "test_deepseek_key"
|
||||
mock.wafer_api_key = "test_wafer_key"
|
||||
mock.minimax_api_key = "test_minimax_key"
|
||||
mock.opencode_api_key = "test_opencode_key"
|
||||
mock.zai_api_key = "test_zai_key"
|
||||
mock.lm_studio_base_url = "http://localhost:1234/v1"
|
||||
|
|
@ -54,6 +60,7 @@ def _make_settings(**overrides):
|
|||
mock.kimi_proxy = ""
|
||||
mock.kimi_api_key = "test_kimi_key"
|
||||
mock.wafer_proxy = ""
|
||||
mock.minimax_proxy = ""
|
||||
mock.opencode_proxy = ""
|
||||
mock.opencode_go_proxy = ""
|
||||
mock.zai_proxy = ""
|
||||
|
|
@ -133,6 +140,15 @@ def test_zai_provider_config_ignores_stale_base_url_setting():
|
|||
assert config.base_url == ZAI_DEFAULT_BASE
|
||||
|
||||
|
||||
def test_minimax_descriptor_uses_native_anthropic_transport():
|
||||
descriptor = PROVIDER_CATALOG["minimax"]
|
||||
|
||||
assert descriptor.transport_type == "anthropic_messages"
|
||||
assert descriptor.default_base_url == MINIMAX_DEFAULT_BASE
|
||||
assert descriptor.credential_env == "MINIMAX_API_KEY"
|
||||
assert "native_anthropic" in descriptor.capabilities
|
||||
|
||||
|
||||
def test_cloudflare_descriptor_uses_api_root_not_account_url():
|
||||
descriptor = PROVIDER_CATALOG["cloudflare"]
|
||||
|
||||
|
|
@ -207,6 +223,7 @@ def test_create_provider_instantiates_each_builtin():
|
|||
"mistral_codestral": CodestralProvider,
|
||||
"deepseek": DeepSeekProvider,
|
||||
"kimi": KimiProvider,
|
||||
"minimax": MiniMaxProvider,
|
||||
"fireworks": FireworksProvider,
|
||||
"cloudflare": CloudflareProvider,
|
||||
"lmstudio": LMStudioProvider,
|
||||
|
|
|
|||
22
uv.lock
generated
22
uv.lock
generated
|
|
@ -350,34 +350,34 @@ wheels = [
|
|||
|
||||
[package.optional-dependencies]
|
||||
cudart = [
|
||||
{ name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "nvidia-cuda-runtime" },
|
||||
]
|
||||
cufft = [
|
||||
{ name = "nvidia-cufft", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "nvidia-cufft" },
|
||||
]
|
||||
cufile = [
|
||||
{ name = "nvidia-cufile", marker = "sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cufile" },
|
||||
]
|
||||
cupti = [
|
||||
{ name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "nvidia-cuda-cupti" },
|
||||
]
|
||||
curand = [
|
||||
{ name = "nvidia-curand", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "nvidia-curand" },
|
||||
]
|
||||
cusolver = [
|
||||
{ name = "nvidia-cusolver", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "nvidia-cusolver" },
|
||||
]
|
||||
cusparse = [
|
||||
{ name = "nvidia-cusparse", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "nvidia-cusparse" },
|
||||
]
|
||||
nvjitlink = [
|
||||
{ name = "nvidia-nvjitlink", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "nvidia-nvjitlink" },
|
||||
]
|
||||
nvrtc = [
|
||||
{ name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "nvidia-cuda-nvrtc" },
|
||||
]
|
||||
nvtx = [
|
||||
{ name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "nvidia-nvtx" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -561,7 +561,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "free-claude-code"
|
||||
version = "2.4.4"
|
||||
version = "2.5.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue