Skip to content

Commit e338220

Browse files
author
cobot
committed
More merging
1 parent a4d57b6 commit e338220

File tree

4 files changed

+275
-0
lines changed

4 files changed

+275
-0
lines changed

cli.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#!/usr/bin/env python3
2+
3+
import asyncio
4+
import websockets
5+
import json
6+
import argparse
7+
import sys
8+
9+
async def send_request(uri, prompt, execute=False, verbose=True):
10+
try:
11+
async with websockets.connect(uri) as websocket:
12+
request = {
13+
"type": "code",
14+
"prompt": prompt,
15+
"execute": False
16+
}
17+
if verbose:
18+
print(f"Sending request: {json.dumps(request, indent=2)}")
19+
await websocket.send(json.dumps(request))
20+
21+
response_message = await websocket.recv()
22+
response = json.loads(response_message)
23+
24+
if verbose:
25+
print(f"Raw Response: {json.dumps(response, indent=2)}")
26+
if "code" in response:
27+
print(f"\nGenerated Code:")
28+
print("--------------------------------------------------")
29+
print(response["code"])
30+
print("--------------------------------------------------")
31+
print(f"Timing: {response.get('timing', 'N/A')} seconds")
32+
else:
33+
print(f"Received non-code response: {response}")
34+
35+
# If execute is True and we got code, send the execute request
36+
if execute and "code" in response:
37+
execute_request = {
38+
"type": "execute",
39+
"task": prompt,
40+
"code": response["code"]
41+
}
42+
if verbose:
43+
print(f"Sending execute request: {json.dumps(execute_request, indent=2)}")
44+
45+
await websocket.send(json.dumps(execute_request))
46+
47+
# We might not get a response for execute, or maybe we do?
48+
# The server code 'execute' function prints but doesn't seem to send a response back on the websocket immediately
49+
# unless post_transcript/post_code happens.
50+
# But handle_message doesn't await anything after execute().
51+
# Codebotler server:
52+
# elif data['type'] == 'execute':
53+
# execute(data['code'])
54+
55+
# So we probably won't receive a specific confirmation message for 'execute' type from handle_message.
56+
# But let's assume we proceed.
57+
58+
return response
59+
60+
except ConnectionRefusedError:
61+
error_msg = f"Error: Could not connect to server at {uri}. Is Codebotler running?"
62+
if verbose:
63+
print(error_msg)
64+
return {"error": error_msg}
65+
except Exception as e:
66+
error_msg = f"An error occurred: {e}"
67+
if verbose:
68+
print(error_msg)
69+
return {"error": error_msg}
70+
71+
def main():
72+
parser = argparse.ArgumentParser(description="CLI for Codebotler Code Generation")
73+
parser.add_argument("prompt", type=str, help="Natural language prompt for code generation")
74+
parser.add_argument("--host", type=str, default="localhost", help="Server host (default: localhost)")
75+
parser.add_argument("--port", type=int, default=8190, help="Server port (default: 8190)")
76+
parser.add_argument("--execute", action="store_true", help="Execute the generated code on the server")
77+
78+
args = parser.parse_args()
79+
80+
uri = f"ws://{args.host}:{args.port}"
81+
82+
asyncio.run(send_request(uri, args.prompt, args.execute))
83+
84+
if __name__ == "__main__":
85+
main()

