@hybridlabor-api/bdb-hardware-pcb 0.1.0

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 (192) hide show
  1. package/README.md +116 -0
  2. package/config/mcp/antigravity.json +22 -0
  3. package/config/mcp/claude.json +22 -0
  4. package/config/mcp/codex.toml +25 -0
  5. package/docs/adr/ADR-001-KICAD-OPENSCAD-MCP-STRATEGY.md +334 -0
  6. package/docs/review_documentation.md +121 -0
  7. package/installer.js +358 -0
  8. package/mcp_servers/kicad-mcp-server/.env.example +22 -0
  9. package/mcp_servers/kicad-mcp-server/.github/workflows/ci.yml +36 -0
  10. package/mcp_servers/kicad-mcp-server/CLAUDE.md +487 -0
  11. package/mcp_servers/kicad-mcp-server/README.md +316 -0
  12. package/mcp_servers/kicad-mcp-server/docs/DEVICE_TREE.md +416 -0
  13. package/mcp_servers/kicad-mcp-server/docs/INSTALLATION.md +332 -0
  14. package/mcp_servers/kicad-mcp-server/docs/PIN_ANALYSIS.md +332 -0
  15. package/mcp_servers/kicad-mcp-server/docs/README.md +240 -0
  16. package/mcp_servers/kicad-mcp-server/docs/TESTING.md +613 -0
  17. package/mcp_servers/kicad-mcp-server/docs/VALIDATION.md +268 -0
  18. package/mcp_servers/kicad-mcp-server/pyproject.toml +96 -0
  19. package/mcp_servers/kicad-mcp-server/requirements-dev.txt +16 -0
  20. package/mcp_servers/kicad-mcp-server/requirements-test.txt +24 -0
  21. package/mcp_servers/kicad-mcp-server/requirements.txt +13 -0
  22. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/__init__.py +3 -0
  23. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/__main__.py +17 -0
  24. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/config.py +46 -0
  25. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/models/__init__.py +1 -0
  26. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/models/types.py +87 -0
  27. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/__init__.py +1 -0
  28. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/netlist_parser.py +234 -0
  29. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/pcb_parser.py +375 -0
  30. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/pcb_parser_kicad.py +327 -0
  31. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/schematic_parser.py +902 -0
  32. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/server.py +71 -0
  33. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/__init__.py +1 -0
  34. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/arduino/connectivity_test.cpp.j2 +189 -0
  35. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/atmega.dts.j2 +77 -0
  36. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/esp32.dts.j2 +77 -0
  37. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/nrf52.dts.j2 +77 -0
  38. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/stm32f4.dts.j2 +89 -0
  39. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/esp_idf/test_suite.c.j2 +340 -0
  40. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/pytest/test_connectivity.py.j2 +147 -0
  41. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/st_hal/hal_test.c.j2 +313 -0
  42. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_gpio_test.py.j2 +99 -0
  43. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_i2c_test.py.j2 +117 -0
  44. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_pinmux_test.py.j2 +43 -0
  45. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_spi_test.py.j2 +94 -0
  46. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_gpio_test.c.j2 +113 -0
  47. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_i2c_test.c.j2 +101 -0
  48. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_spi_test.c.j2 +94 -0
  49. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/unittest/test_schematic.py.j2 +172 -0
  50. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/__init__.py +36 -0
  51. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/device_tree.py +1187 -0
  52. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/hierarchical_analysis.py +211 -0
  53. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/netlist.py +320 -0
  54. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/parts_registry.py +142 -0
  55. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pcb.py +955 -0
  56. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pcb_layout.py +308 -0
  57. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pin_analysis.py +765 -0
  58. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/project.py +196 -0
  59. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic.py +319 -0
  60. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic_editor.py +674 -0
  61. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic_search.py +158 -0
  62. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/validation.py +866 -0
  63. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/visualization.py +225 -0
  64. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/__init__.py +1 -0
  65. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/file_handlers.py +65 -0
  66. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/kicad_cli.py +103 -0
  67. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/kicad_version.py +103 -0
  68. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/parts_registry.py +197 -0
  69. package/mcp_servers/kicad-mcp-server/tests/__init__.py +1 -0
  70. package/mcp_servers/kicad-mcp-server/tests/examples/ESP32S3_TEST.md +219 -0
  71. package/mcp_servers/kicad-mcp-server/tests/fixtures/README.md +65 -0
  72. package/mcp_servers/kicad-mcp-server/tests/fixtures/__init__.py +1 -0
  73. package/mcp_servers/kicad-mcp-server/tests/fixtures/example_pcb.kicad_pcb +177 -0
  74. package/mcp_servers/kicad-mcp-server/tests/fixtures/example_schematic.kicad_sch +145 -0
  75. package/mcp_servers/kicad-mcp-server/tests/fixtures/hier/child.kicad_sch +24 -0
  76. package/mcp_servers/kicad-mcp-server/tests/fixtures/hier/root.kicad_sch +38 -0
  77. package/mcp_servers/kicad-mcp-server/tests/test_tools/__init__.py +1 -0
  78. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_hierarchical_labels.py +195 -0
  79. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_kicad_cli.py +116 -0
  80. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_netlist_cache_path.py +29 -0
  81. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_schematic.py +189 -0
  82. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_schematic_hierarchy.py +69 -0
  83. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_visualization.py +136 -0
  84. package/mcp_servers/kicad-mcp-server/uv.lock +2873 -0
  85. package/mcp_servers/openscad-mcp-server/.dockerignore +9 -0
  86. package/mcp_servers/openscad-mcp-server/.github/workflows/test.yml +40 -0
  87. package/mcp_servers/openscad-mcp-server/Dockerfile +29 -0
  88. package/mcp_servers/openscad-mcp-server/LICENSE +21 -0
  89. package/mcp_servers/openscad-mcp-server/README.md +154 -0
  90. package/mcp_servers/openscad-mcp-server/docs/audit.md +56 -0
  91. package/mcp_servers/openscad-mcp-server/docs/docker.md +66 -0
  92. package/mcp_servers/openscad-mcp-server/docs/issue-followup.md +19 -0
  93. package/mcp_servers/openscad-mcp-server/docs/jetson.md +17 -0
  94. package/mcp_servers/openscad-mcp-server/glama.json +4 -0
  95. package/mcp_servers/openscad-mcp-server/legacy/README.md +15 -0
  96. package/mcp_servers/openscad-mcp-server/legacy/README.original.md +294 -0
  97. package/mcp_servers/openscad-mcp-server/legacy/implementation_plan.md +100 -0
  98. package/mcp_servers/openscad-mcp-server/legacy/old/download_sam2_checkpoint.py +115 -0
  99. package/mcp_servers/openscad-mcp-server/legacy/old/src/ai/sam_segmentation.py +209 -0
  100. package/mcp_servers/openscad-mcp-server/legacy/old/src/models/threestudio_generator.py +231 -0
  101. package/mcp_servers/openscad-mcp-server/legacy/old/src/workflow/image_to_model_pipeline.py +260 -0
  102. package/mcp_servers/openscad-mcp-server/legacy/old/test_sam2_segmentation.py +96 -0
  103. package/mcp_servers/openscad-mcp-server/legacy/requirements.txt +57 -0
  104. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/README.md +39 -0
  105. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/decisions/ai-driven-code-generation.md +122 -0
  106. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/decisions/export-formats.md +76 -0
  107. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/ai/ai_service.py.md +51 -0
  108. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/main.py.md +63 -0
  109. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/models/code_generator.py.md +63 -0
  110. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/nlp/parameter_extractor.py.md +63 -0
  111. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/ai/natural-language-processing.md +78 -0
  112. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/nlp/parameter-extraction.md +173 -0
  113. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/export-formats.md +91 -0
  114. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/openscad-basics.md +66 -0
  115. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/primitive-testing.md +79 -0
  116. package/mcp_servers/openscad-mcp-server/legacy/src/__init__.py +0 -0
  117. package/mcp_servers/openscad-mcp-server/legacy/src/ai/ai_service.py +257 -0
  118. package/mcp_servers/openscad-mcp-server/legacy/src/ai/gemini_api.py +161 -0
  119. package/mcp_servers/openscad-mcp-server/legacy/src/ai/venice_api.py +203 -0
  120. package/mcp_servers/openscad-mcp-server/legacy/src/config.py +121 -0
  121. package/mcp_servers/openscad-mcp-server/legacy/src/main.py +1456 -0
  122. package/mcp_servers/openscad-mcp-server/legacy/src/main.py.new +404 -0
  123. package/mcp_servers/openscad-mcp-server/legacy/src/main_remote.py +401 -0
  124. package/mcp_servers/openscad-mcp-server/legacy/src/models/__init__.py +0 -0
  125. package/mcp_servers/openscad-mcp-server/legacy/src/models/code_generator.py +321 -0
  126. package/mcp_servers/openscad-mcp-server/legacy/src/models/cuda_mvs.py +209 -0
  127. package/mcp_servers/openscad-mcp-server/legacy/src/models/scad_templates/basic_shapes.scad +144 -0
  128. package/mcp_servers/openscad-mcp-server/legacy/src/nlp/__init__.py +0 -0
  129. package/mcp_servers/openscad-mcp-server/legacy/src/nlp/parameter_extractor.py +388 -0
  130. package/mcp_servers/openscad-mcp-server/legacy/src/openscad_wrapper/__init__.py +0 -0
  131. package/mcp_servers/openscad-mcp-server/legacy/src/openscad_wrapper/wrapper.py +418 -0
  132. package/mcp_servers/openscad-mcp-server/legacy/src/printer_discovery/__init__.py +1 -0
  133. package/mcp_servers/openscad-mcp-server/legacy/src/printer_discovery/printer_discovery.py +471 -0
  134. package/mcp_servers/openscad-mcp-server/legacy/src/remote/connection_manager.py +537 -0
  135. package/mcp_servers/openscad-mcp-server/legacy/src/remote/cuda_mvs_client.py +435 -0
  136. package/mcp_servers/openscad-mcp-server/legacy/src/remote/cuda_mvs_server.py +787 -0
  137. package/mcp_servers/openscad-mcp-server/legacy/src/remote/error_handling.py +415 -0
  138. package/mcp_servers/openscad-mcp-server/legacy/src/testing/__init__.py +0 -0
  139. package/mcp_servers/openscad-mcp-server/legacy/src/testing/primitive_tester.py +203 -0
  140. package/mcp_servers/openscad-mcp-server/legacy/src/testing/test_primitives.py +98 -0
  141. package/mcp_servers/openscad-mcp-server/legacy/src/utils/__init__.py +1 -0
  142. package/mcp_servers/openscad-mcp-server/legacy/src/utils/cad_exporter.py +241 -0
  143. package/mcp_servers/openscad-mcp-server/legacy/src/utils/format_validator.py +206 -0
  144. package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_exporter.py +140 -0
  145. package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_repair.py +91 -0
  146. package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_validator.py +123 -0
  147. package/mcp_servers/openscad-mcp-server/legacy/src/visualization/__init__.py +0 -0
  148. package/mcp_servers/openscad-mcp-server/legacy/src/visualization/headless_renderer.py +52 -0
  149. package/mcp_servers/openscad-mcp-server/legacy/src/visualization/renderer.py +177 -0
  150. package/mcp_servers/openscad-mcp-server/legacy/src/visualization/web_interface.py +639 -0
  151. package/mcp_servers/openscad-mcp-server/legacy/src/workflow/image_approval.py +148 -0
  152. package/mcp_servers/openscad-mcp-server/legacy/src/workflow/multi_view_to_model_pipeline.py +338 -0
  153. package/mcp_servers/openscad-mcp-server/legacy/test_complete_workflow.py +374 -0
  154. package/mcp_servers/openscad-mcp-server/legacy/test_cuda_mvs.py +191 -0
  155. package/mcp_servers/openscad-mcp-server/legacy/test_gemini_api.py +168 -0
  156. package/mcp_servers/openscad-mcp-server/legacy/test_image_approval.py +192 -0
  157. package/mcp_servers/openscad-mcp-server/legacy/test_image_approval_workflow.py +251 -0
  158. package/mcp_servers/openscad-mcp-server/legacy/test_image_to_model_pipeline.py +145 -0
  159. package/mcp_servers/openscad-mcp-server/legacy/test_model_selection.py +41 -0
  160. package/mcp_servers/openscad-mcp-server/legacy/test_multi_view_pipeline.py +290 -0
  161. package/mcp_servers/openscad-mcp-server/legacy/test_primitives.sh +13 -0
  162. package/mcp_servers/openscad-mcp-server/legacy/test_rabbit_direct.py +71 -0
  163. package/mcp_servers/openscad-mcp-server/legacy/test_remote_cuda_mvs.py +283 -0
  164. package/mcp_servers/openscad-mcp-server/legacy/test_venice_example.py +69 -0
  165. package/mcp_servers/openscad-mcp-server/pyproject.toml +33 -0
  166. package/mcp_servers/openscad-mcp-server/requirements.txt +2 -0
  167. package/mcp_servers/openscad-mcp-server/scad/simple_cube.scad +2 -0
  168. package/mcp_servers/openscad-mcp-server/scripts/test_docker.py +220 -0
  169. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/__init__.py +3 -0
  170. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/__main__.py +3 -0
  171. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/engine.py +94 -0
  172. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/geometry.py +242 -0
  173. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/server.py +245 -0
  174. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/service.py +190 -0
  175. package/mcp_servers/openscad-mcp-server/tests/conftest.py +17 -0
  176. package/mcp_servers/openscad-mcp-server/tests/test_engine.py +37 -0
  177. package/mcp_servers/openscad-mcp-server/tests/test_geometry.py +106 -0
  178. package/mcp_servers/openscad-mcp-server/tests/test_integration.py +172 -0
  179. package/mcp_servers/openscad-mcp-server/tests/test_transports.py +314 -0
  180. package/mcp_servers/openscad-mcp-server/uv.lock +1123 -0
  181. package/package.json +44 -0
  182. package/scripts/install_mcps.sh +86 -0
  183. package/scripts/openscad_wrapper.sh +62 -0
  184. package/scripts/run_kicad_mcp.sh +25 -0
  185. package/scripts/run_openscad_mcp.sh +41 -0
  186. package/scripts/test_mcp_connection.py +626 -0
  187. package/scripts/test_mcp_connection.sh +168 -0
  188. package/skills/code-first-hardware-design/SKILL.md +260 -0
  189. package/skills/pcb-constraint-definition/SKILL.md +236 -0
  190. package/skills/pcb-layout-routing-automation/SKILL.md +153 -0
  191. package/skills/pcb-validation-dfm-signoff/SKILL.md +194 -0
  192. package/skills/schematic-datasheet-analysis/SKILL.md +202 -0
