Metadata-Version: 2.4
Name: chamfer-cad
Version: 0.2.0
Summary: A small, plain-Python CAD agent harness: the LLM drives an external CAD MCP server to build a part from a prompt, and a deterministic kernel verifier decides PASS.
Project-URL: Homepage, https://smartai.github.io/Chamfer/
Project-URL: Repository, https://github.com/SmartAI/Chamfer
Project-URL: Issues, https://github.com/SmartAI/Chamfer/issues
Author: Min Liu
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Requires-Python: >=3.11
Requires-Dist: fastmcp>=3.4
Requires-Dist: pyyaml>=6.0
Provides-Extra: langfuse
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.25; extra == 'langfuse'
Requires-Dist: opentelemetry-sdk>=1.25; extra == 'langfuse'
Requires-Dist: pydantic-settings>=2.0; extra == 'langfuse'
Provides-Extra: llm
Requires-Dist: litellm>=1.60; extra == 'llm'
Requires-Dist: python-dotenv>=1.0; extra == 'llm'
Requires-Dist: tenacity>=8.0; extra == 'llm'
Provides-Extra: otel
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.25; extra == 'otel'
Requires-Dist: opentelemetry-sdk>=1.25; extra == 'otel'
Requires-Dist: pydantic-settings>=2.0; extra == 'otel'
Description-Content-Type: text/markdown

# chamfer