scripts/integration_test.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import sys
5+
import yaml
6+
import random
7+
import time
8+
import asyncio
9+
import argparse
10+
import termios
11+
import tty
12+
from pathlib import Path
13+
14+
# Ensure we can import from src/codebotler
15+
# Assuming the script is run from the workspace root or src/codebotler/scripts
16+
# We want to add 'src/codebotler' to path so we can import cli
17+
# Or better, add 'src' so we can do 'from codebotler import cli'
18+
19+
CURRENT_DIR = Path(__file__).resolve().parent
20+
# Add src/codebotler to sys.path
21+
CODEBOTLER_DIR = CURRENT_DIR.parent
22+
SRC_DIR = CODEBOTLER_DIR.parent
23+
sys.path.insert(0, str(CODEBOTLER_DIR))
24+
25+
print(f"DEBUG: CURRENT_DIR={CURRENT_DIR}")
26+
print(f"DEBUG: CODEBOTLER_DIR={CODEBOTLER_DIR}")
27+
print(f"DEBUG: sys.path[0]={sys.path[0]}")
28+
print(f"DEBUG: Files in CODEBOTLER_DIR: {list(CODEBOTLER_DIR.iterdir())}")
29+
30+
try:
31+
import cli
32+
except ImportError as e:
33+
print(f"Error: Could not import 'cli': {e}")
34+
sys.exit(1)
35+
36+
COMMAND_TEMPLATES = {
37+
"move": "Go to {{location}}.",
38+
"observe": "If there is an {{object}} in the room, say yes",
39+
#"say": "Say {{phrase}}"
40+
#"pick": "Grab the {{object}}.",
41+
"pick": "If there is an {{object}}, grab it. If not say that the {{object}} does not exist"
42+
#"place": "Place the object in the bin"
43+
}
44+
45+
OBJECTS = ["can", "cup", "chair", "bottle", "book", "laptop", "phone", "notebook", "pen", "backpack", "keyboard", "mouse", "table", "lamp"]
46+
PICK_OBJECTS = ["soda can", "cup", "plastic bottle"]
47+
PHRASES = [
48+
"Hello",
49+
"How are you?",
50+
"I am a robot.",
51+
"I like to help humans.",
52+
"I am testing Codebotler.",
53+
"I will be moving around.",
54+
"I will be looking for objects.",
55+
]
56+
57+
def load_locations():
58+
data_path = SRC_DIR / "codebotler_amrl_impl" / "data.yaml"
59+
if not data_path.exists():
60+
print(f"Error: Could not find data.yaml at {data_path}")
61+
return []
62+
63+
with open(data_path, "r") as f:
64+
data = yaml.safe_load(f)
65+
66+
map_name = data.get("MAP", "ahg2apt")
67+
locations_dict = data.get("LOCATIONS", {}).get(map_name, {})
68+
return list(locations_dict.keys())
69+
70+
def get_key():
71+
fd = sys.stdin.fileno()
72+
old_settings = termios.tcgetattr(fd)
73+
try:
74+
tty.setraw(sys.stdin.fileno())
75+
ch = sys.stdin.read(1)
76+
finally:
77+
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
78+
return ch
79+
80+
def wait_for_user_input():
81+
print("Press Enter or Space to continue, Esc to quit...")
82+
while True:
83+
key = get_key()
84+
if key == '\x1b': # Escape key
85+
return False
86+
if key == ' ' or key == '\r' or key == '\n':
87+
return True
88+
# Ignore other keys
89+
90+
def main():
91+
parser = argparse.ArgumentParser(description="Integration test for Codebotler")
92+
parser.add_argument("--single-step", action="store_true", help="Enable single-step mode (wait for user input before each command)")
93+
args = parser.parse_args()
94+
95+
locations = load_locations()
96+
if not locations:
97+
print("No locations found. Exiting.")
98+
return
99+
100+
print(f"Loaded {len(locations)} locations: {locations}")
101+
102+
last_action = None
103+
104+
# Default server config
105+
host = "10.1.0.13"
106+
port = 8190
107+
uri = f"ws://{host}:{port}"
108+
109+
try:
110+
while True:
111+
if args.single_step:
112+
if not wait_for_user_input():
113+
print("\nQuitting...")
114+
break
115+
116+
command_type = ""
117+
command_text = ""
118+
119+
if last_action == "pick" and False:
120+
command_type = "place"
121+
command_text = COMMAND_TEMPLATES["place"]
122+
else:
123+
# Sample action type (move or pick)
124+
command_type = random.choice(list(COMMAND_TEMPLATES.keys()))
125+
126+
if command_type == "move":
127+
loc = random.choice(locations)
128+
command_text = COMMAND_TEMPLATES["move"].replace("{{location}}", loc)
129+
elif command_type == "say":
130+
phrase = random.choice(PHRASES)
131+
command_text = COMMAND_TEMPLATES[command_type].replace("{{phrase}}", phrase)
132+
elif command_type == "observe":
133+
obj = random.choice(OBJECTS)
134+
command_text = COMMAND_TEMPLATES[command_type].replace("{{object}}", obj)
135+
elif command_type == "pick":
136+
obj = random.choice(PICK_OBJECTS)
137+
command_text = COMMAND_TEMPLATES[command_type].replace("{{object}}", obj)
138+
139+
print(f"\n--- Spending command: {command_text} ---")
140+
141+
# Send to codebotler via cli
142+
# We use the programmatic interface we added to cli.py
143+
response = asyncio.run(cli.send_request(uri, command_text, execute=True, verbose=True))
144+
145+
if "error" in response:
146+
print("Failed to send command. Is the server running?")
147+
# Wait a bit before retrying to strictly avoid spamming failed connect requests
148+
time.sleep(5)
149+
else:
150+
# Update state
151+
last_action = command_type
152+
153+
# Wait a bit before next command
154+
time.sleep(5)
155+
156+
except KeyboardInterrupt:
157+
print("\nStopping integration test.")
158+
159+
if __name__ == "__main__":
160+
main()

scripts/verify_exec.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import sys
2+
import os
3+
from robot_interface.src.robot_client_interface import execute_task_program
4+
5+
# Simulating a user program that just prints something
6+
# Important: this defines task_program() because that's what the system assumes
7+
program = """
8+
def task_program():
9+
print("Hello from subprocess!")
10+
if 'go_to' in locals() or 'go_to' in globals():
11+
print("go_to is available")
12+
else:
13+
print("go_to is MISSING")
14+
"""
15+
16+
# We don't need a real robot instance for this test, as the subprocess will create a new one.
17+
# But execute_task_program expects one.
18+
class MockRobot:
19+
pass
20+
21+
print("Running verification...")
22+
try:
23+
execute_task_program(program, MockRobot())
24+
print("Verification passed (no crash).")
25+
except Exception as e:
26+
print(f"Verification failed: {e}")

server.log

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Traceback (most recent call last):
2+
File "/home/ros/cobot_ws/src/codebotler/codebotler.py", line 9, in <module>
3+
import websockets
4+
ModuleNotFoundError: No module named 'websockets'

0 commit comments

Comments
 (0)