solidworks-mcp-python 1.5.0__py3-none-any.whl
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.
- solidworks_mcp/__init__.py +57 -0
- solidworks_mcp/adapters/__init__.py +40 -0
- solidworks_mcp/adapters/base.py +1882 -0
- solidworks_mcp/adapters/circuit_breaker.py +2052 -0
- solidworks_mcp/adapters/com_executor.py +252 -0
- solidworks_mcp/adapters/complexity_analyzer.py +272 -0
- solidworks_mcp/adapters/connection_pool.py +1884 -0
- solidworks_mcp/adapters/factory.py +366 -0
- solidworks_mcp/adapters/intelligent_router.py +218 -0
- solidworks_mcp/adapters/mock_adapter.py +4016 -0
- solidworks_mcp/adapters/pywin32_adapter.py +2842 -0
- solidworks_mcp/adapters/solidworks/__init__.py +17 -0
- solidworks_mcp/adapters/solidworks/bitmap.py +180 -0
- solidworks_mcp/adapters/solidworks/com_utils.py +56 -0
- solidworks_mcp/adapters/solidworks/dimension_layout.py +753 -0
- solidworks_mcp/adapters/solidworks/documentation.py +1476 -0
- solidworks_mcp/adapters/solidworks/drawing_standard.py +1993 -0
- solidworks_mcp/adapters/solidworks/face_selection.py +392 -0
- solidworks_mcp/adapters/solidworks/features.py +3362 -0
- solidworks_mcp/adapters/solidworks/io.py +3649 -0
- solidworks_mcp/adapters/solidworks/rebuild_diagnostics.py +269 -0
- solidworks_mcp/adapters/solidworks/rebuild_inspector.py +146 -0
- solidworks_mcp/adapters/solidworks/selection.py +308 -0
- solidworks_mcp/adapters/solidworks/sketch.py +2545 -0
- solidworks_mcp/adapters/sw_feature_errors.py +66 -0
- solidworks_mcp/adapters/sw_type_info.py +317 -0
- solidworks_mcp/adapters/vba_adapter.py +318 -0
- solidworks_mcp/adapters/vba_macro_executor.py +215 -0
- solidworks_mcp/agents/__init__.py +68 -0
- solidworks_mcp/agents/design_knowledge/assembly_patterns.md +127 -0
- solidworks_mcp/agents/design_knowledge/hinges.md +118 -0
- solidworks_mcp/agents/design_knowledge/snap_fits.md +93 -0
- solidworks_mcp/agents/design_knowledge/solidworks_basics.md +3 -0
- solidworks_mcp/agents/design_knowledge/tolerancing.md +128 -0
- solidworks_mcp/agents/harness.py +216 -0
- solidworks_mcp/agents/history_db.py +1508 -0
- solidworks_mcp/agents/retrieval_index.py +164 -0
- solidworks_mcp/agents/schemas.py +165 -0
- solidworks_mcp/agents/smoke_test.py +285 -0
- solidworks_mcp/agents/soc_exporter.py +650 -0
- solidworks_mcp/agents/soc_pickup.py +332 -0
- solidworks_mcp/agents/soc_rewind.py +221 -0
- solidworks_mcp/agents/vector_rag.py +665 -0
- solidworks_mcp/cache/__init__.py +5 -0
- solidworks_mcp/cache/response_cache.py +164 -0
- solidworks_mcp/config.py +729 -0
- solidworks_mcp/exceptions.py +37 -0
- solidworks_mcp/security/__init__.py +68 -0
- solidworks_mcp/security/auth.py +128 -0
- solidworks_mcp/security/cors.py +27 -0
- solidworks_mcp/security/rate_limiting.py +90 -0
- solidworks_mcp/security/runtime.py +155 -0
- solidworks_mcp/server.py +777 -0
- solidworks_mcp/server_cli_fixed.py +120 -0
- solidworks_mcp/setup/__init__.py +1 -0
- solidworks_mcp/setup/cli.py +399 -0
- solidworks_mcp/setup/clients.py +157 -0
- solidworks_mcp/setup/config_file.py +230 -0
- solidworks_mcp/setup/handshake.py +202 -0
- solidworks_mcp/setup/launcher.py +102 -0
- solidworks_mcp/setup/legacy.py +100 -0
- solidworks_mcp/setup/registration.py +193 -0
- solidworks_mcp/setup/solidworks.py +49 -0
- solidworks_mcp/tools/__init__.py +64 -0
- solidworks_mcp/tools/analysis.py +723 -0
- solidworks_mcp/tools/automation.py +578 -0
- solidworks_mcp/tools/docs_discovery.py +1476 -0
- solidworks_mcp/tools/drawing.py +1015 -0
- solidworks_mcp/tools/drawing_analysis.py +456 -0
- solidworks_mcp/tools/drawing_automation.py +627 -0
- solidworks_mcp/tools/engineering_data.py +466 -0
- solidworks_mcp/tools/error_classifier.py +232 -0
- solidworks_mcp/tools/export.py +833 -0
- solidworks_mcp/tools/file_facts.py +40 -0
- solidworks_mcp/tools/file_management.py +1473 -0
- solidworks_mcp/tools/input_compat.py +17 -0
- solidworks_mcp/tools/macro_recording.py +464 -0
- solidworks_mcp/tools/modeling.py +2360 -0
- solidworks_mcp/tools/sketching.py +1422 -0
- solidworks_mcp/tools/template_management.py +367 -0
- solidworks_mcp/tools/tool_boundary.py +280 -0
- solidworks_mcp/tools/vba_generation.py +1145 -0
- solidworks_mcp/ui/__init__.py +9 -0
- solidworks_mcp/ui/local_llm.py +725 -0
- solidworks_mcp/ui/routers/__init__.py +10 -0
- solidworks_mcp/ui/routers/checkpoint.py +24 -0
- solidworks_mcp/ui/routers/docs.py +47 -0
- solidworks_mcp/ui/routers/llm.py +83 -0
- solidworks_mcp/ui/routers/local_model.py +69 -0
- solidworks_mcp/ui/routers/model.py +75 -0
- solidworks_mcp/ui/routers/preview.py +38 -0
- solidworks_mcp/ui/routers/session.py +161 -0
- solidworks_mcp/ui/routers/viewer.py +154 -0
- solidworks_mcp/ui/schemas.py +502 -0
- solidworks_mcp/ui/server.py +298 -0
- solidworks_mcp/ui/service.py +481 -0
- solidworks_mcp/ui/services/__init__.py +136 -0
- solidworks_mcp/ui/services/_utils.py +958 -0
- solidworks_mcp/ui/services/checkpoint_service.py +1110 -0
- solidworks_mcp/ui/services/docs_service.py +282 -0
- solidworks_mcp/ui/services/llm_service.py +956 -0
- solidworks_mcp/ui/services/model_service.py +459 -0
- solidworks_mcp/ui/services/parameter_repair_service.py +314 -0
- solidworks_mcp/ui/services/preview_service.py +489 -0
- solidworks_mcp/ui/services/session_service.py +1389 -0
- solidworks_mcp/utils/__init__.py +9 -0
- solidworks_mcp/utils/feature_tree_classifier.py +342 -0
- solidworks_mcp/utils/logging.py +76 -0
- solidworks_mcp/utils/validation.py +77 -0
- solidworks_mcp/version.py +8 -0
- solidworks_mcp_python-1.5.0.dist-info/METADATA +410 -0
- solidworks_mcp_python-1.5.0.dist-info/RECORD +115 -0
- solidworks_mcp_python-1.5.0.dist-info/WHEEL +4 -0
- solidworks_mcp_python-1.5.0.dist-info/entry_points.txt +3 -0
- solidworks_mcp_python-1.5.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""FastAPI backend for the Prefab CAD assistant dashboard.
|
|
2
|
+
|
|
3
|
+
REST API endpoints for dashboard actions: - GET /api/ui/state - Hydrate UI with current
|
|
4
|
+
session state - POST /api/ui/brief/approve - Accept user goal - POST /api/ui/clarify -
|
|
5
|
+
Call GitHub Copilot to generate Q&A - POST /api/ui/family/inspect - Call GitHub Copilot
|
|
6
|
+
to classify design family - POST /api/ui/family/accept - Accept proposed family - POST
|
|
7
|
+
/api/ui/checkpoints/execute-next - Execute next checkpoint (adapter-backed; unsupported
|
|
8
|
+
tools marked MOCKED) - POST /api/ui/preview/refresh - Sync 3D view from SolidWorks
|
|
9
|
+
export_image - POST /api/ui/manual-sync/reconcile - Detect manual edits via snapshot
|
|
10
|
+
diff - GET /previews/* - Serve static PNG preview images - GET /docs - FastAPI OpenAPI
|
|
11
|
+
UI for local endpoint inspection
|
|
12
|
+
|
|
13
|
+
LLM Integration: - Clarify button: GitHub Copilot (default: github:openai/gpt-4.1) -
|
|
14
|
+
Inspect button: GitHub Copilot family classification - Requires: GH_TOKEN or
|
|
15
|
+
GITHUB_API_KEY environment variable with models:read scope
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import time
|
|
22
|
+
from pathlib import Path as FilePath
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
import uvicorn
|
|
26
|
+
from fastapi import FastAPI, Request
|
|
27
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
28
|
+
from fastapi.staticfiles import StaticFiles
|
|
29
|
+
from loguru import logger
|
|
30
|
+
|
|
31
|
+
from .routers import checkpoint as checkpoint_router
|
|
32
|
+
from .routers import docs as docs_router
|
|
33
|
+
from .routers import llm as llm_router
|
|
34
|
+
from .routers import local_model as local_model_router
|
|
35
|
+
from .routers import model as model_router
|
|
36
|
+
from .routers import preview as preview_router
|
|
37
|
+
from .routers import session as session_router
|
|
38
|
+
from .routers import viewer as viewer_router
|
|
39
|
+
from .services import ( # noqa: F401 (re-exported for monkeypatching in tests)
|
|
40
|
+
DEFAULT_API_ORIGIN,
|
|
41
|
+
DEFAULT_SESSION_ID,
|
|
42
|
+
accept_family_choice,
|
|
43
|
+
approve_design_brief,
|
|
44
|
+
build_dashboard_state,
|
|
45
|
+
build_dashboard_trace_payload,
|
|
46
|
+
connect_target_model,
|
|
47
|
+
ensure_preview_dir,
|
|
48
|
+
execute_next_checkpoint,
|
|
49
|
+
ingest_reference_source,
|
|
50
|
+
inspect_family,
|
|
51
|
+
reconcile_manual_edits,
|
|
52
|
+
refresh_preview,
|
|
53
|
+
request_clarifications,
|
|
54
|
+
select_workflow_mode,
|
|
55
|
+
update_ui_preferences,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
UI_LOG_DIR = FilePath(".solidworks_mcp") / "ui_logs"
|
|
59
|
+
UI_HTTP_LOG_FILE = UI_LOG_DIR / "ui_http.log"
|
|
60
|
+
_UI_FILE_SINK_ID: int | None = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _configure_ui_file_logging() -> None:
|
|
64
|
+
"""Build internal configure ui file logging.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
None: None.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
global _UI_FILE_SINK_ID
|
|
71
|
+
if _UI_FILE_SINK_ID is not None:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
UI_LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
75
|
+
_UI_FILE_SINK_ID = logger.add(
|
|
76
|
+
str(UI_HTTP_LOG_FILE),
|
|
77
|
+
level="INFO",
|
|
78
|
+
rotation="10 MB",
|
|
79
|
+
retention="14 days",
|
|
80
|
+
encoding="utf-8",
|
|
81
|
+
enqueue=True,
|
|
82
|
+
filter=lambda record: "[ui." in record["message"],
|
|
83
|
+
format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level:<8} | {message}",
|
|
84
|
+
)
|
|
85
|
+
logger.info(
|
|
86
|
+
"[ui.logging] file sink enabled path={}", str(UI_HTTP_LOG_FILE.resolve())
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _should_log_request(path: str) -> bool:
|
|
91
|
+
"""Build internal should log request.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
path (str): Filesystem path for the operation.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
bool: True if should log request, otherwise False.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
return path == "/api/health" or path.startswith("/api/ui/")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _sanitize_log_payload(value: Any) -> Any:
|
|
104
|
+
"""Build internal sanitize log payload.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
value (Any): The value value.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Any: The result produced by the operation.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
if isinstance(value, dict):
|
|
114
|
+
sanitized: dict[str, Any] = {}
|
|
115
|
+
for key, item in value.items():
|
|
116
|
+
if key == "data" and isinstance(item, str):
|
|
117
|
+
sanitized[key] = f"<omitted base64 payload len={len(item)}>"
|
|
118
|
+
elif key == "uploaded_files" and isinstance(item, list):
|
|
119
|
+
sanitized[key] = [_sanitize_log_payload(entry) for entry in item]
|
|
120
|
+
else:
|
|
121
|
+
sanitized[key] = _sanitize_log_payload(item)
|
|
122
|
+
return sanitized
|
|
123
|
+
if isinstance(value, list):
|
|
124
|
+
return [_sanitize_log_payload(item) for item in value]
|
|
125
|
+
return value
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _decode_request_body(body: bytes) -> Any:
|
|
129
|
+
"""Build internal decode request body.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
body (bytes): The body value.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Any: The result produced by the operation.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
if not body:
|
|
139
|
+
return None
|
|
140
|
+
try:
|
|
141
|
+
parsed = json.loads(body)
|
|
142
|
+
except json.JSONDecodeError:
|
|
143
|
+
return body.decode("utf-8", errors="replace")
|
|
144
|
+
return _sanitize_log_payload(parsed)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
app = FastAPI(
|
|
148
|
+
title="SolidWorks Prefab UI Server",
|
|
149
|
+
version="0.1.0",
|
|
150
|
+
summary="FastAPI backend for the interactive SolidWorks Prefab dashboard.",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
_configure_ui_file_logging()
|
|
154
|
+
|
|
155
|
+
app.add_middleware(
|
|
156
|
+
CORSMiddleware,
|
|
157
|
+
allow_origin_regex=r"https?://(127\.0\.0\.1|localhost):\d+$",
|
|
158
|
+
allow_methods=["*"],
|
|
159
|
+
allow_headers=["*"],
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# TODO: Tasks pending completion -@andre at 4/15/2026, 7:57:32 PM
|
|
164
|
+
# Need to update on_event to the correct fastapi lifecycle event hook
|
|
165
|
+
@app.on_event("startup")
|
|
166
|
+
async def _startup_ingest_design_knowledge() -> None:
|
|
167
|
+
"""Auto-ingest bundled design knowledge markdown files into FAISS on first startup.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
None: None.
|
|
171
|
+
"""
|
|
172
|
+
import logging as _logging
|
|
173
|
+
|
|
174
|
+
_log = _logging.getLogger(__name__)
|
|
175
|
+
knowledge_dir = FilePath(__file__).parent.parent / "agents" / "design_knowledge"
|
|
176
|
+
if not knowledge_dir.is_dir():
|
|
177
|
+
return
|
|
178
|
+
md_files = sorted(knowledge_dir.glob("*.md"))
|
|
179
|
+
if not md_files:
|
|
180
|
+
return
|
|
181
|
+
try:
|
|
182
|
+
from ..agents.vector_rag import VectorRAGIndex # noqa: PLC0415
|
|
183
|
+
|
|
184
|
+
namespace = "solidworks-design-knowledge"
|
|
185
|
+
rag_dir = FilePath(".solidworks_mcp") / "rag"
|
|
186
|
+
idx = VectorRAGIndex.load(namespace=namespace, rag_dir=rag_dir)
|
|
187
|
+
# Only re-ingest if the index is empty (fresh install) or stale
|
|
188
|
+
if idx.chunk_count == 0:
|
|
189
|
+
for md_file in md_files:
|
|
190
|
+
text = md_file.read_text(encoding="utf-8")
|
|
191
|
+
idx.ingest_text(
|
|
192
|
+
text, source=md_file.name, tags=[namespace, md_file.stem]
|
|
193
|
+
)
|
|
194
|
+
idx.save()
|
|
195
|
+
_log.info(
|
|
196
|
+
"Auto-ingested %d design knowledge files into FAISS namespace '%s'",
|
|
197
|
+
len(md_files),
|
|
198
|
+
namespace,
|
|
199
|
+
)
|
|
200
|
+
else:
|
|
201
|
+
_log.debug(
|
|
202
|
+
"FAISS namespace '%s' already loaded (%d chunks) — skipping auto-ingest",
|
|
203
|
+
namespace,
|
|
204
|
+
idx.chunk_count,
|
|
205
|
+
)
|
|
206
|
+
except ImportError:
|
|
207
|
+
_log.debug(
|
|
208
|
+
"faiss/sentence-transformers not installed — skipping design knowledge auto-ingest"
|
|
209
|
+
)
|
|
210
|
+
except Exception as exc:
|
|
211
|
+
_log.warning("Design knowledge auto-ingest failed (non-fatal): %s", exc)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@app.middleware("http")
|
|
215
|
+
async def log_ui_requests(request: Request, call_next: Any) -> Any:
|
|
216
|
+
"""Log each UI request (method, path, body) before passing it on.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
request (Request): The incoming request.
|
|
220
|
+
call_next (Any): Starlette's handler for the rest of the chain.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Any: The downstream response.
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
if not _should_log_request(request.url.path):
|
|
227
|
+
return await call_next(request)
|
|
228
|
+
|
|
229
|
+
started_at = time.perf_counter()
|
|
230
|
+
raw_body = await request.body()
|
|
231
|
+
payload = {
|
|
232
|
+
"method": request.method,
|
|
233
|
+
"path": request.url.path,
|
|
234
|
+
"query": dict(request.query_params),
|
|
235
|
+
"body": _decode_request_body(raw_body),
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
logger.info(
|
|
239
|
+
"[ui.http.request] payload={}",
|
|
240
|
+
json.dumps(payload, ensure_ascii=True, default=str),
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
try:
|
|
244
|
+
response = await call_next(request)
|
|
245
|
+
except Exception as exc:
|
|
246
|
+
duration_ms = round((time.perf_counter() - started_at) * 1000, 2)
|
|
247
|
+
logger.exception(
|
|
248
|
+
"[ui.http.error] method={} path={} duration_ms={} error={}",
|
|
249
|
+
request.method,
|
|
250
|
+
request.url.path,
|
|
251
|
+
duration_ms,
|
|
252
|
+
exc,
|
|
253
|
+
)
|
|
254
|
+
raise
|
|
255
|
+
|
|
256
|
+
duration_ms = round((time.perf_counter() - started_at) * 1000, 2)
|
|
257
|
+
logger.info(
|
|
258
|
+
"[ui.http.response] method={} path={} status_code={} duration_ms={}",
|
|
259
|
+
request.method,
|
|
260
|
+
request.url.path,
|
|
261
|
+
response.status_code,
|
|
262
|
+
duration_ms,
|
|
263
|
+
)
|
|
264
|
+
return response
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
app.mount(
|
|
268
|
+
"/previews",
|
|
269
|
+
StaticFiles(directory=str(ensure_preview_dir().resolve())),
|
|
270
|
+
name="previews",
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# ---------------------------------------------------------------------------
|
|
274
|
+
# Include modular routers
|
|
275
|
+
# ---------------------------------------------------------------------------
|
|
276
|
+
app.include_router(session_router.router)
|
|
277
|
+
app.include_router(model_router.router)
|
|
278
|
+
app.include_router(preview_router.router)
|
|
279
|
+
app.include_router(llm_router.router)
|
|
280
|
+
app.include_router(checkpoint_router.router)
|
|
281
|
+
app.include_router(docs_router.router)
|
|
282
|
+
app.include_router(local_model_router.router)
|
|
283
|
+
app.include_router(viewer_router.router)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@app.get("/api/health")
|
|
287
|
+
async def health() -> dict[str, str]:
|
|
288
|
+
"""Health check endpoint."""
|
|
289
|
+
return {"status": "ok"}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def main() -> None:
|
|
293
|
+
"""Run the dashboard backend locally."""
|
|
294
|
+
uvicorn.run(app, host="127.0.0.1", port=8766)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
if __name__ == "__main__":
|
|
298
|
+
main()
|