Files
llm-connect/contracts/functional/adapters.md
Bernd Worsch d71f4114d1 feat: WP-0001 foundation + WP-0002 core extensions
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>
2026-04-01 22:24:14 +00:00

95 lines
3.0 KiB
Markdown

# 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`.