feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
"""Tests for GrokBackend in src/timmy/backends.py and Grok dashboard routes."""
|
|
|
|
|
|
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
|
|
|
|
|
|
# ── grok_available ───────────────────────────────────────────────────────────
|
|
|
|
|
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
def test_grok_available_false_when_disabled():
|
|
|
|
|
"""Grok not available when GROK_ENABLED is false."""
|
|
|
|
|
with patch("config.settings") as mock_settings:
|
|
|
|
|
mock_settings.grok_enabled = False
|
|
|
|
|
mock_settings.xai_api_key = "xai-test-key"
|
|
|
|
|
from timmy.backends import grok_available
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
assert grok_available() is False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_available_false_when_no_key():
|
|
|
|
|
"""Grok not available when XAI_API_KEY is empty."""
|
|
|
|
|
with patch("config.settings") as mock_settings:
|
|
|
|
|
mock_settings.grok_enabled = True
|
|
|
|
|
mock_settings.xai_api_key = ""
|
|
|
|
|
from timmy.backends import grok_available
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
assert grok_available() is False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_available_true_when_enabled_and_key_set():
|
|
|
|
|
"""Grok available when both enabled and key are set."""
|
|
|
|
|
with patch("config.settings") as mock_settings:
|
|
|
|
|
mock_settings.grok_enabled = True
|
|
|
|
|
mock_settings.xai_api_key = "xai-test-key"
|
|
|
|
|
from timmy.backends import grok_available
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
assert grok_available() is True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── GrokBackend construction ────────────────────────────────────────────────
|
|
|
|
|
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
def test_grok_backend_init_with_explicit_params():
|
|
|
|
|
"""GrokBackend can be created with explicit api_key and model."""
|
|
|
|
|
from timmy.backends import GrokBackend
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
backend = GrokBackend(api_key="xai-test", model="grok-3-fast")
|
|
|
|
|
assert backend._api_key == "xai-test"
|
|
|
|
|
assert backend._model == "grok-3-fast"
|
|
|
|
|
assert backend.stats.total_requests == 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_backend_init_from_settings():
|
|
|
|
|
"""GrokBackend reads from config.settings when no params given."""
|
|
|
|
|
with patch("config.settings") as mock_settings:
|
|
|
|
|
mock_settings.xai_api_key = "xai-from-env"
|
|
|
|
|
mock_settings.grok_default_model = "grok-3"
|
|
|
|
|
from timmy.backends import GrokBackend
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
backend = GrokBackend()
|
|
|
|
|
assert backend._api_key == "xai-from-env"
|
|
|
|
|
assert backend._model == "grok-3"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_backend_run_no_key_returns_error():
|
|
|
|
|
"""run() gracefully returns error message when no API key."""
|
|
|
|
|
from timmy.backends import GrokBackend
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
backend = GrokBackend(api_key="", model="grok-3-fast")
|
|
|
|
|
result = backend.run("hello")
|
|
|
|
|
assert "not configured" in result.content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_backend_run_success():
|
|
|
|
|
"""run() returns content from the API on success."""
|
|
|
|
|
from timmy.backends import GrokBackend
|
|
|
|
|
|
|
|
|
|
backend = GrokBackend(api_key="xai-test", model="grok-3-fast")
|
|
|
|
|
|
|
|
|
|
mock_response = MagicMock()
|
|
|
|
|
mock_response.choices = [MagicMock()]
|
|
|
|
|
mock_response.choices[0].message.content = "Grok says hello"
|
|
|
|
|
mock_response.usage = MagicMock()
|
|
|
|
|
mock_response.usage.prompt_tokens = 10
|
|
|
|
|
mock_response.usage.completion_tokens = 5
|
|
|
|
|
mock_response.model = "grok-3-fast"
|
|
|
|
|
|
|
|
|
|
mock_client = MagicMock()
|
|
|
|
|
mock_client.chat.completions.create.return_value = mock_response
|
|
|
|
|
|
|
|
|
|
with patch.object(backend, "_get_client", return_value=mock_client):
|
|
|
|
|
result = backend.run("hello")
|
|
|
|
|
|
|
|
|
|
assert result.content == "Grok says hello"
|
|
|
|
|
assert backend.stats.total_requests == 1
|
|
|
|
|
assert backend.stats.total_prompt_tokens == 10
|
|
|
|
|
assert backend.stats.total_completion_tokens == 5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_backend_run_api_error():
|
|
|
|
|
"""run() returns error message on API failure."""
|
|
|
|
|
from timmy.backends import GrokBackend
|
|
|
|
|
|
|
|
|
|
backend = GrokBackend(api_key="xai-test", model="grok-3-fast")
|
|
|
|
|
|
|
|
|
|
mock_client = MagicMock()
|
|
|
|
|
mock_client.chat.completions.create.side_effect = Exception("API timeout")
|
|
|
|
|
|
|
|
|
|
with patch.object(backend, "_get_client", return_value=mock_client):
|
|
|
|
|
result = backend.run("hello")
|
|
|
|
|
|
|
|
|
|
assert "unavailable" in result.content
|
|
|
|
|
assert backend.stats.errors == 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_backend_history_management():
|
|
|
|
|
"""GrokBackend maintains conversation history."""
|
|
|
|
|
from timmy.backends import GrokBackend
|
|
|
|
|
|
|
|
|
|
backend = GrokBackend(api_key="xai-test", model="grok-3-fast")
|
|
|
|
|
|
|
|
|
|
mock_response = MagicMock()
|
|
|
|
|
mock_response.choices = [MagicMock()]
|
|
|
|
|
mock_response.choices[0].message.content = "response"
|
|
|
|
|
mock_response.usage = MagicMock()
|
|
|
|
|
mock_response.usage.prompt_tokens = 10
|
|
|
|
|
mock_response.usage.completion_tokens = 5
|
|
|
|
|
|
|
|
|
|
mock_client = MagicMock()
|
|
|
|
|
mock_client.chat.completions.create.return_value = mock_response
|
|
|
|
|
|
|
|
|
|
with patch.object(backend, "_get_client", return_value=mock_client):
|
|
|
|
|
backend.run("first message")
|
|
|
|
|
backend.run("second message")
|
|
|
|
|
|
|
|
|
|
assert len(backend._history) == 4 # 2 user + 2 assistant
|
|
|
|
|
assert backend._history[0]["role"] == "user"
|
|
|
|
|
assert backend._history[1]["role"] == "assistant"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_backend_health_check_no_key():
|
|
|
|
|
"""health_check() returns not-ok when no API key."""
|
|
|
|
|
from timmy.backends import GrokBackend
|
|
|
|
|
|
|
|
|
|
backend = GrokBackend(api_key="", model="grok-3-fast")
|
|
|
|
|
health = backend.health_check()
|
|
|
|
|
assert health["ok"] is False
|
|
|
|
|
assert "not configured" in health["error"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_backend_health_check_success():
|
|
|
|
|
"""health_check() returns ok when API key is set and models endpoint works."""
|
|
|
|
|
from timmy.backends import GrokBackend
|
|
|
|
|
|
|
|
|
|
backend = GrokBackend(api_key="xai-test", model="grok-3-fast")
|
|
|
|
|
|
|
|
|
|
mock_client = MagicMock()
|
|
|
|
|
mock_client.models.list.return_value = []
|
|
|
|
|
|
|
|
|
|
with patch.object(backend, "_get_client", return_value=mock_client):
|
|
|
|
|
health = backend.health_check()
|
|
|
|
|
|
|
|
|
|
assert health["ok"] is True
|
|
|
|
|
assert health["backend"] == "grok"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_backend_estimated_cost():
|
|
|
|
|
"""estimated_cost property calculates sats from token usage."""
|
|
|
|
|
from timmy.backends import GrokUsageStats
|
|
|
|
|
|
|
|
|
|
stats = GrokUsageStats(
|
|
|
|
|
total_prompt_tokens=1_000_000,
|
|
|
|
|
total_completion_tokens=500_000,
|
|
|
|
|
)
|
|
|
|
|
# Input: 1M tokens * $5/1M = $5
|
|
|
|
|
# Output: 500K tokens * $15/1M = $7.50
|
|
|
|
|
# Total: $12.50 / $0.001 = 12,500 sats
|
|
|
|
|
assert stats.estimated_cost_sats == 12500
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_backend_build_messages():
|
|
|
|
|
"""_build_messages includes system prompt and history."""
|
|
|
|
|
from timmy.backends import GrokBackend
|
|
|
|
|
|
|
|
|
|
backend = GrokBackend(api_key="xai-test", model="grok-3-fast")
|
|
|
|
|
backend._history = [
|
|
|
|
|
{"role": "user", "content": "previous"},
|
|
|
|
|
{"role": "assistant", "content": "yes"},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
messages = backend._build_messages("new question")
|
|
|
|
|
assert messages[0]["role"] == "system"
|
|
|
|
|
assert messages[1]["role"] == "user"
|
|
|
|
|
assert messages[1]["content"] == "previous"
|
|
|
|
|
assert messages[-1]["role"] == "user"
|
|
|
|
|
assert messages[-1]["content"] == "new question"
|
|
|
|
|
|
|
|
|
|
|
[loop-cycle-40] fix: use get_system_prompt() in cloud backends (#135) (#138)
## What
Cloud backends (Grok, Claude, AirLLM) were importing SYSTEM_PROMPT directly, which is always SYSTEM_PROMPT_LITE and contains unformatted {model_name} and {session_id} placeholders.
## Changes
- backends.py: Replace `from timmy.prompts import SYSTEM_PROMPT` with `from timmy.prompts import get_system_prompt`
- AirLLM: uses `get_system_prompt(tools_enabled=False, session_id="airllm")` (LITE tier, correct)
- Grok: uses `get_system_prompt(tools_enabled=True, session_id="grok")` (FULL tier)
- Claude: uses `get_system_prompt(tools_enabled=True, session_id="claude")` (FULL tier)
- 9 new tests verify formatted model names, correct tier selection, and session_id formatting
## Tests
1508 passed, 0 failed (41 new tests this cycle)
Fixes #135
Co-authored-by: Kimi Agent <kimi@timmy.local>
Reviewed-on: http://localhost:3000/rockachopa/Timmy-time-dashboard/pulls/138
Reviewed-by: rockachopa <alexpaynex@gmail.com>
Co-authored-by: hermes <hermes@timmy.local>
Co-committed-by: hermes <hermes@timmy.local>
2026-03-15 09:44:43 -04:00
|
|
|
def test_grok_prompt_contains_formatted_model_name():
|
|
|
|
|
"""Grok prompt should have actual model name, not literal {model_name}."""
|
|
|
|
|
with patch("config.settings") as mock_settings:
|
|
|
|
|
mock_settings.ollama_model = "llama3.2:3b"
|
|
|
|
|
from timmy.backends import GrokBackend
|
|
|
|
|
|
|
|
|
|
backend = GrokBackend(api_key="xai-test", model="grok-3-fast")
|
|
|
|
|
messages = backend._build_messages("test message")
|
|
|
|
|
system_prompt = messages[0]["content"]
|
|
|
|
|
|
|
|
|
|
# Should contain the actual model name, not the placeholder
|
|
|
|
|
assert "{model_name}" not in system_prompt
|
|
|
|
|
assert "llama3.2:3b" in system_prompt
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_prompt_gets_full_tier():
|
|
|
|
|
"""Grok should get FULL tier prompt (tools_enabled=True)."""
|
|
|
|
|
with patch("config.settings") as mock_settings:
|
|
|
|
|
mock_settings.ollama_model = "test-model"
|
|
|
|
|
from timmy.backends import GrokBackend
|
|
|
|
|
|
|
|
|
|
backend = GrokBackend(api_key="xai-test", model="grok-3-fast")
|
|
|
|
|
messages = backend._build_messages("test message")
|
|
|
|
|
system_prompt = messages[0]["content"]
|
|
|
|
|
|
|
|
|
|
# FULL tier should have TOOL USAGE section
|
|
|
|
|
assert "TOOL USAGE" in system_prompt
|
|
|
|
|
# FULL tier should have the full voice and brevity section
|
|
|
|
|
assert "VOICE AND BREVITY" in system_prompt
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_prompt_contains_session_id():
|
|
|
|
|
"""Grok prompt should have session_id formatted, not placeholder."""
|
|
|
|
|
with patch("config.settings") as mock_settings:
|
|
|
|
|
mock_settings.ollama_model = "test-model"
|
|
|
|
|
from timmy.backends import GrokBackend
|
|
|
|
|
|
|
|
|
|
backend = GrokBackend(api_key="xai-test", model="grok-3-fast")
|
|
|
|
|
messages = backend._build_messages("test message")
|
|
|
|
|
system_prompt = messages[0]["content"]
|
|
|
|
|
|
|
|
|
|
# Should contain the session_id, not the placeholder
|
|
|
|
|
assert '{session_id}"' not in system_prompt
|
|
|
|
|
assert 'session "grok"' in system_prompt
|
|
|
|
|
|
|
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
# ── get_grok_backend singleton ──────────────────────────────────────────────
|
|
|
|
|
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
def test_get_grok_backend_returns_singleton():
|
|
|
|
|
"""get_grok_backend returns the same instance on repeated calls."""
|
|
|
|
|
import timmy.backends as backends_mod
|
|
|
|
|
|
|
|
|
|
# Reset singleton
|
|
|
|
|
backends_mod._grok_backend = None
|
|
|
|
|
|
|
|
|
|
b1 = backends_mod.get_grok_backend()
|
|
|
|
|
b2 = backends_mod.get_grok_backend()
|
|
|
|
|
assert b1 is b2
|
|
|
|
|
|
|
|
|
|
# Cleanup
|
|
|
|
|
backends_mod._grok_backend = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── GROK_MODELS constant ───────────────────────────────────────────────────
|
|
|
|
|
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
def test_grok_models_dict_has_expected_entries():
|
|
|
|
|
from timmy.backends import GROK_MODELS
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
assert "grok-3-fast" in GROK_MODELS
|
|
|
|
|
assert "grok-3" in GROK_MODELS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── consult_grok tool ──────────────────────────────────────────────────────
|
|
|
|
|
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
def test_consult_grok_returns_unavailable_when_disabled():
|
|
|
|
|
"""consult_grok tool returns error when Grok is not available."""
|
|
|
|
|
with patch("timmy.backends.grok_available", return_value=False):
|
|
|
|
|
from timmy.tools import consult_grok
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
result = consult_grok("test query")
|
|
|
|
|
assert "not available" in result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_consult_grok_calls_backend_when_available():
|
|
|
|
|
"""consult_grok tool calls the Grok backend when available."""
|
|
|
|
|
from timmy.backends import RunResult
|
|
|
|
|
|
|
|
|
|
mock_backend = MagicMock()
|
|
|
|
|
mock_backend.run.return_value = RunResult(content="Grok answer")
|
|
|
|
|
mock_backend.stats = MagicMock()
|
|
|
|
|
mock_backend.stats.total_latency_ms = 100
|
|
|
|
|
|
ruff (#169)
* polish: streamline nav, extract inline styles, improve tablet UX
- Restructure desktop nav from 8+ flat links + overflow dropdown into
5 grouped dropdowns (Core, Agents, Intel, System, More) matching
the mobile menu structure to reduce decision fatigue
- Extract all inline styles from mission_control.html and base.html
notification elements into mission-control.css with semantic classes
- Replace JS-built innerHTML with secure DOM construction in
notification loader and chat history
- Add CONNECTING state to connection indicator (amber) instead of
showing OFFLINE before WebSocket connects
- Add tablet breakpoint (1024px) with larger touch targets for
Apple Pencil / stylus use and safe-area padding for iPad toolbar
- Add active-link highlighting in desktop dropdown menus
- Rename "Mission Control" page title to "System Overview" to
disambiguate from the chat home page
- Add "Home — Timmy Time" page title to index.html
https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h
* fix(security): move auth-gate credentials to environment variables
Hardcoded username, password, and HMAC secret in auth-gate.py replaced
with os.environ lookups. Startup now refuses to run if any variable is
unset. Added AUTH_GATE_SECRET/USER/PASS to .env.example.
https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h
* refactor(tooling): migrate from black+isort+bandit to ruff
Replace three separate linting/formatting tools with a single ruff
invocation. Updates tox.ini (lint, format, pre-push, pre-commit envs),
.pre-commit-config.yaml, and CI workflow. Fixes all ruff errors
including unused imports, missing raise-from, and undefined names.
Ruff config maps existing bandit skips to equivalent S-rules.
https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-11 12:23:35 -04:00
|
|
|
with (
|
|
|
|
|
patch("timmy.backends.grok_available", return_value=True),
|
|
|
|
|
patch("timmy.backends.get_grok_backend", return_value=mock_backend),
|
|
|
|
|
patch("config.settings") as mock_settings,
|
|
|
|
|
):
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
mock_settings.grok_free = True
|
|
|
|
|
mock_settings.grok_enabled = True
|
|
|
|
|
mock_settings.xai_api_key = "xai-test"
|
|
|
|
|
from timmy.tools import consult_grok
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
result = consult_grok("complex question")
|
|
|
|
|
|
|
|
|
|
assert "Grok answer" in result
|
|
|
|
|
mock_backend.run.assert_called_once_with("complex question")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── Grok dashboard route tests ─────────────────────────────────────────────
|
|
|
|
|
|
2026-03-08 12:50:44 -04:00
|
|
|
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
def test_grok_status_endpoint(client):
|
feat: agentic loop for multi-step tasks + regression fixes (#148)
* fix: name extraction blocklist, memory preview escaping, and gitignore cleanup
- Add _NAME_BLOCKLIST to extract_user_name() to reject gerunds and UI-state
words like "Sending" that were incorrectly captured as user names
- Collapse whitespace in get_memory_status() preview so newlines survive
JSON serialization without showing raw \n escape sequences
- Broaden .gitignore from specific memory/self/user_profile.md to memory/self/
and untrack memory/self/methodology.md (runtime-edited file)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: catch Ollama connection errors in session.py + add 71 smoke tests
- Wrap agent.run() in session.py with try/except so Ollama connection
failures return a graceful fallback message instead of dumping raw
tracebacks to Docker logs
- Add tests/test_smoke.py with 71 tests covering every GET route:
core pages, feature pages, JSON APIs, and a parametrized no-500 sweep
— catches import errors, template failures, and schema mismatches
that unit tests miss
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: agentic loop for multi-step tasks + Round 10 regression fixes
Agentic loop (Parts 1-4):
- Add multi-step chaining instructions to system prompt
- New agentic_loop.py with plan→execute→adapt→summarize flow
- Register plan_and_execute tool for background task execution
- Add max_agent_steps config setting (default: 10)
- Discord fix: 300s timeout, typing indicator, send error handling
- 16 new unit + e2e tests for agentic loop
Round 10 regressions (R1-R5, P1):
- R1: Fix literal \n escape sequences in tool responses
- R2: Chat timeout/error feedback in agent panel
- R3: /hands infinite spinner → static empty states
- R4: /self-coding infinite spinner → static stats + journal
- R5: /grok/status raw JSON → HTML dashboard template
- P1: VETO confirmation dialog on task cards
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: briefing route 500 in CI when agno is MagicMock stub
_call_agent() returned a MagicMock instead of a string when agno is
stubbed in tests, causing SQLite "Error binding parameter 4" on save.
Ensure the return value is always an actual string.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: briefing route 500 in CI — graceful degradation at route level
When agno is stubbed with MagicMock in CI, agent.run() returns a
MagicMock instead of raising — so the exception handler never fires
and a MagicMock propagates as the summary to SQLite, which can't
bind it.
Fix: catch at the route level and return a fallback Briefing object.
This follows the project's graceful degradation pattern — the briefing
page always renders, even when the backend is completely unavailable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Trip T <trip@local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 01:46:29 -05:00
|
|
|
"""GET /grok/status returns HTML dashboard page."""
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
response = client.get("/grok/status")
|
|
|
|
|
assert response.status_code == 200
|
feat: agentic loop for multi-step tasks + regression fixes (#148)
* fix: name extraction blocklist, memory preview escaping, and gitignore cleanup
- Add _NAME_BLOCKLIST to extract_user_name() to reject gerunds and UI-state
words like "Sending" that were incorrectly captured as user names
- Collapse whitespace in get_memory_status() preview so newlines survive
JSON serialization without showing raw \n escape sequences
- Broaden .gitignore from specific memory/self/user_profile.md to memory/self/
and untrack memory/self/methodology.md (runtime-edited file)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: catch Ollama connection errors in session.py + add 71 smoke tests
- Wrap agent.run() in session.py with try/except so Ollama connection
failures return a graceful fallback message instead of dumping raw
tracebacks to Docker logs
- Add tests/test_smoke.py with 71 tests covering every GET route:
core pages, feature pages, JSON APIs, and a parametrized no-500 sweep
— catches import errors, template failures, and schema mismatches
that unit tests miss
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: agentic loop for multi-step tasks + Round 10 regression fixes
Agentic loop (Parts 1-4):
- Add multi-step chaining instructions to system prompt
- New agentic_loop.py with plan→execute→adapt→summarize flow
- Register plan_and_execute tool for background task execution
- Add max_agent_steps config setting (default: 10)
- Discord fix: 300s timeout, typing indicator, send error handling
- 16 new unit + e2e tests for agentic loop
Round 10 regressions (R1-R5, P1):
- R1: Fix literal \n escape sequences in tool responses
- R2: Chat timeout/error feedback in agent panel
- R3: /hands infinite spinner → static empty states
- R4: /self-coding infinite spinner → static stats + journal
- R5: /grok/status raw JSON → HTML dashboard template
- P1: VETO confirmation dialog on task cards
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: briefing route 500 in CI when agno is MagicMock stub
_call_agent() returned a MagicMock instead of a string when agno is
stubbed in tests, causing SQLite "Error binding parameter 4" on save.
Ensure the return value is always an actual string.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: briefing route 500 in CI — graceful degradation at route level
When agno is stubbed with MagicMock in CI, agent.run() returns a
MagicMock instead of raising — so the exception handler never fires
and a MagicMock propagates as the summary to SQLite, which can't
bind it.
Fix: catch at the route level and return a fallback Briefing object.
This follows the project's graceful degradation pattern — the briefing
page always renders, even when the backend is completely unavailable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Trip T <trip@local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 01:46:29 -05:00
|
|
|
assert "text/html" in response.headers.get("content-type", "")
|
|
|
|
|
# Verify key status info is present in the rendered HTML
|
|
|
|
|
text = response.text
|
|
|
|
|
assert "Grok Status" in text
|
|
|
|
|
assert "Status" in text
|
feat: add Grok (xAI) as opt-in premium backend with monetization
- Add GrokBackend class in src/timmy/backends.py with full sync/async
support, health checks, usage stats, and cost estimation in sats
- Add consult_grok tool to Timmy's toolkit for proactive Grok queries
- Extend cascade router with Grok provider type for failover chain
- Add Grok Mode toggle card to Mission Control dashboard (HTMX live)
- Add "Ask Grok" button on chat input for direct Grok queries
- Add /grok/* routes: status, toggle, chat, stats endpoints
- Integrate Lightning invoice generation for Grok usage monetization
- Add GROK_ENABLED, XAI_API_KEY, GROK_DEFAULT_MODEL, GROK_MAX_SATS_PER_QUERY,
GROK_FREE config settings via pydantic-settings
- Update .env.example and docker-compose.yml with Grok env vars
- Add 21 tests covering backend, tools, and route endpoints (all green)
Local-first ethos preserved: Grok is premium augmentation only,
disabled by default, and Lightning-payable when enabled.
https://claude.ai/code/session_01FygwN8wS8J6WGZ8FPb7XGV
2026-02-27 01:12:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_toggle_returns_html(client):
|
|
|
|
|
"""POST /grok/toggle returns HTML response."""
|
|
|
|
|
response = client.post("/grok/toggle")
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_stats_endpoint(client):
|
|
|
|
|
"""GET /grok/stats returns usage statistics."""
|
|
|
|
|
response = client.get("/grok/stats")
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
data = response.json()
|
|
|
|
|
assert "total_requests" in data or "error" in data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_grok_chat_without_key(client):
|
|
|
|
|
"""POST /grok/chat returns error when Grok is not available."""
|
|
|
|
|
response = client.post(
|
|
|
|
|
"/grok/chat",
|
|
|
|
|
data={"message": "test query"},
|
|
|
|
|
)
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
# Should contain error since GROK_ENABLED is false in test mode
|
2026-03-08 12:50:44 -04:00
|
|
|
assert (
|
|
|
|
|
"not available" in response.text.lower()
|
|
|
|
|
or "error" in response.text.lower()
|
|
|
|
|
or "grok" in response.text.lower()
|
|
|
|
|
)
|