Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

101 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FreeCAD Copilot

An assistant that lives inside FreeCAD's own UI. You state an Intent in your own words — "make a gear with 24 teeth" — and the Copilot turns it into a Plan of Steps, shows you the Plan before anything is written, and then applies the approved Steps to your Document.

It is text-to-CAD, with one difference from the usual meaning of that phrase: the output is a live parametric feature tree you can keep editing, not a dead mesh or an STL.

This is a diploma project. The inspiration and the deliberate departure is MEDA (ASME IDETC-CIE 2025), which routes seven agents through a group chat. This runs a fixed pipeline instead — the claim being that a fixed pipeline with Preflight and live introspection gives a controllable, measurable result where LLM-chosen routing gives neither exit conditions nor honest progress.


How a run works

Intent ──► Plan ──► [ Plan Gate ] ──► Step 1 ──► Step 2 ──► … ──► Review
                          │             │                            │
                     you approve    Lint → Preflight → Apply     verdict returns
                     the whole Plan   (disposable worker)           to Plan

Plan → Execute → Review is a fixed order with deterministic routing (ADR-0003). Plan produces the Steps, Execute works through them one at a time, Review judges the outcome against the Intent, and Review's verdict returns to Plan — never to Execute. There is no model-chosen speaker selection anywhere.

The Plan Gate

One mandatory approval, covering every Step in the Plan. There is no per-Step confirmation. Nothing that is not an Owned Object — something the assistant created in this Session — is deleted without asking.

Lint, then Preflight, then Apply

Before a Step's code runs anywhere:

  • Lint reads the code as an AST. It carries only the rules a Preflight traceback cannot teach the way out of — a literal Edge12 / Face3 (ADR-0005), anything reaching for the GUI or the live selection, anything trying to make an object active.
  • Preflight runs the Step against a throwaway copy of the Document in a separate, disposable headless FreeCAD process (ADR-0002). A Step that fails Preflight never reaches your Document. The worker dying is a normal outcome, not an incident: it is restarted and the crash comes back as a traceback the assistant can correct against.
  • Apply commits what survived, as one undoable transaction.

A failed Step answers itself (ADR-0008): the traceback goes back to Execute with introspection of whatever object the call failed on, so the next attempt has the real attribute list rather than a second guess at the same wrong spelling.

Where API knowledge comes from

Introspection of the running FreeCAD instance, not retrieval over documentation (ADR-0006). Execute may spend a bounded number of read-only Looks at the Document before it writes a Step's code (ADR-0009). What cannot be introspected is carried as a Trap — one sentence of knowledge about this specific FreeCAD build, in the system prompt, so a phase never discovers it by failing.


What is in the repo

