owenkaplinsky commited on
Commit
5f90a0c
·
1 Parent(s): 596096e

Add variable creation

Browse files
Files changed (2) hide show
  1. project/chat.py +150 -3
  2. project/src/index.js +94 -0
project/chat.py CHANGED
@@ -10,6 +10,8 @@ import gradio as gr
10
  import asyncio
11
  import queue
12
  import json
 
 
13
  from colorama import Fore, Style
14
 
15
  # Initialize OpenAI client (will be updated when API key is set)
@@ -29,6 +31,10 @@ deletion_results = {}
29
  creation_queue = queue.Queue()
30
  creation_results = {}
31
 
 
 
 
 
32
  blocks_context = ""
33
  try:
34
  file_path = os.path.join(os.path.dirname(__file__), "blocks.txt")
@@ -275,6 +281,47 @@ def create_block(block_spec, under_block_id=None):
275
  traceback.print_exc()
276
  return f"Error creating block: {str(e)}"
277
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  # Server-Sent Events endpoint for creation requests
279
  @app.get("/create_stream")
280
  async def create_stream():
@@ -426,6 +473,83 @@ async def deletion_result(request: Request):
426
 
427
  return {"received": True}
428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
  def create_gradio_interface():
430
  # Hardcoded system prompt
431
  SYSTEM_PROMPT = f"""You are an AI assistant that helps users build **MCP servers** using Blockly blocks.
@@ -564,6 +688,23 @@ in one call.
564
  },
565
  }
566
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  {
568
  "type": "function",
569
  "function": {
@@ -665,7 +806,7 @@ in one call.
665
 
666
  if function_name == "delete_block":
667
  block_id = function_args.get("id", "")
668
- print(Fore.YELLOW + f"Agent ran delete with ID `{block_id}`." + Style.RESET_ALL)
669
  tool_result = delete_block(block_id)
670
  result_label = "Delete Operation"
671
 
@@ -673,11 +814,17 @@ in one call.
673
  command = function_args.get("command", "")
674
  under_block_id = function_args.get("under", None)
675
  if under_block_id == None:
676
- print(Fore.YELLOW + f"Agent ran create with command `{command}`." + Style.RESET_ALL)
677
  else:
678
- print(Fore.YELLOW + f"Agent ran create with command: `{command}`, under block ID: `{under_block_id}`." + Style.RESET_ALL)
679
  tool_result = create_block(command, under_block_id)
680
  result_label = "Create Operation"
 
 
 
 
 
 
681
 
682
  elif function_name == "run_mcp":
683
  # Build the MCP call string from the arguments
 
10
  import asyncio
11
  import queue
12
  import json
13
+ import uuid
14
+ import time
15
  from colorama import Fore, Style
16
 
17
  # Initialize OpenAI client (will be updated when API key is set)
 
31
  creation_queue = queue.Queue()
32
  creation_results = {}
33
 
34
+ # Queue for variable creation requests and results storage
35
+ variable_queue = queue.Queue()
36
+ variable_results = {}
37
+
38
  blocks_context = ""
39
  try:
40
  file_path = os.path.join(os.path.dirname(__file__), "blocks.txt")
 
281
  traceback.print_exc()
282
  return f"Error creating block: {str(e)}"
283
 
284
+ def create_variable(var_name):
285
+ """Create a variable in the Blockly workspace"""
286
+ try:
287
+ print(f"[VARIABLE REQUEST] Attempting to create variable: {var_name}")
288
+
289
+ # Generate a unique request ID
290
+ request_id = str(uuid.uuid4())
291
+
292
+ # Clear any old results for this request ID first
293
+ if request_id in variable_results:
294
+ variable_results.pop(request_id)
295
+
296
+ # Add to variable creation queue
297
+ queue_data = {"request_id": request_id, "variable_name": var_name}
298
+ variable_queue.put(queue_data)
299
+ print(f"[VARIABLE REQUEST] Added to queue with ID: {request_id}")
300
+
301
+ # Wait for result with timeout
302
+ timeout = 8 # 8 seconds timeout
303
+ start_time = time.time()
304
+ check_interval = 0.05 # Check more frequently
305
+
306
+ while time.time() - start_time < timeout:
307
+ if request_id in variable_results:
308
+ result = variable_results.pop(request_id)
309
+ print(f"[VARIABLE RESULT] Received result for {request_id}: success={result.get('success')}, error={result.get('error')}")
310
+ if result["success"]:
311
+ return f"[TOOL] Successfully created variable: {result.get('variable_id', var_name)}"
312
+ else:
313
+ return f"[TOOL] Failed to create variable: {result.get('error', 'Unknown error')}"
314
+ time.sleep(check_interval)
315
+
316
+ print(f"[VARIABLE TIMEOUT] No response received for request {request_id} after {timeout} seconds")
317
+ return f"Timeout waiting for variable creation confirmation"
318
+
319
+ except Exception as e:
320
+ print(f"[VARIABLE ERROR] {e}")
321
+ import traceback
322
+ traceback.print_exc()
323
+ return f"Error creating variable: {str(e)}"
324
+
325
  # Server-Sent Events endpoint for creation requests
326
  @app.get("/create_stream")
327
  async def create_stream():
 
473
 
474
  return {"received": True}
475
 
476
+ # Server-Sent Events endpoint for variable creation requests
477
+ @app.get("/variable_stream")
478
+ async def variable_stream():
479
+ """Stream variable creation requests to the frontend using Server-Sent Events"""
480
+
481
+ async def clear_sent_request(sent_requests, request_id, delay):
482
+ """Clear request_id from sent_requests after delay seconds"""
483
+ await asyncio.sleep(delay)
484
+ if request_id in sent_requests:
485
+ sent_requests.discard(request_id)
486
+
487
+ async def event_generator():
488
+ sent_requests = set() # Track sent requests to avoid duplicates
489
+ heartbeat_counter = 0
490
+
491
+ while True:
492
+ try:
493
+ # Check for variable creation requests (non-blocking)
494
+ if not variable_queue.empty():
495
+ var_request = variable_queue.get_nowait()
496
+ request_id = var_request.get("request_id")
497
+
498
+ # Avoid sending duplicate requests too quickly
499
+ if request_id not in sent_requests:
500
+ sent_requests.add(request_id)
501
+ print(f"[SSE VARIABLE SEND] Sending variable creation request with ID: {request_id}")
502
+ yield f"data: {json.dumps(var_request)}\n\n"
503
+
504
+ # Clear from sent_requests after 10 seconds
505
+ asyncio.create_task(clear_sent_request(sent_requests, request_id, 10))
506
+ else:
507
+ print(f"[SSE VARIABLE SKIP] Skipping duplicate request for ID: {request_id}")
508
+
509
+ await asyncio.sleep(0.1) # Small delay between messages
510
+ else:
511
+ # Send a heartbeat every 30 seconds to keep connection alive
512
+ heartbeat_counter += 1
513
+ if heartbeat_counter >= 300: # 300 * 0.1 = 30 seconds
514
+ yield f"data: {json.dumps({'heartbeat': True})}\n\n"
515
+ heartbeat_counter = 0
516
+ await asyncio.sleep(0.1)
517
+
518
+ except queue.Empty:
519
+ await asyncio.sleep(0.1)
520
+ except Exception as e:
521
+ print(f"[SSE VARIABLE ERROR] {e}")
522
+ await asyncio.sleep(1)
523
+
524
+ return StreamingResponse(
525
+ event_generator(),
526
+ media_type="text/event-stream",
527
+ headers={
528
+ "Cache-Control": "no-cache",
529
+ "Connection": "keep-alive",
530
+ "X-Accel-Buffering": "no",
531
+ }
532
+ )
533
+
534
+ # Endpoint to receive variable creation results from frontend
535
+ @app.post("/variable_result")
536
+ async def variable_result(request: Request):
537
+ """Receive variable creation results from the frontend"""
538
+ data = await request.json()
539
+ request_id = data.get("request_id")
540
+ success = data.get("success")
541
+ error = data.get("error")
542
+ variable_id = data.get("variable_id")
543
+
544
+ print(f"[VARIABLE RESULT RECEIVED] request_id={request_id}, success={success}, error={error}, variable_id={variable_id}")
545
+
546
+ if request_id:
547
+ # Store the result for the create_variable function to retrieve
548
+ variable_results[request_id] = data
549
+ print(f"[VARIABLE RESULT STORED] Results dict now has {len(variable_results)} items")
550
+
551
+ return {"received": True}
552
+
553
  def create_gradio_interface():
554
  # Hardcoded system prompt
555
  SYSTEM_PROMPT = f"""You are an AI assistant that helps users build **MCP servers** using Blockly blocks.
 
