Spaces:
Sleeping
Sleeping
File size: 6,881 Bytes
1bd9ce8 ca22196 1bd9ce8 ca22196 1bd9ce8 ca22196 1bd9ce8 ca22196 1bd9ce8 e5e3402 1bd9ce8 ca22196 1bd9ce8 784651e 1bd9ce8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | #!/usr/bin/env python
import subprocess
import time
import threading
import re
import json
import os
from pathlib import Path
import gradio as gr
from src.ai_agentic_coder.crew import EngineeringTeam
from src.ai_agentic_coder.tools.python_code_run_tool import RUN_RESULT_FILE
OUTPUT_DIR = Path(__file__).resolve().parent / "output"
def _run_result_path() -> Path:
return OUTPUT_DIR / RUN_RESULT_FILE
def _clear_run_result() -> None:
try:
_run_result_path().unlink()
except FileNotFoundError:
pass
def _load_run_result() -> dict[str, str]:
try:
data = json.loads(_run_result_path().read_text(encoding="utf-8"))
except (FileNotFoundError, json.JSONDecodeError):
return {}
return {
"download_url": str(data.get("download_url", "")),
"live_url": str(data.get("live_url", "")),
}
def _set_base_url_from_request(request: gr.Request | None) -> None:
if request is None:
return
host = request.headers.get("x-forwarded-host") or request.headers.get("host")
if not host:
return
proto = request.headers.get("x-forwarded-proto") or request.url.scheme or "http"
os.environ["AI_AGENTIC_CODER_BASE_URL"] = f"{proto}://{host}"
def run_crew(requirements, module_name, class_name, progress=gr.Progress()):
"""Run the crew with the given inputs and return ONLY the raw result text."""
try:
# Kill any running processes from previous runs
for pattern in (
"python.*src.ai_agentic_coder.output.app",
"python.*ai_agentic_coder.generated_app_runner",
):
subprocess.run(
["pkill", "-f", pattern],
stderr=subprocess.DEVNULL,
stdout=subprocess.DEVNULL
)
inputs = {
'requirements': requirements,
'module_name': f"{module_name}.py", # Add .py extension
'class_name': class_name
}
# Baseline progress hints (wrapper controls real-time progress)
progress(0.02, desc="Initializing crew...")
crew = EngineeringTeam().crew()
progress(0.05, desc="Running crew...")
result = crew.kickoff(inputs=inputs)
raw = str(result.raw) if hasattr(result, 'raw') else str(result)
return raw
except Exception as e:
return f"❌ Error: {str(e)}"
# Generator function to manage state, progress, and output
def run_crew_wrapper(requirements, module_name, class_name, request: gr.Request | None = None):
_clear_run_result()
_set_base_url_from_request(request)
progress = gr.Progress()
# Immediately disable the button, show Output as progress area, and hide URL boxes
yield (
gr.update(value="[░░░░░░░░░░░░░░░░░░░░] 1% - AI is running hard", visible=True, label="Progress"),
gr.update(value="", visible=False), # clear Download URL box at start
gr.update(value="", visible=False), # clear Live URL box at start
gr.update(interactive=False, value="Running…")
)
# Kick progress so overlay appears immediately
progress(0.01, desc="Starting…")
done = threading.Event()
result_holder: dict[str, str | None] = {"output": None, "error": None}
def worker():
try:
result_holder["output"] = run_crew(requirements, module_name, class_name, progress)
except Exception as e: # pragma: no cover
result_holder["error"] = str(e)
finally:
done.set()
t = threading.Thread(target=worker, daemon=True)
t.start()
start = time.time()
total = 300.0 # ~5 minutes
last_pct = 1
while not done.is_set():
elapsed = time.time() - start
pct = min(95, max(1, int((elapsed / total) * 95)))
if pct != last_pct:
# Overlay progress and textual progress in output box
progress(pct / 100.0, desc=f"AI is running hard")
last_pct = pct
bar_len = 24
filled = int(bar_len * pct / 100)
bar = ("█" * filled) + ("░" * (bar_len - filled))
yield (
gr.update(value=f"[{bar}] {pct}% - AI is running hard", visible=True, label="Progress"),
gr.update(visible=False),
gr.update(visible=False),
gr.update(interactive=False, value="Running…")
)
time.sleep(1.0)
# Worker is done
t.join()
progress(1.0, desc="Finalizing…")
if result_holder["error"]:
final_output = f"❌ Error: {result_holder['error']}"
# Show error in output, keep URL boxes hidden, re-enable button
yield (
gr.update(value=final_output, visible=True, label="Error", elem_classes=["card", "error"]),
gr.update(visible=False),
gr.update(visible=False),
gr.update(interactive=True, value="Run AI Coder")
)
return
raw_output = result_holder["output"] or ""
run_result = _load_run_result()
if run_result.get("download_url") and run_result.get("live_url"):
yield (
gr.update(value="", visible=False, label="Output"),
gr.update(value=run_result["download_url"], visible=True),
gr.update(value=run_result["live_url"], visible=True),
gr.update(interactive=True, value="Run AI Coder")
)
return
# If the output looks like a failure (but no exception was thrown), treat it as an error
failure_like = bool(re.search(r"\b(failed|failure|error|exception|traceback)\b", raw_output, re.IGNORECASE))
# Extract and validate URLs strictly (avoid putting random text into URL boxes)
rough_urls = re.findall(r"https?://[^\s\"'<>]+", raw_output)
cleaned_urls = [u.rstrip(".,);]") for u in rough_urls]
valid_urls = [u for u in cleaned_urls if re.match(r"^https?://(?:[A-Za-z0-9.-]+|localhost|(?:\d{1,3}\.){3}\d{1,3})(?::\d+)?(?:/\S*)?$", u)]
if failure_like or len(valid_urls) == 0:
final_output = f"❌ Error: {raw_output.strip()}"
yield (
gr.update(value=final_output, visible=True, label="Error", elem_classes=["card", "error"]),
gr.update(value="", visible=False),
gr.update(value="", visible=False),
gr.update(interactive=True, value="Run AI Coder")
)
return
# Use up to two validated URLs
download_url = valid_urls[0] if len(valid_urls) > 0 else ""
live_url = valid_urls[1] if len(valid_urls) > 1 else ""
# Success: hide output, show URLs, re-enable button
yield (
gr.update(value="", visible=False, label="Output"),
gr.update(value=download_url, visible=bool(download_url)),
gr.update(value=live_url, visible=bool(live_url)),
gr.update(interactive=True, value="Run AI Coder")
)
|