feat(grok): first-class xAI/Grok routing mode (UI + backend)

The Routing-mode toggle had Anthropic / Ollama / Self-Hosted / Mix but no way
to route the whole org to Grok. Add it end to end:

- backend: apply_mode("grok") + _apply_grok (GLOBAL default -> grok-build-0.1) +
  derive_mode "grok" detection; ApplyModeRequest/ModeResponse accept "grok".
- panel: a "Grok" routing-mode card (between Anthropic and Ollama, gated on the
  xAI key) + flipToGrok; a Grok group in the per-agent mix dropdown +
  catalogGrokOnly + a grok ProviderBadge variant; the mix-save key check and
  the AI-routing description now cover Grok.
- tests: integration derive_mode/apply_mode "grok" cases (+ grok provider row
  in the fixture).

Gated: ruff + mypy clean; panel typecheck + lint clean.
This commit is contained in:
Renn F
2026-06-18 10:13:14 +02:00
parent b0857915a1
commit 0e9bbc15db
5 changed files with 130 additions and 12 deletions
+27 -1
View File
@@ -37,13 +37,19 @@ async def llm_setup(
type=ModelProvider.ANTHROPIC,
enabled=True,
)
grok = ProviderConfigTable(
name="grok-test",
type=ModelProvider.GROK,
enabled=True,
base_url="https://api.x.ai/v1",
)
ollama = ProviderConfigTable(
name="ollama-test",
type=ModelProvider.OLLAMA_CLOUD,
enabled=True,
base_url="https://ollama.example.com",
)
db_session.add_all([anthropic, ollama])
db_session.add_all([anthropic, grok, ollama])
await db_session.flush()
yield {"svc": ModelRoutingService(db_session)}
@@ -177,6 +183,16 @@ async def test_derive_mode_ollama_when_only_ollama_global(llm_setup: dict) -> No
assert await svc.derive_mode() == "ollama"
@pytest.mark.asyncio
async def test_derive_mode_grok_when_only_grok_global(llm_setup: dict) -> None:
svc = llm_setup["svc"]
grok_model = _first_model_for_type(ModelProvider.GROK)
await svc.upsert_assignment(
scope=AssignmentScope.GLOBAL, scope_value=None, model_name=grok_model
)
assert await svc.derive_mode() == "grok"
@pytest.mark.asyncio
async def test_derive_mode_mix_with_per_agent(llm_setup: dict) -> None:
svc = llm_setup["svc"]
@@ -215,6 +231,16 @@ async def test_apply_mode_ollama_sets_global(llm_setup: dict) -> None:
assert assignments[0].scope == AssignmentScope.GLOBAL
@pytest.mark.asyncio
async def test_apply_mode_grok_sets_global(llm_setup: dict) -> None:
svc = llm_setup["svc"]
await svc.apply_mode(mode="grok")
assignments = await svc.list_assignments()
assert len(assignments) == 1
assert assignments[0].scope == AssignmentScope.GLOBAL
assert assignments[0].provider.type == ModelProvider.GROK
@pytest.mark.asyncio
async def test_apply_mode_mix_requires_per_agent(llm_setup: dict) -> None:
svc = llm_setup["svc"]