Takes a photo of a real-world object (or a text description) and produces 3 LEGO build suggestions — compact, standard, and detailed — each with a rendered 3D preview and a parts list. Select one and download a step-by-step instruction book in official LEGO manual style.
Optionally photograph your own LEGO piece pile: Brickomancer identifies available parts and uses them as soft build constraints.
Local-first personal tool. Python/FastAPI backend + React frontend. Clean REST API designed for future migration to a phone or desktop app.
| Layer | Tool |
|---|---|
| Backend | Python 3.12, FastAPI, uv |
| Frontend | React 18 + Vite (port 5173) |
| Image → 3D → voxels | rembg (background removal) + Hunyuan3D-2mini (CUDA GPU) + trimesh voxelization |
| Voxelization | trimesh (voxelized(method="subdivide").fill()) |
| Text → shape | Claude CLI (claude -p) sparse-voxel emit via CLAUDE_CODE_OAUTH_TOKEN |
| Piece detection | Claude claude-sonnet-4-6 via CLAUDE_CODE_OAUTH_TOKEN subprocess |
| Color matching | scikit-learn + scikit-image + basic-colormath (ΔE2000) |
| Parts database | Rebrickable CC0 CSVs + LDraw LDConfig.ldr (offline) |
| 3D rendering | LDView (headless CLI) |
| Instruction PDF | LPub3D (headless CLI) |
| Testing | pytest, ruff, mypy |
- Python 3.12+, uv, Node.js 20+
claudeCLI on PATH withCLAUDE_CODE_OAUTH_TOKENset (text path —claude -psparse-voxel emit; no GPU, no llama-server)LDViewon PATH (LDView --versionorldview --versionworks)LPub3Don PATH (lpub3d -?works)- Image path: a CUDA GPU + Hunyuan3D-2mini installed in the project venv.
POST /api/generate/from-imagereturns a clean 503 if torch/CUDA/hy3dgen/weights are unavailable.hy3dgenis installed editable into the project venv and is deliberately not inpyproject/lock, so a fresh checkout won't have it — see the image-path step under Setup. CLAUDE_CODE_OAUTH_TOKENset as a Windows user environment variable (not.env); load in PowerShell via$env:CLAUDE_CODE_OAUTH_TOKEN = [System.Environment]::GetEnvironmentVariable("CLAUDE_CODE_OAUTH_TOKEN", "User")
uv sync
cd frontend; npm install; cd ..
uv run python scripts/download_data.py # downloads ~50 MB of Rebrickable CSVs + LDConfig.ldrImage path (optional — needs a CUDA GPU). hy3dgen (Hunyuan3D-2mini) is installed editable and is deliberately not in pyproject/lock, so uv sync above won't pull it. Install it into the project venv from the local Hunyuan3D-2 checkout:
uv pip install -e C:\Tools\hunyuan-src # shape-only editable install; NOT in pyproject/lockFor a clean-machine reproduction (cu118 torch pin, weights, from-scratch clone), follow the reproducible install note.
# Terminal 1 — backend (fastapi dev fails on cp1252 Windows terminals; use uvicorn)
$env:PATH += ";C:\Tools\LPub3D" # before starting the server
uv run uvicorn --app-dir src brickomancer.main:app # http://localhost:8000 (no --reload)
# Terminal 2 — frontend
cd frontend; npm run dev # http://localhost:5173Health check:
curl http://localhost:8000/api/status
# {"status":"ok","llama_server_ok":true,"ldview_ok":true,"lpub3d_ok":true}uv run pytest -q
uv run ruff check .
uv run mypy src
npm run build --prefix frontendPhoto/text input
↓
Image path: rembg (background removal) → Hunyuan3D-2mini → trimesh voxelization → (X,Y,Z) voxel grid (ImageShaper)
Text path: TextShaper → claude -p sparse 20³ voxel occupancy → fill/crop → (X,Y,Z) voxel grid
↓
Connectivity-graph brick packing (components + grounding + zero-added-height in-volume bonding)
↓
3 LDraw files (compact / standard / detailed)
↓
LDView renders 3 PNG previews
↓
User selects suggestion → LPub3D generates instruction PDF
- Ephemeral sessions, no database. All state lives in a per-request
tmp/<uuid>/directory. V1 keeps it (no cleanup) so the LDraw file survives the follow-up/instructionscall; adding job history later requires only a data-service layer. - LDraw + LPub3D for instructions. LPub3D headless produces publication-quality step illustrations; replicating this with ReportLab would take weeks.
- CLAUDE_CODE_OAUTH_TOKEN subprocess for piece detection. No API key billing on the existing subscription. The detector is behind
subprocess_utils.run_claude_subprocess(); swapping to a local LLaVA model requires changing one function. - Connectivity-graph packing. Replaces v1's greedy + masonry + bolt-on-repair: a connectivity graph makes structural weakness (disconnected components, articulation points) visible, and in-volume bonding adds interlocks with zero added height. (Phase 2, Steps 3–4.)
- Hunyuan3D-2mini for true image→3D. The v1 2D silhouette+dome heuristic fabricated depth (a star became a domed slab, losing its points); the rebuild voxelizes a real single-image→3D mesh behind the
Shaperseam. TripoSG was install-blocked on Windows, so Hunyuan3D-2mini (shape-only, no CUDA texture extensions) was chosen in Phase 0.
src/brickomancer/
main.py FastAPI app, CORS, startup data load
routers/ generate.py, info.py
services/ color_service, data_service, shaper (seam),
image_shaper, text_shaper, brick_packer, ldraw_writer,
piece_detector, suggestion_service, instruction_service
models/ schemas.py (Pydantic), brick.py (dataclasses)
utils/ temp_dir.py, subprocess_utils.py
frontend/src/
components/ WorkflowStepper, InputStep, PiecesStep,
SuggestionsStep, InstructionsStep
hooks/ useGenerate.ts
types.ts
data/
rebrickable/ CC0 CSVs (gitignored; run download_data.py)
ldraw/ LDConfig.ldr + dimensions.csv
tests/
integration/ test_smoke.py + fixtures/
scripts/
download_data.py
Full rebuild in progress — Phase 3 COMPLETE (Steps 5–8), 273 tests passing. The v1 silhouette+dome image path (which fabricated depth) and the pytest-only quality harness were removed; the project is being rebuilt around a Shaper seam (services/shaper.py, to_voxels() -> (X, Y, Z) bool grid) feeding a connectivity-graph brick packer. Both input paths now render end-to-end (smoke-verified). Done so far:
- Phase 0 — Hunyuan3D-2mini chosen for image→3D (TripoSG install-blocked on Windows); the LPub3D instruction header is BOM-only because
INSERT COVER_PAGEcrashes LPub3D 2.4.9. - Phase 1 — in-place clean + the
Shaperseam; the image/text generate routes were 503-stubbed pending the Shapers. - Phase 2 Steps 3–4 — connectivity-graph packer (component/unsupported/articulation analysis) + zero-added-height in-volume bonding.
- Phase 3 Step 5 —
ImageShaper: rembg → Hunyuan3D-2mini → trimesh voxelize, wired throughPOST /api/generate/from-image; returns 503 when the model/GPU/weights are unavailable.height_studsis the resolution knob. - Phase 3 Step 6 —
TextShaper: aclaude -psubprocess emits a sparse 20³ voxel occupancy (strict JSON) → fill/crop → grid, wired throughPOST /api/generate/from-text; returns 503 when the Claude CLI is unavailable. Retires the v1 llama-server text path (no GPU needed). - Phase 3 Step 7 — frozen BOM-only LPub3D instruction header; fixed a latent crash (
ldraw_writerstill emittedCOVER_PAGE, which crashes LPub3D 2.4.9). Render-verified: LDView PNG + LPub3D multi-page PDF with BOM. - Phase 3 Step 8 — React wizard wired to the rebuilt routes (
npm run buildclean);tests/integration/test_smoke.pyexercises both paths + instructions against the real services (gated onBRICKOMANCER_INTEGRATION=1). Smoke PASSED: text 73 s, image 1019 s (~17 min — one-time model load, cached since f471412), instructions 9 s.
Next: Phase 4 — Step 9 (render-scoring harness with a regression gate), then Step 10 (calibration run). 273 tests passing, 0 type errors, 0 lint violations. See documentation/rebuild-plan.md.