Spaces:
Sleeping
Sleeping
| """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") |