from __future__ import annotations
import os
from typing import Protocol
class ChatProvider(Protocol):
def complete(self, *, system: str, messages: list[dict[str, str]]) -> str: ...
class AnthropicChat:
def __init__(
self,
*,
api_key: str | None = None,
model: str = "claude-haiku-4-5",
max_tokens: int = 512,
) -> None:
try:
from anthropic import Anthropic except ImportError as e: raise RuntimeError(
"install the 'anthropic' extra to use AnthropicChat: "
"`pip install 'sqlrite-agent[anthropic]'`"
) from e
self.model = model
self.max_tokens = max_tokens
self._client = Anthropic(api_key=api_key or os.environ.get("ANTHROPIC_API_KEY"))
def complete(self, *, system: str, messages: list[dict[str, str]]) -> str:
resp = self._client.messages.create(
model=self.model,
max_tokens=self.max_tokens,
system=system,
messages=messages,
)
out: list[str] = []
for block in resp.content:
text = getattr(block, "text", None)
if text:
out.append(text)
return "".join(out).strip()
class EchoChat:
def complete(self, *, system: str, messages: list[dict[str, str]]) -> str:
last_user = next(
(m["content"] for m in reversed(messages) if m.get("role") == "user"),
"",
)
return (
"[echo agent — no LLM configured; set ANTHROPIC_API_KEY for real replies]\n"
f"I heard: {last_user!r}\n"
"(The system prompt recalled context above this line — that's the part "
"this example is showing off. The reply itself is canned.)"
)
def build_chat(name: str | None) -> ChatProvider:
if not name or name == "auto":
name = "anthropic" if os.environ.get("ANTHROPIC_API_KEY") else "echo"
name = name.lower()
if name == "anthropic":
return AnthropicChat()
if name == "echo":
return EchoChat()
raise ValueError(f"unknown chat provider: {name!r}")