foodforthought-cli 0.2.7__py3-none-any.whl → 0.3.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 (131) hide show
  1. ate/__init__.py +6 -0
  2. ate/__main__.py +16 -0
  3. ate/auth/__init__.py +1 -0
  4. ate/auth/device_flow.py +141 -0
  5. ate/auth/token_store.py +96 -0
  6. ate/behaviors/__init__.py +100 -0
  7. ate/behaviors/approach.py +399 -0
  8. ate/behaviors/common.py +686 -0
  9. ate/behaviors/tree.py +454 -0
  10. ate/cli.py +855 -3995
  11. ate/client.py +90 -0
  12. ate/commands/__init__.py +168 -0
  13. ate/commands/auth.py +389 -0
  14. ate/commands/bridge.py +448 -0
  15. ate/commands/data.py +185 -0
  16. ate/commands/deps.py +111 -0
  17. ate/commands/generate.py +384 -0
  18. ate/commands/memory.py +907 -0
  19. ate/commands/parts.py +166 -0
  20. ate/commands/primitive.py +399 -0
  21. ate/commands/protocol.py +288 -0
  22. ate/commands/recording.py +524 -0
  23. ate/commands/repo.py +154 -0
  24. ate/commands/simulation.py +291 -0
  25. ate/commands/skill.py +303 -0
  26. ate/commands/skills.py +487 -0
  27. ate/commands/team.py +147 -0
  28. ate/commands/workflow.py +271 -0
  29. ate/detection/__init__.py +38 -0
  30. ate/detection/base.py +142 -0
  31. ate/detection/color_detector.py +399 -0
  32. ate/detection/trash_detector.py +322 -0
  33. ate/drivers/__init__.py +39 -0
  34. ate/drivers/ble_transport.py +405 -0
  35. ate/drivers/mechdog.py +942 -0
  36. ate/drivers/wifi_camera.py +477 -0
  37. ate/interfaces/__init__.py +187 -0
  38. ate/interfaces/base.py +273 -0
  39. ate/interfaces/body.py +267 -0
  40. ate/interfaces/detection.py +282 -0
  41. ate/interfaces/locomotion.py +422 -0
  42. ate/interfaces/manipulation.py +408 -0
  43. ate/interfaces/navigation.py +389 -0
  44. ate/interfaces/perception.py +362 -0
  45. ate/interfaces/sensors.py +247 -0
  46. ate/interfaces/types.py +371 -0
  47. ate/llm_proxy.py +239 -0
  48. ate/mcp_server.py +387 -0
  49. ate/memory/__init__.py +35 -0
  50. ate/memory/cloud.py +244 -0
  51. ate/memory/context.py +269 -0
  52. ate/memory/embeddings.py +184 -0
  53. ate/memory/export.py +26 -0
  54. ate/memory/merge.py +146 -0
  55. ate/memory/migrate/__init__.py +34 -0
  56. ate/memory/migrate/base.py +89 -0
  57. ate/memory/migrate/pipeline.py +189 -0
  58. ate/memory/migrate/sources/__init__.py +13 -0
  59. ate/memory/migrate/sources/chroma.py +170 -0
  60. ate/memory/migrate/sources/pinecone.py +120 -0
  61. ate/memory/migrate/sources/qdrant.py +110 -0
  62. ate/memory/migrate/sources/weaviate.py +160 -0
  63. ate/memory/reranker.py +353 -0
  64. ate/memory/search.py +26 -0
  65. ate/memory/store.py +548 -0
  66. ate/recording/__init__.py +83 -0
  67. ate/recording/demonstration.py +378 -0
  68. ate/recording/session.py +415 -0
  69. ate/recording/upload.py +304 -0
  70. ate/recording/visual.py +416 -0
  71. ate/recording/wrapper.py +95 -0
  72. ate/robot/__init__.py +221 -0
  73. ate/robot/agentic_servo.py +856 -0
  74. ate/robot/behaviors.py +493 -0
  75. ate/robot/ble_capture.py +1000 -0
  76. ate/robot/ble_enumerate.py +506 -0
  77. ate/robot/calibration.py +668 -0
  78. ate/robot/calibration_state.py +388 -0
  79. ate/robot/commands.py +3735 -0
  80. ate/robot/direction_calibration.py +554 -0
  81. ate/robot/discovery.py +441 -0
  82. ate/robot/introspection.py +330 -0
  83. ate/robot/llm_system_id.py +654 -0
  84. ate/robot/locomotion_calibration.py +508 -0
  85. ate/robot/manager.py +270 -0
  86. ate/robot/marker_generator.py +611 -0
  87. ate/robot/perception.py +502 -0
  88. ate/robot/primitives.py +614 -0
  89. ate/robot/profiles.py +281 -0
  90. ate/robot/registry.py +322 -0
  91. ate/robot/servo_mapper.py +1153 -0
  92. ate/robot/skill_upload.py +675 -0
  93. ate/robot/target_calibration.py +500 -0
  94. ate/robot/teach.py +515 -0
  95. ate/robot/types.py +242 -0
  96. ate/robot/visual_labeler.py +1048 -0
  97. ate/robot/visual_servo_loop.py +494 -0
  98. ate/robot/visual_servoing.py +570 -0
  99. ate/robot/visual_system_id.py +906 -0
  100. ate/transports/__init__.py +121 -0
  101. ate/transports/base.py +394 -0
  102. ate/transports/ble.py +405 -0
  103. ate/transports/hybrid.py +444 -0
  104. ate/transports/serial.py +345 -0
  105. ate/urdf/__init__.py +30 -0
  106. ate/urdf/capture.py +582 -0
  107. ate/urdf/cloud.py +491 -0
  108. ate/urdf/collision.py +271 -0
  109. ate/urdf/commands.py +708 -0
  110. ate/urdf/depth.py +360 -0
  111. ate/urdf/inertial.py +312 -0
  112. ate/urdf/kinematics.py +330 -0
  113. ate/urdf/lifting.py +415 -0
  114. ate/urdf/meshing.py +300 -0
  115. ate/urdf/models/__init__.py +110 -0
  116. ate/urdf/models/depth_anything.py +253 -0
  117. ate/urdf/models/sam2.py +324 -0
  118. ate/urdf/motion_analysis.py +396 -0
  119. ate/urdf/pipeline.py +468 -0
  120. ate/urdf/scale.py +256 -0
  121. ate/urdf/scan_session.py +411 -0
  122. ate/urdf/segmentation.py +299 -0
  123. ate/urdf/synthesis.py +319 -0
  124. ate/urdf/topology.py +336 -0
  125. ate/urdf/validation.py +371 -0
  126. {foodforthought_cli-0.2.7.dist-info → foodforthought_cli-0.3.0.dist-info}/METADATA +9 -1
  127. foodforthought_cli-0.3.0.dist-info/RECORD +166 -0
  128. {foodforthought_cli-0.2.7.dist-info → foodforthought_cli-0.3.0.dist-info}/WHEEL +1 -1
  129. foodforthought_cli-0.2.7.dist-info/RECORD +0 -44
  130. {foodforthought_cli-0.2.7.dist-info → foodforthought_cli-0.3.0.dist-info}/entry_points.txt +0 -0
  131. {foodforthought_cli-0.2.7.dist-info → foodforthought_cli-0.3.0.dist-info}/top_level.txt +0 -0
