feat(claude-cli): add local Claude Code CLI provider bridge

Spawn the local `claude` binary as a subprocess and expose it as an
Anthropic Messages-compatible provider. Hosted in brightstaff
(`CLAUDE_CLI_LISTEN_ADDR`), with session reuse, idle TTL, and watchdog.

User-facing surface is `model_providers: [{ model: claude-cli/* }]` —
the Python CLI auto-fills name/provider_interface/base_url/access_key
and the launcher (native + supervisord) enables the bridge listener
only when at least one claude-cli provider is present.
This commit is contained in:
Spherrrical 2026-05-04 12:57:53 -07:00
parent b71a555f19
commit 9fdfeb7cbf
26 changed files with 2847 additions and 2 deletions

View file

@ -3,8 +3,11 @@ import pytest
import yaml
from unittest import mock
from planoai.config_generator import (
validate_and_render_schema,
CLAUDE_CLI_DEFAULT_BASE_URL,
_apply_claude_cli_autofill,
_is_claude_cli_provider,
migrate_inline_routing_preferences,
validate_and_render_schema,
)
@ -738,3 +741,64 @@ model_providers:
migrate_inline_routing_preferences(config_yaml)
assert config_yaml["version"] == "v0.5.0"
def test_claude_cli_autofill_wildcard_provider():
provider = {"model": "claude-cli/*"}
assert _is_claude_cli_provider(provider) is True
assert _apply_claude_cli_autofill(provider) is True
assert provider["name"] == "claude-cli/*"
assert provider["provider_interface"] == "claude-cli"
assert provider["base_url"] == CLAUDE_CLI_DEFAULT_BASE_URL
assert provider["access_key"] == "claude-cli-local"
# `model` itself must not be rewritten — the wildcard expansion happens
# downstream and we want to preserve the user's intent.
assert provider["model"] == "claude-cli/*"
def test_claude_cli_autofill_specific_model():
provider = {"model": "claude-cli/sonnet", "default": True}
assert _apply_claude_cli_autofill(provider) is True
assert provider["name"] == "claude-cli/sonnet"
assert provider["provider_interface"] == "claude-cli"
assert provider["base_url"] == CLAUDE_CLI_DEFAULT_BASE_URL
# Existing fields like `default` survive.
assert provider["default"] is True
def test_claude_cli_autofill_does_not_override_user_fields():
provider = {
"model": "claude-cli/*",
"name": "custom-name",
"base_url": "http://192.0.2.10:9000",
"access_key": "do-not-touch",
}
assert _apply_claude_cli_autofill(provider) is True
assert provider["name"] == "custom-name"
assert provider["base_url"] == "http://192.0.2.10:9000"
assert provider["access_key"] == "do-not-touch"
# provider_interface still gets injected because it was missing.
assert provider["provider_interface"] == "claude-cli"
def test_claude_cli_autofill_skips_non_matching_providers():
provider = {"model": "openai/gpt-4o"}
assert _is_claude_cli_provider(provider) is False
assert _apply_claude_cli_autofill(provider) is False
assert "provider_interface" not in provider
def test_claude_cli_autofill_passthrough_auth_skips_access_key():
provider = {"model": "claude-cli/*", "passthrough_auth": True}
assert _apply_claude_cli_autofill(provider) is True
# Honor passthrough_auth: do not inject a placeholder access_key.
assert "access_key" not in provider
assert provider["passthrough_auth"] is True
def test_claude_cli_autofill_detects_via_provider_interface_only():
provider = {"model": "sonnet", "provider_interface": "claude-cli"}
assert _is_claude_cli_provider(provider) is True
assert _apply_claude_cli_autofill(provider) is True
assert provider["base_url"] == CLAUDE_CLI_DEFAULT_BASE_URL
assert provider["name"] == "sonnet"

View file

@ -0,0 +1,112 @@
"""Unit tests for the claude-cli env wiring in native_runner.py."""
import os
import textwrap
from planoai.native_runner import (
CLAUDE_CLI_DEFAULT_LISTEN_ADDR,
_apply_claude_cli_env,
_needs_claude_cli_runtime,
)
def _write(path, body):
path.write_text(textwrap.dedent(body).lstrip())
return str(path)
def test_needs_claude_cli_runtime_detects_provider(tmp_path):
rendered = _write(
tmp_path / "rendered.yaml",
"""
version: v0.4.0
listeners: []
model_providers:
- name: claude-cli/*
model: '*'
provider_interface: claude-cli
base_url: http://127.0.0.1:14001
""",
)
assert _needs_claude_cli_runtime(rendered) is True
def test_needs_claude_cli_runtime_skips_other_providers(tmp_path):
rendered = _write(
tmp_path / "rendered.yaml",
"""
version: v0.4.0
model_providers:
- name: openai/gpt-4o
model: gpt-4o
provider_interface: openai
""",
)
assert _needs_claude_cli_runtime(rendered) is False
def test_needs_claude_cli_runtime_handles_missing_file(tmp_path):
assert _needs_claude_cli_runtime(str(tmp_path / "does-not-exist.yaml")) is False
def test_apply_claude_cli_env_injects_default_addr(tmp_path, monkeypatch):
rendered = _write(
tmp_path / "rendered.yaml",
"""
model_providers:
- provider_interface: claude-cli
model: '*'
""",
)
monkeypatch.delenv("CLAUDE_CLI_LISTEN_ADDR", raising=False)
monkeypatch.delenv("CLAUDE_CLI_BIN", raising=False)
env = {}
assert _apply_claude_cli_env(env, rendered) is True
assert env["CLAUDE_CLI_LISTEN_ADDR"] == CLAUDE_CLI_DEFAULT_LISTEN_ADDR
def test_apply_claude_cli_env_honors_user_override(tmp_path, monkeypatch):
rendered = _write(
tmp_path / "rendered.yaml",
"""
model_providers:
- provider_interface: claude-cli
model: '*'
""",
)
monkeypatch.delenv("CLAUDE_CLI_LISTEN_ADDR", raising=False)
env = {"CLAUDE_CLI_LISTEN_ADDR": "127.0.0.1:25000"}
assert _apply_claude_cli_env(env, rendered) is True
assert env["CLAUDE_CLI_LISTEN_ADDR"] == "127.0.0.1:25000"
def test_apply_claude_cli_env_passes_through_user_env(tmp_path, monkeypatch):
rendered = _write(
tmp_path / "rendered.yaml",
"""
model_providers:
- provider_interface: claude-cli
model: '*'
""",
)
monkeypatch.delenv("CLAUDE_CLI_LISTEN_ADDR", raising=False)
monkeypatch.setenv("CLAUDE_CLI_BIN", "/usr/local/bin/claude-test")
monkeypatch.setenv("CLAUDE_CLI_PERMISSION_MODE", "default")
env = {}
assert _apply_claude_cli_env(env, rendered) is True
assert env["CLAUDE_CLI_BIN"] == "/usr/local/bin/claude-test"
assert env["CLAUDE_CLI_PERMISSION_MODE"] == "default"
def test_apply_claude_cli_env_noop_for_other_configs(tmp_path):
rendered = _write(
tmp_path / "rendered.yaml",
"""
model_providers:
- provider_interface: openai
model: gpt-4o
""",
)
env = {}
assert _apply_claude_cli_env(env, rendered) is False
assert "CLAUDE_CLI_LISTEN_ADDR" not in env