foodforthought-cli 0.2.1__py3-none-any.whl → 0.2.3__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 (46) hide show
  1. ate/__init__.py +1 -1
  2. ate/bridge_server.py +622 -0
  3. ate/cli.py +2625 -242
  4. ate/compatibility.py +580 -0
  5. ate/generators/__init__.py +19 -0
  6. ate/generators/docker_generator.py +461 -0
  7. ate/generators/hardware_config.py +469 -0
  8. ate/generators/ros2_generator.py +617 -0
  9. ate/generators/skill_generator.py +783 -0
  10. ate/marketplace.py +524 -0
  11. ate/mcp_server.py +1341 -107
  12. ate/primitives.py +1016 -0
  13. ate/robot_setup.py +2222 -0
  14. ate/skill_schema.py +537 -0
  15. ate/telemetry/__init__.py +33 -0
  16. ate/telemetry/cli.py +455 -0
  17. ate/telemetry/collector.py +444 -0
  18. ate/telemetry/context.py +318 -0
  19. ate/telemetry/fleet_agent.py +419 -0
  20. ate/telemetry/formats/__init__.py +18 -0
  21. ate/telemetry/formats/hdf5_serializer.py +503 -0
  22. ate/telemetry/formats/mcap_serializer.py +457 -0
  23. ate/telemetry/types.py +334 -0
  24. foodforthought_cli-0.2.3.dist-info/METADATA +300 -0
  25. foodforthought_cli-0.2.3.dist-info/RECORD +44 -0
  26. foodforthought_cli-0.2.3.dist-info/top_level.txt +6 -0
  27. mechdog_labeled/__init__.py +3 -0
  28. mechdog_labeled/primitives.py +113 -0
  29. mechdog_labeled/servo_map.py +209 -0
  30. mechdog_output/__init__.py +3 -0
  31. mechdog_output/primitives.py +59 -0
  32. mechdog_output/servo_map.py +203 -0
  33. test_autodetect/__init__.py +3 -0
  34. test_autodetect/primitives.py +113 -0
  35. test_autodetect/servo_map.py +209 -0
  36. test_full_auto/__init__.py +3 -0
  37. test_full_auto/primitives.py +113 -0
  38. test_full_auto/servo_map.py +209 -0
  39. test_smart_detect/__init__.py +3 -0
  40. test_smart_detect/primitives.py +113 -0
  41. test_smart_detect/servo_map.py +209 -0
  42. foodforthought_cli-0.2.1.dist-info/METADATA +0 -151
  43. foodforthought_cli-0.2.1.dist-info/RECORD +0 -9
  44. foodforthought_cli-0.2.1.dist-info/top_level.txt +0 -1
  45. {foodforthought_cli-0.2.1.dist-info → foodforthought_cli-0.2.3.dist-info}/WHEEL +0 -0
  46. {foodforthought_cli-0.2.1.dist-info → foodforthought_cli-0.2.3.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,461 @@
1
+ """
2
+ Docker Generator - Generate containerized deployment for skills.
3
+
4
+ This generator creates:
5
+ - Dockerfile: Container definition
6
+ - docker-compose.yml: Local testing configuration
7
+ - entrypoint.sh: Container entry script
8
+ - .dockerignore: Build exclusions
9
+ """
10
+
11
+ from datetime import datetime
12
+ from pathlib import Path
13
+ from typing import Any, Dict, List, Set
14
+
15
+ from ..skill_schema import SkillSpecification
16
+ from .skill_generator import to_snake_case, to_pascal_case
17
+
18
+
19
+ class DockerGenerator:
20
+ """
21
+ Generate Docker containerization for a skill.
22
+
23
+ Creates all necessary files for building and running the skill
24
+ in a Docker container, with optional ROS2 integration.
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ spec: SkillSpecification,
30
+ base_image: str = "ros:humble",
31
+ include_ros2: bool = True,
32
+ ):
33
+ """
34
+ Initialize the generator.
35
+
36
+ Args:
37
+ spec: The skill specification
38
+ base_image: Base Docker image to use
39
+ include_ros2: Whether to include ROS2 support
40
+ """
41
+ self.spec = spec
42
+ self.base_image = base_image
43
+ self.include_ros2 = include_ros2
44
+ self.package_name = to_snake_case(spec.name) + "_skill"
45
+ self.class_name = to_pascal_case(spec.name)
46
+
47
+ def get_apt_dependencies(self) -> List[str]:
48
+ """Get apt packages needed based on skill requirements."""
49
+ deps = [
50
+ "python3-pip",
51
+ "python3-dev",
52
+ ]
53
+
54
+ # Add deps based on hardware requirements
55
+ for req in self.spec.hardware_requirements:
56
+ if req.component_type == "camera":
57
+ deps.extend(["libopencv-dev", "python3-opencv"])
58
+ elif req.component_type in ("arm", "gripper"):
59
+ deps.append("libserial-dev")
60
+
61
+ return sorted(set(deps))
62
+
63
+ def get_pip_dependencies(self) -> List[str]:
64
+ """Get pip packages needed based on skill requirements."""
65
+ deps = [
66
+ "pyyaml",
67
+ "numpy",
68
+ ]
69
+
70
+ # Add deps based on hardware requirements
71
+ for req in self.spec.hardware_requirements:
72
+ if req.component_type == "camera":
73
+ deps.extend(["opencv-python", "pillow"])
74
+ elif req.component_type in ("arm", "gripper"):
75
+ deps.append("pyserial")
76
+ elif req.component_type == "force_sensor":
77
+ deps.append("scipy")
78
+
79
+ return sorted(set(deps))
80
+
81
+ def generate_dockerfile(self) -> str:
82
+ """Generate Dockerfile for the skill."""
83
+ apt_deps = self.get_apt_dependencies()
84
+ pip_deps = self.get_pip_dependencies()
85
+
86
+ apt_deps_str = " \\\n ".join(apt_deps)
87
+ pip_deps_str = " ".join(pip_deps)
88
+
89
+ if self.include_ros2:
90
+ return self._generate_ros2_dockerfile(apt_deps_str, pip_deps_str)
91
+ else:
92
+ return self._generate_python_dockerfile(apt_deps_str, pip_deps_str)
93
+
94
+ def _generate_ros2_dockerfile(self, apt_deps: str, pip_deps: str) -> str:
95
+ """Generate Dockerfile with ROS2 support."""
96
+ return f'''# {self.spec.name} Skill Container
97
+ # Generated by Skill Compiler v1.0.0
98
+ # Base image: {self.base_image}
99
+
100
+ FROM {self.base_image}
101
+
102
+ # Set environment variables
103
+ ENV DEBIAN_FRONTEND=noninteractive
104
+ ENV ROS_DISTRO=humble
105
+ ENV SKILL_NAME={self.package_name}
106
+
107
+ # Install system dependencies
108
+ RUN apt-get update && apt-get install -y \\
109
+ {apt_deps} \\
110
+ && rm -rf /var/lib/apt/lists/*
111
+
112
+ # Install Python dependencies
113
+ RUN pip3 install --no-cache-dir {pip_deps}
114
+
115
+ # Create workspace
116
+ WORKDIR /ws
117
+
118
+ # Copy skill package
119
+ COPY . /ws/src/{self.package_name}
120
+
121
+ # Build workspace
122
+ RUN . /opt/ros/${{ROS_DISTRO}}/setup.sh && \\
123
+ colcon build --packages-select {self.package_name}
124
+
125
+ # Copy entrypoint
126
+ COPY entrypoint.sh /ws/entrypoint.sh
127
+ RUN chmod +x /ws/entrypoint.sh
128
+
129
+ # Set entrypoint
130
+ ENTRYPOINT ["/ws/entrypoint.sh"]
131
+
132
+ # Default command
133
+ CMD ["ros2", "launch", "{self.package_name}", "skill.launch.py"]
134
+
135
+ # Labels
136
+ LABEL org.opencontainers.image.title="{self.spec.name} Skill"
137
+ LABEL org.opencontainers.image.description="{self.spec.description}"
138
+ LABEL org.opencontainers.image.version="{self.spec.version}"
139
+ LABEL org.opencontainers.image.authors="{self.spec.author or 'Unknown'}"
140
+ LABEL skill.name="{self.spec.name}"
141
+ LABEL skill.version="{self.spec.version}"
142
+ '''
143
+
144
+ def _generate_python_dockerfile(self, apt_deps: str, pip_deps: str) -> str:
145
+ """Generate Dockerfile without ROS2 (standalone Python)."""
146
+ return f'''# {self.spec.name} Skill Container (Standalone)
147
+ # Generated by Skill Compiler v1.0.0
148
+
149
+ FROM python:3.10-slim
150
+
151
+ # Set environment variables
152
+ ENV DEBIAN_FRONTEND=noninteractive
153
+ ENV SKILL_NAME={self.package_name}
154
+ ENV PYTHONUNBUFFERED=1
155
+
156
+ # Install system dependencies
157
+ RUN apt-get update && apt-get install -y \\
158
+ {apt_deps} \\
159
+ && rm -rf /var/lib/apt/lists/*
160
+
161
+ # Install Python dependencies
162
+ RUN pip3 install --no-cache-dir {pip_deps}
163
+
164
+ # Create app directory
165
+ WORKDIR /app
166
+
167
+ # Copy skill package
168
+ COPY {self.package_name}/ /app/{self.package_name}/
169
+ COPY config/ /app/config/
170
+ COPY setup.py /app/
171
+ COPY requirements.txt /app/
172
+
173
+ # Install skill package
174
+ RUN pip3 install -e .
175
+
176
+ # Copy entrypoint
177
+ COPY entrypoint.sh /app/entrypoint.sh
178
+ RUN chmod +x /app/entrypoint.sh
179
+
180
+ # Set entrypoint
181
+ ENTRYPOINT ["/app/entrypoint.sh"]
182
+
183
+ # Default command
184
+ CMD ["python3", "-m", "{self.package_name}"]
185
+
186
+ # Labels
187
+ LABEL org.opencontainers.image.title="{self.spec.name} Skill"
188
+ LABEL org.opencontainers.image.description="{self.spec.description}"
189
+ LABEL org.opencontainers.image.version="{self.spec.version}"
190
+ LABEL skill.name="{self.spec.name}"
191
+ LABEL skill.version="{self.spec.version}"
192
+ '''
193
+
194
+ def generate_compose(self) -> str:
195
+ """Generate docker-compose.yml for local testing."""
196
+ hardware_volumes = []
197
+ hardware_devices = []
198
+
199
+ # Add device access based on hardware requirements
200
+ for req in self.spec.hardware_requirements:
201
+ if req.component_type in ("arm", "gripper"):
202
+ hardware_devices.append("/dev/ttyUSB0:/dev/ttyUSB0")
203
+ hardware_devices.append("/dev/ttyACM0:/dev/ttyACM0")
204
+ elif req.component_type == "camera":
205
+ hardware_devices.append("/dev/video0:/dev/video0")
206
+
207
+ devices_str = ""
208
+ if hardware_devices:
209
+ devices_str = "\n devices:\n" + "\n".join(
210
+ f" - {d}" for d in sorted(set(hardware_devices))
211
+ )
212
+
213
+ return f'''# Docker Compose for {self.spec.name} Skill
214
+ # Generated by Skill Compiler v1.0.0
215
+ #
216
+ # Usage:
217
+ # docker-compose up # Run skill
218
+ # docker-compose up -d # Run in background
219
+ # docker-compose build # Rebuild image
220
+ # docker-compose logs -f # View logs
221
+ # docker-compose down # Stop and remove
222
+
223
+ version: "3.8"
224
+
225
+ services:
226
+ {self.package_name}:
227
+ build:
228
+ context: .
229
+ dockerfile: Dockerfile
230
+ container_name: {self.package_name}
231
+ environment:
232
+ - ROS_DOMAIN_ID=${{ROS_DOMAIN_ID:-0}}
233
+ - USE_SIM=${{USE_SIM:-false}}
234
+ volumes:
235
+ - ./config:/ws/config:ro
236
+ - /tmp/.X11-unix:/tmp/.X11-unix:rw # For GUI (if needed)
237
+ network_mode: host{devices_str}
238
+ restart: unless-stopped
239
+
240
+ # Optional: Simulation environment
241
+ sim:
242
+ image: osrf/ros:humble-simulation
243
+ container_name: {self.package_name}_sim
244
+ environment:
245
+ - DISPLAY=${{DISPLAY}}
246
+ - QT_X11_NO_MITSHM=1
247
+ volumes:
248
+ - /tmp/.X11-unix:/tmp/.X11-unix:rw
249
+ - ./config:/ws/config:ro
250
+ network_mode: host
251
+ profiles:
252
+ - simulation
253
+
254
+ networks:
255
+ default:
256
+ name: {self.package_name}_network
257
+ '''
258
+
259
+ def generate_entrypoint(self) -> str:
260
+ """Generate entrypoint.sh script."""
261
+ if self.include_ros2:
262
+ return f'''#!/bin/bash
263
+ # Entrypoint for {self.spec.name} skill container
264
+ # Generated by Skill Compiler v1.0.0
265
+
266
+ set -e
267
+
268
+ # Source ROS2 environment
269
+ source /opt/ros/${{ROS_DISTRO}}/setup.bash
270
+
271
+ # Source workspace
272
+ if [ -f /ws/install/setup.bash ]; then
273
+ source /ws/install/setup.bash
274
+ fi
275
+
276
+ # Print info
277
+ echo "========================================"
278
+ echo " {self.spec.name} Skill Container"
279
+ echo " Version: {self.spec.version}"
280
+ echo " ROS Distro: ${{ROS_DISTRO}}"
281
+ echo "========================================"
282
+
283
+ # Handle signals gracefully
284
+ trap 'echo "Shutting down..."; exit 0' SIGTERM SIGINT
285
+
286
+ # Execute command
287
+ exec "$@"
288
+ '''
289
+ else:
290
+ return f'''#!/bin/bash
291
+ # Entrypoint for {self.spec.name} skill container (standalone)
292
+ # Generated by Skill Compiler v1.0.0
293
+
294
+ set -e
295
+
296
+ # Print info
297
+ echo "========================================"
298
+ echo " {self.spec.name} Skill Container"
299
+ echo " Version: {self.spec.version}"
300
+ echo "========================================"
301
+
302
+ # Handle signals gracefully
303
+ trap 'echo "Shutting down..."; exit 0' SIGTERM SIGINT
304
+
305
+ # Execute command
306
+ exec "$@"
307
+ '''
308
+
309
+ def generate_dockerignore(self) -> str:
310
+ """Generate .dockerignore file."""
311
+ return '''# Docker build exclusions
312
+ # Generated by Skill Compiler v1.0.0
313
+
314
+ # Git
315
+ .git
316
+ .gitignore
317
+
318
+ # Python
319
+ __pycache__/
320
+ *.py[cod]
321
+ *$py.class
322
+ *.so
323
+ .Python
324
+ build/
325
+ develop-eggs/
326
+ dist/
327
+ downloads/
328
+ eggs/
329
+ .eggs/
330
+ lib/
331
+ lib64/
332
+ parts/
333
+ sdist/
334
+ var/
335
+ wheels/
336
+ *.egg-info/
337
+ .installed.cfg
338
+ *.egg
339
+ .pytest_cache/
340
+ .coverage
341
+ htmlcov/
342
+
343
+ # Virtual environments
344
+ venv/
345
+ ENV/
346
+ env/
347
+ .venv/
348
+
349
+ # IDE
350
+ .idea/
351
+ .vscode/
352
+ *.swp
353
+ *.swo
354
+ *~
355
+
356
+ # Build artifacts
357
+ log/
358
+ install/
359
+ build/
360
+
361
+ # Documentation
362
+ docs/
363
+ *.md
364
+ !README.md
365
+
366
+ # Tests (optional, include if needed for debugging)
367
+ # test/
368
+
369
+ # Local config
370
+ .env
371
+ *.local.yaml
372
+ secrets/
373
+ '''
374
+
375
+ def generate_requirements_txt(self) -> str:
376
+ """Generate requirements.txt for pip dependencies."""
377
+ deps = self.get_pip_dependencies()
378
+ return "\n".join(deps)
379
+
380
+ def generate_makefile(self) -> str:
381
+ """Generate Makefile for common Docker operations."""
382
+ return f'''# Makefile for {self.spec.name} skill container
383
+ # Generated by Skill Compiler v1.0.0
384
+
385
+ IMAGE_NAME := {self.package_name}
386
+ VERSION := {self.spec.version}
387
+
388
+ .PHONY: build run stop logs shell clean test
389
+
390
+ # Build the Docker image
391
+ build:
392
+ docker build -t $(IMAGE_NAME):$(VERSION) -t $(IMAGE_NAME):latest .
393
+
394
+ # Run the container
395
+ run:
396
+ docker-compose up
397
+
398
+ # Run in background
399
+ run-detached:
400
+ docker-compose up -d
401
+
402
+ # Stop the container
403
+ stop:
404
+ docker-compose down
405
+
406
+ # View logs
407
+ logs:
408
+ docker-compose logs -f
409
+
410
+ # Open shell in container
411
+ shell:
412
+ docker exec -it {self.package_name} /bin/bash
413
+
414
+ # Clean up
415
+ clean:
416
+ docker-compose down -v --rmi local
417
+
418
+ # Run tests in container
419
+ test:
420
+ docker run --rm $(IMAGE_NAME):$(VERSION) python3 -m pytest
421
+
422
+ # Push to registry (customize registry URL)
423
+ push:
424
+ docker push $(IMAGE_NAME):$(VERSION)
425
+ docker push $(IMAGE_NAME):latest
426
+
427
+ # Run with simulation
428
+ sim:
429
+ docker-compose --profile simulation up
430
+ '''
431
+
432
+ def generate(self, output_dir: Path) -> Dict[str, str]:
433
+ """
434
+ Generate complete Docker deployment structure.
435
+
436
+ Args:
437
+ output_dir: Directory to write files to
438
+
439
+ Returns:
440
+ Dict mapping filenames to generated content
441
+ """
442
+ output_dir = Path(output_dir)
443
+ output_dir.mkdir(parents=True, exist_ok=True)
444
+
445
+ files = {
446
+ "Dockerfile": self.generate_dockerfile(),
447
+ "docker-compose.yml": self.generate_compose(),
448
+ "entrypoint.sh": self.generate_entrypoint(),
449
+ ".dockerignore": self.generate_dockerignore(),
450
+ "requirements.txt": self.generate_requirements_txt(),
451
+ "Makefile": self.generate_makefile(),
452
+ }
453
+
454
+ # Write files
455
+ for filename, content in files.items():
456
+ file_path = output_dir / filename
457
+ file_path.write_text(content)
458
+ if filename.endswith(".sh"):
459
+ file_path.chmod(0o755)
460
+
461
+ return files