Merge pull request 'fix(adapter): map persona-friendly slugs (claude-code, anthropic) to registry names' (#10) from fix/dispatch-alias-map-followup into main
This commit is contained in:
commit
4b038f2947
40
adapter.py
40
adapter.py
@ -278,6 +278,26 @@ def _load_providers(config_path: str) -> tuple:
|
|||||||
return tuple(parsed)
|
return tuple(parsed)
|
||||||
|
|
||||||
|
|
||||||
|
# Aliases for `MODEL_PROVIDER` env values that should map to a registry
|
||||||
|
# provider name. The persona env files use shorter / friendlier slugs
|
||||||
|
# than the registry's canonical names — without this alias map a value
|
||||||
|
# like ``MODEL_PROVIDER=claude-code`` would fall through to YAML-based
|
||||||
|
# resolution and (when the YAML doesn't pin a provider) hit the
|
||||||
|
# model-prefix matcher with the operator-picked MODEL, mis-routing a
|
||||||
|
# lead workspace through MiniMax even though its CLAUDE_CODE_OAUTH_TOKEN
|
||||||
|
# was clearly meant to be used.
|
||||||
|
#
|
||||||
|
# Maintain this list in sync with the persona env file convention:
|
||||||
|
# - ``claude-code`` → ``anthropic-oauth`` (Claude Code subscription path)
|
||||||
|
# - ``anthropic`` → ``anthropic-api`` (direct Anthropic API key)
|
||||||
|
# Provider names already in the registry alias to themselves implicitly
|
||||||
|
# (the ``in registry`` check catches them before this map is consulted).
|
||||||
|
_PROVIDER_SLUG_ALIASES = {
|
||||||
|
"claude-code": "anthropic-oauth",
|
||||||
|
"anthropic": "anthropic-api",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _resolve_model_and_provider_from_env(
|
def _resolve_model_and_provider_from_env(
|
||||||
yaml_model: str,
|
yaml_model: str,
|
||||||
yaml_provider: str,
|
yaml_provider: str,
|
||||||
@ -331,8 +351,20 @@ def _resolve_model_and_provider_from_env(
|
|||||||
# (provider name) vs. the legacy convention (model id). Persona-
|
# (provider name) vs. the legacy convention (model id). Persona-
|
||||||
# convention wins when the value matches a registered provider; we
|
# convention wins when the value matches a registered provider; we
|
||||||
# fall back to legacy interpretation only when it doesn't.
|
# fall back to legacy interpretation only when it doesn't.
|
||||||
|
#
|
||||||
|
# First, apply the alias map so persona-friendly slugs like
|
||||||
|
# ``claude-code`` resolve to the canonical registry name
|
||||||
|
# ``anthropic-oauth``. Without this, a lead workspace's
|
||||||
|
# ``MODEL_PROVIDER=claude-code`` env would fall through to the model-
|
||||||
|
# prefix matcher, see ``MODEL=MiniMax-M2.7`` and mis-route to MiniMax
|
||||||
|
# even though the operator's intent (and the OAuth token they set)
|
||||||
|
# was the OAuth subscription path.
|
||||||
|
env_provider_resolved = _PROVIDER_SLUG_ALIASES.get(
|
||||||
|
env_provider.lower(), env_provider,
|
||||||
|
) if env_provider else ""
|
||||||
env_provider_is_slug = (
|
env_provider_is_slug = (
|
||||||
bool(env_provider) and env_provider.lower() in provider_names_lower
|
bool(env_provider_resolved)
|
||||||
|
and env_provider_resolved.lower() in provider_names_lower
|
||||||
)
|
)
|
||||||
|
|
||||||
# Picked model resolution
|
# Picked model resolution
|
||||||
@ -345,10 +377,10 @@ def _resolve_model_and_provider_from_env(
|
|||||||
else:
|
else:
|
||||||
picked_model = yaml_model or ""
|
picked_model = yaml_model or ""
|
||||||
|
|
||||||
# Explicit provider resolution — env wins when it's a registered slug,
|
# Explicit provider resolution — env wins when it's a registered slug
|
||||||
# otherwise fall back to YAML.
|
# (after alias mapping), otherwise fall back to YAML.
|
||||||
if env_provider_is_slug:
|
if env_provider_is_slug:
|
||||||
explicit_provider = env_provider
|
explicit_provider = env_provider_resolved
|
||||||
else:
|
else:
|
||||||
explicit_provider = yaml_provider or None
|
explicit_provider = yaml_provider or None
|
||||||
|
|
||||||
|
|||||||
@ -73,10 +73,11 @@ def test_persona_env_minimax_resolves_correctly(monkeypatch):
|
|||||||
|
|
||||||
def test_persona_env_lead_claude_code_resolves_correctly(monkeypatch):
|
def test_persona_env_lead_claude_code_resolves_correctly(monkeypatch):
|
||||||
"""Lead persona env (MODEL=opus, MODEL_PROVIDER=claude-code) —
|
"""Lead persona env (MODEL=opus, MODEL_PROVIDER=claude-code) —
|
||||||
``claude-code`` isn't a registered provider name (registry uses
|
``claude-code`` is the persona-friendly alias for the canonical
|
||||||
``anthropic-oauth``), so it falls back to legacy interpretation
|
``anthropic-oauth`` registry name. Must resolve via the alias map
|
||||||
and yields no explicit provider, letting the model-based
|
so the lead boots through the OAuth subscription path even when
|
||||||
fall-through to providers[0]=anthropic-oauth do the right thing."""
|
MODEL is a non-Anthropic model id (e.g. an operator who picked
|
||||||
|
MiniMax in canvas but whose persona env still pins claude-code)."""
|
||||||
_clear_env(monkeypatch)
|
_clear_env(monkeypatch)
|
||||||
monkeypatch.setenv("MODEL", "opus")
|
monkeypatch.setenv("MODEL", "opus")
|
||||||
monkeypatch.setenv("MODEL_PROVIDER", "claude-code")
|
monkeypatch.setenv("MODEL_PROVIDER", "claude-code")
|
||||||
@ -84,10 +85,38 @@ def test_persona_env_lead_claude_code_resolves_correctly(monkeypatch):
|
|||||||
yaml_model="", yaml_provider="", providers=_REGISTRY,
|
yaml_model="", yaml_provider="", providers=_REGISTRY,
|
||||||
)
|
)
|
||||||
assert model == "opus"
|
assert model == "opus"
|
||||||
# claude-code is not a registered slug, so this falls back —
|
# claude-code → anthropic-oauth via the alias map
|
||||||
# provider is None and the caller will model-resolve to
|
assert provider == "anthropic-oauth"
|
||||||
# anthropic-oauth via the alias match on "opus".
|
|
||||||
assert provider is None
|
|
||||||
|
def test_persona_env_lead_with_minimax_model_routes_via_oauth(monkeypatch):
|
||||||
|
"""Lead workspace whose persona pins MODEL_PROVIDER=claude-code but
|
||||||
|
whose YAML/canvas selection happens to be a MiniMax model still
|
||||||
|
routes via OAuth — the persona's provider pin wins over the
|
||||||
|
model-prefix matcher. Without the alias map, the fall-through
|
||||||
|
mis-routed leads to MiniMax even when their CLAUDE_CODE_OAUTH_TOKEN
|
||||||
|
was set."""
|
||||||
|
_clear_env(monkeypatch)
|
||||||
|
monkeypatch.setenv("MODEL", "MiniMax-M2.7")
|
||||||
|
monkeypatch.setenv("MODEL_PROVIDER", "claude-code")
|
||||||
|
model, provider = _resolve_model_and_provider_from_env(
|
||||||
|
yaml_model="", yaml_provider="", providers=_REGISTRY,
|
||||||
|
)
|
||||||
|
assert model == "MiniMax-M2.7"
|
||||||
|
assert provider == "anthropic-oauth"
|
||||||
|
|
||||||
|
|
||||||
|
def test_anthropic_alias_resolves_to_anthropic_api(monkeypatch):
|
||||||
|
"""``MODEL_PROVIDER=anthropic`` alias → ``anthropic-api`` (direct
|
||||||
|
Anthropic API key path)."""
|
||||||
|
_clear_env(monkeypatch)
|
||||||
|
monkeypatch.setenv("MODEL", "claude-opus-4-7")
|
||||||
|
monkeypatch.setenv("MODEL_PROVIDER", "anthropic")
|
||||||
|
model, provider = _resolve_model_and_provider_from_env(
|
||||||
|
yaml_model="", yaml_provider="", providers=_REGISTRY,
|
||||||
|
)
|
||||||
|
assert model == "claude-opus-4-7"
|
||||||
|
assert provider == "anthropic-api"
|
||||||
|
|
||||||
|
|
||||||
def test_persona_env_glm_resolves_correctly(monkeypatch):
|
def test_persona_env_glm_resolves_correctly(monkeypatch):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user