Viewport3D- Three.js scene (React Three Fiber)
- Orbit/pan/zoom controls
- Flat-shaded mesh render + edge highlights
- Selection highlighting in blue
- Grid aligned at
Z=0and gizmo axis indicator
FeatureTreePanel- Dependency tree display from
FeatureTree - Expand/collapse parent nodes
- Operation badges and status markers
- Node selection callback into viewport
- Dependency tree display from
SketchEditor- SVG overlay mode
- Constraint icons rendered near entities
- Calls solver API (or mock solver) whenever constraints change
ChatPanel- Prompt input + send flow
- Streaming assistant response rendering
- Operation execution list with per-step status
High Reasoningtoggle
- Mesh contract:
GET /shape/{id}/mesh -> { vertices, faces, normals }
- Tree contract:
GET /tree -> FeatureTree
- Feature updates:
POST /featurePATCH /feature/{id}POST /trees/{tree_id}/nodes/{node_id}/typed-parametersPOST /trees/{tree_id}/nodes/{node_id}/suppressPOST /trees/{tree_id}/branchesPOST /trees/{tree_id}/branches/{branch_name}/switchPOST /trees/{tree_id}/solver/{sketch_id}
The viewport client (src/api/client.ts) defaults viewport geometry/solver flows to mock mode via VITE_USE_MOCK.
Chat uses the live agent service by default, with an opt-in mocked chat path via VITE_USE_CHAT_MOCK=true.
Viewport3D.stories.tsxFeatureTreePanel.stories.tsxSketchEditor.stories.tsxChatPanel.stories.tsx
- Accept natural language feature requests
- Build a full operation sequence before execution
- Execute tool calls against an in-memory feature-tree runtime
- Return:
- Human-readable response
- Structured
operations_executed - Updated
FeatureTree
GET /healthzPOST /chat- Input:
message: strtree_state: FeatureTreeconversation_history: []reasoning: boolllm_provider: str | null(optional, for LiteLLM-backed code generation)llm_model: str | null(optional, for LiteLLM-backed code generation)generate_code: bool(optional, returns example-style Python instead of executing tools)
- Output:
response: strgenerated_code: str | nulloperations_executed: []new_tree_state: FeatureTree
- Input:
prompting.py- Builds required system prompt sections:
- current tree state
- available operation schemas
- naming and validation instructions
- example-script references for code generation
- Builds required system prompt sections:
tools.py- Tool runtime for:
add_sketchextrudeboolean_cutfillet_edgesadd_cylinderget_tree_stateget_shape_info
- Tool runtime for:
planner.py- Sequence planning + deterministic execution path
- Includes a mission prompt path for mounting bracket generation (8+ operations)
service.py- Request orchestration and response assembly
- Optional LiteLLM-backed code generation path for multi-provider responses
- 4 (
ChatPanel) calls 5 (/chat) - 5 outputs updated
FeatureTree - 4
FeatureTreePanelrenders updated nodes/status - 4
SketchEditorcalls 2 solver endpoint - 2 solver results can feed 3 feature-tree parameter bindings (
source=solver) - 4 mesh loading contract aligns to 1/3 shape identity model
- Feature nodes support typed inputs (
typed_parameters) and explicit binding metadata (parameter_bindings). - Rebuild is incremental: only
pending,stale, orfailednodes with all parents inbuiltstate are executed. - Suppression is first-class (
suppressed=true): suppressed nodes produce no shape and stale descendants until re-enabled. - Branching is snapshot-based (
branch_snapshots+active_branch) for fast variant exploration and deterministic replay. - Full history export is JSON-native through tree serialization, including branch snapshots and solver cache values.
OpenCAD now supports a headless mode where kernel, tree, and fluent API calls execute in one Python process.
opencad.runtime.RuntimeContext- Owns
OpenCadKernel,OperationRegistry, and in-memoryFeatureTree - Executes operations without inter-service HTTP
- Tracks latest feature and shape IDs for fluent chaining
- Supports tree serialization/deserialization and in-process rebuild
- Exposes
chat()that runsOpenCadAgentServicein-process via injected kernel calls
- Owns
opencad.part.Part- Kernel-backed v1 methods for primitives, booleans, extrude, edge/face ops, patterns, and export
opencad.sketch.Sketch- Fluent segment builder (
line,rect,circle) that materializes viacreate_sketch
- Fluent segment builder (
- Public import surface:
from opencad import Part, Sketch
Every fluent call appends a FeatureNode to the active tree branch.
- Operation execution and DAG writes are coupled in
RuntimeContext.execute_operation - Dependencies are recorded as feature-node IDs (not only shape IDs)
- Resulting tree can be persisted and rebuilt with
FeatureTreeService
When ToolRuntime has an injected kernel call (single-process mode), it now:
- Converts supported sketch entities to
create_sketchsegments - Calls kernel
extrudeagainst the created sketch shape when available - Falls back to synthetic/legacy approximations only when conversion is not possible
Current planner output now prefers explicit sketch entities (line, circle) over point-only payloads,
which improves deterministic translation to kernel sketch segments in headless mode.
Planner sketch payloads may also include profile_order (entity ID sequence) to guarantee
deterministic loop ordering independent of dictionary iteration order.
The fluent opencad.Sketch path now writes the same entities + profile_order metadata
into sketch feature-node parameters, aligning script-authored sketches with agent-authored
sketch ordering semantics.
opencad build model.json --output model.built.json- Loads a tree JSON model and rebuilds it in-process
opencad run model.py --export output.step --tree-output output-tree.json- Executes fluent scripts in-process and optionally exports STEP + tree JSON
This path is intended for script-first workflows where users want Build123d-like ergonomics while retaining recoverable, editable feature DAG state.
The solver service (opencad_solver) uses a backend abstraction (SolverBackend ABC
in opencad_solver/backend.py) to decouple constraint-solving logic from the API layer.
- PythonSolverBackend (default) — NumPy/SciPy Gauss-Newton with numerical Jacobian.
- SolveSpaceBackend — delegates to SolveSpace via
python-solvespacebindings. Falls back gracefully when the package is not installed. - Backend selection:
OPENCAD_SOLVER_BACKEND=solvespace|python|auto(auto prefers SolveSpace).
POST /sketch/diagnose returns ConstraintDiagnostics with:
| Field | Description |
|---|---|
dof |
Remaining degrees of freedom |
status |
SOLVED, UNDERCONSTRAINED, OVERCONSTRAINED |
jacobian |
JacobianInfo — shape, rank, sparse nonzero (row, col) pairs |
variables |
VariableInfo[] — maps solver variable index → entity ID + parameter name |
constraints |
ConstraintInfo[] — maps constraint → row span + residual norm |
over_constrained_ids |
Constraint IDs with high residual (conflicting) |
under_constrained_variables |
Variable indices with near-zero Jacobian column norm |
This is the primary API for AI agents reasoning about constraint state.
Assembly mates are first-class kernel operations registered in the operation registry:
create_assembly_mate— validates entity references, creates mate inMateStore.delete_assembly_mate— removes a mate by ID.list_assembly_mates— lists all mates, optionally filtered by entity involvement.
Supported mate types: coincident, concentric, distance, angle, parallel, perpendicular.
Mates are modeled as feature nodes in the tree with is_assembly_mate=True and bidirectional
dependencies on the constrained shapes. When either shape rebuilds, the mate node goes stale.
Requires topology reference stability. See TOPOLOGY.md for:
- Problem statement and concrete failure walkthrough
- Prior-art analysis (FreeCAD TNaming, Build123d hash tracking, Fusion 360, STEP)
- Community proposal template and acceptance criteria
- 5 tests in
opencad_agent/tests/test_agent.pyverify:- system prompt completeness
- mounting bracket prompt creates >=8 valid operations
- operation references use existing IDs
- reasoning toggle changes response style
- API round-trip and health endpoint