@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,145 @@
1
+ import os
2
+ import sys
3
+ import logging
4
+ import argparse
5
+ from pathlib import Path
6
+ from typing import Dict, Any, List, Optional, Tuple
7
+
8
+ # Configure logging
9
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
10
+ logger = logging.getLogger(__name__)
11
+
12
+ # Add project root to path
13
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
14
+
15
+ # Import components
16
+ from src.ai.venice_api import VeniceImageGenerator
17
+ from src.ai.sam_segmentation import SAMSegmenter
18
+ from src.models.threestudio_generator import ThreeStudioGenerator
19
+ from src.openscad_wrapper.wrapper import OpenSCADWrapper
20
+ from src.workflow.image_to_model_pipeline import ImageToModelPipeline
21
+ from src.config import (
22
+ VENICE_API_KEY, IMAGES_DIR, MASKS_DIR, MODELS_DIR, SCAD_DIR,
23
+ SAM2_CHECKPOINT_PATH, SAM2_MODEL_TYPE, SAM2_USE_GPU, THREESTUDIO_PATH
24
+ )
25
+
26
+ def test_pipeline(prompt: str, output_dir: Optional[str] = None,
27
+ venice_model: str = "fluently-xl", skip_steps: List[str] = None):
28
+ """
29
+ Test the full image-to-model pipeline.
30
+
31
+ Args:
32
+ prompt: Text prompt for image generation
33
+ output_dir: Directory to save pipeline results
34
+ venice_model: Venice.ai model to use for image generation
35
+ skip_steps: List of steps to skip ('image', 'segment', 'model3d', 'openscad')
36
+ """
37
+ # Use default output directory if not provided
38
+ if not output_dir:
39
+ output_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "output", "pipeline_test")
40
+
41
+ # Create output directory
42
+ os.makedirs(output_dir, exist_ok=True)
43
+
44
+ # Initialize skip_steps if None
45
+ skip_steps = skip_steps or []
46
+
47
+ logger.info(f"Testing image-to-model pipeline with prompt: {prompt}")
48
+ logger.info(f"Output directory: {output_dir}")
49
+ logger.info(f"Venice model: {venice_model}")
50
+ logger.info(f"Skipping steps: {skip_steps}")
51
+
52
+ try:
53
+ # Initialize components
54
+ logger.info("Initializing pipeline components...")
55
+
56
+ # Venice.ai image generator
57
+ venice_generator = VeniceImageGenerator(
58
+ api_key=VENICE_API_KEY,
59
+ output_dir=os.path.join(output_dir, "images")
60
+ )
61
+
62
+ # SAM2 segmenter
63
+ sam_segmenter = SAMSegmenter(
64
+ model_type=SAM2_MODEL_TYPE,
65
+ checkpoint_path=SAM2_CHECKPOINT_PATH,
66
+ use_gpu=SAM2_USE_GPU,
67
+ output_dir=os.path.join(output_dir, "masks")
68
+ )
69
+
70
+ # ThreeStudio generator
71
+ threestudio_generator = ThreeStudioGenerator(
72
+ threestudio_path=THREESTUDIO_PATH,
73
+ output_dir=os.path.join(output_dir, "models")
74
+ )
75
+
76
+ # OpenSCAD wrapper
77
+ openscad_wrapper = OpenSCADWrapper(
78
+ output_dir=os.path.join(output_dir, "scad")
79
+ )
80
+
81
+ # Initialize pipeline
82
+ pipeline = ImageToModelPipeline(
83
+ venice_generator=venice_generator,
84
+ sam_segmenter=sam_segmenter,
85
+ threestudio_generator=threestudio_generator,
86
+ openscad_wrapper=openscad_wrapper,
87
+ output_dir=output_dir
88
+ )
89
+
90
+ # Run pipeline with custom steps
91
+ if 'image' in skip_steps:
92
+ # Skip image generation, use a test image
93
+ logger.info("Skipping image generation, using test image")
94
+ image_path = os.path.join(IMAGES_DIR, "test_image.png")
95
+ if not os.path.exists(image_path):
96
+ logger.error(f"Test image not found: {image_path}")
97
+ return
98
+
99
+ # TODO: Implement custom pipeline execution with skipped steps
100
+ logger.info("Custom pipeline execution not implemented yet")
101
+ return
102
+ else:
103
+ # Run full pipeline
104
+ logger.info("Running full pipeline...")
105
+ result = pipeline.generate_model_from_text(
106
+ prompt=prompt,
107
+ venice_params={"model": venice_model},
108
+ sam_params={},
109
+ threestudio_params={}
110
+ )
111
+
112
+ # Print results
113
+ logger.info("Pipeline completed successfully")
114
+ logger.info(f"Pipeline ID: {result.get('pipeline_id')}")
115
+ logger.info(f"Image path: {result.get('image', {}).get('local_path')}")
116
+ logger.info(f"Mask count: {result.get('segmentation', {}).get('num_masks', 0)}")
117
+ logger.info(f"3D model path: {result.get('model_3d', {}).get('exported_files', [])}")
118
+ logger.info(f"OpenSCAD file: {result.get('openscad', {}).get('scad_file')}")
119
+
120
+ return result
121
+
122
+ except Exception as e:
123
+ logger.error(f"Error in pipeline: {str(e)}")
124
+ import traceback
125
+ traceback.print_exc()
126
+ return None
127
+
128
+ if __name__ == "__main__":
129
+ # Parse command line arguments
130
+ parser = argparse.ArgumentParser(description="Test image-to-model pipeline")
131
+ parser.add_argument("prompt", help="Text prompt for image generation")
132
+ parser.add_argument("--output-dir", help="Directory to save pipeline results")
133
+ parser.add_argument("--venice-model", default="fluently-xl", help="Venice.ai model to use")
134
+ parser.add_argument("--skip", nargs="+", choices=["image", "segment", "model3d", "openscad"],
135
+ help="Steps to skip in the pipeline")
136
+
137
+ args = parser.parse_args()
138
+
139
+ # Run test
140
+ test_pipeline(
141
+ args.prompt,
142
+ args.output_dir,
143
+ args.venice_model,
144
+ args.skip
145
+ )
@@ -0,0 +1,41 @@
1
+ import os
2
+ import logging
3
+ from src.ai.venice_api import VeniceImageGenerator
4
+
5
+ # Configure logging
6
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
7
+ logger = logging.getLogger(__name__)
8
+
9
+ # Venice.ai API key (replace with your own or use environment variable)
10
+ VENICE_API_KEY = os.getenv("VENICE_API_KEY", "B9Y68yQgatQw8wmpmnIMYcGip1phCt-43CS0OktZU6")
11
+ OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "output", "images")
12
+
13
+ # Test natural language model selection
14
+ def test_model_selection():
15
+ """Test the natural language model selection functionality."""
16
+ # Initialize the Venice API client
17
+ venice_generator = VeniceImageGenerator(VENICE_API_KEY, OUTPUT_DIR)
18
+
19
+ # Test cases - natural language preferences to expected model mappings
20
+ test_cases = [
21
+ ("default", "fluently-xl"),
22
+ ("fastest model please", "fluently-xl"),
23
+ ("I need a high quality image", "flux-dev"),
24
+ ("create an uncensored image", "flux-dev-uncensored"),
25
+ ("make it realistic", "pony-realism"),
26
+ ("I want something artistic", "lustify-sdxl"),
27
+ ("use stable diffusion", "stable-diffusion-3.5"),
28
+ ("invalid model name", "fluently-xl"), # Should default to fluently-xl
29
+ ]
30
+
31
+ # Run tests
32
+ for preference, expected_model in test_cases:
33
+ mapped_model = venice_generator.map_model_preference(preference)
34
+ logger.info(f"Preference: '{preference}' -> Model: '{mapped_model}'")
35
+ assert mapped_model == expected_model, f"Expected {expected_model}, got {mapped_model}"
36
+
37
+ logger.info("All model preference mappings tests passed!")
38
+
39
+ if __name__ == "__main__":
40
+ logger.info("Starting Venice.ai model selection mapping tests")
41
+ test_model_selection()
@@ -0,0 +1,290 @@
1
+ """
2
+ Test script for multi-view to model pipeline.
3
+ """
4
+
5
+ import os
6
+ import sys
7
+ import logging
8
+ import unittest
9
+ from unittest.mock import patch, MagicMock
10
+ from pathlib import Path
11
+
12
+ # Add the src directory to the path
13
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
14
+
15
+ from src.workflow.multi_view_to_model_pipeline import MultiViewToModelPipeline
16
+ from src.ai.gemini_api import GeminiImageGenerator
17
+ from src.models.cuda_mvs import CUDAMultiViewStereo
18
+ from src.workflow.image_approval import ImageApprovalTool
19
+
20
+ # Configure logging
21
+ logging.basicConfig(level=logging.INFO)
22
+ logger = logging.getLogger(__name__)
23
+
24
+ class TestMultiViewPipeline(unittest.TestCase):
25
+ """
26
+ Test cases for multi-view to model pipeline.
27
+ """
28
+
29
+ def setUp(self):
30
+ """
31
+ Set up test environment.
32
+ """
33
+ # Create test directories
34
+ self.test_output_dir = "output/test_pipeline"
35
+ self.test_images_dir = os.path.join(self.test_output_dir, "images")
36
+ self.test_models_dir = os.path.join(self.test_output_dir, "models")
37
+
38
+ for directory in [self.test_output_dir, self.test_images_dir, self.test_models_dir]:
39
+ os.makedirs(directory, exist_ok=True)
40
+
41
+ # Create mock CUDA MVS path
42
+ self.cuda_mvs_path = "mock_cuda_mvs"
43
+ os.makedirs(os.path.join(self.cuda_mvs_path, "build"), exist_ok=True)
44
+
45
+ # Create mock executable
46
+ with open(os.path.join(self.cuda_mvs_path, "build", "app_patch_match_mvs"), "w") as f:
47
+ f.write("#!/bin/bash\necho 'Mock CUDA MVS'\n")
48
+ os.chmod(os.path.join(self.cuda_mvs_path, "build", "app_patch_match_mvs"), 0o755)
49
+
50
+ # Create mock components
51
+ self.mock_gemini = MagicMock(spec=GeminiImageGenerator)
52
+ self.mock_cuda_mvs = MagicMock(spec=CUDAMultiViewStereo)
53
+ self.mock_approval = MagicMock(spec=ImageApprovalTool)
54
+
55
+ # Configure mock responses
56
+ self.configure_mocks()
57
+
58
+ # Create the pipeline with mock components
59
+ self.pipeline = MultiViewToModelPipeline(
60
+ gemini_generator=self.mock_gemini,
61
+ cuda_mvs=self.mock_cuda_mvs,
62
+ approval_tool=self.mock_approval,
63
+ output_dir=self.test_output_dir
64
+ )
65
+
66
+ def configure_mocks(self):
67
+ """
68
+ Configure mock responses for components.
69
+ """
70
+ # Mock Gemini image generation
71
+ def mock_generate_image(prompt, **kwargs):
72
+ image_path = os.path.join(self.test_images_dir, f"{prompt[:10].replace(' ', '_')}.png")
73
+ with open(image_path, "w") as f:
74
+ f.write(f"Mock image for {prompt}")
75
+ return {
76
+ "prompt": prompt,
77
+ "local_path": image_path,
78
+ "image_data": b"mock_image_data"
79
+ }
80
+
81
+ def mock_generate_multiple_views(prompt, num_views, **kwargs):
82
+ results = []
83
+ for i in range(num_views):
84
+ image_path = os.path.join(self.test_images_dir, f"view_{i}.png")
85
+ with open(image_path, "w") as f:
86
+ f.write(f"Mock image for {prompt} - view {i}")
87
+ results.append({
88
+ "prompt": f"{prompt} - view {i}",
89
+ "local_path": image_path,
90
+ "image_data": b"mock_image_data",
91
+ "view_direction": f"view {i}",
92
+ "view_index": i + 1
93
+ })
94
+ return results
95
+
96
+ self.mock_gemini.generate_image.side_effect = mock_generate_image
97
+ self.mock_gemini.generate_multiple_views.side_effect = mock_generate_multiple_views
98
+
99
+ # Mock CUDA MVS
100
+ def mock_generate_model(image_paths, **kwargs):
101
+ model_dir = os.path.join(self.test_models_dir, "mock_model")
102
+ os.makedirs(model_dir, exist_ok=True)
103
+
104
+ point_cloud_file = os.path.join(model_dir, "mock_model.ply")
105
+ with open(point_cloud_file, "w") as f:
106
+ f.write("Mock point cloud")
107
+
108
+ obj_file = os.path.join(model_dir, "mock_model.obj")
109
+ with open(obj_file, "w") as f:
110
+ f.write("Mock OBJ file")
111
+
112
+ return {
113
+ "model_id": "mock_model",
114
+ "output_dir": model_dir,
115
+ "point_cloud_file": point_cloud_file,
116
+ "obj_file": obj_file,
117
+ "input_images": image_paths
118
+ }
119
+
120
+ self.mock_cuda_mvs.generate_model_from_images.side_effect = mock_generate_model
121
+ self.mock_cuda_mvs.convert_ply_to_obj.return_value = os.path.join(self.test_models_dir, "mock_model", "mock_model.obj")
122
+
123
+ # Mock approval tool
124
+ def mock_present_image(image_path, metadata):
125
+ return {
126
+ "approval_id": os.path.basename(image_path).split('.')[0],
127
+ "image_path": image_path,
128
+ "image_url": f"/images/{os.path.basename(image_path)}",
129
+ "metadata": metadata or {}
130
+ }
131
+
132
+ def mock_process_approval(approval_id, approved, image_path):
133
+ if approved:
134
+ approved_path = os.path.join(self.test_output_dir, "approved", os.path.basename(image_path))
135
+ os.makedirs(os.path.dirname(approved_path), exist_ok=True)
136
+ with open(approved_path, "w") as f:
137
+ f.write(f"Approved image {approval_id}")
138
+
139
+ return {
140
+ "approval_id": approval_id,
141
+ "approved": True,
142
+ "original_path": image_path,
143
+ "approved_path": approved_path
144
+ }
145
+ else:
146
+ return {
147
+ "approval_id": approval_id,
148
+ "approved": False,
149
+ "original_path": image_path
150
+ }
151
+
152
+ self.mock_approval.present_image_for_approval.side_effect = mock_present_image
153
+ self.mock_approval.process_approval.side_effect = mock_process_approval
154
+ self.mock_approval.get_approved_images.return_value = [
155
+ os.path.join(self.test_output_dir, "approved", f"view_{i}.png") for i in range(3)
156
+ ]
157
+
158
+ def test_generate_model_from_text(self):
159
+ """
160
+ Test generating a 3D model from text prompt.
161
+ """
162
+ # Test parameters
163
+ prompt = "A low-poly rabbit"
164
+ num_views = 3
165
+
166
+ # Mock approvals - approve all images
167
+ def mock_get_approval(approval_request):
168
+ return True
169
+
170
+ # Call the method
171
+ result = self.pipeline.generate_model_from_text(
172
+ prompt, num_views=num_views, get_approval_callback=mock_get_approval
173
+ )
174
+
175
+ # Verify the result
176
+ self.assertIsNotNone(result)
177
+ self.assertTrue("model_id" in result)
178
+ self.assertTrue("obj_file" in result)
179
+ self.assertTrue("point_cloud_file" in result)
180
+
181
+ # Verify component calls
182
+ self.mock_gemini.generate_multiple_views.assert_called_once_with(
183
+ prompt, num_views=num_views, output_dir=os.path.join(self.test_output_dir, "multi_view")
184
+ )
185
+
186
+ self.assertEqual(self.mock_approval.present_image_for_approval.call_count, num_views)
187
+ self.assertEqual(self.mock_approval.process_approval.call_count, num_views)
188
+
189
+ self.mock_cuda_mvs.generate_model_from_images.assert_called_once()
190
+ self.mock_cuda_mvs.convert_ply_to_obj.assert_called_once()
191
+
192
+ def test_generate_model_from_image(self):
193
+ """
194
+ Test generating a 3D model from a base image.
195
+ """
196
+ # Create a mock base image
197
+ base_image_path = os.path.join(self.test_images_dir, "base_image.png")
198
+ with open(base_image_path, "w") as f:
199
+ f.write("Mock base image")
200
+
201
+ # Test parameters
202
+ prompt = "A low-poly rabbit based on this image"
203
+ num_views = 3
204
+
205
+ # Mock approvals - approve all images
206
+ def mock_get_approval(approval_request):
207
+ return True
208
+
209
+ # Call the method
210
+ result = self.pipeline.generate_model_from_image(
211
+ base_image_path, prompt, num_views=num_views, get_approval_callback=mock_get_approval
212
+ )
213
+
214
+ # Verify the result
215
+ self.assertIsNotNone(result)
216
+ self.assertTrue("model_id" in result)
217
+ self.assertTrue("obj_file" in result)
218
+ self.assertTrue("point_cloud_file" in result)
219
+
220
+ # Verify component calls
221
+ self.mock_gemini.generate_multiple_views.assert_called_once_with(
222
+ prompt, num_views=num_views, base_image_path=base_image_path,
223
+ output_dir=os.path.join(self.test_output_dir, "multi_view")
224
+ )
225
+
226
+ self.assertEqual(self.mock_approval.present_image_for_approval.call_count, num_views)
227
+ self.assertEqual(self.mock_approval.process_approval.call_count, num_views)
228
+
229
+ self.mock_cuda_mvs.generate_model_from_images.assert_called_once()
230
+ self.mock_cuda_mvs.convert_ply_to_obj.assert_called_once()
231
+
232
+ def test_selective_approval(self):
233
+ """
234
+ Test selective approval of generated images.
235
+ """
236
+ # Test parameters
237
+ prompt = "A low-poly rabbit"
238
+ num_views = 4
239
+
240
+ # Mock approvals - only approve views 0 and 2
241
+ def mock_get_approval(approval_request):
242
+ view_index = int(approval_request["approval_id"].split('_')[1])
243
+ return view_index % 2 == 0 # Approve even-indexed views
244
+
245
+ # Call the method
246
+ result = self.pipeline.generate_model_from_text(
247
+ prompt, num_views=num_views, get_approval_callback=mock_get_approval
248
+ )
249
+
250
+ # Verify the result
251
+ self.assertIsNotNone(result)
252
+
253
+ # Verify component calls
254
+ self.assertEqual(self.mock_approval.present_image_for_approval.call_count, num_views)
255
+ self.assertEqual(self.mock_approval.process_approval.call_count, num_views)
256
+
257
+ # Only 2 images should be approved and used for model generation
258
+ approved_images = [call[0][0] for call in self.mock_cuda_mvs.generate_model_from_images.call_args_list]
259
+ if approved_images:
260
+ self.assertEqual(len(approved_images[0]), 2) # Only 2 images approved
261
+
262
+ def test_error_handling(self):
263
+ """
264
+ Test error handling in the pipeline.
265
+ """
266
+ # Test parameters
267
+ prompt = "A low-poly rabbit"
268
+
269
+ # Mock error in Gemini API
270
+ self.mock_gemini.generate_multiple_views.side_effect = Exception("Mock API error")
271
+
272
+ # Call the method and expect an exception
273
+ with self.assertRaises(Exception):
274
+ self.pipeline.generate_model_from_text(prompt)
275
+
276
+ def tearDown(self):
277
+ """
278
+ Clean up after tests.
279
+ """
280
+ # Clean up test output directory
281
+ import shutil
282
+ if os.path.exists(self.test_output_dir):
283
+ shutil.rmtree(self.test_output_dir)
284
+
285
+ # Clean up mock CUDA MVS path
286
+ if os.path.exists(self.cuda_mvs_path):
287
+ shutil.rmtree(self.cuda_mvs_path)
288
+
289
+ if __name__ == "__main__":
290
+ unittest.main()
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+ # Test OpenSCAD primitives with different export formats
3
+
4
+ PYTHON="python"
5
+ OUTPUT_DIR="test_output"
6
+
7
+ # Create output directory
8
+ mkdir -p $OUTPUT_DIR
9
+
10
+ # Run the tests
11
+ $PYTHON -m src.testing.test_primitives --output-dir $OUTPUT_DIR --validate
12
+
13
+ echo "Tests completed. Results are in $OUTPUT_DIR"
@@ -0,0 +1,71 @@
1
+ import os
2
+ import sys
3
+ import json
4
+ import requests
5
+ import base64
6
+ import logging
7
+
8
+ # Configure logging
9
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
10
+ logger = logging.getLogger(__name__)
11
+
12
+ # Venice.ai API configuration
13
+ VENICE_API_KEY = os.getenv("VENICE_API_KEY", "B9Y68yQgatQw8wmpmnIMYcGip1phCt-43CS0OktZU6")
14
+ OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "output", "images")
15
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
16
+
17
+ # API configuration
18
+ url = "https://api.venice.ai/api/v1/image/generate"
19
+ headers = {
20
+ "Authorization": f"Bearer {VENICE_API_KEY}",
21
+ "Content-Type": "application/json"
22
+ }
23
+
24
+ # Payload for image generation
25
+ payload = {
26
+ "height": 1024,
27
+ "width": 1024,
28
+ "steps": 20,
29
+ "return_binary": True, # Request binary data directly
30
+ "hide_watermark": False,
31
+ "format": "png",
32
+ "embed_exif_metadata": False,
33
+ "model": "flux-dev",
34
+ "prompt": "A low-poly rabbit with black background. 3d file"
35
+ }
36
+
37
+ def generate_image():
38
+ """Generate image using Venice.ai API with the rabbit prompt."""
39
+ try:
40
+ logger.info(f"Sending request to {url} with prompt: '{payload['prompt']}'")
41
+ response = requests.post(url, json=payload, headers=headers)
42
+
43
+ logger.info(f"Response status: {response.status_code}")
44
+
45
+ if response.status_code == 200:
46
+ # Save the raw binary response
47
+ filename = "rabbit_low_poly_3d.png"
48
+ output_path = os.path.join(OUTPUT_DIR, filename)
49
+
50
+ with open(output_path, "wb") as f:
51
+ f.write(response.content)
52
+
53
+ logger.info(f"Image saved to {output_path}")
54
+ return output_path
55
+ else:
56
+ logger.error(f"Error: {response.status_code} - {response.text}")
57
+
58
+ return None
59
+ except Exception as e:
60
+ logger.error(f"Error: {str(e)}")
61
+ return None
62
+
63
+ if __name__ == "__main__":
64
+ logger.info("Starting Venice.ai image generation test with rabbit prompt")
65
+ image_path = generate_image()
66
+
67
+ if image_path:
68
+ logger.info(f"Successfully generated and saved image to {image_path}")
69
+ print(f"\nImage saved to: {image_path}")
70
+ else:
71
+ logger.error("Failed to generate image")