PitchIn / app.py
Hayeonbang's picture
Use validated client rendering for public Gradio embed
a08bccb verified
Raw History Blame Contribute Delete
4.37 kB
"""Gradio entry point. PITCHIN_PREVIEW_ONLY=1 starts an explicitly marked UI preview."""
import os
from pathlib import Path
os.environ.setdefault("GRADIO_ANALYTICS_ENABLED", "False")
import gradio as gr
from audio_input import DEFAULT_QUESTION, MAX_SECONDS, prepare_input
APP_DIR = Path(__file__).resolve().parent
CSS = (APP_DIR / "gradio-embed.css").read_text(encoding="utf-8")
EMBED_JS = (APP_DIR / "embed-resize.js").read_text(encoding="utf-8")
def build_demo(engine=None):
def respond(audio, question):
try:
wave, question = prepare_input(audio, question)
except ValueError as exc:
raise gr.Error(str(exc)) from None
if engine is None:
raise gr.Error("This is a UI preview. Model inference is not connected yet.")
try:
return engine.generate(wave, question)
except Exception:
# Never return internal paths, tokens, or uploaded content in errors.
raise gr.Error("Could not finish this request. The GPU may be busy; try a shorter clip or try again shortly.") from None
with gr.Blocks(
title="PitchIn · Singing feedback", theme=gr.themes.Soft(primary_hue="teal", neutral_hue="slate"),
css=CSS, js=EMBED_JS, analytics_enabled=False, delete_cache=(300, 3600),
) as demo:
gr.Markdown("# PitchIn\nListen to your singing. Ask a question. Explore the feedback.\n\n"
"Natural-language singing coaching from community feedback.", elem_id="intro")
if engine is None:
gr.Markdown("**UI preview — model inference is not connected.**")
with gr.Row():
with gr.Column(scale=1):
audio = gr.Audio(sources=["upload", "microphone"], type="filepath", label="Your singing",
max_length=MAX_SECONDS)
gr.Markdown("Upload or record **1–60 seconds** of singing (up to 30 MB). Clear vocals work best.")
question = gr.Textbox(label="What would you like feedback on?", value=DEFAULT_QUESTION,
lines=3, max_lines=6, max_length=1000)
gr.Examples(examples=[
[DEFAULT_QUESTION], ["What should I focus on improving first?"],
["How is my pitch accuracy?"], ["What practice exercise would you suggest?"],
], inputs=[question], label="Try a question")
run = gr.Button("Get feedback", variant="primary")
with gr.Column(scale=1):
answer = gr.Textbox(label="PitchIn-Q feedback", placeholder="Your feedback will appear here.",
lines=15, interactive=False, elem_id="answer")
status = gr.Markdown()
with gr.Accordion("About this demo", open=False):
gr.Markdown("PitchIn-Q adapts Qwen2.5-Omni-7B using Focused QA and Overall Feedback targets "
"reconstructed from community feedback. English questions are recommended. "
"Each request uses the current clip and question; previous requests are not conversation history.\n\n"
"This is a research model. It can miss or misidentify issues in a recording. "
"Generated feedback does not establish an improvement in singing ability.\n\n"
"Uploads are processed for your request. This app does not add them to a training dataset "
"or save a response history. Temporary Gradio files are scheduled for cleanup after one hour.")
run.click(respond, inputs=[audio, question], outputs=[answer, status],
api_name="coach", concurrency_limit=1, concurrency_id="pitchin")
return demo.queue(max_size=12, default_concurrency_limit=1)
if __name__ == "__main__":
engine = None
if os.environ.get("PITCHIN_PREVIEW_ONLY") != "1":
from inference import PitchIn
engine = PitchIn() # Load on startup, outside ZeroGPU's decorated function.
build_demo(engine).launch(
server_name=os.environ.get("GRADIO_SERVER_NAME", "127.0.0.1"),
server_port=int(os.environ.get("GRADIO_SERVER_PORT", "7860")),
share=False, show_error=False, max_file_size="30mb",
ssr_mode=False, # Use the validated client renderer for iframe embedding.
)