owenkaplinsky commited on
Commit
ea991cb
·
1 Parent(s): 0daf9fe

Merge results into one endpoint

Browse files
Files changed (3) hide show
  1. project/chat.py +181 -278
  2. project/src/index.js +10 -5
  3. 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
- # Queue for deletion requests and results storage
31
- deletion_queue = queue.Queue()
32
- deletion_results = {}
33
 
34
- # Queue for creation requests and results storage
35
- creation_queue = queue.Queue()
36
- creation_results = {}
37
 
38
- # Queue for variable creation requests and results storage
39
- variable_queue = queue.Queue()
40
- variable_results = {}
41
-
42
- # Queue for edit MCP requests and results storage
43
- edit_mcp_queue = queue.Queue()
44
- edit_mcp_results = {}
45
-
46
- # Queue for replace block requests and results storage
47
- replace_block_queue = queue.Queue()
48
- replace_block_results = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- # Clear any old results for this block ID first
113
- if block_id in deletion_results:
114
- deletion_results.pop(block_id)
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 with timeout
121
- import time
122
- timeout = 8 # Increased timeout to 8 seconds
123
- start_time = time.time()
124
- check_interval = 0.05 # Check more frequently
125
-
126
- while time.time() - start_time < timeout:
127
- if block_id in deletion_results:
128
- result = deletion_results.pop(block_id)
129
- print(f"[DELETE RESULT] Received result for {block_id}: success={result.get('success')}, error={result.get('error')}")
130
- if result["success"]:
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
- creation_queue.put(queue_data)
164
 
165
- # Wait for result with timeout
166
- import time
167
- timeout = 8 # 8 seconds timeout
168
- start_time = time.time()
169
- check_interval = 0.05 # Check more frequently
170
-
171
- while time.time() - start_time < timeout:
172
- if request_id in creation_results:
173
- result = creation_results.pop(request_id)
174
- if result["success"]:
175
- return f"[TOOL] Successfully created block: {result.get('block_id', 'unknown')}"
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
- variable_queue.put(queue_data)
204
  print(f"[VARIABLE REQUEST] Added to queue with ID: {request_id}")
205
 
206
- # Wait for result with timeout
207
- timeout = 8 # 8 seconds timeout
208
- start_time = time.time()
209
- check_interval = 0.05 # Check more frequently
210
-
211
- while time.time() - start_time < timeout:
212
- if request_id in variable_results:
213
- result = variable_results.pop(request_id)
214
- print(f"[VARIABLE RESULT] Received result for {request_id}: success={result.get('success')}, error={result.get('error')}")
215
- if result["success"]:
216
- return f"[TOOL] Successfully created variable: {result.get('variable_id', var_name)}"
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
- edit_mcp_queue.put(edit_data)
250
  print(f"[EDIT MCP REQUEST] Added to queue with ID: {request_id}")
251
 
252
- # Wait for result with timeout
253
- timeout = 8 # 8 seconds timeout
254
- start_time = time.time()
255
- check_interval = 0.05 # Check more frequently
256
-
257
- while time.time() - start_time < timeout:
258
- if request_id in edit_mcp_results:
259
- result = edit_mcp_results.pop(request_id)
260
- print(f"[EDIT MCP RESULT] Received result for {request_id}: success={result.get('success')}, error={result.get('error')}")
261
- if result["success"]:
262
- return f"[TOOL] Successfully edited MCP block inputs/outputs"
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
- replace_block_queue.put(replace_data)
292
  print(f"[REPLACE REQUEST] Added to queue with ID: {request_id}")
293
 
294
- # Wait for result with timeout
295
- timeout = 8 # 8 seconds timeout
296
- start_time = time.time()
297
- check_interval = 0.05 # Check more frequently
298
-
299
- while time.time() - start_time < timeout:
300
- if request_id in replace_block_results:
301
- result = replace_block_results.pop(request_id)
302
- print(f"[REPLACE RESULT] Received result for {request_id}: success={result.get('success')}, error={result.get('error')}")
303
- if result["success"]:
304
- return f"[TOOL] Successfully replaced block {block_id}"
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 deletion queue
334
- if not deletion_queue.empty():
335
- deletion_request = deletion_queue.get_nowait()
336
- block_id = deletion_request.get("block_id")
337
- request_key = f"delete_{block_id}"
338
 
339
- # Avoid sending duplicate requests too quickly
340
- if request_key not in sent_requests:
341
- sent_requests.add(request_key)
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
- print(f"[SSE SKIP] Skipping duplicate request for block: {block_id}")
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
- creation_request["type"] = "create" # Add type identifier
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 for ID: {request_id}")
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
- # Endpoint to receive creation results from frontend
443
- @app.post("/creation_result")
444
- async def creation_result(request: Request):
445
- data = await request.json()
446
- request_id = data.get("request_id")
447
-
448
- if request_id:
449
- # Store the result for the create_block function to retrieve
450
- creation_results[request_id] = data
451
-
452
- return {"received": True}
453
-
454
- # Endpoint to receive deletion results from frontend
455
- @app.post("/deletion_result")
456
- async def deletion_result(request: Request):
457
- data = await request.json()
458
- block_id = data.get("block_id")
459
- success = data.get("success")
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 {"received": True}
 
 
 
 
 
 
 
 
502
 
503
- # Endpoint to receive replace block results from frontend
504
- @app.post("/replace_block_result")
505
- async def replace_block_result(request: Request):
506
  data = await request.json()
507
- request_id = data.get("request_id")
508
- success = data.get("success")
509
- error = data.get("error")
510
 
511
- print(f"[REPLACE RESULT RECEIVED] request_id={request_id}, success={success}, error={error}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
 
513
- if request_id:
514
- # Store the result for the replace_block function to retrieve
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('/edit_mcp_result', {
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('/replace_block_result', {
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('/deletion_result', {
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('/creation_result', {
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('/variable_result', {
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")