Files
llm-connect/contracts/core/llm-adapter.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

123 lines
4.0 KiB
Markdown

# Contract: Core — LLMAdapter Interface
**Layer:** Core
**Version:** 0.1.0
**Status:** Draft (stabilises at v1.0.0)
**Last updated:** 2026-04-01
---
## LLMAdapter ABC
`llm_connect.adapter.LLMAdapter`
### Interface
```python
class LLMAdapter(ABC):
@abstractmethod
def execute_prompt(self, prompt: str, config: RunConfig) -> LLMResponse: ...
@abstractmethod
def validate_config(self, config: RunConfig) -> bool: ...
```
**Planned addition (WP-0002 T07):**
```python
async def async_execute_prompt(self, prompt: str, config: RunConfig) -> LLMResponse:
# Default: runs execute_prompt in a thread executor
...
```
### Invariants
1. `execute_prompt` MUST return an `LLMResponse` with a non-empty `content` field on success.
2. `execute_prompt` MUST raise a subclass of `LLMError` on any failure — never a bare exception.
3. `validate_config` MUST be side-effect-free and return `bool` only.
4. `validate_config` returning `False` does not preclude calling `execute_prompt` — it is advisory.
5. Adapters MUST NOT mutate the `config` argument.
6. `execute_prompt` is allowed to be slow (network I/O) but MUST respect `config.timeout_seconds`.
### Failure modes
| Condition | Exception |
|-----------|-----------|
| Missing / invalid API key | `LLMConfigurationError` |
| HTTP 4xx (non-429) | `LLMAPIError` (with `.status_code`) |
| HTTP 429 | `LLMRateLimitError` |
| Request timeout | `LLMTimeoutError` |
| CLI subprocess failure | `LLMSubprocessError` (with `.return_code`, `.stderr`) |
| Token budget exceeded (WP-0002) | `LLMBudgetExceededError` |
### Compatibility rules
- Any code that accepts `LLMAdapter` MUST work with `MockLLMAdapter`.
- Adding new optional methods to the ABC is non-breaking (default implementations provided).
- Removing or changing the signature of `execute_prompt` or `validate_config` is a **breaking Core change** requiring a major version bump.
---
## RunConfig
`llm_connect.models.RunConfig`
### Fields and invariants
| Field | Type | Default | Invariant |
|-------|------|---------|-----------|
| `model_name` | `str` | `"gpt-4"` | Non-empty string; adapters MAY override |
| `temperature` | `float` | `0.7` | 0.0 ≤ temperature ≤ 2.0 |
| `max_tokens` | `int` | `2000` | > 0 |
| `model_params` | `dict` | `{}` | Provider-specific pass-through; no invariants |
| `max_depth` | `int` | `3` | ≥ 0 |
| `skip_if_exists` | `bool` | `True` | — |
| `timeout_seconds` | `int` | `300` | > 0 |
| `budget_tracker` | `BudgetTracker \| None` | `None` | Optional; added in WP-0002 |
Adapters MUST NOT mutate `RunConfig` fields.
---
## LLMResponse
`llm_connect.models.LLMResponse`
### Fields and invariants
| Field | Type | Invariant |
|-------|------|-----------|
| `content` | `str` | Non-empty on success; may be empty only if provider returned empty output |
| `model` | `str` | Non-empty; the model actually used (may differ from `RunConfig.model_name`) |
| `usage` | `dict` | Keys: `prompt_tokens`, `completion_tokens`, `total_tokens` (all int ≥ 0) |
| `finish_reason` | `str` | Provider-reported; `"stop"` is the normal value |
| `metadata` | `dict` | Arbitrary; always includes `"provider"` key |
---
## LLMError Hierarchy
```
LLMError
├── LLMConfigurationError bad key / unknown provider
├── LLMAPIError HTTP error (has .status_code, .response_body)
│ └── LLMRateLimitError 429
├── LLMTimeoutError request or subprocess timed out
├── LLMSubprocessError CLI failed (has .return_code, .stderr)
└── LLMBudgetExceededError token budget cap exceeded (WP-0002)
```
All exceptions carry optional `cause` (chained exception) and `context` (dict).
---
## Mock adapters
`MockLLMAdapter` and `ErrorLLMAdapter` are part of Core — they are test
primitives that any consumer may depend on without importing dev extras.
`MockLLMAdapter` invariants:
- Returns deterministic response without network I/O
- Increments `call_count` on each call
- Records `last_prompt` and `last_config`
- `reset()` clears all counters and recorded state