Files
physicalCombinatorics/STATUS.md

141 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 15
│ ├── 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 24 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/<name>.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