feat: grok-4.1 support https://github.com/BeehiveInnovations/pal-mcp-server/issues/339
This commit is contained in:
@@ -473,6 +473,24 @@
|
||||
"temperature_constraint": "range",
|
||||
"description": "xAI's Grok 4 via OpenRouter with vision and advanced reasoning",
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"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_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",
|
||||
"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",
|
||||
@@ -46,42 +46,27 @@
|
||||
"max_image_size_mb": 20.0
|
||||
},
|
||||
{
|
||||
"model_name": "grok-3",
|
||||
"friendly_name": "X.AI (Grok 3)",
|
||||
"model_name": "grok-4-1-fast-reasoning",
|
||||
"friendly_name": "X.AI (Grok 4.1 Fast Reasoning)",
|
||||
"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,
|
||||
"description": "GROK-3 (131K context) - Advanced reasoning model from X.AI, excellent for complex analysis",
|
||||
"context_window": 131072,
|
||||
"max_output_tokens": 131072,
|
||||
"supports_extended_thinking": false,
|
||||
"intelligence_score": 15,
|
||||
"description": "GROK-4.1 Fast Reasoning (2M context) - High-performance multimodal reasoning model with function calling",
|
||||
"context_window": 2000000,
|
||||
"max_output_tokens": 2000000,
|
||||
"supports_extended_thinking": true,
|
||||
"supports_system_prompts": true,
|
||||
"supports_streaming": true,
|
||||
"supports_function_calling": true,
|
||||
"supports_json_mode": false,
|
||||
"supports_images": false,
|
||||
"supports_temperature": true
|
||||
},
|
||||
{
|
||||
"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
|
||||
"supports_json_mode": true,
|
||||
"supports_images": true,
|
||||
"supports_temperature": true,
|
||||
"max_image_size_mb": 20.0
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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-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-3`** | X.AI | 131K tokens | Advanced reasoning model | Deep analysis, complex problems |
|
||||
| **`grok-3-fast`** | X.AI | 131K tokens | Higher performance variant | Fast responses with reasoning |
|
||||
| **`grok-4.1-fast-reasoning`** | X.AI | 2M tokens | High-performance Grok 4.1 Fast Reasoning with vision | Fast responses and light reasoning |
|
||||
| **`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 |
|
||||
|
||||
@@ -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 Mini**: Balanced efficiency and capability
|
||||
- **GPT-5 Nano**: Optimized for fast, low-cost tasks
|
||||
- **Grok-4**: Extended thinking support, vision capabilities, 256K context
|
||||
- **Grok-3 Models**: Advanced reasoning, 131K context
|
||||
- **Grok-4 / Grok-4.1-fast-reasoning**: Extended thinking support, vision capabilities (256K / 2M context)
|
||||
|
||||
## Model Usage Restrictions
|
||||
|
||||
|
||||
@@ -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` |
|
||||
| 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` |
|
||||
| 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
|
||||
|
||||
# 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_ALLOWED_MODELS=opus,sonnet,mistral
|
||||
@@ -208,7 +208,7 @@ GOOGLE_ALLOWED_MODELS=pro
|
||||
# Balanced selection
|
||||
GOOGLE_ALLOWED_MODELS=flash,pro
|
||||
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
|
||||
|
||||
@@ -26,6 +26,10 @@ class XAIModelProvider(RegistryBackedProviderMixin, OpenAICompatibleProvider):
|
||||
REGISTRY_CLASS = XAIModelRegistry
|
||||
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):
|
||||
"""Initialize X.AI provider with API key."""
|
||||
# Set X.AI base URL
|
||||
@@ -54,32 +58,27 @@ class XAIModelProvider(RegistryBackedProviderMixin, OpenAICompatibleProvider):
|
||||
return None
|
||||
|
||||
if category == ToolModelCategory.EXTENDED_REASONING:
|
||||
# Prefer GROK-4 for advanced reasoning with thinking mode
|
||||
if "grok-4" in allowed_models:
|
||||
return "grok-4"
|
||||
elif "grok-3" in allowed_models:
|
||||
return "grok-3"
|
||||
# Fall back to any available model
|
||||
# Prefer Grok 4.1 Fast Reasoning for advanced tasks
|
||||
if self.PRIMARY_MODEL in allowed_models:
|
||||
return self.PRIMARY_MODEL
|
||||
if self.FALLBACK_MODEL in allowed_models:
|
||||
return self.FALLBACK_MODEL
|
||||
return allowed_models[0]
|
||||
|
||||
elif category == ToolModelCategory.FAST_RESPONSE:
|
||||
# Prefer GROK-3-Fast for speed, then GROK-4
|
||||
if "grok-3-fast" in allowed_models:
|
||||
return "grok-3-fast"
|
||||
elif "grok-4" in allowed_models:
|
||||
return "grok-4"
|
||||
# Fall back to any available model
|
||||
# Prefer Grok 4.1 Fast Reasoning for speed as well (latest fast SKU).
|
||||
if self.PRIMARY_MODEL in allowed_models:
|
||||
return self.PRIMARY_MODEL
|
||||
if self.FALLBACK_MODEL in allowed_models:
|
||||
return self.FALLBACK_MODEL
|
||||
return allowed_models[0]
|
||||
|
||||
else: # BALANCED or default
|
||||
# Prefer GROK-4 for balanced use (best overall capabilities)
|
||||
if "grok-4" in allowed_models:
|
||||
return "grok-4"
|
||||
elif "grok-3" in allowed_models:
|
||||
return "grok-3"
|
||||
elif "grok-3-fast" in allowed_models:
|
||||
return "grok-3-fast"
|
||||
# Fall back to any available model
|
||||
# Prefer Grok 4.1 Fast Reasoning for balanced use.
|
||||
if self.PRIMARY_MODEL in allowed_models:
|
||||
return self.PRIMARY_MODEL
|
||||
if self.FALLBACK_MODEL in allowed_models:
|
||||
return self.FALLBACK_MODEL
|
||||
return allowed_models[0]
|
||||
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
X.AI GROK Model Tests
|
||||
|
||||
Tests that verify X.AI GROK functionality including:
|
||||
- Model alias resolution (grok, grok3, grokfast map to actual GROK models)
|
||||
- GROK-3 and GROK-3-fast models work correctly
|
||||
- Model alias resolution (grok maps to Grok 4)
|
||||
- GROK-4 and GROK-4.1 Fast Reasoning models work correctly
|
||||
- Conversation continuity works with GROK models
|
||||
- API integration and response validation
|
||||
"""
|
||||
@@ -63,75 +63,44 @@ class XAIModelsTest(BaseSimulatorTest):
|
||||
if continuation_id:
|
||||
self.logger.info(f" ✅ Got continuation_id: {continuation_id}")
|
||||
|
||||
# Test 2: Direct grok-3 model name
|
||||
self.logger.info(" 2: Testing direct model name (grok-3)")
|
||||
# Test 2: Direct grok-4.1-fast model name
|
||||
self.logger.info(" 2: Testing direct model name (grok-4.1-fast)")
|
||||
|
||||
response2, _ = self.call_mcp_tool(
|
||||
"chat",
|
||||
{
|
||||
"prompt": "Say 'Hello from GROK-3!' and nothing else.",
|
||||
"model": "grok-3",
|
||||
"prompt": "Say 'Hello from GROK-4.1 Fast!' and nothing else.",
|
||||
"model": "grok-4.1-fast",
|
||||
"temperature": 0.1,
|
||||
},
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
self.logger.info(" 3: Testing GROK-3-fast model")
|
||||
# Test 3: grok-4.1-fast-reasoning alias
|
||||
self.logger.info(" 3: Testing 'grok-4.1-fast-reasoning' alias")
|
||||
|
||||
response3, _ = self.call_mcp_tool(
|
||||
"chat",
|
||||
{
|
||||
"prompt": "Say 'Hello from GROK-3-fast!' and nothing else.",
|
||||
"model": "grok-3-fast",
|
||||
"prompt": "Say 'Hello from GROK-4.1 Fast Reasoning alias!' and nothing else.",
|
||||
"model": "grok-4.1-fast-reasoning",
|
||||
"temperature": 0.1,
|
||||
},
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
self.logger.info(" ✅ GROK-3-fast model call completed")
|
||||
self.logger.info(" ✅ GROK-4.1-fast-reasoning alias call completed")
|
||||
|
||||
# Test 4: Shorthand aliases
|
||||
self.logger.info(" 4: Testing shorthand aliases (grok3, grokfast)")
|
||||
|
||||
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")
|
||||
# Test 4: Conversation continuity with GROK models
|
||||
self.logger.info(" 4: Testing conversation continuity with GROK")
|
||||
|
||||
response6, new_continuation_id = self.call_mcp_tool(
|
||||
"chat",
|
||||
@@ -167,8 +136,8 @@ class XAIModelsTest(BaseSimulatorTest):
|
||||
else:
|
||||
self.logger.warning(" ⚠️ Model may not have remembered the number")
|
||||
|
||||
# Test 6: Validate X.AI API usage from logs
|
||||
self.logger.info(" 6: Validating X.AI API usage in logs")
|
||||
# Test 5: Validate X.AI API usage from logs
|
||||
self.logger.info(" 5: Validating X.AI API usage in logs")
|
||||
logs = self.get_recent_server_logs()
|
||||
|
||||
# Check for X.AI API calls
|
||||
|
||||
@@ -108,9 +108,9 @@ class TestAutoModeComprehensive:
|
||||
"OPENROUTER_API_KEY": None,
|
||||
},
|
||||
{
|
||||
"EXTENDED_REASONING": "grok-4", # GROK-4 for reasoning (now preferred)
|
||||
"FAST_RESPONSE": "grok-3-fast", # GROK-3-fast for speed
|
||||
"BALANCED": "grok-4", # GROK-4 as balanced (now preferred)
|
||||
"EXTENDED_REASONING": "grok-4-1-fast-reasoning", # Latest Grok 4.1 Fast Reasoning
|
||||
"FAST_RESPONSE": "grok-4-1-fast-reasoning", # Latest fast SKU
|
||||
"BALANCED": "grok-4-1-fast-reasoning", # Latest balanced default
|
||||
},
|
||||
),
|
||||
# Both Gemini and OpenAI available - Google comes first in priority
|
||||
|
||||
@@ -321,7 +321,7 @@ class TestAutoModeProviderSelection:
|
||||
("mini", ProviderType.OPENAI, "gpt-5-mini"), # "mini" now resolves to gpt-5-mini
|
||||
("o3mini", ProviderType.OPENAI, "o3-mini"),
|
||||
("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:
|
||||
|
||||
@@ -161,7 +161,7 @@ class TestModelEnumeration:
|
||||
("grok", False), # X.AI - 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
|
||||
("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):
|
||||
|
||||
@@ -86,20 +86,17 @@ class TestSupportedModelsAliases:
|
||||
# Test specific aliases
|
||||
assert "grok" 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 "grok3fast" in provider.MODEL_CAPABILITIES["grok-3-fast"].aliases
|
||||
assert "grokfast" in provider.MODEL_CAPABILITIES["grok-3-fast"].aliases
|
||||
assert "grok-4.1-fast-reasoning" in provider.MODEL_CAPABILITIES["grok-4-1-fast-reasoning"].aliases
|
||||
|
||||
# Test alias resolution
|
||||
assert provider._resolve_model_name("grok") == "grok-4"
|
||||
assert provider._resolve_model_name("grok4") == "grok-4"
|
||||
assert provider._resolve_model_name("grok3") == "grok-3"
|
||||
assert provider._resolve_model_name("grok3fast") == "grok-3-fast"
|
||||
assert provider._resolve_model_name("grokfast") == "grok-3-fast"
|
||||
assert provider._resolve_model_name("grok-4.1-fast-reasoning") == "grok-4-1-fast-reasoning"
|
||||
assert provider._resolve_model_name("grok-4.1-fast-reasoning-latest") == "grok-4-1-fast-reasoning"
|
||||
|
||||
# Test case insensitive resolution
|
||||
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):
|
||||
"""Test DIAL provider's alias structure."""
|
||||
@@ -148,10 +145,10 @@ class TestSupportedModelsAliases:
|
||||
# Test XAI
|
||||
xai_provider = XAIModelProvider("test-key")
|
||||
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-3-fast" in xai_models
|
||||
assert "grokfast" in xai_models
|
||||
assert "grok-4.1-fast" in xai_models
|
||||
assert "grok-4.1-fast-reasoning" in xai_models
|
||||
|
||||
# Test DIAL
|
||||
dial_provider = DIALModelProvider("test-key")
|
||||
|
||||
@@ -47,17 +47,21 @@ class TestXAIProvider:
|
||||
# Test valid models
|
||||
assert provider.validate_model_name("grok-4") 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("grok3") is True
|
||||
assert provider.validate_model_name("grokfast") is True
|
||||
assert provider.validate_model_name("grok3fast") 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
|
||||
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
|
||||
assert provider.validate_model_name("invalid-model") 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("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):
|
||||
"""Test model name resolution."""
|
||||
@@ -66,33 +70,12 @@ class TestXAIProvider:
|
||||
# Test shorthand resolution
|
||||
assert provider._resolve_model_name("grok") == "grok-4"
|
||||
assert provider._resolve_model_name("grok4") == "grok-4"
|
||||
assert provider._resolve_model_name("grok3") == "grok-3"
|
||||
assert provider._resolve_model_name("grokfast") == "grok-3-fast"
|
||||
assert provider._resolve_model_name("grok3fast") == "grok-3-fast"
|
||||
assert provider._resolve_model_name("grok-4.1-fast-reasoning") == "grok-4-1-fast-reasoning"
|
||||
assert provider._resolve_model_name("grok-4.1-fast-reasoning-latest") == "grok-4-1-fast-reasoning"
|
||||
|
||||
# Test full name passthrough
|
||||
assert provider._resolve_model_name("grok-4") == "grok-4"
|
||||
assert provider._resolve_model_name("grok-3") == "grok-3"
|
||||
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
|
||||
assert provider._resolve_model_name("grok-4.1-fast") == "grok-4-1-fast-reasoning"
|
||||
|
||||
def test_get_capabilities_grok4(self):
|
||||
"""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.default_temp == 0.3
|
||||
|
||||
def test_get_capabilities_grok3_fast(self):
|
||||
"""Test getting model capabilities for GROK-3 Fast."""
|
||||
def test_get_capabilities_grok4_1_fast(self):
|
||||
"""Test getting model capabilities for GROK-4.1 Fast Reasoning."""
|
||||
provider = XAIModelProvider("test-key")
|
||||
|
||||
capabilities = provider.get_capabilities("grok-3-fast")
|
||||
assert capabilities.model_name == "grok-3-fast"
|
||||
assert capabilities.friendly_name == "X.AI (Grok 3 Fast)"
|
||||
assert capabilities.context_window == 131_072
|
||||
capabilities = provider.get_capabilities("grok-4.1-fast")
|
||||
assert capabilities.model_name == "grok-4-1-fast-reasoning"
|
||||
assert capabilities.friendly_name == "X.AI (Grok 4.1 Fast Reasoning)"
|
||||
assert capabilities.context_window == 2_000_000
|
||||
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):
|
||||
"""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.context_window == 256_000
|
||||
|
||||
capabilities_fast = provider.get_capabilities("grokfast")
|
||||
assert capabilities_fast.model_name == "grok-3-fast" # Should resolve to full name
|
||||
capabilities_fast = provider.get_capabilities("grok-4.1-fast-reasoning")
|
||||
assert capabilities_fast.model_name == "grok-4-1-fast-reasoning" # Should resolve to full name
|
||||
|
||||
def test_unsupported_model_capabilities(self):
|
||||
"""Test error handling for unsupported models."""
|
||||
@@ -148,20 +134,23 @@ class TestXAIProvider:
|
||||
"""X.AI capabilities should expose extended thinking support correctly."""
|
||||
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:
|
||||
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):
|
||||
"""Test provider type identification."""
|
||||
provider = XAIModelProvider("test-key")
|
||||
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):
|
||||
"""Test model restrictions functionality."""
|
||||
# Clear cached restriction service
|
||||
@@ -173,20 +162,17 @@ class TestXAIProvider:
|
||||
|
||||
provider = XAIModelProvider("test-key")
|
||||
|
||||
# grok-3 should be allowed
|
||||
assert provider.validate_model_name("grok-3") is True
|
||||
assert provider.validate_model_name("grok3") is True # Shorthand for grok-3
|
||||
# grok-4 should be allowed (including alias)
|
||||
assert provider.validate_model_name("grok-4") is True
|
||||
assert provider.validate_model_name("grok") is True
|
||||
|
||||
# grok should be blocked (resolves to grok-4 which is not allowed)
|
||||
assert provider.validate_model_name("grok") is False
|
||||
# grok-4.1-fast should be blocked by restrictions
|
||||
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
|
||||
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"})
|
||||
@patch.dict(os.environ, {"XAI_ALLOWED_MODELS": "grok-4.1-fast-reasoning"})
|
||||
def test_multiple_model_restrictions(self):
|
||||
"""Test multiple models in restrictions."""
|
||||
"""Restrictions should allow aliases for Grok 4.1 Fast."""
|
||||
# Clear cached restriction service
|
||||
import utils.model_restrictions
|
||||
from providers.registry import ModelProviderRegistry
|
||||
@@ -196,24 +182,18 @@ class TestXAIProvider:
|
||||
|
||||
provider = XAIModelProvider("test-key")
|
||||
|
||||
# Shorthand "grok" should be allowed (resolves to grok-4)
|
||||
assert provider.validate_model_name("grok") is True
|
||||
# Alias should be allowed (resolves to grok-4.1-fast)
|
||||
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
|
||||
|
||||
# "grok-3" should NOT be allowed (not in restriction list)
|
||||
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"})
|
||||
@patch.dict(os.environ, {"XAI_ALLOWED_MODELS": "grok,grok-4.1-fast"})
|
||||
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
|
||||
import utils.model_restrictions
|
||||
|
||||
@@ -223,12 +203,8 @@ class TestXAIProvider:
|
||||
|
||||
# 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-3") is True
|
||||
assert provider.validate_model_name("grok-4") 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
|
||||
assert provider.validate_model_name("grok-4.1-fast") is True
|
||||
|
||||
@patch.dict(os.environ, {"XAI_ALLOWED_MODELS": ""})
|
||||
def test_empty_restrictions_allows_all(self):
|
||||
@@ -241,10 +217,9 @@ class TestXAIProvider:
|
||||
provider = XAIModelProvider("test-key")
|
||||
|
||||
assert provider.validate_model_name("grok-4") 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-4.1-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("grokfast") is True
|
||||
assert provider.validate_model_name("grok4") is True
|
||||
|
||||
def test_friendly_name(self):
|
||||
@@ -252,8 +227,8 @@ class TestXAIProvider:
|
||||
provider = XAIModelProvider("test-key")
|
||||
assert provider.FRIENDLY_NAME == "X.AI"
|
||||
|
||||
capabilities = provider.get_capabilities("grok-3")
|
||||
assert capabilities.friendly_name == "X.AI (Grok 3)"
|
||||
capabilities = provider.get_capabilities("grok-4")
|
||||
assert capabilities.friendly_name == "X.AI (Grok 4)"
|
||||
|
||||
def test_supported_models_structure(self):
|
||||
"""Test that MODEL_CAPABILITIES has the correct structure."""
|
||||
@@ -261,8 +236,7 @@ class TestXAIProvider:
|
||||
|
||||
# Check that all expected base models are present
|
||||
assert "grok-4" in provider.MODEL_CAPABILITIES
|
||||
assert "grok-3" in provider.MODEL_CAPABILITIES
|
||||
assert "grok-3-fast" in provider.MODEL_CAPABILITIES
|
||||
assert "grok-4-1-fast-reasoning" in provider.MODEL_CAPABILITIES
|
||||
|
||||
# Check model configs have required fields
|
||||
from providers.shared import ModelCapabilities
|
||||
@@ -280,20 +254,11 @@ class TestXAIProvider:
|
||||
assert "grok-4" in grok4_config.aliases
|
||||
assert "grok4" in grok4_config.aliases
|
||||
|
||||
grok3_config = provider.MODEL_CAPABILITIES["grok-3"]
|
||||
assert grok3_config.context_window == 131_072
|
||||
assert grok3_config.supports_extended_thinking is False
|
||||
# Check aliases are correctly structured
|
||||
assert "grok3" in grok3_config.aliases # grok3 resolves to grok-3
|
||||
|
||||
# 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
|
||||
grok41fast_config = provider.MODEL_CAPABILITIES["grok-4-1-fast-reasoning"]
|
||||
assert grok41fast_config.context_window == 2_000_000
|
||||
assert grok41fast_config.supports_extended_thinking is True
|
||||
assert "grok-4.1-fast" in grok41fast_config.aliases
|
||||
assert "grok-4.1-fast-reasoning" in grok41fast_config.aliases
|
||||
|
||||
@patch("providers.openai_compatible.OpenAI")
|
||||
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]
|
||||
assert call_kwargs["model"] == "grok-4"
|
||||
|
||||
# Test grok3 -> grok-3
|
||||
mock_response.model = "grok-3"
|
||||
provider.generate_content(prompt="Test", model_name="grok3", temperature=0.7)
|
||||
# Test grok-4.1-fast-reasoning -> grok-4-1-fast-reasoning
|
||||
mock_response.model = "grok-4-1-fast-reasoning"
|
||||
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]
|
||||
assert call_kwargs["model"] == "grok-3"
|
||||
assert call_kwargs["model"] == "grok-4-1-fast-reasoning"
|
||||
|
||||
# Test grokfast -> grok-3-fast
|
||||
mock_response.model = "grok-3-fast"
|
||||
provider.generate_content(prompt="Test", model_name="grokfast", temperature=0.7)
|
||||
# Test grok-4.1-fast -> grok-4-1-fast-reasoning
|
||||
provider.generate_content(prompt="Test", model_name="grok-4.1-fast", temperature=0.7)
|
||||
call_kwargs = mock_client.chat.completions.create.call_args[1]
|
||||
assert call_kwargs["model"] == "grok-3-fast"
|
||||
|
||||
# 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"
|
||||
assert call_kwargs["model"] == "grok-4-1-fast-reasoning"
|
||||
|
||||
@@ -16,7 +16,7 @@ Environment Variables:
|
||||
Example:
|
||||
OPENAI_ALLOWED_MODELS=o3-mini,o4-mini
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user