generated from coulomb/repo-seed
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>
4.0 KiB
4.0 KiB
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
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):
async def async_execute_prompt(self, prompt: str, config: RunConfig) -> LLMResponse:
# Default: runs execute_prompt in a thread executor
...
Invariants
execute_promptMUST return anLLMResponsewith a non-emptycontentfield on success.execute_promptMUST raise a subclass ofLLMErroron any failure — never a bare exception.validate_configMUST be side-effect-free and returnboolonly.validate_configreturningFalsedoes not preclude callingexecute_prompt— it is advisory.- Adapters MUST NOT mutate the
configargument. execute_promptis allowed to be slow (network I/O) but MUST respectconfig.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
LLMAdapterMUST work withMockLLMAdapter. - Adding new optional methods to the ABC is non-breaking (default implementations provided).
- Removing or changing the signature of
execute_promptorvalidate_configis 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_counton each call - Records
last_promptandlast_config reset()clears all counters and recorded state