mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
feat(providers): native Grok runtime — opencode image, config gen, panel key
Complete the native Grok (xAI) path so grok-build-0.1 runs as a real RoboCo agent, not just the provider seam. - roboco-agent-grok image (docker/agent-grok.Dockerfile): FROM agent-base + opencode (the OpenAI-protocol runtime). One image serves every role; role behaviour comes from the mounted manifest / mcp-config / system prompt, exactly as on the Claude path. - Entrypoint renders opencode.json at spawn from the GrokProvider env contract + the mounted Claude Code mcp-config.json (roboco.llm.providers.opencode_config): translates RoboCo's gateway servers (roboco-flow / roboco-do / ...) into opencode's mcp block, declares the xAI OpenAI-compatible provider + model, and wires permissions + instructions. Pure, unit-tested translation. - Orchestrator registers GrokProvider with the registry-qualified image (_qualify_agent_image) so it resolves in local and registry deploys. - Compose (both files + the registry compose) gain an agent-grok-image builder service. - Panel: a Grok (xAI) API key card on the AI Providers page, plus the grok ModelProvider value. KNOWN PARITY GAP (opencode runtime): the bash-guard PAT-scrub and the transcript-based usage/cost capture are Claude Code hooks and do not transfer to opencode. bash permission is operator-tunable (ROBOCO_GROK_BASH_PERMISSION) so a deployment can fail closed until a security/usage-parity opencode plugin lands. That plugin and live E2E validation are the remaining work to finalize with xAI.
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
"""Tests for the Grok opencode.json generator (RoboCo MCP -> opencode config)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from roboco.llm.providers.opencode_config import (
|
||||
XaiTarget,
|
||||
build_opencode_config,
|
||||
translate_mcp_servers,
|
||||
)
|
||||
|
||||
_TARGET = XaiTarget(
|
||||
base_url="https://api.x.ai/v1", api_key="xai-key", model="grok-build-0.1"
|
||||
)
|
||||
|
||||
_MCP = {
|
||||
"mcpServers": {
|
||||
"roboco-flow": {
|
||||
"command": "uv",
|
||||
"args": ["run", "--no-sync", "python", "-m", "roboco.mcp.flow_server"],
|
||||
"env": {
|
||||
"ROBOCO_AGENT_ID": "uuid-1",
|
||||
"UV_PROJECT_ENVIRONMENT": "/app/.venv",
|
||||
},
|
||||
},
|
||||
"roboco-do": {
|
||||
"command": "uv",
|
||||
"args": ["run", "--no-sync", "python", "-m", "roboco.mcp.do_server"],
|
||||
"env": {"ROBOCO_AGENT_ID": "uuid-1"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def test_translate_mcp_servers_shape() -> None:
|
||||
out = translate_mcp_servers(_MCP)
|
||||
flow = out["roboco-flow"]
|
||||
assert flow["type"] == "local"
|
||||
assert flow["enabled"] is True
|
||||
# command + args collapse into a single command array (opencode shape).
|
||||
assert flow["command"] == [
|
||||
"uv",
|
||||
"run",
|
||||
"--no-sync",
|
||||
"python",
|
||||
"-m",
|
||||
"roboco.mcp.flow_server",
|
||||
]
|
||||
# env -> environment (opencode key).
|
||||
assert flow["environment"]["ROBOCO_AGENT_ID"] == "uuid-1"
|
||||
assert "env" not in flow
|
||||
assert set(out) == {"roboco-flow", "roboco-do"}
|
||||
|
||||
|
||||
def test_translate_mcp_servers_empty() -> None:
|
||||
assert translate_mcp_servers({}) == {}
|
||||
assert translate_mcp_servers({"mcpServers": {}}) == {}
|
||||
|
||||
|
||||
def test_translate_mcp_servers_omits_environment_when_no_env() -> None:
|
||||
out = translate_mcp_servers(
|
||||
{"mcpServers": {"x": {"command": "uv", "args": ["run"]}}}
|
||||
)
|
||||
assert "environment" not in out["x"]
|
||||
assert out["x"]["command"] == ["uv", "run"]
|
||||
|
||||
|
||||
def test_build_opencode_config_provider_and_model() -> None:
|
||||
cfg = build_opencode_config(
|
||||
_MCP,
|
||||
_TARGET,
|
||||
instruction_paths=["/app/system-prompt.md"],
|
||||
)
|
||||
provider = cfg["provider"]["xai"]
|
||||
assert provider["npm"] == "@ai-sdk/openai-compatible"
|
||||
assert provider["options"]["baseURL"] == "https://api.x.ai/v1"
|
||||
assert provider["options"]["apiKey"] == "xai-key"
|
||||
assert "grok-build-0.1" in provider["models"]
|
||||
# Top-level model selector is "<provider>/<model>".
|
||||
assert cfg["model"] == "xai/grok-build-0.1"
|
||||
# Gateway servers carried through.
|
||||
assert "roboco-flow" in cfg["mcp"]
|
||||
assert cfg["instructions"] == ["/app/system-prompt.md"]
|
||||
|
||||
|
||||
def test_build_opencode_config_bash_permission_is_tunable() -> None:
|
||||
cfg = build_opencode_config(
|
||||
{},
|
||||
_TARGET,
|
||||
instruction_paths=[],
|
||||
bash_permission="deny",
|
||||
)
|
||||
assert cfg["permission"]["bash"] == "deny"
|
||||
assert cfg["permission"]["edit"] == "allow"
|
||||
Reference in New Issue
Block a user