@@ -0,0 +1,148 @@
1
+ """
2
+ Image approval tool for MCP clients.
3
+ """
4
+
5
+ import os
6
+ import logging
7
+ import shutil
8
+ from typing import Dict, Any, List, Optional
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ class ImageApprovalTool:
13
+ """
14
+ Tool for image approval/denial in MCP clients.
15
+ """
16
+
17
+ def __init__(self, output_dir: str = "output/approved_images"):
18
+ """
19
+ Initialize the image approval tool.
20
+
21
+ Args:
22
+ output_dir: Directory to store approved images
23
+ """
24
+ self.output_dir = output_dir
25
+
26
+ # Create output directory if it doesn't exist
27
+ os.makedirs(output_dir, exist_ok=True)
28
+
29
+ def present_image_for_approval(self, image_path: str, metadata: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
30
+ """
31
+ Present an image to the user for approval.
32
+
33
+ Args:
34
+ image_path: Path to the image
35
+ metadata: Optional metadata about the image
36
+
37
+ Returns:
38
+ Dictionary with image path and approval request ID
39
+ """
40
+ # For MCP server, we just prepare the response
41
+ # The actual approval is handled by the client
42
+
43
+ approval_id = os.path.basename(image_path).split('.')[0]
44
+
45
+ return {
46
+ "approval_id": approval_id,
47
+ "image_path": image_path,
48
+ "image_url": f"/images/{os.path.basename(image_path)}",
49
+ "metadata": metadata or {}
50
+ }
51
+
52
+ def process_approval(self, approval_id: str, approved: bool, image_path: str) -> Dict[str, Any]:
53
+ """
54
+ Process user's approval or denial of an image.
55
+
56
+ Args:
57
+ approval_id: ID of the approval request
58
+ approved: Whether the image was approved
59
+ image_path: Path to the image
60
+
61
+ Returns:
62
+ Dictionary with approval status and image path
63
+ """
64
+ if approved:
65
+ # Copy approved image to output directory
66
+ approved_path = os.path.join(self.output_dir, os.path.basename(image_path))
67
+ os.makedirs(os.path.dirname(approved_path), exist_ok=True)
68
+ shutil.copy2(image_path, approved_path)
69
+
70
+ return {
71
+ "approval_id": approval_id,
72
+ "approved": True,
73
+ "original_path": image_path,
74
+ "approved_path": approved_path
75
+ }
76
+ else:
77
+ return {
78
+ "approval_id": approval_id,
79
+ "approved": False,
80
+ "original_path": image_path
81
+ }
82
+
83
+ def get_approved_images(self, filter_pattern: Optional[str] = None) -> List[str]:
84
+ """
85
+ Get list of approved images.
86
+
87
+ Args:
88
+ filter_pattern: Optional pattern to filter image names
89
+
90
+ Returns:
91
+ List of paths to approved images
92
+ """
93
+ import glob
94
+
95
+ if filter_pattern:
96
+ pattern = os.path.join(self.output_dir, filter_pattern)
97
+ else:
98
+ pattern = os.path.join(self.output_dir, "*")
99
+
100
+ return glob.glob(pattern)
101
+
102
+ def get_approval_status(self, approval_id: str) -> Dict[str, Any]:
103
+ """
104
+ Get the approval status for a specific approval ID.
105
+
106
+ Args:
107
+ approval_id: ID of the approval request
108
+
109
+ Returns:
110
+ Dictionary with approval status
111
+ """
112
+ # Check if any approved image matches the approval ID
113
+ approved_images = self.get_approved_images()
114
+
115
+ for image_path in approved_images:
116
+ if approval_id in os.path.basename(image_path):
117
+ return {
118
+ "approval_id": approval_id,
119
+ "approved": True,
120
+ "approved_path": image_path
121
+ }
122
+
123
+ return {
124
+ "approval_id": approval_id,
125
+ "approved": False
126
+ }
127
+
128
+ def batch_process_approvals(self, approvals: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
129
+ """
130
+ Process multiple approvals at once.
131
+
132
+ Args:
133
+ approvals: List of dictionaries with approval_id, approved, and image_path
134
+
135
+ Returns:
136
+ List of dictionaries with approval results
137
+ """
138
+ results = []
139
+
140
+ for approval in approvals:
141
+ result = self.process_approval(
142
+ approval_id=approval["approval_id"],
143
+ approved=approval["approved"],
144
+ image_path=approval["image_path"]
145
+ )
146
+ results.append(result)
147
+
148
+ return results
@@ -0,0 +1,338 @@
1
+ """
2
+ Workflow orchestration for the multi-view to model pipeline.
3
+ """
4
+
5
+ import os
6
+ import logging
7
+ import uuid
8
+ from typing import Dict, Any, List, Optional
9
+ from pathlib import Path
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ class MultiViewToModelPipeline:
14
+ """
15
+ Orchestrates the workflow from image or text prompt to 3D model:
16
+ 1. Generate image with Venice.ai or Google Gemini (optional)
17
+ 2. Generate multiple views with Google Gemini
18
+ 3. Process user approval of views
19
+ 4. Create 3D model with CUDA Multi-View Stereo
20
+ 5. Convert to OpenSCAD for parametric editing (optional)
21
+ """
22
+
23
+ def __init__(self,
24
+ gemini_generator=None,
25
+ venice_generator=None,
26
+ cuda_mvs=None,
27
+ openscad_wrapper=None,
28
+ approval_tool=None,
29
+ output_dir: str = "output/pipeline"):
30
+ """
31
+ Initialize the pipeline.
32
+
33
+ Args:
34
+ gemini_generator: Instance of GeminiImageGenerator
35
+ venice_generator: Instance of VeniceImageGenerator (optional)
36
+ cuda_mvs: Instance of CUDAMultiViewStereo
37
+ openscad_wrapper: Instance of OpenSCADWrapper (optional)
38
+ approval_tool: Instance of ImageApprovalTool
39
+ output_dir: Directory to store output files
40
+ """
41
+ self.gemini_generator = gemini_generator
42
+ self.venice_generator = venice_generator
43
+ self.cuda_mvs = cuda_mvs
44
+ self.openscad_wrapper = openscad_wrapper
45
+ self.approval_tool = approval_tool
46
+ self.output_dir = output_dir
47
+
48
+ # Create output directories
49
+ os.makedirs(os.path.join(output_dir, "images"), exist_ok=True)
50
+ os.makedirs(os.path.join(output_dir, "multi_view"), exist_ok=True)
51
+ os.makedirs(os.path.join(output_dir, "approved"), exist_ok=True)
52
+ os.makedirs(os.path.join(output_dir, "models"), exist_ok=True)
53
+ os.makedirs(os.path.join(output_dir, "scad"), exist_ok=True)
54
+
55
+ def generate_model_from_text(self, prompt: str,
56
+ use_venice: bool = False,
57
+ num_views: int = 4,
58
+ gemini_params: Optional[Dict[str, Any]] = None,
59
+ venice_params: Optional[Dict[str, Any]] = None,
60
+ cuda_mvs_params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
61
+ """
62
+ Generate a 3D model from a text prompt.
63
+
64
+ Args:
65
+ prompt: Text description for image generation
66
+ use_venice: Whether to use Venice.ai for initial image
67
+ num_views: Number of views to generate
68
+ gemini_params: Optional parameters for Google Gemini
69
+ venice_params: Optional parameters for Venice.ai
70
+ cuda_mvs_params: Optional parameters for CUDA MVS
71
+
72
+ Returns:
73
+ Dictionary containing paths to generated files and metadata
74
+ """
75
+ try:
76
+ # Generate a unique ID for this pipeline run
77
+ pipeline_id = str(uuid.uuid4())
78
+ logger.info(f"Starting pipeline {pipeline_id} for prompt: {prompt}")
79
+
80
+ # Step 1: Generate initial image
81
+ if use_venice and self.venice_generator:
82
+ # Use Venice.ai for initial image
83
+ logger.info("Using Venice.ai for initial image generation")
84
+ image_path = os.path.join(self.output_dir, "images", f"{pipeline_id}_venice.png")
85
+ initial_result = self.venice_generator.generate_image(
86
+ prompt=prompt,
87
+ output_path=image_path,
88
+ **(venice_params or {})
89
+ )
90
+ else:
91
+ # Use Google Gemini for initial image
92
+ logger.info("Using Google Gemini for initial image generation")
93
+ image_path = os.path.join(self.output_dir, "images", f"{pipeline_id}_gemini.png")
94
+ initial_result = self.gemini_generator.generate_image(
95
+ prompt=prompt,
96
+ output_path=image_path,
97
+ **(gemini_params or {})
98
+ )
99
+
100
+ # Step 2: Generate multiple views
101
+ logger.info(f"Generating {num_views} views with Google Gemini")
102
+ multi_view_dir = os.path.join(self.output_dir, "multi_view", pipeline_id)
103
+
104
+ multi_views = self.gemini_generator.generate_multiple_views(
105
+ prompt=prompt,
106
+ num_views=num_views,
107
+ base_image_path=image_path,
108
+ output_dir=multi_view_dir
109
+ )
110
+
111
+ # Step 3: Present images for approval
112
+ # In a real implementation, this would be handled by the MCP client
113
+ # through the MCP tools interface
114
+ logger.info("Preparing images for approval")
115
+ approval_requests = []
116
+ for view in multi_views:
117
+ approval_request = self.approval_tool.present_image_for_approval(
118
+ image_path=view["local_path"],
119
+ metadata={
120
+ "prompt": view.get("prompt"),
121
+ "view_direction": view.get("view_direction"),
122
+ "view_index": view.get("view_index")
123
+ }
124
+ )
125
+ approval_requests.append(approval_request)
126
+
127
+ # For the purpose of this implementation, we'll assume all views are approved
128
+ # In a real implementation, this would be handled by the MCP client
129
+ approved_images = []
130
+ for req in approval_requests:
131
+ approval_result = self.approval_tool.process_approval(
132
+ approval_id=req["approval_id"],
133
+ approved=True,
134
+ image_path=req["image_path"]
135
+ )
136
+ if approval_result["approved"]:
137
+ approved_images.append(approval_result["approved_path"])
138
+
139
+ # Step 4: Generate 3D model with CUDA MVS
140
+ logger.info("Generating 3D model with CUDA MVS")
141
+ model_result = self.cuda_mvs.generate_model_from_images(
142
+ image_paths=approved_images,
143
+ output_name=pipeline_id,
144
+ **(cuda_mvs_params or {})
145
+ )
146
+
147
+ # Step 5: Convert to OpenSCAD (if wrapper is available)
148
+ scad_result = None
149
+ if self.openscad_wrapper and model_result.get("point_cloud_file"):
150
+ logger.info("Converting to OpenSCAD")
151
+ scad_result = self._convert_to_openscad(model_result["point_cloud_file"], pipeline_id)
152
+
153
+ # Compile results
154
+ result = {
155
+ "pipeline_id": pipeline_id,
156
+ "prompt": prompt,
157
+ "initial_image": initial_result,
158
+ "multi_views": multi_views,
159
+ "approved_images": approved_images,
160
+ "model_3d": model_result,
161
+ }
162
+
163
+ if scad_result:
164
+ result["openscad"] = scad_result
165
+
166
+ logger.info(f"Pipeline {pipeline_id} completed successfully")
167
+ return result
168
+
169
+ except Exception as e:
170
+ logger.error(f"Error in pipeline: {str(e)}")
171
+ raise
172
+
173
+ def generate_model_from_image(self, image_path: str,
174
+ prompt: Optional[str] = None,
175
+ num_views: int = 4,
176
+ gemini_params: Optional[Dict[str, Any]] = None,
177
+ cuda_mvs_params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
178
+ """
179
+ Generate a 3D model from an existing image.
180
+
181
+ Args:
182
+ image_path: Path to input image
183
+ prompt: Optional text description to guide multi-view generation
184
+ num_views: Number of views to generate
185
+ gemini_params: Optional parameters for Google Gemini
186
+ cuda_mvs_params: Optional parameters for CUDA MVS
187
+
188
+ Returns:
189
+ Dictionary containing paths to generated files and metadata
190
+ """
191
+ try:
192
+ # Generate a unique ID for this pipeline run
193
+ pipeline_id = str(uuid.uuid4())
194
+ logger.info(f"Starting pipeline {pipeline_id} from image: {image_path}")
195
+
196
+ # Use provided prompt or generate one from the image
197
+ if not prompt:
198
+ # In a real implementation, you might use an image captioning model
199
+ # to generate a description of the image
200
+ prompt = f"3D object in the image {os.path.basename(image_path)}"
201
+
202
+ # Step 1: Generate multiple views
203
+ logger.info(f"Generating {num_views} views with Google Gemini")
204
+ multi_view_dir = os.path.join(self.output_dir, "multi_view", pipeline_id)
205
+
206
+ multi_views = self.gemini_generator.generate_multiple_views(
207
+ prompt=prompt,
208
+ num_views=num_views,
209
+ base_image_path=image_path,
210
+ output_dir=multi_view_dir
211
+ )
212
+
213
+ # Step 2: Present images for approval
214
+ logger.info("Preparing images for approval")
215
+ approval_requests = []
216
+ for view in multi_views:
217
+ approval_request = self.approval_tool.present_image_for_approval(
218
+ image_path=view["local_path"],
219
+ metadata={
220
+ "prompt": view.get("prompt"),
221
+ "view_direction": view.get("view_direction"),
222
+ "view_index": view.get("view_index")
223
+ }
224
+ )
225
+ approval_requests.append(approval_request)
226
+
227
+ # For the purpose of this implementation, we'll assume all views are approved
228
+ approved_images = []
229
+ for req in approval_requests:
230
+ approval_result = self.approval_tool.process_approval(
231
+ approval_id=req["approval_id"],
232
+ approved=True,
233
+ image_path=req["image_path"]
234
+ )
235
+ if approval_result["approved"]:
236
+ approved_images.append(approval_result["approved_path"])
237
+
238
+ # Step 3: Generate 3D model with CUDA MVS
239
+ logger.info("Generating 3D model with CUDA MVS")
240
+ model_result = self.cuda_mvs.generate_model_from_images(
241
+ image_paths=approved_images,
242
+ output_name=pipeline_id,
243
+ **(cuda_mvs_params or {})
244
+ )
245
+
246
+ # Step 4: Convert to OpenSCAD (if wrapper is available)
247
+ scad_result = None
248
+ if self.openscad_wrapper and model_result.get("point_cloud_file"):
249
+ logger.info("Converting to OpenSCAD")
250
+ scad_result = self._convert_to_openscad(model_result["point_cloud_file"], pipeline_id)
251
+
252
+ # Compile results
253
+ result = {
254
+ "pipeline_id": pipeline_id,
255
+ "prompt": prompt,
256
+ "input_image": image_path,
257
+ "multi_views": multi_views,
258
+ "approved_images": approved_images,
259
+ "model_3d": model_result,
260
+ }
261
+
262
+ if scad_result:
263
+ result["openscad"] = scad_result
264
+
265
+ logger.info(f"Pipeline {pipeline_id} completed successfully")
266
+ return result
267
+
268
+ except Exception as e:
269
+ logger.error(f"Error in pipeline: {str(e)}")
270
+ raise
271
+
272
+ def _convert_to_openscad(self, model_path: str, model_id: str) -> Dict[str, Any]:
273
+ """
274
+ Convert 3D model to OpenSCAD format.
275
+
276
+ Args:
277
+ model_path: Path to input model (PLY file)
278
+ model_id: Unique identifier for the model
279
+
280
+ Returns:
281
+ Dictionary containing paths to generated files
282
+ """
283
+ logger.info(f"Converting model to OpenSCAD: {model_path}")
284
+
285
+ # Convert PLY to OBJ if needed
286
+ if model_path.endswith('.ply'):
287
+ obj_path = self.cuda_mvs.convert_ply_to_obj(model_path)
288
+ model_path = obj_path
289
+
290
+ # Generate OpenSCAD code for importing the model
291
+ scad_code = f"""// Generated OpenSCAD code for model {model_id}
292
+ // Imported from {os.path.basename(model_path)}
293
+
294
+ // Parameters
295
+ scale_factor = 1.0;
296
+ position_x = 0;
297
+ position_y = 0;
298
+ position_z = 0;
299
+ rotation_x = 0;
300
+ rotation_y = 0;
301
+ rotation_z = 0;
302
+
303
+ // Import and transform the model
304
+ translate([position_x, position_y, position_z])
305
+ rotate([rotation_x, rotation_y, rotation_z])
306
+ scale(scale_factor)
307
+ import("{model_path}");
308
+ """
309
+
310
+ # Save SCAD code to file
311
+ scad_file = self.openscad_wrapper.generate_scad(scad_code, model_id)
312
+
313
+ # Generate previews
314
+ previews = self.openscad_wrapper.generate_multi_angle_previews(scad_file)
315
+
316
+ return {
317
+ "scad_file": scad_file,
318
+ "previews": previews,
319
+ "model_path": model_path
320
+ }
321
+
322
+ def process_approval_results(self, approval_results: List[Dict[str, Any]]) -> List[str]:
323
+ """
324
+ Process approval results from the MCP client.
325
+
326
+ Args:
327
+ approval_results: List of approval results from the client
328
+
329
+ Returns:
330
+ List of paths to approved images
331
+ """
332
+ approved_images = []
333
+
334
+ for result in approval_results:
335
+ if result.get("approved", False) and "approved_path" in result:
336
+ approved_images.append(result["approved_path"])
337
+
338
+ return approved_images