[![CI](https://github.com/SmartAI/Chamfer/actions/workflows/ci.yml/badge.svg)](https://github.com/SmartAI/Chamfer/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/chamfer-cad.svg)](https://pypi.org/project/chamfer-cad/)
[![Python](https://img.shields.io/pypi/pyversions/chamfer-cad.svg)](https://pypi.org/project/chamfer-cad/)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
[![Homepage](https://img.shields.io/badge/home-smartai.github.io%2FChamfer-ea580c.svg)](https://smartai.github.io/Chamfer/)

**Type what you want — or show a photo. Get a real, verified CAD part.**

Describe a product in plain words — a kettle, a desk lamp, a bracket — or hand
over a reference image, and an AI agent designs it as real, editable,
correctly-sized geometry. It builds the part step by step through a CAD engine,
measures its own work, fixes its own mistakes, and only claims success when a
deterministic kernel check proves every requirement is met.

`chamfer` is a small, plain-Python **agent harness**, not a framework and not a
chat wrapper. The harness itself is CAD-agnostic: the LLM drives an **external
CAD MCP server** (build123d by default) to synthesize geometry, and a fixed
verifier — never the model's own word — decides PASS. The whole loop reads top
to bottom.

![The agent building a part step by step, measuring its own work and self-correcting](docs/media/drip-coffee-maker-build.gif)

*One real run, replayed frame by frame: the agent places every component, **measures its own geometry, catches interpenetrating parts, and rebuilds them** before the independent verifier passes it. (This GIF is from an earlier Autodesk-Fusion-backed version; the current harness targets an external build123d MCP server — same loop, same measured finish line.)*

## How it works

```mermaid
flowchart TD
    P["📝 A plain-language prompt<br>(optionally with reference photos)"] --> LOOP

    subgraph LOOP["🔁 The agent loop — builds the design one small step at a time"]
        direction TB
        ORCH["Harness<br>feeds in the task, skills,<br>and the CAD tools"] --> LLM["LLM<br>decides the next small step"]
        LLM -- "writes a bit of CAD code" --> RUN["Run it on the CAD MCP server<br>a real part appears"]
        LLM -- "unsure of the API" --> DOCS["Look it up<br>in the server's own resources"]
        LLM -- "wants ground truth" --> MEAS["Measure the parts<br>fixed probe code — not written by the LLM"]
        RUN -- "result, or the error to fix" --> LLM
        DOCS -- "the real answer" --> LLM
        MEAS -- "real sizes, volumes, positions,<br>renders the agent actually sees" --> LLM
    end

    LOOP -- "the agent says: done" --> RECAP["📋 Evidence recap<br>declared measurement tools re-run;<br>fresh numbers injected as authoritative"]
    RECAP --> CHECK["🔍 Independent check<br>the harness re-measures the exported STEP with fixed code<br>— no LLM involved"]
    CHECK -- "checks fail: measured problems go back" --> LOOP
    CHECK -- "every check passes" --> DONE["✅ Verified STEP"]

    style LLM fill:#fff7ed,stroke:#ea580c
    style ORCH fill:#fff7ed,stroke:#ea580c
    style RUN fill:#eff6ff,stroke:#2563eb
    style DOCS fill:#eff6ff,stroke:#2563eb
    style MEAS fill:#eff6ff,stroke:#2563eb
    style RECAP fill:#eff6ff,stroke:#2563eb
    style CHECK fill:#eff6ff,stroke:#2563eb
    style DONE fill:#dcfce7,stroke:#059669
```

1. **You ask in plain words** — optionally with reference photos the agent
   designs toward.
2. **It designs in small steps.** The agent loop: write a small piece of CAD
   code, run it on the CAD MCP server, look at what happened, continue. Unsure
   of an API? It reads the server's own resources instead of guessing. A step
   fails? It reads the error and fixes it.
3. **It measures — and looks at — its own work.** After building, it asks the
   CAD kernel for the real numbers (sizes, volumes, positions, hole
   inventories) and sees its own renders as images, so visual mistakes get
   caught the same way numeric ones do.
4. **Its last words are grounded too.** When the agent says "done", the
   declared measurement tools are re-run one final time and their fresh output
   is injected as the authoritative basis for the final report — the model
   never gets to summarize from memory.
5. **The harness has the final word.** The harness independently re-measures
   the exported STEP and grades it with fixed code. Anything failing goes back
   to the agent, with the measured numbers, for another bounded round. Success
   is only reported when every check passes.

## Install

```bash
uv tool install chamfer-cad      # or: pip install chamfer-cad  (the CLI command is `chamfer`)
```

On first run, chamfer provisions **`~/.chamfer/`** with a set of default skills
and an MCP config (the build123d server), the same way other CLIs provision
their config directory.

## Quickstart

```bash
# build a part from a prompt (set your provider's API key in the environment first)
export OPENAI_API_KEY=...          # or ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENROUTER_API_KEY, ...
chamfer run "a 2 L electric kettle with a rounded rim" --model openai/gpt-5

# any provider litellm supports — the model string picks it
chamfer run "a desk lamp" --model anthropic/claude-opus-4-8 -o out/lamp.step

# Claude Code subscribers: no API key needed — reuse your local login
chamfer run "a mounting bracket" --model claude-sub
```

Copy [.env.example](.env.example) to `.env` for a documented starting point:
model choice, provider keys, cost caps, and tracing all configure from the
environment (a `.env` never overrides variables already set in your shell).

### From a photo to a part

Attach reference images and the agent designs toward them: a requirements
stage first describes each image in CAD-engineering terms to sharpen the
prompt, and during the build the agent compares its own renders against your
photos.

```bash
chamfer run "a wall bracket like this photo, 100 mm wide" --image photo.jpg

# repeatable; order is preserved, so the prompt can reference each image
chamfer run "front view first, side view second; 6 mm plate" \
  --image front.jpg --image side.png
```

![A reference photo turned into a measured, verified part](docs/media/bracket-photo-to-part.gif)

*A real `--image` run: the reference render goes in, the agent builds the
bracket step by step, checks its own renders against the photo, and the
kernel confirms all four through-holes before the verifier passes it.*

### Iterate in sessions

Give a run a session id and follow-up runs become edits of the last verified
part — each version is kept, and the agent gets the previous geometry (and its
renders) as context:

```bash
chamfer run "an L-bracket, 100x50x50, 5 mm walls" --session bracket
chamfer run "make the holes 8 mm and add a corner fillet" --session bracket
# sessions/<id>/v1..vN under the run root — every version stays replayable
```

### Watch it work — `chamfer serve`

Every run writes a full per-turn event log, and `chamfer serve` is a local web
app over it: start runs from a composer, watch the thread live next to a 3D
canvas, scrub back through every step of a finished run, and read the
spec-sheet acceptance checks on the Evidence tab.

```bash
chamfer serve            # http://localhost:8000, token-guarded, read-only viewer
```

![Replaying a run in the chamfer serve web app](docs/media/webapp-session.gif)

*Live and replay are the same code path over `session.jsonl`: the scrubber
walks the actual per-turn geometry; the Evidence tab shows what the verifier
measured, check by check.*

### Options

- `--model <litellm-model>` — the reasoning model, as a litellm string (`openai/gpt-5`, `anthropic/claude-opus-4-8`, `gemini/…`, `openrouter/…`, a local endpoint, or `claude-sub` for a local Claude Code login). Defaults to `$CHAMFER_MODEL` or `openai/gpt-5`.
- `--image <path>` — attach a reference image (repeatable; order preserved).
- `--session <id>` — group runs into an iterative edit chain (`sessions/<id>/v1..vN`).
- `-o, --output <path>` — copy the final verified CAD artifact to this path (parent dirs created).
- `--out <dir>` — run root for workspaces/evidence (default `~/.chamfer/runs`).
- `--reasoning-effort {minimal,low,medium,high}` — for models that support it (or `$CHAMFER_REASONING_EFFORT`).
- `--max-tokens <n>` — max output tokens per turn (or `$CHAMFER_MAX_TOKENS`).
- `--max-total-tokens <n>` — hard cost cap: stop once cumulative tokens exceed this (or `$CHAMFER_MAX_TOTAL_TOKENS`).
- `--max-turns <n>` — agent turn budget.
- `--probe {off,soft,info,gate}` — mid-loop deterministic feedback level (or `$CHAMFER_PROBE`; explicit flag wins). `off`: none; `soft`: prompt instruction only; `info`: soft + harness-run post-op checks injected as `[auto-check]`; `gate`: info + an unfixed failing objective check blocks finishing. Default: `info`.
- `--judge {self,off}` — one-shot render-vs-intent self-check before the agent may finish. Default: `self`.
- `--sandbox` / `--no-sandbox` — filesystem discipline for tool calls (disabled by default for now).

The full test suite runs offline — no key, no network:

```bash
uv run --group dev pytest -q          # CI runs this with -m "not bench"
```

Benchmark sweeps (CADGenBench-style fixtures through the real agent loop) live
in `evals/` in the repo: `python -m evals.run_bench --help`.

## Observability

Every run writes a full per-turn trace to its `session.jsonl` — the exact
messages sent (images redacted), tool calls and results, tokens, latency, and
finish reason — plus every render the agent saw, persisted under `renders/`.
Zero setup, so any run can be replayed and audited offline; `chamfer serve`
is a UI over exactly this.

For richer tracing (dev or production), chamfer exports to
[Langfuse](https://langfuse.com) (OSS, self-hostable or Cloud) via litellm. Set
your keys and it turns on automatically:

```bash
uv sync --extra llm --extra langfuse
export LANGFUSE_PUBLIC_KEY=pk-lf-...  LANGFUSE_SECRET_KEY=sk-lf-...
export LANGFUSE_HOST=https://cloud.langfuse.com   # or your self-hosted URL
chamfer run "a desk lamp" --model openai/gpt-5     # traces stream to Langfuse
```

`CHAMFER_TRACE=langfuse` forces it on explicitly; `CHAMFER_TRACE=otel` exports
vendor-neutral OTLP instead; unset (and no Langfuse keys) uses the local JSONL
trace only.

## Configuration & skills

chamfer layers configuration highest-precedence first:

| Scope | Instructions | Skills | MCP servers |
|---|---|---|---|
| **Project** (your working dir) | `CHAMFER.md` (searched up from the cwd) | `.chamfer/skills/*/SKILL.md` | `.mcp.json` or `.chamfer/mcp.*` |
| **User global** | — | `~/.chamfer/skills/*/SKILL.md` | `~/.chamfer/mcp.json` |

A project skill overrides a same-named global one. MCP server declarations
merge **key-deep**: a project `.mcp.json` that only registers a server inherits
the global declaration's probe and artifact settings for the keys it doesn't
set (an explicit `null` removes an inherited key). So registering a server in a
project never silently drops the measured-feedback defaults.

The defaults in `~/.chamfer/` are seeded on first run and are yours to edit or
extend. Skills are progressive-disclosure Markdown (`SKILL.md` with a name +
description in front-matter); their names go into the prompt and the agent
reads the body on demand.

### Project instructions — `CHAMFER.md`

Drop a `CHAMFER.md` in your project (chamfer searches up from the working directory) to add your own instructions to the agent's system prompt — conventions, house rules, units, whatever the model should always know for this project. It is optional and **not** shipped with chamfer; the base system instruction lives in code. Think of it as chamfer's equivalent of a project `AGENTS.md`/`CLAUDE.md`.

The system prompt is assembled in a fixed order:

```
chamfer system instruction (built-in)
  → built-in tools + MCP tools
  → MCP server instructions / resources
  → your CHAMFER.md
  → skills (names + descriptions only; bodies read on demand)
  → your prompt
```

Any MCP server you declare is bridged generically into the agent loop — its
tools are discovered via `tools/list`, risk-classified, and registered
alongside the harness's built-ins. A server declaration can also carry a
`probe` block naming which of its tools the harness re-runs after build
operations (that is how the measured feedback above stays CAD-agnostic).
build123d ships as the default; point chamfer at another CAD MCP server and
the same loop drives it.

## Gallery

Each GIF below is a script-for-script replay of a real session log — actual geometry states, not illustrations. **These runs are from an earlier Autodesk-Fusion-backed version of the harness**; the current release drives an external build123d MCP server through the same measured loop.

### Electric kettle

![Electric kettle build](docs/media/electric-kettle-build.gif)

A cylindrical body on a power base, a C-handle with measured hand clearance, a domed lid and a spout — with one live self-correction: the spout first extruded upside-down, the agent saw it in its own measurement and rebuilt it at the rim.

### Desk lamp

![Desk lamp build](docs/media/desk-lamp-build.gif)

A weighted base, a slim stem, an overhanging arm and a hollow shade — with a kept-clear light zone verified at a measured 258 mm, and stability confirmed from the center of mass sitting inside the base edge.

## Under the hood

The one non-negotiable: **the finish line is kernel-measured, never self-reported.**

- **The LLM gets freedom where geometry is synthesized.** It free-writes small CAD scripts against the live document, looks up real API details on demand, and repairs from raw errors — the standard coding-agent loop, applied to CAD.
- **Feedback is symmetric.** Numbers come back as numbers, renders come back as images the model actually sees — its own render calls included — so visual drift is catchable mid-loop, not just at the end.
- **Determinism decides what is true.** After the agent declares done, its declared measurement tools re-run one last time (the evidence recap), then a fixed tiered verifier re-reads the exported STEP and the acceptance checks run against that snapshot. Failures — with measured numbers — become the next round's task.
- **Risk is gated.** Every tool carries a risk class behind a policy gate: reads run freely; writes and CAD execution are consequential; a human can be required before irreversible actions.
- **Everything is logged.** Full session JSONL (every prompt, reply, and tool call) plus persisted renders under `~/.chamfer/` — the web app and the replay tooling are both just views over it.

## Architecture

![Architecture](docs/media/architecture.svg)

The load-bearing decision is **where determinism lives**: the LLM writes any CAD code it wants (freedom where geometry is synthesized), and deterministic code decides what is true (a kernel-backed verifier + acceptance checks, with measured failures driving bounded repair rounds).

The source is flat, plain Python you can read top to bottom — the agent loop, the tools, the generic MCP bridge, the verifier, and the web viewer — under `src/`, with no agent framework.

## What it is not

Not a product, not a chat assistant, and not affiliated with Autodesk. It is a reference harness for building *reliable* codegen agents on top of a CAD kernel: bounded loops, measured verification, risk-gated actions, every deterministic module unit-tested.

## License

Apache-2.0 — see [LICENSE](LICENSE).

*This is an independent open-source project — not affiliated with, endorsed by, or sponsored by Autodesk, Inc. Autodesk and Fusion are trademarks of Autodesk, Inc.*
