This commit is contained in:
Fahad
2025-12-11 20:08:17 +00:00
parent 8b16405f06
commit 514c9c58fc
12 changed files with 157 additions and 232 deletions

View File

@@ -473,6 +473,24 @@
"temperature_constraint": "range", "temperature_constraint": "range",
"description": "xAI's Grok 4 via OpenRouter with vision and advanced reasoning", "description": "xAI's Grok 4 via OpenRouter with vision and advanced reasoning",
"intelligence_score": 15 "intelligence_score": 15
},
{
"model_name": "x-ai/grok-4.1-fast",
"aliases": [
"grok-4.1-fast-openrouter",
"grok-4.1-openrouter"
],
"context_window": 2000000,
"max_output_tokens": 2000000,
"supports_extended_thinking": true,
"supports_json_mode": true,
"supports_function_calling": true,
"supports_images": true,
"max_image_size_mb": 20.0,
"supports_temperature": true,
"temperature_constraint": "range",
"description": "xAI's Grok 4.1 Fast Reasoning via OpenRouter (2M context) with vision and advanced reasoning",
"intelligence_score": 15
} }
] ]
} }

View File

@@ -5,7 +5,7 @@
"usage": "Models listed here are exposed directly through the X.AI provider. Aliases are case-insensitive.", "usage": "Models listed here are exposed directly through the X.AI provider. Aliases are case-insensitive.",
"field_notes": "Matches providers/shared/model_capabilities.py.", "field_notes": "Matches providers/shared/model_capabilities.py.",
"field_descriptions": { "field_descriptions": {
"model_name": "The model identifier (e.g., 'grok-4', 'grok-3-fast')", "model_name": "The model identifier (e.g., 'grok-4', 'grok-4.1-fast')",
"aliases": "Array of short names users can type instead of the full model name", "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)", "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", "max_output_tokens": "Maximum number of tokens the model can generate in a single response",
@@ -46,42 +46,27 @@
"max_image_size_mb": 20.0 "max_image_size_mb": 20.0
}, },
{ {
"model_name": "grok-3", "model_name": "grok-4-1-fast-reasoning",
"friendly_name": "X.AI (Grok 3)", "friendly_name": "X.AI (Grok 4.1 Fast Reasoning)",
"aliases": [ "aliases": [
"grok3" "grok-4.1",
"grok-4-1",
"grok-4.1-fast-reasoning",
"grok-4.1-fast-reasoning-latest",
"grok-4.1-fast"
], ],
"intelligence_score": 13, "intelligence_score": 15,
"description": "GROK-3 (131K context) - Advanced reasoning model from X.AI, excellent for complex analysis", "description": "GROK-4.1 Fast Reasoning (2M context) - High-performance multimodal reasoning model with function calling",
"context_window": 131072, "context_window": 2000000,
"max_output_tokens": 131072, "max_output_tokens": 2000000,
"supports_extended_thinking": false, "supports_extended_thinking": true,
"supports_system_prompts": true, "supports_system_prompts": true,
"supports_streaming": true, "supports_streaming": true,
"supports_function_calling": true, "supports_function_calling": true,
"supports_json_mode": false, "supports_json_mode": true,
"supports_images": false, "supports_images": true,
"supports_temperature": true "supports_temperature": true,
}, "max_image_size_mb": 20.0
{
"model_name": "grok-3-fast",
"friendly_name": "X.AI (Grok 3 Fast)",
"aliases": [
"grok3fast",
"grokfast",
"grok3-fast"
],
"intelligence_score": 12,
"description": "GROK-3 Fast (131K context) - Higher performance variant, faster processing but more expensive",
"context_window": 131072,
"max_output_tokens": 131072,
"supports_extended_thinking": false,
"supports_system_prompts": true,
"supports_streaming": true,
"supports_function_calling": true,
"supports_json_mode": false,
"supports_images": false,
"supports_temperature": true
} }
] ]
} }

View File

@@ -48,8 +48,7 @@ Regardless of your default configuration, you can specify models per request:
| **`gpt5-mini`** (GPT-5 Mini) | OpenAI | 400K tokens | Efficient variant with reasoning | Balanced performance and capability | | **`gpt5-mini`** (GPT-5 Mini) | OpenAI | 400K tokens | Efficient variant with reasoning | Balanced performance and capability |
| **`gpt5-nano`** (GPT-5 Nano) | OpenAI | 400K tokens | Fastest, cheapest GPT-5 variant | Summarization and classification tasks | | **`gpt5-nano`** (GPT-5 Nano) | OpenAI | 400K tokens | Fastest, cheapest GPT-5 variant | Summarization and classification tasks |
| **`grok-4`** | X.AI | 256K tokens | Latest flagship Grok model with reasoning, vision | Complex analysis, reasoning tasks | | **`grok-4`** | X.AI | 256K tokens | Latest flagship Grok model with reasoning, vision | Complex analysis, reasoning tasks |
| **`grok-3`** | X.AI | 131K tokens | Advanced reasoning model | Deep analysis, complex problems | | **`grok-4.1-fast-reasoning`** | X.AI | 2M tokens | High-performance Grok 4.1 Fast Reasoning with vision | Fast responses and light reasoning |
| **`grok-3-fast`** | X.AI | 131K tokens | Higher performance variant | Fast responses with reasoning |
| **`llama`** (Llama 3.2) | Custom/Local | 128K tokens | Local inference, privacy | On-device analysis, cost-free processing | | **`llama`** (Llama 3.2) | Custom/Local | 128K tokens | Local inference, privacy | On-device analysis, cost-free processing |
| **Any model** | OpenRouter | Varies | Access to GPT-4, Claude, Llama, etc. | User-specified or based on task requirements | | **Any model** | OpenRouter | Varies | Access to GPT-4, Claude, Llama, etc. | User-specified or based on task requirements |
@@ -72,8 +71,7 @@ cloud models (expensive/powerful) AND local models (free/private) in the same co
- **GPT-5**: Full-featured with reasoning support and vision - **GPT-5**: Full-featured with reasoning support and vision
- **GPT-5 Mini**: Balanced efficiency and capability - **GPT-5 Mini**: Balanced efficiency and capability
- **GPT-5 Nano**: Optimized for fast, low-cost tasks - **GPT-5 Nano**: Optimized for fast, low-cost tasks
- **Grok-4**: Extended thinking support, vision capabilities, 256K context - **Grok-4 / Grok-4.1-fast-reasoning**: Extended thinking support, vision capabilities (256K / 2M context)
- **Grok-3 Models**: Advanced reasoning, 131K context
## Model Usage Restrictions ## Model Usage Restrictions

View File

@@ -83,7 +83,7 @@ DEFAULT_MODEL=auto # Claude picks best model for each task (recommended)
|----------|-----------------|-----------------| |----------|-----------------|-----------------|
| OpenAI | `gpt-5.2`, `gpt-5.1-codex`, `gpt-5.1-codex-mini`, `gpt-5`, `gpt-5.2-pro`, `gpt-5-mini`, `gpt-5-nano`, `gpt-5-codex`, `gpt-4.1`, `o3`, `o3-mini`, `o3-pro`, `o4-mini` | `gpt5.2`, `gpt-5.2`, `5.2`, `gpt5.1-codex`, `codex-5.1`, `codex-mini`, `gpt5`, `gpt5pro`, `mini`, `nano`, `codex`, `o3mini`, `o3pro`, `o4mini` | | OpenAI | `gpt-5.2`, `gpt-5.1-codex`, `gpt-5.1-codex-mini`, `gpt-5`, `gpt-5.2-pro`, `gpt-5-mini`, `gpt-5-nano`, `gpt-5-codex`, `gpt-4.1`, `o3`, `o3-mini`, `o3-pro`, `o4-mini` | `gpt5.2`, `gpt-5.2`, `5.2`, `gpt5.1-codex`, `codex-5.1`, `codex-mini`, `gpt5`, `gpt5pro`, `mini`, `nano`, `codex`, `o3mini`, `o3pro`, `o4mini` |
| Gemini | `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-2.0-flash`, `gemini-2.0-flash-lite` | `pro`, `gemini-pro`, `flash`, `flash-2.0`, `flashlite` | | Gemini | `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-2.0-flash`, `gemini-2.0-flash-lite` | `pro`, `gemini-pro`, `flash`, `flash-2.0`, `flashlite` |
| X.AI | `grok-4`, `grok-3`, `grok-3-fast` | `grok`, `grok4`, `grok3`, `grok3fast`, `grokfast` | | X.AI | `grok-4`, `grok-4.1-fast` | `grok`, `grok4`, `grok-4.1-fast-reasoning` |
| OpenRouter | See `conf/openrouter_models.json` for the continually evolving catalogue | e.g., `opus`, `sonnet`, `flash`, `pro`, `mistral` | | OpenRouter | See `conf/openrouter_models.json` for the continually evolving catalogue | e.g., `opus`, `sonnet`, `flash`, `pro`, `mistral` |
| Custom | User-managed entries such as `llama3.2` | Define your own aliases per entry | | Custom | User-managed entries such as `llama3.2` | Define your own aliases per entry |
@@ -179,7 +179,7 @@ OPENAI_ALLOWED_MODELS=gpt-5.1-codex-mini,gpt-5-mini,o3-mini,o4-mini,mini
GOOGLE_ALLOWED_MODELS=flash,pro GOOGLE_ALLOWED_MODELS=flash,pro
# X.AI GROK model restrictions # X.AI GROK model restrictions
XAI_ALLOWED_MODELS=grok-3,grok-3-fast,grok-4 XAI_ALLOWED_MODELS=grok-4,grok-4.1-fast-reasoning
# OpenRouter model restrictions (affects models via custom provider) # OpenRouter model restrictions (affects models via custom provider)
OPENROUTER_ALLOWED_MODELS=opus,sonnet,mistral OPENROUTER_ALLOWED_MODELS=opus,sonnet,mistral
@@ -208,7 +208,7 @@ GOOGLE_ALLOWED_MODELS=pro
# Balanced selection # Balanced selection
GOOGLE_ALLOWED_MODELS=flash,pro GOOGLE_ALLOWED_MODELS=flash,pro
OPENAI_ALLOWED_MODELS=gpt-5.1-codex-mini,gpt-5-mini,o4-mini OPENAI_ALLOWED_MODELS=gpt-5.1-codex-mini,gpt-5-mini,o4-mini
XAI_ALLOWED_MODELS=grok,grok-3-fast XAI_ALLOWED_MODELS=grok,grok-4.1-fast-reasoning
``` ```
### Advanced Configuration ### Advanced Configuration

View File

@@ -26,6 +26,10 @@ class XAIModelProvider(RegistryBackedProviderMixin, OpenAICompatibleProvider):
REGISTRY_CLASS = XAIModelRegistry REGISTRY_CLASS = XAIModelRegistry
MODEL_CAPABILITIES: ClassVar[dict[str, ModelCapabilities]] = {} MODEL_CAPABILITIES: ClassVar[dict[str, ModelCapabilities]] = {}
# Canonical model identifiers used for category routing.
PRIMARY_MODEL = "grok-4-1-fast-reasoning"
FALLBACK_MODEL = "grok-4"
def __init__(self, api_key: str, **kwargs): def __init__(self, api_key: str, **kwargs):
"""Initialize X.AI provider with API key.""" """Initialize X.AI provider with API key."""
# Set X.AI base URL # Set X.AI base URL
@@ -54,32 +58,27 @@ class XAIModelProvider(RegistryBackedProviderMixin, OpenAICompatibleProvider):
return None return None
if category == ToolModelCategory.EXTENDED_REASONING: if category == ToolModelCategory.EXTENDED_REASONING:
# Prefer GROK-4 for advanced reasoning with thinking mode # Prefer Grok 4.1 Fast Reasoning for advanced tasks
if "grok-4" in allowed_models: if self.PRIMARY_MODEL in allowed_models:
return "grok-4" return self.PRIMARY_MODEL
elif "grok-3" in allowed_models: if self.FALLBACK_MODEL in allowed_models:
return "grok-3" return self.FALLBACK_MODEL
# Fall back to any available model
return allowed_models[0] return allowed_models[0]
elif category == ToolModelCategory.FAST_RESPONSE: elif category == ToolModelCategory.FAST_RESPONSE:
# Prefer GROK-3-Fast for speed, then GROK-4 # Prefer Grok 4.1 Fast Reasoning for speed as well (latest fast SKU).
if "grok-3-fast" in allowed_models: if self.PRIMARY_MODEL in allowed_models:
return "grok-3-fast" return self.PRIMARY_MODEL
elif "grok-4" in allowed_models: if self.FALLBACK_MODEL in allowed_models:
return "grok-4" return self.FALLBACK_MODEL
# Fall back to any available model
return allowed_models[0] return allowed_models[0]
else: # BALANCED or default else: # BALANCED or default
# Prefer GROK-4 for balanced use (best overall capabilities) # Prefer Grok 4.1 Fast Reasoning for balanced use.
if "grok-4" in allowed_models: if self.PRIMARY_MODEL in allowed_models:
return "grok-4" return self.PRIMARY_MODEL
elif "grok-3" in allowed_models: if self.FALLBACK_MODEL in allowed_models:
return "grok-3" return self.FALLBACK_MODEL
elif "grok-3-fast" in allowed_models:
return "grok-3-fast"
# Fall back to any available model
return allowed_models[0] return allowed_models[0]

View File

