Files
llm-connect/contracts/config/toml-chain.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

2.4 KiB

Contract: Configuration — TOML Config Chain

Layer: Configuration
Version: 0.1.0
Last updated: 2026-04-01


resolve_llm()

llm_connect.toml_config.resolve_llm(cli_provider, cli_model, app_name)

Walks a 7-level priority chain to resolve provider and model independently. Returns ResolvedLLM(provider, model, provider_source, model_source).

Priority chain (highest → lowest)

Level Source
1 CLI flags (cli_provider, cli_model)
2 Env var {APP_NAME}_HELPER_MODEL (model only)
3 User preference — ~/.config/{app_name}/config.toml [llm.preference]
4 Directory preference — .{app_name}.toml [llm.preference]
5 Directory default — .{app_name}.toml [llm.default]
6 User default — ~/.config/{app_name}/config.toml [llm.default]
7 Hardcoded fallback — gemini / gemini-2.5-flash

Invariants

  • Always returns a fully-resolved ResolvedLLM (never raises, never returns None).
  • Provider and model are resolved independently — a preference for model does not imply a preference for provider.
  • TOML parse errors are silently ignored (returns empty layer).
  • app_name defaults to "markitect" for backward compatibility; consumers should pass their own app name.

Known issue

toml_config.py has markitect-specific defaults (MARKITECT_HELPER_MODEL, USER_CONFIG_DIR). These are kept for backward compatibility but callers outside markitect should always pass an explicit app_name.


resolve_api_key()

llm_connect.config.resolve_api_key(explicit, env_var, key_file_paths)

Resolution order:

  1. explicit argument
  2. Environment variable env_var
  3. First readable file in key_file_paths with non-empty content

Returns None if nothing is found. Never raises.


find_project_root()

Walks up from CWD looking for pyproject.toml. Returns the containing directory or None. Used by adapters to locate key files.


LLMConfig

llm_connect.config.LLMConfig

Dataclass holding per-adapter configuration. Used directly by OpenRouterAdapter and ClaudeCodeAdapter. Not required by the Core LLMAdapter ABC.

Field Default
provider "openrouter"
model "anthropic/claude-sonnet-4"
api_key None
api_base "https://openrouter.ai/api/v1"
claude_cli_path "claude"
timeout_seconds 300
max_retries 3