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.
@@ -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,3 @@
1
+ """CadCore: Standalone Agentic CAD Generation Pipeline."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,6 @@
1
+ """Main entry point when invoked as python -m cadcore."""
2
+
3
+ from cadcore.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,7 @@
1
+ """CAD Backends for CadCore."""
2
+
3
+ from .base import CADBackend
4
+ from .build123d_backend import Build123dBackend
5
+ from .freecad_backend import FreeCADBackend
6
+
7
+ __all__ = ["CADBackend", "Build123dBackend", "FreeCADBackend"]
@@ -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())]