mirror of
https://github.com/katanemo/plano.git
synced 2026-06-14 15:15:15 +02:00
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:
parent
b71a555f19
commit
9fdfeb7cbf
26 changed files with 2847 additions and 2 deletions
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue