Paper_Scraper / app.py
Viske's picture
Upload project files
48a71a2
Raw
History Blame Contribute Delete
7.48 kB
"""app.py β€” Gradio UI for BERTopic Multi-Agent Research. Zero if/else/for/while/try/except."""
import sys, os; sys.stdout.reconfigure(line_buffering=True)
from dotenv import load_dotenv
load_dotenv()
os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python"
import json, glob
print(">>> importing gradio...", flush=True)
import gradio as gr
print(">>> importing agents...", flush=True)
from agents import build_agent
from tools import PAPER_CACHE, OUTPUT_DIR, supabase
print(">>> building agent...", flush=True)
agent = build_agent()
_msg_count = 0
print(">>> agent ready!", flush=True)
def _pipeline(phase):
phases = [("β‘  Load", 1), ("β‘‘ Codes", 2), ("β‘’ Themes", 3), ("β‘£ Review", 4), ("β‘€ Names", 5), ("β‘€Β½ PAJAIS", 5.5), ("β‘₯ Report", 6)]
return " β†’ ".join(list(map(lambda p: f"**{p[0]}**" if p[1]==phase else (f"βœ… {p[0]}" if p[1]<phase else p[0]), phases)))
def _topic_rows(chat_id=None):
res = supabase.table("chats").select("topics_json").eq("id", chat_id).execute().data if chat_id else []
tops = res[0].get("topics_json") if res and res[0].get("topics_json") else []
return list(map(lambda t: [t["id"], t["label"], "; ".join(t.get("top_sentences",[])[:1])[:100], t["count"], len(t.get("top_papers",[])), "yes", "", ""], tops))
def _history():
return list(map(lambda r: f"[{r['id']}] {r['title']}", supabase.table("chats").select("id,title").order("created_at", desc=True).limit(20).execute().data))
def _latest_files():
return sorted(glob.glob(os.path.join(OUTPUT_DIR, "*")), key=os.path.getmtime, reverse=True)[:10] or None
def respond(message, chat_history):
global _msg_count; _msg_count += 1
text = (message or "").strip() or "digital social innovation and societal impact"
chat_id = supabase.table("chats").insert({"title": text[:50], "user_message": text, "bot_message": "Started..."}).execute().data[0]["id"]
chat_history = chat_history + [{"role":"user","content":text}, {"role":"assistant","content":"πŸ”„ **Dispatching agents...**\n\nSupervisor β†’ OpenAlex β†’ Tavily β†’ Scopus β†’ Validation β†’ BERTopic Analysis\n\n_This may take 30-60 seconds..._"}]
yield chat_history, "", _pipeline(2), _topic_rows(chat_id), _latest_files(), chat_id
result = agent.invoke({"messages":[{"role":"user","content":f"Research: {text}. The chat_id is {chat_id}. Search all databases, validate, run BERTopic, upload."}]}, config={"configurable":{"thread_id":f"t{_msg_count}"}})
chat_history[-1] = {"role":"assistant","content":result["messages"][-1].content}
yield chat_history, "", _pipeline(6), _topic_rows(chat_id), _latest_files(), chat_id
def submit_review(td):
edits = list(map(lambda r: f"Topic {r[0]}: {r[5]}, Rename='{r[6]}'", filter(lambda r: str(r[5]).lower()=="no" or str(r[6]).strip()!="", td)))
return "Review:\n" + "\n".join(edits)
def load_chart(name):
path = os.path.join(OUTPUT_DIR, str(name or ""))
fallback = "<div style='text-align:center;color:#64748b;padding:60px;background:#fff;border-radius:8px'>πŸ“Š Run a search first to generate BERTopic charts</div>"
return {True: lambda: "<iframe srcdoc='" + open(path,"r",encoding="utf-8").read().replace("'",'"') + "' width='100%' height='480' frameborder='0'></iframe>", False: lambda: fallback}[os.path.exists(path)]()
print(">>> fetching history...", flush=True)
def show_topic_papers(evt: gr.SelectData, chat_id_state):
if not chat_id_state: return []
row = evt.index[0]
chat = supabase.table("chats").select("topics_json").eq("id", chat_id_state).execute().data[0]
tops = chat.get("topics_json", []) if chat.get("topics_json") else []
if row >= len(tops): return []
papers = supabase.table("papers").select("title,web_link,date_of_publication,journal,no_of_citations,confidence_score").eq("topic_label", tops[row]["label"]).eq("chat_id", chat_id_state).execute().data
return list(map(lambda p: [p.get("title", ""), p.get("web_link", ""), p.get("date_of_publication", ""), p.get("journal", ""), p.get("no_of_citations", ""), p.get("confidence_score", "")], papers))
hist = _history()
print(f">>> {len(hist)} past sessions", flush=True)
print(">>> building UI...", flush=True)
with gr.Blocks(title="BERTopic β€” Digital Social Innovation") as demo:
gr.Markdown("# πŸ”¬ Topic Modelling β€” Agentic AI\n*Qwen 72B 🧠 Β· MiniLM Embeddings Β· Cosine Clustering Β· 384d Β· Braun & Clarke Thematic Analysis*")
pipeline_html = gr.Markdown(_pipeline(1))
gr.Markdown("πŸ’‘ **Multi-Agent Architecture:** Supervisor Agent orchestrates 4 workers (OpenAlex, Tavily, Scopus, Validation) + 1 analysis worker (BERTopic embedding β†’ clustering β†’ LLM labeling β†’ Supabase upload)")
chat_state = gr.State(None)
chatbot = gr.Chatbot(height=320, show_label=False)
with gr.Row():
msg = gr.Textbox(placeholder="e.g. 'digital social innovation and societal impact'", show_label=False, scale=9, container=False)
send = gr.Button("Send", scale=1, min_width=70)
with gr.Row():
cmd1 = gr.Button("β–Ά Digital Social Innovation", size="sm")
cmd2 = gr.Button("β–Ά AI in Healthcare", size="sm")
cmd3 = gr.Button("β–Ά Sustainable Tourism Tech", size="sm")
with gr.Tabs():
with gr.TabItem("πŸ“‹ Review Table"):
review_table = gr.Dataframe(headers=["#","Topic Label","Top Evidence","Sents","Papers","Approve","Rename To","Reasoning"], datatype=["number","str","str","number","number","str","str","str"], interactive=True)
submit_btn = gr.Button("βœ… Submit Review to Agent")
review_out = gr.Textbox(label="Review Status", interactive=False)
gr.Markdown("πŸ›‘ **STOP Gate** β€” Agent pauses here. Review each BERTopic cluster label. Edit Approve/Rename columns β†’ click Submit.")
gr.Markdown("πŸ“„ **Papers in Selected Topic**")
paper_table = gr.Dataframe(headers=["Title", "Source", "Year", "Journal", "Citations", "Relevance Score"], interactive=False)
with gr.TabItem("πŸ“Š Charts"):
chart_dd = gr.Dropdown(choices=["rq4_abstract_bars.html","rq4_abstract_heatmap.html","rq4_abstract_intertopic.html"], value="rq4_abstract_bars.html", label="Select Visualization")
chart_html = gr.HTML("<div style='text-align:center;color:#64748b;padding:60px;background:#fff;border-radius:8px'>πŸ“Š Charts appear here after BERTopic analysis</div>")
with gr.TabItem("πŸ“₯ Downloads"):
download_files = gr.File(label="Output Files (summaries.json, emb.npy, charts, CSV)", file_count="multiple", interactive=False)
history_dd = gr.Dropdown(choices=hist, label="πŸ“š Past Research Sessions (Supabase)", interactive=True)
msg.submit(respond, [msg, chatbot], [chatbot, msg, pipeline_html, review_table, download_files, chat_state])
send.click(respond, [msg, chatbot], [chatbot, msg, pipeline_html, review_table, download_files, chat_state])
submit_btn.click(submit_review, [review_table], [review_out])
review_table.select(show_topic_papers, inputs=[chat_state], outputs=[paper_table])
chart_dd.change(load_chart, [chart_dd], [chart_html])
cmd1.click(lambda: "digital social innovation and societal impact", outputs=[msg])
cmd2.click(lambda: "artificial intelligence in healthcare diagnosis", outputs=[msg])
cmd3.click(lambda: "sustainable tourism technology adoption", outputs=[msg])
print(">>> launching...", flush=True)
demo.launch(server_name="0.0.0.0")