# PhysCom — Project Status ## Architecture Overview ``` src/ ├── physcom/ Core engine (CLI + SQLite + 5-pass pipeline) │ ├── cli.py Click CLI: init, seed, entity, domain, run, results, review, export │ ├── db/ │ │ ├── schema.py DDL — 9 tables, 4 indexes │ │ └── repository.py Data-access layer (24 methods) │ ├── engine/ │ │ ├── combinator.py Cartesian product generator │ │ ├── constraint_resolver.py Pass 1 — requires/provides/excludes/range matching │ │ ├── scorer.py Pass 3 — weighted geometric mean normalizer │ │ └── pipeline.py Orchestrator for passes 1–5 │ ├── llm/ │ │ ├── base.py Abstract LLMProvider interface (2 methods) │ │ ├── prompts.py Prompt templates for passes 2 and 4 │ │ └── providers/ │ │ └── mock.py Deterministic stub for tests │ ├── models/ Dataclasses: Entity, Dependency, Domain, MetricBound, Combination, Score │ └── seed/ │ └── transport_example.py 9 platforms + 9 power sources + 2 domains │ ├── physcom_web/ Flask web UI │ ├── app.py App factory, per-request DB connection │ ├── routes/ │ │ ├── entities.py Entity + dependency CRUD (9 routes, HTMX) │ │ ├── domains.py Domain listing (1 route) │ │ ├── pipeline.py Run form + execution (2 routes) │ │ └── results.py Browse, detail, human review (4 routes) │ ├── templates/ Jinja2 + HTMX — 11 templates │ └── static/style.css │ tests/ 37 passing tests Dockerfile Single-stage Python 3.13-slim docker-compose.yml web + cli services, shared volume ``` ## What Works | Area | Status | Notes | |------|--------|-------| | Database schema | Done | 9 tables, WAL mode, foreign keys | | Entity/dependency CRUD | Done | CLI + web UI | | Domain + metric weights | Done | CLI seed + web read-only | | Pass 1 — constraint resolution | Done | requires/provides/excludes/range logic | | Pass 2 — physics estimation | Done | Stub heuristic (force/mass-based); LLM path exists but no real provider | | Pass 3 — scoring + ranking | Done | Weighted geometric mean with min/max normalization | | Pass 4 — LLM plausibility review | Wired | Pipeline calls `self.llm.review_plausibility()` when `llm` is not None; only MockLLMProvider exists | | Pass 5 — human review | Done | CLI interactive + web HTMX form | | Web UI | Done | Entity CRUD, domain view, pipeline run, results browse + review | | Docker | Done | Compose with web + cli services, named volume | | Tests | 37/37 passing | Repository, combinator, constraints, scorer, pipeline | ## What's Missing ### LLM provider — no real implementation yet The `LLMProvider` abstract class defines two methods: ```python class LLMProvider(ABC): def estimate_physics(self, combination_description: str, metrics: list[str]) -> dict[str, float]: ... def review_plausibility(self, combination_description: str, scores: dict[str, float]) -> str: ... ``` **Pass 2** (`estimate_physics`) — given a combination description like *"platform: Bicycle + power_source: Hydrogen Combustion Engine"*, return estimated metric values (speed, cost_efficiency, safety, etc.) as floats. **Pass 4** (`review_plausibility`) — given a combination description and its normalized scores, return a 2–4 sentence plausibility assessment. Prompt templates already exist in `src/physcom/llm/prompts.py`. The pipeline already checks `if self.llm:` and skips gracefully when None. ### To enable LLM reviews, you need to: 1. **Create a real provider** at `src/physcom/llm/providers/.py` that subclasses `LLMProvider`. For example, an Anthropic provider: ```python # src/physcom/llm/providers/anthropic.py import json from anthropic import Anthropic from physcom.llm.base import LLMProvider from physcom.llm.prompts import PHYSICS_ESTIMATION_PROMPT, PLAUSIBILITY_REVIEW_PROMPT class AnthropicProvider(LLMProvider): def __init__(self, model: str = "claude-sonnet-4-20250514"): self.client = Anthropic() # reads ANTHROPIC_API_KEY from env self.model = model def estimate_physics(self, description: str, metrics: list[str]) -> dict[str, float]: prompt = PHYSICS_ESTIMATION_PROMPT.format( description=description, metrics=", ".join(metrics), ) resp = self.client.messages.create( model=self.model, max_tokens=256, messages=[{"role": "user", "content": prompt}], ) return json.loads(resp.content[0].text) def review_plausibility(self, description: str, scores: dict[str, float]) -> str: prompt = PLAUSIBILITY_REVIEW_PROMPT.format( description=description, scores=json.dumps(scores, indent=2), ) resp = self.client.messages.create( model=self.model, max_tokens=512, messages=[{"role": "user", "content": prompt}], ) return resp.content[0].text ``` 2. **Add the dependency** to `pyproject.toml`: ```toml [project.optional-dependencies] llm = ["anthropic>=0.40"] ``` 3. **Wire it into the CLI** — in `cli.py`'s `run` command, instantiate the provider when an `--llm` flag is passed and include pass 4 in the pass list. 4. **Wire it into the web UI** — in `routes/pipeline.py`, same logic: read a config flag or env var (`PHYSCOM_LLM_PROVIDER`), instantiate the provider, pass it to `Pipeline(...)`. 5. **Set the API key** — `ANTHROPIC_API_KEY` env var (or equivalent for your chosen provider). In Docker, add it to `docker-compose.yml`: ```yaml environment: - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} ``` The same pattern works for OpenAI, Databricks, or any other provider — just subclass `LLMProvider` and implement the two methods. ### Other future work - **Domain creation via web UI** — currently seed-only - **Database upgrade** — SQLite → Postgres (docker-compose has a commented placeholder) - **Async pipeline runs** — currently synchronous; fine for 81 combos, may need background tasks at scale - **Export from web UI** — currently CLI-only (`physcom export`) - **Authentication** — no auth on the web UI