- Add default_temperature: 0 to config/team.yaml models block - Read self._default_temperature from models cfg in __init__ - Use self._default_temperature as fallback in complete() instead of hardcoded 0 - Update class docstring to document both default_max_tokens and default_temperature - Update complete() context param docs to reference team.yaml keys
131 lines
4.5 KiB
Python
131 lines
4.5 KiB
Python
"""
|
|
adapters/llm/anthropic.py
|
|
Anthropic Claude LLM adapter — Phase 2 implementation.
|
|
|
|
Uses the ``anthropic`` SDK to call Claude models. Model selection is driven
|
|
by the capability_map in team.yaml so the adapter stays provider-agnostic in
|
|
configuration.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
import anthropic
|
|
|
|
from adapters.base.llm import LLMAdapter
|
|
|
|
|
|
class AnthropicAdapter(LLMAdapter):
|
|
"""
|
|
LLM adapter for Anthropic Claude models.
|
|
|
|
Reads model configuration from the loaded team.yaml config dict::
|
|
|
|
models:
|
|
provider: anthropic
|
|
default_max_tokens: 4096 # fallback max_tokens for all calls
|
|
default_temperature: 0 # fallback temperature for all calls
|
|
capability_map:
|
|
reasoning-heavy:
|
|
anthropic: claude-opus-4-6
|
|
capable:
|
|
anthropic: claude-sonnet-4-6
|
|
fast-cheap:
|
|
anthropic: claude-haiku-3-5
|
|
|
|
Both ``default_max_tokens`` and ``default_temperature`` can be overridden
|
|
per-call via the ``context`` dict passed to :meth:`complete`.
|
|
|
|
Environment variables
|
|
---------------------
|
|
ANTHROPIC_API_KEY : Required. Authenticates with the Anthropic API.
|
|
"""
|
|
|
|
def __init__(self, config: dict) -> None:
|
|
"""
|
|
Initialise the Anthropic adapter.
|
|
|
|
Parameters
|
|
----------
|
|
config : Loaded team.yaml config dict.
|
|
|
|
Raises
|
|
------
|
|
ValueError
|
|
If ANTHROPIC_API_KEY is not set in the environment.
|
|
"""
|
|
self._config = config
|
|
api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
if not api_key:
|
|
raise ValueError(
|
|
"ANTHROPIC_API_KEY environment variable is not set. "
|
|
"Export it before running the-agency."
|
|
)
|
|
self._client = anthropic.Anthropic(api_key=api_key)
|
|
self._models_cfg: dict = config.get("models", {})
|
|
self._default_max_tokens: int = self._models_cfg.get("default_max_tokens", 4096)
|
|
self._default_temperature: float = self._models_cfg.get("default_temperature", 0)
|
|
|
|
def complete(self, prompt: str, capability: str, context: dict) -> str:
|
|
"""
|
|
Send a prompt to a Claude model and return the text response.
|
|
|
|
Parameters
|
|
----------
|
|
prompt : User-role prompt content.
|
|
capability : One of "reasoning-heavy" | "capable" | "fast-cheap".
|
|
context : Optional per-call overrides:
|
|
system_prompt (str) — prepended as the system turn.
|
|
max_tokens (int) — defaults to models.default_max_tokens in team.yaml.
|
|
temperature (float) — defaults to models.default_temperature in team.yaml.
|
|
|
|
Returns
|
|
-------
|
|
The model's text completion as a plain string.
|
|
"""
|
|
model = self.resolve_model(capability)
|
|
max_tokens: int = context.get("max_tokens", self._default_max_tokens)
|
|
temperature: float = context.get("temperature", self._default_temperature)
|
|
system_prompt: str = context.get("system_prompt", "")
|
|
|
|
create_kwargs: dict = {
|
|
"model": model,
|
|
"max_tokens": max_tokens,
|
|
"messages": [{"role": "user", "content": prompt}],
|
|
}
|
|
if system_prompt:
|
|
create_kwargs["system"] = system_prompt
|
|
if temperature != 0.0:
|
|
create_kwargs["temperature"] = temperature
|
|
|
|
response = self._client.messages.create(**create_kwargs)
|
|
return response.content[0].text
|
|
|
|
def resolve_model(self, capability: str) -> str:
|
|
"""
|
|
Map a capability string to the Anthropic model identifier.
|
|
|
|
Looks up ``config.models.capability_map[capability][provider]``.
|
|
Falls back to the "capable" tier model if the capability is unknown.
|
|
|
|
Parameters
|
|
----------
|
|
capability : One of "reasoning-heavy" | "capable" | "fast-cheap".
|
|
|
|
Returns
|
|
-------
|
|
Anthropic model identifier (e.g. "claude-opus-4-6").
|
|
"""
|
|
provider: str = self._models_cfg.get("provider", "anthropic")
|
|
cap_map: dict = self._models_cfg.get("capability_map", {})
|
|
|
|
if capability in cap_map and provider in cap_map[capability]:
|
|
return cap_map[capability][provider]
|
|
|
|
# Fall back to "capable" tier
|
|
if "capable" in cap_map and provider in cap_map["capable"]:
|
|
return cap_map["capable"][provider]
|
|
|
|
# Hard-coded last resort
|
|
return "claude-sonnet-4-6"
|