ate/robot/manager.py ADDED
@@ -0,0 +1,270 @@
1
+ """
2
+ Robot Manager - unified interface for managing robot connections.
3
+
4
+ Handles:
5
+ - Loading robots from profiles
6
+ - Connecting/disconnecting
7
+ - Managing multiple robots
8
+ """
9
+
10
+ from typing import Dict, List, Optional, Any
11
+ from dataclasses import dataclass
12
+
13
+ from .profiles import RobotProfile, load_profile, list_profiles
14
+ from .registry import KNOWN_ROBOTS, RobotType
15
+ from .introspection import get_capabilities, get_methods
16
+
17
+
18
+ @dataclass
19
+ class ManagedRobot:
20
+ """A robot instance managed by RobotManager."""
21
+ profile: RobotProfile
22
+ robot_type: RobotType
23
+ instance: Any = None
24
+ connected: bool = False
25
+
26
+
27
+ class RobotManager:
28
+ """
29
+ Manages robot connections and lifecycle.
30
+
31
+ Example:
32
+ manager = RobotManager()
33
+
34
+ # Load from profile
35
+ dog = manager.load("my_mechdog")
36
+ dog.connect()
37
+
38
+ # Use robot
39
+ dog.instance.stand()
40
+ dog.instance.walk(Vector3.forward())
41
+
42
+ # Disconnect
43
+ manager.disconnect_all()
44
+ """
45
+
46
+ def __init__(self):
47
+ self._robots: Dict[str, ManagedRobot] = {}
48
+
49
+ def load(self, profile_name: str) -> Optional[ManagedRobot]:
50
+ """
51
+ Load a robot from a saved profile.
52
+
53
+ Args:
54
+ profile_name: Name of the profile
55
+
56
+ Returns:
57
+ ManagedRobot or None if profile not found
58
+ """
59
+ profile = load_profile(profile_name)
60
+ if profile is None:
61
+ return None
62
+
63
+ robot_type = KNOWN_ROBOTS.get(profile.robot_type)
64
+ if robot_type is None:
65
+ print(f"Unknown robot type: {profile.robot_type}")
66
+ return None
67
+
68
+ managed = ManagedRobot(
69
+ profile=profile,
70
+ robot_type=robot_type,
71
+ )
72
+
73
+ self._robots[profile_name] = managed
74
+ return managed
75
+
76
+ def create(
77
+ self,
78
+ name: str,
79
+ robot_type: str,
80
+ **config
81
+ ) -> Optional[ManagedRobot]:
82
+ """
83
+ Create a robot instance without loading from profile.
84
+
85
+ Args:
86
+ name: Name for this robot
87
+ robot_type: Robot type ID
88
+ **config: Configuration options
89
+
90
+ Returns:
91
+ ManagedRobot
92
+ """
93
+ rtype = KNOWN_ROBOTS.get(robot_type)
94
+ if rtype is None:
95
+ print(f"Unknown robot type: {robot_type}")
96
+ return None
97
+
98
+ profile = RobotProfile(
99
+ name=name,
100
+ robot_type=robot_type,
101
+ **config
102
+ )
103
+
104
+ managed = ManagedRobot(
105
+ profile=profile,
106
+ robot_type=rtype,
107
+ )
108
+
109
+ self._robots[name] = managed
110
+ return managed
111
+
112
+ def connect(self, name: str) -> bool:
113
+ """
114
+ Connect to a loaded robot.
115
+
116
+ Args:
117
+ name: Robot name
118
+
119
+ Returns:
120
+ True if connected successfully
121
+ """
122
+ managed = self._robots.get(name)
123
+ if managed is None:
124
+ print(f"Robot not loaded: {name}")
125
+ return False
126
+
127
+ # Create robot instance
128
+ instance = self._create_instance(managed)
129
+ if instance is None:
130
+ return False
131
+
132
+ managed.instance = instance
133
+
134
+ # Connect
135
+ result = instance.connect()
136
+ if result.success:
137
+ managed.connected = True
138
+ return True
139
+ else:
140
+ print(f"Connection failed: {result.message}")
141
+ return False
142
+
143
+ def disconnect(self, name: str) -> bool:
144
+ """
145
+ Disconnect from a robot.
146
+
147
+ Args:
148
+ name: Robot name
149
+
150
+ Returns:
151
+ True if disconnected
152
+ """
153
+ managed = self._robots.get(name)
154
+ if managed is None or not managed.connected:
155
+ return True
156
+
157
+ if managed.instance:
158
+ managed.instance.disconnect()
159
+ managed.connected = False
160
+
161
+ return True
162
+
163
+ def disconnect_all(self) -> None:
164
+ """Disconnect from all robots."""
165
+ for name in list(self._robots.keys()):
166
+ self.disconnect(name)
167
+
168
+ def get(self, name: str) -> Optional[Any]:
169
+ """
170
+ Get a robot instance.
171
+
172
+ Args:
173
+ name: Robot name
174
+
175
+ Returns:
176
+ Robot instance or None
177
+ """
178
+ managed = self._robots.get(name)
179
+ if managed and managed.instance:
180
+ return managed.instance
181
+ return None
182
+
183
+ def list_loaded(self) -> List[str]:
184
+ """List all loaded robots."""
185
+ return list(self._robots.keys())
186
+
187
+ def list_connected(self) -> List[str]:
188
+ """List all connected robots."""
189
+ return [
190
+ name for name, managed in self._robots.items()
191
+ if managed.connected
192
+ ]
193
+
194
+ def get_info(self, name: str) -> Optional[Dict[str, Any]]:
195
+ """
196
+ Get information about a loaded robot.
197
+
198
+ Args:
199
+ name: Robot name
200
+
201
+ Returns:
202
+ Dict with robot info
203
+ """
204
+ managed = self._robots.get(name)
205
+ if managed is None:
206
+ return None
207
+
208
+ info = {
209
+ "name": managed.profile.name,
210
+ "type": managed.robot_type.name,
211
+ "manufacturer": managed.robot_type.manufacturer,
212
+ "archetype": managed.robot_type.archetype,
213
+ "connected": managed.connected,
214
+ "profile": {
215
+ "serial_port": managed.profile.serial_port,
216
+ "camera_ip": managed.profile.camera_ip,
217
+ "has_camera": managed.profile.has_camera,
218
+ "has_arm": managed.profile.has_arm,
219
+ },
220
+ }
221
+
222
+ if managed.instance:
223
+ info["capabilities"] = list(get_capabilities(managed.instance).keys())
224
+ info["methods"] = {
225
+ cap: [m.name for m in methods]
226
+ for cap, methods in get_methods(managed.instance).items()
227
+ }
228
+
229
+ return info
230
+
231
+ def _create_instance(self, managed: ManagedRobot) -> Optional[Any]:
232
+ """Create a robot instance from profile and type."""
233
+ rtype = managed.robot_type
234
+ profile = managed.profile
235
+
236
+ # Import the driver module dynamically
237
+ try:
238
+ import importlib
239
+ module = importlib.import_module(rtype.driver_module)
240
+ driver_class = getattr(module, rtype.driver_class)
241
+ config_class = getattr(module, rtype.config_class, None)
242
+ except (ImportError, AttributeError) as e:
243
+ print(f"Failed to import driver: {e}")
244
+ return None
245
+
246
+ # Build configuration
247
+ if config_class:
248
+ config_kwargs = {
249
+ "port": profile.serial_port or "/dev/ttyUSB0",
250
+ }
251
+
252
+ if profile.has_camera and profile.camera_ip:
253
+ config_kwargs["has_camera"] = True
254
+ config_kwargs["camera_ip"] = profile.camera_ip
255
+ config_kwargs["camera_port"] = profile.camera_port
256
+ config_kwargs["camera_stream_port"] = profile.camera_stream_port
257
+
258
+ if profile.has_arm:
259
+ config_kwargs["has_arm"] = True
260
+
261
+ config = config_class(**config_kwargs)
262
+ return driver_class(config=config)
263
+ else:
264
+ return driver_class()
265
+
266
+ def __enter__(self) -> "RobotManager":
267
+ return self
268
+
269
+ def __exit__(self, *args) -> None:
270
+ self.disconnect_all()