# 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 `LLMConfigurationError` immediately if no key is found (fail-fast). - HTTP-based adapters (`OpenAIAdapter`, `GeminiAdapter`, `OpenRouterAdapter`) use `_http.post_json` and do not add runtime dependencies beyond stdlib. - `metadata` in the returned `LLMResponse` always 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` ```python 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`.