foodforthought-cli 0.2.8__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 (116) 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 +12 -0
  7. ate/behaviors/approach.py +399 -0
  8. ate/cli.py +855 -4551
  9. ate/client.py +90 -0
  10. ate/commands/__init__.py +168 -0
  11. ate/commands/auth.py +389 -0
  12. ate/commands/bridge.py +448 -0
  13. ate/commands/data.py +185 -0
  14. ate/commands/deps.py +111 -0
  15. ate/commands/generate.py +384 -0
  16. ate/commands/memory.py +907 -0
  17. ate/commands/parts.py +166 -0
  18. ate/commands/primitive.py +399 -0
  19. ate/commands/protocol.py +288 -0
  20. ate/commands/recording.py +524 -0
  21. ate/commands/repo.py +154 -0
  22. ate/commands/simulation.py +291 -0
  23. ate/commands/skill.py +303 -0
  24. ate/commands/skills.py +487 -0
  25. ate/commands/team.py +147 -0
  26. ate/commands/workflow.py +271 -0
  27. ate/detection/__init__.py +38 -0
  28. ate/detection/base.py +142 -0
  29. ate/detection/color_detector.py +399 -0
  30. ate/detection/trash_detector.py +322 -0
  31. ate/drivers/__init__.py +18 -6
  32. ate/drivers/ble_transport.py +405 -0
  33. ate/drivers/mechdog.py +360 -24
  34. ate/drivers/wifi_camera.py +477 -0
  35. ate/interfaces/__init__.py +16 -0
  36. ate/interfaces/base.py +2 -0
  37. ate/interfaces/sensors.py +247 -0
  38. ate/llm_proxy.py +239 -0
  39. ate/memory/__init__.py +35 -0
  40. ate/memory/cloud.py +244 -0
  41. ate/memory/context.py +269 -0
  42. ate/memory/embeddings.py +184 -0
  43. ate/memory/export.py +26 -0
  44. ate/memory/merge.py +146 -0
  45. ate/memory/migrate/__init__.py +34 -0
  46. ate/memory/migrate/base.py +89 -0
  47. ate/memory/migrate/pipeline.py +189 -0
  48. ate/memory/migrate/sources/__init__.py +13 -0
  49. ate/memory/migrate/sources/chroma.py +170 -0
  50. ate/memory/migrate/sources/pinecone.py +120 -0
  51. ate/memory/migrate/sources/qdrant.py +110 -0
  52. ate/memory/migrate/sources/weaviate.py +160 -0
  53. ate/memory/reranker.py +353 -0
  54. ate/memory/search.py +26 -0
  55. ate/memory/store.py +548 -0
  56. ate/recording/__init__.py +42 -3
  57. ate/recording/session.py +12 -2
  58. ate/recording/visual.py +416 -0
  59. ate/robot/__init__.py +142 -0
  60. ate/robot/agentic_servo.py +856 -0
  61. ate/robot/behaviors.py +493 -0
  62. ate/robot/ble_capture.py +1000 -0
  63. ate/robot/ble_enumerate.py +506 -0
  64. ate/robot/calibration.py +88 -3
  65. ate/robot/calibration_state.py +388 -0
  66. ate/robot/commands.py +143 -11
  67. ate/robot/direction_calibration.py +554 -0
  68. ate/robot/discovery.py +104 -2
  69. ate/robot/llm_system_id.py +654 -0
  70. ate/robot/locomotion_calibration.py +508 -0
  71. ate/robot/marker_generator.py +611 -0
  72. ate/robot/perception.py +502 -0
  73. ate/robot/primitives.py +614 -0
  74. ate/robot/profiles.py +6 -0
  75. ate/robot/registry.py +5 -2
  76. ate/robot/servo_mapper.py +1153 -0
  77. ate/robot/skill_upload.py +285 -3
  78. ate/robot/target_calibration.py +500 -0
  79. ate/robot/teach.py +515 -0
  80. ate/robot/types.py +242 -0
  81. ate/robot/visual_labeler.py +9 -0
  82. ate/robot/visual_servo_loop.py +494 -0
  83. ate/robot/visual_servoing.py +570 -0
  84. ate/robot/visual_system_id.py +906 -0
  85. ate/transports/__init__.py +121 -0
  86. ate/transports/base.py +394 -0
  87. ate/transports/ble.py +405 -0
  88. ate/transports/hybrid.py +444 -0
  89. ate/transports/serial.py +345 -0
  90. ate/urdf/__init__.py +30 -0
  91. ate/urdf/capture.py +582 -0
  92. ate/urdf/cloud.py +491 -0
  93. ate/urdf/collision.py +271 -0
  94. ate/urdf/commands.py +708 -0
  95. ate/urdf/depth.py +360 -0
  96. ate/urdf/inertial.py +312 -0
  97. ate/urdf/kinematics.py +330 -0
  98. ate/urdf/lifting.py +415 -0
  99. ate/urdf/meshing.py +300 -0
  100. ate/urdf/models/__init__.py +110 -0
  101. ate/urdf/models/depth_anything.py +253 -0
  102. ate/urdf/models/sam2.py +324 -0
  103. ate/urdf/motion_analysis.py +396 -0
  104. ate/urdf/pipeline.py +468 -0
  105. ate/urdf/scale.py +256 -0
  106. ate/urdf/scan_session.py +411 -0
  107. ate/urdf/segmentation.py +299 -0
  108. ate/urdf/synthesis.py +319 -0
  109. ate/urdf/topology.py +336 -0
  110. ate/urdf/validation.py +371 -0
  111. {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.0.dist-info}/METADATA +1 -1
  112. foodforthought_cli-0.3.0.dist-info/RECORD +166 -0
  113. {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.0.dist-info}/WHEEL +1 -1
  114. foodforthought_cli-0.2.8.dist-info/RECORD +0 -73
  115. {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.0.dist-info}/entry_points.txt +0 -0
  116. {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,288 @@
1
+ """
2
+ Protocol registry commands for FoodforThought CLI.
3
+
4
+ Commands:
5
+ - ate protocol list - List protocols
6
+ - ate protocol get - Get protocol details
7
+ - ate protocol init - Initialize new protocol template
8
+ - ate protocol push - Upload protocol to FoodForThought
9
+ - ate protocol scan-serial - Scan for serial ports
10
+ - ate protocol scan-ble - Scan for BLE devices
11
+ """
12
+
13
+ import json
14
+ import sys
15
+ from pathlib import Path
16
+ from typing import Optional
17
+
18
+
19
+ def protocol_list(client, robot: Optional[str], transport: Optional[str],
20
+ verified: bool, search: Optional[str]) -> None:
21
+ """List protocols from the registry."""
22
+ print("Fetching protocols...")
23
+
24
+ params = {}
25
+ if robot:
26
+ params["robot"] = robot
27
+ if transport:
28
+ params["transport"] = transport
29
+ if verified:
30
+ params["verified"] = "true"
31
+ if search:
32
+ params["search"] = search
33
+
34
+ try:
35
+ response = client._request("GET", "/protocols", params=params)
36
+ protocols = response.get("protocols", [])
37
+
38
+ if not protocols:
39
+ print("\nNo protocols found matching criteria.")
40
+ return
41
+
42
+ print(f"\n{'=' * 70}")
43
+ print(f"{'Robot Model':<25} {'Transport':<12} {'Status':<12} {'Commands'}")
44
+ print(f"{'=' * 70}")
45
+
46
+ for proto in protocols:
47
+ robot_model = proto.get("robotModel", "")[:23]
48
+ transport_type = proto.get("transport", "")[:10]
49
+ status = "✓ Verified" if proto.get("verified") else "○ Community"
50
+ cmd_count = len(proto.get("commands", []))
51
+ print(f"{robot_model:<25} {transport_type:<12} {status:<12} {cmd_count}")
52
+
53
+ except Exception as e:
54
+ print(f"\n✗ Failed to list protocols: {e}", file=sys.stderr)
55
+ sys.exit(1)
56
+
57
+
58
+ def protocol_get(client, protocol_id: str) -> None:
59
+ """Get detailed information about a protocol."""
60
+ try:
61
+ response = client._request("GET", f"/protocols/{protocol_id}")
62
+ proto = response.get("protocol", {})
63
+
64
+ print(f"\n{'=' * 60}")
65
+ print(f"Protocol: {proto.get('robotModel')}")
66
+ print(f"{'=' * 60}")
67
+
68
+ print(f"\nTransport: {proto.get('transport')}")
69
+ print(f"Verified: {'Yes' if proto.get('verified') else 'No'}")
70
+ print(f"Format: {proto.get('commandFormat')}")
71
+
72
+ # Connection info
73
+ if proto.get("connectionInfo"):
74
+ info = proto["connectionInfo"]
75
+ print(f"\nConnection:")
76
+ if info.get("baudRate"):
77
+ print(f" Baud Rate: {info['baudRate']}")
78
+ if info.get("serviceUuid"):
79
+ print(f" Service UUID: {info['serviceUuid']}")
80
+ if info.get("charUuid"):
81
+ print(f" Characteristic UUID: {info['charUuid']}")
82
+
83
+ # Commands
84
+ commands = proto.get("commands", [])
85
+ if commands:
86
+ print(f"\nCommands ({len(commands)}):")
87
+ for cmd in commands[:10]: # Show first 10
88
+ print(f" • {cmd.get('name')}: {cmd.get('template')}")
89
+ if len(commands) > 10:
90
+ print(f" ... and {len(commands) - 10} more")
91
+
92
+ except Exception as e:
93
+ print(f"\n✗ Failed to get protocol: {e}", file=sys.stderr)
94
+ sys.exit(1)
95
+
96
+
97
+ def protocol_init(client, robot_model: str, transport: str, output: str) -> None:
98
+ """Initialize a new protocol template."""
99
+ print(f"Initializing protocol for {robot_model}")
100
+ print(f" Transport: {transport}")
101
+ print(f" Output: {output}")
102
+
103
+ output_path = Path(output)
104
+ output_path.mkdir(parents=True, exist_ok=True)
105
+
106
+ # Create protocol template
107
+ protocol_template = {
108
+ "robotModel": robot_model,
109
+ "transport": transport,
110
+ "commandFormat": "ascii" if transport == "serial" else "binary",
111
+ "connectionInfo": {},
112
+ "commands": [
113
+ {
114
+ "name": "example_command",
115
+ "template": "CMD {param1}",
116
+ "description": "Example command - replace with actual commands",
117
+ "parameters": [
118
+ {"name": "param1", "type": "int", "range": [0, 100]}
119
+ ]
120
+ }
121
+ ],
122
+ "version": "1.0.0"
123
+ }
124
+
125
+ # Add transport-specific defaults
126
+ if transport == "serial":
127
+ protocol_template["connectionInfo"]["baudRate"] = 115200
128
+ protocol_template["connectionInfo"]["dataBits"] = 8
129
+ protocol_template["connectionInfo"]["stopBits"] = 1
130
+ elif transport == "ble":
131
+ protocol_template["connectionInfo"]["serviceUuid"] = ""
132
+ protocol_template["connectionInfo"]["charUuid"] = ""
133
+ elif transport == "wifi":
134
+ protocol_template["connectionInfo"]["port"] = 8080
135
+
136
+ # Write protocol file
137
+ protocol_file = output_path / "protocol.json"
138
+ with open(protocol_file, 'w') as f:
139
+ json.dump(protocol_template, f, indent=2)
140
+
141
+ print(f"\n✓ Created: {protocol_file}")
142
+ print(f"\nNext steps:")
143
+ print(f" 1. Edit {protocol_file} with your robot's commands")
144
+ print(f" 2. Test connection with: ate bridge connect <port>")
145
+ print(f" 3. Publish with: ate protocol push {protocol_file}")
146
+
147
+
148
+ def protocol_push(client, file: Optional[str]) -> None:
149
+ """Upload protocol to FoodForThought."""
150
+ protocol_file = Path(file or "./protocol.json")
151
+
152
+ if not protocol_file.exists():
153
+ print(f"Error: Protocol file not found: {protocol_file}", file=sys.stderr)
154
+ sys.exit(1)
155
+
156
+ with open(protocol_file) as f:
157
+ protocol_data = json.load(f)
158
+
159
+ print(f"Uploading protocol: {protocol_data.get('robotModel')}")
160
+
161
+ try:
162
+ response = client._request("POST", "/protocols", json=protocol_data)
163
+ proto = response.get("protocol", {})
164
+
165
+ print(f"\n✓ Protocol uploaded!")
166
+ print(f" ID: {proto.get('id')}")
167
+ print(f" Status: Pending review")
168
+
169
+ except Exception as e:
170
+ print(f"\n✗ Upload failed: {e}", file=sys.stderr)
171
+ sys.exit(1)
172
+
173
+
174
+ def protocol_scan_serial() -> None:
175
+ """Scan for available serial ports."""
176
+ try:
177
+ import serial.tools.list_ports
178
+ except ImportError:
179
+ print("Error: pyserial is required. Install with: pip install pyserial", file=sys.stderr)
180
+ sys.exit(1)
181
+
182
+ print("Scanning for serial ports...\n")
183
+
184
+ ports = list(serial.tools.list_ports.comports())
185
+
186
+ if not ports:
187
+ print("No serial ports found.")
188
+ return
189
+
190
+ print(f"Found {len(ports)} port(s):\n")
191
+
192
+ for port in ports:
193
+ print(f" {port.device}")
194
+ if port.description and port.description != "n/a":
195
+ print(f" Description: {port.description}")
196
+ if port.manufacturer:
197
+ print(f" Manufacturer: {port.manufacturer}")
198
+ if port.vid and port.pid:
199
+ print(f" VID:PID: {port.vid:04x}:{port.pid:04x}")
200
+ print()
201
+
202
+
203
+ def protocol_scan_ble() -> None:
204
+ """Scan for BLE devices."""
205
+ try:
206
+ import asyncio
207
+ from bleak import BleakScanner
208
+ except ImportError:
209
+ print("Error: bleak is required. Install with: pip install bleak", file=sys.stderr)
210
+ sys.exit(1)
211
+
212
+ async def scan():
213
+ print("Scanning for BLE devices (5 seconds)...\n")
214
+
215
+ devices = await BleakScanner.discover(timeout=5.0)
216
+
217
+ if not devices:
218
+ print("No BLE devices found.")
219
+ return
220
+
221
+ print(f"Found {len(devices)} device(s):\n")
222
+
223
+ for device in sorted(devices, key=lambda d: d.rssi or -100, reverse=True):
224
+ rssi = f"{device.rssi} dBm" if device.rssi else "N/A"
225
+ print(f" {device.address}")
226
+ if device.name:
227
+ print(f" Name: {device.name}")
228
+ print(f" RSSI: {rssi}")
229
+ print()
230
+
231
+ asyncio.run(scan())
232
+
233
+
234
+ def register_parser(subparsers):
235
+ """Register protocol commands with argparse."""
236
+ protocol_parser = subparsers.add_parser("protocol", help="Manage protocol registry")
237
+ protocol_subparsers = protocol_parser.add_subparsers(dest="protocol_action", help="Protocol action")
238
+
239
+ # protocol list
240
+ protocol_list_parser = protocol_subparsers.add_parser("list", help="List protocols")
241
+ protocol_list_parser.add_argument("-r", "--robot", help="Filter by robot model")
242
+ protocol_list_parser.add_argument("-t", "--transport",
243
+ choices=["ble", "serial", "wifi", "can", "i2c", "spi", "mqtt", "ros2"],
244
+ help="Filter by transport type")
245
+ protocol_list_parser.add_argument("--verified", action="store_true", help="Show only verified protocols")
246
+ protocol_list_parser.add_argument("-s", "--search", help="Search in command format and notes")
247
+
248
+ # protocol get
249
+ protocol_get_parser = protocol_subparsers.add_parser("get", help="Get protocol details")
250
+ protocol_get_parser.add_argument("protocol_id", help="Protocol ID")
251
+
252
+ # protocol init
253
+ protocol_init_parser = protocol_subparsers.add_parser("init", help="Initialize new protocol template")
254
+ protocol_init_parser.add_argument("robot_model", help="Robot model name (e.g., hiwonder-mechdog-pro)")
255
+ protocol_init_parser.add_argument("-t", "--transport", required=True,
256
+ choices=["ble", "serial", "wifi", "can", "i2c", "spi", "mqtt", "ros2"],
257
+ help="Transport type")
258
+ protocol_init_parser.add_argument("-o", "--output", default="./protocol",
259
+ help="Output directory (default: ./protocol)")
260
+
261
+ # protocol push
262
+ protocol_push_parser = protocol_subparsers.add_parser("push", help="Upload protocol to FoodForThought")
263
+ protocol_push_parser.add_argument("file", nargs="?", help="Path to protocol.json (default: ./protocol.json)")
264
+
265
+ # protocol scan-serial
266
+ protocol_subparsers.add_parser("scan-serial", help="Scan for serial ports")
267
+
268
+ # protocol scan-ble
269
+ protocol_subparsers.add_parser("scan-ble", help="Scan for BLE devices")
270
+
271
+
272
+ def handle(client, args):
273
+ """Handle protocol commands."""
274
+ if args.protocol_action == "list":
275
+ protocol_list(client, args.robot, args.transport, args.verified, args.search)
276
+ elif args.protocol_action == "get":
277
+ protocol_get(client, args.protocol_id)
278
+ elif args.protocol_action == "init":
279
+ protocol_init(client, args.robot_model, args.transport, args.output)
280
+ elif args.protocol_action == "push":
281
+ protocol_push(client, args.file)
282
+ elif args.protocol_action == "scan-serial":
283
+ protocol_scan_serial()
284
+ elif args.protocol_action == "scan-ble":
285
+ protocol_scan_ble()
286
+ else:
287
+ print("Usage: ate protocol {list|get|init|push|scan-serial|scan-ble}")
288
+ sys.exit(1)