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
2 changes: 2 additions & 0 deletions nadirclaw/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"google": "GOOGLE_API_KEY",
"cohere": "COHERE_API_KEY",
"mistral": "MISTRAL_API_KEY",
"minimax": "MINIMAX_API_KEY",
}

# Alternative env vars checked as fallback (order matters)
Expand All @@ -49,6 +50,7 @@
"ollama/": "ollama",
"cohere/": "cohere",
"mistral/": "mistral",
"minimax/": "minimax",
"together_ai/": "together_ai",
"replicate/": "replicate",
}
Expand Down
4 changes: 4 additions & 0 deletions nadirclaw/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ def get_pool_for_model(model: str) -> Optional[str]:
"deepseek/deepseek-v4-pro": {"context_window": 1_000_000, "cost_per_m_input": 1.74, "cost_per_m_output": 3.48, "has_vision": False},
"deepseek/deepseek-chat": {"context_window": 128_000, "cost_per_m_input": 0.28, "cost_per_m_output": 0.42, "has_vision": False},
"deepseek/deepseek-reasoner": {"context_window": 128_000, "cost_per_m_input": 0.28, "cost_per_m_output": 0.42, "has_vision": False},
# MiniMax
"minimax/MiniMax-M3": {"context_window": 1_000_000, "cost_per_m_input": 0.60, "cost_per_m_output": 2.40, "has_vision": True},
# Ollama (local, no cost, context varies by model)
"ollama/llama3.1:8b": {"context_window": 128_000, "cost_per_m_input": 0, "cost_per_m_output": 0, "has_vision": False},
"ollama/qwen3:32b": {"context_window": 128_000, "cost_per_m_input": 0, "cost_per_m_output": 0, "has_vision": False},
Expand Down Expand Up @@ -212,6 +214,8 @@ def _merge_external_model_metadata() -> None:
"deepseek-v4-flash": "deepseek/deepseek-v4-flash",
"deepseek-v4-pro": "deepseek/deepseek-v4-pro",
"deepseek-r1": "deepseek/deepseek-reasoner",
"minimax": "minimax/MiniMax-M3",
"minimax-m3": "minimax/MiniMax-M3",
"llama": "ollama/llama3.1:8b",
}

Expand Down
7 changes: 7 additions & 0 deletions tests/test_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def tmp_credentials(tmp_path, monkeypatch):
for var in (
"ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_API_KEY",
"GEMINI_API_KEY", "COHERE_API_KEY", "MISTRAL_API_KEY",
"MINIMAX_API_KEY",
):
monkeypatch.delenv(var, raising=False)
return creds_file
Expand Down Expand Up @@ -133,6 +134,11 @@ def test_gemini_fallback_env(self, monkeypatch):
monkeypatch.setenv("GEMINI_API_KEY", "AIza-gemini")
assert get_credential("google") == "AIza-gemini"

def test_minimax_env(self, monkeypatch):
monkeypatch.setenv("MINIMAX_API_KEY", "mm-from-env")
assert get_credential("minimax") == "mm-from-env"
assert get_credential_source("minimax") == "env"


# ---------------------------------------------------------------------------
# Provider detection
Expand All @@ -149,6 +155,7 @@ class TestDetectProvider:
("gemini/gemini-3-flash", "google"),
("ollama/llama3", "ollama"),
("openai-codex/gpt-5.3-codex", "openai-codex"),
("minimax/MiniMax-M3", "minimax"),
("unknown-model", None),
])
def test_detect_provider(self, model, expected):
Expand Down
9 changes: 9 additions & 0 deletions tests/test_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ def test_deepseek(self):
assert resolve_alias("deepseek-v4-pro") == "deepseek/deepseek-v4-pro"
assert resolve_alias("deepseek-r1") == "deepseek/deepseek-reasoner"

def test_minimax(self):
assert resolve_alias("minimax") == "minimax/MiniMax-M3"
assert resolve_alias("minimax-m3") == "minimax/MiniMax-M3"


# ---------------------------------------------------------------------------
# detect_agentic
Expand Down Expand Up @@ -455,6 +459,10 @@ def test_deepseek_v4_cost(self):
cost = estimate_cost("deepseek/deepseek-v4-pro", 1_000_000, 1_000_000)
assert cost == pytest.approx(5.22)

def test_minimax_m3_cost(self):
cost = estimate_cost("minimax/MiniMax-M3", 1_000_000, 1_000_000)
assert cost == pytest.approx(3.0)

def test_unknown_model(self):
assert estimate_cost("unknown-xyz", 1000, 500) is None

Expand Down Expand Up @@ -690,6 +698,7 @@ def test_vision_models(self):
assert has_vision("gpt-4o") is True
assert has_vision("claude-sonnet-4-5-20250929") is True
assert has_vision("gemini-2.5-pro") is True
assert has_vision("minimax/MiniMax-M3") is True

def test_non_vision_models(self):
assert has_vision("deepseek/deepseek-chat") is False
Expand Down
Loading