Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Miquel Farré
commited on
Commit
·
5504eb2
1
Parent(s):
a8daf8a
changing session_hash with browser generated uuid
Browse files
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 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
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 {
|
388 |
-
SANDBOXES[
|
389 |
except Exception as e:
|
390 |
print(f"Error closing expired sandbox: {str(e)}")
|
391 |
-
|
392 |
-
|
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 |
-
|
400 |
-
|
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,
|
408 |
-
|
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
|
470 |
-
|
471 |
-
|
472 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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,
|
505 |
-
|
|
|
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
|
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 |
-
|
|
|
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,
|
694 |
-
|
695 |
-
return update_html(False, request)
|
696 |
|
697 |
-
def set_interactive(
|
698 |
-
return update_html(True,
|
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,
|
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,
|
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
|