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/behaviors/tree.py ADDED
@@ -0,0 +1,454 @@
1
+ """
2
+ Core Behavior Tree implementation.
3
+
4
+ A behavior tree is a directed acyclic graph that controls the execution flow
5
+ of robot behaviors. Each node returns one of three statuses:
6
+ - SUCCESS: The behavior completed successfully
7
+ - FAILURE: The behavior failed
8
+ - RUNNING: The behavior is still executing
9
+
10
+ Composite nodes:
11
+ - Sequence: Executes children in order, fails if any child fails
12
+ - Selector: Tries children in order, succeeds if any child succeeds
13
+ - Parallel: Runs children concurrently
14
+
15
+ Decorator nodes:
16
+ - Inverter: Inverts child result
17
+ - Repeater: Repeats child N times
18
+ - Succeeder: Always succeeds regardless of child
19
+
20
+ Leaf nodes:
21
+ - Action: Performs an action
22
+ - Condition: Checks a condition
23
+ """
24
+
25
+ from abc import ABC, abstractmethod
26
+ from dataclasses import dataclass, field
27
+ from typing import List, Optional, Callable, Any, Dict
28
+ from enum import Enum, auto
29
+ import time
30
+
31
+
32
+ class BehaviorStatus(Enum):
33
+ """Result of executing a behavior node."""
34
+ SUCCESS = auto() # Behavior completed successfully
35
+ FAILURE = auto() # Behavior failed
36
+ RUNNING = auto() # Behavior is still running
37
+
38
+
39
+ @dataclass
40
+ class Blackboard:
41
+ """
42
+ Shared data store for behavior tree nodes.
43
+
44
+ Nodes can read/write data here to communicate.
45
+ Example: Detection node writes target position, Navigation reads it.
46
+ """
47
+ data: Dict[str, Any] = field(default_factory=dict)
48
+
49
+ def get(self, key: str, default: Any = None) -> Any:
50
+ return self.data.get(key, default)
51
+
52
+ def set(self, key: str, value: Any) -> None:
53
+ self.data[key] = value
54
+
55
+ def has(self, key: str) -> bool:
56
+ return key in self.data
57
+
58
+ def clear(self) -> None:
59
+ self.data.clear()
60
+
61
+
62
+ class BehaviorNode(ABC):
63
+ """Base class for all behavior tree nodes."""
64
+
65
+ def __init__(self, name: str = ""):
66
+ self.name = name or self.__class__.__name__
67
+ self.status = BehaviorStatus.RUNNING
68
+ self.blackboard: Optional[Blackboard] = None
69
+
70
+ @abstractmethod
71
+ def tick(self) -> BehaviorStatus:
72
+ """
73
+ Execute one tick of this node.
74
+
75
+ Returns:
76
+ BehaviorStatus indicating result
77
+ """
78
+ pass
79
+
80
+ def reset(self) -> None:
81
+ """Reset node state for re-execution."""
82
+ self.status = BehaviorStatus.RUNNING
83
+
84
+ def set_blackboard(self, blackboard: Blackboard) -> None:
85
+ """Set the shared blackboard."""
86
+ self.blackboard = blackboard
87
+
88
+
89
+ class Composite(BehaviorNode):
90
+ """Base class for composite nodes with children."""
91
+
92
+ def __init__(self, name: str = "", children: List[BehaviorNode] = None):
93
+ super().__init__(name)
94
+ self.children = children or []
95
+
96
+ def add_child(self, child: BehaviorNode) -> "Composite":
97
+ self.children.append(child)
98
+ return self
99
+
100
+ def set_blackboard(self, blackboard: Blackboard) -> None:
101
+ super().set_blackboard(blackboard)
102
+ for child in self.children:
103
+ child.set_blackboard(blackboard)
104
+
105
+ def reset(self) -> None:
106
+ super().reset()
107
+ for child in self.children:
108
+ child.reset()
109
+
110
+
111
+ class Sequence(Composite):
112
+ """
113
+ Executes children in order.
114
+
115
+ - Returns SUCCESS if all children succeed
116
+ - Returns FAILURE if any child fails
117
+ - Returns RUNNING if a child is running
118
+
119
+ Use case: "stand up, THEN walk forward, THEN stop"
120
+ """
121
+
122
+ def __init__(self, name: str = "", children: List[BehaviorNode] = None):
123
+ super().__init__(name, children)
124
+ self.current_index = 0
125
+
126
+ def tick(self) -> BehaviorStatus:
127
+ while self.current_index < len(self.children):
128
+ child = self.children[self.current_index]
129
+ status = child.tick()
130
+
131
+ if status == BehaviorStatus.FAILURE:
132
+ self.current_index = 0
133
+ return BehaviorStatus.FAILURE
134
+
135
+ if status == BehaviorStatus.RUNNING:
136
+ return BehaviorStatus.RUNNING
137
+
138
+ # SUCCESS - move to next child
139
+ self.current_index += 1
140
+
141
+ self.current_index = 0
142
+ return BehaviorStatus.SUCCESS
143
+
144
+ def reset(self) -> None:
145
+ super().reset()
146
+ self.current_index = 0
147
+
148
+
149
+ class Selector(Composite):
150
+ """
151
+ Tries children in order until one succeeds.
152
+
153
+ - Returns SUCCESS if any child succeeds
154
+ - Returns FAILURE if all children fail
155
+ - Returns RUNNING if a child is running
156
+
157
+ Use case: "try picking with right hand, OR try left hand"
158
+ """
159
+
160
+ def __init__(self, name: str = "", children: List[BehaviorNode] = None):
161
+ super().__init__(name, children)
162
+ self.current_index = 0
163
+
164
+ def tick(self) -> BehaviorStatus:
165
+ while self.current_index < len(self.children):
166
+ child = self.children[self.current_index]
167
+ status = child.tick()
168
+
169
+ if status == BehaviorStatus.SUCCESS:
170
+ self.current_index = 0
171
+ return BehaviorStatus.SUCCESS
172
+
173
+ if status == BehaviorStatus.RUNNING:
174
+ return BehaviorStatus.RUNNING
175
+
176
+ # FAILURE - try next child
177
+ self.current_index += 1
178
+
179
+ self.current_index = 0
180
+ return BehaviorStatus.FAILURE
181
+
182
+ def reset(self) -> None:
183
+ super().reset()
184
+ self.current_index = 0
185
+
186
+
187
+ class Parallel(Composite):
188
+ """
189
+ Runs all children concurrently.
190
+
191
+ Policy options:
192
+ - "require_all": SUCCESS if all succeed, FAILURE if any fails
193
+ - "require_one": SUCCESS if any succeeds, FAILURE if all fail
194
+
195
+ Use case: "navigate AND detect obstacles simultaneously"
196
+ """
197
+
198
+ def __init__(
199
+ self,
200
+ name: str = "",
201
+ children: List[BehaviorNode] = None,
202
+ policy: str = "require_all"
203
+ ):
204
+ super().__init__(name, children)
205
+ self.policy = policy
206
+
207
+ def tick(self) -> BehaviorStatus:
208
+ successes = 0
209
+ failures = 0
210
+ running = 0
211
+
212
+ for child in self.children:
213
+ status = child.tick()
214
+ if status == BehaviorStatus.SUCCESS:
215
+ successes += 1
216
+ elif status == BehaviorStatus.FAILURE:
217
+ failures += 1
218
+ else:
219
+ running += 1
220
+
221
+ if self.policy == "require_all":
222
+ if failures > 0:
223
+ return BehaviorStatus.FAILURE
224
+ if running > 0:
225
+ return BehaviorStatus.RUNNING
226
+ return BehaviorStatus.SUCCESS
227
+ else: # require_one
228
+ if successes > 0:
229
+ return BehaviorStatus.SUCCESS
230
+ if running > 0:
231
+ return BehaviorStatus.RUNNING
232
+ return BehaviorStatus.FAILURE
233
+
234
+
235
+ class Decorator(BehaviorNode):
236
+ """Base class for decorator nodes with a single child."""
237
+
238
+ def __init__(self, child: BehaviorNode, name: str = ""):
239
+ super().__init__(name)
240
+ self.child = child
241
+
242
+ def set_blackboard(self, blackboard: Blackboard) -> None:
243
+ super().set_blackboard(blackboard)
244
+ self.child.set_blackboard(blackboard)
245
+
246
+ def reset(self) -> None:
247
+ super().reset()
248
+ self.child.reset()
249
+
250
+
251
+ class Inverter(Decorator):
252
+ """
253
+ Inverts the result of child node.
254
+
255
+ - SUCCESS becomes FAILURE
256
+ - FAILURE becomes SUCCESS
257
+ - RUNNING stays RUNNING
258
+
259
+ Use case: "is NOT battery low"
260
+ """
261
+
262
+ def tick(self) -> BehaviorStatus:
263
+ status = self.child.tick()
264
+ if status == BehaviorStatus.SUCCESS:
265
+ return BehaviorStatus.FAILURE
266
+ if status == BehaviorStatus.FAILURE:
267
+ return BehaviorStatus.SUCCESS
268
+ return BehaviorStatus.RUNNING
269
+
270
+
271
+ class Succeeder(Decorator):
272
+ """
273
+ Always returns SUCCESS.
274
+
275
+ Use case: Optional actions that shouldn't fail the tree.
276
+ """
277
+
278
+ def tick(self) -> BehaviorStatus:
279
+ self.child.tick()
280
+ return BehaviorStatus.SUCCESS
281
+
282
+
283
+ class Repeater(Decorator):
284
+ """
285
+ Repeats child a specified number of times.
286
+
287
+ Use case: "walk forward 5 steps"
288
+ """
289
+
290
+ def __init__(self, child: BehaviorNode, times: int = 1, name: str = ""):
291
+ super().__init__(child, name)
292
+ self.times = times
293
+ self.count = 0
294
+
295
+ def tick(self) -> BehaviorStatus:
296
+ if self.count >= self.times:
297
+ return BehaviorStatus.SUCCESS
298
+
299
+ status = self.child.tick()
300
+ if status == BehaviorStatus.RUNNING:
301
+ return BehaviorStatus.RUNNING
302
+
303
+ # Child completed (success or failure)
304
+ self.count += 1
305
+ self.child.reset()
306
+
307
+ if self.count >= self.times:
308
+ return BehaviorStatus.SUCCESS
309
+
310
+ return BehaviorStatus.RUNNING
311
+
312
+ def reset(self) -> None:
313
+ super().reset()
314
+ self.count = 0
315
+
316
+
317
+ class RepeatUntilFail(Decorator):
318
+ """
319
+ Repeats child until it fails.
320
+
321
+ Use case: "keep picking up trash until none visible"
322
+ """
323
+
324
+ def tick(self) -> BehaviorStatus:
325
+ status = self.child.tick()
326
+ if status == BehaviorStatus.FAILURE:
327
+ return BehaviorStatus.SUCCESS
328
+ if status == BehaviorStatus.SUCCESS:
329
+ self.child.reset()
330
+ return BehaviorStatus.RUNNING
331
+ return BehaviorStatus.RUNNING
332
+
333
+
334
+ class Action(BehaviorNode):
335
+ """
336
+ Leaf node that performs an action.
337
+
338
+ Wraps a function that returns BehaviorStatus.
339
+ """
340
+
341
+ def __init__(
342
+ self,
343
+ action: Callable[[], BehaviorStatus],
344
+ name: str = ""
345
+ ):
346
+ super().__init__(name)
347
+ self.action = action
348
+
349
+ def tick(self) -> BehaviorStatus:
350
+ return self.action()
351
+
352
+
353
+ class Condition(BehaviorNode):
354
+ """
355
+ Leaf node that checks a condition.
356
+
357
+ Returns SUCCESS if condition is true, FAILURE otherwise.
358
+ Never returns RUNNING.
359
+ """
360
+
361
+ def __init__(
362
+ self,
363
+ condition: Callable[[], bool],
364
+ name: str = ""
365
+ ):
366
+ super().__init__(name)
367
+ self.condition = condition
368
+
369
+ def tick(self) -> BehaviorStatus:
370
+ if self.condition():
371
+ return BehaviorStatus.SUCCESS
372
+ return BehaviorStatus.FAILURE
373
+
374
+
375
+ class BehaviorTree:
376
+ """
377
+ A complete behavior tree with execution control.
378
+
379
+ Provides:
380
+ - Blackboard for shared data
381
+ - Tick loop control
382
+ - Visualization/debugging
383
+ """
384
+
385
+ def __init__(self, root: BehaviorNode, name: str = "BehaviorTree"):
386
+ self.root = root
387
+ self.name = name
388
+ self.blackboard = Blackboard()
389
+ self.root.set_blackboard(self.blackboard)
390
+ self.tick_count = 0
391
+ self.running = False
392
+
393
+ def tick(self) -> BehaviorStatus:
394
+ """Execute one tick of the tree."""
395
+ self.tick_count += 1
396
+ return self.root.tick()
397
+
398
+ def reset(self) -> None:
399
+ """Reset the tree for re-execution."""
400
+ self.root.reset()
401
+ self.tick_count = 0
402
+
403
+ def run(
404
+ self,
405
+ tick_rate: float = 10.0,
406
+ max_ticks: int = 0,
407
+ on_tick: Optional[Callable[[int, BehaviorStatus], None]] = None
408
+ ) -> BehaviorStatus:
409
+ """
410
+ Run the tree until completion.
411
+
412
+ Args:
413
+ tick_rate: Ticks per second
414
+ max_ticks: Maximum ticks (0 = unlimited)
415
+ on_tick: Callback after each tick
416
+
417
+ Returns:
418
+ Final status
419
+ """
420
+ self.running = True
421
+ interval = 1.0 / tick_rate
422
+
423
+ while self.running:
424
+ start = time.time()
425
+ status = self.tick()
426
+
427
+ if on_tick:
428
+ on_tick(self.tick_count, status)
429
+
430
+ if status != BehaviorStatus.RUNNING:
431
+ self.running = False
432
+ return status
433
+
434
+ if max_ticks > 0 and self.tick_count >= max_ticks:
435
+ self.running = False
436
+ return BehaviorStatus.FAILURE
437
+
438
+ # Sleep to maintain tick rate
439
+ elapsed = time.time() - start
440
+ if elapsed < interval:
441
+ time.sleep(interval - elapsed)
442
+
443
+ return BehaviorStatus.FAILURE
444
+
445
+ def stop(self) -> None:
446
+ """Stop the tree execution."""
447
+ self.running = False
448
+
449
+ def get_blackboard(self) -> Blackboard:
450
+ """Get the shared blackboard."""
451
+ return self.blackboard
452
+
453
+ def __repr__(self) -> str:
454
+ return f"BehaviorTree(name='{self.name}', ticks={self.tick_count})"