688
  },
689
  }
690
  },
691
+ {
692
+ "type": "function",
693
+ "function": {
694
+ "name": "create_variable",
695
+ "description": "Creates a variable.",
696
+ "parameters": {
697
+ "type": "object",
698
+ "properties": {
699
+ "name": {
700
+ "type": "string",
701
+ "description": "The name of the variable you want to create.",
702
+ },
703
+ },
704
+ "required": ["name"],
705
+ },
706
+ }
707
+ },
708
  {
709
  "type": "function",
710
  "function": {
 
806
 
807
  if function_name == "delete_block":
808
  block_id = function_args.get("id", "")
809
+ print(Fore.YELLOW + f"Agent deleted block with ID `{block_id}`." + Style.RESET_ALL)
810
  tool_result = delete_block(block_id)
811
  result_label = "Delete Operation"
812
 
 
814
  command = function_args.get("command", "")
815
  under_block_id = function_args.get("under", None)
816
  if under_block_id == None:
817
+ print(Fore.YELLOW + f"Agent created block with command `{command}`." + Style.RESET_ALL)
818
  else:
819
+ print(Fore.YELLOW + f"Agent created block with command: `{command}`, under block ID: `{under_block_id}`." + Style.RESET_ALL)
820
  tool_result = create_block(command, under_block_id)
821
  result_label = "Create Operation"
822
+
823
+ elif function_name == "create_variable":
824
+ name = function_args.get("name", "")
825
+ print(Fore.YELLOW + f"Agent created variable with name `{name}`." + Style.RESET_ALL)
826
+ tool_result = create_variable(name)
827
+ result_label = "Create Var Operation"
828
 
829
  elif function_name == "run_mcp":
830
  # Build the MCP call string from the arguments
project/src/index.js CHANGED
@@ -749,6 +749,100 @@ const setupCreationStream = () => {
749
  // Start the creation SSE connection
750
  setupCreationStream();
751
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
752
  // Observe any size change to the blockly container
753
  const observer = new ResizeObserver(() => {
754
  Blockly.svgResize(ws);
 
749
  // Start the creation SSE connection
750
  setupCreationStream();
751
 
752
+ const setupVariableStream = () => {
753
+ const eventSource = new EventSource('http://127.0.0.1:7861/variable_stream');
754
+ const processedRequests = new Set(); // Track processed variable requests
755
+
756
+ eventSource.onmessage = (event) => {
757
+ try {
758
+ const data = JSON.parse(event.data);
759
+
760
+ // Skip heartbeat messages
761
+ if (data.heartbeat) return;
762
+
763
+ // Skip if we've already processed this request
764
+ if (data.request_id && processedRequests.has(data.request_id)) {
765
+ console.log('[SSE VARIABLE] Skipping duplicate variable request:', data.request_id);
766
+ return;
767
+ }
768
+ if (data.request_id) {
769
+ processedRequests.add(data.request_id);
770
+ // Clear after 10 seconds to allow retries if needed
771
+ setTimeout(() => processedRequests.delete(data.request_id), 10000);
772
+ }
773
+
774
+ if (data.variable_name && data.request_id) {
775
+ console.log('[SSE VARIABLE] Received variable creation request:', data.request_id, data.variable_name);
776
+
777
+ let success = false;
778
+ let error = null;
779
+ let variableId = null;
780
+
781
+ try {
782
+ // Create the variable using Blockly's variable map
783
+ const variableName = data.variable_name;
784
+
785
+ // Use the workspace's variable map to create a new variable
786
+ const variableModel = ws.getVariableMap().createVariable(variableName);
787
+
788
+ if (variableModel) {
789
+ variableId = variableModel.getId();
790
+ success = true;
791
+ console.log('[SSE VARIABLE] Successfully created variable:', variableName, 'with ID:', variableId);
792
+ } else {
793
+ throw new Error('Failed to create variable model');
794
+ }
795
+
796
+ } catch (e) {
797
+ error = e.toString();
798
+ console.error('[SSE VARIABLE] Error creating variable:', e);
799
+ }
800
+
801
+ // Send result back to backend immediately
802
+ console.log('[SSE VARIABLE] Sending variable creation result:', {
803
+ request_id: data.request_id,
804
+ success,
805
+ error,
806
+ variable_id: variableId
807
+ });
808
+
809
+ fetch('http://127.0.0.1:7861/variable_result', {
810
+ method: 'POST',
811
+ headers: { 'Content-Type': 'application/json' },
812
+ body: JSON.stringify({
813
+ request_id: data.request_id,
814
+ success: success,
815
+ error: error,
816
+ variable_id: variableId
817
+ })
818
+ }).then(response => {
819
+ console.log('[SSE VARIABLE] Variable creation result sent successfully');
820
+ }).catch(err => {
821
+ console.error('[SSE VARIABLE] Error sending variable creation result:', err);
822
+ });
823
+ }
824
+ } catch (err) {
825
+ console.error('[SSE VARIABLE] Error processing message:', err);
826
+ }
827
+ };
828
+
829
+ eventSource.onerror = (error) => {
830
+ console.error('[SSE VARIABLE] Connection error:', error);
831
+ // Reconnect after 5 seconds
832
+ setTimeout(() => {
833
+ console.log('[SSE VARIABLE] Attempting to reconnect...');
834
+ setupVariableStream();
835
+ }, 5000);
836
+ };
837
+
838
+ eventSource.onopen = () => {
839
+ console.log('[SSE VARIABLE] Connected to variable stream');
840
+ };
841
+ };
842
+
843
+ // Start the variable SSE connection
844
+ setupVariableStream();
845
+
846
  // Observe any size change to the blockly container
847
  const observer = new ResizeObserver(() => {
848
  Blockly.svgResize(ws);