WP-0001 — Foundation & GAAF Baseline - SCOPE.md, ARCHITECTURE-LAYERS.md, contracts/ tree - .claude/rules/ stubs filled (architecture, stack, boundary) - 57 tests (pytest), pyproject.toml with ruff+mypy, CI workflow WP-0002 — Core Extensions (FR-4 + FR-3) - FR-4: BudgetTracker (thread-safe) + LLMBudgetExceededError + optional RunConfig.budget_tracker + enforcement in all adapters - FR-3: async_execute_prompt on LLMAdapter ABC (asyncio.to_thread fallback) + native asyncio.create_subprocess_exec in ClaudeCodeAdapter 81 tests passing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.0 KiB
Contract: Functional — Provider Adapters
Layer: Functional
Version: 0.1.0
Maturity: Beta (all adapters)
Last updated: 2026-04-01
Common adapter contract
All provider adapters implement LLMAdapter (see contracts/core/llm-adapter.md).
Additional shared guarantees:
- Constructors resolve API keys at instantiation and raise
LLMConfigurationErrorimmediately if no key is found (fail-fast). - HTTP-based adapters (
OpenAIAdapter,GeminiAdapter,OpenRouterAdapter) use_http.post_jsonand do not add runtime dependencies beyond stdlib. metadatain the returnedLLMResponsealways contains"provider"and"latency_seconds"keys.- HTTP adapters that retry (
OpenAIAdapter,OpenRouterAdapter) use exponential backoff:sleep(2 ** attempt)on 429 and 5xx.
OpenAIAdapter
Provider key: "openai"
Default model: gpt-4.1-mini
API: https://api.openai.com/v1/chat/completions
Auth: OPENAI_API_KEY env var or apikey-chatgpt.txt in project root
Retries: 3 (exponential backoff on 429 and 5xx)
GeminiAdapter
Provider key: "gemini"
Default model: gemini-2.5-flash
API: https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent
Auth: GEMINI_API_KEY env var or apikey-geminifree.txt in project root
Retries: 0 (no retry logic; rate-limit handling deferred)
Note: System prompt is simulated via a user/model turn pair (Gemini has no native system role).
OpenRouterAdapter
Provider key: "openrouter"
Default model: anthropic/claude-sonnet-4
API: https://openrouter.ai/api/v1/chat/completions (configurable via LLMConfig.api_base)
Auth: OPENROUTER_API_KEY env var or apikey-openrouter.txt in project root
Retries: 3 (exponential backoff on 429 and 5xx)
Note: OpenRouter is an OpenAI-compatible endpoint; RunConfig.model_params are merged into the payload.
ClaudeCodeAdapter
Provider key: "claude-code"
Default model: n/a (uses the CLI's configured default)
Auth: none (delegates to locally installed claude CLI)
Subprocess: claude --print [--model M] with prompt on stdin
Token counts: estimated via _token_estimator (not provider-reported)
validate_config: runs claude --version; returns False if CLI not found
EmbeddingAdapter ABC
llm_connect.embedding_adapter.EmbeddingAdapter
class EmbeddingAdapter(ABC):
@abstractmethod
def embed(self, texts: list[str]) -> list[list[float]]: ...
Invariant: returns a list of the same length as texts.
OpenAICompatibleEmbeddingAdapter
Compatible with any OpenAI-format embedding endpoint (/v1/embeddings).
Default model: text-embedding-3-small.
EmbeddingCache
llm_connect.embedding_cache.EmbeddingCache
Disk-backed cache keyed by text content (SHA-256 hash).
get_or_compute(text, compute_fn) returns cached vector or calls compute_fn.