Spaces:
Sleeping
Sleeping
from __future__ import annotations | |
import os | |
import json | |
import tempfile | |
import gradio as gr | |
from dotenv import load_dotenv | |
load_dotenv() | |
from genesis.pipeline import research_once | |
from genesis.providers import postprocess_summary, synthesize_tts | |
from genesis.graph import build_preview_graph_html | |
from genesis.graphdb import write_topic_and_papers | |
APP_TITLE = "GENESIS-AI β Synthetic Biology Deep Research (Safety-First)" | |
APP_DESC = ( | |
"High-level synthetic biology literature synthesis with citations. " | |
"This app NEVER produces operational protocols." | |
) | |
DEFAULT_POST = os.getenv("POSTPROCESSOR_DEFAULT", "none").lower() | |
DEFAULT_RERANK_MODEL = os.getenv("RERANK_MODEL", "mixedbread-ai/mxbai-rerank-large-v1") | |
async def run_pipeline( | |
query: str, | |
fast: bool, | |
postprocessor: str, | |
want_graph: bool, | |
state: dict, | |
) -> tuple[str, str, str, str | None, dict]: | |
""" | |
Orchestrates deep research + optional post-processing + optional graph preview. | |
Returns: (final_markdown, citations_markdown, json_blob, graph_html, state) | |
""" | |
out = await research_once(query, fast=fast, rerank_model=DEFAULT_RERANK_MODEL) | |
# Optional polish (Gemini/DeepSeek) β never add lab steps | |
if postprocessor and postprocessor != "none": | |
out["final_output"] = await postprocess_summary( | |
base_text=out.get("final_output") or "", | |
citations=out.get("citations", []), | |
engine=postprocessor, | |
) | |
# Keep state for TTS / graph writer | |
state = state or {} | |
state["final_text"] = out.get("final_output") or "" | |
state["citations"] = out.get("citations", []) | |
state["query"] = query | |
# Graph preview HTML | |
graph_html = build_preview_graph_html(state["citations"]) if want_graph else None | |
# Report | |
final_md = state["final_text"] if state["final_text"] else "_No output_" | |
# Citations list (robust string building) | |
cite_lines: list[str] = [] | |
for c in state["citations"]: | |
title = c.get("title") or "link" | |
url = c.get("url") or "" | |
cite_lines.append(f"- [{title}]({url})") | |
cites_md = "\n".join(cite_lines) if cite_lines else "_None detected_" | |
# JSON export | |
json_blob = json.dumps(out, indent=2) | |
return final_md, cites_md, json_blob, graph_html, state | |
async def do_tts(state: dict) -> tuple[str | None, str]: | |
"""Narrate the high-level summary via ElevenLabs.""" | |
text = (state or {}).get("final_text") or "" | |
if not text.strip(): | |
return None, "Nothing to narrate yet β run research first." | |
audio_bytes, mime = await synthesize_tts(text) | |
if not audio_bytes: | |
return None, "TTS not configured or failed. Ensure ELEVEN_LABS_API_KEY/VOICE_ID are set." | |
suffix = ".mp3" if (mime or "").find("mpeg") >= 0 else ".wav" | |
with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as f: | |
f.write(audio_bytes) | |
path = f.name | |
return path, "Narration ready." | |
async def do_graph_write(state: dict) -> str: | |
"""Write Topic -> Paper knowledge graph into Neo4j.""" | |
topic = (state or {}).get("query") or "Untitled Topic" | |
citations = (state or {}).get("citations") or [] | |
if not citations: | |
return "No citations present β run research first." | |
counts = await write_topic_and_papers(topic, citations) | |
return f"Wrote to Neo4j: nodes={counts.get('nodes',0)}, rels={counts.get('rels',0)}" | |
with gr.Blocks(theme=gr.themes.Soft(), fill_height=True) as demo: | |
gr.Markdown(f"# {APP_TITLE}") | |
gr.Markdown(APP_DESC) | |
state = gr.State({"final_text": "", "citations": [], "query": ""}) | |
with gr.Row(): | |
query = gr.Textbox( | |
label="Your high-level research request", | |
lines=5, | |
placeholder=( | |
"e.g., High-level synthesis of CRISPR base-editing trends in oncology (last 2 years). " | |
"Summarize mechanisms, targets, ethics, and provide citations." | |
), | |
) | |
with gr.Row(): | |
fast = gr.Checkbox(label="Fast mode (o4-mini-deep-research)", value=False) | |
post = gr.Dropdown( | |
label="Post-processor", | |
choices=["none", "gemini", "deepseek"], | |
value=DEFAULT_POST, | |
allow_custom_value=False, | |
) | |
want_graph = gr.Checkbox(label="Build graph preview", value=False) | |
go = gr.Button("Run Deep Research", variant="primary") | |
with gr.Tabs(): | |
with gr.Tab("Research Report"): | |
report = gr.Markdown() | |
with gr.Tab("Citations"): | |
citations = gr.Markdown() | |
with gr.Tab("JSON Export"): | |
json_out = gr.Code(language="json") | |
with gr.Tab("Graph Preview"): | |
graph_html = gr.HTML() | |
with gr.Tab("Graph Writer (Neo4j)"): | |
write_btn = gr.Button("Write Topic & Papers to Neo4j", variant="secondary") | |
write_status = gr.Markdown() | |
with gr.Tab("Narration (ElevenLabs)"): | |
tts_btn = gr.Button("Narrate Summary", variant="secondary") | |
tts_audio = gr.Audio(label="Narration", autoplay=False) | |
tts_status = gr.Markdown() | |
go.click( | |
fn=run_pipeline, | |
inputs=[query, fast, post, want_graph, state], | |
outputs=[report, citations, json_out, graph_html, state], | |
) | |
tts_btn.click(fn=do_tts, inputs=[state], outputs=[tts_audio, tts_status]) | |
write_btn.click(fn=do_graph_write, inputs=[state], outputs=[write_status]) | |
if __name__ == "__main__": | |
demo.launch() | |