feat(adapter/llm): implement AnthropicAdapter
Implements AnthropicAdapter using the anthropic SDK. - Reads ANTHROPIC_API_KEY from env; raises ValueError if missing - resolve_model() looks up capability_map in team.yaml config, falls back to "capable" tier then hard-coded claude-sonnet-4-6 - complete() supports system_prompt, max_tokens (default 4096), and temperature (default 0) via the context dict - Adds PyGithub to requirements.txt (needed by GitHubAdapter) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,17 @@
|
|||||||
"""
|
"""
|
||||||
adapters/llm/anthropic.py
|
adapters/llm/anthropic.py
|
||||||
Anthropic Claude adapter — Phase 2 stub.
|
Anthropic Claude LLM adapter — Phase 2 implementation.
|
||||||
|
|
||||||
TODO (Phase 2):
|
Uses the ``anthropic`` SDK to call Claude models. Model selection is driven
|
||||||
- Implement complete() using the anthropic SDK (anthropic.Anthropic client).
|
by the capability_map in team.yaml so the adapter stays provider-agnostic in
|
||||||
- Implement resolve_model() by reading config/team.yaml capability_map.
|
configuration.
|
||||||
- Handle streaming responses, rate-limit retries, and token counting.
|
|
||||||
- Support system-prompt injection via context["system_prompt"].
|
|
||||||
- Map capability → model using the provider's capability_map config.
|
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
import anthropic
|
||||||
|
|
||||||
from adapters.base.llm import LLMAdapter
|
from adapters.base.llm import LLMAdapter
|
||||||
|
|
||||||
|
|
||||||
@@ -18,27 +19,105 @@ class AnthropicAdapter(LLMAdapter):
|
|||||||
"""
|
"""
|
||||||
LLM adapter for Anthropic Claude models.
|
LLM adapter for Anthropic Claude models.
|
||||||
|
|
||||||
Reads model configuration from config/team.yaml:
|
Reads model configuration from the loaded team.yaml config dict::
|
||||||
models.provider: anthropic
|
|
||||||
models.capability_map.reasoning-heavy.anthropic: claude-opus-4-6
|
models:
|
||||||
models.capability_map.capable.anthropic: claude-sonnet-4-6
|
provider: anthropic
|
||||||
models.capability_map.fast-cheap.anthropic: claude-haiku-3-5
|
capability_map:
|
||||||
|
reasoning-heavy:
|
||||||
|
anthropic: claude-opus-4-6
|
||||||
|
capable:
|
||||||
|
anthropic: claude-sonnet-4-6
|
||||||
|
fast-cheap:
|
||||||
|
anthropic: claude-haiku-3-5
|
||||||
|
|
||||||
|
Environment variables
|
||||||
|
---------------------
|
||||||
|
ANTHROPIC_API_KEY : Required. Authenticates with the Anthropic API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config: dict) -> None:
|
def __init__(self, config: dict) -> None:
|
||||||
# TODO (Phase 2): Accept loaded team.yaml config dict.
|
"""
|
||||||
# Extract API key from environment (ANTHROPIC_API_KEY).
|
Initialise the Anthropic adapter.
|
||||||
# Initialise the anthropic.Anthropic() client.
|
|
||||||
raise NotImplementedError("AnthropicAdapter.__init__ is not yet implemented.")
|
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", {})
|
||||||
|
|
||||||
def complete(self, prompt: str, capability: str, context: dict) -> str:
|
def complete(self, prompt: str, capability: str, context: dict) -> str:
|
||||||
# TODO (Phase 2): Call anthropic client messages.create().
|
"""
|
||||||
# Use resolve_model(capability) to pick the model.
|
Send a prompt to a Claude model and return the text response.
|
||||||
# Support context keys: system_prompt, max_tokens, temperature.
|
|
||||||
# Return response text as a plain string.
|
Parameters
|
||||||
raise NotImplementedError("AnthropicAdapter.complete is not yet implemented.")
|
----------
|
||||||
|
prompt : User-role prompt content.
|
||||||
|
capability : One of "reasoning-heavy" | "capable" | "fast-cheap".
|
||||||
|
context : Optional overrides:
|
||||||
|
system_prompt (str) — prepended as the system turn.
|
||||||
|
max_tokens (int) — defaults to 4096.
|
||||||
|
temperature (float) — defaults to 0.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
The model's text completion as a plain string.
|
||||||
|
"""
|
||||||
|
model = self.resolve_model(capability)
|
||||||
|
max_tokens: int = context.get("max_tokens", 4096)
|
||||||
|
temperature: float = context.get("temperature", 0)
|
||||||
|
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:
|
||||||
|
create_kwargs["temperature"] = temperature
|
||||||
|
|
||||||
|
response = self._client.messages.create(**create_kwargs)
|
||||||
|
return response.content[0].text
|
||||||
|
|
||||||
def resolve_model(self, capability: str) -> str:
|
def resolve_model(self, capability: str) -> str:
|
||||||
# TODO (Phase 2): Look up capability in team.yaml capability_map.
|
"""
|
||||||
# Fall back to "capable" tier model if capability is unknown.
|
Map a capability string to the Anthropic model identifier.
|
||||||
raise NotImplementedError("AnthropicAdapter.resolve_model is not yet implemented.")
|
|
||||||
|
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"
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ pyyaml
|
|||||||
# Environment variable management
|
# Environment variable management
|
||||||
python-dotenv
|
python-dotenv
|
||||||
|
|
||||||
|
# GitHub VCS adapter
|
||||||
|
PyGithub
|
||||||
|
|
||||||
# --- stdlib-only (no pip install needed) ---
|
# --- stdlib-only (no pip install needed) ---
|
||||||
# sqlite3 — blackboard persistence
|
# sqlite3 — blackboard persistence
|
||||||
# dataclasses — task_brief schema
|
# dataclasses — task_brief schema
|
||||||
|
|||||||
Reference in New Issue
Block a user