@@ -3,8 +3,8 @@
X.AI GROK Model Tests X.AI GROK Model Tests
Tests that verify X.AI GROK functionality including: Tests that verify X.AI GROK functionality including:
- Model alias resolution (grok, grok3, grokfast map to actual GROK models) - Model alias resolution (grok maps to Grok 4)
- GROK-3 and GROK-3-fast models work correctly - GROK-4 and GROK-4.1 Fast Reasoning models work correctly
- Conversation continuity works with GROK models - Conversation continuity works with GROK models
- API integration and response validation - API integration and response validation
""" """
@@ -63,75 +63,44 @@ class XAIModelsTest(BaseSimulatorTest):
if continuation_id: if continuation_id:
self.logger.info(f" ✅ Got continuation_id: {continuation_id}") self.logger.info(f" ✅ Got continuation_id: {continuation_id}")
# Test 2: Direct grok-3 model name # Test 2: Direct grok-4.1-fast model name
self.logger.info(" 2: Testing direct model name (grok-3)") self.logger.info(" 2: Testing direct model name (grok-4.1-fast)")
response2, _ = self.call_mcp_tool( response2, _ = self.call_mcp_tool(
"chat", "chat",
{ {
"prompt": "Say 'Hello from GROK-3!' and nothing else.", "prompt": "Say 'Hello from GROK-4.1 Fast!' and nothing else.",
"model": "grok-3", "model": "grok-4.1-fast",
"temperature": 0.1, "temperature": 0.1,
}, },
) )
if not response2: if not response2:
self.logger.error(" ❌ Direct GROK-3 model test failed") self.logger.error(" ❌ Direct GROK-4.1-fast model test failed")
return False return False
self.logger.info(" ✅ Direct GROK-3 model call completed") self.logger.info(" ✅ Direct GROK-4.1-fast model call completed")
# Test 3: grok-3-fast model # Test 3: grok-4.1-fast-reasoning alias
self.logger.info(" 3: Testing GROK-3-fast model") self.logger.info(" 3: Testing 'grok-4.1-fast-reasoning' alias")
response3, _ = self.call_mcp_tool( response3, _ = self.call_mcp_tool(
"chat", "chat",
{ {
"prompt": "Say 'Hello from GROK-3-fast!' and nothing else.", "prompt": "Say 'Hello from GROK-4.1 Fast Reasoning alias!' and nothing else.",
"model": "grok-3-fast", "model": "grok-4.1-fast-reasoning",
"temperature": 0.1, "temperature": 0.1,
}, },
) )
if not response3: if not response3:
self.logger.error(" ❌ GROK-3-fast model test failed") self.logger.error(" ❌ GROK-4.1-fast-reasoning alias test failed")
return False return False
self.logger.info(" ✅ GROK-3-fast model call completed") self.logger.info(" ✅ GROK-4.1-fast-reasoning alias call completed")
# Test 4: Shorthand aliases # Test 4: Conversation continuity with GROK models
self.logger.info(" 4: Testing shorthand aliases (grok3, grokfast)") self.logger.info(" 4: Testing conversation continuity with GROK")
response4, _ = self.call_mcp_tool(
"chat",
{
"prompt": "Say 'Hello from grok3 alias!' and nothing else.",
"model": "grok3",
"temperature": 0.1,
},
)
if not response4:
self.logger.error(" ❌ grok3 alias test failed")
return False
response5, _ = self.call_mcp_tool(
"chat",
{
"prompt": "Say 'Hello from grokfast alias!' and nothing else.",
"model": "grokfast",
"temperature": 0.1,
},
)
if not response5:
self.logger.error(" ❌ grokfast alias test failed")
return False
self.logger.info(" ✅ Shorthand aliases work correctly")
# Test 5: Conversation continuity with GROK models
self.logger.info(" 5: Testing conversation continuity with GROK")
response6, new_continuation_id = self.call_mcp_tool( response6, new_continuation_id = self.call_mcp_tool(
"chat", "chat",
@@ -167,8 +136,8 @@ class XAIModelsTest(BaseSimulatorTest):
else: else:
self.logger.warning(" ⚠️ Model may not have remembered the number") self.logger.warning(" ⚠️ Model may not have remembered the number")
# Test 6: Validate X.AI API usage from logs # Test 5: Validate X.AI API usage from logs
self.logger.info(" 6: Validating X.AI API usage in logs") self.logger.info(" 5: Validating X.AI API usage in logs")
logs = self.get_recent_server_logs() logs = self.get_recent_server_logs()
# Check for X.AI API calls # Check for X.AI API calls

View File

@@ -108,9 +108,9 @@ class TestAutoModeComprehensive:
"OPENROUTER_API_KEY": None, "OPENROUTER_API_KEY": None,
}, },
{ {
"EXTENDED_REASONING": "grok-4", # GROK-4 for reasoning (now preferred) "EXTENDED_REASONING": "grok-4-1-fast-reasoning", # Latest Grok 4.1 Fast Reasoning
"FAST_RESPONSE": "grok-3-fast", # GROK-3-fast for speed "FAST_RESPONSE": "grok-4-1-fast-reasoning", # Latest fast SKU
"BALANCED": "grok-4", # GROK-4 as balanced (now preferred) "BALANCED": "grok-4-1-fast-reasoning", # Latest balanced default
}, },
), ),
# Both Gemini and OpenAI available - Google comes first in priority # Both Gemini and OpenAI available - Google comes first in priority

View File

@@ -321,7 +321,7 @@ class TestAutoModeProviderSelection:
("mini", ProviderType.OPENAI, "gpt-5-mini"), # "mini" now resolves to gpt-5-mini ("mini", ProviderType.OPENAI, "gpt-5-mini"), # "mini" now resolves to gpt-5-mini
("o3mini", ProviderType.OPENAI, "o3-mini"), ("o3mini", ProviderType.OPENAI, "o3-mini"),
("grok", ProviderType.XAI, "grok-4"), ("grok", ProviderType.XAI, "grok-4"),
("grokfast", ProviderType.XAI, "grok-3-fast"), ("grok-4.1-fast-reasoning", ProviderType.XAI, "grok-4-1-fast-reasoning"),
] ]
for alias, expected_provider_type, expected_resolved_name in test_cases: for alias, expected_provider_type, expected_resolved_name in test_cases:

View File

@@ -161,7 +161,7 @@ class TestModelEnumeration:
("grok", False), # X.AI - not available without API key ("grok", False), # X.AI - not available without API key
("gemini-2.5-flash", False), # Full Gemini name - not available without API key ("gemini-2.5-flash", False), # Full Gemini name - not available without API key
("o4-mini", False), # OpenAI variant - not available without API key ("o4-mini", False), # OpenAI variant - not available without API key
("grok-3-fast", False), # X.AI variant - not available without API key ("grok-4.1-fast", False), # X.AI variant - not available without API key
], ],
) )
def test_specific_native_models_only_with_api_keys(self, model_name, should_exist): def test_specific_native_models_only_with_api_keys(self, model_name, should_exist):

View File

@@ -86,20 +86,17 @@ class TestSupportedModelsAliases:
# Test specific aliases # Test specific aliases
assert "grok" in provider.MODEL_CAPABILITIES["grok-4"].aliases assert "grok" in provider.MODEL_CAPABILITIES["grok-4"].aliases
assert "grok4" in provider.MODEL_CAPABILITIES["grok-4"].aliases assert "grok4" in provider.MODEL_CAPABILITIES["grok-4"].aliases
assert "grok3" in provider.MODEL_CAPABILITIES["grok-3"].aliases assert "grok-4.1-fast-reasoning" in provider.MODEL_CAPABILITIES["grok-4-1-fast-reasoning"].aliases
assert "grok3fast" in provider.MODEL_CAPABILITIES["grok-3-fast"].aliases
assert "grokfast" in provider.MODEL_CAPABILITIES["grok-3-fast"].aliases
# Test alias resolution # Test alias resolution
assert provider._resolve_model_name("grok") == "grok-4" assert provider._resolve_model_name("grok") == "grok-4"
assert provider._resolve_model_name("grok4") == "grok-4" assert provider._resolve_model_name("grok4") == "grok-4"
assert provider._resolve_model_name("grok3") == "grok-3" assert provider._resolve_model_name("grok-4.1-fast-reasoning") == "grok-4-1-fast-reasoning"
assert provider._resolve_model_name("grok3fast") == "grok-3-fast" assert provider._resolve_model_name("grok-4.1-fast-reasoning-latest") == "grok-4-1-fast-reasoning"
assert provider._resolve_model_name("grokfast") == "grok-3-fast"
# Test case insensitive resolution # Test case insensitive resolution
assert provider._resolve_model_name("Grok") == "grok-4" assert provider._resolve_model_name("Grok") == "grok-4"
assert provider._resolve_model_name("GROKFAST") == "grok-3-fast" assert provider._resolve_model_name("GROK-4.1-FAST-REASONING") == "grok-4-1-fast-reasoning"
def test_dial_provider_aliases(self): def test_dial_provider_aliases(self):
"""Test DIAL provider's alias structure.""" """Test DIAL provider's alias structure."""
@@ -148,10 +145,10 @@ class TestSupportedModelsAliases:
# Test XAI # Test XAI
xai_provider = XAIModelProvider("test-key") xai_provider = XAIModelProvider("test-key")
xai_models = xai_provider.list_models(respect_restrictions=False) xai_models = xai_provider.list_models(respect_restrictions=False)
assert "grok-3" in xai_models assert "grok-4" in xai_models
assert "grok" in xai_models assert "grok" in xai_models
assert "grok-3-fast" in xai_models assert "grok-4.1-fast" in xai_models
assert "grokfast" in xai_models assert "grok-4.1-fast-reasoning" in xai_models
# Test DIAL # Test DIAL
dial_provider = DIALModelProvider("test-key") dial_provider = DIALModelProvider("test-key")

View File

