cadcore-ai 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cadcore_ai-0.1.0/LICENSE +21 -0
- cadcore_ai-0.1.0/PKG-INFO +265 -0
- cadcore_ai-0.1.0/README.md +228 -0
- cadcore_ai-0.1.0/cadcore/__init__.py +3 -0
- cadcore_ai-0.1.0/cadcore/__main__.py +6 -0
- cadcore_ai-0.1.0/cadcore/backends/__init__.py +7 -0
- cadcore_ai-0.1.0/cadcore/backends/base.py +35 -0
- cadcore_ai-0.1.0/cadcore/backends/build123d_backend.py +117 -0
- cadcore_ai-0.1.0/cadcore/backends/freecad_backend.py +98 -0
- cadcore_ai-0.1.0/cadcore/cli.py +119 -0
- cadcore_ai-0.1.0/cadcore/config.py +108 -0
- cadcore_ai-0.1.0/cadcore/executor.py +169 -0
- cadcore_ai-0.1.0/cadcore/llm/__init__.py +6 -0
- cadcore_ai-0.1.0/cadcore/llm/client.py +288 -0
- cadcore_ai-0.1.0/cadcore/llm/prompts.py +169 -0
- cadcore_ai-0.1.0/cadcore/pipeline.py +151 -0
- cadcore_ai-0.1.0/cadcore/viewer.py +353 -0
- cadcore_ai-0.1.0/cadcore_ai.egg-info/PKG-INFO +265 -0
- cadcore_ai-0.1.0/cadcore_ai.egg-info/SOURCES.txt +24 -0
- cadcore_ai-0.1.0/cadcore_ai.egg-info/dependency_links.txt +1 -0
- cadcore_ai-0.1.0/cadcore_ai.egg-info/entry_points.txt +2 -0
- cadcore_ai-0.1.0/cadcore_ai.egg-info/requires.txt +15 -0
- cadcore_ai-0.1.0/cadcore_ai.egg-info/top_level.txt +1 -0
- cadcore_ai-0.1.0/pyproject.toml +59 -0
- cadcore_ai-0.1.0/setup.cfg +4 -0
- cadcore_ai-0.1.0/tests/test_pipeline.py +124 -0
cadcore_ai-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kukil Kashyap Borgohain
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cadcore-ai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Standalone Agentic Python CAD generation pipeline
|
|
5
|
+
Author: Kukil Kashyap Borgohain
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/kXborg/CadCore
|
|
8
|
+
Project-URL: Repository, https://github.com/kXborg/CadCore
|
|
9
|
+
Project-URL: Issues, https://github.com/kXborg/CadCore/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/kXborg/CadCore#readme
|
|
11
|
+
Keywords: cad,build123d,freecad,agentic-ai,text-to-cad,3d-printing,opencascade
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Manufacturing
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: build123d>=0.8.0
|
|
24
|
+
Requires-Dist: pydantic>=2.0.0
|
|
25
|
+
Requires-Dist: rich>=13.0.0
|
|
26
|
+
Requires-Dist: typer>=0.9.0
|
|
27
|
+
Requires-Dist: jinja2>=3.1.0
|
|
28
|
+
Provides-Extra: llm
|
|
29
|
+
Requires-Dist: google-genai>=0.1.0; extra == "llm"
|
|
30
|
+
Requires-Dist: openai>=1.0.0; extra == "llm"
|
|
31
|
+
Requires-Dist: anthropic>=0.18.0; extra == "llm"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
35
|
+
Requires-Dist: trimesh>=4.0.0; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# CadCore
|
|
39
|
+
|
|
40
|
+
CadCore is a standalone agentic pipeline that translates natural language descriptions into parametric 3D CAD models (`.step`, `.stl`), 2D technical drawings (`.svg`), and interactive 3D Web Viewers (`.html`) with an automated self-healing execution loop.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Architecture
|
|
45
|
+
|
|
46
|
+
```mermaid
|
|
47
|
+
flowchart TD
|
|
48
|
+
User("User Prompt<br/>(Natural Language)") --> LLM("LLM Planner & Coder<br/>(Gemini / OpenAI / Anthropic / Ollama)")
|
|
49
|
+
LLM --> Code("Generated Parametric Script<br/>(build123d / FreeCAD)")
|
|
50
|
+
Code --> Sandbox("Subprocess Sandbox Executor")
|
|
51
|
+
Sandbox --> Validate{"Execution & Solid Validation"}
|
|
52
|
+
|
|
53
|
+
Validate -- "Error / Invalid Solid" --> Heal("Self-Healing Feedback<br/>(Traceback + Code Context)")
|
|
54
|
+
Heal -->|"Retry (Up to max_retries)"| LLM
|
|
55
|
+
|
|
56
|
+
Validate -- "Success" --> Exporter("Multi-Format Exporter")
|
|
57
|
+
|
|
58
|
+
Exporter --> STEP("model.step<br/>(Standard B-Rep CAD)")
|
|
59
|
+
Exporter --> STL("model.stl<br/>(3D Printing Mesh)")
|
|
60
|
+
Exporter --> SVG("drawing.svg<br/>(2D Technical Drawing)")
|
|
61
|
+
Exporter --> HTML("viewer.html<br/>(Interactive 3D Web Viewer)")
|
|
62
|
+
Exporter --> META("cadcore_meta.json<br/>(Metrics & Metadata)")
|
|
63
|
+
|
|
64
|
+
classDef prompt fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#f8fafc;
|
|
65
|
+
classDef llm fill:#1e1b4b,stroke:#818cf8,stroke-width:2px,color:#f8fafc;
|
|
66
|
+
classDef script fill:#0f172a,stroke:#94a3b8,stroke-width:2px,color:#f8fafc;
|
|
67
|
+
classDef decision fill:#172554,stroke:#60a5fa,stroke-width:2px,color:#f8fafc;
|
|
68
|
+
classDef healing fill:#451a03,stroke:#f59e0b,stroke-width:2px,color:#f8fafc;
|
|
69
|
+
classDef exporter fill:#064e3b,stroke:#10b981,stroke-width:2px,color:#f8fafc;
|
|
70
|
+
classDef artifact fill:#0c4a6e,stroke:#38bdf8,stroke-width:1.5px,color:#f8fafc;
|
|
71
|
+
|
|
72
|
+
class User prompt;
|
|
73
|
+
class LLM llm;
|
|
74
|
+
class Code,Sandbox script;
|
|
75
|
+
class Validate decision;
|
|
76
|
+
class Heal healing;
|
|
77
|
+
class Exporter exporter;
|
|
78
|
+
class STEP,STL,SVG,HTML,META artifact;
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Key Features
|
|
84
|
+
|
|
85
|
+
* **Multi-Backend CAD Support**:
|
|
86
|
+
* **`build123d` (Default)**: Modern OpenCASCADE-based Pythonic CAD engine. Fast, headless, and runs in pure Python.
|
|
87
|
+
* **`freecad`**: Executes native FreeCAD scripts headlessly via `FreeCADCmd`.
|
|
88
|
+
* **Pluggable LLM Providers**: Built-in support for Google Gemini (`google-genai`), OpenAI / Ollama (`openai`), Anthropic (`anthropic`), and offline `mock` testing.
|
|
89
|
+
* **Self-Healing Loop**: Captures runtime errors, missing imports, or invalid topology, then sends tracebacks back to the LLM for automatic correction.
|
|
90
|
+
* **Multi-Format Export**: Generates STEP files for CAD exchange, STL meshes for 3D printing, SVG for 2D engineering drawings, and standalone HTML 3D viewers.
|
|
91
|
+
* **Geometric Validation**: Analyzes exported meshes for volume, bounding box dimensions, and watertight manifold status.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Installation
|
|
96
|
+
|
|
97
|
+
### Prerequisites
|
|
98
|
+
* Python 3.10, 3.11, or 3.12 (Python 3.11 recommended).
|
|
99
|
+
* Optional: FreeCAD 0.20+ if using the FreeCAD backend.
|
|
100
|
+
|
|
101
|
+
### 1. From PyPI
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip install cadcore-ai
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 2. From Source
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Clone repository
|
|
111
|
+
git clone https://github.com/kXborg/CadCore.git
|
|
112
|
+
cd CadCore
|
|
113
|
+
|
|
114
|
+
# Create and activate virtual environment
|
|
115
|
+
python -m venv .venv
|
|
116
|
+
.\.venv\Scripts\activate # On Windows
|
|
117
|
+
# source .venv/bin/activate # On Linux/macOS
|
|
118
|
+
|
|
119
|
+
# Install dependencies in editable mode
|
|
120
|
+
pip install -e .
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Configuration
|
|
126
|
+
|
|
127
|
+
Set API keys via environment variables according to your provider:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Google Gemini (Default)
|
|
131
|
+
export GEMINI_API_KEY="your-gemini-key"
|
|
132
|
+
|
|
133
|
+
# OpenAI / Compatible
|
|
134
|
+
export OPENAI_API_KEY="your-openai-key"
|
|
135
|
+
|
|
136
|
+
# Anthropic
|
|
137
|
+
export ANTHROPIC_API_KEY="your-anthropic-key"
|
|
138
|
+
|
|
139
|
+
# Ollama / Local LLM (Optional URL override)
|
|
140
|
+
export OLLAMA_BASE_URL="http://localhost:11434/v1"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## CLI Usage
|
|
146
|
+
|
|
147
|
+
CadCore provides a CLI interface through `cadcore` or `python -m cadcore`.
|
|
148
|
+
|
|
149
|
+
### 1. Check Supported Backends
|
|
150
|
+
```bash
|
|
151
|
+
python -m cadcore list-backends
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 2. Generate a CAD Part
|
|
155
|
+
```bash
|
|
156
|
+
python -m cadcore generate "NEMA 17 stepper motor mount plate 42x42mm with central 22mm hole and 4 corner M3 holes at 31mm spacing" --output ./outputs/nema17
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 3. Generate and Open Interactive 3D Viewer
|
|
160
|
+
```bash
|
|
161
|
+
python -m cadcore generate "L-bracket 50x50x25mm with 4mm thickness and 2 M5 mounting holes on each leg" --output ./outputs/l_bracket --view
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 4. CLI Options Reference
|
|
165
|
+
```text
|
|
166
|
+
Arguments:
|
|
167
|
+
PROMPT Natural language description of the CAD model.
|
|
168
|
+
|
|
169
|
+
Options:
|
|
170
|
+
-o, --output PATH Directory to save generated artifacts. [default: ./output]
|
|
171
|
+
-b, --backend [build123d|freecad]
|
|
172
|
+
CAD engine backend. [default: build123d]
|
|
173
|
+
-p, --provider [gemini|openai|anthropic|ollama|mock]
|
|
174
|
+
LLM Provider. [default: gemini]
|
|
175
|
+
-m, --model TEXT LLM model identifier override.
|
|
176
|
+
-r, --retries INTEGER Maximum self-healing retry attempts. [default: 3]
|
|
177
|
+
-v, --view Open 3D interactive viewer in browser upon completion.
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Python API Usage
|
|
183
|
+
|
|
184
|
+
CadCore can be embedded directly into Python workflows:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from pathlib import Path
|
|
188
|
+
from cadcore.config import PipelineConfig, CADBackendType, LLMConfig, LLMProvider
|
|
189
|
+
from cadcore.pipeline import CADAgentPipeline
|
|
190
|
+
|
|
191
|
+
# Configure pipeline
|
|
192
|
+
config = PipelineConfig(
|
|
193
|
+
backend=CADBackendType.BUILD123D,
|
|
194
|
+
output_dir=Path("./outputs/flange"),
|
|
195
|
+
max_retries=2,
|
|
196
|
+
export_step=True,
|
|
197
|
+
export_stl=True,
|
|
198
|
+
export_svg=True,
|
|
199
|
+
generate_viewer=True,
|
|
200
|
+
llm=LLMConfig(
|
|
201
|
+
provider=LLMProvider.GEMINI,
|
|
202
|
+
model="gemini-2.5-flash",
|
|
203
|
+
),
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Run pipeline
|
|
207
|
+
pipeline = CADAgentPipeline(config)
|
|
208
|
+
result = pipeline.run("Round pipe flange 60mm OD, 30mm ID, 8mm thickness with 4 bolt holes of 5mm diameter")
|
|
209
|
+
|
|
210
|
+
if result.success:
|
|
211
|
+
print(f"Generated successfully in {result.execution_time_seconds:.2f}s ({result.iterations} attempt(s))")
|
|
212
|
+
print(f"STEP: {result.artifacts['model.step']}")
|
|
213
|
+
print(f"STL: {result.artifacts['model.stl']}")
|
|
214
|
+
print(f"SVG: {result.artifacts['drawing.svg']}")
|
|
215
|
+
print(f"3D Viewer: {result.artifacts['viewer.html']}")
|
|
216
|
+
print(f"Volume: {result.metrics.get('volume_mm3')} mm³")
|
|
217
|
+
else:
|
|
218
|
+
print(f"Generation failed: {result.error_message}")
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Repository Structure
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
CadCore/
|
|
227
|
+
├── cadcore/
|
|
228
|
+
│ ├── __init__.py
|
|
229
|
+
│ ├── __main__.py # Entrypoint for python -m cadcore
|
|
230
|
+
│ ├── cli.py # Typer & Rich CLI
|
|
231
|
+
│ ├── config.py # Configuration & provider settings
|
|
232
|
+
│ ├── executor.py # Subprocess sandbox & trimesh validation
|
|
233
|
+
│ ├── pipeline.py # Orchestrator & self-healing loop
|
|
234
|
+
│ ├── viewer.py # Three.js 3D/2D HTML viewer generator
|
|
235
|
+
│ ├── backends/
|
|
236
|
+
│ │ ├── base.py # Abstract CADBackend
|
|
237
|
+
│ │ ├── build123d_backend.py
|
|
238
|
+
│ │ └── freecad_backend.py
|
|
239
|
+
│ └── llm/
|
|
240
|
+
│ ├── client.py # Pluggable LLM clients
|
|
241
|
+
│ └── prompts.py # CAD prompts & few-shot examples
|
|
242
|
+
├── examples/
|
|
243
|
+
│ └── basic_pipeline_demo.py
|
|
244
|
+
├── tests/
|
|
245
|
+
│ └── test_pipeline.py
|
|
246
|
+
├── pyproject.toml
|
|
247
|
+
├── requirements.txt
|
|
248
|
+
└── README.md
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Testing
|
|
254
|
+
|
|
255
|
+
Run the test suite with `pytest`:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
pytest tests/ -v
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
MIT License.
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# CadCore
|
|
2
|
+
|
|
3
|
+
CadCore is a standalone agentic pipeline that translates natural language descriptions into parametric 3D CAD models (`.step`, `.stl`), 2D technical drawings (`.svg`), and interactive 3D Web Viewers (`.html`) with an automated self-healing execution loop.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
flowchart TD
|
|
11
|
+
User("User Prompt<br/>(Natural Language)") --> LLM("LLM Planner & Coder<br/>(Gemini / OpenAI / Anthropic / Ollama)")
|
|
12
|
+
LLM --> Code("Generated Parametric Script<br/>(build123d / FreeCAD)")
|
|
13
|
+
Code --> Sandbox("Subprocess Sandbox Executor")
|
|
14
|
+
Sandbox --> Validate{"Execution & Solid Validation"}
|
|
15
|
+
|
|
16
|
+
Validate -- "Error / Invalid Solid" --> Heal("Self-Healing Feedback<br/>(Traceback + Code Context)")
|
|
17
|
+
Heal -->|"Retry (Up to max_retries)"| LLM
|
|
18
|
+
|
|
19
|
+
Validate -- "Success" --> Exporter("Multi-Format Exporter")
|
|
20
|
+
|
|
21
|
+
Exporter --> STEP("model.step<br/>(Standard B-Rep CAD)")
|
|
22
|
+
Exporter --> STL("model.stl<br/>(3D Printing Mesh)")
|
|
23
|
+
Exporter --> SVG("drawing.svg<br/>(2D Technical Drawing)")
|
|
24
|
+
Exporter --> HTML("viewer.html<br/>(Interactive 3D Web Viewer)")
|
|
25
|
+
Exporter --> META("cadcore_meta.json<br/>(Metrics & Metadata)")
|
|
26
|
+
|
|
27
|
+
classDef prompt fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#f8fafc;
|
|
28
|
+
classDef llm fill:#1e1b4b,stroke:#818cf8,stroke-width:2px,color:#f8fafc;
|
|
29
|
+
classDef script fill:#0f172a,stroke:#94a3b8,stroke-width:2px,color:#f8fafc;
|
|
30
|
+
classDef decision fill:#172554,stroke:#60a5fa,stroke-width:2px,color:#f8fafc;
|
|
31
|
+
classDef healing fill:#451a03,stroke:#f59e0b,stroke-width:2px,color:#f8fafc;
|
|
32
|
+
classDef exporter fill:#064e3b,stroke:#10b981,stroke-width:2px,color:#f8fafc;
|
|
33
|
+
classDef artifact fill:#0c4a6e,stroke:#38bdf8,stroke-width:1.5px,color:#f8fafc;
|
|
34
|
+
|
|
35
|
+
class User prompt;
|
|
36
|
+
class LLM llm;
|
|
37
|
+
class Code,Sandbox script;
|
|
38
|
+
class Validate decision;
|
|
39
|
+
class Heal healing;
|
|
40
|
+
class Exporter exporter;
|
|
41
|
+
class STEP,STL,SVG,HTML,META artifact;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Key Features
|
|
47
|
+
|
|
48
|
+
* **Multi-Backend CAD Support**:
|
|
49
|
+
* **`build123d` (Default)**: Modern OpenCASCADE-based Pythonic CAD engine. Fast, headless, and runs in pure Python.
|
|
50
|
+
* **`freecad`**: Executes native FreeCAD scripts headlessly via `FreeCADCmd`.
|
|
51
|
+
* **Pluggable LLM Providers**: Built-in support for Google Gemini (`google-genai`), OpenAI / Ollama (`openai`), Anthropic (`anthropic`), and offline `mock` testing.
|
|
52
|
+
* **Self-Healing Loop**: Captures runtime errors, missing imports, or invalid topology, then sends tracebacks back to the LLM for automatic correction.
|
|
53
|
+
* **Multi-Format Export**: Generates STEP files for CAD exchange, STL meshes for 3D printing, SVG for 2D engineering drawings, and standalone HTML 3D viewers.
|
|
54
|
+
* **Geometric Validation**: Analyzes exported meshes for volume, bounding box dimensions, and watertight manifold status.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
### Prerequisites
|
|
61
|
+
* Python 3.10, 3.11, or 3.12 (Python 3.11 recommended).
|
|
62
|
+
* Optional: FreeCAD 0.20+ if using the FreeCAD backend.
|
|
63
|
+
|
|
64
|
+
### 1. From PyPI
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install cadcore-ai
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 2. From Source
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Clone repository
|
|
74
|
+
git clone https://github.com/kXborg/CadCore.git
|
|
75
|
+
cd CadCore
|
|
76
|
+
|
|
77
|
+
# Create and activate virtual environment
|
|
78
|
+
python -m venv .venv
|
|
79
|
+
.\.venv\Scripts\activate # On Windows
|
|
80
|
+
# source .venv/bin/activate # On Linux/macOS
|
|
81
|
+
|
|
82
|
+
# Install dependencies in editable mode
|
|
83
|
+
pip install -e .
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Configuration
|
|
89
|
+
|
|
90
|
+
Set API keys via environment variables according to your provider:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Google Gemini (Default)
|
|
94
|
+
export GEMINI_API_KEY="your-gemini-key"
|
|
95
|
+
|
|
96
|
+
# OpenAI / Compatible
|
|
97
|
+
export OPENAI_API_KEY="your-openai-key"
|
|
98
|
+
|
|
99
|
+
# Anthropic
|
|
100
|
+
export ANTHROPIC_API_KEY="your-anthropic-key"
|
|
101
|
+
|
|
102
|
+
# Ollama / Local LLM (Optional URL override)
|
|
103
|
+
export OLLAMA_BASE_URL="http://localhost:11434/v1"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## CLI Usage
|
|
109
|
+
|
|
110
|
+
CadCore provides a CLI interface through `cadcore` or `python -m cadcore`.
|
|
111
|
+
|
|
112
|
+
### 1. Check Supported Backends
|
|
113
|
+
```bash
|
|
114
|
+
python -m cadcore list-backends
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 2. Generate a CAD Part
|
|
118
|
+
```bash
|
|
119
|
+
python -m cadcore generate "NEMA 17 stepper motor mount plate 42x42mm with central 22mm hole and 4 corner M3 holes at 31mm spacing" --output ./outputs/nema17
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 3. Generate and Open Interactive 3D Viewer
|
|
123
|
+
```bash
|
|
124
|
+
python -m cadcore generate "L-bracket 50x50x25mm with 4mm thickness and 2 M5 mounting holes on each leg" --output ./outputs/l_bracket --view
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 4. CLI Options Reference
|
|
128
|
+
```text
|
|
129
|
+
Arguments:
|
|
130
|
+
PROMPT Natural language description of the CAD model.
|
|
131
|
+
|
|
132
|
+
Options:
|
|
133
|
+
-o, --output PATH Directory to save generated artifacts. [default: ./output]
|
|
134
|
+
-b, --backend [build123d|freecad]
|
|
135
|
+
CAD engine backend. [default: build123d]
|
|
136
|
+
-p, --provider [gemini|openai|anthropic|ollama|mock]
|
|
137
|
+
LLM Provider. [default: gemini]
|
|
138
|
+
-m, --model TEXT LLM model identifier override.
|
|
139
|
+
-r, --retries INTEGER Maximum self-healing retry attempts. [default: 3]
|
|
140
|
+
-v, --view Open 3D interactive viewer in browser upon completion.
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Python API Usage
|
|
146
|
+
|
|
147
|
+
CadCore can be embedded directly into Python workflows:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from pathlib import Path
|
|
151
|
+
from cadcore.config import PipelineConfig, CADBackendType, LLMConfig, LLMProvider
|
|
152
|
+
from cadcore.pipeline import CADAgentPipeline
|
|
153
|
+
|
|
154
|
+
# Configure pipeline
|
|
155
|
+
config = PipelineConfig(
|
|
156
|
+
backend=CADBackendType.BUILD123D,
|
|
157
|
+
output_dir=Path("./outputs/flange"),
|
|
158
|
+
max_retries=2,
|
|
159
|
+
export_step=True,
|
|
160
|
+
export_stl=True,
|
|
161
|
+
export_svg=True,
|
|
162
|
+
generate_viewer=True,
|
|
163
|
+
llm=LLMConfig(
|
|
164
|
+
provider=LLMProvider.GEMINI,
|
|
165
|
+
model="gemini-2.5-flash",
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Run pipeline
|
|
170
|
+
pipeline = CADAgentPipeline(config)
|
|
171
|
+
result = pipeline.run("Round pipe flange 60mm OD, 30mm ID, 8mm thickness with 4 bolt holes of 5mm diameter")
|
|
172
|
+
|
|
173
|
+
if result.success:
|
|
174
|
+
print(f"Generated successfully in {result.execution_time_seconds:.2f}s ({result.iterations} attempt(s))")
|
|
175
|
+
print(f"STEP: {result.artifacts['model.step']}")
|
|
176
|
+
print(f"STL: {result.artifacts['model.stl']}")
|
|
177
|
+
print(f"SVG: {result.artifacts['drawing.svg']}")
|
|
178
|
+
print(f"3D Viewer: {result.artifacts['viewer.html']}")
|
|
179
|
+
print(f"Volume: {result.metrics.get('volume_mm3')} mm³")
|
|
180
|
+
else:
|
|
181
|
+
print(f"Generation failed: {result.error_message}")
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Repository Structure
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
CadCore/
|
|
190
|
+
├── cadcore/
|
|
191
|
+
│ ├── __init__.py
|
|
192
|
+
│ ├── __main__.py # Entrypoint for python -m cadcore
|
|
193
|
+
│ ├── cli.py # Typer & Rich CLI
|
|
194
|
+
│ ├── config.py # Configuration & provider settings
|
|
195
|
+
│ ├── executor.py # Subprocess sandbox & trimesh validation
|
|
196
|
+
│ ├── pipeline.py # Orchestrator & self-healing loop
|
|
197
|
+
│ ├── viewer.py # Three.js 3D/2D HTML viewer generator
|
|
198
|
+
│ ├── backends/
|
|
199
|
+
│ │ ├── base.py # Abstract CADBackend
|
|
200
|
+
│ │ ├── build123d_backend.py
|
|
201
|
+
│ │ └── freecad_backend.py
|
|
202
|
+
│ └── llm/
|
|
203
|
+
│ ├── client.py # Pluggable LLM clients
|
|
204
|
+
│ └── prompts.py # CAD prompts & few-shot examples
|
|
205
|
+
├── examples/
|
|
206
|
+
│ └── basic_pipeline_demo.py
|
|
207
|
+
├── tests/
|
|
208
|
+
│ └── test_pipeline.py
|
|
209
|
+
├── pyproject.toml
|
|
210
|
+
├── requirements.txt
|
|
211
|
+
└── README.md
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Testing
|
|
217
|
+
|
|
218
|
+
Run the test suite with `pytest`:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
pytest tests/ -v
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT License.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Base class for CAD engine backends."""
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List, Tuple
|
|
6
|
+
|
|
7
|
+
from cadcore.config import PipelineConfig
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CADBackend(abc.ABC):
|
|
11
|
+
"""Abstract interface for CAD modeling backends."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, config: PipelineConfig):
|
|
14
|
+
self.config = config
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
@abc.abstractmethod
|
|
18
|
+
def name(self) -> str:
|
|
19
|
+
"""Name of the backend engine."""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
@abc.abstractmethod
|
|
23
|
+
def validate_environment(self) -> Tuple[bool, str]:
|
|
24
|
+
"""Check if the backend runtime/dependencies are available."""
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
@abc.abstractmethod
|
|
28
|
+
def prepare_script(self, raw_code: str, output_dir: Path) -> str:
|
|
29
|
+
"""Post-process or wrap generated code to guarantee standard output paths."""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
@abc.abstractmethod
|
|
33
|
+
def get_execution_command(self, script_path: Path) -> List[str]:
|
|
34
|
+
"""Get the subprocess command line to run the generated script."""
|
|
35
|
+
pass
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Build123d CAD backend implementation."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import List, Tuple
|
|
8
|
+
|
|
9
|
+
from cadcore.backends.base import CADBackend
|
|
10
|
+
from cadcore.config import PipelineConfig
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Build123dBackend(CADBackend):
|
|
14
|
+
"""Build123d parametric OpenCASCADE backend."""
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def name(self) -> str:
|
|
18
|
+
return "build123d"
|
|
19
|
+
|
|
20
|
+
def _get_python_path(self) -> str:
|
|
21
|
+
"""Resolve python interpreter path."""
|
|
22
|
+
if self.config.python_executable:
|
|
23
|
+
return self.config.python_executable
|
|
24
|
+
|
|
25
|
+
# Check local .venv
|
|
26
|
+
venv_py = Path.cwd() / ".venv" / "Scripts" / "python.exe"
|
|
27
|
+
if venv_py.exists():
|
|
28
|
+
return str(venv_py)
|
|
29
|
+
|
|
30
|
+
venv_py_posix = Path.cwd() / ".venv" / "bin" / "python"
|
|
31
|
+
if venv_py_posix.exists():
|
|
32
|
+
return str(venv_py_posix)
|
|
33
|
+
|
|
34
|
+
return sys.executable
|
|
35
|
+
|
|
36
|
+
def validate_environment(self) -> Tuple[bool, str]:
|
|
37
|
+
"""Verify build123d is installed in the target Python interpreter."""
|
|
38
|
+
import subprocess
|
|
39
|
+
python_exe = self._get_python_path()
|
|
40
|
+
try:
|
|
41
|
+
res = subprocess.run(
|
|
42
|
+
[python_exe, "-c", "import build123d; print(build123d.__version__)"],
|
|
43
|
+
capture_output=True,
|
|
44
|
+
text=True,
|
|
45
|
+
timeout=30,
|
|
46
|
+
)
|
|
47
|
+
if res.returncode == 0:
|
|
48
|
+
return True, f"build123d v{res.stdout.strip()} ready at {python_exe}"
|
|
49
|
+
return False, f"Failed to import build123d: {res.stderr.strip()}"
|
|
50
|
+
except Exception as e:
|
|
51
|
+
return False, f"Python execution error: {e}"
|
|
52
|
+
|
|
53
|
+
def prepare_script(self, raw_code: str, output_dir: Path) -> str:
|
|
54
|
+
"""Inject output directory and automatic fallback exporters if missing."""
|
|
55
|
+
out_str = str(output_dir.resolve()).replace("\\", "/")
|
|
56
|
+
|
|
57
|
+
# Sanitize any duplicate output_dir = Path(...) definitions from LLM script
|
|
58
|
+
cleaned_code = re.sub(r'output_dir\s*=\s*Path\([\'"][^\'"]*[\'"]\)', '# output_dir configured by CadCore', raw_code)
|
|
59
|
+
|
|
60
|
+
# Check if export statements are already present in code
|
|
61
|
+
has_step_export = "export_step" in cleaned_code
|
|
62
|
+
has_stl_export = "export_stl" in cleaned_code
|
|
63
|
+
has_svg_export = "ExportSVG" in cleaned_code
|
|
64
|
+
|
|
65
|
+
header = f"""# CadCore Auto-generated Script
|
|
66
|
+
import sys
|
|
67
|
+
from pathlib import Path
|
|
68
|
+
output_dir = Path(r"{out_str}")
|
|
69
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
# Wrap with safety fallback exporters
|
|
73
|
+
footer = f"""
|
|
74
|
+
# CadCore Automatic Export Fallback
|
|
75
|
+
try:
|
|
76
|
+
if 'result_part' in locals() and result_part is not None:
|
|
77
|
+
target_solid = result_part
|
|
78
|
+
elif 'part' in locals() and hasattr(part, 'part'):
|
|
79
|
+
target_solid = part.part
|
|
80
|
+
elif 'bracket' in locals() and hasattr(bracket, 'part'):
|
|
81
|
+
target_solid = bracket.part
|
|
82
|
+
elif 'flange' in locals() and hasattr(flange, 'part'):
|
|
83
|
+
target_solid = flange.part
|
|
84
|
+
else:
|
|
85
|
+
# Search for any Compound/Solid in locals
|
|
86
|
+
from build123d import Compound, Solid, Part
|
|
87
|
+
candidates = [v for v in locals().values() if isinstance(v, (Compound, Solid, Part))]
|
|
88
|
+
target_solid = candidates[-1] if candidates else None
|
|
89
|
+
|
|
90
|
+
if target_solid is not None:
|
|
91
|
+
step_path = output_dir / "model.step"
|
|
92
|
+
stl_path = output_dir / "model.stl"
|
|
93
|
+
svg_path = output_dir / "drawing.svg"
|
|
94
|
+
|
|
95
|
+
if not step_path.exists() or {not has_step_export}:
|
|
96
|
+
export_step(target_solid, str(step_path))
|
|
97
|
+
if not stl_path.exists() or {not has_stl_export}:
|
|
98
|
+
export_stl(target_solid, str(stl_path))
|
|
99
|
+
if not svg_path.exists() or {not has_svg_export}:
|
|
100
|
+
try:
|
|
101
|
+
visible, hidden = target_solid.project_to_viewport((1, 1, 1))
|
|
102
|
+
exporter = ExportSVG(scale=2.0)
|
|
103
|
+
exporter.add_layer("Visible", fill_color=Color("white"), line_color=Color("black"), line_weight=0.35)
|
|
104
|
+
exporter.add_shape(visible, layer="Visible")
|
|
105
|
+
exporter.write(str(svg_path))
|
|
106
|
+
except Exception as svg_err:
|
|
107
|
+
print(f"[Warning] SVG export failed: {{svg_err}}")
|
|
108
|
+
print(f"[CadCore] Successfully exported CAD artifacts to {{output_dir}}")
|
|
109
|
+
else:
|
|
110
|
+
raise RuntimeError("No valid 3D Solid or Compound was created in the script namespace. Ensure you build a solid (e.g. using BuildPart) and assign result_part = ...")
|
|
111
|
+
except Exception as export_err:
|
|
112
|
+
raise export_err
|
|
113
|
+
"""
|
|
114
|
+
return f"{header}\n{cleaned_code}\n{footer}"
|
|
115
|
+
|
|
116
|
+
def get_execution_command(self, script_path: Path) -> List[str]:
|
|
117
|
+
return [self._get_python_path(), str(script_path.resolve())]
|