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,366 @@
|
|
|
1
|
+
"""Adapter factory for creating SolidWorks adapters.
|
|
2
|
+
|
|
3
|
+
Provides centralized adapter creation with configuration-based selection and automatic
|
|
4
|
+
fallback strategies.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import platform
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from ..config import AdapterType, SolidWorksMCPConfig
|
|
13
|
+
from .base import SolidWorksAdapter
|
|
14
|
+
from .mock_adapter import MockSolidWorksAdapter
|
|
15
|
+
from .vba_adapter import VbaGeneratorAdapter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AdapterFactory:
|
|
19
|
+
"""Factory-pattern adapter creator with singleton and fallback strategies.
|
|
20
|
+
|
|
21
|
+
Manages adapter registration and creation with configuration-based selection.
|
|
22
|
+
Automatically wraps adapters with decorators (circuit breaker, connection pool) based on
|
|
23
|
+
configuration. Implements singleton pattern for consistency.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
_adapter_registry (dict[AdapterType, type[SolidWorksAdapter]]): The adapter registry
|
|
27
|
+
value.
|
|
28
|
+
_adapters (dict[AdapterType, type[SolidWorksAdapter]]): The adapters value.
|
|
29
|
+
_instance (AdapterFactory | None): The instance value.
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
>>> factory = AdapterFactory()
|
|
33
|
+
>>> config = SolidWorksMCPConfig(adapter_type=AdapterType.PYWIN32)
|
|
34
|
+
>>> adapter = factory.create_adapter(config)
|
|
35
|
+
>>> await adapter.connect()
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
_adapter_registry: dict[AdapterType, type[SolidWorksAdapter]] = {}
|
|
39
|
+
_adapters: dict[AdapterType, type[SolidWorksAdapter]] = _adapter_registry
|
|
40
|
+
_instance: AdapterFactory | None = None
|
|
41
|
+
|
|
42
|
+
def __new__(cls) -> AdapterFactory:
|
|
43
|
+
"""Initialize or return singleton AdapterFactory instance.
|
|
44
|
+
|
|
45
|
+
Enforces singleton pattern to ensure single adapter registry and consistent factory
|
|
46
|
+
behavior across application lifetime.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
AdapterFactory: The result produced by the operation.
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
>>> factory1 = AdapterFactory()
|
|
53
|
+
>>> factory2 = AdapterFactory()
|
|
54
|
+
>>> factory1 is factory2
|
|
55
|
+
True
|
|
56
|
+
"""
|
|
57
|
+
if cls._instance is None:
|
|
58
|
+
cls._instance = super().__new__(cls)
|
|
59
|
+
return cls._instance
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def register_adapter(
|
|
63
|
+
cls, adapter_type: AdapterType, adapter_class: type[SolidWorksAdapter]
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Register an adapter class for a given adapter type.
|
|
66
|
+
|
|
67
|
+
Updates the adapter registry to map a specific AdapterType enum value to a
|
|
68
|
+
SolidWorksAdapter implementation class. Called during initialization to register built-
|
|
69
|
+
in adapters (PyWin32, Mock, etc.).
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
adapter_type (AdapterType): The adapter type value.
|
|
73
|
+
adapter_class (type[SolidWorksAdapter]): The adapter class value.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
None: None.
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
>>> from adapters.pywin32_adapter import PyWin32Adapter
|
|
80
|
+
>>> AdapterFactory.register_adapter(
|
|
81
|
+
... AdapterType.PYWIN32, PyWin32Adapter
|
|
82
|
+
... )
|
|
83
|
+
"""
|
|
84
|
+
cls._adapter_registry[adapter_type] = adapter_class
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def create_adapter(cls, config: SolidWorksMCPConfig) -> SolidWorksAdapter:
|
|
88
|
+
"""Create and configure a SolidWorks adapter instance.
|
|
89
|
+
|
|
90
|
+
Determines the best adapter type based on configuration and environment. Selects
|
|
91
|
+
implementation, applies wrappers (circuit breaker, connection pool), and returns a
|
|
92
|
+
fully-initialized adapter ready for connection.
|
|
93
|
+
|
|
94
|
+
The adapter is automatically downgraded to Mock on non-Windows platforms or when testing
|
|
95
|
+
is enabled, with fallback to configured type otherwise.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
config (SolidWorksMCPConfig): Configuration values for the operation.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
SolidWorksAdapter: The result produced by the operation.
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
>>> config = SolidWorksMCPConfig(mock_solidworks=True)
|
|
105
|
+
>>> adapter = AdapterFactory.create_adapter(config)
|
|
106
|
+
>>> isinstance(adapter, MockSolidWorksAdapter)
|
|
107
|
+
True
|
|
108
|
+
"""
|
|
109
|
+
factory = cls()
|
|
110
|
+
return factory._create_adapter_impl(config)
|
|
111
|
+
|
|
112
|
+
def _create_adapter_impl(self, config: SolidWorksMCPConfig) -> SolidWorksAdapter:
|
|
113
|
+
"""Internal implementation for adapter creation and wrapping.
|
|
114
|
+
|
|
115
|
+
Handles the core logic of creating base adapter, applying optional wrappers (circuit
|
|
116
|
+
breaker, connection pooling), and returning final adapter configured for use.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
config (SolidWorksMCPConfig): Configuration values for the operation.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
SolidWorksAdapter: The result produced by the operation.
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
ValueError: If the operation cannot be completed.
|
|
126
|
+
"""
|
|
127
|
+
# Determine the best adaptertype
|
|
128
|
+
adapter_type = self._determine_adapter_type(config)
|
|
129
|
+
|
|
130
|
+
# Get adapter class
|
|
131
|
+
if adapter_type not in self._adapter_registry:
|
|
132
|
+
raise ValueError(f"Adaptertype {adapter_type} not registered")
|
|
133
|
+
|
|
134
|
+
adapter_class = self._adapter_registry[adapter_type]
|
|
135
|
+
|
|
136
|
+
# Create base adapter
|
|
137
|
+
if adapter_type == AdapterType.VBA:
|
|
138
|
+
backing_type = self._determine_vba_backing_type(config)
|
|
139
|
+
if backing_type not in self._adapter_registry:
|
|
140
|
+
raise ValueError(f"Backing adapter type {backing_type} not registered")
|
|
141
|
+
|
|
142
|
+
backing_class = self._adapter_registry[backing_type]
|
|
143
|
+
if backing_type == AdapterType.MOCK:
|
|
144
|
+
backing_adapter = backing_class(config)
|
|
145
|
+
else:
|
|
146
|
+
backing_adapter = backing_class(self._build_adapter_config(config))
|
|
147
|
+
base_adapter = VbaGeneratorAdapter(backing_adapter=backing_adapter)
|
|
148
|
+
elif adapter_type == AdapterType.MOCK:
|
|
149
|
+
base_adapter = adapter_class(config) # type: ignore[assignment]
|
|
150
|
+
else:
|
|
151
|
+
adapter_config = self._build_adapter_config(config)
|
|
152
|
+
base_adapter = adapter_class(adapter_config) # type: ignore[assignment]
|
|
153
|
+
|
|
154
|
+
# Wrap with additional features if enabled
|
|
155
|
+
adapter: SolidWorksAdapter = base_adapter # type: ignore[assignment]
|
|
156
|
+
|
|
157
|
+
# Keep mock/testing adapters simple and deterministic.
|
|
158
|
+
if adapter_type != AdapterType.MOCK and config.enable_circuit_breaker:
|
|
159
|
+
adapter = self._wrap_with_circuit_breaker(adapter, config)
|
|
160
|
+
|
|
161
|
+
if adapter_type != AdapterType.MOCK and config.enable_connection_pooling:
|
|
162
|
+
adapter = self._wrap_with_connection_pool(adapter, config)
|
|
163
|
+
|
|
164
|
+
self._apply_soc_logging(adapter, config)
|
|
165
|
+
|
|
166
|
+
return adapter
|
|
167
|
+
|
|
168
|
+
@staticmethod
|
|
169
|
+
def _apply_soc_logging(
|
|
170
|
+
adapter: SolidWorksAdapter, config: SolidWorksMCPConfig
|
|
171
|
+
) -> None:
|
|
172
|
+
"""Turn on SolidWorks-as-Code session logging when the config asks for it.
|
|
173
|
+
|
|
174
|
+
Sets ``soc_session_id`` / ``soc_db_path`` on the returned adapter from
|
|
175
|
+
config. Off unless ``config.soc_logging_enabled``. The actual
|
|
176
|
+
``ToolCallRecord`` writes happen in ``CircuitBreakerAdapter._soc_log``,
|
|
177
|
+
so logging is effective only when the circuit breaker wrapper is in
|
|
178
|
+
play (the default for non-mock adapters).
|
|
179
|
+
"""
|
|
180
|
+
if not getattr(config, "soc_logging_enabled", False):
|
|
181
|
+
return
|
|
182
|
+
adapter.soc_session_id = config.soc_session_id
|
|
183
|
+
if config.soc_db_path is not None:
|
|
184
|
+
adapter.soc_db_path = config.soc_db_path
|
|
185
|
+
|
|
186
|
+
def _determine_adapter_type(self, config: SolidWorksMCPConfig) -> AdapterType:
|
|
187
|
+
"""Determine optimal adapter type based on environment and config.
|
|
188
|
+
|
|
189
|
+
Applies decision logic to select best adapter implementation: 1. Force Mock if testing
|
|
190
|
+
or mock_solidworks flag set 2. Fall back to Mock if PyWin32 requested but not on Windows
|
|
191
|
+
3. Use configured adapter_type otherwise
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
config (SolidWorksMCPConfig): Configuration values for the operation.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
AdapterType: The result produced by the operation.
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
>>> config = SolidWorksMCPConfig(adapter_type=AdapterType.PYWIN32)
|
|
201
|
+
>>> factory = AdapterFactory()
|
|
202
|
+
>>> adapter_type = factory._determine_adapter_type(config)
|
|
203
|
+
>>> # On non-Windows: AdapterType.MOCK
|
|
204
|
+
>>> # On Windows: AdapterType.PYWIN32
|
|
205
|
+
"""
|
|
206
|
+
# Force mock adapter for testing
|
|
207
|
+
if config.testing or config.mock_solidworks:
|
|
208
|
+
return AdapterType.MOCK
|
|
209
|
+
|
|
210
|
+
# Check platform compatibility
|
|
211
|
+
if (
|
|
212
|
+
platform.system() != "Windows"
|
|
213
|
+
and config.adapter_type == AdapterType.PYWIN32
|
|
214
|
+
):
|
|
215
|
+
# Fallback to mock on non-Windows platforms
|
|
216
|
+
return AdapterType.MOCK
|
|
217
|
+
|
|
218
|
+
# Use configured adaptertype
|
|
219
|
+
return config.adapter_type
|
|
220
|
+
|
|
221
|
+
def _determine_vba_backing_type(self, config: SolidWorksMCPConfig) -> AdapterType:
|
|
222
|
+
"""Determine which adapter should back the VBA wrapper.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
config (SolidWorksMCPConfig): Configuration values for the operation.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
AdapterType: The result produced by the operation.
|
|
229
|
+
"""
|
|
230
|
+
if config.testing or config.mock_solidworks:
|
|
231
|
+
return AdapterType.MOCK
|
|
232
|
+
if platform.system() != "Windows":
|
|
233
|
+
return AdapterType.MOCK
|
|
234
|
+
return AdapterType.PYWIN32
|
|
235
|
+
|
|
236
|
+
def _build_adapter_config(self, config: SolidWorksMCPConfig) -> dict[str, Any]:
|
|
237
|
+
"""Extract and transform config for adapter instantiation.
|
|
238
|
+
|
|
239
|
+
Creates adapter-specific configuration dict from main config object. Includes SolidWorks
|
|
240
|
+
paths, validation settings, and timeouts/retries.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
config (SolidWorksMCPConfig): Configuration values for the operation.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
dict[str, Any]: A dictionary containing the resulting values.
|
|
247
|
+
|
|
248
|
+
Example:
|
|
249
|
+
>>> main_config = SolidWorksMCPConfig(solidworks_path='/sw/sw.exe')
|
|
250
|
+
>>> factory = AdapterFactory()
|
|
251
|
+
>>> adapter_cfg = factory._build_adapter_config(main_config)
|
|
252
|
+
>>> adapter_cfg['timeout']
|
|
253
|
+
30
|
|
254
|
+
"""
|
|
255
|
+
return {
|
|
256
|
+
"solidworks_path": config.solidworks_path,
|
|
257
|
+
"enable_windows_validation": config.enable_windows_validation,
|
|
258
|
+
"debug": config.debug,
|
|
259
|
+
"timeout": 30, # Default timeout in seconds
|
|
260
|
+
"retry_attempts": 3,
|
|
261
|
+
"retry_delay": 1.0,
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
def _wrap_with_circuit_breaker(
|
|
265
|
+
self, adapter: SolidWorksAdapter, config: SolidWorksMCPConfig
|
|
266
|
+
) -> SolidWorksAdapter:
|
|
267
|
+
"""Wrap adapter with circuit breaker for fault tolerance.
|
|
268
|
+
|
|
269
|
+
Applies circuit breaker decorator to handle COM failures, timeouts, and transient
|
|
270
|
+
errors. Automatically opens circuit after threshold failures and enters half-open
|
|
271
|
+
recovery state.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
adapter (SolidWorksAdapter): Adapter instance used for the operation.
|
|
275
|
+
config (SolidWorksMCPConfig): Configuration values for the operation.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
SolidWorksAdapter: The result produced by the operation.
|
|
279
|
+
|
|
280
|
+
Example:
|
|
281
|
+
>>> from adapters.mock_adapter import MockSolidWorksAdapter
|
|
282
|
+
>>> base = MockSolidWorksAdapter()
|
|
283
|
+
>>> config = SolidWorksMCPConfig()
|
|
284
|
+
>>> wrapped = factory._wrap_with_circuit_breaker(base, config)
|
|
285
|
+
"""
|
|
286
|
+
from .circuit_breaker import CircuitBreakerAdapter
|
|
287
|
+
|
|
288
|
+
return CircuitBreakerAdapter(
|
|
289
|
+
adapter=adapter,
|
|
290
|
+
failure_threshold=config.circuit_breaker_threshold,
|
|
291
|
+
recovery_timeout=config.circuit_breaker_timeout,
|
|
292
|
+
half_open_max_calls=3,
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
def _wrap_with_connection_pool(
|
|
296
|
+
self, adapter: SolidWorksAdapter, config: SolidWorksMCPConfig
|
|
297
|
+
) -> SolidWorksAdapter:
|
|
298
|
+
"""Wrap adapter with connection pooling for performance.
|
|
299
|
+
|
|
300
|
+
Applies connection pool decorator to reuse SolidWorks COM connections, reducing
|
|
301
|
+
connection overhead for repeated operations. Pool size and timeout configured via config
|
|
302
|
+
object.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
adapter (SolidWorksAdapter): Adapter instance used for the operation.
|
|
306
|
+
config (SolidWorksMCPConfig): Configuration values for the operation.
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
SolidWorksAdapter: The result produced by the operation.
|
|
310
|
+
|
|
311
|
+
Example:
|
|
312
|
+
>>> from adapters.mock_adapter import MockSolidWorksAdapter
|
|
313
|
+
>>> base = MockSolidWorksAdapter()
|
|
314
|
+
>>> config = SolidWorksMCPConfig()
|
|
315
|
+
>>> pooled = factory._wrap_with_connection_pool(base, config)
|
|
316
|
+
"""
|
|
317
|
+
from .connection_pool import ConnectionPoolAdapter
|
|
318
|
+
|
|
319
|
+
return ConnectionPoolAdapter(
|
|
320
|
+
adapter_factory=lambda: adapter,
|
|
321
|
+
pool_size=config.connection_pool_size,
|
|
322
|
+
max_retries=3,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
async def create_adapter(config: SolidWorksMCPConfig) -> SolidWorksAdapter:
|
|
327
|
+
"""Async factory function for creating SolidWorks adapters.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
config (SolidWorksMCPConfig): Configuration values for the operation.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
SolidWorksAdapter: The result produced by the operation.
|
|
334
|
+
"""
|
|
335
|
+
# Register adapters if not already done
|
|
336
|
+
_register_default_adapters()
|
|
337
|
+
|
|
338
|
+
# Create adapter using factory
|
|
339
|
+
adapter = AdapterFactory.create_adapter(config)
|
|
340
|
+
|
|
341
|
+
return adapter
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _register_default_adapters() -> None:
|
|
345
|
+
"""Register default adapter implementations.
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
None: None.
|
|
349
|
+
"""
|
|
350
|
+
# Always register mock adapter
|
|
351
|
+
AdapterFactory.register_adapter(AdapterType.MOCK, MockSolidWorksAdapter)
|
|
352
|
+
AdapterFactory.register_adapter(AdapterType.VBA, VbaGeneratorAdapter) # type: ignore[arg-type]
|
|
353
|
+
|
|
354
|
+
# Register pywin32 adapter if on Windows
|
|
355
|
+
if platform.system() == "Windows":
|
|
356
|
+
try:
|
|
357
|
+
from .pywin32_adapter import PyWin32Adapter
|
|
358
|
+
|
|
359
|
+
AdapterFactory.register_adapter(AdapterType.PYWIN32, PyWin32Adapter)
|
|
360
|
+
except ImportError:
|
|
361
|
+
# pywin32 not available, will fall back to mock
|
|
362
|
+
pass
|
|
363
|
+
|
|
364
|
+
# Future adapters can be registered here
|
|
365
|
+
# AdapterFactory.register_adapter(AdapterType.EDGE_DOTNET, EdgeDotNetAdapter)
|
|
366
|
+
# AdapterFactory.register_adapter(AdapterType.POWERSHELL, PowerShellAdapter)
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""Intelligent router for COM/VBA execution with optional response caching."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Awaitable, Callable
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from ..cache.response_cache import ResponseCache
|
|
10
|
+
from .base import AdapterResult, AdapterResultStatus
|
|
11
|
+
from .complexity_analyzer import ComplexityAnalyzer
|
|
12
|
+
|
|
13
|
+
OperationCallable = Callable[..., Awaitable[AdapterResult[Any]]]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class RouteResult:
|
|
18
|
+
"""Route execution metadata.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
route (str): The route value.
|
|
22
|
+
used_cache (bool): The used cache value.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
route: str
|
|
26
|
+
used_cache: bool
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class IntelligentRouter:
|
|
30
|
+
"""Route operations between COM and VBA paths using complexity analysis.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
analyzer (ComplexityAnalyzer): The analyzer value.
|
|
34
|
+
cache (ResponseCache): The cache value.
|
|
35
|
+
cacheable_operations (set[str] | None): The cacheable operations value. Defaults to
|
|
36
|
+
None.
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
_analyzer (Any): The analyzer value.
|
|
40
|
+
_cache (Any): The cache value.
|
|
41
|
+
_cacheable_operations (Any): The cacheable operations value.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
analyzer: ComplexityAnalyzer,
|
|
47
|
+
cache: ResponseCache,
|
|
48
|
+
cacheable_operations: set[str] | None = None,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Initialize router dependencies.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
analyzer (ComplexityAnalyzer): The analyzer value.
|
|
54
|
+
cache (ResponseCache): The cache value.
|
|
55
|
+
cacheable_operations (set[str] | None): The cacheable operations value. Defaults to
|
|
56
|
+
None.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
None: None.
|
|
60
|
+
"""
|
|
61
|
+
self._analyzer = analyzer
|
|
62
|
+
self._cache = cache
|
|
63
|
+
self._cacheable_operations = cacheable_operations or {
|
|
64
|
+
# Live document metadata is deliberately NOT cached. Model state,
|
|
65
|
+
# save state, paths, features, configurations, dimensions and
|
|
66
|
+
# properties can all change after any CAD operation, and this
|
|
67
|
+
# router has no reliable mutation-triggered invalidation.
|
|
68
|
+
# "get_model_info",
|
|
69
|
+
# "list_features",
|
|
70
|
+
# "list_configurations",
|
|
71
|
+
# "get_file_properties",
|
|
72
|
+
# "get_dimension",
|
|
73
|
+
# Analysis operations — NOT cached: model state changes with
|
|
74
|
+
# every feature-creation tool call and the router has no
|
|
75
|
+
# automatic invalidation. Caching these produced stale
|
|
76
|
+
# volumes mid-build.
|
|
77
|
+
# "get_mass_properties",
|
|
78
|
+
# "calculate_mass_properties",
|
|
79
|
+
"get_material_properties",
|
|
80
|
+
"analyze_geometry",
|
|
81
|
+
"check_interference",
|
|
82
|
+
# Drawing analysis operations
|
|
83
|
+
"analyze_drawing_comprehensive",
|
|
84
|
+
"analyze_drawing_dimensions",
|
|
85
|
+
"analyze_drawing_views",
|
|
86
|
+
"analyze_drawing_annotations",
|
|
87
|
+
"check_drawing_compliance",
|
|
88
|
+
"check_drawing_standards",
|
|
89
|
+
"compare_drawing_versions",
|
|
90
|
+
# Classification and indexing
|
|
91
|
+
"classify_feature_tree",
|
|
92
|
+
"discover_solidworks_docs",
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async def execute(
|
|
96
|
+
self,
|
|
97
|
+
operation: str,
|
|
98
|
+
payload: object,
|
|
99
|
+
call_args: tuple[Any, ...],
|
|
100
|
+
call_kwargs: dict[str, Any],
|
|
101
|
+
com_operation: OperationCallable,
|
|
102
|
+
vba_operation: OperationCallable | None,
|
|
103
|
+
cache_ttl_seconds: int | None = None,
|
|
104
|
+
) -> tuple[AdapterResult[Any], RouteResult]:
|
|
105
|
+
"""Provide execute support for the intelligent router.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
operation (str): Callable object executed by the helper.
|
|
109
|
+
payload (object): The payload value.
|
|
110
|
+
call_args (tuple[Any, ...]): The call args value.
|
|
111
|
+
call_kwargs (dict[str, Any]): The call kwargs value.
|
|
112
|
+
com_operation (OperationCallable): The com operation value.
|
|
113
|
+
vba_operation (OperationCallable | None): The vba operation value.
|
|
114
|
+
cache_ttl_seconds (int | None): The cache ttl seconds value. Defaults to None.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
tuple[AdapterResult[Any], RouteResult]: A tuple containing the resulting values.
|
|
118
|
+
"""
|
|
119
|
+
if operation in self._cacheable_operations and self._cache.enabled:
|
|
120
|
+
key = self._cache.make_key(operation, payload)
|
|
121
|
+
cached = self._cache.get(key)
|
|
122
|
+
if isinstance(cached, AdapterResult):
|
|
123
|
+
return cached, RouteResult(route="cache", used_cache=True)
|
|
124
|
+
|
|
125
|
+
decision = self._analyzer.analyze(operation=operation, payload=payload)
|
|
126
|
+
|
|
127
|
+
if decision.prefer_vba and vba_operation is not None:
|
|
128
|
+
vba_result = await self._safe_call(vba_operation, call_args, call_kwargs)
|
|
129
|
+
self._analyzer.record_result(
|
|
130
|
+
operation=operation,
|
|
131
|
+
route="vba",
|
|
132
|
+
success=vba_result.is_success,
|
|
133
|
+
)
|
|
134
|
+
if vba_result.is_success:
|
|
135
|
+
self._cache_result(operation, payload, vba_result, cache_ttl_seconds)
|
|
136
|
+
return vba_result, RouteResult(route="vba", used_cache=False)
|
|
137
|
+
|
|
138
|
+
com_result = await self._safe_call(com_operation, call_args, call_kwargs)
|
|
139
|
+
self._analyzer.record_result(
|
|
140
|
+
operation=operation,
|
|
141
|
+
route="com",
|
|
142
|
+
success=com_result.is_success,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if com_result.is_success:
|
|
146
|
+
self._cache_result(operation, payload, com_result, cache_ttl_seconds)
|
|
147
|
+
return com_result, RouteResult(route="com", used_cache=False)
|
|
148
|
+
|
|
149
|
+
if not decision.prefer_vba and vba_operation is not None:
|
|
150
|
+
fallback_result = await self._safe_call(
|
|
151
|
+
vba_operation,
|
|
152
|
+
call_args,
|
|
153
|
+
call_kwargs,
|
|
154
|
+
)
|
|
155
|
+
self._analyzer.record_result(
|
|
156
|
+
operation=operation,
|
|
157
|
+
route="vba",
|
|
158
|
+
success=fallback_result.is_success,
|
|
159
|
+
)
|
|
160
|
+
if fallback_result.is_success:
|
|
161
|
+
self._cache_result(
|
|
162
|
+
operation,
|
|
163
|
+
payload,
|
|
164
|
+
fallback_result,
|
|
165
|
+
cache_ttl_seconds,
|
|
166
|
+
)
|
|
167
|
+
return fallback_result, RouteResult(
|
|
168
|
+
route="vba-fallback", used_cache=False
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
return com_result, RouteResult(route="com-error", used_cache=False)
|
|
172
|
+
|
|
173
|
+
async def _safe_call(
|
|
174
|
+
self,
|
|
175
|
+
operation_callable: OperationCallable,
|
|
176
|
+
call_args: tuple[Any, ...],
|
|
177
|
+
call_kwargs: dict[str, Any],
|
|
178
|
+
) -> AdapterResult[Any]:
|
|
179
|
+
"""Call adapter operation and normalize unexpected exceptions.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
operation_callable (OperationCallable): The operation callable value.
|
|
183
|
+
call_args (tuple[Any, ...]): The call args value.
|
|
184
|
+
call_kwargs (dict[str, Any]): The call kwargs value.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
AdapterResult[Any]: The result produced by the operation.
|
|
188
|
+
"""
|
|
189
|
+
try:
|
|
190
|
+
return await operation_callable(*call_args, **call_kwargs)
|
|
191
|
+
except Exception as exc:
|
|
192
|
+
return AdapterResult(
|
|
193
|
+
status=AdapterResultStatus.ERROR,
|
|
194
|
+
error=f"operation failed: {exc}",
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def _cache_result(
|
|
198
|
+
self,
|
|
199
|
+
operation: str,
|
|
200
|
+
payload: object,
|
|
201
|
+
result: AdapterResult[Any],
|
|
202
|
+
ttl_seconds: int | None,
|
|
203
|
+
) -> None:
|
|
204
|
+
"""Persist successful operation result in cache when eligible.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
operation (str): Callable object executed by the helper.
|
|
208
|
+
payload (object): The payload value.
|
|
209
|
+
result (AdapterResult[Any]): The result value.
|
|
210
|
+
ttl_seconds (int | None): The ttl seconds value.
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
None: None.
|
|
214
|
+
"""
|
|
215
|
+
if operation not in self._cacheable_operations:
|
|
216
|
+
return
|
|
217
|
+
key = self._cache.make_key(operation, payload)
|
|
218
|
+
self._cache.set(key=key, value=result, ttl_seconds=ttl_seconds)
|