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.
Files changed (115) hide show
  1. solidworks_mcp/__init__.py +57 -0
  2. solidworks_mcp/adapters/__init__.py +40 -0
  3. solidworks_mcp/adapters/base.py +1882 -0
  4. solidworks_mcp/adapters/circuit_breaker.py +2052 -0
  5. solidworks_mcp/adapters/com_executor.py +252 -0
  6. solidworks_mcp/adapters/complexity_analyzer.py +272 -0
  7. solidworks_mcp/adapters/connection_pool.py +1884 -0
  8. solidworks_mcp/adapters/factory.py +366 -0
  9. solidworks_mcp/adapters/intelligent_router.py +218 -0
  10. solidworks_mcp/adapters/mock_adapter.py +4016 -0
  11. solidworks_mcp/adapters/pywin32_adapter.py +2842 -0
  12. solidworks_mcp/adapters/solidworks/__init__.py +17 -0
  13. solidworks_mcp/adapters/solidworks/bitmap.py +180 -0
  14. solidworks_mcp/adapters/solidworks/com_utils.py +56 -0
  15. solidworks_mcp/adapters/solidworks/dimension_layout.py +753 -0
  16. solidworks_mcp/adapters/solidworks/documentation.py +1476 -0
  17. solidworks_mcp/adapters/solidworks/drawing_standard.py +1993 -0
  18. solidworks_mcp/adapters/solidworks/face_selection.py +392 -0
  19. solidworks_mcp/adapters/solidworks/features.py +3362 -0
  20. solidworks_mcp/adapters/solidworks/io.py +3649 -0
  21. solidworks_mcp/adapters/solidworks/rebuild_diagnostics.py +269 -0
  22. solidworks_mcp/adapters/solidworks/rebuild_inspector.py +146 -0
  23. solidworks_mcp/adapters/solidworks/selection.py +308 -0
  24. solidworks_mcp/adapters/solidworks/sketch.py +2545 -0
  25. solidworks_mcp/adapters/sw_feature_errors.py +66 -0
  26. solidworks_mcp/adapters/sw_type_info.py +317 -0
  27. solidworks_mcp/adapters/vba_adapter.py +318 -0
  28. solidworks_mcp/adapters/vba_macro_executor.py +215 -0
  29. solidworks_mcp/agents/__init__.py +68 -0
  30. solidworks_mcp/agents/design_knowledge/assembly_patterns.md +127 -0
  31. solidworks_mcp/agents/design_knowledge/hinges.md +118 -0
  32. solidworks_mcp/agents/design_knowledge/snap_fits.md +93 -0
  33. solidworks_mcp/agents/design_knowledge/solidworks_basics.md +3 -0
  34. solidworks_mcp/agents/design_knowledge/tolerancing.md +128 -0
  35. solidworks_mcp/agents/harness.py +216 -0
  36. solidworks_mcp/agents/history_db.py +1508 -0
  37. solidworks_mcp/agents/retrieval_index.py +164 -0
  38. solidworks_mcp/agents/schemas.py +165 -0
  39. solidworks_mcp/agents/smoke_test.py +285 -0
  40. solidworks_mcp/agents/soc_exporter.py +650 -0
  41. solidworks_mcp/agents/soc_pickup.py +332 -0
  42. solidworks_mcp/agents/soc_rewind.py +221 -0
  43. solidworks_mcp/agents/vector_rag.py +665 -0
  44. solidworks_mcp/cache/__init__.py +5 -0
  45. solidworks_mcp/cache/response_cache.py +164 -0
  46. solidworks_mcp/config.py +729 -0
  47. solidworks_mcp/exceptions.py +37 -0
  48. solidworks_mcp/security/__init__.py +68 -0
  49. solidworks_mcp/security/auth.py +128 -0
  50. solidworks_mcp/security/cors.py +27 -0
  51. solidworks_mcp/security/rate_limiting.py +90 -0
  52. solidworks_mcp/security/runtime.py +155 -0
  53. solidworks_mcp/server.py +777 -0
  54. solidworks_mcp/server_cli_fixed.py +120 -0
  55. solidworks_mcp/setup/__init__.py +1 -0
  56. solidworks_mcp/setup/cli.py +399 -0
  57. solidworks_mcp/setup/clients.py +157 -0
  58. solidworks_mcp/setup/config_file.py +230 -0
  59. solidworks_mcp/setup/handshake.py +202 -0
  60. solidworks_mcp/setup/launcher.py +102 -0
  61. solidworks_mcp/setup/legacy.py +100 -0
  62. solidworks_mcp/setup/registration.py +193 -0
  63. solidworks_mcp/setup/solidworks.py +49 -0
  64. solidworks_mcp/tools/__init__.py +64 -0
  65. solidworks_mcp/tools/analysis.py +723 -0
  66. solidworks_mcp/tools/automation.py +578 -0
  67. solidworks_mcp/tools/docs_discovery.py +1476 -0
  68. solidworks_mcp/tools/drawing.py +1015 -0
  69. solidworks_mcp/tools/drawing_analysis.py +456 -0
  70. solidworks_mcp/tools/drawing_automation.py +627 -0
  71. solidworks_mcp/tools/engineering_data.py +466 -0
  72. solidworks_mcp/tools/error_classifier.py +232 -0
  73. solidworks_mcp/tools/export.py +833 -0
  74. solidworks_mcp/tools/file_facts.py +40 -0
  75. solidworks_mcp/tools/file_management.py +1473 -0
  76. solidworks_mcp/tools/input_compat.py +17 -0
  77. solidworks_mcp/tools/macro_recording.py +464 -0
  78. solidworks_mcp/tools/modeling.py +2360 -0
  79. solidworks_mcp/tools/sketching.py +1422 -0
  80. solidworks_mcp/tools/template_management.py +367 -0
  81. solidworks_mcp/tools/tool_boundary.py +280 -0
  82. solidworks_mcp/tools/vba_generation.py +1145 -0
  83. solidworks_mcp/ui/__init__.py +9 -0
  84. solidworks_mcp/ui/local_llm.py +725 -0
  85. solidworks_mcp/ui/routers/__init__.py +10 -0
  86. solidworks_mcp/ui/routers/checkpoint.py +24 -0
  87. solidworks_mcp/ui/routers/docs.py +47 -0
  88. solidworks_mcp/ui/routers/llm.py +83 -0
  89. solidworks_mcp/ui/routers/local_model.py +69 -0
  90. solidworks_mcp/ui/routers/model.py +75 -0
  91. solidworks_mcp/ui/routers/preview.py +38 -0
  92. solidworks_mcp/ui/routers/session.py +161 -0
  93. solidworks_mcp/ui/routers/viewer.py +154 -0
  94. solidworks_mcp/ui/schemas.py +502 -0
  95. solidworks_mcp/ui/server.py +298 -0
  96. solidworks_mcp/ui/service.py +481 -0
  97. solidworks_mcp/ui/services/__init__.py +136 -0
  98. solidworks_mcp/ui/services/_utils.py +958 -0
  99. solidworks_mcp/ui/services/checkpoint_service.py +1110 -0
  100. solidworks_mcp/ui/services/docs_service.py +282 -0
  101. solidworks_mcp/ui/services/llm_service.py +956 -0
  102. solidworks_mcp/ui/services/model_service.py +459 -0
  103. solidworks_mcp/ui/services/parameter_repair_service.py +314 -0
  104. solidworks_mcp/ui/services/preview_service.py +489 -0
  105. solidworks_mcp/ui/services/session_service.py +1389 -0
  106. solidworks_mcp/utils/__init__.py +9 -0
  107. solidworks_mcp/utils/feature_tree_classifier.py +342 -0
  108. solidworks_mcp/utils/logging.py +76 -0
  109. solidworks_mcp/utils/validation.py +77 -0
  110. solidworks_mcp/version.py +8 -0
  111. solidworks_mcp_python-1.5.0.dist-info/METADATA +410 -0
  112. solidworks_mcp_python-1.5.0.dist-info/RECORD +115 -0
  113. solidworks_mcp_python-1.5.0.dist-info/WHEEL +4 -0
  114. solidworks_mcp_python-1.5.0.dist-info/entry_points.txt +3 -0
  115. 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)