Spaces:
Paused
Paused
Upload folder using huggingface_hub
Browse files- app.py +43 -10
- verify_report_viewer.py +25 -0
- verify_tabs.py +35 -0
app.py
CHANGED
|
@@ -65,15 +65,16 @@ def patch_tinytroupe():
|
|
| 65 |
content = content.replace("i = 0", "parallel_retry = False\n i = 0")
|
| 66 |
|
| 67 |
# Modify the model call inside the loop
|
| 68 |
-
|
| 69 |
-
|
|
|
|
| 70 |
logger.info("Attempting parallel call to alias-large and alias-huge.")
|
| 71 |
response = self._raw_model_call_parallel(["alias-large", "alias-huge"], chat_api_params)
|
| 72 |
if isinstance(response, Exception):
|
| 73 |
raise response
|
| 74 |
else:
|
| 75 |
response = self._raw_model_call(model, chat_api_params)"""
|
| 76 |
-
|
| 77 |
|
| 78 |
# Update the 502 catch block
|
| 79 |
pattern = r"if isinstance\(e, openai\.APIStatusError\) and e\.status_code == 502 and isinstance\(self, HelmholtzBlabladorClient\):.*?except Exception as fallback_e:.*?logger\.error\(f\"Fallback to OpenAI also failed: \{fallback_e\}\"\)"
|
|
@@ -266,6 +267,17 @@ def get_persona_pool():
|
|
| 266 |
print(f"Error fetching persona pool: {e}")
|
| 267 |
return []
|
| 268 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
def upload_persona_to_pool(persona_data):
|
| 270 |
if not gh:
|
| 271 |
return
|
|
@@ -286,7 +298,22 @@ def upload_persona_to_pool(persona_data):
|
|
| 286 |
except Exception as e:
|
| 287 |
print(f"Error uploading persona to pool: {e}")
|
| 288 |
|
| 289 |
-
def select_or_create_personas(theme, customer_profile, num_personas, force_method=None):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 290 |
if force_method == "DeepPersona":
|
| 291 |
add_log("Forcing DeepPersona generation...")
|
| 292 |
personas = []
|
|
@@ -296,7 +323,6 @@ def select_or_create_personas(theme, customer_profile, num_personas, force_metho
|
|
| 296 |
if len(personas) >= int(num_personas): return personas[:int(num_personas)]
|
| 297 |
# fallback if some failed
|
| 298 |
num_personas = int(num_personas) - len(personas)
|
| 299 |
-
final_personas_base = personas
|
| 300 |
elif force_method == "TinyTroupe":
|
| 301 |
add_log("Forcing TinyTroupe generation...")
|
| 302 |
return generate_personas_from_tiny_factory(theme, customer_profile, num_personas)
|
|
@@ -599,13 +625,13 @@ def generate_tasks(theme, customer_profile):
|
|
| 599 |
|
| 600 |
return [f"Task {i+1} for {theme} (Manual fallback)" for i in range(10)]
|
| 601 |
|
| 602 |
-
def handle_generate(theme, customer_profile, num_personas, method):
|
| 603 |
try:
|
| 604 |
yield "Generating tasks...", None, None
|
| 605 |
tasks = generate_tasks(theme, customer_profile)
|
| 606 |
|
| 607 |
yield "Selecting or creating personas...", tasks, None
|
| 608 |
-
personas = select_or_create_personas(theme, customer_profile, num_personas, force_method=method)
|
| 609 |
|
| 610 |
yield "Generation complete!", tasks, personas
|
| 611 |
except Exception as e:
|
|
@@ -993,9 +1019,10 @@ def generate_full_ui_call(repo, branch, session_id, selected_solutions_json):
|
|
| 993 |
return f"Error reading template: {e}"
|
| 994 |
|
| 995 |
prompt = template.replace("{{selected_solutions}}", selected_solutions_json)
|
| 996 |
-
prompt = prompt.replace("{{url}}", "
|
| 997 |
prompt = prompt.replace("{{analysis_report}}", "See previous activities in this session")
|
| 998 |
prompt = prompt.replace("{{report_id}}", session_id[:8])
|
|
|
|
| 999 |
|
| 1000 |
headers = {
|
| 1001 |
"X-Goog-Api-Key": ANALYSIS_API_KEY,
|
|
@@ -1103,10 +1130,16 @@ with gr.Blocks(title="UX Analysis Orchestrator") as demo:
|
|
| 1103 |
theme_input = gr.Textbox(label="Theme", placeholder="e.g., Communication, Purchase decisions, Information gathering")
|
| 1104 |
profile_input = gr.Textbox(label="Customer Profile Description", placeholder="Describe the target customer...")
|
| 1105 |
num_personas_input = gr.Number(label="Number of Personas", value=1, precision=0)
|
| 1106 |
-
persona_method = gr.Radio(["TinyTroupe", "DeepPersona"], label="Persona Generation Method", value="TinyTroupe")
|
|
|
|
| 1107 |
url_input = gr.Textbox(label="Target URL", value="https://example.com")
|
| 1108 |
generate_btn = gr.Button("Generate Personas & Tasks")
|
| 1109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1110 |
with gr.Column():
|
| 1111 |
status_output = gr.Textbox(label="Status", interactive=False)
|
| 1112 |
task_list_display = gr.JSON(label="Tasks")
|
|
@@ -1352,7 +1385,7 @@ with gr.Blocks(title="UX Analysis Orchestrator") as demo:
|
|
| 1352 |
# Event handlers
|
| 1353 |
generate_btn.click(
|
| 1354 |
fn=handle_generate,
|
| 1355 |
-
inputs=[theme_input, profile_input, num_personas_input, persona_method],
|
| 1356 |
outputs=[status_output, task_list_display, persona_display]
|
| 1357 |
)
|
| 1358 |
|
|
|
|
| 65 |
content = content.replace("i = 0", "parallel_retry = False\n i = 0")
|
| 66 |
|
| 67 |
# Modify the model call inside the loop
|
| 68 |
+
if 'if parallel_retry:' not in content:
|
| 69 |
+
old_call = "response = self._raw_model_call(model, chat_api_params)"
|
| 70 |
+
new_call = """if parallel_retry:
|
| 71 |
logger.info("Attempting parallel call to alias-large and alias-huge.")
|
| 72 |
response = self._raw_model_call_parallel(["alias-large", "alias-huge"], chat_api_params)
|
| 73 |
if isinstance(response, Exception):
|
| 74 |
raise response
|
| 75 |
else:
|
| 76 |
response = self._raw_model_call(model, chat_api_params)"""
|
| 77 |
+
content = content.replace(old_call, new_call)
|
| 78 |
|
| 79 |
# Update the 502 catch block
|
| 80 |
pattern = r"if isinstance\(e, openai\.APIStatusError\) and e\.status_code == 502 and isinstance\(self, HelmholtzBlabladorClient\):.*?except Exception as fallback_e:.*?logger\.error\(f\"Fallback to OpenAI also failed: \{fallback_e\}\"\)"
|
|
|
|
| 267 |
print(f"Error fetching persona pool: {e}")
|
| 268 |
return []
|
| 269 |
|
| 270 |
+
def get_example_personas():
|
| 271 |
+
example_path = "external/TinyTroupe/examples/agents/"
|
| 272 |
+
if not os.path.exists(example_path):
|
| 273 |
+
return []
|
| 274 |
+
try:
|
| 275 |
+
files = [f for f in os.listdir(example_path) if f.endswith(".json")]
|
| 276 |
+
return sorted(files)
|
| 277 |
+
except Exception as e:
|
| 278 |
+
print(f"Error listing example personas: {e}")
|
| 279 |
+
return []
|
| 280 |
+
|
| 281 |
def upload_persona_to_pool(persona_data):
|
| 282 |
if not gh:
|
| 283 |
return
|
|
|
|
| 298 |
except Exception as e:
|
| 299 |
print(f"Error uploading persona to pool: {e}")
|
| 300 |
|
| 301 |
+
def select_or_create_personas(theme, customer_profile, num_personas, force_method=None, example_file=None):
|
| 302 |
+
if force_method == "Example Persona" and example_file:
|
| 303 |
+
add_log(f"Loading example persona from {example_file}...")
|
| 304 |
+
try:
|
| 305 |
+
with open(os.path.join("external/TinyTroupe/examples/agents/", example_file), "r") as f:
|
| 306 |
+
data = json.load(f)
|
| 307 |
+
# Adapt TinyTroupe format to our internal format
|
| 308 |
+
persona = {
|
| 309 |
+
"name": data.get("name", "Unknown"),
|
| 310 |
+
"minibio": data.get("mental_faculties", [{}])[0].get("context", "An example persona.") if "mental_faculties" in data else "An example persona.",
|
| 311 |
+
"persona": data
|
| 312 |
+
}
|
| 313 |
+
return [persona] * int(num_personas)
|
| 314 |
+
except Exception as e:
|
| 315 |
+
add_log(f"Failed to load example persona: {e}")
|
| 316 |
+
|
| 317 |
if force_method == "DeepPersona":
|
| 318 |
add_log("Forcing DeepPersona generation...")
|
| 319 |
personas = []
|
|
|
|
| 323 |
if len(personas) >= int(num_personas): return personas[:int(num_personas)]
|
| 324 |
# fallback if some failed
|
| 325 |
num_personas = int(num_personas) - len(personas)
|
|
|
|
| 326 |
elif force_method == "TinyTroupe":
|
| 327 |
add_log("Forcing TinyTroupe generation...")
|
| 328 |
return generate_personas_from_tiny_factory(theme, customer_profile, num_personas)
|
|
|
|
| 625 |
|
| 626 |
return [f"Task {i+1} for {theme} (Manual fallback)" for i in range(10)]
|
| 627 |
|
| 628 |
+
def handle_generate(theme, customer_profile, num_personas, method, example_file):
|
| 629 |
try:
|
| 630 |
yield "Generating tasks...", None, None
|
| 631 |
tasks = generate_tasks(theme, customer_profile)
|
| 632 |
|
| 633 |
yield "Selecting or creating personas...", tasks, None
|
| 634 |
+
personas = select_or_create_personas(theme, customer_profile, num_personas, force_method=method, example_file=example_file)
|
| 635 |
|
| 636 |
yield "Generation complete!", tasks, personas
|
| 637 |
except Exception as e:
|
|
|
|
| 1019 |
return f"Error reading template: {e}"
|
| 1020 |
|
| 1021 |
prompt = template.replace("{{selected_solutions}}", selected_solutions_json)
|
| 1022 |
+
prompt = prompt.replace("{{url}}", "the analyzed website")
|
| 1023 |
prompt = prompt.replace("{{analysis_report}}", "See previous activities in this session")
|
| 1024 |
prompt = prompt.replace("{{report_id}}", session_id[:8])
|
| 1025 |
+
prompt = prompt.replace("{{screenshots_dir}}", f"user_experience_reports/screenshots/{session_id[:8]}")
|
| 1026 |
|
| 1027 |
headers = {
|
| 1028 |
"X-Goog-Api-Key": ANALYSIS_API_KEY,
|
|
|
|
| 1130 |
theme_input = gr.Textbox(label="Theme", placeholder="e.g., Communication, Purchase decisions, Information gathering")
|
| 1131 |
profile_input = gr.Textbox(label="Customer Profile Description", placeholder="Describe the target customer...")
|
| 1132 |
num_personas_input = gr.Number(label="Number of Personas", value=1, precision=0)
|
| 1133 |
+
persona_method = gr.Radio(["Example Persona", "TinyTroupe", "DeepPersona"], label="Persona Generation Method", value="TinyTroupe")
|
| 1134 |
+
example_persona_select = gr.Dropdown(label="Select Example Persona", choices=get_example_personas(), visible=False)
|
| 1135 |
url_input = gr.Textbox(label="Target URL", value="https://example.com")
|
| 1136 |
generate_btn = gr.Button("Generate Personas & Tasks")
|
| 1137 |
|
| 1138 |
+
def update_method_visibility(method):
|
| 1139 |
+
return gr.update(visible=(method == "Example Persona"))
|
| 1140 |
+
|
| 1141 |
+
persona_method.change(fn=update_method_visibility, inputs=[persona_method], outputs=[example_persona_select])
|
| 1142 |
+
|
| 1143 |
with gr.Column():
|
| 1144 |
status_output = gr.Textbox(label="Status", interactive=False)
|
| 1145 |
task_list_display = gr.JSON(label="Tasks")
|
|
|
|
| 1385 |
# Event handlers
|
| 1386 |
generate_btn.click(
|
| 1387 |
fn=handle_generate,
|
| 1388 |
+
inputs=[theme_input, profile_input, num_personas_input, persona_method, example_persona_select],
|
| 1389 |
outputs=[status_output, task_list_display, persona_display]
|
| 1390 |
)
|
| 1391 |
|
verify_report_viewer.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
from playwright.async_api import async_playwright
|
| 3 |
+
|
| 4 |
+
async def run():
|
| 5 |
+
async with async_playwright() as p:
|
| 6 |
+
browser = await p.chromium.launch()
|
| 7 |
+
page = await browser.new_page()
|
| 8 |
+
await page.goto("http://localhost:7860")
|
| 9 |
+
|
| 10 |
+
# Wait for Gradio to load
|
| 11 |
+
await page.wait_for_selector("button:has-text('Report Viewer')", state='visible')
|
| 12 |
+
|
| 13 |
+
# Click Report Viewer
|
| 14 |
+
await page.evaluate("""
|
| 15 |
+
Array.from(document.querySelectorAll('button')).find(el => el.textContent === 'Report Viewer').click()
|
| 16 |
+
""")
|
| 17 |
+
await asyncio.sleep(2)
|
| 18 |
+
|
| 19 |
+
# The Report Viewer might be empty if no reports exist.
|
| 20 |
+
# I'll check if there's any content or at least the dropdown.
|
| 21 |
+
await page.screenshot(path="/home/jules/verification/report_viewer_tab.png")
|
| 22 |
+
|
| 23 |
+
await browser.close()
|
| 24 |
+
|
| 25 |
+
asyncio.run(run())
|
verify_tabs.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
from playwright.async_api import async_playwright
|
| 3 |
+
|
| 4 |
+
async def run():
|
| 5 |
+
async with async_playwright() as p:
|
| 6 |
+
browser = await p.chromium.launch()
|
| 7 |
+
page = await browser.new_page()
|
| 8 |
+
await page.goto("http://localhost:7860")
|
| 9 |
+
|
| 10 |
+
# Wait for Gradio to load
|
| 11 |
+
await page.wait_for_selector("button:has-text('Average User Journey Heatmaps')", state='visible')
|
| 12 |
+
|
| 13 |
+
async def click_tab(name):
|
| 14 |
+
print(f"Clicking {name}...")
|
| 15 |
+
await page.evaluate(f"""
|
| 16 |
+
const btns = Array.from(document.querySelectorAll('button')).filter(el => el.textContent.includes('{name}'));
|
| 17 |
+
btns.forEach(btn => btn.click());
|
| 18 |
+
""")
|
| 19 |
+
await asyncio.sleep(2)
|
| 20 |
+
|
| 21 |
+
# Screenshot Heatmaps tab
|
| 22 |
+
await click_tab('Average User Journey Heatmaps')
|
| 23 |
+
await page.screenshot(path="/home/jules/verification/heatmaps_tab.png")
|
| 24 |
+
|
| 25 |
+
# Screenshot Agents.txt tab
|
| 26 |
+
await click_tab('Agents.txt')
|
| 27 |
+
await page.screenshot(path="/home/jules/verification/agents_tab.png")
|
| 28 |
+
|
| 29 |
+
# Screenshot Full New UI tab
|
| 30 |
+
await click_tab('Full New UI')
|
| 31 |
+
await page.screenshot(path="/home/jules/verification/full_ui_tab.png")
|
| 32 |
+
|
| 33 |
+
await browser.close()
|
| 34 |
+
|
| 35 |
+
asyncio.run(run())
|