Merge pull request #191 from rwl4/grok4-support

Add configuration for Grok 4.
This commit is contained in:
Beehive Innovations
2025-08-07 23:08:03 -07:00
committed by GitHub
4 changed files with 78 additions and 13 deletions

View File

@@ -290,6 +290,7 @@ nano .env
# The file will contain, at least one should be set:
# GEMINI_API_KEY=your-gemini-api-key-here # For Gemini models
# OPENAI_API_KEY=your-openai-api-key-here # For O3 model
# XAI_API_KEY=your-xai-api-key-here # For Grok models
# OPENROUTER_API_KEY=your-openrouter-key # For OpenRouter (see docs/custom_models.md)
# DIAL_API_KEY=your-dial-api-key-here # For DIAL platform

View File

@@ -39,6 +39,9 @@ Regardless of your default configuration, you can specify models per request:
| **`o3-mini`** | OpenAI | 200K tokens | Balanced speed/quality | Moderate complexity tasks |
| **`o4-mini`** | OpenAI | 200K tokens | Latest reasoning model | Optimized for shorter contexts |
| **`gpt4.1`** | OpenAI | 1M tokens | Latest GPT-4 with extended context | Large codebase analysis, comprehensive reviews |
| **`grok-4-latest`** | X.AI | 256K tokens | Latest flagship 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 |
| **`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 |
@@ -49,6 +52,8 @@ cloud models (expensive/powerful) AND local models (free/private) in the same co
- **Gemini Models**: Support thinking modes (minimal to max), web search, 1M context
- **O3 Models**: Excellent reasoning, systematic analysis, 200K context
- **GPT-4.1**: Extended context window (1M tokens), general capabilities
- **Grok-4**: Extended thinking support, vision capabilities, 256K context
- **Grok-3 Models**: Advanced reasoning, 131K context
## Model Usage Restrictions

View File

@@ -74,7 +74,8 @@ DEFAULT_MODEL=auto # Claude picks best model for each task (recommended)
- **`o3`**: Strong logical reasoning (200K context)
- **`o3-mini`**: Balanced speed/quality (200K context)
- **`o4-mini`**: Latest reasoning model, optimized for shorter contexts
- **`grok`**: GROK-3 advanced reasoning (131K context)
- **`grok-3`**: GROK-3 advanced reasoning (131K context)
- **`grok-4-latest`**: GROK-4 latest flagship model (256K context)
- **Custom models**: via OpenRouter or local APIs
### Thinking Mode Configuration
@@ -107,7 +108,7 @@ OPENAI_ALLOWED_MODELS=o3-mini,o4-mini,mini
GOOGLE_ALLOWED_MODELS=flash,pro
# X.AI GROK model restrictions
XAI_ALLOWED_MODELS=grok-3,grok-3-fast
XAI_ALLOWED_MODELS=grok-3,grok-3-fast,grok-4-latest
# OpenRouter model restrictions (affects models via custom provider)
OPENROUTER_ALLOWED_MODELS=opus,sonnet,mistral
@@ -128,9 +129,11 @@ OPENROUTER_ALLOWED_MODELS=opus,sonnet,mistral
- `pro` (shorthand for Pro model)
**X.AI GROK Models:**
- `grok-4-latest` (256K context, latest flagship model with reasoning, vision, and structured outputs)
- `grok-3` (131K context, advanced reasoning)
- `grok-3-fast` (131K context, higher performance)
- `grok` (shorthand for grok-3)
- `grok` (shorthand for grok-4-latest)
- `grok4` (shorthand for grok-4-latest)
- `grok3` (shorthand for grok-3)
- `grokfast` (shorthand for grok-3-fast)

View File

@@ -45,6 +45,9 @@ class TestXAIProvider:
provider = XAIModelProvider("test-key")
# Test valid models
assert provider.validate_model_name("grok-4") is True
assert provider.validate_model_name("grok-4-latest") 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
@@ -69,6 +72,7 @@ class TestXAIProvider:
assert provider._resolve_model_name("grok3fast") == "grok-3-fast"
# 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"
@@ -91,6 +95,27 @@ class TestXAIProvider:
assert capabilities.temperature_constraint.max_temp == 2.0
assert capabilities.temperature_constraint.default_temp == 0.3
def test_get_capabilities_grok4(self):
"""Test getting model capabilities for GROK-4."""
provider = XAIModelProvider("test-key")
capabilities = provider.get_capabilities("grok-4")
assert capabilities.model_name == "grok-4"
assert capabilities.friendly_name == "X.AI (Grok 4)"
assert capabilities.context_window == 256_000
assert capabilities.provider == ProviderType.XAI
assert capabilities.supports_extended_thinking is True
assert capabilities.supports_system_prompts is True
assert capabilities.supports_streaming is True
assert capabilities.supports_function_calling is True
assert capabilities.supports_json_mode is True
assert capabilities.supports_images 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.7
def test_get_capabilities_grok3_fast(self):
"""Test getting model capabilities for GROK-3 Fast."""
provider = XAIModelProvider("test-key")
@@ -121,9 +146,14 @@ class TestXAIProvider:
provider.get_capabilities("invalid-model")
def test_thinking_mode_support(self):
"""Test X.AI model thinking mode support - grok-4 supports it, earlier models don't."""
"""Test thinking mode support for X.AI models."""
provider = XAIModelProvider("test-key")
# Grok-4 supports thinking mode
assert provider.supports_thinking_mode("grok-4") is True
assert provider.supports_thinking_mode("grok") is True # Resolves to grok-4
# Grok-3 models don't support thinking mode
assert not provider.supports_thinking_mode("grok-3")
assert not provider.supports_thinking_mode("grok-3-fast")
assert provider.supports_thinking_mode("grok-4") # grok-4 supports thinking mode
@@ -170,7 +200,10 @@ class TestXAIProvider:
# Shorthand "grok" should be allowed (resolves to grok-4)
assert provider.validate_model_name("grok") is True
# Full name "grok-3" should NOT be allowed (only shorthand "grok" is in restriction list)
# Full name "grok-4" should NOT be allowed (only shorthand "grok" is in restriction list)
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)
@@ -179,7 +212,7 @@ class TestXAIProvider:
# 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"})
@patch.dict(os.environ, {"XAI_ALLOWED_MODELS": "grok,grok-3,grok-4"})
def test_both_shorthand_and_full_name_allowed(self):
"""Test that both shorthand and full name can be allowed."""
# Clear cached restriction service
@@ -190,8 +223,9 @@ class TestXAIProvider:
provider = XAIModelProvider("test-key")
# Both shorthand and full name should be allowed
assert provider.validate_model_name("grok") is True
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
@@ -207,10 +241,12 @@ 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") is True
assert provider.validate_model_name("grokfast") is True
assert provider.validate_model_name("grok4") is True
def test_friendly_name(self):
"""Test friendly name constant."""
@@ -232,14 +268,23 @@ class TestXAIProvider:
# Check model configs have required fields
from providers.base import ModelCapabilities
grok4_config = provider.SUPPORTED_MODELS["grok-4"]
assert isinstance(grok4_config, ModelCapabilities)
assert hasattr(grok4_config, "context_window")
assert hasattr(grok4_config, "supports_extended_thinking")
assert hasattr(grok4_config, "aliases")
assert grok4_config.context_window == 256_000
assert grok4_config.supports_extended_thinking is True
# Check aliases are correctly structured
assert "grok" in grok4_config.aliases
assert "grok-4" in grok4_config.aliases
assert "grok-4-latest" in grok4_config.aliases
assert "grok4" in grok4_config.aliases
grok3_config = provider.SUPPORTED_MODELS["grok-3"]
assert isinstance(grok3_config, ModelCapabilities)
assert hasattr(grok3_config, "context_window")
assert hasattr(grok3_config, "supports_extended_thinking")
assert hasattr(grok3_config, "aliases")
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
@@ -268,7 +313,7 @@ class TestXAIProvider:
mock_response.choices = [MagicMock()]
mock_response.choices[0].message.content = "Test response"
mock_response.choices[0].finish_reason = "stop"
mock_response.model = "grok-3" # API returns the resolved model name
mock_response.model = "grok-4" # API returns the resolved model name
mock_response.id = "test-id"
mock_response.created = 1234567890
mock_response.usage = MagicMock()
@@ -322,6 +367,17 @@ class TestXAIProvider:
provider = XAIModelProvider("test-key")
# Test grok4 -> grok-4
mock_response.model = "grok-4"
provider.generate_content(prompt="Test", model_name="grok4", temperature=0.7)
call_kwargs = mock_client.chat.completions.create.call_args[1]
assert call_kwargs["model"] == "grok-4"
# Test grok-4 -> grok-4
provider.generate_content(prompt="Test", model_name="grok-4", temperature=0.7)
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)