computer-use-ootb-internal 0.0.109.post2__py3-none-any.whl → 0.0.111__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.
@@ -1,483 +1,484 @@
1
- import argparse
2
- import time
3
- import json
4
- from datetime import datetime
5
- import threading
6
- import requests
7
- import platform # Add platform import
8
- import pyautogui # Add pyautogui import
9
- import webbrowser # Add webbrowser import
10
- import os # Import os for path joining
11
- from fastapi import FastAPI, Request
12
- from fastapi.responses import JSONResponse
13
- from fastapi.middleware.cors import CORSMiddleware
14
- from computer_use_ootb_internal.computer_use_demo.tools.computer import get_screen_details
15
- from computer_use_ootb_internal.run_teachmode_ootb_args import simple_teachmode_sampling_loop
16
-
17
- app = FastAPI()
18
-
19
- # Add CORS middleware to allow requests from the frontend
20
- app.add_middleware(
21
- CORSMiddleware,
22
- allow_origins=["*"],
23
- allow_credentials=True,
24
- allow_methods=["*"],
25
- allow_headers=["*"],
26
- )
27
-
28
- # Rate limiter for API endpoints
29
- class RateLimiter:
30
- def __init__(self, interval_seconds=2):
31
- self.interval = interval_seconds
32
- self.last_request_time = {}
33
- self.lock = threading.Lock()
34
-
35
- def allow_request(self, endpoint):
36
- with self.lock:
37
- current_time = time.time()
38
- # Priority endpoints always allowed
39
- if endpoint in ["/update_params", "/update_message"]:
40
- return True
41
-
42
- # For other endpoints, apply rate limiting
43
- if endpoint not in self.last_request_time:
44
- self.last_request_time[endpoint] = current_time
45
- return True
46
-
47
- elapsed = current_time - self.last_request_time[endpoint]
48
- if elapsed < self.interval:
49
- return False
50
-
51
- self.last_request_time[endpoint] = current_time
52
- return True
53
-
54
-
55
- def log_ootb_request(server_url, ootb_request_type, data):
56
- logging_data = {
57
- "type": ootb_request_type,
58
- "data": data,
59
- "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
60
- }
61
- if not server_url.endswith("/update_ootb_logging"):
62
- server_logging_url = server_url + "/update_ootb_logging"
63
- else:
64
- server_logging_url = server_url
65
- requests.post(server_logging_url, json=logging_data)
66
-
67
-
68
- class SharedState:
69
- def __init__(self, args):
70
- self.args = args
71
- self.task_updated = False
72
- self.chatbot_messages = []
73
- # Store all state-related data here
74
- self.model = args.model
75
- self.task = getattr(args, 'task', "")
76
- self.selected_screen = args.selected_screen
77
- self.user_id = args.user_id
78
- self.trace_id = args.trace_id
79
- self.api_keys = args.api_keys
80
- self.server_url = args.server_url
81
- self.message_queue = []
82
- self.is_processing = False
83
- self.should_stop = False
84
- self.is_paused = False
85
- # Add a new event to better control stopping
86
- self.stop_event = threading.Event()
87
- # Add a reference to the processing thread
88
- self.processing_thread = None
89
-
90
- shared_state = None
91
- rate_limiter = RateLimiter(interval_seconds=2)
92
-
93
- # Add the new prepare_environment function here
94
- def prepare_environment(state):
95
- """Prepares the environment before starting the main processing loop, e.g., opening specific apps."""
96
- if platform.system() == "Windows":
97
- # Assuming Star Rail mode is indicated by user_id containing "star_rail"
98
- # You might need to adjust this condition based on the actual logic in run_teachmode_args
99
- is_star_rail = "star_rail" in state.user_id.lower() or \
100
- "star_rail" in state.trace_id.lower() or \
101
- "hero_case" in state.trace_id.lower()
102
-
103
- if is_star_rail:
104
- print("Star Rail mode detected on Windows. Opening Edge browser...")
105
- url = "https://sr.mihoyo.com/cloud/#/"
106
- browser_opened = False
107
- try:
108
- # Use only webbrowser.open
109
- print(f"Attempting to open {url} using webbrowser.open()...")
110
- if webbrowser.open(url):
111
- print(f"Successfully requested browser to open {url} via webbrowser.open().")
112
- browser_opened = True
113
- else:
114
- print("webbrowser.open() returned False, indicating potential failure.")
115
-
116
- if not browser_opened:
117
- print("ERROR: Failed to confirm browser opening via webbrowser.open().")
118
- # Still proceed to click attempt
119
-
120
- # Add pyautogui click after attempting to open the browser
121
- print("Proceeding with pyautogui actions...")
122
- time.sleep(5) # Wait time for the browser to load
123
-
124
- # Print detected screen size
125
- screen_width, screen_height = pyautogui.size()
126
- print(f"Detected screen size: {screen_width}x{screen_height}")
127
-
128
- click_x = int(screen_width * (1036 / 1280))
129
- click_y = int(screen_height * (500 / 720))
130
- print(f"Calculated click coordinates: ({click_x}, {click_y})")
131
-
132
- # Disable failsafe before clicking
133
- pyautogui.FAILSAFE = False
134
- print("PyAutoGUI failsafe temporarily disabled.")
135
-
136
- print(f"Clicking at coordinates: ({click_x}, {click_y})")
137
- pyautogui.click(click_x, click_y)
138
- time.sleep(2)
139
- pyautogui.click(click_x, click_y)
140
-
141
- # Re-enable failsafe (optional, as script might end anyway)
142
- # pyautogui.FAILSAFE = True
143
- # print("PyAutoGUI failsafe re-enabled.")
144
-
145
- except Exception as e:
146
- print(f"Error during environment preparation (browser/click): {e}")
147
- finally:
148
- # Ensure failsafe is re-enabled if an error occurs after disabling it
149
- pyautogui.FAILSAFE = True
150
- print("PyAutoGUI failsafe re-enabled.")
151
- else:
152
- # Placeholder for potential preparations on other OS or non-Star Rail modes
153
- print("Environment preparation: No specific actions required for this OS/mode.")
154
-
155
-
156
- @app.post("/update_params")
157
- async def update_parameters(request: Request):
158
- data = await request.json()
159
-
160
- if 'task' not in data:
161
- return JSONResponse(
162
- content={"status": "error", "message": "Missing required field: task"},
163
- status_code=400
164
- )
165
-
166
- shared_state.args = argparse.Namespace(**data)
167
- shared_state.task_updated = True
168
-
169
- # Update shared state when parameters change
170
- shared_state.model = getattr(shared_state.args, 'model', "teach-mode-gpt-4o")
171
- shared_state.task = getattr(shared_state.args, 'task', "Following the instructions to complete the task.")
172
- shared_state.selected_screen = getattr(shared_state.args, 'selected_screen', 0)
173
- shared_state.user_id = getattr(shared_state.args, 'user_id', "hero_cases")
174
- shared_state.trace_id = getattr(shared_state.args, 'trace_id', "build_scroll_combat")
175
- shared_state.api_keys = getattr(shared_state.args, 'api_keys', "sk-proj-1234567890")
176
- shared_state.server_url = getattr(shared_state.args, 'server_url', "http://ec2-44-234-43-86.us-west-2.compute.amazonaws.com")
177
-
178
- log_ootb_request(shared_state.server_url, "update_params", data)
179
-
180
- # Call the preparation function here, after parameters are updated
181
- prepare_environment(shared_state)
182
-
183
- return JSONResponse(
184
- content={"status": "success", "message": "Parameters updated", "new_args": vars(shared_state.args)},
185
- status_code=200
186
- )
187
-
188
- @app.post("/update_message")
189
- async def update_message(request: Request):
190
- data = await request.json()
191
-
192
- if 'message' not in data:
193
- return JSONResponse(
194
- content={"status": "error", "message": "Missing required field: message"},
195
- status_code=400
196
- )
197
-
198
- log_ootb_request(shared_state.server_url, "update_message", data)
199
-
200
- message = data['message']
201
- shared_state.chatbot_messages.append({"role": "user", "content": message})
202
- shared_state.task = message
203
- shared_state.args.task = message
204
-
205
- # Reset stop event before starting
206
- shared_state.stop_event.clear()
207
-
208
- # Start processing if not already running
209
- if not shared_state.is_processing:
210
- # Create and store the thread
211
- shared_state.processing_thread = threading.Thread(target=process_input, daemon=True)
212
- shared_state.processing_thread.start()
213
-
214
- return JSONResponse(
215
- content={"status": "success", "message": "Message received", "task": shared_state.task},
216
- status_code=200
217
- )
218
-
219
- @app.get("/get_messages")
220
- async def get_messages(request: Request):
221
- # Apply rate limiting
222
- if not rate_limiter.allow_request(request.url.path):
223
- return JSONResponse(
224
- content={"status": "error", "message": "Rate limit exceeded. Try again after 2 seconds."},
225
- status_code=429
226
- )
227
-
228
- # log_ootb_request(shared_state.server_url, "get_messages", {})
229
-
230
- # Return all messages in the queue and clear it
231
- messages = shared_state.message_queue.copy()
232
- shared_state.message_queue = []
233
-
234
- return JSONResponse(
235
- content={"status": "success", "messages": messages},
236
- status_code=200
237
- )
238
-
239
- @app.get("/get_screens")
240
- async def get_screens(request: Request):
241
- # Apply rate limiting
242
- if not rate_limiter.allow_request(request.url.path):
243
- return JSONResponse(
244
- content={"status": "error", "message": "Rate limit exceeded. Try again after 2 seconds."},
245
- status_code=429
246
- )
247
-
248
- log_ootb_request(shared_state.server_url, "get_screens", {})
249
-
250
- screen_options, primary_index = get_screen_details()
251
-
252
- return JSONResponse(
253
- content={"status": "success", "screens": screen_options, "primary_index": primary_index},
254
- status_code=200
255
- )
256
-
257
- @app.post("/stop_processing")
258
- async def stop_processing(request: Request):
259
- # Apply rate limiting
260
- if not rate_limiter.allow_request(request.url.path):
261
- return JSONResponse(
262
- content={"status": "error", "message": "Rate limit exceeded. Try again after 2 seconds."},
263
- status_code=429
264
- )
265
-
266
- log_ootb_request(shared_state.server_url, "stop_processing", {})
267
-
268
- if shared_state.is_processing:
269
- # Set both flags to ensure stopping the current task
270
- shared_state.should_stop = True
271
- shared_state.stop_event.set()
272
-
273
- # Send an immediate message to the queue to inform the user
274
- stop_initiated_msg = {"role": "assistant", "content": f"Stopping task '{shared_state.task}'..."}
275
- shared_state.message_queue.append(stop_initiated_msg)
276
-
277
- return JSONResponse(
278
- content={"status": "success", "message": "Task is being stopped, server will remain available for new tasks"},
279
- status_code=200
280
- )
281
- else:
282
- return JSONResponse(
283
- content={"status": "error", "message": "No active processing to stop"},
284
- status_code=400
285
- )
286
-
287
- @app.post("/toggle_pause")
288
- async def toggle_pause(request: Request):
289
- # Apply rate limiting
290
- if not rate_limiter.allow_request(request.url.path):
291
- return JSONResponse(
292
- content={"status": "error", "message": "Rate limit exceeded. Try again after 2 seconds."},
293
- status_code=429
294
- )
295
-
296
- log_ootb_request(shared_state.server_url, "toggle_pause", {})
297
-
298
- if not shared_state.is_processing:
299
- return JSONResponse(
300
- content={"status": "error", "message": "No active processing to pause/resume"},
301
- status_code=400
302
- )
303
-
304
- # Toggle the pause state
305
- shared_state.is_paused = not shared_state.is_paused
306
- current_state = shared_state.is_paused
307
-
308
- print(f"Toggled pause state to: {current_state}")
309
-
310
- status_message = "paused" if current_state else "resumed"
311
-
312
- # Add a message to the queue to inform the user
313
- if current_state:
314
- message = {"role": "assistant", "content": f"Task '{shared_state.task}' has been paused. Click Continue to resume."}
315
- else:
316
- message = {"role": "assistant", "content": f"Task '{shared_state.task}' has been resumed."}
317
-
318
- shared_state.chatbot_messages.append(message)
319
- shared_state.message_queue.append(message)
320
-
321
- return JSONResponse(
322
- content={
323
- "status": "success",
324
- "message": f"Processing {status_message}",
325
- "is_paused": current_state
326
- },
327
- status_code=200
328
- )
329
-
330
- @app.get("/status")
331
- async def get_status(request: Request):
332
- # Apply rate limiting
333
- if not rate_limiter.allow_request(request.url.path):
334
- return JSONResponse(
335
- content={"status": "error", "message": "Rate limit exceeded. Try again after 2 seconds."},
336
- status_code=429
337
- )
338
-
339
- # log_ootb_request(shared_state.server_url, "get_status", {})
340
-
341
- print(f"Status check - Processing: {shared_state.is_processing}, Paused: {shared_state.is_paused}")
342
- return JSONResponse(
343
- content={
344
- "status": "success",
345
- "is_processing": shared_state.is_processing,
346
- "is_paused": shared_state.is_paused
347
- },
348
- status_code=200
349
- )
350
-
351
- def process_input():
352
- shared_state.is_processing = True
353
- shared_state.should_stop = False
354
- shared_state.is_paused = False
355
- shared_state.stop_event.clear() # Ensure stop event is cleared at the start
356
-
357
- print(f"start sampling loop: {shared_state.chatbot_messages}")
358
- print(f"shared_state.args before sampling loop: {shared_state.args}")
359
-
360
-
361
- try:
362
- # Get the generator for the sampling loop
363
- sampling_loop = simple_teachmode_sampling_loop(
364
- model=shared_state.model,
365
- task=shared_state.task,
366
- selected_screen=shared_state.selected_screen,
367
- user_id=shared_state.user_id,
368
- trace_id=shared_state.trace_id,
369
- api_keys=shared_state.api_keys,
370
- server_url=shared_state.server_url,
371
- )
372
-
373
- # Process messages from the sampling loop
374
- for loop_msg in sampling_loop:
375
- # Check stop condition more frequently
376
- if shared_state.should_stop or shared_state.stop_event.is_set():
377
- print("Processing stopped by user")
378
- break
379
-
380
- # Check if paused and wait while paused
381
- while shared_state.is_paused and not shared_state.should_stop and not shared_state.stop_event.is_set():
382
- print(f"Processing paused at: {time.strftime('%H:%M:%S')}")
383
- # Wait a short time and check stop condition regularly
384
- for _ in range(5): # Check 5 times per second
385
- if shared_state.should_stop or shared_state.stop_event.is_set():
386
- break
387
- time.sleep(0.2)
388
-
389
- # Check again after pause loop
390
- if shared_state.should_stop or shared_state.stop_event.is_set():
391
- print("Processing stopped while paused or resuming")
392
- break
393
-
394
- shared_state.chatbot_messages.append(loop_msg)
395
- shared_state.message_queue.append(loop_msg)
396
-
397
- # Short sleep to allow stop signals to be processed
398
- for _ in range(5): # Check 5 times per second
399
- if shared_state.should_stop or shared_state.stop_event.is_set():
400
- print("Processing stopped during sleep")
401
- break
402
- time.sleep(0.1)
403
-
404
- if shared_state.should_stop or shared_state.stop_event.is_set():
405
- break
406
-
407
- except Exception as e:
408
- # Handle any exceptions in the processing loop
409
- error_msg = f"Error during task processing: {str(e)}"
410
- print(error_msg)
411
- error_message = {"role": "assistant", "content": error_msg, "type": "error"}
412
- shared_state.message_queue.append(error_message)
413
-
414
- finally:
415
- # Handle completion or interruption
416
- if shared_state.should_stop or shared_state.stop_event.is_set():
417
- stop_msg = f"Task '{shared_state.task}' was stopped. Ready for new tasks."
418
- final_message = {"role": "assistant", "content": stop_msg, "type": "text"}
419
- else:
420
- complete_msg = f"Task '{shared_state.task}' completed. Thanks for using Teachmode-OOTB."
421
- final_message = {"role": "assistant", "content": complete_msg, "type": "text"}
422
-
423
- shared_state.chatbot_messages.append(final_message)
424
- shared_state.message_queue.append(final_message)
425
-
426
- # Reset all state flags to allow for new tasks
427
- shared_state.is_processing = False
428
- shared_state.should_stop = False
429
- shared_state.is_paused = False
430
- shared_state.stop_event.clear()
431
- print("Processing completed, ready for new tasks")
432
-
433
- def main():
434
- global app, shared_state, rate_limiter
435
-
436
- parser = argparse.ArgumentParser(
437
- description="Run a synchronous sampling loop for assistant/tool interactions in teach-mode."
438
- )
439
- parser.add_argument("--model", default="teach-mode-gpt-4o")
440
- parser.add_argument("--task", default="Create a claim on the SAP system, using Receipt.pdf as attachment.")
441
- parser.add_argument("--selected_screen", type=int, default=0)
442
- parser.add_argument("--user_id", default="star_rail_dev")
443
- parser.add_argument("--trace_id", default="scroll")
444
- parser.add_argument("--api_key_file", default="api_key.json")
445
- parser.add_argument("--api_keys", default="")
446
- parser.add_argument(
447
- "--server_url",
448
- default="http://ec2-44-234-43-86.us-west-2.compute.amazonaws.com",
449
- help="Server URL for the session"
450
- )
451
-
452
- args = parser.parse_args()
453
- shared_state = SharedState(args)
454
- rate_limiter = RateLimiter(interval_seconds=2)
455
-
456
- import uvicorn
457
- import platform
458
- import os
459
-
460
- # Default port
461
- port = 7888
462
-
463
- # Determine port based on Windows username
464
- if platform.system() == "Windows":
465
- username = os.environ["USERNAME"].lower()
466
- if username == "altair":
467
- port = 14000
468
- elif username.startswith("guest") and username[5:].isdigit():
469
- num = int(username[5:])
470
- if 1 <= num <= 10:
471
- port = 14000 + num
472
- else:
473
- port = 7888
474
- else:
475
- port = 7888
476
-
477
- uvicorn.run(app, host="0.0.0.0", port=port)
478
-
479
- if __name__ == "__main__":
480
- # main()
481
-
482
- # Test log_ootb_request
1
+ import argparse
2
+ import time
3
+ import json
4
+ from datetime import datetime
5
+ import threading
6
+ import requests
7
+ import platform # Add platform import
8
+ import pyautogui # Add pyautogui import
9
+ import webbrowser # Add webbrowser import
10
+ import os # Import os for path joining
11
+ from fastapi import FastAPI, Request
12
+ from fastapi.responses import JSONResponse
13
+ from fastapi.middleware.cors import CORSMiddleware
14
+ from computer_use_ootb_internal.computer_use_demo.tools.computer import get_screen_details
15
+ from computer_use_ootb_internal.run_teachmode_ootb_args import simple_teachmode_sampling_loop
16
+
17
+ app = FastAPI()
18
+
19
+ # Add CORS middleware to allow requests from the frontend
20
+ app.add_middleware(
21
+ CORSMiddleware,
22
+ allow_origins=["*"],
23
+ allow_credentials=True,
24
+ allow_methods=["*"],
25
+ allow_headers=["*"],
26
+ )
27
+
28
+ # Rate limiter for API endpoints
29
+ class RateLimiter:
30
+ def __init__(self, interval_seconds=2):
31
+ self.interval = interval_seconds
32
+ self.last_request_time = {}
33
+ self.lock = threading.Lock()
34
+
35
+ def allow_request(self, endpoint):
36
+ with self.lock:
37
+ current_time = time.time()
38
+ # Priority endpoints always allowed
39
+ if endpoint in ["/update_params", "/update_message"]:
40
+ return True
41
+
42
+ # For other endpoints, apply rate limiting
43
+ if endpoint not in self.last_request_time:
44
+ self.last_request_time[endpoint] = current_time
45
+ return True
46
+
47
+ elapsed = current_time - self.last_request_time[endpoint]
48
+ if elapsed < self.interval:
49
+ return False
50
+
51
+ self.last_request_time[endpoint] = current_time
52
+ return True
53
+
54
+
55
+ def log_ootb_request(server_url, ootb_request_type, data):
56
+ logging_data = {
57
+ "type": ootb_request_type,
58
+ "data": data,
59
+ "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
60
+ }
61
+ if not server_url.endswith("/update_ootb_logging"):
62
+ server_logging_url = server_url + "/update_ootb_logging"
63
+ else:
64
+ server_logging_url = server_url
65
+ requests.post(server_logging_url, json=logging_data)
66
+
67
+
68
+ class SharedState:
69
+ def __init__(self, args):
70
+ self.args = args
71
+ self.task_updated = False
72
+ self.chatbot_messages = []
73
+ # Store all state-related data here
74
+ self.model = args.model
75
+ self.task = getattr(args, 'task', "")
76
+ self.selected_screen = args.selected_screen
77
+ self.user_id = args.user_id
78
+ self.trace_id = args.trace_id
79
+ self.api_keys = args.api_keys
80
+ self.server_url = args.server_url
81
+ self.message_queue = []
82
+ self.is_processing = False
83
+ self.should_stop = False
84
+ self.is_paused = False
85
+ # Add a new event to better control stopping
86
+ self.stop_event = threading.Event()
87
+ # Add a reference to the processing thread
88
+ self.processing_thread = None
89
+
90
+ shared_state = None
91
+ rate_limiter = RateLimiter(interval_seconds=2)
92
+
93
+ # Add the new prepare_environment function here
94
+ def prepare_environment(state):
95
+ """Prepares the environment before starting the main processing loop, e.g., opening specific apps."""
96
+ if platform.system() == "Windows":
97
+ # Assuming Star Rail mode is indicated by user_id containing "star_rail"
98
+ # You might need to adjust this condition based on the actual logic in run_teachmode_args
99
+ is_star_rail = "star_rail" in state.user_id.lower() or \
100
+ "star_rail" in state.trace_id.lower() or \
101
+ "hero_case" in state.user_id.lower() or \
102
+ "hero_case" in state.trace_id.lower()
103
+
104
+ if is_star_rail:
105
+ print("Star Rail mode detected on Windows. Opening Edge browser...")
106
+ url = "https://sr.mihoyo.com/cloud/#/"
107
+ browser_opened = False
108
+ try:
109
+ # Use only webbrowser.open
110
+ print(f"Attempting to open {url} using webbrowser.open()...")
111
+ if webbrowser.open(url):
112
+ print(f"Successfully requested browser to open {url} via webbrowser.open().")
113
+ browser_opened = True
114
+ else:
115
+ print("webbrowser.open() returned False, indicating potential failure.")
116
+
117
+ if not browser_opened:
118
+ print("ERROR: Failed to confirm browser opening via webbrowser.open().")
119
+ # Still proceed to click attempt
120
+
121
+ # Add pyautogui click after attempting to open the browser
122
+ print("Proceeding with pyautogui actions...")
123
+ time.sleep(5) # Wait time for the browser to load
124
+
125
+ # Print detected screen size
126
+ screen_width, screen_height = pyautogui.size()
127
+ print(f"Detected screen size: {screen_width}x{screen_height}")
128
+
129
+ click_x = int(screen_width * (1036 / 1280))
130
+ click_y = int(screen_height * (500 / 720))
131
+ print(f"Calculated click coordinates: ({click_x}, {click_y})")
132
+
133
+ # Disable failsafe before clicking
134
+ pyautogui.FAILSAFE = False
135
+ print("PyAutoGUI failsafe temporarily disabled.")
136
+
137
+ print(f"Clicking at coordinates: ({click_x}, {click_y})")
138
+ pyautogui.click(click_x, click_y)
139
+ time.sleep(2)
140
+ pyautogui.click(click_x, click_y)
141
+
142
+ # Re-enable failsafe (optional, as script might end anyway)
143
+ # pyautogui.FAILSAFE = True
144
+ # print("PyAutoGUI failsafe re-enabled.")
145
+
146
+ except Exception as e:
147
+ print(f"Error during environment preparation (browser/click): {e}")
148
+ finally:
149
+ # Ensure failsafe is re-enabled if an error occurs after disabling it
150
+ pyautogui.FAILSAFE = True
151
+ print("PyAutoGUI failsafe re-enabled.")
152
+ else:
153
+ # Placeholder for potential preparations on other OS or non-Star Rail modes
154
+ print("Environment preparation: No specific actions required for this OS/mode.")
155
+
156
+
157
+ @app.post("/update_params")
158
+ async def update_parameters(request: Request):
159
+ data = await request.json()
160
+
161
+ if 'task' not in data:
162
+ return JSONResponse(
163
+ content={"status": "error", "message": "Missing required field: task"},
164
+ status_code=400
165
+ )
166
+
167
+ shared_state.args = argparse.Namespace(**data)
168
+ shared_state.task_updated = True
169
+
170
+ # Update shared state when parameters change
171
+ shared_state.model = getattr(shared_state.args, 'model', "teach-mode-gpt-4o")
172
+ shared_state.task = getattr(shared_state.args, 'task', "Following the instructions to complete the task.")
173
+ shared_state.selected_screen = getattr(shared_state.args, 'selected_screen', 0)
174
+ shared_state.user_id = getattr(shared_state.args, 'user_id', "hero_cases")
175
+ shared_state.trace_id = getattr(shared_state.args, 'trace_id', "build_scroll_combat")
176
+ shared_state.api_keys = getattr(shared_state.args, 'api_keys', "sk-proj-1234567890")
177
+ shared_state.server_url = getattr(shared_state.args, 'server_url', "http://ec2-44-234-43-86.us-west-2.compute.amazonaws.com")
178
+
179
+ log_ootb_request(shared_state.server_url, "update_params", data)
180
+
181
+ # Call the preparation function here, after parameters are updated
182
+ prepare_environment(shared_state)
183
+
184
+ return JSONResponse(
185
+ content={"status": "success", "message": "Parameters updated", "new_args": vars(shared_state.args)},
186
+ status_code=200
187
+ )
188
+
189
+ @app.post("/update_message")
190
+ async def update_message(request: Request):
191
+ data = await request.json()
192
+
193
+ if 'message' not in data:
194
+ return JSONResponse(
195
+ content={"status": "error", "message": "Missing required field: message"},
196
+ status_code=400
197
+ )
198
+
199
+ log_ootb_request(shared_state.server_url, "update_message", data)
200
+
201
+ message = data['message']
202
+ shared_state.chatbot_messages.append({"role": "user", "content": message})
203
+ shared_state.task = message
204
+ shared_state.args.task = message
205
+
206
+ # Reset stop event before starting
207
+ shared_state.stop_event.clear()
208
+
209
+ # Start processing if not already running
210
+ if not shared_state.is_processing:
211
+ # Create and store the thread
212
+ shared_state.processing_thread = threading.Thread(target=process_input, daemon=True)
213
+ shared_state.processing_thread.start()
214
+
215
+ return JSONResponse(
216
+ content={"status": "success", "message": "Message received", "task": shared_state.task},
217
+ status_code=200
218
+ )
219
+
220
+ @app.get("/get_messages")
221
+ async def get_messages(request: Request):
222
+ # Apply rate limiting
223
+ if not rate_limiter.allow_request(request.url.path):
224
+ return JSONResponse(
225
+ content={"status": "error", "message": "Rate limit exceeded. Try again after 2 seconds."},
226
+ status_code=429
227
+ )
228
+
229
+ # log_ootb_request(shared_state.server_url, "get_messages", {})
230
+
231
+ # Return all messages in the queue and clear it
232
+ messages = shared_state.message_queue.copy()
233
+ shared_state.message_queue = []
234
+
235
+ return JSONResponse(
236
+ content={"status": "success", "messages": messages},
237
+ status_code=200
238
+ )
239
+
240
+ @app.get("/get_screens")
241
+ async def get_screens(request: Request):
242
+ # Apply rate limiting
243
+ if not rate_limiter.allow_request(request.url.path):
244
+ return JSONResponse(
245
+ content={"status": "error", "message": "Rate limit exceeded. Try again after 2 seconds."},
246
+ status_code=429
247
+ )
248
+
249
+ log_ootb_request(shared_state.server_url, "get_screens", {})
250
+
251
+ screen_options, primary_index = get_screen_details()
252
+
253
+ return JSONResponse(
254
+ content={"status": "success", "screens": screen_options, "primary_index": primary_index},
255
+ status_code=200
256
+ )
257
+
258
+ @app.post("/stop_processing")
259
+ async def stop_processing(request: Request):
260
+ # Apply rate limiting
261
+ if not rate_limiter.allow_request(request.url.path):
262
+ return JSONResponse(
263
+ content={"status": "error", "message": "Rate limit exceeded. Try again after 2 seconds."},
264
+ status_code=429
265
+ )
266
+
267
+ log_ootb_request(shared_state.server_url, "stop_processing", {})
268
+
269
+ if shared_state.is_processing:
270
+ # Set both flags to ensure stopping the current task
271
+ shared_state.should_stop = True
272
+ shared_state.stop_event.set()
273
+
274
+ # Send an immediate message to the queue to inform the user
275
+ stop_initiated_msg = {"role": "assistant", "content": f"Stopping task '{shared_state.task}'..."}
276
+ shared_state.message_queue.append(stop_initiated_msg)
277
+
278
+ return JSONResponse(
279
+ content={"status": "success", "message": "Task is being stopped, server will remain available for new tasks"},
280
+ status_code=200
281
+ )
282
+ else:
283
+ return JSONResponse(
284
+ content={"status": "error", "message": "No active processing to stop"},
285
+ status_code=400
286
+ )
287
+
288
+ @app.post("/toggle_pause")
289
+ async def toggle_pause(request: Request):
290
+ # Apply rate limiting
291
+ if not rate_limiter.allow_request(request.url.path):
292
+ return JSONResponse(
293
+ content={"status": "error", "message": "Rate limit exceeded. Try again after 2 seconds."},
294
+ status_code=429
295
+ )
296
+
297
+ log_ootb_request(shared_state.server_url, "toggle_pause", {})
298
+
299
+ if not shared_state.is_processing:
300
+ return JSONResponse(
301
+ content={"status": "error", "message": "No active processing to pause/resume"},
302
+ status_code=400
303
+ )
304
+
305
+ # Toggle the pause state
306
+ shared_state.is_paused = not shared_state.is_paused
307
+ current_state = shared_state.is_paused
308
+
309
+ print(f"Toggled pause state to: {current_state}")
310
+
311
+ status_message = "paused" if current_state else "resumed"
312
+
313
+ # Add a message to the queue to inform the user
314
+ if current_state:
315
+ message = {"role": "assistant", "content": f"Task '{shared_state.task}' has been paused. Click Continue to resume."}
316
+ else:
317
+ message = {"role": "assistant", "content": f"Task '{shared_state.task}' has been resumed."}
318
+
319
+ shared_state.chatbot_messages.append(message)
320
+ shared_state.message_queue.append(message)
321
+
322
+ return JSONResponse(
323
+ content={
324
+ "status": "success",
325
+ "message": f"Processing {status_message}",
326
+ "is_paused": current_state
327
+ },
328
+ status_code=200
329
+ )
330
+
331
+ @app.get("/status")
332
+ async def get_status(request: Request):
333
+ # Apply rate limiting
334
+ if not rate_limiter.allow_request(request.url.path):
335
+ return JSONResponse(
336
+ content={"status": "error", "message": "Rate limit exceeded. Try again after 2 seconds."},
337
+ status_code=429
338
+ )
339
+
340
+ # log_ootb_request(shared_state.server_url, "get_status", {})
341
+
342
+ print(f"Status check - Processing: {shared_state.is_processing}, Paused: {shared_state.is_paused}")
343
+ return JSONResponse(
344
+ content={
345
+ "status": "success",
346
+ "is_processing": shared_state.is_processing,
347
+ "is_paused": shared_state.is_paused
348
+ },
349
+ status_code=200
350
+ )
351
+
352
+ def process_input():
353
+ shared_state.is_processing = True
354
+ shared_state.should_stop = False
355
+ shared_state.is_paused = False
356
+ shared_state.stop_event.clear() # Ensure stop event is cleared at the start
357
+
358
+ print(f"start sampling loop: {shared_state.chatbot_messages}")
359
+ print(f"shared_state.args before sampling loop: {shared_state.args}")
360
+
361
+
362
+ try:
363
+ # Get the generator for the sampling loop
364
+ sampling_loop = simple_teachmode_sampling_loop(
365
+ model=shared_state.model,
366
+ task=shared_state.task,
367
+ selected_screen=shared_state.selected_screen,
368
+ user_id=shared_state.user_id,
369
+ trace_id=shared_state.trace_id,
370
+ api_keys=shared_state.api_keys,
371
+ server_url=shared_state.server_url,
372
+ )
373
+
374
+ # Process messages from the sampling loop
375
+ for loop_msg in sampling_loop:
376
+ # Check stop condition more frequently
377
+ if shared_state.should_stop or shared_state.stop_event.is_set():
378
+ print("Processing stopped by user")
379
+ break
380
+
381
+ # Check if paused and wait while paused
382
+ while shared_state.is_paused and not shared_state.should_stop and not shared_state.stop_event.is_set():
383
+ print(f"Processing paused at: {time.strftime('%H:%M:%S')}")
384
+ # Wait a short time and check stop condition regularly
385
+ for _ in range(5): # Check 5 times per second
386
+ if shared_state.should_stop or shared_state.stop_event.is_set():
387
+ break
388
+ time.sleep(0.2)
389
+
390
+ # Check again after pause loop
391
+ if shared_state.should_stop or shared_state.stop_event.is_set():
392
+ print("Processing stopped while paused or resuming")
393
+ break
394
+
395
+ shared_state.chatbot_messages.append(loop_msg)
396
+ shared_state.message_queue.append(loop_msg)
397
+
398
+ # Short sleep to allow stop signals to be processed
399
+ for _ in range(5): # Check 5 times per second
400
+ if shared_state.should_stop or shared_state.stop_event.is_set():
401
+ print("Processing stopped during sleep")
402
+ break
403
+ time.sleep(0.1)
404
+
405
+ if shared_state.should_stop or shared_state.stop_event.is_set():
406
+ break
407
+
408
+ except Exception as e:
409
+ # Handle any exceptions in the processing loop
410
+ error_msg = f"Error during task processing: {str(e)}"
411
+ print(error_msg)
412
+ error_message = {"role": "assistant", "content": error_msg, "type": "error"}
413
+ shared_state.message_queue.append(error_message)
414
+
415
+ finally:
416
+ # Handle completion or interruption
417
+ if shared_state.should_stop or shared_state.stop_event.is_set():
418
+ stop_msg = f"Task '{shared_state.task}' was stopped. Ready for new tasks."
419
+ final_message = {"role": "assistant", "content": stop_msg, "type": "text"}
420
+ else:
421
+ complete_msg = f"Task '{shared_state.task}' completed. Thanks for using Teachmode-OOTB."
422
+ final_message = {"role": "assistant", "content": complete_msg, "type": "text"}
423
+
424
+ shared_state.chatbot_messages.append(final_message)
425
+ shared_state.message_queue.append(final_message)
426
+
427
+ # Reset all state flags to allow for new tasks
428
+ shared_state.is_processing = False
429
+ shared_state.should_stop = False
430
+ shared_state.is_paused = False
431
+ shared_state.stop_event.clear()
432
+ print("Processing completed, ready for new tasks")
433
+
434
+ def main():
435
+ global app, shared_state, rate_limiter
436
+
437
+ parser = argparse.ArgumentParser(
438
+ description="Run a synchronous sampling loop for assistant/tool interactions in teach-mode."
439
+ )
440
+ parser.add_argument("--model", default="teach-mode-gpt-4o")
441
+ parser.add_argument("--task", default="Create a claim on the SAP system, using Receipt.pdf as attachment.")
442
+ parser.add_argument("--selected_screen", type=int, default=0)
443
+ parser.add_argument("--user_id", default="star_rail_dev")
444
+ parser.add_argument("--trace_id", default="scroll")
445
+ parser.add_argument("--api_key_file", default="api_key.json")
446
+ parser.add_argument("--api_keys", default="")
447
+ parser.add_argument(
448
+ "--server_url",
449
+ default="http://ec2-44-234-43-86.us-west-2.compute.amazonaws.com",
450
+ help="Server URL for the session"
451
+ )
452
+
453
+ args = parser.parse_args()
454
+ shared_state = SharedState(args)
455
+ rate_limiter = RateLimiter(interval_seconds=2)
456
+
457
+ import uvicorn
458
+ import platform
459
+ import os
460
+
461
+ # Default port
462
+ port = 7888
463
+
464
+ # Determine port based on Windows username
465
+ if platform.system() == "Windows":
466
+ username = os.environ["USERNAME"].lower()
467
+ if username == "altair":
468
+ port = 14000
469
+ elif username.startswith("guest") and username[5:].isdigit():
470
+ num = int(username[5:])
471
+ if 1 <= num <= 10:
472
+ port = 14000 + num
473
+ else:
474
+ port = 7888
475
+ else:
476
+ port = 7888
477
+
478
+ uvicorn.run(app, host="0.0.0.0", port=port)
479
+
480
+ if __name__ == "__main__":
481
+ # main()
482
+
483
+ # Test log_ootb_request
483
484
  log_ootb_request("http://ec2-44-234-43-86.us-west-2.compute.amazonaws.com", "test_request", {"message": "Test message"})