feat: Add top 10 OpenRouter models to CLI model selection
- Added top-ranked models from OpenRouter.ai rankings: - Kimi K2.5 0127 (#1 ranked) - Claude Opus 4.5 / Sonnet 4.5 - Gemini 3 Flash Preview / 2.5 Flash / 2.5 Flash Lite - Deepseek V3.2 - Grok 4.1 Fast / Grok Code Fast 1 - Minimax M2.1 - Refactored SHALLOW_AGENT_OPTIONS and DEEP_AGENT_OPTIONS to module-level constants - Added comprehensive tests for OpenRouter model configuration in tests/test_cli_utils.py - Preserved existing free models (Nemotron, GLM) The model options dictionaries were moved from function scope to module level to enable better testing and reusability.
This commit is contained in:
parent
5fec171a1e
commit
f35feafe9a
232
cli/utils.py
232
cli/utils.py
|
|
@ -10,6 +10,130 @@ ANALYST_ORDER = [
|
||||||
("Fundamentals Analyst", AnalystType.FUNDAMENTALS),
|
("Fundamentals Analyst", AnalystType.FUNDAMENTALS),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Define shallow thinking llm engine options with their corresponding model names
|
||||||
|
SHALLOW_AGENT_OPTIONS = {
|
||||||
|
"openai": [
|
||||||
|
("GPT-5 Mini - Cost-optimized reasoning", "gpt-5-mini"),
|
||||||
|
("GPT-5 Nano - Ultra-fast, high-throughput", "gpt-5-nano"),
|
||||||
|
("GPT-5.2 - Latest flagship", "gpt-5.2"),
|
||||||
|
("GPT-5.1 - Flexible reasoning", "gpt-5.1"),
|
||||||
|
("GPT-4.1 - Smartest non-reasoning, 1M context", "gpt-4.1"),
|
||||||
|
],
|
||||||
|
"anthropic": [
|
||||||
|
("Claude Haiku 4.5 - Fast + extended thinking", "claude-haiku-4-5"),
|
||||||
|
("Claude Sonnet 4.5 - Best for agents/coding", "claude-sonnet-4-5"),
|
||||||
|
("Claude Sonnet 4 - High-performance", "claude-sonnet-4-20250514"),
|
||||||
|
],
|
||||||
|
"google": [
|
||||||
|
("Gemini 3 Flash - Next-gen fast", "gemini-3-flash-preview"),
|
||||||
|
("Gemini 2.5 Flash - Balanced, recommended", "gemini-2.5-flash"),
|
||||||
|
("Gemini 3 Pro - Reasoning-first", "gemini-3-pro-preview"),
|
||||||
|
("Gemini 2.5 Flash Lite - Fast, low-cost", "gemini-2.5-flash-lite"),
|
||||||
|
],
|
||||||
|
"xai": [
|
||||||
|
(
|
||||||
|
"Grok 4.1 Fast (Non-Reasoning) - Speed optimized, 2M ctx",
|
||||||
|
"grok-4-1-fast-non-reasoning",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Grok 4 Fast (Non-Reasoning) - Speed optimized",
|
||||||
|
"grok-4-fast-non-reasoning",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Grok 4.1 Fast (Reasoning) - High-performance, 2M ctx",
|
||||||
|
"grok-4-1-fast-reasoning",
|
||||||
|
),
|
||||||
|
("Grok 4 Fast (Reasoning) - High-performance", "grok-4-fast-reasoning"),
|
||||||
|
],
|
||||||
|
"openrouter": [
|
||||||
|
("Kimi K2.5 0127 - Top ranked", "moonshotai/kimi-k2.5-0127"),
|
||||||
|
(
|
||||||
|
"Claude Sonnet 4.5 - Best for agents",
|
||||||
|
"anthropic/claude-4.5-sonnet-20250929",
|
||||||
|
),
|
||||||
|
("Claude Opus 4.5 - Premium", "anthropic/claude-4.5-opus-20251124"),
|
||||||
|
("Gemini 3 Flash Preview - Fast", "google/gemini-3-flash-preview-20251217"),
|
||||||
|
("Gemini 2.5 Flash - Balanced", "google/gemini-2.5-flash"),
|
||||||
|
("Gemini 2.5 Flash Lite - Efficient", "google/gemini-2.5-flash-lite"),
|
||||||
|
("Deepseek V3.2 - Cost effective", "deepseek/deepseek-v3.2-20251201"),
|
||||||
|
("Grok 4.1 Fast - High performance", "x-ai/grok-4.1-fast"),
|
||||||
|
("Grok Code Fast 1 - Coding optimized", "x-ai/grok-code-fast-1"),
|
||||||
|
("Minimax M2.1 - Rising star", "minimax/minimax-m2.1"),
|
||||||
|
(
|
||||||
|
"NVIDIA Nemotron 3 Nano 30B (free)",
|
||||||
|
"nvidia/nemotron-3-nano-30b-a3b:free",
|
||||||
|
),
|
||||||
|
("Z.AI GLM 4.5 Air (free)", "z-ai/glm-4.5-air:free"),
|
||||||
|
],
|
||||||
|
"ollama": [
|
||||||
|
("Qwen3:latest (8B, local)", "qwen3:latest"),
|
||||||
|
("GPT-OSS:latest (20B, local)", "gpt-oss:latest"),
|
||||||
|
("GLM-4.7-Flash:latest (30B, local)", "glm-4.7-flash:latest"),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Define deep thinking llm engine options with their corresponding model names
|
||||||
|
DEEP_AGENT_OPTIONS = {
|
||||||
|
"openai": [
|
||||||
|
("GPT-5.2 - Latest flagship", "gpt-5.2"),
|
||||||
|
("GPT-5.1 - Flexible reasoning", "gpt-5.1"),
|
||||||
|
("GPT-5 - Advanced reasoning", "gpt-5"),
|
||||||
|
("GPT-4.1 - Smartest non-reasoning, 1M context", "gpt-4.1"),
|
||||||
|
("GPT-5 Mini - Cost-optimized reasoning", "gpt-5-mini"),
|
||||||
|
("GPT-5 Nano - Ultra-fast, high-throughput", "gpt-5-nano"),
|
||||||
|
],
|
||||||
|
"anthropic": [
|
||||||
|
("Claude Sonnet 4.5 - Best for agents/coding", "claude-sonnet-4-5"),
|
||||||
|
("Claude Opus 4.5 - Premium, max intelligence", "claude-opus-4-5"),
|
||||||
|
("Claude Opus 4.1 - Most capable model", "claude-opus-4-1-20250805"),
|
||||||
|
("Claude Haiku 4.5 - Fast + extended thinking", "claude-haiku-4-5"),
|
||||||
|
("Claude Sonnet 4 - High-performance", "claude-sonnet-4-20250514"),
|
||||||
|
],
|
||||||
|
"google": [
|
||||||
|
("Gemini 3 Pro - Reasoning-first", "gemini-3-pro-preview"),
|
||||||
|
("Gemini 3 Flash - Next-gen fast", "gemini-3-flash-preview"),
|
||||||
|
("Gemini 2.5 Flash - Balanced, recommended", "gemini-2.5-flash"),
|
||||||
|
],
|
||||||
|
"xai": [
|
||||||
|
(
|
||||||
|
"Grok 4.1 Fast (Reasoning) - High-performance, 2M ctx",
|
||||||
|
"grok-4-1-fast-reasoning",
|
||||||
|
),
|
||||||
|
("Grok 4 Fast (Reasoning) - High-performance", "grok-4-fast-reasoning"),
|
||||||
|
("Grok 4 - Flagship model", "grok-4-0709"),
|
||||||
|
(
|
||||||
|
"Grok 4.1 Fast (Non-Reasoning) - Speed optimized, 2M ctx",
|
||||||
|
"grok-4-1-fast-non-reasoning",
|
||||||
|
),
|
||||||
|
("Grok 4 Fast (Non-Reasoning) - Speed optimized", "grok-4-fast-non-reasoning"),
|
||||||
|
],
|
||||||
|
"openrouter": [
|
||||||
|
("Kimi K2.5 0127 - Top ranked", "moonshotai/kimi-k2.5-0127"),
|
||||||
|
(
|
||||||
|
"Claude Opus 4.5 - Premium intelligence",
|
||||||
|
"anthropic/claude-4.5-opus-20251124",
|
||||||
|
),
|
||||||
|
("Claude Sonnet 4.5 - Best for agents", "anthropic/claude-4.5-sonnet-20250929"),
|
||||||
|
(
|
||||||
|
"Gemini 3 Flash Preview - Fast reasoning",
|
||||||
|
"google/gemini-3-flash-preview-20251217",
|
||||||
|
),
|
||||||
|
("Gemini 2.5 Flash - Balanced", "google/gemini-2.5-flash"),
|
||||||
|
("Gemini 2.5 Flash Lite - Efficient", "google/gemini-2.5-flash-lite"),
|
||||||
|
("Grok 4.1 Fast - High performance", "x-ai/grok-4.1-fast"),
|
||||||
|
("Deepseek V3.2 - Cost effective", "deepseek/deepseek-v3.2-20251201"),
|
||||||
|
("Grok Code Fast 1 - Coding optimized", "x-ai/grok-code-fast-1"),
|
||||||
|
("Minimax M2.1 - Rising star", "minimax/minimax-m2.1"),
|
||||||
|
("Z.AI GLM 4.5 Air (free)", "z-ai/glm-4.5-air:free"),
|
||||||
|
("NVIDIA Nemotron 3 Nano 30B (free)", "nvidia/nemotron-3-nano-30b-a3b:free"),
|
||||||
|
],
|
||||||
|
"ollama": [
|
||||||
|
("GLM-4.7-Flash:latest (30B, local)", "glm-4.7-flash:latest"),
|
||||||
|
("GPT-OSS:latest (20B, local)", "gpt-oss:latest"),
|
||||||
|
("Qwen3:latest (8B, local)", "qwen3:latest"),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_ticker() -> str:
|
def get_ticker() -> str:
|
||||||
"""Prompt the user to enter a ticker symbol."""
|
"""Prompt the user to enter a ticker symbol."""
|
||||||
|
|
@ -125,43 +249,6 @@ def select_research_depth() -> int:
|
||||||
def select_shallow_thinking_agent(provider) -> str:
|
def select_shallow_thinking_agent(provider) -> str:
|
||||||
"""Select shallow thinking llm engine using an interactive selection."""
|
"""Select shallow thinking llm engine using an interactive selection."""
|
||||||
|
|
||||||
# Define shallow thinking llm engine options with their corresponding model names
|
|
||||||
SHALLOW_AGENT_OPTIONS = {
|
|
||||||
"openai": [
|
|
||||||
("GPT-5 Mini - Cost-optimized reasoning", "gpt-5-mini"),
|
|
||||||
("GPT-5 Nano - Ultra-fast, high-throughput", "gpt-5-nano"),
|
|
||||||
("GPT-5.2 - Latest flagship", "gpt-5.2"),
|
|
||||||
("GPT-5.1 - Flexible reasoning", "gpt-5.1"),
|
|
||||||
("GPT-4.1 - Smartest non-reasoning, 1M context", "gpt-4.1"),
|
|
||||||
],
|
|
||||||
"anthropic": [
|
|
||||||
("Claude Haiku 4.5 - Fast + extended thinking", "claude-haiku-4-5"),
|
|
||||||
("Claude Sonnet 4.5 - Best for agents/coding", "claude-sonnet-4-5"),
|
|
||||||
("Claude Sonnet 4 - High-performance", "claude-sonnet-4-20250514"),
|
|
||||||
],
|
|
||||||
"google": [
|
|
||||||
("Gemini 3 Flash - Next-gen fast", "gemini-3-flash-preview"),
|
|
||||||
("Gemini 2.5 Flash - Balanced, recommended", "gemini-2.5-flash"),
|
|
||||||
("Gemini 3 Pro - Reasoning-first", "gemini-3-pro-preview"),
|
|
||||||
("Gemini 2.5 Flash Lite - Fast, low-cost", "gemini-2.5-flash-lite"),
|
|
||||||
],
|
|
||||||
"xai": [
|
|
||||||
("Grok 4.1 Fast (Non-Reasoning) - Speed optimized, 2M ctx", "grok-4-1-fast-non-reasoning"),
|
|
||||||
("Grok 4 Fast (Non-Reasoning) - Speed optimized", "grok-4-fast-non-reasoning"),
|
|
||||||
("Grok 4.1 Fast (Reasoning) - High-performance, 2M ctx", "grok-4-1-fast-reasoning"),
|
|
||||||
("Grok 4 Fast (Reasoning) - High-performance", "grok-4-fast-reasoning"),
|
|
||||||
],
|
|
||||||
"openrouter": [
|
|
||||||
("NVIDIA Nemotron 3 Nano 30B (free)", "nvidia/nemotron-3-nano-30b-a3b:free"),
|
|
||||||
("Z.AI GLM 4.5 Air (free)", "z-ai/glm-4.5-air:free"),
|
|
||||||
],
|
|
||||||
"ollama": [
|
|
||||||
("Qwen3:latest (8B, local)", "qwen3:latest"),
|
|
||||||
("GPT-OSS:latest (20B, local)", "gpt-oss:latest"),
|
|
||||||
("GLM-4.7-Flash:latest (30B, local)", "glm-4.7-flash:latest"),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
choice = questionary.select(
|
choice = questionary.select(
|
||||||
"Select Your [Quick-Thinking LLM Engine]:",
|
"Select Your [Quick-Thinking LLM Engine]:",
|
||||||
choices=[
|
choices=[
|
||||||
|
|
@ -190,46 +277,6 @@ def select_shallow_thinking_agent(provider) -> str:
|
||||||
def select_deep_thinking_agent(provider) -> str:
|
def select_deep_thinking_agent(provider) -> str:
|
||||||
"""Select deep thinking llm engine using an interactive selection."""
|
"""Select deep thinking llm engine using an interactive selection."""
|
||||||
|
|
||||||
# Define deep thinking llm engine options with their corresponding model names
|
|
||||||
DEEP_AGENT_OPTIONS = {
|
|
||||||
"openai": [
|
|
||||||
("GPT-5.2 - Latest flagship", "gpt-5.2"),
|
|
||||||
("GPT-5.1 - Flexible reasoning", "gpt-5.1"),
|
|
||||||
("GPT-5 - Advanced reasoning", "gpt-5"),
|
|
||||||
("GPT-4.1 - Smartest non-reasoning, 1M context", "gpt-4.1"),
|
|
||||||
("GPT-5 Mini - Cost-optimized reasoning", "gpt-5-mini"),
|
|
||||||
("GPT-5 Nano - Ultra-fast, high-throughput", "gpt-5-nano"),
|
|
||||||
],
|
|
||||||
"anthropic": [
|
|
||||||
("Claude Sonnet 4.5 - Best for agents/coding", "claude-sonnet-4-5"),
|
|
||||||
("Claude Opus 4.5 - Premium, max intelligence", "claude-opus-4-5"),
|
|
||||||
("Claude Opus 4.1 - Most capable model", "claude-opus-4-1-20250805"),
|
|
||||||
("Claude Haiku 4.5 - Fast + extended thinking", "claude-haiku-4-5"),
|
|
||||||
("Claude Sonnet 4 - High-performance", "claude-sonnet-4-20250514"),
|
|
||||||
],
|
|
||||||
"google": [
|
|
||||||
("Gemini 3 Pro - Reasoning-first", "gemini-3-pro-preview"),
|
|
||||||
("Gemini 3 Flash - Next-gen fast", "gemini-3-flash-preview"),
|
|
||||||
("Gemini 2.5 Flash - Balanced, recommended", "gemini-2.5-flash"),
|
|
||||||
],
|
|
||||||
"xai": [
|
|
||||||
("Grok 4.1 Fast (Reasoning) - High-performance, 2M ctx", "grok-4-1-fast-reasoning"),
|
|
||||||
("Grok 4 Fast (Reasoning) - High-performance", "grok-4-fast-reasoning"),
|
|
||||||
("Grok 4 - Flagship model", "grok-4-0709"),
|
|
||||||
("Grok 4.1 Fast (Non-Reasoning) - Speed optimized, 2M ctx", "grok-4-1-fast-non-reasoning"),
|
|
||||||
("Grok 4 Fast (Non-Reasoning) - Speed optimized", "grok-4-fast-non-reasoning"),
|
|
||||||
],
|
|
||||||
"openrouter": [
|
|
||||||
("Z.AI GLM 4.5 Air (free)", "z-ai/glm-4.5-air:free"),
|
|
||||||
("NVIDIA Nemotron 3 Nano 30B (free)", "nvidia/nemotron-3-nano-30b-a3b:free"),
|
|
||||||
],
|
|
||||||
"ollama": [
|
|
||||||
("GLM-4.7-Flash:latest (30B, local)", "glm-4.7-flash:latest"),
|
|
||||||
("GPT-OSS:latest (20B, local)", "gpt-oss:latest"),
|
|
||||||
("Qwen3:latest (8B, local)", "qwen3:latest"),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
choice = questionary.select(
|
choice = questionary.select(
|
||||||
"Select Your [Deep-Thinking LLM Engine]:",
|
"Select Your [Deep-Thinking LLM Engine]:",
|
||||||
choices=[
|
choices=[
|
||||||
|
|
@ -252,6 +299,7 @@ def select_deep_thinking_agent(provider) -> str:
|
||||||
|
|
||||||
return choice
|
return choice
|
||||||
|
|
||||||
|
|
||||||
def select_llm_provider() -> tuple[str, str]:
|
def select_llm_provider() -> tuple[str, str]:
|
||||||
"""Select the OpenAI api url using interactive selection."""
|
"""Select the OpenAI api url using interactive selection."""
|
||||||
# Define OpenAI api options with their corresponding endpoints
|
# Define OpenAI api options with their corresponding endpoints
|
||||||
|
|
@ -263,7 +311,7 @@ def select_llm_provider() -> tuple[str, str]:
|
||||||
("Openrouter", "https://openrouter.ai/api/v1"),
|
("Openrouter", "https://openrouter.ai/api/v1"),
|
||||||
("Ollama", "http://localhost:11434/v1"),
|
("Ollama", "http://localhost:11434/v1"),
|
||||||
]
|
]
|
||||||
|
|
||||||
choice = questionary.select(
|
choice = questionary.select(
|
||||||
"Select your LLM Provider:",
|
"Select your LLM Provider:",
|
||||||
choices=[
|
choices=[
|
||||||
|
|
@ -279,11 +327,11 @@ def select_llm_provider() -> tuple[str, str]:
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
).ask()
|
).ask()
|
||||||
|
|
||||||
if choice is None:
|
if choice is None:
|
||||||
console.print("\n[red]no OpenAI backend selected. Exiting...[/red]")
|
console.print("\n[red]no OpenAI backend selected. Exiting...[/red]")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
display_name, url = choice
|
display_name, url = choice
|
||||||
print(f"You selected: {display_name}\tURL: {url}")
|
print(f"You selected: {display_name}\tURL: {url}")
|
||||||
|
|
||||||
|
|
@ -300,11 +348,13 @@ def ask_openai_reasoning_effort() -> str:
|
||||||
return questionary.select(
|
return questionary.select(
|
||||||
"Select Reasoning Effort:",
|
"Select Reasoning Effort:",
|
||||||
choices=choices,
|
choices=choices,
|
||||||
style=questionary.Style([
|
style=questionary.Style(
|
||||||
("selected", "fg:cyan noinherit"),
|
[
|
||||||
("highlighted", "fg:cyan noinherit"),
|
("selected", "fg:cyan noinherit"),
|
||||||
("pointer", "fg:cyan noinherit"),
|
("highlighted", "fg:cyan noinherit"),
|
||||||
]),
|
("pointer", "fg:cyan noinherit"),
|
||||||
|
]
|
||||||
|
),
|
||||||
).ask()
|
).ask()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -320,9 +370,11 @@ def ask_gemini_thinking_config() -> str | None:
|
||||||
questionary.Choice("Enable Thinking (recommended)", "high"),
|
questionary.Choice("Enable Thinking (recommended)", "high"),
|
||||||
questionary.Choice("Minimal/Disable Thinking", "minimal"),
|
questionary.Choice("Minimal/Disable Thinking", "minimal"),
|
||||||
],
|
],
|
||||||
style=questionary.Style([
|
style=questionary.Style(
|
||||||
("selected", "fg:green noinherit"),
|
[
|
||||||
("highlighted", "fg:green noinherit"),
|
("selected", "fg:green noinherit"),
|
||||||
("pointer", "fg:green noinherit"),
|
("highlighted", "fg:green noinherit"),
|
||||||
]),
|
("pointer", "fg:green noinherit"),
|
||||||
|
]
|
||||||
|
),
|
||||||
).ask()
|
).ask()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
"""Tests for CLI utilities, specifically OpenRouter model configuration."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from cli.utils import SHALLOW_AGENT_OPTIONS, DEEP_AGENT_OPTIONS
|
||||||
|
|
||||||
|
|
||||||
|
class TestOpenRouterModels:
|
||||||
|
"""Test suite for OpenRouter model configuration."""
|
||||||
|
|
||||||
|
def test_shallow_thinking_openrouter_models_count(self):
|
||||||
|
"""Verify OpenRouter shallow thinking models list has expected entries."""
|
||||||
|
openrouter_models = SHALLOW_AGENT_OPTIONS.get("openrouter", [])
|
||||||
|
|
||||||
|
# Should have at least 12 models (top 10 + 2 free models)
|
||||||
|
assert len(openrouter_models) >= 12, (
|
||||||
|
f"Expected at least 12 models, got {len(openrouter_models)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_deep_thinking_openrouter_models_count(self):
|
||||||
|
"""Verify OpenRouter deep thinking models list has expected entries."""
|
||||||
|
openrouter_models = DEEP_AGENT_OPTIONS.get("openrouter", [])
|
||||||
|
|
||||||
|
# Should have at least 12 models (top 10 + 2 free models)
|
||||||
|
assert len(openrouter_models) >= 12, (
|
||||||
|
f"Expected at least 12 models, got {len(openrouter_models)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_openrouter_models_have_required_format(self):
|
||||||
|
"""Verify all OpenRouter models follow the expected format."""
|
||||||
|
for model_list in [
|
||||||
|
SHALLOW_AGENT_OPTIONS.get("openrouter", []),
|
||||||
|
DEEP_AGENT_OPTIONS.get("openrouter", []),
|
||||||
|
]:
|
||||||
|
for display_name, model_id in model_list:
|
||||||
|
# Display name should be a string
|
||||||
|
assert isinstance(display_name, str), (
|
||||||
|
f"Display name must be string, got {type(display_name)}"
|
||||||
|
)
|
||||||
|
assert len(display_name) > 0, "Display name cannot be empty"
|
||||||
|
|
||||||
|
# Model ID should be a string with provider/model format
|
||||||
|
assert isinstance(model_id, str), (
|
||||||
|
f"Model ID must be string, got {type(model_id)}"
|
||||||
|
)
|
||||||
|
assert "/" in model_id, (
|
||||||
|
f"Model ID {model_id} should contain provider prefix"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_top_models_included(self):
|
||||||
|
"""Verify top ranked models from OpenRouter are included."""
|
||||||
|
# Key models that should be present (based on OpenRouter rankings)
|
||||||
|
expected_models = [
|
||||||
|
"moonshotai/kimi-k2.5-0127", # #1 ranked
|
||||||
|
"anthropic/claude-4.5-opus-20251124", # Top Claude
|
||||||
|
"anthropic/claude-4.5-sonnet-20250929", # Popular Claude
|
||||||
|
"google/gemini-3-flash-preview-20251217", # Top Gemini
|
||||||
|
"deepseek/deepseek-v3.2-20251201", # Popular open source
|
||||||
|
"x-ai/grok-4.1-fast", # xAI model
|
||||||
|
]
|
||||||
|
|
||||||
|
all_models = []
|
||||||
|
for options in [SHALLOW_AGENT_OPTIONS, DEEP_AGENT_OPTIONS]:
|
||||||
|
all_models.extend(
|
||||||
|
[model_id for _, model_id in options.get("openrouter", [])]
|
||||||
|
)
|
||||||
|
|
||||||
|
for expected in expected_models:
|
||||||
|
assert expected in all_models, (
|
||||||
|
f"Expected model {expected} not found in configuration"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_free_models_still_available(self):
|
||||||
|
"""Verify free models are still included."""
|
||||||
|
free_models = [
|
||||||
|
"nvidia/nemotron-3-nano-30b-a3b:free",
|
||||||
|
"z-ai/glm-4.5-air:free",
|
||||||
|
]
|
||||||
|
|
||||||
|
all_models = []
|
||||||
|
for options in [SHALLOW_AGENT_OPTIONS, DEEP_AGENT_OPTIONS]:
|
||||||
|
all_models.extend(
|
||||||
|
[model_id for _, model_id in options.get("openrouter", [])]
|
||||||
|
)
|
||||||
|
|
||||||
|
for free_model in free_models:
|
||||||
|
assert free_model in all_models, (
|
||||||
|
f"Free model {free_model} should be preserved"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_no_duplicate_model_ids(self):
|
||||||
|
"""Verify no duplicate model IDs exist in OpenRouter lists."""
|
||||||
|
for options in [SHALLOW_AGENT_OPTIONS, DEEP_AGENT_OPTIONS]:
|
||||||
|
model_ids = [model_id for _, model_id in options.get("openrouter", [])]
|
||||||
|
assert len(model_ids) == len(set(model_ids)), "Duplicate model IDs found"
|
||||||
|
|
||||||
|
def test_all_providers_have_models(self):
|
||||||
|
"""Verify all supported providers have model entries."""
|
||||||
|
expected_providers = [
|
||||||
|
"openai",
|
||||||
|
"anthropic",
|
||||||
|
"google",
|
||||||
|
"xai",
|
||||||
|
"openrouter",
|
||||||
|
"ollama",
|
||||||
|
]
|
||||||
|
|
||||||
|
for provider in expected_providers:
|
||||||
|
assert provider in SHALLOW_AGENT_OPTIONS, (
|
||||||
|
f"Provider {provider} missing from shallow options"
|
||||||
|
)
|
||||||
|
assert len(SHALLOW_AGENT_OPTIONS[provider]) > 0, (
|
||||||
|
f"Provider {provider} has no shallow models"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert provider in DEEP_AGENT_OPTIONS, (
|
||||||
|
f"Provider {provider} missing from deep options"
|
||||||
|
)
|
||||||
|
assert len(DEEP_AGENT_OPTIONS[provider]) > 0, (
|
||||||
|
f"Provider {provider} has no deep models"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_model_consistency_between_lists(self):
|
||||||
|
"""Verify common models appear in both shallow and deep lists where applicable."""
|
||||||
|
shallow_models = set(
|
||||||
|
model_id for _, model_id in SHALLOW_AGENT_OPTIONS.get("openrouter", [])
|
||||||
|
)
|
||||||
|
deep_models = set(
|
||||||
|
model_id for _, model_id in DEEP_AGENT_OPTIONS.get("openrouter", [])
|
||||||
|
)
|
||||||
|
|
||||||
|
# Free models should be in both lists
|
||||||
|
free_models = {"nvidia/nemotron-3-nano-30b-a3b:free", "z-ai/glm-4.5-air:free"}
|
||||||
|
for free_model in free_models:
|
||||||
|
assert free_model in shallow_models, (
|
||||||
|
f"Free model {free_model} should be in shallow list"
|
||||||
|
)
|
||||||
|
assert free_model in deep_models, (
|
||||||
|
f"Free model {free_model} should be in deep list"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pytest.main([__file__, "-v"])
|
||||||
Loading…
Reference in New Issue