Path What lives there
core/loop.py The Plan → Execute → Review loop, budgets, the Plan Gate
core/tools.py The Tool registry — the only sanctioned route to the Document
core/freecad_tools.py The Tools themselves: snapshot, describe_object, dependencies, apply_step, and the TRAPS
core/lint.py The AST lint that runs before Preflight
core/preflight.py The parent half: owns one warm headless FreeCAD, enforces the timeout
core/preflight_worker.py The child half: runs inside that FreeCAD, renders the result
core/apply.py Lint → Preflight → Apply, as one undoable transaction
core/llm.py The provider seam — LiteLLM, plus a scripted client for tests
core/events.py The event stream every phase narrates into, and the per-Session log
core/session_title.py The short call that names a Session from its first Intent
ui/copilot_panel.py The dock: composer, transcript, Plan Gate card, model picker
ui/multi_tab_manager.py Sessions and the stack of chat areas
ui/history_popup.py The Session Picker
config/prompts/*.md The three phase prompts — copy to tune, kept out of the source
Mod/ Workbench registration and the Preferences pages
tests/ The suite; tools/run_slice.py drives a slice against a live provider

Install

FreeCAD 1.0, Python 3.11.

git clone <this repo> ~/Library/Application\ Support/FreeCAD/Mod/FreeCadCopilot

On Linux the addon directory is ~/.local/share/FreeCAD/Mod/. A symlink from the Mod directory to a checkout elsewhere works too, and is how this repo is developed.

Start FreeCAD and pick Copilot from the workbench list. On first launch the addon checks for litellm, numpy and Pillow and offers to install what is missing into FreeCAD's own AdditionalPythonPackages.

Then open Edit → Preferences → Copilot → Providers, paste an API key, and pick a default model.

Providers

OpenAI, Google, Anthropic, and a Custom/Local OpenAI-compatible endpoint. Access goes through LiteLLM (ADR-0004) — one client for every provider, carrying Anthropic thinking blocks and cache_control through the stream. Keys are yours and stay in your FreeCAD settings; there is no service in the middle.

config/models.json is the offline fallback and the registry of which providers exist. The real list is fetched from the provider on a worker thread as soon as a key is present, so the model picker shows what your key actually entitles you to.


Sessions

A Session is one continuous stretch of work on one Document, and the scope over which Owned Objects are remembered. Every launch opens a new empty Session; earlier ones stay in the Session Picker, which drops down from the top of the dock.

A Session enters the picker at the moment it gets its name — on its first Intent. It is named from that Intent: the text truncated immediately so nothing is ever "Untitled", replaced by a short generated title when it arrives. A name you type by hand always wins, before or after. Which model writes the name is a Preferences setting; unset means whichever model the chat is on.

The transcript is the event log, replayed through the same switch that rendered it live — so a restored Session cannot drift from a running one.


Tests

.venv/bin/python -m pytest -q

FreeCAD is never mocked (ADR-0007). The suite is layered instead:

  • Level 1 (281 tests) — pure logic with no FreeCAD at all: the loop over a scripted LLM client, the lint, the event stream, the title call. This is where TDD happens.
  • Level 2 (120 tests, marked @pytest.mark.freecad) — run against a real FreeCAD: the Tools, Preflight in a real worker, and the Qt panel built offscreen against FreeCAD's own PySide2 — its arithmetic and its state machine, not its appearance.
  • Level 3 — by hand. How a widget looks under a theme is not something a test can tell you, which is the finding behind ADR-0011.

Level 2 is skipped when FreeCAD is not importable. conftest.py finds it at /Applications/FreeCAD.app/Contents/Resources/lib or /usr/lib/freecad/lib, or wherever FREECAD_LIB points.

Known wrinkle. conftest.py puts the checkout on sys.path before importing FreeCAD, and FreeCAD then puts the Mod directory in front of it. If your Mod entry is a symlink to a different checkout, Level 2 tests exercise that one instead of the one you are in. It matters when running the suite from a git worktree.


Scope

In scope: one Document, one part, parametric solid modelling — Part Design in the usual range (sketch → pad / pocket / revolve / fillet), plus whatever you have selected live.

Out of scope for now: assemblies, TechDraw, FEM, mesh and Surface. Each is a separate API vocabulary and a separate failure taxonomy. Nothing enforces this technically — a Step runs arbitrary FreeCAD Python — it is the boundary of what is demonstrated.

What this is not: not a script generator (Python is how a Step is applied, not a saved artifact — ADR-0001); not a chatbot about FreeCAD; not an autopilot; not a cloud service; not an abstraction layer over other CAD.

Who it is for: engineers who already use FreeCAD and are tired of clicking what one sentence describes. Not beginners — for a beginner the Plan Gate is a meaningless yes.


Vocabulary

The project is strict about its words: Intent, Plan, Step, Plan Gate, Document, Snapshot, Owned Object, Computed Reference, Tool, Look, Lint, Trap, Preflight, Preflight Worker, Apply, Review, Session, Session Name, Session Picker, Refusal. Tickets, tests and commit messages use these and not synonyms.

The full Glossary and the Architecture Decisions live in the YouTrack knowledge base (FCC-A-1 and FCC-A-5), not in this repo. Issues are tracked in YouTrack project FCC, not GitHub Issues — see youtrack.md.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages