Update app.py
Browse files
app.py
CHANGED
|
@@ -574,165 +574,94 @@ def youtube_search(query, max_results=10):
|
|
| 574 |
# =======================================
|
| 575 |
|
| 576 |
def run_playwright_action(action_data, prompt_generator, target_url):
|
| 577 |
-
"""
|
| 578 |
-
Executes real Playwright actions while sending visual signals
|
| 579 |
-
to the chat stream generator.
|
| 580 |
-
|
| 581 |
-
Args:
|
| 582 |
-
action_data (list): List of LLM-generated actions.
|
| 583 |
-
prompt_generator (generator): Generator used to stream data back to the frontend.
|
| 584 |
-
target_url (str): The URL to be visited.
|
| 585 |
-
"""
|
| 586 |
print(f"[AGENT] π Starting Playwright Automation on: {target_url}")
|
| 587 |
-
|
| 588 |
-
#
|
| 589 |
def send_frontend_signal(action, selector=None, text=""):
|
| 590 |
signal = {"agent_action": action, "selector": selector, "text": text}
|
| 591 |
-
|
| 592 |
-
prompt_generator.send(f"data: {json.dumps(signal)}\n\n")
|
| 593 |
time.sleep(0.05)
|
| 594 |
-
|
| 595 |
browser = None
|
|
|
|
| 596 |
try:
|
| 597 |
with sync_playwright() as p:
|
| 598 |
-
|
| 599 |
-
browser = p.chromium.launch()
|
| 600 |
page = browser.new_page()
|
| 601 |
|
| 602 |
-
|
| 603 |
-
|
|
|
|
|
|
|
| 604 |
page.wait_for_selector("body", timeout=10000)
|
| 605 |
time.sleep(1)
|
| 606 |
|
| 607 |
-
#
|
| 608 |
for step in action_data:
|
| 609 |
action_type = step["action"]
|
| 610 |
selector = step.get("selector")
|
| 611 |
text = step.get("text", "")
|
| 612 |
-
|
| 613 |
print(f"[AGENT] Executing: {action_type} on {selector or 'N/A'}")
|
| 614 |
|
| 615 |
if action_type == "click":
|
| 616 |
-
send_frontend_signal("start_visual_automation", selector, f"Clicking {selector}...")
|
| 617 |
-
page.wait_for_selector(selector, timeout=10000)
|
| 618 |
page.click(selector)
|
| 619 |
-
send_frontend_signal("click", selector)
|
| 620 |
time.sleep(2)
|
| 621 |
|
| 622 |
elif action_type == "type_text":
|
| 623 |
-
send_frontend_signal("start_visual_automation", selector, f"Typing
|
| 624 |
page.wait_for_selector(selector, timeout=10000)
|
| 625 |
-
page.fill(selector, "")
|
| 626 |
-
|
| 627 |
for char in text:
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
send_frontend_signal("type_char", selector, char)
|
| 631 |
time.sleep(0.01)
|
| 632 |
|
| 633 |
-
send_frontend_signal("type_text", selector, "Typing Complete")
|
| 634 |
time.sleep(1)
|
| 635 |
-
|
| 636 |
elif action_type == "scroll":
|
| 637 |
-
|
| 638 |
-
send_frontend_signal("start_visual_automation", "body", f"Scrolling to {
|
| 639 |
-
|
|
|
|
| 640 |
page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
|
| 641 |
-
elif
|
| 642 |
page.evaluate("window.scrollTo(0, 0)")
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
send_frontend_signal("scroll", "body",
|
| 647 |
time.sleep(1)
|
| 648 |
-
|
| 649 |
elif action_type == "wait":
|
| 650 |
wait_time = step.get("time", 1)
|
| 651 |
-
send_frontend_signal("start_visual_automation", "body", f"Waiting
|
| 652 |
time.sleep(wait_time)
|
| 653 |
|
| 654 |
-
|
| 655 |
-
# --- Capture Proof ---
|
| 656 |
page.screenshot(path="/tmp/agent_proof.png")
|
| 657 |
-
final_content = page.locator("body").inner_text()
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
return f"\n\n[AGENT PROOF] Action completed on {target_url}. Final page text snapshot:\n\n---\n{final_proof}\n---"
|
| 664 |
|
| 665 |
except Exception as e:
|
| 666 |
print(f"[AGENT] β Playwright Error: {e}")
|
| 667 |
-
send_frontend_signal("end_visual_automation")
|
| 668 |
return f"\n\n[AGENT PROOF] Automation failed on {target_url}: {e}"
|
|
|
|
| 669 |
finally:
|
| 670 |
if browser:
|
| 671 |
browser.close()
|
| 672 |
print("[AGENT] π Playwright Session Closed.")
|
| 673 |
# =========================
|
| 674 |
-
# π₯ DEEP RESEARCH MODE
|
| 675 |
-
# =========================
|
| 676 |
-
def deep_research_mode(user_query: str, history=None, num_sources=15):
|
| 677 |
-
"""
|
| 678 |
-
Workflow singkat:
|
| 679 |
-
1) Fetch sources.
|
| 680 |
-
2) Buat draft + final answer.
|
| 681 |
-
"""
|
| 682 |
-
wib = timezone(timedelta(hours=7))
|
| 683 |
-
now = datetime.now(wib)
|
| 684 |
-
|
| 685 |
-
# 1) Search
|
| 686 |
-
search_block = serpapi_search(user_query, num_results=num_sources)
|
| 687 |
-
|
| 688 |
-
# 2) System prompt + draft
|
| 689 |
-
system_prompt = """
|
| 690 |
-
You are an expert researcher.
|
| 691 |
-
Use the search results as evidence to create a comprehensive, well-structured, and referenced answer.
|
| 692 |
-
Include summary, analysis, comparisons, and actionable recommendations.
|
| 693 |
-
Write clearly and highlight key patterns or insights.
|
| 694 |
-
"""
|
| 695 |
-
initial_prompt = f"""
|
| 696 |
-
SYSTEM PROMPT:
|
| 697 |
-
{system_prompt}
|
| 698 |
-
|
| 699 |
-
Time: {now.strftime('%Y-%m-%d %H:%M:%S WIB')}
|
| 700 |
-
|
| 701 |
-
USER QUESTION:
|
| 702 |
-
{user_query}
|
| 703 |
-
|
| 704 |
-
SEARCH RESULTS:
|
| 705 |
-
{search_block}
|
| 706 |
-
|
| 707 |
-
Produce a comprehensive draft answer (long form).
|
| 708 |
-
"""
|
| 709 |
-
draft = call_chat_once(initial_prompt, history)
|
| 710 |
-
if not draft:
|
| 711 |
-
return "[DEEP-RESEARCH] β Failed to create draft."
|
| 712 |
-
|
| 713 |
-
# 3) Final Answer Preparation
|
| 714 |
-
final_prompt = f"""
|
| 715 |
-
FINAL ANSWER PREPARATION:
|
| 716 |
-
|
| 717 |
-
USER QUESTION:
|
| 718 |
-
{user_query}
|
| 719 |
-
|
| 720 |
-
DRAFT:
|
| 721 |
-
{draft}
|
| 722 |
-
|
| 723 |
-
INSTRUCTIONS:
|
| 724 |
-
- Provide a clear final answer.
|
| 725 |
-
- Include 3-4 line TL;DR at the top.
|
| 726 |
-
- List top 5 references (title + URL) from search results.
|
| 727 |
-
- Add actionable recommendations.
|
| 728 |
-
- End with 'END OF DEEP RESEARCH'.
|
| 729 |
-
"""
|
| 730 |
-
final_answer = call_chat_once(final_prompt, history)
|
| 731 |
-
if not final_answer:
|
| 732 |
-
return "[DEEP-RESEARCH] β Failed to produce final answer."
|
| 733 |
-
|
| 734 |
-
return final_answer
|
| 735 |
-
# =========================
|
| 736 |
# Chat Endpoint (Text + Voice)
|
| 737 |
# =========================
|
| 738 |
@app.route("/chat", methods=["POST"])
|
|
|
|
| 574 |
# =======================================
|
| 575 |
|
| 576 |
def run_playwright_action(action_data, prompt_generator, target_url):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 577 |
print(f"[AGENT] π Starting Playwright Automation on: {target_url}")
|
| 578 |
+
|
| 579 |
+
# Generator pengirim signal
|
| 580 |
def send_frontend_signal(action, selector=None, text=""):
|
| 581 |
signal = {"agent_action": action, "selector": selector, "text": text}
|
| 582 |
+
yield f"data: {json.dumps(signal)}\n\n"
|
|
|
|
| 583 |
time.sleep(0.05)
|
| 584 |
+
|
| 585 |
browser = None
|
| 586 |
+
|
| 587 |
try:
|
| 588 |
with sync_playwright() as p:
|
| 589 |
+
browser = p.chromium.launch()
|
|
|
|
| 590 |
page = browser.new_page()
|
| 591 |
|
| 592 |
+
# β¬
οΈ HARUS pakai yield from
|
| 593 |
+
yield from send_frontend_signal("start_visual_automation", "body", f"Visiting {target_url}...")
|
| 594 |
+
|
| 595 |
+
page.goto(target_url, wait_until="domcontentloaded")
|
| 596 |
page.wait_for_selector("body", timeout=10000)
|
| 597 |
time.sleep(1)
|
| 598 |
|
| 599 |
+
# === REAL ACTIONS ===
|
| 600 |
for step in action_data:
|
| 601 |
action_type = step["action"]
|
| 602 |
selector = step.get("selector")
|
| 603 |
text = step.get("text", "")
|
| 604 |
+
|
| 605 |
print(f"[AGENT] Executing: {action_type} on {selector or 'N/A'}")
|
| 606 |
|
| 607 |
if action_type == "click":
|
| 608 |
+
yield from send_frontend_signal("start_visual_automation", selector, f"Clicking {selector}...")
|
| 609 |
+
page.wait_for_selector(selector, timeout=10000)
|
| 610 |
page.click(selector)
|
| 611 |
+
yield from send_frontend_signal("click", selector)
|
| 612 |
time.sleep(2)
|
| 613 |
|
| 614 |
elif action_type == "type_text":
|
| 615 |
+
yield from send_frontend_signal("start_visual_automation", selector, f"Typing '{text[:20]}...'")
|
| 616 |
page.wait_for_selector(selector, timeout=10000)
|
| 617 |
+
page.fill(selector, "")
|
| 618 |
+
|
| 619 |
for char in text:
|
| 620 |
+
page.type(selector, char, delay=random.randint(5, 10))
|
| 621 |
+
yield from send_frontend_signal("type_char", selector, char)
|
|
|
|
| 622 |
time.sleep(0.01)
|
| 623 |
|
| 624 |
+
yield from send_frontend_signal("type_text", selector, "Typing Complete")
|
| 625 |
time.sleep(1)
|
| 626 |
+
|
| 627 |
elif action_type == "scroll":
|
| 628 |
+
target = step.get("target", "bottom")
|
| 629 |
+
yield from send_frontend_signal("start_visual_automation", "body", f"Scrolling to {target}...")
|
| 630 |
+
|
| 631 |
+
if target == "bottom":
|
| 632 |
page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
|
| 633 |
+
elif target == "top":
|
| 634 |
page.evaluate("window.scrollTo(0, 0)")
|
| 635 |
+
else:
|
| 636 |
+
page.locator(target).scroll_into_view_if_needed()
|
| 637 |
+
|
| 638 |
+
yield from send_frontend_signal("scroll", "body", target)
|
| 639 |
time.sleep(1)
|
| 640 |
+
|
| 641 |
elif action_type == "wait":
|
| 642 |
wait_time = step.get("time", 1)
|
| 643 |
+
yield from send_frontend_signal("start_visual_automation", "body", f"Waiting {wait_time}s...")
|
| 644 |
time.sleep(wait_time)
|
| 645 |
|
| 646 |
+
# === CAPTURE PROOF ===
|
|
|
|
| 647 |
page.screenshot(path="/tmp/agent_proof.png")
|
| 648 |
+
final_content = page.locator("body").inner_text()
|
| 649 |
+
proof = final_content[:1000]
|
| 650 |
+
|
| 651 |
+
yield from send_frontend_signal("end_visual_automation")
|
| 652 |
+
|
| 653 |
+
return f"\n\n[AGENT PROOF] Action completed on {target_url}.\n\n---\n{proof}\n---"
|
|
|
|
| 654 |
|
| 655 |
except Exception as e:
|
| 656 |
print(f"[AGENT] β Playwright Error: {e}")
|
| 657 |
+
yield from send_frontend_signal("end_visual_automation")
|
| 658 |
return f"\n\n[AGENT PROOF] Automation failed on {target_url}: {e}"
|
| 659 |
+
|
| 660 |
finally:
|
| 661 |
if browser:
|
| 662 |
browser.close()
|
| 663 |
print("[AGENT] π Playwright Session Closed.")
|
| 664 |
# =========================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 665 |
# Chat Endpoint (Text + Voice)
|
| 666 |
# =========================
|
| 667 |
@app.route("/chat", methods=["POST"])
|