Miquel Farré commited on
Commit
5504eb2
·
1 Parent(s): a8daf8a

changing session_hash with browser generated uuid

Browse files
Files changed (1) hide show
  1. app.py +53 -57
app.py CHANGED
@@ -3,6 +3,7 @@ import os
3
  import json
4
  import shutil
5
  import traceback
 
6
  from textwrap import dedent
7
  import time
8
  from threading import Timer
@@ -368,72 +369,57 @@ def cleanup_sandboxes():
368
  except Exception as e:
369
  print(f"Error cleaning up sandbox {session_id}: {str(e)}")
370
 
371
-
372
- def get_or_create_sandbox(session_hash):
373
  current_time = time.time()
374
 
375
- # Check if sandbox exists and is still valid
376
- if (session_hash in SANDBOXES and
377
- session_hash in SANDBOX_METADATA and
378
- current_time - SANDBOX_METADATA[session_hash]['created_at'] < SANDBOX_TIMEOUT):
379
-
380
- # Update last accessed time
381
- SANDBOX_METADATA[session_hash]['last_accessed'] = current_time
382
- return SANDBOXES[session_hash]
383
-
384
- # Close existing sandbox if it exists but is too old
385
- if session_hash in SANDBOXES:
386
  try:
387
- print(f"Closing expired sandbox for session {session_hash}")
388
- SANDBOXES[session_hash].kill()
389
  except Exception as e:
390
  print(f"Error closing expired sandbox: {str(e)}")
391
-
392
- # Create new sandbox
393
- print(f"Creating new sandbox for session {session_hash}")
394
  desktop = Sandbox(api_key=E2B_API_KEY, resolution=(WIDTH, HEIGHT), dpi=96, timeout=SANDBOX_TIMEOUT)
395
  desktop.stream.start(require_auth=True)
396
  setup_cmd = """sudo mkdir -p /usr/lib/firefox-esr/distribution && echo '{"policies":{"OverrideFirstRunPage":"","OverridePostUpdatePage":"","DisableProfileImport":true,"DontCheckDefaultBrowser":true}}' | sudo tee /usr/lib/firefox-esr/distribution/policies.json > /dev/null"""
397
  desktop.commands.run(setup_cmd)
398
-
399
- # Store sandbox with metadata
400
- SANDBOXES[session_hash] = desktop
401
- SANDBOX_METADATA[session_hash] = {
402
  'created_at': current_time,
403
  'last_accessed': current_time
404
  }
405
  return desktop
406
 
407
- def update_html(interactive_mode: bool, request: gr.Request):
408
- session_hash = request.session_hash
409
- desktop = get_or_create_sandbox(session_hash)
410
  auth_key = desktop.stream.get_auth_key()
411
-
412
- # Add view_only parameter based on interactive_mode
413
  base_url = desktop.stream.get_url(auth_key=auth_key)
414
  stream_url = base_url if interactive_mode else f"{base_url}&view_only=true"
415
 
416
- # Set status indicator class and text
417
  status_class = "status-interactive" if interactive_mode else "status-view-only"
418
  status_text = "Interactive" if interactive_mode else "Agent running..."
419
-
420
- creation_time = SANDBOX_METADATA[session_hash]['created_at'] if session_hash in SANDBOX_METADATA else time.time()
421
 
422
  sandbox_html_content = sandbox_html_template.format(
423
  stream_url=stream_url,
424
  status_class=status_class,
425
  status_text=status_text,
426
  )
427
-
428
- # Add hidden field with creation time for JavaScript to use
429
  sandbox_html_content += f'<div id="sandbox-creation-time" style="display:none;" data-time="{creation_time}" data-timeout="{SANDBOX_TIMEOUT}"></div>'
430
-
431
  return sandbox_html_content
432
 
433
- def generate_interaction_id(request):
434
- """Generate a unique ID combining session hash and timestamp"""
435
- return f"{request.session_hash}_{int(time.time())}"
436
 
 
 
437
 
438
  def chat_message_to_json(obj):
439
  """Custom JSON serializer for ChatMessage and related objects"""
@@ -466,10 +452,18 @@ def save_final_status(folder, status: str, summary, error_message = None) -> Non
466
  output_file.write(json.dumps({"status":status, "summary":summary, "error_message": error_message}, default=chat_message_to_json))
467
  output_file.close()
468
 
469
- def initialize_session(interactive_mode, request: gr.Request):
470
- session_hash = request.session_hash
471
- # Return HTML and session hash
472
- return update_html(interactive_mode, request), session_hash
 
 
 
 
 
 
 
 
473
 
474
 
475
  def create_agent(data_dir, desktop):
@@ -501,22 +495,19 @@ class EnrichedGradioUI(GradioUI):
501
  gr.Button(interactive=False),
502
  )
503
 
504
- def interact_with_agent(self, task_input, stored_messages, session_state, session_hash, consent_storage, request: gr.Request):
505
- import gradio as gr
 
