mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
127 lines
4.6 KiB
Python
127 lines
4.6 KiB
Python
"""Interactive intake/secretary builders fork a GROK route onto the grok CLI.
|
|||
|
|
|
||
|
|
A GROK route swaps the Claude SDK-driver image for the grok-CLI prompter/secretary
|
||
|
|
image and the ANTHROPIC_* env for the subscription auth mount + the per-agent
|
||
|
|
usage mount (no metered xAI key, no permission env — the driver computes the grok
|
||
|
|
permission flags). Every other provider keeps the Claude path's ANTHROPIC_*.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from typing import TYPE_CHECKING
|
||
|
|
|
||
|
|
from roboco.llm.providers import grok as grok_provider
|
||
|
|
from roboco.runtime.orchestrator import (
|
||
|
|
GROK_PROMPTER_IMAGE,
|
||
|
|
GROK_SECRETARY_IMAGE,
|
||
|
|
AgentOrchestrator,
|
||
|
|
_IntakeRunSpec,
|
||
|
|
_SecretaryRunSpec,
|
||
|
|
)
|
||
|
|
|
||
|
|
if TYPE_CHECKING:
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
_HOSTS: dict[str, str | None] = {
|
||
|
|
"claude": "/h/.claude",
|
||
|
|
"prompt": "/h/p.md",
|
||
|
|
"workspaces": "/h/ws",
|
||
|
|
"grok_usage": "/h/gu/intake-1",
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _intake_spec(
|
||
|
|
provider_type: str, *, base_url: str | None, token: str | None
|
||
|
|
) -> _IntakeRunSpec:
|
||
|
|
return _IntakeRunSpec(
|
||
|
|
container_name="roboco-agent-intake-1",
|
||
|
|
image=GROK_PROMPTER_IMAGE
|
||
|
|
if provider_type == "grok"
|
||
|
|
else "roboco-agent-prompter",
|
||
|
|
hosts=_HOSTS,
|
||
|
|
session_id="sess-1",
|
||
|
|
cwd="/data/workspace",
|
||
|
|
cli_model="grok-build",
|
||
|
|
api_url="http://roboco-orchestrator:8000",
|
||
|
|
provider_base_url=base_url,
|
||
|
|
provider_auth_token=token,
|
||
|
|
provider_type=provider_type,
|
||
|
|
model="grok-build",
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def test_intake_grok_uses_grok_cli_usage_mount_and_env() -> None:
|
||
|
|
cmd = AgentOrchestrator._build_intake_run_cmd(
|
||
|
|
_intake_spec("grok", base_url="https://api.x.ai/v1", token="xai-key")
|
||
|
|
)
|
||
|
|
# The per-agent usage dir is mounted so finalize reads usage.json back.
|
||
|
|
assert "/h/gu/intake-1:/home/agent/.grok-usage" in cmd
|
||
|
|
assert "ROBOCO_AGENT_MODEL=grok-build" in cmd
|
||
|
|
assert "ROBOCO_GROK_USAGE_FILE=/home/agent/.grok-usage/usage.json" in cmd
|
||
|
|
assert cmd[-1] == GROK_PROMPTER_IMAGE
|
||
|
|
# No metered xAI key, no Anthropic mislabelling, no stale opencode contract.
|
||
|
|
assert not any(c.startswith("XAI_") for c in cmd)
|
||
|
|
assert not any(c.startswith("ANTHROPIC_") for c in cmd)
|
||
|
|
assert not any(c.startswith("ROBOCO_GROK_VARIANT") for c in cmd)
|
||
|
|
assert not any(c.startswith("ROBOCO_GROK_EDIT_PERMISSION") for c in cmd)
|
||
|
|
assert "/home/agent/.local/share/opencode" not in " ".join(cmd)
|
||
|
|
|
||
|
|
|
||
|
|
def test_intake_grok_mounts_subscription_auth_when_present(
|
||
|
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||
|
|
) -> None:
|
||
|
|
# The auth mount is .exists()-guarded; point the host dir at a tmp ~/.grok
|
||
|
|
# holding an auth.json so the mount is emitted.
|
||
|
|
grok_dir = tmp_path / ".grok"
|
||
|
|
grok_dir.mkdir()
|
||
|
|
(grok_dir / "auth.json").write_text("{}", encoding="utf-8")
|
||
|
|
monkeypatch.setattr(grok_provider, "GROK_AUTH_HOST_PATH", str(grok_dir))
|
||
|
|
|
||
|
|
cmd = AgentOrchestrator._build_intake_run_cmd(
|
||
|
|
_intake_spec("grok", base_url="https://api.x.ai/v1", token="xai-key")
|
||
|
|
)
|
||
|
|
assert f"{grok_dir / 'auth.json'}:/home/agent/.grok/auth.json:ro" in cmd
|
||
|
|
|
||
|
|
|
||
|
|
def test_intake_anthropic_keeps_anthropic_env() -> None:
|
||
|
|
cmd = AgentOrchestrator._build_intake_run_cmd(
|
||
|
|
_intake_spec("anthropic", base_url="https://api.anthropic.com", token="sk-ant")
|
||
|
|
)
|
||
|
|
assert "ANTHROPIC_BASE_URL=https://api.anthropic.com" in cmd
|
||
|
|
assert "ANTHROPIC_AUTH_TOKEN=sk-ant" in cmd
|
||
|
|
assert not any(c.startswith("XAI_") for c in cmd)
|
||
|
|
assert not any(c.startswith("ROBOCO_GROK_USAGE_FILE") for c in cmd)
|
||
|
|
assert cmd[-1] == "roboco-agent-prompter"
|
||
|
|
|
||
|
|
|
||
|
|
def test_secretary_grok_uses_grok_cli_env_and_keeps_hmac() -> None:
|
||
|
|
spec = _SecretaryRunSpec(
|
||
|
|
container_name="roboco-agent-secretary-1",
|
||
|
|
image=GROK_SECRETARY_IMAGE,
|
||
|
|
hosts={
|
||
|
|
"claude": "/h/.claude",
|
||
|
|
"prompt": "/h/p.md",
|
||
|
|
"grok_usage": "/h/gu/sec-1",
|
||
|
|
},
|
||
|
|
session_id="sess-2",
|
||
|
|
cwd="/app",
|
||
|
|
cli_model="grok-build",
|
||
|
|
api_url="http://roboco-orchestrator:8000",
|
||
|
|
agent_uuid="uuid-sec",
|
||
|
|
agent_token="hmac-secretary",
|
||
|
|
provider_base_url="https://api.x.ai/v1",
|
||
|
|
provider_auth_token="xai-key",
|
||
|
|
provider_type="grok",
|
||
|
|
model="grok-build",
|
||
|
|
)
|
||
|
|
cmd = AgentOrchestrator._build_secretary_run_cmd(spec)
|
||
|
|
assert "/h/gu/sec-1:/home/agent/.grok-usage" in cmd
|
||
|
|
assert "ROBOCO_AGENT_MODEL=grok-build" in cmd
|
||
|
|
# The HMAC identity the directive tools authenticate with survives.
|
||
|
|
assert "ROBOCO_AGENT_TOKEN=hmac-secretary" in cmd
|
||
|
|
assert cmd[-1] == GROK_SECRETARY_IMAGE
|
||
|
|
assert not any(c.startswith("XAI_") for c in cmd)
|
||
|
|
assert not any(c.startswith("ANTHROPIC_") for c in cmd)
|