Spaces:
Paused
Paused
Commit ·
06947bf
1
Parent(s): 26401b4
patch
Browse files- app.py +2 -0
- app.py.bak +98 -0
- app_gradio.py +19 -12
- app_gradio.py.bak +221 -0
- patch_gradio_jsonschema.py +66 -0
app.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
|
|
|
|
|
| 1 |
import patch_gradio_jsonschema # MUST be first - patches boolean JSON Schema handling\n
|
| 2 |
# app.py (Updated with Triage Orchestration)
|
| 3 |
|
|
|
|
| 1 |
+
import patch_gradio_jsonschema # MUST be first - patches boolean JSON Schema handling
|
| 2 |
+
import patch_gradio_jsonschema # MUST be first - patches boolean JSON Schema handling
|
| 3 |
import patch_gradio_jsonschema # MUST be first - patches boolean JSON Schema handling\n
|
| 4 |
# app.py (Updated with Triage Orchestration)
|
| 5 |
|
app.py.bak
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app.py (Updated with Triage Orchestration)
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
import math
|
| 5 |
+
from flask import Flask, render_template, request, jsonify
|
| 6 |
+
from dotenv import load_dotenv
|
| 7 |
+
from graph import triage_app, planner_app, main_app # Import all three compiled apps
|
| 8 |
+
from utils import generate_mermaid_diagram
|
| 9 |
+
|
| 10 |
+
load_dotenv()
|
| 11 |
+
app = Flask(__name__)
|
| 12 |
+
|
| 13 |
+
# Create necessary directories on startup
|
| 14 |
+
os.makedirs("outputs", exist_ok=True)
|
| 15 |
+
os.makedirs("uploads", exist_ok=True)
|
| 16 |
+
os.makedirs("memory", exist_ok=True)
|
| 17 |
+
os.makedirs("logs", exist_ok=True)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
@app.route('/')
|
| 21 |
+
def index():
|
| 22 |
+
return render_template('index.html')
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@app.route('/estimate', methods=['POST'])
|
| 26 |
+
def estimate():
|
| 27 |
+
data = request.json
|
| 28 |
+
user_input = data.get('message', '').strip()
|
| 29 |
+
if not user_input:
|
| 30 |
+
return jsonify({"error": "Message cannot be empty."}), 400
|
| 31 |
+
|
| 32 |
+
# --- NEW TRIAGE STEP ---
|
| 33 |
+
# First, check if the input is a simple greeting
|
| 34 |
+
triage_inputs = {"userInput": user_input}
|
| 35 |
+
triage_result = triage_app.invoke(triage_inputs)
|
| 36 |
+
|
| 37 |
+
# If the triage agent provided a direct response, it's a greeting.
|
| 38 |
+
if triage_result.get("draftResponse"):
|
| 39 |
+
# We add a special key to let the frontend know to just display the message
|
| 40 |
+
return jsonify({"is_greeting": True, "response": triage_result["draftResponse"]})
|
| 41 |
+
|
| 42 |
+
# --- If not a greeting, proceed to the planner ---
|
| 43 |
+
planner_inputs = {"userInput": user_input}
|
| 44 |
+
try:
|
| 45 |
+
estimate_result = planner_app.invoke(planner_inputs)
|
| 46 |
+
estimate_result['pmPlan']['is_greeting'] = False
|
| 47 |
+
return jsonify(estimate_result.get('pmPlan', {}))
|
| 48 |
+
except Exception as e:
|
| 49 |
+
return jsonify({"error": "An unexpected error occurred during planning."}), 500
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
@app.route('/chat', methods=['POST'])
|
| 53 |
+
def chat():
|
| 54 |
+
data = request.json
|
| 55 |
+
user_input = data.get('message', '').strip()
|
| 56 |
+
|
| 57 |
+
try:
|
| 58 |
+
user_budget = float(data.get('user_budget', 0.0))
|
| 59 |
+
cost_per_loop = float(data.get('cost_per_loop', 0.05))
|
| 60 |
+
except (ValueError, TypeError):
|
| 61 |
+
return jsonify({"error": "Invalid budget or cost format."}), 400
|
| 62 |
+
|
| 63 |
+
if not user_input:
|
| 64 |
+
return jsonify({"error": "Message cannot be empty."}), 400
|
| 65 |
+
|
| 66 |
+
if cost_per_loop > 0:
|
| 67 |
+
total_runs_affordable = max(1, math.floor(user_budget / cost_per_loop))
|
| 68 |
+
max_loops_calibrated = total_runs_affordable - 1
|
| 69 |
+
else:
|
| 70 |
+
max_loops_calibrated = 0
|
| 71 |
+
|
| 72 |
+
initial_state = {
|
| 73 |
+
"userInput": user_input, "chatHistory": [], "coreObjectivePrompt": "",
|
| 74 |
+
"retrievedMemory": "", "pmPlan": {}, "experimentCode": None,
|
| 75 |
+
"experimentResults": None, "draftResponse": "", "qaFeedback": None,
|
| 76 |
+
"approved": False, "execution_path": [], "rework_cycles": 0,
|
| 77 |
+
"max_loops": max_loops_calibrated
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
try:
|
| 81 |
+
final_state = main_app.invoke(initial_state)
|
| 82 |
+
except Exception as e:
|
| 83 |
+
return jsonify({"response": "An unexpected error occurred during execution. Please check the logs."}), 500
|
| 84 |
+
|
| 85 |
+
run_path = final_state.get('execution_path', [])
|
| 86 |
+
if run_path:
|
| 87 |
+
mermaid_syntax = generate_mermaid_diagram(run_path)
|
| 88 |
+
with open("outputs/last_run_flow.md", "w") as f:
|
| 89 |
+
f.write("# Last Run Execution Flow\n\n")
|
| 90 |
+
f.write("```mermaid\n")
|
| 91 |
+
f.write(mermaid_syntax)
|
| 92 |
+
f.write("```\n")
|
| 93 |
+
|
| 94 |
+
response = final_state.get('draftResponse', "An error occurred, and no response was generated.")
|
| 95 |
+
return jsonify({"response": response})
|
| 96 |
+
|
| 97 |
+
if __name__ == '__main__':
|
| 98 |
+
app.run(debug=True, port=5001)
|
app_gradio.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
|
|
|
|
|
| 1 |
import patch_gradio_jsonschema # MUST be first - patches boolean JSON Schema handling\n
|
| 2 |
# app_gradio.py (Final Version)
|
| 3 |
|
|
@@ -95,7 +97,7 @@ def update_artifact_list():
|
|
| 95 |
def start_estimation(message, history, state):
|
| 96 |
log.info(f"Starting estimation for: '{message}'")
|
| 97 |
state["original_user_message"] = message
|
| 98 |
-
history.append(
|
| 99 |
|
| 100 |
yield history, state, gr.update(value="", interactive=False), gr.update(visible=False), gr.update(), "Analyzing request..."
|
| 101 |
|
|
@@ -103,7 +105,7 @@ def start_estimation(message, history, state):
|
|
| 103 |
triage_result = triage_app.invoke(triage_inputs)
|
| 104 |
|
| 105 |
if triage_result.get("draftResponse"):
|
| 106 |
-
history.append(
|
| 107 |
yield history, state, gr.update(interactive=True), gr.update(visible=False), gr.update(), "Ready."
|
| 108 |
return
|
| 109 |
|
|
@@ -114,7 +116,7 @@ def start_estimation(message, history, state):
|
|
| 114 |
|
| 115 |
if estimate.get("error"):
|
| 116 |
error_msg = f"Error during planning: {estimate['error']}"
|
| 117 |
-
history.append(
|
| 118 |
yield history, state, gr.update(interactive=True), gr.update(visible=False), gr.update(value=0.10), error_msg
|
| 119 |
else:
|
| 120 |
plan_text = "\n".join([f"- {step}" for step in estimate.get('plan', [])])
|
|
@@ -126,7 +128,7 @@ def execute_main_task(history, state, budget):
|
|
| 126 |
|
| 127 |
yield history, state, gr.update(visible=False), gr.update(value="", interactive=False), "Task approved. Starting execution..."
|
| 128 |
|
| 129 |
-
history.append(
|
| 130 |
|
| 131 |
cost_per_loop = state["estimate"].get('cost_per_loop_usd', 0.05)
|
| 132 |
if cost_per_loop > 0:
|
|
@@ -153,11 +155,11 @@ def execute_main_task(history, state, budget):
|
|
| 153 |
if "draftResponse" in node_output and node_output["draftResponse"]:
|
| 154 |
final_response = node_output["draftResponse"]
|
| 155 |
|
| 156 |
-
history[-1] =
|
| 157 |
yield history, state, gr.update(visible=False), gr.update(interactive=True), "Task complete. Ready."
|
| 158 |
|
| 159 |
def cancel_task(history, state):
|
| 160 |
-
history.append(
|
| 161 |
return history, state, gr.update(visible=False), gr.update(interactive=True), "Task cancelled. Ready."
|
| 162 |
|
| 163 |
# --- Gradio UI Definition ---
|
|
@@ -214,9 +216,14 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Autonomous AI Lab") as demo:
|
|
| 214 |
|
| 215 |
# --- Launch Configuration ---
|
| 216 |
if __name__ == "__main__":
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import patch_gradio_jsonschema # MUST be first - patches boolean JSON Schema handling
|
| 2 |
+
import patch_gradio_jsonschema # MUST be first - patches boolean JSON Schema handling
|
| 3 |
import patch_gradio_jsonschema # MUST be first - patches boolean JSON Schema handling\n
|
| 4 |
# app_gradio.py (Final Version)
|
| 5 |
|
|
|
|
| 97 |
def start_estimation(message, history, state):
|
| 98 |
log.info(f"Starting estimation for: '{message}'")
|
| 99 |
state["original_user_message"] = message
|
| 100 |
+
history.append([message, None])
|
| 101 |
|
| 102 |
yield history, state, gr.update(value="", interactive=False), gr.update(visible=False), gr.update(), "Analyzing request..."
|
| 103 |
|
|
|
|
| 105 |
triage_result = triage_app.invoke(triage_inputs)
|
| 106 |
|
| 107 |
if triage_result.get("draftResponse"):
|
| 108 |
+
history.append(["", triage_result["draftResponse"]])
|
| 109 |
yield history, state, gr.update(interactive=True), gr.update(visible=False), gr.update(), "Ready."
|
| 110 |
return
|
| 111 |
|
|
|
|
| 116 |
|
| 117 |
if estimate.get("error"):
|
| 118 |
error_msg = f"Error during planning: {estimate['error']}"
|
| 119 |
+
history.append(["", error_msg])
|
| 120 |
yield history, state, gr.update(interactive=True), gr.update(visible=False), gr.update(value=0.10), error_msg
|
| 121 |
else:
|
| 122 |
plan_text = "\n".join([f"- {step}" for step in estimate.get('plan', [])])
|
|
|
|
| 128 |
|
| 129 |
yield history, state, gr.update(visible=False), gr.update(value="", interactive=False), "Task approved. Starting execution..."
|
| 130 |
|
| 131 |
+
history.append(["", "..."])
|
| 132 |
|
| 133 |
cost_per_loop = state["estimate"].get('cost_per_loop_usd', 0.05)
|
| 134 |
if cost_per_loop > 0:
|
|
|
|
| 155 |
if "draftResponse" in node_output and node_output["draftResponse"]:
|
| 156 |
final_response = node_output["draftResponse"]
|
| 157 |
|
| 158 |
+
history[-1] = ["", final_response]
|
| 159 |
yield history, state, gr.update(visible=False), gr.update(interactive=True), "Task complete. Ready."
|
| 160 |
|
| 161 |
def cancel_task(history, state):
|
| 162 |
+
history.append(["", "Task cancelled."])
|
| 163 |
return history, state, gr.update(visible=False), gr.update(interactive=True), "Task cancelled. Ready."
|
| 164 |
|
| 165 |
# --- Gradio UI Definition ---
|
|
|
|
| 216 |
|
| 217 |
# --- Launch Configuration ---
|
| 218 |
if __name__ == "__main__":
|
| 219 |
+
|
| 220 |
+
# --- recommended Gradio launch for Hugging Face Spaces ---
|
| 221 |
+
import os
|
| 222 |
+
server_port = int(os.environ.get("PORT", 7860))
|
| 223 |
+
demo.launch(
|
| 224 |
+
server_name="0.0.0.0",
|
| 225 |
+
server_port=server_port,
|
| 226 |
+
share=False,
|
| 227 |
+
inbrowser=False,
|
| 228 |
+
)
|
| 229 |
+
# --- end snippet ---
|
app_gradio.py.bak
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app_gradio.py (Final Version)
|
| 2 |
+
|
| 3 |
+
import gradio as gr
|
| 4 |
+
import math
|
| 5 |
+
import os
|
| 6 |
+
import json
|
| 7 |
+
from datetime import datetime
|
| 8 |
+
from dotenv import load_dotenv
|
| 9 |
+
|
| 10 |
+
# Import your existing agentic logic
|
| 11 |
+
from graph import triage_app, planner_app, main_app
|
| 12 |
+
from logging_config import get_logger
|
| 13 |
+
|
| 14 |
+
# --- Setup & Configuration ---
|
| 15 |
+
load_dotenv()
|
| 16 |
+
log = get_logger(__name__)
|
| 17 |
+
|
| 18 |
+
# Create necessary directories on startup
|
| 19 |
+
os.makedirs("outputs", exist_ok=True)
|
| 20 |
+
os.makedirs("uploads", exist_ok=True)
|
| 21 |
+
os.makedirs("conversations", exist_ok=True)
|
| 22 |
+
|
| 23 |
+
# --- Authentication ---
|
| 24 |
+
USERS = {
|
| 25 |
+
"tester1": "pass123",
|
| 26 |
+
"researcher": "lab456",
|
| 27 |
+
"admin": "admin789"
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
# --- State Management ---
|
| 31 |
+
def get_default_state():
|
| 32 |
+
"""Initializes a new session state."""
|
| 33 |
+
return {
|
| 34 |
+
"original_user_message": "",
|
| 35 |
+
"estimate": {},
|
| 36 |
+
"current_conversation_file": None,
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
# --- Conversation Management ---
|
| 40 |
+
def save_conversation(history, state):
|
| 41 |
+
if not history:
|
| 42 |
+
return "Nothing to save.", gr.update()
|
| 43 |
+
|
| 44 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 45 |
+
filename = f"conv_{timestamp}.json"
|
| 46 |
+
filepath = os.path.join("conversations", filename)
|
| 47 |
+
|
| 48 |
+
with open(filepath, 'w', encoding='utf-8') as f:
|
| 49 |
+
json.dump(history, f, indent=2)
|
| 50 |
+
|
| 51 |
+
state["current_conversation_file"] = filepath
|
| 52 |
+
log.info(f"Conversation saved to {filepath}")
|
| 53 |
+
return f"Saved to {filename}", gr.update(choices=get_saved_conversations())
|
| 54 |
+
|
| 55 |
+
def load_conversation(filepath, state):
|
| 56 |
+
if not filepath:
|
| 57 |
+
return [], state
|
| 58 |
+
|
| 59 |
+
with open(filepath, 'r', encoding='utf-8') as f:
|
| 60 |
+
history = json.load(f)
|
| 61 |
+
|
| 62 |
+
state["current_conversation_file"] = filepath
|
| 63 |
+
log.info(f"Conversation loaded from {filepath}")
|
| 64 |
+
return history, state
|
| 65 |
+
|
| 66 |
+
def get_saved_conversations():
|
| 67 |
+
return [os.path.join("conversations", f) for f in os.listdir("conversations") if f.endswith(".json")]
|
| 68 |
+
|
| 69 |
+
def clear_conversation():
|
| 70 |
+
return [], get_default_state(), "Conversation cleared.", "No artifacts generated yet."
|
| 71 |
+
|
| 72 |
+
# --- Artifact Management ---
|
| 73 |
+
def handle_file_upload(file):
|
| 74 |
+
if file is None:
|
| 75 |
+
return "No file uploaded."
|
| 76 |
+
|
| 77 |
+
basename = os.path.basename(file.name)
|
| 78 |
+
destination_path = os.path.join("uploads", basename)
|
| 79 |
+
os.rename(file.name, destination_path)
|
| 80 |
+
log.info(f"File uploaded to {destination_path}")
|
| 81 |
+
return f"Uploaded: {basename}"
|
| 82 |
+
|
| 83 |
+
def update_artifact_list():
|
| 84 |
+
output_files = os.listdir("outputs")
|
| 85 |
+
if not output_files:
|
| 86 |
+
return "No artifacts generated yet."
|
| 87 |
+
|
| 88 |
+
markdown_list = "### Generated Artifacts:\n"
|
| 89 |
+
for f in output_files:
|
| 90 |
+
markdown_list += f"- `{f}` (in your 'outputs' folder)\n"
|
| 91 |
+
return markdown_list
|
| 92 |
+
|
| 93 |
+
# --- Core Logic Functions ---
|
| 94 |
+
def start_estimation(message, history, state):
|
| 95 |
+
log.info(f"Starting estimation for: '{message}'")
|
| 96 |
+
state["original_user_message"] = message
|
| 97 |
+
history.append({"role": "user", "content": message})
|
| 98 |
+
|
| 99 |
+
yield history, state, gr.update(value="", interactive=False), gr.update(visible=False), gr.update(), "Analyzing request..."
|
| 100 |
+
|
| 101 |
+
triage_inputs = {"userInput": message}
|
| 102 |
+
triage_result = triage_app.invoke(triage_inputs)
|
| 103 |
+
|
| 104 |
+
if triage_result.get("draftResponse"):
|
| 105 |
+
history.append({"role": "assistant", "content": triage_result["draftResponse"]})
|
| 106 |
+
yield history, state, gr.update(interactive=True), gr.update(visible=False), gr.update(), "Ready."
|
| 107 |
+
return
|
| 108 |
+
|
| 109 |
+
planner_inputs = {"userInput": message}
|
| 110 |
+
estimate_result = planner_app.invoke(planner_inputs)
|
| 111 |
+
estimate = estimate_result.get('pmPlan', {})
|
| 112 |
+
state["estimate"] = estimate
|
| 113 |
+
|
| 114 |
+
if estimate.get("error"):
|
| 115 |
+
error_msg = f"Error during planning: {estimate['error']}"
|
| 116 |
+
history.append({"role": "assistant", "content": error_msg})
|
| 117 |
+
yield history, state, gr.update(interactive=True), gr.update(visible=False), gr.update(value=0.10), error_msg
|
| 118 |
+
else:
|
| 119 |
+
plan_text = "\n".join([f"- {step}" for step in estimate.get('plan', [])])
|
| 120 |
+
approval_text_md = (f"**Here is my plan:**\n{plan_text}\n\nThis may require up to **{estimate.get('max_loops_initial', 0) + 1} attempts** and could cost approximately **${estimate.get('estimated_cost_usd', 0.0)}**.")
|
| 121 |
+
yield history, state, gr.update(interactive=False), gr.update(visible=True, value=approval_text_md), gr.update(value=estimate.get('estimated_cost_usd', 0.10)), "Awaiting your approval to proceed."
|
| 122 |
+
|
| 123 |
+
def execute_main_task(history, state, budget):
|
| 124 |
+
log.info(f"Executing main task with budget: ${budget}")
|
| 125 |
+
|
| 126 |
+
yield history, state, gr.update(visible=False), gr.update(value="", interactive=False), "Task approved. Starting execution..."
|
| 127 |
+
|
| 128 |
+
history.append({"role": "assistant", "content": "..."})
|
| 129 |
+
|
| 130 |
+
cost_per_loop = state["estimate"].get('cost_per_loop_usd', 0.05)
|
| 131 |
+
if cost_per_loop > 0:
|
| 132 |
+
total_runs_affordable = max(1, math.floor(float(budget) / cost_per_loop))
|
| 133 |
+
max_loops_calibrated = total_runs_affordable - 1
|
| 134 |
+
else:
|
| 135 |
+
max_loops_calibrated = 0
|
| 136 |
+
|
| 137 |
+
initial_state = {
|
| 138 |
+
"userInput": state["original_user_message"],
|
| 139 |
+
"chatHistory": [],
|
| 140 |
+
"max_loops": max_loops_calibrated,
|
| 141 |
+
"status_update": "Task approved. Starting execution..."
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
final_response = "An error occurred during execution."
|
| 145 |
+
for step in main_app.stream(initial_state):
|
| 146 |
+
node_name = list(step.keys())[0]
|
| 147 |
+
node_output = step[node_name]
|
| 148 |
+
|
| 149 |
+
status = node_output.get("status_update", "Processing...")
|
| 150 |
+
yield history, state, gr.update(visible=False), gr.update(interactive=False), status
|
| 151 |
+
|
| 152 |
+
if "draftResponse" in node_output and node_output["draftResponse"]:
|
| 153 |
+
final_response = node_output["draftResponse"]
|
| 154 |
+
|
| 155 |
+
history[-1] = {"role": "assistant", "content": final_response}
|
| 156 |
+
yield history, state, gr.update(visible=False), gr.update(interactive=True), "Task complete. Ready."
|
| 157 |
+
|
| 158 |
+
def cancel_task(history, state):
|
| 159 |
+
history.append({"role": "assistant", "content": "Task cancelled."})
|
| 160 |
+
return history, state, gr.update(visible=False), gr.update(interactive=True), "Task cancelled. Ready."
|
| 161 |
+
|
| 162 |
+
# --- Gradio UI Definition ---
|
| 163 |
+
with gr.Blocks(theme=gr.themes.Soft(), title="Autonomous AI Lab") as demo:
|
| 164 |
+
state = gr.State(value=get_default_state())
|
| 165 |
+
|
| 166 |
+
gr.Markdown("# Autonomous AI Lab")
|
| 167 |
+
|
| 168 |
+
with gr.Row():
|
| 169 |
+
with gr.Column(scale=1):
|
| 170 |
+
gr.Markdown("### Controls")
|
| 171 |
+
clear_btn = gr.Button("🗑️ New Conversation")
|
| 172 |
+
gr.Markdown("### Saved Chats")
|
| 173 |
+
saved_chats_dropdown = gr.Dropdown(label="Load a past conversation", choices=get_saved_conversations(), interactive=True)
|
| 174 |
+
save_chat_btn = gr.Button("💾 Save Current Chat")
|
| 175 |
+
save_status_text = gr.Textbox(label="Save Status", interactive=False)
|
| 176 |
+
gr.Markdown("### Artifacts")
|
| 177 |
+
file_uploader = gr.File(label="Upload a file")
|
| 178 |
+
upload_status_text = gr.Textbox(label="Upload Status", interactive=False)
|
| 179 |
+
refresh_artifacts_btn = gr.Button("🔄 Refresh Artifacts List")
|
| 180 |
+
artifact_list_display = gr.Markdown("No artifacts generated yet.")
|
| 181 |
+
|
| 182 |
+
with gr.Column(scale=4):
|
| 183 |
+
chatbot = gr.Chatbot(label="Conversation", height=600, avatar_images=(None, "https://i.imgur.com/b5OqI32.png"))
|
| 184 |
+
status_display = gr.Markdown("Status: Ready.")
|
| 185 |
+
with gr.Group(visible=False) as approval_box:
|
| 186 |
+
approval_text = gr.Markdown()
|
| 187 |
+
with gr.Row():
|
| 188 |
+
budget_input = gr.Number(label="Set your maximum budget ($)", value=0.10, minimum=0.01, step=0.05)
|
| 189 |
+
with gr.Row():
|
| 190 |
+
proceed_btn = gr.Button("✅ Approve & Proceed", variant="primary")
|
| 191 |
+
cancel_btn = gr.Button("❌ Cancel")
|
| 192 |
+
|
| 193 |
+
with gr.Row():
|
| 194 |
+
msg_textbox = gr.Textbox(label="Your Message", placeholder="Ask a question or describe a task...", scale=7)
|
| 195 |
+
submit_btn = gr.Button("Send", variant="primary", scale=1)
|
| 196 |
+
|
| 197 |
+
# --- Event Handlers ---
|
| 198 |
+
estimation_outputs = [chatbot, state, msg_textbox, approval_box, budget_input, status_display]
|
| 199 |
+
execution_outputs = [chatbot, state, approval_box, msg_textbox, status_display]
|
| 200 |
+
cancel_outputs = [chatbot, state, approval_box, msg_textbox, status_display]
|
| 201 |
+
clear_outputs = [chatbot, state, save_status_text, artifact_list_display]
|
| 202 |
+
|
| 203 |
+
msg_textbox.submit(fn=start_estimation, inputs=[msg_textbox, chatbot, state], outputs=estimation_outputs)
|
| 204 |
+
submit_btn.click(fn=start_estimation, inputs=[msg_textbox, chatbot, state], outputs=estimation_outputs)
|
| 205 |
+
proceed_btn.click(fn=execute_main_task, inputs=[chatbot, state, budget_input], outputs=execution_outputs)
|
| 206 |
+
cancel_btn.click(fn=cancel_task, inputs=[chatbot, state], outputs=cancel_outputs)
|
| 207 |
+
|
| 208 |
+
clear_btn.click(fn=clear_conversation, inputs=[], outputs=clear_outputs)
|
| 209 |
+
save_chat_btn.click(fn=save_conversation, inputs=[chatbot, state], outputs=[save_status_text, saved_chats_dropdown])
|
| 210 |
+
saved_chats_dropdown.change(fn=load_conversation, inputs=[saved_chats_dropdown, state], outputs=[chatbot, state])
|
| 211 |
+
file_uploader.upload(fn=handle_file_upload, inputs=[file_uploader], outputs=[upload_status_text])
|
| 212 |
+
refresh_artifacts_btn.click(fn=update_artifact_list, inputs=[], outputs=[artifact_list_display])
|
| 213 |
+
|
| 214 |
+
# --- Launch Configuration ---
|
| 215 |
+
if __name__ == "__main__":
|
| 216 |
+
demo.launch(
|
| 217 |
+
share=True,
|
| 218 |
+
server_name="0.0.0.0",
|
| 219 |
+
auth=[(user, pwd) for user, pwd in USERS.items()],
|
| 220 |
+
auth_message="Enter your credentials to access the Autonomous AI Lab."
|
| 221 |
+
)
|
patch_gradio_jsonschema.py
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# patch_gradio_jsonschema.py
|
| 2 |
+
"""
|
| 3 |
+
Runtime monkeypatch to make gradio_client JSON Schema handling robust to boolean schemas.
|
| 4 |
+
Import this BEFORE importing gradio or gradio_client (so do it at the top of your entrypoint).
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
try:
|
| 8 |
+
import gradio_client.utils as _gc_utils
|
| 9 |
+
except Exception:
|
| 10 |
+
_gc_utils = None
|
| 11 |
+
|
| 12 |
+
def _is_bool_schema(x):
|
| 13 |
+
return isinstance(x, bool)
|
| 14 |
+
|
| 15 |
+
def _bool_to_safe_schema(b: bool):
|
| 16 |
+
if b:
|
| 17 |
+
return {}
|
| 18 |
+
else:
|
| 19 |
+
return {"type": "___boolean_schema_false___"}
|
| 20 |
+
|
| 21 |
+
def _wrap_get_type(orig):
|
| 22 |
+
def wrapper(schema):
|
| 23 |
+
if _is_bool_schema(schema):
|
| 24 |
+
schema = _bool_to_safe_schema(schema)
|
| 25 |
+
return orig(schema)
|
| 26 |
+
return wrapper
|
| 27 |
+
|
| 28 |
+
def _wrap_json_schema_to_python_type(orig):
|
| 29 |
+
def wrapper(schema, defs=None):
|
| 30 |
+
if _is_bool_schema(schema):
|
| 31 |
+
schema = _bool_to_safe_schema(schema)
|
| 32 |
+
return orig(schema, defs)
|
| 33 |
+
return wrapper
|
| 34 |
+
|
| 35 |
+
def apply_patch():
|
| 36 |
+
global _gc_utils
|
| 37 |
+
if _gc_utils is None:
|
| 38 |
+
try:
|
| 39 |
+
import gradio_client.utils as _gc_utils
|
| 40 |
+
except Exception:
|
| 41 |
+
return False
|
| 42 |
+
|
| 43 |
+
if hasattr(_gc_utils, "get_type"):
|
| 44 |
+
_gc_utils.get_type = _wrap_get_type(_gc_utils.get_type)
|
| 45 |
+
|
| 46 |
+
if hasattr(_gc_utils, "_json_schema_to_python_type"):
|
| 47 |
+
_gc_utils._json_schema_to_python_type = _wrap_json_schema_to_python_type(
|
| 48 |
+
_gc_utils._json_schema_to_python_type
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
if hasattr(_gc_utils, "json_schema_to_python_type"):
|
| 52 |
+
orig_public = _gc_utils.json_schema_to_python_type
|
| 53 |
+
def public_wrapper(schema, defs=None):
|
| 54 |
+
if _is_bool_schema(schema):
|
| 55 |
+
schema = _bool_to_safe_schema(schema)
|
| 56 |
+
return orig_public(schema, defs)
|
| 57 |
+
_gc_utils.json_schema_to_python_type = public_wrapper
|
| 58 |
+
|
| 59 |
+
return True
|
| 60 |
+
|
| 61 |
+
apply_patch()
|
| 62 |
+
# diagnostic print
|
| 63 |
+
try:
|
| 64 |
+
print("patch_gradio_jsonschema applied")
|
| 65 |
+
except Exception:
|
| 66 |
+
pass
|