Spaces:
Running
Running
owenkaplinsky
commited on
Commit
·
ea991cb
1
Parent(s):
0daf9fe
Merge results into one endpoint
Browse files- project/chat.py +181 -278
- project/src/index.js +10 -5
- project/unified_server.py +4 -0
project/chat.py
CHANGED
|
@@ -27,25 +27,63 @@ latest_blockly_chat_code = ""
|
|
| 27 |
# Global variable to store the workspace's variables
|
| 28 |
latest_blockly_vars = ""
|
| 29 |
|
| 30 |
-
#
|
| 31 |
-
|
| 32 |
-
deletion_results = {}
|
| 33 |
|
| 34 |
-
#
|
| 35 |
-
|
| 36 |
-
creation_results = {}
|
| 37 |
|
| 38 |
-
#
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
# Global variable to store the deployed HF MCP server URL
|
| 51 |
current_mcp_server_url = None
|
|
@@ -109,32 +147,22 @@ def delete_block(block_id):
|
|
| 109 |
try:
|
| 110 |
print(f"[DELETE REQUEST] Attempting to delete block: {block_id}")
|
| 111 |
|
| 112 |
-
#
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
# Add to deletion queue
|
| 117 |
-
deletion_queue.put({"block_id": block_id})
|
| 118 |
print(f"[DELETE REQUEST] Added to queue: {block_id}")
|
| 119 |
|
| 120 |
-
# Wait for result
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
return f"[TOOL] Successfully deleted block {block_id}"
|
| 132 |
-
else:
|
| 133 |
-
return f"[TOOL] Failed to delete block {block_id}: {result.get('error', 'Unknown error')}"
|
| 134 |
-
time.sleep(check_interval)
|
| 135 |
-
|
| 136 |
-
print(f"[DELETE TIMEOUT] No response received for block {block_id} after {timeout} seconds")
|
| 137 |
-
return f"Timeout waiting for deletion confirmation for block {block_id}"
|
| 138 |
|
| 139 |
except Exception as e:
|
| 140 |
print(f"[DELETE ERROR] {e}")
|
|
@@ -145,41 +173,29 @@ def delete_block(block_id):
|
|
| 145 |
def create_block(block_spec, blockID=None, placement_type=None, input_name=None):
|
| 146 |
try:
|
| 147 |
# Generate a unique request ID
|
| 148 |
-
import uuid
|
| 149 |
request_id = str(uuid.uuid4())
|
| 150 |
|
| 151 |
-
# Clear any old results for this request ID first
|
| 152 |
-
if request_id in creation_results:
|
| 153 |
-
creation_results.pop(request_id)
|
| 154 |
-
|
| 155 |
# Add to creation queue with optional blockID, placement_type, and input_name
|
| 156 |
-
queue_data = {"request_id": request_id, "block_spec": block_spec}
|
| 157 |
if blockID:
|
| 158 |
queue_data["blockID"] = blockID
|
| 159 |
if placement_type:
|
| 160 |
queue_data["placement_type"] = placement_type
|
| 161 |
if input_name:
|
| 162 |
queue_data["input_name"] = input_name
|
| 163 |
-
|
| 164 |
|
| 165 |
-
# Wait for result
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
else:
|
| 177 |
-
error_msg = result.get('error') or 'Unknown error'
|
| 178 |
-
return f"[TOOL] Failed to create block: {error_msg}"
|
| 179 |
-
time.sleep(check_interval)
|
| 180 |
-
|
| 181 |
-
print(f"[CREATE TIMEOUT] No response received for request {request_id} after {timeout} seconds")
|
| 182 |
-
return f"Timeout waiting for block creation confirmation"
|
| 183 |
|
| 184 |
except Exception as e:
|
| 185 |
print(f"[CREATE ERROR] {e}")
|
|
@@ -194,32 +210,22 @@ def create_variable(var_name):
|
|
| 194 |
# Generate a unique request ID
|
| 195 |
request_id = str(uuid.uuid4())
|
| 196 |
|
| 197 |
-
# Clear any old results for this request ID first
|
| 198 |
-
if request_id in variable_results:
|
| 199 |
-
variable_results.pop(request_id)
|
| 200 |
-
|
| 201 |
# Add to variable creation queue
|
| 202 |
-
queue_data = {"request_id": request_id, "variable_name": var_name}
|
| 203 |
-
|
| 204 |
print(f"[VARIABLE REQUEST] Added to queue with ID: {request_id}")
|
| 205 |
|
| 206 |
-
# Wait for result
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
else:
|
| 218 |
-
return f"[TOOL] Failed to create variable: {result.get('error', 'Unknown error')}"
|
| 219 |
-
time.sleep(check_interval)
|
| 220 |
-
|
| 221 |
-
print(f"[VARIABLE TIMEOUT] No response received for request {request_id} after {timeout} seconds")
|
| 222 |
-
return f"Timeout waiting for variable creation confirmation"
|
| 223 |
|
| 224 |
except Exception as e:
|
| 225 |
print(f"[VARIABLE ERROR] {e}")
|
|
@@ -234,38 +240,28 @@ def edit_mcp(inputs=None, outputs=None):
|
|
| 234 |
# Generate a unique request ID
|
| 235 |
request_id = str(uuid.uuid4())
|
| 236 |
|
| 237 |
-
# Clear any old results for this request ID first
|
| 238 |
-
if request_id in edit_mcp_results:
|
| 239 |
-
edit_mcp_results.pop(request_id)
|
| 240 |
-
|
| 241 |
# Build the edit data
|
| 242 |
-
edit_data = {"request_id": request_id}
|
| 243 |
if inputs is not None:
|
| 244 |
edit_data["inputs"] = inputs
|
| 245 |
if outputs is not None:
|
| 246 |
edit_data["outputs"] = outputs
|
| 247 |
|
| 248 |
# Add to edit MCP queue
|
| 249 |
-
|
| 250 |
print(f"[EDIT MCP REQUEST] Added to queue with ID: {request_id}")
|
| 251 |
|
| 252 |
-
# Wait for result
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
else:
|
| 264 |
-
return f"[TOOL] Failed to edit MCP block: {result.get('error', 'Unknown error')}"
|
| 265 |
-
time.sleep(check_interval)
|
| 266 |
-
|
| 267 |
-
print(f"[EDIT MCP TIMEOUT] No response received for request {request_id} after {timeout} seconds")
|
| 268 |
-
return f"Timeout waiting for MCP edit confirmation"
|
| 269 |
|
| 270 |
except Exception as e:
|
| 271 |
print(f"[EDIT MCP ERROR] {e}")
|
|
@@ -280,34 +276,24 @@ def replace_block(block_id, command):
|
|
| 280 |
# Generate a unique request ID
|
| 281 |
request_id = str(uuid.uuid4())
|
| 282 |
|
| 283 |
-
# Clear any old results for this request ID first
|
| 284 |
-
if request_id in replace_block_results:
|
| 285 |
-
replace_block_results.pop(request_id)
|
| 286 |
-
|
| 287 |
# Build the replace data
|
| 288 |
-
replace_data = {"request_id": request_id, "block_id": block_id, "block_spec": command}
|
| 289 |
|
| 290 |
# Add to replace block queue
|
| 291 |
-
|
| 292 |
print(f"[REPLACE REQUEST] Added to queue with ID: {request_id}")
|
| 293 |
|
| 294 |
-
# Wait for result
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
else:
|
| 306 |
-
return f"[TOOL] Failed to replace block: {result.get('error', 'Unknown error')}"
|
| 307 |
-
time.sleep(check_interval)
|
| 308 |
-
|
| 309 |
-
print(f"[REPLACE TIMEOUT] No response received for request {request_id} after {timeout} seconds")
|
| 310 |
-
return f"Timeout waiting for block replacement confirmation"
|
| 311 |
|
| 312 |
except Exception as e:
|
| 313 |
print(f"[REPLACE ERROR] {e}")
|
|
@@ -330,90 +316,26 @@ async def unified_stream():
|
|
| 330 |
|
| 331 |
while True:
|
| 332 |
try:
|
| 333 |
-
# Check
|
| 334 |
-
if not
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
request_key = f"delete_{block_id}"
|
| 338 |
|
| 339 |
-
#
|
| 340 |
-
if
|
| 341 |
-
|
| 342 |
-
deletion_request["type"] = "delete" # Add type identifier
|
| 343 |
-
yield f"data: {json.dumps(deletion_request)}\n\n"
|
| 344 |
-
|
| 345 |
-
# Clear from sent_requests after 10 seconds
|
| 346 |
-
asyncio.create_task(clear_sent_request(sent_requests, request_key, 10))
|
| 347 |
else:
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
# Check creation queue
|
| 351 |
-
elif not creation_queue.empty():
|
| 352 |
-
creation_request = creation_queue.get_nowait()
|
| 353 |
-
request_id = creation_request.get("request_id")
|
| 354 |
-
request_key = f"create_{request_id}"
|
| 355 |
|
| 356 |
# Avoid sending duplicate requests too quickly
|
| 357 |
if request_key not in sent_requests:
|
| 358 |
sent_requests.add(request_key)
|
| 359 |
-
|
| 360 |
-
yield f"data: {json.dumps(creation_request)}\n\n"
|
| 361 |
|
| 362 |
# Clear from sent_requests after 10 seconds
|
| 363 |
asyncio.create_task(clear_sent_request(sent_requests, request_key, 10))
|
| 364 |
else:
|
| 365 |
-
print(f"[SSE SKIP] Skipping duplicate request
|
| 366 |
-
|
| 367 |
-
# Check variable queue
|
| 368 |
-
elif not variable_queue.empty():
|
| 369 |
-
variable_request = variable_queue.get_nowait()
|
| 370 |
-
request_id = variable_request.get("request_id")
|
| 371 |
-
request_key = f"variable_{request_id}"
|
| 372 |
-
|
| 373 |
-
# Avoid sending duplicate requests too quickly
|
| 374 |
-
if request_key not in sent_requests:
|
| 375 |
-
sent_requests.add(request_key)
|
| 376 |
-
variable_request["type"] = "variable" # Add type identifier
|
| 377 |
-
yield f"data: {json.dumps(variable_request)}\n\n"
|
| 378 |
-
|
| 379 |
-
# Clear from sent_requests after 10 seconds
|
| 380 |
-
asyncio.create_task(clear_sent_request(sent_requests, request_key, 10))
|
| 381 |
-
else:
|
| 382 |
-
print(f"[SSE SKIP] Skipping duplicate request for ID: {request_id}")
|
| 383 |
-
|
| 384 |
-
# Check edit MCP queue
|
| 385 |
-
elif not edit_mcp_queue.empty():
|
| 386 |
-
edit_request = edit_mcp_queue.get_nowait()
|
| 387 |
-
request_id = edit_request.get("request_id")
|
| 388 |
-
request_key = f"edit_mcp_{request_id}"
|
| 389 |
-
|
| 390 |
-
# Avoid sending duplicate requests too quickly
|
| 391 |
-
if request_key not in sent_requests:
|
| 392 |
-
sent_requests.add(request_key)
|
| 393 |
-
edit_request["type"] = "edit_mcp" # Add type identifier
|
| 394 |
-
yield f"data: {json.dumps(edit_request)}\n\n"
|
| 395 |
-
|
| 396 |
-
# Clear from sent_requests after 10 seconds
|
| 397 |
-
asyncio.create_task(clear_sent_request(sent_requests, request_key, 10))
|
| 398 |
-
else:
|
| 399 |
-
print(f"[SSE SKIP] Skipping duplicate request for ID: {request_id}")
|
| 400 |
-
|
| 401 |
-
# Check replace block queue
|
| 402 |
-
elif not replace_block_queue.empty():
|
| 403 |
-
replace_request = replace_block_queue.get_nowait()
|
| 404 |
-
request_id = replace_request.get("request_id")
|
| 405 |
-
request_key = f"replace_{request_id}"
|
| 406 |
-
|
| 407 |
-
# Avoid sending duplicate requests too quickly
|
| 408 |
-
if request_key not in sent_requests:
|
| 409 |
-
sent_requests.add(request_key)
|
| 410 |
-
replace_request["type"] = "replace" # Add type identifier
|
| 411 |
-
yield f"data: {json.dumps(replace_request)}\n\n"
|
| 412 |
-
|
| 413 |
-
# Clear from sent_requests after 10 seconds
|
| 414 |
-
asyncio.create_task(clear_sent_request(sent_requests, request_key, 10))
|
| 415 |
-
else:
|
| 416 |
-
print(f"[SSE SKIP] Skipping duplicate request for ID: {request_id}")
|
| 417 |
|
| 418 |
else:
|
| 419 |
# Send a heartbeat every 30 seconds to keep connection alive
|
|
@@ -439,80 +361,61 @@ async def unified_stream():
|
|
| 439 |
}
|
| 440 |
)
|
| 441 |
|
| 442 |
-
#
|
| 443 |
-
@app.
|
| 444 |
-
async def
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
error = data.get("error")
|
| 461 |
-
|
| 462 |
-
print(f"[DELETION RESULT RECEIVED] block_id={block_id}, success={success}, error={error}")
|
| 463 |
-
|
| 464 |
-
if block_id:
|
| 465 |
-
# Store the result for the delete_block function to retrieve
|
| 466 |
-
deletion_results[block_id] = data
|
| 467 |
-
print(f"[DELETION RESULT STORED] Results dict now has {len(deletion_results)} items")
|
| 468 |
-
|
| 469 |
-
return {"received": True}
|
| 470 |
-
|
| 471 |
-
# Endpoint to receive variable creation results from frontend
|
| 472 |
-
@app.post("/variable_result")
|
| 473 |
-
async def variable_result(request: Request):
|
| 474 |
-
data = await request.json()
|
| 475 |
-
request_id = data.get("request_id")
|
| 476 |
-
success = data.get("success")
|
| 477 |
-
error = data.get("error")
|
| 478 |
-
variable_id = data.get("variable_id")
|
| 479 |
-
|
| 480 |
-
print(f"[VARIABLE RESULT RECEIVED] request_id={request_id}, success={success}, error={error}, variable_id={variable_id}")
|
| 481 |
-
|
| 482 |
-
if request_id:
|
| 483 |
-
# Store the result for the create_variable function to retrieve
|
| 484 |
-
variable_results[request_id] = data
|
| 485 |
-
print(f"[VARIABLE RESULT STORED] Results dict now has {len(variable_results)} items")
|
| 486 |
-
|
| 487 |
-
return {"received": True}
|
| 488 |
-
|
| 489 |
-
# Endpoint to receive edit MCP results from frontend
|
| 490 |
-
@app.post("/edit_mcp_result")
|
| 491 |
-
async def edit_mcp_result(request: Request):
|
| 492 |
-
data = await request.json()
|
| 493 |
-
request_id = data.get("request_id")
|
| 494 |
-
success = data.get("success")
|
| 495 |
-
error = data.get("error")
|
| 496 |
-
|
| 497 |
-
if request_id:
|
| 498 |
-
# Store the result for the edit_mcp function to retrieve
|
| 499 |
-
edit_mcp_results[request_id] = data
|
| 500 |
|
| 501 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
|
| 503 |
-
#
|
| 504 |
-
@app.post("/
|
| 505 |
-
async def
|
| 506 |
data = await request.json()
|
| 507 |
-
|
| 508 |
-
success = data.get("success")
|
| 509 |
-
error = data.get("error")
|
| 510 |
|
| 511 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 512 |
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
replace_block_results[request_id] = data
|
| 516 |
|
| 517 |
return {"received": True}
|
| 518 |
|
|
|
|
| 27 |
# Global variable to store the workspace's variables
|
| 28 |
latest_blockly_vars = ""
|
| 29 |
|
| 30 |
+
# Unified queue for all block operation requests (Py -> JS)
|
| 31 |
+
requests_queue = queue.Queue()
|
|
|
|
| 32 |
|
| 33 |
+
# Unified queue for all request results from frontend (JS -> Py)
|
| 34 |
+
results_queue = queue.Queue()
|
|
|
|
| 35 |
|
| 36 |
+
# Helper function to wait for a result from the unified queue
|
| 37 |
+
def wait_for_result(request_id, request_type, timeout=8, id_field='request_id'):
|
| 38 |
+
"""
|
| 39 |
+
Wait for a result from the unified results_queue.
|
| 40 |
+
Matches results by request_id and request_type.
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
request_id: Identifier for the request (UUID or block_id)
|
| 44 |
+
request_type: Type of request ('delete', 'create', 'variable', 'edit_mcp', 'replace')
|
| 45 |
+
timeout: Maximum time to wait in seconds
|
| 46 |
+
id_field: Field name to match against (default 'request_id', use 'block_id' for delete)
|
| 47 |
+
|
| 48 |
+
Returns:
|
| 49 |
+
Result dict if found and successful, raises exception otherwise
|
| 50 |
+
"""
|
| 51 |
+
start_time = time.time()
|
| 52 |
+
check_interval = 0.05
|
| 53 |
+
results_buffer = [] # Buffer for results we read but don't match
|
| 54 |
+
|
| 55 |
+
while time.time() - start_time < timeout:
|
| 56 |
+
# Check if we have buffered results that match
|
| 57 |
+
for i, result in enumerate(results_buffer):
|
| 58 |
+
if (result.get(id_field) == request_id and
|
| 59 |
+
result.get('request_type') == request_type):
|
| 60 |
+
results_buffer.pop(i)
|
| 61 |
+
return result
|
| 62 |
+
|
| 63 |
+
# Try to get a new result from queue
|
| 64 |
+
try:
|
| 65 |
+
result = results_queue.get_nowait()
|
| 66 |
+
# Check if this is our result
|
| 67 |
+
if (result.get(id_field) == request_id and
|
| 68 |
+
result.get('request_type') == request_type):
|
| 69 |
+
# Put back any buffered results we collected
|
| 70 |
+
for buffered in results_buffer:
|
| 71 |
+
results_queue.put(buffered)
|
| 72 |
+
results_buffer = []
|
| 73 |
+
return result
|
| 74 |
+
else:
|
| 75 |
+
# Not our result, buffer it for other functions to find
|
| 76 |
+
results_buffer.append(result)
|
| 77 |
+
except queue.Empty:
|
| 78 |
+
pass
|
| 79 |
+
|
| 80 |
+
time.sleep(check_interval)
|
| 81 |
+
|
| 82 |
+
# Timeout - put back any buffered results
|
| 83 |
+
for buffered in results_buffer:
|
| 84 |
+
results_queue.put(buffered)
|
| 85 |
+
|
| 86 |
+
raise TimeoutError(f"No response received for {request_type} request {request_id} after {timeout} seconds")
|
| 87 |
|
| 88 |
# Global variable to store the deployed HF MCP server URL
|
| 89 |
current_mcp_server_url = None
|
|
|
|
| 147 |
try:
|
| 148 |
print(f"[DELETE REQUEST] Attempting to delete block: {block_id}")
|
| 149 |
|
| 150 |
+
# Add to unified requests queue
|
| 151 |
+
delete_data = {"type": "delete", "block_id": block_id}
|
| 152 |
+
requests_queue.put(delete_data)
|
|
|
|
|
|
|
|
|
|
| 153 |
print(f"[DELETE REQUEST] Added to queue: {block_id}")
|
| 154 |
|
| 155 |
+
# Wait for result from unified queue (delete uses 'block_id' as the identifier field)
|
| 156 |
+
try:
|
| 157 |
+
result = wait_for_result(block_id, "delete", timeout=8, id_field='block_id')
|
| 158 |
+
print(f"[DELETE RESULT] Received result for {block_id}: success={result.get('success')}, error={result.get('error')}")
|
| 159 |
+
if result["success"]:
|
| 160 |
+
return f"[TOOL] Successfully deleted block {block_id}"
|
| 161 |
+
else:
|
| 162 |
+
return f"[TOOL] Failed to delete block {block_id}: {result.get('error', 'Unknown error')}"
|
| 163 |
+
except TimeoutError as e:
|
| 164 |
+
print(f"[DELETE TIMEOUT] {e}")
|
| 165 |
+
return f"Timeout waiting for deletion confirmation for block {block_id}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
|
| 167 |
except Exception as e:
|
| 168 |
print(f"[DELETE ERROR] {e}")
|
|
|
|
| 173 |
def create_block(block_spec, blockID=None, placement_type=None, input_name=None):
|
| 174 |
try:
|
| 175 |
# Generate a unique request ID
|
|
|
|
| 176 |
request_id = str(uuid.uuid4())
|
| 177 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
# Add to creation queue with optional blockID, placement_type, and input_name
|
| 179 |
+
queue_data = {"type": "create", "request_id": request_id, "block_spec": block_spec}
|
| 180 |
if blockID:
|
| 181 |
queue_data["blockID"] = blockID
|
| 182 |
if placement_type:
|
| 183 |
queue_data["placement_type"] = placement_type
|
| 184 |
if input_name:
|
| 185 |
queue_data["input_name"] = input_name
|
| 186 |
+
requests_queue.put(queue_data)
|
| 187 |
|
| 188 |
+
# Wait for result from unified queue
|
| 189 |
+
try:
|
| 190 |
+
result = wait_for_result(request_id, "create", timeout=8)
|
| 191 |
+
if result["success"]:
|
| 192 |
+
return f"[TOOL] Successfully created block: {result.get('block_id', 'unknown')}"
|
| 193 |
+
else:
|
| 194 |
+
error_msg = result.get('error') or 'Unknown error'
|
| 195 |
+
return f"[TOOL] Failed to create block: {error_msg}"
|
| 196 |
+
except TimeoutError as e:
|
| 197 |
+
print(f"[CREATE TIMEOUT] {e}")
|
| 198 |
+
return f"Timeout waiting for block creation confirmation"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
except Exception as e:
|
| 201 |
print(f"[CREATE ERROR] {e}")
|
|
|
|
| 210 |
# Generate a unique request ID
|
| 211 |
request_id = str(uuid.uuid4())
|
| 212 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
# Add to variable creation queue
|
| 214 |
+
queue_data = {"type": "variable", "request_id": request_id, "variable_name": var_name}
|
| 215 |
+
requests_queue.put(queue_data)
|
| 216 |
print(f"[VARIABLE REQUEST] Added to queue with ID: {request_id}")
|
| 217 |
|
| 218 |
+
# Wait for result from unified queue
|
| 219 |
+
try:
|
| 220 |
+
result = wait_for_result(request_id, "variable", timeout=8)
|
| 221 |
+
print(f"[VARIABLE RESULT] Received result for {request_id}: success={result.get('success')}, error={result.get('error')}")
|
| 222 |
+
if result["success"]:
|
| 223 |
+
return f"[TOOL] Successfully created variable: {result.get('variable_id', var_name)}"
|
| 224 |
+
else:
|
| 225 |
+
return f"[TOOL] Failed to create variable: {result.get('error', 'Unknown error')}"
|
| 226 |
+
except TimeoutError as e:
|
| 227 |
+
print(f"[VARIABLE TIMEOUT] {e}")
|
| 228 |
+
return f"Timeout waiting for variable creation confirmation"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
|
| 230 |
except Exception as e:
|
| 231 |
print(f"[VARIABLE ERROR] {e}")
|
|
|
|
| 240 |
# Generate a unique request ID
|
| 241 |
request_id = str(uuid.uuid4())
|
| 242 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
# Build the edit data
|
| 244 |
+
edit_data = {"type": "edit_mcp", "request_id": request_id}
|
| 245 |
if inputs is not None:
|
| 246 |
edit_data["inputs"] = inputs
|
| 247 |
if outputs is not None:
|
| 248 |
edit_data["outputs"] = outputs
|
| 249 |
|
| 250 |
# Add to edit MCP queue
|
| 251 |
+
requests_queue.put(edit_data)
|
| 252 |
print(f"[EDIT MCP REQUEST] Added to queue with ID: {request_id}")
|
| 253 |
|
| 254 |
+
# Wait for result from unified queue
|
| 255 |
+
try:
|
| 256 |
+
result = wait_for_result(request_id, "edit_mcp", timeout=8)
|
| 257 |
+
print(f"[EDIT MCP RESULT] Received result for {request_id}: success={result.get('success')}, error={result.get('error')}")
|
| 258 |
+
if result["success"]:
|
| 259 |
+
return f"[TOOL] Successfully edited MCP block inputs/outputs"
|
| 260 |
+
else:
|
| 261 |
+
return f"[TOOL] Failed to edit MCP block: {result.get('error', 'Unknown error')}"
|
| 262 |
+
except TimeoutError as e:
|
| 263 |
+
print(f"[EDIT MCP TIMEOUT] {e}")
|
| 264 |
+
return f"Timeout waiting for MCP edit confirmation"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
|
| 266 |
except Exception as e:
|
| 267 |
print(f"[EDIT MCP ERROR] {e}")
|
|
|
|
| 276 |
# Generate a unique request ID
|
| 277 |
request_id = str(uuid.uuid4())
|
| 278 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
# Build the replace data
|
| 280 |
+
replace_data = {"type": "replace", "request_id": request_id, "block_id": block_id, "block_spec": command}
|
| 281 |
|
| 282 |
# Add to replace block queue
|
| 283 |
+
requests_queue.put(replace_data)
|
| 284 |
print(f"[REPLACE REQUEST] Added to queue with ID: {request_id}")
|
| 285 |
|
| 286 |
+
# Wait for result from unified queue
|
| 287 |
+
try:
|
| 288 |
+
result = wait_for_result(request_id, "replace", timeout=8)
|
| 289 |
+
print(f"[REPLACE RESULT] Received result for {request_id}: success={result.get('success')}, error={result.get('error')}")
|
| 290 |
+
if result["success"]:
|
| 291 |
+
return f"[TOOL] Successfully replaced block {block_id}"
|
| 292 |
+
else:
|
| 293 |
+
return f"[TOOL] Failed to replace block: {result.get('error', 'Unknown error')}"
|
| 294 |
+
except TimeoutError as e:
|
| 295 |
+
print(f"[REPLACE TIMEOUT] {e}")
|
| 296 |
+
return f"Timeout waiting for block replacement confirmation"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
|
| 298 |
except Exception as e:
|
| 299 |
print(f"[REPLACE ERROR] {e}")
|
|
|
|
| 316 |
|
| 317 |
while True:
|
| 318 |
try:
|
| 319 |
+
# Check unified requests queue (no elif - checks every iteration)
|
| 320 |
+
if not requests_queue.empty():
|
| 321 |
+
request = requests_queue.get_nowait()
|
| 322 |
+
request_type = request.get("type")
|
|
|
|
| 323 |
|
| 324 |
+
# Build request key for duplicate detection
|
| 325 |
+
if request_type == "delete":
|
| 326 |
+
request_key = f"delete_{request.get('block_id')}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 327 |
else:
|
| 328 |
+
request_key = f"{request_type}_{request.get('request_id')}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
|
| 330 |
# Avoid sending duplicate requests too quickly
|
| 331 |
if request_key not in sent_requests:
|
| 332 |
sent_requests.add(request_key)
|
| 333 |
+
yield f"data: {json.dumps(request)}\n\n"
|
|
|
|
| 334 |
|
| 335 |
# Clear from sent_requests after 10 seconds
|
| 336 |
asyncio.create_task(clear_sent_request(sent_requests, request_key, 10))
|
| 337 |
else:
|
| 338 |
+
print(f"[SSE SKIP] Skipping duplicate request: {request_key}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
|
| 340 |
else:
|
| 341 |
# Send a heartbeat every 30 seconds to keep connection alive
|
|
|
|
| 361 |
}
|
| 362 |
)
|
| 363 |
|
| 364 |
+
# Unified Server-Sent Events endpoint for all results from frontend
|
| 365 |
+
@app.get("/results_stream")
|
| 366 |
+
async def results_stream():
|
| 367 |
+
async def event_generator():
|
| 368 |
+
while True:
|
| 369 |
+
try:
|
| 370 |
+
# Check if there are any results to send
|
| 371 |
+
if not results_queue.empty():
|
| 372 |
+
result_data = results_queue.get_nowait()
|
| 373 |
+
yield f"data: {json.dumps(result_data)}\n\n"
|
| 374 |
+
else:
|
| 375 |
+
# Send a heartbeat every 30 seconds to keep connection alive
|
| 376 |
+
await asyncio.sleep(0.1)
|
| 377 |
+
except queue.Empty:
|
| 378 |
+
await asyncio.sleep(0.1)
|
| 379 |
+
except Exception as e:
|
| 380 |
+
print(f"[RESULTS SSE ERROR] {e}")
|
| 381 |
+
await asyncio.sleep(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 382 |
|
| 383 |
+
return StreamingResponse(
|
| 384 |
+
event_generator(),
|
| 385 |
+
media_type="text/event-stream",
|
| 386 |
+
headers={
|
| 387 |
+
"Cache-Control": "no-cache",
|
| 388 |
+
"Connection": "keep-alive",
|
| 389 |
+
"X-Accel-Buffering": "no",
|
| 390 |
+
}
|
| 391 |
+
)
|
| 392 |
|
| 393 |
+
# Unified endpoint to receive all results from frontend
|
| 394 |
+
@app.post("/request_result")
|
| 395 |
+
async def request_result(request: Request):
|
| 396 |
data = await request.json()
|
| 397 |
+
request_type = data.get("request_type")
|
|
|
|
|
|
|
| 398 |
|
| 399 |
+
# Log based on type
|
| 400 |
+
if request_type == "delete":
|
| 401 |
+
block_id = data.get("block_id")
|
| 402 |
+
success = data.get("success")
|
| 403 |
+
error = data.get("error")
|
| 404 |
+
print(f"[RESULT RECEIVED] type={request_type}, block_id={block_id}, success={success}, error={error}")
|
| 405 |
+
elif request_type == "variable":
|
| 406 |
+
request_id = data.get("request_id")
|
| 407 |
+
variable_id = data.get("variable_id")
|
| 408 |
+
success = data.get("success")
|
| 409 |
+
error = data.get("error")
|
| 410 |
+
print(f"[RESULT RECEIVED] type={request_type}, request_id={request_id}, success={success}, error={error}, variable_id={variable_id}")
|
| 411 |
+
elif request_type in ("create", "replace", "edit_mcp"):
|
| 412 |
+
request_id = data.get("request_id")
|
| 413 |
+
success = data.get("success")
|
| 414 |
+
error = data.get("error")
|
| 415 |
+
print(f"[RESULT RECEIVED] type={request_type}, request_id={request_id}, success={success}, error={error}")
|
| 416 |
|
| 417 |
+
# Put directly in unified results queue
|
| 418 |
+
results_queue.put(data)
|
|
|
|
| 419 |
|
| 420 |
return {"received": True}
|
| 421 |
|
project/src/index.js
CHANGED
|
@@ -880,10 +880,11 @@ const setupUnifiedStream = () => {
|
|
| 880 |
|
| 881 |
// Send result back to backend immediately
|
| 882 |
console.log('[SSE] Sending edit MCP result:', { request_id: data.request_id, success, error });
|
| 883 |
-
fetch('/
|
| 884 |
method: 'POST',
|
| 885 |
headers: { 'Content-Type': 'application/json' },
|
| 886 |
body: JSON.stringify({
|
|
|
|
| 887 |
request_id: data.request_id,
|
| 888 |
success: success,
|
| 889 |
error: error
|
|
@@ -973,10 +974,11 @@ const setupUnifiedStream = () => {
|
|
| 973 |
|
| 974 |
// Send result back to backend
|
| 975 |
console.log('[SSE] Sending replace block result:', { request_id: data.request_id, success, error, block_id: blockId });
|
| 976 |
-
fetch('/
|
| 977 |
method: 'POST',
|
| 978 |
headers: { 'Content-Type': 'application/json' },
|
| 979 |
body: JSON.stringify({
|
|
|
|
| 980 |
request_id: data.request_id,
|
| 981 |
success: success,
|
| 982 |
error: error,
|
|
@@ -1020,10 +1022,11 @@ const setupUnifiedStream = () => {
|
|
| 1020 |
|
| 1021 |
// Send result back to backend immediately
|
| 1022 |
console.log('[SSE] Sending deletion result:', { block_id: data.block_id, success, error });
|
| 1023 |
-
fetch('/
|
| 1024 |
method: 'POST',
|
| 1025 |
headers: { 'Content-Type': 'application/json' },
|
| 1026 |
body: JSON.stringify({
|
|
|
|
| 1027 |
block_id: data.block_id,
|
| 1028 |
success: success,
|
| 1029 |
error: error
|
|
@@ -1212,10 +1215,11 @@ const setupUnifiedStream = () => {
|
|
| 1212 |
block_id: blockId
|
| 1213 |
});
|
| 1214 |
|
| 1215 |
-
fetch('/
|
| 1216 |
method: 'POST',
|
| 1217 |
headers: { 'Content-Type': 'application/json' },
|
| 1218 |
body: JSON.stringify({
|
|
|
|
| 1219 |
request_id: data.request_id,
|
| 1220 |
success: success,
|
| 1221 |
error: error,
|
|
@@ -1263,10 +1267,11 @@ const setupUnifiedStream = () => {
|
|
| 1263 |
variable_id: variableId
|
| 1264 |
});
|
| 1265 |
|
| 1266 |
-
fetch('/
|
| 1267 |
method: 'POST',
|
| 1268 |
headers: { 'Content-Type': 'application/json' },
|
| 1269 |
body: JSON.stringify({
|
|
|
|
| 1270 |
request_id: data.request_id,
|
| 1271 |
success: success,
|
| 1272 |
error: error,
|
|
|
|
| 880 |
|
| 881 |
// Send result back to backend immediately
|
| 882 |
console.log('[SSE] Sending edit MCP result:', { request_id: data.request_id, success, error });
|
| 883 |
+
fetch('/request_result', {
|
| 884 |
method: 'POST',
|
| 885 |
headers: { 'Content-Type': 'application/json' },
|
| 886 |
body: JSON.stringify({
|
| 887 |
+
request_type: 'edit_mcp',
|
| 888 |
request_id: data.request_id,
|
| 889 |
success: success,
|
| 890 |
error: error
|
|
|
|
| 974 |
|
| 975 |
// Send result back to backend
|
| 976 |
console.log('[SSE] Sending replace block result:', { request_id: data.request_id, success, error, block_id: blockId });
|
| 977 |
+
fetch('/request_result', {
|
| 978 |
method: 'POST',
|
| 979 |
headers: { 'Content-Type': 'application/json' },
|
| 980 |
body: JSON.stringify({
|
| 981 |
+
request_type: 'replace',
|
| 982 |
request_id: data.request_id,
|
| 983 |
success: success,
|
| 984 |
error: error,
|
|
|
|
| 1022 |
|
| 1023 |
// Send result back to backend immediately
|
| 1024 |
console.log('[SSE] Sending deletion result:', { block_id: data.block_id, success, error });
|
| 1025 |
+
fetch('/request_result', {
|
| 1026 |
method: 'POST',
|
| 1027 |
headers: { 'Content-Type': 'application/json' },
|
| 1028 |
body: JSON.stringify({
|
| 1029 |
+
request_type: 'delete',
|
| 1030 |
block_id: data.block_id,
|
| 1031 |
success: success,
|
| 1032 |
error: error
|
|
|
|
| 1215 |
block_id: blockId
|
| 1216 |
});
|
| 1217 |
|
| 1218 |
+
fetch('/request_result', {
|
| 1219 |
method: 'POST',
|
| 1220 |
headers: { 'Content-Type': 'application/json' },
|
| 1221 |
body: JSON.stringify({
|
| 1222 |
+
request_type: 'create',
|
| 1223 |
request_id: data.request_id,
|
| 1224 |
success: success,
|
| 1225 |
error: error,
|
|
|
|
| 1267 |
variable_id: variableId
|
| 1268 |
});
|
| 1269 |
|
| 1270 |
+
fetch('/request_result', {
|
| 1271 |
method: 'POST',
|
| 1272 |
headers: { 'Content-Type': 'application/json' },
|
| 1273 |
body: JSON.stringify({
|
| 1274 |
+
request_type: 'variable',
|
| 1275 |
request_id: data.request_id,
|
| 1276 |
success: success,
|
| 1277 |
error: error,
|
project/unified_server.py
CHANGED
|
@@ -54,6 +54,10 @@ async def variable_result_route(request: Request):
|
|
| 54 |
async def edit_mcp_result_route(request: Request):
|
| 55 |
return await chat.edit_mcp_result(request)
|
| 56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
# === test.py API endpoints ===
|
| 59 |
@app.post("/update_code")
|
|
|
|
| 54 |
async def edit_mcp_result_route(request: Request):
|
| 55 |
return await chat.edit_mcp_result(request)
|
| 56 |
|
| 57 |
+
@app.post("/request_result")
|
| 58 |
+
async def request_result_route(request: Request):
|
| 59 |
+
return await chat.request_result(request)
|
| 60 |
+
|
| 61 |
|
| 62 |
# === test.py API endpoints ===
|
| 63 |
@app.post("/update_code")
|