@@ -47,17 +47,21 @@ class TestXAIProvider:
# Test valid models # Test valid models
assert provider.validate_model_name("grok-4") is True assert provider.validate_model_name("grok-4") is True
assert provider.validate_model_name("grok4") is True assert provider.validate_model_name("grok4") is True
assert provider.validate_model_name("grok-3") is True
assert provider.validate_model_name("grok-3-fast") is True
assert provider.validate_model_name("grok") is True assert provider.validate_model_name("grok") is True
assert provider.validate_model_name("grok3") is True assert provider.validate_model_name("grok-4.1-fast") is True
assert provider.validate_model_name("grokfast") is True assert provider.validate_model_name("grok-4.1-fast-reasoning") is True
assert provider.validate_model_name("grok3fast") is True assert provider.validate_model_name("grok-4.1-fast-reasoning-latest") is True
assert provider.validate_model_name("grok-4.1-fast") is True
assert provider.validate_model_name("grok-4.1-fast-reasoning") is True
assert provider.validate_model_name("grok-4.1-fast-reasoning-latest") is True
# Test invalid model # Test invalid model
assert provider.validate_model_name("invalid-model") is False assert provider.validate_model_name("invalid-model") is False
assert provider.validate_model_name("gpt-4") is False assert provider.validate_model_name("gpt-4") is False
assert provider.validate_model_name("gemini-pro") is False assert provider.validate_model_name("gemini-pro") is False
assert provider.validate_model_name("grok-3") is False
assert provider.validate_model_name("grok-3-fast") is False
assert provider.validate_model_name("grokfast") is False
def test_resolve_model_name(self): def test_resolve_model_name(self):
"""Test model name resolution.""" """Test model name resolution."""
@@ -66,33 +70,12 @@ class TestXAIProvider:
# Test shorthand resolution # Test shorthand resolution
assert provider._resolve_model_name("grok") == "grok-4" assert provider._resolve_model_name("grok") == "grok-4"
assert provider._resolve_model_name("grok4") == "grok-4" assert provider._resolve_model_name("grok4") == "grok-4"
assert provider._resolve_model_name("grok3") == "grok-3" assert provider._resolve_model_name("grok-4.1-fast-reasoning") == "grok-4-1-fast-reasoning"
assert provider._resolve_model_name("grokfast") == "grok-3-fast" assert provider._resolve_model_name("grok-4.1-fast-reasoning-latest") == "grok-4-1-fast-reasoning"
assert provider._resolve_model_name("grok3fast") == "grok-3-fast"
# Test full name passthrough # Test full name passthrough
assert provider._resolve_model_name("grok-4") == "grok-4" assert provider._resolve_model_name("grok-4") == "grok-4"
assert provider._resolve_model_name("grok-3") == "grok-3" assert provider._resolve_model_name("grok-4.1-fast") == "grok-4-1-fast-reasoning"
assert provider._resolve_model_name("grok-3-fast") == "grok-3-fast"
def test_get_capabilities_grok3(self):
"""Test getting model capabilities for GROK-3."""
provider = XAIModelProvider("test-key")
capabilities = provider.get_capabilities("grok-3")
assert capabilities.model_name == "grok-3"
assert capabilities.friendly_name == "X.AI (Grok 3)"
assert capabilities.context_window == 131_072
assert capabilities.provider == ProviderType.XAI
assert not capabilities.supports_extended_thinking
assert capabilities.supports_system_prompts is True
assert capabilities.supports_streaming is True
assert capabilities.supports_function_calling is True
# Test temperature range
assert capabilities.temperature_constraint.min_temp == 0.0
assert capabilities.temperature_constraint.max_temp == 2.0
assert capabilities.temperature_constraint.default_temp == 0.3
def test_get_capabilities_grok4(self): def test_get_capabilities_grok4(self):
"""Test getting model capabilities for GROK-4.""" """Test getting model capabilities for GROK-4."""
@@ -115,16 +98,19 @@ class TestXAIProvider:
assert capabilities.temperature_constraint.max_temp == 2.0 assert capabilities.temperature_constraint.max_temp == 2.0
assert capabilities.temperature_constraint.default_temp == 0.3 assert capabilities.temperature_constraint.default_temp == 0.3
def test_get_capabilities_grok3_fast(self): def test_get_capabilities_grok4_1_fast(self):
"""Test getting model capabilities for GROK-3 Fast.""" """Test getting model capabilities for GROK-4.1 Fast Reasoning."""
provider = XAIModelProvider("test-key") provider = XAIModelProvider("test-key")
capabilities = provider.get_capabilities("grok-3-fast") capabilities = provider.get_capabilities("grok-4.1-fast")
assert capabilities.model_name == "grok-3-fast" assert capabilities.model_name == "grok-4-1-fast-reasoning"
assert capabilities.friendly_name == "X.AI (Grok 3 Fast)" assert capabilities.friendly_name == "X.AI (Grok 4.1 Fast Reasoning)"
assert capabilities.context_window == 131_072 assert capabilities.context_window == 2_000_000
assert capabilities.provider == ProviderType.XAI assert capabilities.provider == ProviderType.XAI
assert not capabilities.supports_extended_thinking assert capabilities.supports_extended_thinking is True
assert capabilities.supports_function_calling is True
assert capabilities.supports_json_mode is True
assert capabilities.supports_images is True
def test_get_capabilities_with_shorthand(self): def test_get_capabilities_with_shorthand(self):
"""Test getting model capabilities with shorthand.""" """Test getting model capabilities with shorthand."""
@@ -134,8 +120,8 @@ class TestXAIProvider:
assert capabilities.model_name == "grok-4" # Should resolve to full name assert capabilities.model_name == "grok-4" # Should resolve to full name
assert capabilities.context_window == 256_000 assert capabilities.context_window == 256_000
capabilities_fast = provider.get_capabilities("grokfast") capabilities_fast = provider.get_capabilities("grok-4.1-fast-reasoning")
assert capabilities_fast.model_name == "grok-3-fast" # Should resolve to full name assert capabilities_fast.model_name == "grok-4-1-fast-reasoning" # Should resolve to full name
def test_unsupported_model_capabilities(self): def test_unsupported_model_capabilities(self):
"""Test error handling for unsupported models.""" """Test error handling for unsupported models."""
@@ -148,20 +134,23 @@ class TestXAIProvider:
"""X.AI capabilities should expose extended thinking support correctly.""" """X.AI capabilities should expose extended thinking support correctly."""
provider = XAIModelProvider("test-key") provider = XAIModelProvider("test-key")
thinking_aliases = ["grok-4", "grok", "grok4"] thinking_aliases = [
"grok-4",
"grok",
"grok4",
"grok-4.1-fast",
"grok-4.1-fast-reasoning",
"grok-4.1-fast-reasoning-latest",
]
for alias in thinking_aliases: for alias in thinking_aliases:
assert provider.get_capabilities(alias).supports_extended_thinking is True assert provider.get_capabilities(alias).supports_extended_thinking is True
non_thinking_aliases = ["grok-3", "grok-3-fast", "grokfast"]
for alias in non_thinking_aliases:
assert provider.get_capabilities(alias).supports_extended_thinking is False
def test_provider_type(self): def test_provider_type(self):
"""Test provider type identification.""" """Test provider type identification."""
provider = XAIModelProvider("test-key") provider = XAIModelProvider("test-key")
assert provider.get_provider_type() == ProviderType.XAI assert provider.get_provider_type() == ProviderType.XAI
@patch.dict(os.environ, {"XAI_ALLOWED_MODELS": "grok-3"}) @patch.dict(os.environ, {"XAI_ALLOWED_MODELS": "grok-4"})
def test_model_restrictions(self): def test_model_restrictions(self):
"""Test model restrictions functionality.""" """Test model restrictions functionality."""
# Clear cached restriction service # Clear cached restriction service
@@ -173,20 +162,17 @@ class TestXAIProvider:
provider = XAIModelProvider("test-key") provider = XAIModelProvider("test-key")
# grok-3 should be allowed # grok-4 should be allowed (including alias)
assert provider.validate_model_name("grok-3") is True assert provider.validate_model_name("grok-4") is True
assert provider.validate_model_name("grok3") is True # Shorthand for grok-3 assert provider.validate_model_name("grok") is True
# grok should be blocked (resolves to grok-4 which is not allowed) # grok-4.1-fast should be blocked by restrictions
assert provider.validate_model_name("grok") is False assert provider.validate_model_name("grok-4.1-fast") is False
assert provider.validate_model_name("grok-4.1-fast-reasoning") is False
# grok-3-fast should be blocked by restrictions @patch.dict(os.environ, {"XAI_ALLOWED_MODELS": "grok-4.1-fast-reasoning"})
assert provider.validate_model_name("grok-3-fast") is False
assert provider.validate_model_name("grokfast") is False
@patch.dict(os.environ, {"XAI_ALLOWED_MODELS": "grok,grok-3-fast"})
def test_multiple_model_restrictions(self): def test_multiple_model_restrictions(self):
"""Test multiple models in restrictions.""" """Restrictions should allow aliases for Grok 4.1 Fast."""
# Clear cached restriction service # Clear cached restriction service
import utils.model_restrictions import utils.model_restrictions
from providers.registry import ModelProviderRegistry from providers.registry import ModelProviderRegistry
@@ -196,24 +182,18 @@ class TestXAIProvider:
provider = XAIModelProvider("test-key") provider = XAIModelProvider("test-key")
# Shorthand "grok" should be allowed (resolves to grok-4) # Alias should be allowed (resolves to grok-4.1-fast)
assert provider.validate_model_name("grok") is True assert provider.validate_model_name("grok-4.1-fast-reasoning") is True
# Full name "grok-4" should NOT be allowed (only shorthand "grok" is in restriction list) # Canonical name is not allowed unless explicitly listed
assert provider.validate_model_name("grok-4.1-fast") is False
# grok-4 should NOT be allowed
assert provider.validate_model_name("grok-4") is False assert provider.validate_model_name("grok-4") is False
# "grok-3" should NOT be allowed (not in restriction list) @patch.dict(os.environ, {"XAI_ALLOWED_MODELS": "grok,grok-4.1-fast"})
assert provider.validate_model_name("grok-3") is False
# "grok-3-fast" should be allowed (explicitly listed)
assert provider.validate_model_name("grok-3-fast") is True
# Shorthand "grokfast" should be allowed (resolves to grok-3-fast)
assert provider.validate_model_name("grokfast") is True
@patch.dict(os.environ, {"XAI_ALLOWED_MODELS": "grok,grok-3,grok-4"})
def test_both_shorthand_and_full_name_allowed(self): def test_both_shorthand_and_full_name_allowed(self):
"""Test that both shorthand and full name can be allowed.""" """Test that aliases and canonical names can be allowed together."""
# Clear cached restriction service # Clear cached restriction service
import utils.model_restrictions import utils.model_restrictions
@@ -223,12 +203,8 @@ class TestXAIProvider:
# Both shorthand and full name should be allowed # Both shorthand and full name should be allowed
assert provider.validate_model_name("grok") is True # Resolves to grok-4 assert provider.validate_model_name("grok") is True # Resolves to grok-4
assert provider.validate_model_name("grok-3") is True
assert provider.validate_model_name("grok-4") is True assert provider.validate_model_name("grok-4") is True
assert provider.validate_model_name("grok-4.1-fast") is True
# Other models should not be allowed
assert provider.validate_model_name("grok-3-fast") is False
assert provider.validate_model_name("grokfast") is False
@patch.dict(os.environ, {"XAI_ALLOWED_MODELS": ""}) @patch.dict(os.environ, {"XAI_ALLOWED_MODELS": ""})
def test_empty_restrictions_allows_all(self): def test_empty_restrictions_allows_all(self):
@@ -241,10 +217,9 @@ class TestXAIProvider:
provider = XAIModelProvider("test-key") provider = XAIModelProvider("test-key")
assert provider.validate_model_name("grok-4") is True assert provider.validate_model_name("grok-4") is True
assert provider.validate_model_name("grok-3") is True assert provider.validate_model_name("grok-4.1-fast") is True
assert provider.validate_model_name("grok-3-fast") is True assert provider.validate_model_name("grok-4.1-fast-reasoning") is True
assert provider.validate_model_name("grok") is True assert provider.validate_model_name("grok") is True
assert provider.validate_model_name("grokfast") is True
assert provider.validate_model_name("grok4") is True assert provider.validate_model_name("grok4") is True
def test_friendly_name(self): def test_friendly_name(self):
@@ -252,8 +227,8 @@ class TestXAIProvider:
provider = XAIModelProvider("test-key") provider = XAIModelProvider("test-key")
assert provider.FRIENDLY_NAME == "X.AI" assert provider.FRIENDLY_NAME == "X.AI"
capabilities = provider.get_capabilities("grok-3") capabilities = provider.get_capabilities("grok-4")
assert capabilities.friendly_name == "X.AI (Grok 3)" assert capabilities.friendly_name == "X.AI (Grok 4)"
def test_supported_models_structure(self): def test_supported_models_structure(self):
"""Test that MODEL_CAPABILITIES has the correct structure.""" """Test that MODEL_CAPABILITIES has the correct structure."""
@@ -261,8 +236,7 @@ class TestXAIProvider:
# Check that all expected base models are present # Check that all expected base models are present
assert "grok-4" in provider.MODEL_CAPABILITIES assert "grok-4" in provider.MODEL_CAPABILITIES
assert "grok-3" in provider.MODEL_CAPABILITIES assert "grok-4-1-fast-reasoning" in provider.MODEL_CAPABILITIES
assert "grok-3-fast" in provider.MODEL_CAPABILITIES
# Check model configs have required fields # Check model configs have required fields
from providers.shared import ModelCapabilities from providers.shared import ModelCapabilities
@@ -280,20 +254,11 @@ class TestXAIProvider:
assert "grok-4" in grok4_config.aliases assert "grok-4" in grok4_config.aliases
assert "grok4" in grok4_config.aliases assert "grok4" in grok4_config.aliases
grok3_config = provider.MODEL_CAPABILITIES["grok-3"] grok41fast_config = provider.MODEL_CAPABILITIES["grok-4-1-fast-reasoning"]
assert grok3_config.context_window == 131_072 assert grok41fast_config.context_window == 2_000_000
assert grok3_config.supports_extended_thinking is False assert grok41fast_config.supports_extended_thinking is True
# Check aliases are correctly structured assert "grok-4.1-fast" in grok41fast_config.aliases
assert "grok3" in grok3_config.aliases # grok3 resolves to grok-3 assert "grok-4.1-fast-reasoning" in grok41fast_config.aliases
# Check grok-4 aliases
grok4_config = provider.MODEL_CAPABILITIES["grok-4"]
assert "grok" in grok4_config.aliases # grok resolves to grok-4
assert "grok4" in grok4_config.aliases
grok3fast_config = provider.MODEL_CAPABILITIES["grok-3-fast"]
assert "grok3fast" in grok3fast_config.aliases
assert "grokfast" in grok3fast_config.aliases
@patch("providers.openai_compatible.OpenAI") @patch("providers.openai_compatible.OpenAI")
def test_generate_content_resolves_alias_before_api_call(self, mock_openai_class): def test_generate_content_resolves_alias_before_api_call(self, mock_openai_class):
@@ -376,19 +341,13 @@ class TestXAIProvider:
call_kwargs = mock_client.chat.completions.create.call_args[1] call_kwargs = mock_client.chat.completions.create.call_args[1]
assert call_kwargs["model"] == "grok-4" assert call_kwargs["model"] == "grok-4"
# Test grok3 -> grok-3 # Test grok-4.1-fast-reasoning -> grok-4-1-fast-reasoning
mock_response.model = "grok-3" mock_response.model = "grok-4-1-fast-reasoning"
provider.generate_content(prompt="Test", model_name="grok3", temperature=0.7) provider.generate_content(prompt="Test", model_name="grok-4.1-fast-reasoning", temperature=0.7)
call_kwargs = mock_client.chat.completions.create.call_args[1] call_kwargs = mock_client.chat.completions.create.call_args[1]
assert call_kwargs["model"] == "grok-3" assert call_kwargs["model"] == "grok-4-1-fast-reasoning"
# Test grokfast -> grok-3-fast # Test grok-4.1-fast -> grok-4-1-fast-reasoning
mock_response.model = "grok-3-fast" provider.generate_content(prompt="Test", model_name="grok-4.1-fast", temperature=0.7)
provider.generate_content(prompt="Test", model_name="grokfast", temperature=0.7)
call_kwargs = mock_client.chat.completions.create.call_args[1] call_kwargs = mock_client.chat.completions.create.call_args[1]
assert call_kwargs["model"] == "grok-3-fast" assert call_kwargs["model"] == "grok-4-1-fast-reasoning"
# Test grok3fast -> grok-3-fast
provider.generate_content(prompt="Test", model_name="grok3fast", temperature=0.7)
call_kwargs = mock_client.chat.completions.create.call_args[1]
assert call_kwargs["model"] == "grok-3-fast"

View File

@@ -16,7 +16,7 @@ Environment Variables:
Example: Example:
OPENAI_ALLOWED_MODELS=o3-mini,o4-mini OPENAI_ALLOWED_MODELS=o3-mini,o4-mini
GOOGLE_ALLOWED_MODELS=flash GOOGLE_ALLOWED_MODELS=flash
XAI_ALLOWED_MODELS=grok-3,grok-3-fast XAI_ALLOWED_MODELS=grok-4,grok-4.1-fast-reasoning
OPENROUTER_ALLOWED_MODELS=opus,sonnet,mistral OPENROUTER_ALLOWED_MODELS=opus,sonnet,mistral
""" """