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,459 @@
|
|
|
1
|
+
"""Model connection and upload service for the Prefab CAD assistant dashboard.
|
|
2
|
+
|
|
3
|
+
Handles opening a target SolidWorks model (open-only) and connecting to it
|
|
4
|
+
(open + inspect feature tree + trigger preview refresh).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from loguru import logger
|
|
14
|
+
|
|
15
|
+
from ...adapters import create_adapter
|
|
16
|
+
from ...agents.history_db import (
|
|
17
|
+
insert_evidence_link,
|
|
18
|
+
insert_model_state_snapshot,
|
|
19
|
+
insert_tool_call_record,
|
|
20
|
+
)
|
|
21
|
+
from ...config import load_config
|
|
22
|
+
from ...utils.feature_tree_classifier import classify_feature_tree_snapshot
|
|
23
|
+
from ._utils import (
|
|
24
|
+
DEFAULT_API_ORIGIN,
|
|
25
|
+
DEFAULT_PREVIEW_ORIENTATION,
|
|
26
|
+
ensure_preview_dir,
|
|
27
|
+
materialize_uploaded_model,
|
|
28
|
+
merge_metadata,
|
|
29
|
+
sanitize_model_path_text,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# Public API
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def open_target_model(
|
|
38
|
+
session_id: str,
|
|
39
|
+
*,
|
|
40
|
+
model_path: str | None = None,
|
|
41
|
+
uploaded_files: list[dict[str, Any]] | None = None,
|
|
42
|
+
feature_target_text: str | None = None,
|
|
43
|
+
db_path: Path | None = None,
|
|
44
|
+
api_origin: str = DEFAULT_API_ORIGIN,
|
|
45
|
+
) -> dict[str, Any]:
|
|
46
|
+
"""Open a target model in SolidWorks and persist session state.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
session_id: Dashboard session identifier.
|
|
50
|
+
model_path: Absolute path to the SolidWorks model file.
|
|
51
|
+
uploaded_files: List of base64-encoded file upload dicts.
|
|
52
|
+
feature_target_text: Optional comma-separated feature target references.
|
|
53
|
+
db_path: Optional SQLite path override.
|
|
54
|
+
api_origin: Base URL of the running FastAPI server.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Full dashboard state payload.
|
|
58
|
+
"""
|
|
59
|
+
from .session_service import ( # noqa: PLC0415
|
|
60
|
+
build_dashboard_state,
|
|
61
|
+
ensure_dashboard_session,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
ensure_dashboard_session(session_id, db_path=db_path)
|
|
65
|
+
adapter = None
|
|
66
|
+
resolved_path: Path | None = None
|
|
67
|
+
|
|
68
|
+
logger.info(
|
|
69
|
+
"[ui.open_target_model] session_id={} model_path={} uploaded_files={} feature_targets={}",
|
|
70
|
+
session_id,
|
|
71
|
+
model_path,
|
|
72
|
+
len(uploaded_files) if uploaded_files else 0,
|
|
73
|
+
feature_target_text or "",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
resolved_path = _resolve_model_path(
|
|
77
|
+
session_id,
|
|
78
|
+
model_path=model_path,
|
|
79
|
+
uploaded_files=uploaded_files,
|
|
80
|
+
feature_target_text=feature_target_text,
|
|
81
|
+
db_path=db_path,
|
|
82
|
+
api_origin=api_origin,
|
|
83
|
+
)
|
|
84
|
+
if resolved_path is None:
|
|
85
|
+
return build_dashboard_state(session_id, db_path=db_path, api_origin=api_origin)
|
|
86
|
+
|
|
87
|
+
config = load_config()
|
|
88
|
+
adapter = await create_adapter(config)
|
|
89
|
+
model_info: dict[str, Any] = {}
|
|
90
|
+
tool_input = {
|
|
91
|
+
"model_path": str(resolved_path.resolve()),
|
|
92
|
+
"uploaded_file_name": uploaded_files[0].get("name") if uploaded_files else None,
|
|
93
|
+
"feature_target_text": feature_target_text or "",
|
|
94
|
+
}
|
|
95
|
+
try:
|
|
96
|
+
await adapter.connect()
|
|
97
|
+
open_result = await adapter.open_model(str(resolved_path.resolve()))
|
|
98
|
+
if not open_result.is_success:
|
|
99
|
+
raise RuntimeError(open_result.error or "Failed to open target model.")
|
|
100
|
+
|
|
101
|
+
if hasattr(adapter, "get_model_info"):
|
|
102
|
+
info_result = await adapter.get_model_info()
|
|
103
|
+
if info_result.is_success and isinstance(info_result.data, dict):
|
|
104
|
+
model_info = info_result.data
|
|
105
|
+
|
|
106
|
+
metadata = merge_metadata(
|
|
107
|
+
session_id,
|
|
108
|
+
db_path=db_path,
|
|
109
|
+
workflow_mode="edit_existing",
|
|
110
|
+
active_model_path=str(resolved_path.resolve()),
|
|
111
|
+
active_model_status=(
|
|
112
|
+
f"Opened model: {resolved_path.name}"
|
|
113
|
+
f" | type={model_info.get('type', 'unknown')}"
|
|
114
|
+
),
|
|
115
|
+
active_model_type=str(model_info.get("type") or ""),
|
|
116
|
+
active_model_configuration=str(
|
|
117
|
+
model_info.get("configuration") or "Default"
|
|
118
|
+
),
|
|
119
|
+
feature_target_text=feature_target_text or "",
|
|
120
|
+
latest_message=f"Opened target model {resolved_path.name} in SolidWorks.",
|
|
121
|
+
latest_error_text="",
|
|
122
|
+
remediation_hint="",
|
|
123
|
+
)
|
|
124
|
+
insert_tool_call_record(
|
|
125
|
+
session_id=session_id,
|
|
126
|
+
tool_name="ui.open_target_model",
|
|
127
|
+
input_json=json.dumps(tool_input, ensure_ascii=True),
|
|
128
|
+
output_json=json.dumps(metadata, ensure_ascii=True),
|
|
129
|
+
success=True,
|
|
130
|
+
db_path=db_path,
|
|
131
|
+
)
|
|
132
|
+
except Exception as exc:
|
|
133
|
+
logger.exception(
|
|
134
|
+
"[ui.open_target_model] failed session_id={} path={} error={}",
|
|
135
|
+
session_id,
|
|
136
|
+
str(resolved_path.resolve()) if resolved_path else "<none>",
|
|
137
|
+
exc,
|
|
138
|
+
)
|
|
139
|
+
merge_metadata(
|
|
140
|
+
session_id,
|
|
141
|
+
db_path=db_path,
|
|
142
|
+
workflow_mode="edit_existing",
|
|
143
|
+
active_model_path=str(resolved_path.resolve()),
|
|
144
|
+
feature_target_text=feature_target_text or "",
|
|
145
|
+
latest_message="Failed to open target model.",
|
|
146
|
+
latest_error_text=str(exc),
|
|
147
|
+
remediation_hint="Open SolidWorks, verify COM access, and retry with a valid .sldprt/.sldasm path.",
|
|
148
|
+
)
|
|
149
|
+
insert_tool_call_record(
|
|
150
|
+
session_id=session_id,
|
|
151
|
+
tool_name="ui.open_target_model",
|
|
152
|
+
input_json=json.dumps(tool_input, ensure_ascii=True),
|
|
153
|
+
output_json=json.dumps({"error": str(exc)}, ensure_ascii=True),
|
|
154
|
+
success=False,
|
|
155
|
+
db_path=db_path,
|
|
156
|
+
)
|
|
157
|
+
finally:
|
|
158
|
+
if adapter is not None:
|
|
159
|
+
try:
|
|
160
|
+
await adapter.disconnect()
|
|
161
|
+
except Exception:
|
|
162
|
+
logger.debug("Adapter disconnect failed during open-model cleanup")
|
|
163
|
+
|
|
164
|
+
return build_dashboard_state(session_id, db_path=db_path, api_origin=api_origin)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async def connect_target_model(
|
|
168
|
+
session_id: str,
|
|
169
|
+
*,
|
|
170
|
+
model_path: str | None = None,
|
|
171
|
+
uploaded_files: list[dict[str, Any]] | None = None,
|
|
172
|
+
feature_target_text: str | None = None,
|
|
173
|
+
db_path: Path | None = None,
|
|
174
|
+
api_origin: str = DEFAULT_API_ORIGIN,
|
|
175
|
+
) -> dict[str, Any]:
|
|
176
|
+
"""Open a target model, inspect its feature tree, and trigger a preview refresh.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
session_id: Dashboard session identifier.
|
|
180
|
+
model_path: Absolute path to the SolidWorks model file.
|
|
181
|
+
uploaded_files: List of base64-encoded file upload dicts.
|
|
182
|
+
feature_target_text: Optional comma-separated feature target references.
|
|
183
|
+
db_path: Optional SQLite path override.
|
|
184
|
+
api_origin: Base URL of the running FastAPI server.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
Full dashboard state payload (includes preview state after the refresh).
|
|
188
|
+
"""
|
|
189
|
+
from .preview_service import refresh_preview # noqa: PLC0415
|
|
190
|
+
from .session_service import ( # noqa: PLC0415
|
|
191
|
+
build_dashboard_state,
|
|
192
|
+
ensure_dashboard_session,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
ensure_dashboard_session(session_id, db_path=db_path)
|
|
196
|
+
adapter = None
|
|
197
|
+
resolved_path: Path | None = None
|
|
198
|
+
|
|
199
|
+
logger.info(
|
|
200
|
+
"[ui.connect_target_model] session_id={} model_path={} uploaded_files={} feature_targets={}",
|
|
201
|
+
session_id,
|
|
202
|
+
model_path,
|
|
203
|
+
len(uploaded_files) if uploaded_files else 0,
|
|
204
|
+
feature_target_text or "",
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
resolved_path = _resolve_model_path(
|
|
208
|
+
session_id,
|
|
209
|
+
model_path=model_path,
|
|
210
|
+
uploaded_files=uploaded_files,
|
|
211
|
+
feature_target_text=feature_target_text,
|
|
212
|
+
db_path=db_path,
|
|
213
|
+
api_origin=api_origin,
|
|
214
|
+
)
|
|
215
|
+
if resolved_path is None:
|
|
216
|
+
return build_dashboard_state(session_id, db_path=db_path, api_origin=api_origin)
|
|
217
|
+
|
|
218
|
+
config = load_config()
|
|
219
|
+
adapter = await create_adapter(config)
|
|
220
|
+
model_info: dict[str, Any] = {}
|
|
221
|
+
features: list[dict[str, Any]] = []
|
|
222
|
+
attach_succeeded = False
|
|
223
|
+
tool_input = {
|
|
224
|
+
"model_path": str(resolved_path.resolve()),
|
|
225
|
+
"uploaded_file_name": uploaded_files[0].get("name") if uploaded_files else None,
|
|
226
|
+
"feature_target_text": feature_target_text or "",
|
|
227
|
+
}
|
|
228
|
+
try:
|
|
229
|
+
await adapter.connect()
|
|
230
|
+
open_result = await adapter.open_model(str(resolved_path.resolve()))
|
|
231
|
+
if not open_result.is_success:
|
|
232
|
+
raise RuntimeError(open_result.error or "Failed to open target model.")
|
|
233
|
+
|
|
234
|
+
if hasattr(adapter, "get_model_info"):
|
|
235
|
+
info_result = await adapter.get_model_info()
|
|
236
|
+
if info_result.is_success and isinstance(info_result.data, dict):
|
|
237
|
+
model_info = info_result.data
|
|
238
|
+
|
|
239
|
+
if hasattr(adapter, "list_features"):
|
|
240
|
+
feature_result = await adapter.list_features(include_suppressed=True)
|
|
241
|
+
if feature_result.is_success and isinstance(feature_result.data, list):
|
|
242
|
+
features = [
|
|
243
|
+
item for item in feature_result.data if isinstance(item, dict)
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
from ._utils import feature_target_status # noqa: PLC0415
|
|
247
|
+
|
|
248
|
+
classification = classify_feature_tree_snapshot(model_info, features) # type: ignore[arg-type]
|
|
249
|
+
target_status, matched_targets, missing_targets = feature_target_status(
|
|
250
|
+
features, feature_target_text
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
snapshot_id = insert_model_state_snapshot(
|
|
254
|
+
session_id=session_id,
|
|
255
|
+
model_path=str(resolved_path.resolve()),
|
|
256
|
+
feature_tree_json=json.dumps(features, ensure_ascii=True),
|
|
257
|
+
state_fingerprint=f"{resolved_path.resolve()}::{resolved_path.stat().st_mtime_ns}",
|
|
258
|
+
db_path=db_path,
|
|
259
|
+
)
|
|
260
|
+
for evidence_line in classification.get("evidence", []):
|
|
261
|
+
insert_evidence_link(
|
|
262
|
+
session_id=session_id,
|
|
263
|
+
source_type="active_model",
|
|
264
|
+
source_id=str(resolved_path.resolve()),
|
|
265
|
+
relevance_score=0.96,
|
|
266
|
+
rationale=str(evidence_line),
|
|
267
|
+
payload_json=json.dumps(classification, ensure_ascii=True),
|
|
268
|
+
db_path=db_path,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
if matched_targets or missing_targets:
|
|
272
|
+
insert_evidence_link(
|
|
273
|
+
session_id=session_id,
|
|
274
|
+
source_type="feature_target",
|
|
275
|
+
source_id=str(resolved_path.resolve()),
|
|
276
|
+
relevance_score=0.9 if matched_targets else 0.4,
|
|
277
|
+
rationale=target_status,
|
|
278
|
+
payload_json=json.dumps(
|
|
279
|
+
{"matched": matched_targets, "missing": missing_targets},
|
|
280
|
+
ensure_ascii=True,
|
|
281
|
+
),
|
|
282
|
+
db_path=db_path,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
metadata = merge_metadata(
|
|
286
|
+
session_id,
|
|
287
|
+
db_path=db_path,
|
|
288
|
+
workflow_mode="edit_existing",
|
|
289
|
+
active_model_path=str(resolved_path.resolve()),
|
|
290
|
+
active_model_status=(
|
|
291
|
+
f"Attached model: {resolved_path.name}"
|
|
292
|
+
f" | type={model_info.get('type', 'unknown')}"
|
|
293
|
+
f" | features={len(features)}"
|
|
294
|
+
),
|
|
295
|
+
active_model_type=str(model_info.get("type") or ""),
|
|
296
|
+
active_model_configuration=str(
|
|
297
|
+
model_info.get("configuration") or "Default"
|
|
298
|
+
),
|
|
299
|
+
feature_target_text=feature_target_text or "",
|
|
300
|
+
feature_target_status=target_status,
|
|
301
|
+
proposed_family=classification.get("family") or "unknown",
|
|
302
|
+
family_confidence=classification.get("confidence") or "low",
|
|
303
|
+
family_evidence=classification.get("evidence") or [],
|
|
304
|
+
family_warnings=classification.get("warnings") or [],
|
|
305
|
+
latest_message=(
|
|
306
|
+
f"Opened target model {resolved_path.name}. Generating preview views..."
|
|
307
|
+
),
|
|
308
|
+
preview_status="Generating preview views from attached model...",
|
|
309
|
+
preview_stl_ready=False,
|
|
310
|
+
preview_png_ready=False,
|
|
311
|
+
preview_viewer_url="",
|
|
312
|
+
latest_error_text="",
|
|
313
|
+
remediation_hint="",
|
|
314
|
+
latest_snapshot_id=snapshot_id,
|
|
315
|
+
)
|
|
316
|
+
insert_tool_call_record(
|
|
317
|
+
session_id=session_id,
|
|
318
|
+
tool_name="ui.connect_target_model",
|
|
319
|
+
input_json=json.dumps(tool_input, ensure_ascii=True),
|
|
320
|
+
output_json=json.dumps(metadata, ensure_ascii=True),
|
|
321
|
+
success=True,
|
|
322
|
+
db_path=db_path,
|
|
323
|
+
)
|
|
324
|
+
attach_succeeded = True
|
|
325
|
+
except Exception as exc:
|
|
326
|
+
logger.exception(
|
|
327
|
+
"[ui.connect_target_model] failed session_id={} path={} error={}",
|
|
328
|
+
session_id,
|
|
329
|
+
str(resolved_path.resolve()) if resolved_path else "<none>",
|
|
330
|
+
exc,
|
|
331
|
+
)
|
|
332
|
+
merge_metadata(
|
|
333
|
+
session_id,
|
|
334
|
+
db_path=db_path,
|
|
335
|
+
workflow_mode="edit_existing",
|
|
336
|
+
active_model_path=str(resolved_path.resolve()),
|
|
337
|
+
feature_target_text=feature_target_text or "",
|
|
338
|
+
latest_message="Failed to attach target model.",
|
|
339
|
+
preview_status="Preview generation failed while attaching model.",
|
|
340
|
+
latest_error_text=str(exc),
|
|
341
|
+
remediation_hint="Open SolidWorks, verify COM access, and retry with a valid .sldprt/.sldasm path.",
|
|
342
|
+
)
|
|
343
|
+
insert_tool_call_record(
|
|
344
|
+
session_id=session_id,
|
|
345
|
+
tool_name="ui.connect_target_model",
|
|
346
|
+
input_json=json.dumps(tool_input, ensure_ascii=True),
|
|
347
|
+
output_json=json.dumps({"error": str(exc)}, ensure_ascii=True),
|
|
348
|
+
success=False,
|
|
349
|
+
db_path=db_path,
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
if attach_succeeded:
|
|
353
|
+
try:
|
|
354
|
+
return await refresh_preview(
|
|
355
|
+
session_id,
|
|
356
|
+
orientation=DEFAULT_PREVIEW_ORIENTATION,
|
|
357
|
+
db_path=db_path,
|
|
358
|
+
preview_dir=ensure_preview_dir(),
|
|
359
|
+
api_origin=api_origin,
|
|
360
|
+
adapter_override=adapter,
|
|
361
|
+
active_model_path_override=str(resolved_path.resolve()),
|
|
362
|
+
reopen_active_model=False,
|
|
363
|
+
)
|
|
364
|
+
except Exception as refresh_exc:
|
|
365
|
+
logger.warning(
|
|
366
|
+
"[ui.connect_target_model] post-attach preview refresh failed: {}",
|
|
367
|
+
str(refresh_exc),
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
if adapter is not None:
|
|
371
|
+
try:
|
|
372
|
+
await adapter.disconnect()
|
|
373
|
+
except Exception:
|
|
374
|
+
logger.debug("Adapter disconnect failed during target-model cleanup")
|
|
375
|
+
|
|
376
|
+
return build_dashboard_state(session_id, db_path=db_path, api_origin=api_origin)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
# ---------------------------------------------------------------------------
|
|
380
|
+
# Internal helpers
|
|
381
|
+
# ---------------------------------------------------------------------------
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _resolve_model_path(
|
|
385
|
+
session_id: str,
|
|
386
|
+
*,
|
|
387
|
+
model_path: str | None,
|
|
388
|
+
uploaded_files: list[dict[str, Any]] | None,
|
|
389
|
+
feature_target_text: str | None,
|
|
390
|
+
db_path: Path | None,
|
|
391
|
+
api_origin: str,
|
|
392
|
+
) -> Path | None:
|
|
393
|
+
"""Resolve and validate the model path from either upload payload or explicit path.
|
|
394
|
+
|
|
395
|
+
Args:
|
|
396
|
+
session_id: Dashboard session identifier.
|
|
397
|
+
model_path: Explicit absolute path string.
|
|
398
|
+
uploaded_files: Optional list of upload dicts.
|
|
399
|
+
feature_target_text: Optional feature target annotation.
|
|
400
|
+
db_path: Optional SQLite path override.
|
|
401
|
+
api_origin: Base URL of the running FastAPI server.
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
Resolved ``Path`` on success, ``None`` when validation fails (error state
|
|
405
|
+
already persisted to the session).
|
|
406
|
+
"""
|
|
407
|
+
if uploaded_files:
|
|
408
|
+
try:
|
|
409
|
+
return materialize_uploaded_model(session_id, uploaded_files)
|
|
410
|
+
except RuntimeError as exc:
|
|
411
|
+
merge_metadata(
|
|
412
|
+
session_id,
|
|
413
|
+
db_path=db_path,
|
|
414
|
+
latest_message="Uploaded model could not be prepared.",
|
|
415
|
+
latest_error_text=str(exc),
|
|
416
|
+
remediation_hint="Choose a valid .sldprt or .sldasm file and retry.",
|
|
417
|
+
feature_target_text=feature_target_text or "",
|
|
418
|
+
workflow_mode="edit_existing",
|
|
419
|
+
)
|
|
420
|
+
return None
|
|
421
|
+
|
|
422
|
+
if model_path:
|
|
423
|
+
normalized = sanitize_model_path_text(model_path)
|
|
424
|
+
if not normalized:
|
|
425
|
+
merge_metadata(
|
|
426
|
+
session_id,
|
|
427
|
+
db_path=db_path,
|
|
428
|
+
latest_message="No target model was provided.",
|
|
429
|
+
latest_error_text="Missing model path or uploaded model file.",
|
|
430
|
+
remediation_hint="Choose a local SolidWorks file or provide an absolute model path.",
|
|
431
|
+
feature_target_text=feature_target_text or "",
|
|
432
|
+
workflow_mode="edit_existing",
|
|
433
|
+
)
|
|
434
|
+
return None
|
|
435
|
+
resolved = Path(normalized).expanduser()
|
|
436
|
+
if not resolved.exists():
|
|
437
|
+
merge_metadata(
|
|
438
|
+
session_id,
|
|
439
|
+
db_path=db_path,
|
|
440
|
+
latest_message="Target model path was not found.",
|
|
441
|
+
latest_error_text=f"Missing file: {resolved}",
|
|
442
|
+
remediation_hint="Provide an absolute path to an existing .sldprt or .sldasm file.",
|
|
443
|
+
active_model_path=str(resolved),
|
|
444
|
+
feature_target_text=feature_target_text or "",
|
|
445
|
+
workflow_mode="edit_existing",
|
|
446
|
+
)
|
|
447
|
+
return None
|
|
448
|
+
return resolved
|
|
449
|
+
|
|
450
|
+
merge_metadata(
|
|
451
|
+
session_id,
|
|
452
|
+
db_path=db_path,
|
|
453
|
+
latest_message="No target model was provided.",
|
|
454
|
+
latest_error_text="Missing model path or uploaded model file.",
|
|
455
|
+
remediation_hint="Choose a local SolidWorks file or provide an absolute model path.",
|
|
456
|
+
feature_target_text=feature_target_text or "",
|
|
457
|
+
workflow_mode="edit_existing",
|
|
458
|
+
)
|
|
459
|
+
return None
|