506
 
507
- interaction_id = generate_interaction_id(request)
508
- desktop = get_or_create_sandbox(session_hash)
509
-
510
- # Create data directory for this session
511
  data_dir = os.path.join(TMP_DIR, interaction_id)
512
  if not os.path.exists(data_dir):
513
  os.makedirs(data_dir)
514
 
515
  if "agent" in session_state:
516
- session_state["agent"].data_dir = data_dir # Update data dir to new interaction
517
  else:
518
  session_state["agent"] = create_agent(data_dir=data_dir, desktop=desktop)
519
-
520
  try:
521
  stored_messages.append(gr.ChatMessage(role="user", content=task_input))
522
  yield stored_messages
@@ -553,7 +544,8 @@ theme = gr.themes.Default(font=["Oxanium", "sans-serif"], primary_hue="amber", s
553
  # Create a Gradio app with Blocks
554
  with gr.Blocks(theme=theme, css=custom_css, js=custom_js) as demo:
555
  #Storing session hash in a state variable
556
- session_hash_state = gr.State(None)
 
557
 
558
 
559
  with gr.Row():
@@ -690,12 +682,11 @@ _Please note that we store the task logs by default so **do not write any person
690
  return f"Guru meditation: {str(e)}"
691
 
692
  # Function to set view-only mode
693
- def clear_and_set_view_only(task_input, request: gr.Request):
694
- # set view-only mode
695
- return update_html(False, request)
696
 
697
- def set_interactive(request: gr.Request):
698
- return update_html(True, request)
699
 
700
  def reactivate_stop_btn():
701
  return gr.Button("Stop the agent!", variant="huggingface")
@@ -705,14 +696,15 @@ _Please note that we store the task logs by default so **do not write any person
705
  # Chain the events
706
  run_event = run_btn.click(
707
  fn=clear_and_set_view_only,
708
- inputs=[task_input],
709
  outputs=[sandbox_html]
710
  ).then(
711
  agent_ui.interact_with_agent,
712
- inputs=[task_input, stored_messages, session_state, session_hash_state, consent_storage],
713
  outputs=[chatbot_display]
714
  ).then(
715
  fn=set_interactive,
 
716
  outputs=[sandbox_html]
717
  ).then(
718
  fn=reactivate_stop_btn,
@@ -749,9 +741,13 @@ _Please note that we store the task logs by default so **do not write any person
749
  # )
750
 
751
  demo.load(
 
 
 
752
  fn=initialize_session,
 
753
  inputs=[is_interactive],
754
- outputs=[sandbox_html, session_hash_state],
755
  )
756
 
757
  # Launch the app
 
3
  import json
4
  import shutil
5
  import traceback
6
+ import uuid
7
  from textwrap import dedent
8
  import time
9
  from threading import Timer
 
369
  except Exception as e:
370
  print(f"Error cleaning up sandbox {session_id}: {str(e)}")
371
 
372
+ def get_or_create_sandbox(session_uuid):
 
373
  current_time = time.time()
374
 
375
+ if (session_uuid in SANDBOXES and
376
+ session_uuid in SANDBOX_METADATA and
377
+ current_time - SANDBOX_METADATA[session_uuid]['created_at'] < SANDBOX_TIMEOUT):
378
+ print(f"Reusing Sandbox for {session_uuid}")
379
+ SANDBOX_METADATA[session_uuid]['last_accessed'] = current_time
380
+ return SANDBOXES[session_uuid]
381
+
382
+ if session_uuid in SANDBOXES:
 
 
 
383
  try:
384
+ print(f"Closing expired sandbox for session {session_uuid}")
385
+ SANDBOXES[session_uuid].kill()
386
  except Exception as e:
387
  print(f"Error closing expired sandbox: {str(e)}")
388
+
389
+ print(f"Creating new sandbox for session {session_uuid}")
 
390
  desktop = Sandbox(api_key=E2B_API_KEY, resolution=(WIDTH, HEIGHT), dpi=96, timeout=SANDBOX_TIMEOUT)
391
  desktop.stream.start(require_auth=True)
392
  setup_cmd = """sudo mkdir -p /usr/lib/firefox-esr/distribution && echo '{"policies":{"OverrideFirstRunPage":"","OverridePostUpdatePage":"","DisableProfileImport":true,"DontCheckDefaultBrowser":true}}' | sudo tee /usr/lib/firefox-esr/distribution/policies.json > /dev/null"""
393
  desktop.commands.run(setup_cmd)
394
+
395
+ SANDBOXES[session_uuid] = desktop
396
+ SANDBOX_METADATA[session_uuid] = {
 
397
  'created_at': current_time,
398
  'last_accessed': current_time
399
  }
400
  return desktop
401
 
402
+ def update_html(interactive_mode: bool, session_uuid):
403
+ desktop = get_or_create_sandbox(session_uuid)
 
404
  auth_key = desktop.stream.get_auth_key()
 
 
405
  base_url = desktop.stream.get_url(auth_key=auth_key)
406
  stream_url = base_url if interactive_mode else f"{base_url}&view_only=true"
407
 
 
408
  status_class = "status-interactive" if interactive_mode else "status-view-only"
409
  status_text = "Interactive" if interactive_mode else "Agent running..."
410
+ creation_time = SANDBOX_METADATA[session_uuid]['created_at'] if session_uuid in SANDBOX_METADATA else time.time()
 
411
 
412
  sandbox_html_content = sandbox_html_template.format(
413
  stream_url=stream_url,
414
  status_class=status_class,
415
  status_text=status_text,
416
  )
 
 
417
  sandbox_html_content += f'<div id="sandbox-creation-time" style="display:none;" data-time="{creation_time}" data-timeout="{SANDBOX_TIMEOUT}"></div>'
 
418
  return sandbox_html_content
419
 
 
 
 
420
 
421
+ def generate_interaction_id(session_uuid):
422
+ return f"{session_uuid}_{int(time.time())}"
423
 
424
  def chat_message_to_json(obj):
425
  """Custom JSON serializer for ChatMessage and related objects"""
 
452
  output_file.write(json.dumps({"status":status, "summary":summary, "error_message": error_message}, default=chat_message_to_json))
453
  output_file.close()
454
 
455
+ def extract_browser_uuid(js_uuid):
456
+ print(f"[BROWSER] Got browser UUID from JS: {js_uuid}")
457
+ return js_uuid
458
+
459
+ def initialize_session(request: gr.Request, interactive_mode, browser_uuid):
460
+ if not browser_uuid:
461
+ new_uuid = str(uuid.uuid4())
462
+ print(f"[LOAD] No UUID from browser, generating: {new_uuid}")
463
+ return update_html(interactive_mode, new_uuid), new_uuid
464
+ else:
465
+ print(f"[LOAD] Got UUID from browser: {browser_uuid}")
466
+ return update_html(interactive_mode, browser_uuid), browser_uuid
467
 
468
 
469
  def create_agent(data_dir, desktop):
 
495
  gr.Button(interactive=False),
496
  )
497
 
498
+ def interact_with_agent(self, task_input, stored_messages, session_state, session_uuid, consent_storage, request: gr.Request):
499
+ interaction_id = generate_interaction_id(session_uuid)
500
+ desktop = get_or_create_sandbox(session_uuid)
501
 
 
 
 
 
502
  data_dir = os.path.join(TMP_DIR, interaction_id)
503
  if not os.path.exists(data_dir):
504
  os.makedirs(data_dir)
505
 
506
  if "agent" in session_state:
507
+ session_state["agent"].data_dir = data_dir
508
  else:
509
  session_state["agent"] = create_agent(data_dir=data_dir, desktop=desktop)
510
+
511
  try:
512
  stored_messages.append(gr.ChatMessage(role="user", content=task_input))
513
  yield stored_messages
 
544
  # Create a Gradio app with Blocks
545
  with gr.Blocks(theme=theme, css=custom_css, js=custom_js) as demo:
546
  #Storing session hash in a state variable
547
+ session_uuid_state = gr.State(None)
548
+
549
 
550
 
551
  with gr.Row():
 
682
  return f"Guru meditation: {str(e)}"
683
 
684
  # Function to set view-only mode
685
+ def clear_and_set_view_only(task_input, session_uuid):
686
+ return update_html(False, session_uuid)
 
687
 
688
+ def set_interactive(session_uuid):
689
+ return update_html(True, session_uuid)
690
 
691
  def reactivate_stop_btn():
692
  return gr.Button("Stop the agent!", variant="huggingface")
 
696
  # Chain the events
697
  run_event = run_btn.click(
698
  fn=clear_and_set_view_only,
699
+ inputs=[task_input, session_uuid_state],
700
  outputs=[sandbox_html]
701
  ).then(
702
  agent_ui.interact_with_agent,
703
+ inputs=[task_input, stored_messages, session_state, session_uuid_state, consent_storage],
704
  outputs=[chatbot_display]
705
  ).then(
706
  fn=set_interactive,
707
+ inputs=[session_uuid_state],
708
  outputs=[sandbox_html]
709
  ).then(
710
  fn=reactivate_stop_btn,
 
741
  # )
742
 
743
  demo.load(
744
+ fn=lambda: True, # dummy to trigger the load
745
+ outputs=[is_interactive],
746
+ ).then(
747
  fn=initialize_session,
748
+ js="() => localStorage.getItem('gradio-session-uuid') || (() => { const id = self.crypto.randomUUID(); localStorage.setItem('gradio-session-uuid', id); return id })()",
749
  inputs=[is_interactive],
750
+ outputs=[sandbox_html, session_uuid_state],
751
  )
752
 
753
  # Launch the app