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

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

  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