Spaces:
Sleeping
Sleeping
File size: 5,693 Bytes
074e8ce 0df8486 074e8ce 0df8486 074e8ce b7e3c16 2021484 074e8ce 5cfd2e1 074e8ce 9783711 074e8ce 2021484 074e8ce 2021484 f2c5e84 2021484 f2c5e84 5420911 2021484 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
"""Modal deployment configuration for Explainor.
Deploy with: modal deploy modal_app.py
Run locally: modal serve modal_app.py
"""
import modal
# Define the Modal app
app = modal.App("explainor-v6")
# Create image with dependencies
image = (
modal.Image.debian_slim(python_version="3.11")
.pip_install(
"gradio==4.44.1", # Use older stable version without aggressive SSE
"elevenlabs>=1.0.0",
"httpx>=0.25.0",
"python-dotenv>=1.0.0",
"fastapi",
"uvicorn",
)
.add_local_dir("src", remote_path="/app/src", copy=True)
)
@app.function(
image=image,
secrets=[
modal.Secret.from_name("nebius-api-key"),
modal.Secret.from_name("elevenlabs-api-key"),
],
timeout=600,
scaledown_window=300,
)
@modal.web_server(port=7860, startup_timeout=120)
def serve():
"""Serve the Gradio app via web_server."""
import subprocess
import sys
import os
os.chdir("/app")
sys.path.insert(0, "/app")
# Write a standalone gradio script
script = '''
import sys
sys.path.insert(0, "/app")
import os
import tempfile
import gradio as gr
from src.personas import get_persona_names, get_persona
from src.agent import run_agent
from src.tts import generate_speech
def format_sources(sources):
if not sources:
return "*No external sources used*"
md = ""
for i, src in enumerate(sources, 1):
if src.get("url"):
md += f"{i}. [{src['title']}]({src['url']})\\n"
else:
md += f"{i}. {src['title']} ({src.get('source', 'General')})\\n"
return md
def format_mcp_tools(tools):
if not tools:
return "*No tools used*"
md = "**Agent Tool Calls:**\\n\\n"
for tool in tools:
md += f"| {tool['icon']} | `{tool['name']}` | {tool['desc']} |\\n"
return md
def explain_topic(topic, persona_name, audience=""):
import traceback
if not topic.strip():
return "Please enter a topic!", "", "", ""
if not persona_name:
persona_name = "5-Year-Old"
# Check API key
nebius_key = os.getenv("NEBIUS_API_KEY")
if not nebius_key:
available_keys = [k for k in os.environ.keys() if "KEY" in k or "API" in k or "NEBIUS" in k]
return f"Error: NEBIUS_API_KEY not found. Available: {available_keys}", "", "", ""
steps_log = []
explanation = ""
sources = []
mcp_tools = []
try:
for update in run_agent(topic, persona_name, audience):
if update["type"] == "step":
steps_log.append(f"**{update['title']}**\\n{update['content']}")
if update["step"] == "research_done" and "sources" in update:
sources = update["sources"]
elif update["type"] == "result":
explanation = update["explanation"]
sources = update.get("sources", sources)
mcp_tools = update.get("mcp_tools", [])
except Exception as e:
return f"Error: {str(e)}\\n\\n{traceback.format_exc()}", "", "\\n\\n---\\n\\n".join(steps_log), ""
return explanation, format_sources(sources), "\\n\\n---\\n\\n".join(steps_log), format_mcp_tools(mcp_tools)
def generate_audio(explanation, persona_name):
if not explanation or not explanation.strip():
return None
if not persona_name:
persona_name = "5-Year-Old"
try:
persona = get_persona(persona_name)
audio_bytes = generate_speech(explanation, persona["voice_id"], persona.get("voice_settings"))
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f:
f.write(audio_bytes)
return f.name
except Exception as e:
raise gr.Error(f"Audio generation failed: {str(e)}")
# Get persona names as a static list
persona_names = list(get_persona_names())
with gr.Blocks(title="Explainor") as demo:
gr.Markdown("# Explainor\\n### Learn anything through the voice of your favorite characters!")
with gr.Row():
topic_input = gr.Textbox(label="Topic", placeholder="e.g., Quantum Computing")
persona_dropdown = gr.Dropdown(choices=persona_names, value="5-Year-Old", label="Persona")
audience_dropdown = gr.Dropdown(
choices=["Just me", "Confused grandmother", "Skeptical robot", "Alien"],
value="Just me",
label="Audience"
)
explain_btn = gr.Button("Explain!", variant="primary")
explanation_output = gr.Textbox(label="Explanation", lines=6)
read_aloud_btn = gr.Button("Read Aloud")
audio_output = gr.Audio(label="Listen", type="filepath", autoplay=True)
with gr.Accordion("Tools", open=False):
mcp_output = gr.Markdown("")
with gr.Accordion("Sources", open=False):
sources_output = gr.Markdown("")
with gr.Accordion("Trace", open=False):
steps_output = gr.Markdown("")
def do_explain(topic, persona, audience):
aud = "" if "Just me" in audience else audience
return explain_topic(topic, persona, aud)
explain_btn.click(
fn=do_explain,
inputs=[topic_input, persona_dropdown, audience_dropdown],
outputs=[explanation_output, sources_output, steps_output, mcp_output],
)
read_aloud_btn.click(
fn=generate_audio,
inputs=[explanation_output, persona_dropdown],
outputs=[audio_output],
)
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
'''
# Write to file and run
with open("/app/run_gradio.py", "w") as f:
f.write(script)
# Run the script with environment variables
env = os.environ.copy()
subprocess.Popen([sys.executable, "/app/run_gradio.py"], env=env)
|