Compare commits
2 Commits
b055c8d7bb
...
eaf4a955af
| Author | SHA1 | Date | |
|---|---|---|---|
| eaf4a955af | |||
| e9dc9a8517 |
34
CLAUDE.md
Normal file
34
CLAUDE.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Markitect — Claude Code Instructions
|
||||||
|
|
||||||
|
## Custodian State Hub Integration
|
||||||
|
|
||||||
|
This project is tracked as the **markitect** domain in the Custodian State Hub.
|
||||||
|
Hub topic ID: `5571d954-0d30-4950-980d-7bcaaad8e3e2`
|
||||||
|
|
||||||
|
### Session Protocol
|
||||||
|
|
||||||
|
**At the start of every session:**
|
||||||
|
Call `get_state_summary()` via the `state-hub` MCP tool to orient yourself.
|
||||||
|
If the hub is not reachable, start it: `cd ~/the-custodian/state-hub && make api`
|
||||||
|
|
||||||
|
**At the end of every session:**
|
||||||
|
Call `add_progress_event()` with at minimum:
|
||||||
|
- `topic_id`: `5571d954-0d30-4950-980d-7bcaaad8e3e2`
|
||||||
|
- `summary`: what was accomplished or left in-flight
|
||||||
|
- `event_type`: `note` for routine updates, `milestone` for completions, `blocker` for blockers
|
||||||
|
|
||||||
|
### Available State-Hub MCP Tools
|
||||||
|
|
||||||
|
- `get_state_summary()` — full cross-domain overview
|
||||||
|
- `add_progress_event(summary, topic_id, event_type, detail)` — log progress
|
||||||
|
- `create_workstream(topic_id, title, ...)` — create a new workstream
|
||||||
|
- `create_task(workstream_id, title, ...)` — create a task under a workstream
|
||||||
|
- `update_task_status(task_id, status)` — move task through lifecycle
|
||||||
|
- `record_decision(title, decision_type, topic_id, ...)` — log decisions
|
||||||
|
- `resolve_decision(decision_id, rationale, decided_by)` — close a decision
|
||||||
|
|
||||||
|
### If the MCP Server is Not Available
|
||||||
|
|
||||||
|
The state-hub MCP server (`state-hub`) is registered at user scope in `~/.claude.json`.
|
||||||
|
It requires the API to be running at `http://127.0.0.1:8000`.
|
||||||
|
Fallback: use `curl` directly against the REST API — see `/docs` at the hub URL.
|
||||||
214
roadmap/llm-shared-library/PLAN.md
Normal file
214
roadmap/llm-shared-library/PLAN.md
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
# LLM Adapter Layer — Extract as Shared Library
|
||||||
|
|
||||||
|
## Vision
|
||||||
|
|
||||||
|
The `markitect.llm` module is a clean, stdlib-only adapter layer for calling
|
||||||
|
LLMs via OpenRouter, Gemini, OpenAI, and the Claude Code CLI. It implements a
|
||||||
|
uniform interface, a 7-layer TOML config chain, embedding support with caching,
|
||||||
|
and typed exceptions. It should be usable by all projects in the Bernd Worsch
|
||||||
|
ecosystem without pulling in all of markitect.
|
||||||
|
|
||||||
|
This roadmap tracks extracting it into a standalone installable library.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
The module lives at `markitect/llm/` (~16 files, ~1500 LOC, stdlib-only) and
|
||||||
|
provides:
|
||||||
|
- **4 text adapters**: OpenRouter, Gemini, OpenAI, Claude Code CLI
|
||||||
|
- **2 embedding adapters**: OpenAI-compatible (OpenAI + OpenRouter)
|
||||||
|
- **Embedding cache**: JSON-backed, content-digest validated
|
||||||
|
- **Similarity utilities**: pure-Python cosine similarity, matrix, pair-finding
|
||||||
|
- **7-layer TOML config chain**: CLI > env > user/dir preference/default > hardcoded
|
||||||
|
- **Typed exceptions**: LLMError hierarchy
|
||||||
|
- **HTTP wrapper**: urllib-only, typed exception translation
|
||||||
|
|
||||||
|
### Two Coupling Issues Blocking Clean Extraction
|
||||||
|
|
||||||
|
| Issue | Location | Severity |
|
||||||
|
|-------|----------|----------|
|
||||||
|
| `RunConfig` and `LLMResponse` are defined in `markitect.prompts.execution.models`, not in `markitect.llm` | `markitect/prompts/execution/models.py` | High — creates cross-module import for all consumers |
|
||||||
|
| TOML config chain hardcodes `"markitect"` as app name (paths: `~/.config/markitect/`, env prefix `MARKITECT_`, files: `.markitect.toml`) | `markitect/llm/toml_config.py` | Medium — consumers either accept markitect config or can't use the chain |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Terminology
|
||||||
|
|
||||||
|
- **adapter**: concrete implementation of `LLMAdapter` for a single provider
|
||||||
|
- **factory**: `create_adapter()` / `create_embedding_adapter()` — provider-agnostic entry points
|
||||||
|
- **config chain**: 7-layer resolution of provider + model (CLI → env → TOML → hardcoded)
|
||||||
|
- **standalone library**: a Python package installable with `pip install` from a git URL or local path, without PyPI
|
||||||
|
- **consumer**: any project that imports and uses the library (markitect itself, custodian, railiance, etc.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Packaging Decision (Pending)
|
||||||
|
|
||||||
|
Before Phase 2 starts, one architectural decision must be resolved:
|
||||||
|
|
||||||
|
> **D1: Where does the extracted library live?**
|
||||||
|
>
|
||||||
|
> **Option A — Standalone repo** (`~/bw-llm` or similar):
|
||||||
|
> - Clean separation, versioned independently, installable via `pip install git+file:///...` or git URL
|
||||||
|
> - Adds a repo to maintain; changes require bumping version in dependents
|
||||||
|
>
|
||||||
|
> **Option B — Subfolder of markitect with own `pyproject.toml`** (monorepo-lite):
|
||||||
|
> - Stays co-located with the main codebase that will use it most
|
||||||
|
> - Less friction for iteration; single git history
|
||||||
|
> - Slightly unorthodox but valid for personal infrastructure
|
||||||
|
>
|
||||||
|
> **Option C — Just `pip install markitect` in other projects**:
|
||||||
|
> - Zero extraction work; reuse today
|
||||||
|
> - Pulls all of markitect (prompts, infospace, CLI, etc.) as transitive deps
|
||||||
|
> - Acceptable short-term if other projects are small
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stages
|
||||||
|
|
||||||
|
### Stage 1 — Decouple (within markitect)
|
||||||
|
|
||||||
|
Prepare the module for extraction without changing its public API.
|
||||||
|
|
||||||
|
#### S1.1 — Move RunConfig + LLMResponse into markitect.llm
|
||||||
|
|
||||||
|
`RunConfig` and `LLMResponse` are currently in `markitect.prompts.execution.models`.
|
||||||
|
The LLM adapters import from there, creating a hard dependency on the prompt system.
|
||||||
|
|
||||||
|
**Work:**
|
||||||
|
- Move both dataclasses to `markitect/llm/models.py`
|
||||||
|
- Update all imports in `markitect.llm` and `markitect.prompts`
|
||||||
|
- Keep a re-export shim in `markitect.prompts.execution.models` for backwards compat
|
||||||
|
|
||||||
|
**Acceptance:** `markitect/llm/` has zero imports from `markitect.prompts.*`
|
||||||
|
|
||||||
|
#### S1.2 — Parameterize the TOML config chain
|
||||||
|
|
||||||
|
Replace the hardcoded `"markitect"` app name with a configurable `app_name` parameter.
|
||||||
|
|
||||||
|
**Work:**
|
||||||
|
- Add `app_name: str = "markitect"` parameter to `resolve_llm()` and the config
|
||||||
|
path helpers in `toml_config.py`
|
||||||
|
- Derive config file path (`~/.config/{app_name}/config.toml`), env prefix
|
||||||
|
(`{APP_NAME}_HELPER_MODEL`), and local config file (`.{app_name}.toml`) from it
|
||||||
|
- All existing behaviour is preserved when `app_name="markitect"` (default)
|
||||||
|
|
||||||
|
**Acceptance:** A consumer can call `resolve_llm(app_name="railiance")` and get
|
||||||
|
config from `~/.config/railiance/config.toml` and `RAILIANCE_HELPER_MODEL`.
|
||||||
|
|
||||||
|
#### S1.3 — Isolation tests
|
||||||
|
|
||||||
|
Write a test file that imports only from `markitect.llm.*` and verifies no
|
||||||
|
accidental coupling remains.
|
||||||
|
|
||||||
|
**Acceptance:** `pytest tests/test_llm_isolation.py` passes; no import of
|
||||||
|
`markitect.prompts` or `markitect.infospace` in the LLM module tree.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Stage 2 — Extract
|
||||||
|
|
||||||
|
#### S2.1 — Resolve D1: packaging location
|
||||||
|
|
||||||
|
Record the decision and create the package scaffold.
|
||||||
|
|
||||||
|
**Acceptance:** D1 resolved, `pyproject.toml` for the library exists at the
|
||||||
|
chosen location with name, version `0.1.0`, and declared dependencies.
|
||||||
|
|
||||||
|
#### S2.2 — Create standalone package
|
||||||
|
|
||||||
|
Move (or symlink) the llm module into the new package structure. Wire up
|
||||||
|
the `pyproject.toml` entry points. Verify `pip install -e <path>` works.
|
||||||
|
|
||||||
|
**Files to carry over:**
|
||||||
|
```
|
||||||
|
llm/
|
||||||
|
__init__.py # re-exports: create_adapter, create_embedding_adapter,
|
||||||
|
# LLMAdapter, EmbeddingAdapter, LLMConfig, exceptions
|
||||||
|
models.py # RunConfig, LLMResponse (moved from S1.1)
|
||||||
|
config.py # load_config, resolve_api_key
|
||||||
|
toml_config.py # resolve_llm (parameterized from S1.2)
|
||||||
|
factory.py # create_adapter
|
||||||
|
exceptions.py # LLM exception hierarchy
|
||||||
|
openrouter.py
|
||||||
|
claude_code.py
|
||||||
|
gemini.py
|
||||||
|
openai.py
|
||||||
|
embedding_adapter.py
|
||||||
|
embedding_openai.py
|
||||||
|
embedding_factory.py # create_embedding_adapter
|
||||||
|
embedding_cache.py
|
||||||
|
similarity.py
|
||||||
|
_http.py
|
||||||
|
_token_estimator.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Acceptance:** `python -c "from bw_llm import create_adapter; print('ok')"` works
|
||||||
|
in a fresh venv with only the new package installed.
|
||||||
|
|
||||||
|
#### S2.3 — Update markitect to depend on extracted package
|
||||||
|
|
||||||
|
Replace `markitect/llm/` with an import alias pointing to the new package, or
|
||||||
|
add the package as a path dependency in markitect's `pyproject.toml`.
|
||||||
|
|
||||||
|
**Acceptance:** All markitect tests pass; `markitect/llm/__init__.py` is either
|
||||||
|
removed or becomes a thin re-export of `bw_llm`.
|
||||||
|
|
||||||
|
#### S2.4 — Integration smoke test
|
||||||
|
|
||||||
|
Run the full markitect infospace pipeline (entity extraction + evaluation) end-to-end
|
||||||
|
against a small fixture to confirm nothing broke.
|
||||||
|
|
||||||
|
**Acceptance:** `markitect infospace evaluate --dry-run` succeeds on a 3-entity fixture.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Stage 3 — Adopt in First Consumer
|
||||||
|
|
||||||
|
#### S3.1 — Integrate in one other project
|
||||||
|
|
||||||
|
Pick the first real consumer (likely the custodian state-hub, for LLM-assisted
|
||||||
|
state summaries or decision rationale generation) and wire up the library.
|
||||||
|
|
||||||
|
**Work:**
|
||||||
|
- Add `bw-llm` (or equivalent) as a dependency
|
||||||
|
- Write a small usage example (e.g., `llm_helper.py`)
|
||||||
|
- Confirm config chain works with the consumer's own app name
|
||||||
|
|
||||||
|
#### S3.2 — Usage guide
|
||||||
|
|
||||||
|
Write `README.md` for the library covering:
|
||||||
|
- Installation (local path / git URL)
|
||||||
|
- Supported providers and env vars
|
||||||
|
- TOML config file locations and format
|
||||||
|
- `create_adapter()` / `create_embedding_adapter()` quick-start
|
||||||
|
- Error handling
|
||||||
|
|
||||||
|
**Acceptance:** Another developer (or agent) can follow the README to use the library
|
||||||
|
in a new project without reading source code.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stage Summary
|
||||||
|
|
||||||
|
| Stage | Description | Key Deliverable | Blocks |
|
||||||
|
|-------|-------------|-----------------|--------|
|
||||||
|
| S1.1 | Move RunConfig/LLMResponse to llm | Zero cross-module deps | S2.2 |
|
||||||
|
| S1.2 | Parameterize app name | Configurable config chain | S2.2 |
|
||||||
|
| S1.3 | Isolation tests | Green test suite | S2.1 |
|
||||||
|
| S2.1 | Resolve packaging decision (D1) | pyproject.toml scaffold | S2.2 |
|
||||||
|
| S2.2 | Create standalone package | `pip install` works | S2.3 |
|
||||||
|
| S2.3 | Update markitect | markitect uses extracted lib | S2.4 |
|
||||||
|
| S2.4 | Integration smoke test | Full pipeline passes | S3.1 |
|
||||||
|
| S3.1 | First consumer integration | Library used in real project | S3.2 |
|
||||||
|
| S3.2 | Usage guide | README published | — |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- Publishing to PyPI (unnecessary for personal infrastructure; git/local installs suffice)
|
||||||
|
- Adding new LLM providers (separate concern)
|
||||||
|
- Porting the helper CLI to the library (the CLI is markitect-specific)
|
||||||
|
- Async adapters (current sync interface is sufficient; can be added later)
|
||||||
Reference in New Issue
Block a user