Spaces:
Running on Zero
Running on Zero
feat(ui): wire lyrics tab + cross-tab use-in-generate handoff (m4)
Browse files
app.py
CHANGED
|
@@ -261,6 +261,50 @@ def on_extend_click(
|
|
| 261 |
raise gr.Error(str(e)) from e
|
| 262 |
|
| 263 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
def on_edit_click(
|
| 265 |
source_audio,
|
| 266 |
sub_mode: str,
|
|
@@ -513,7 +557,35 @@ def build_app() -> gr.Blocks:
|
|
| 513 |
outputs=[e["output_audio"], e["output_meta"]],
|
| 514 |
)
|
| 515 |
with gr.Group(visible=False, elem_classes=["ams-tab-pane"]) as pane_lyrics:
|
| 516 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 517 |
|
| 518 |
panes = [pane_generate, pane_cover, pane_extend, pane_edit, pane_lyrics]
|
| 519 |
|
|
|
|
| 261 |
raise gr.Error(str(e)) from e
|
| 262 |
|
| 263 |
|
| 264 |
+
def on_draft_lyrics(
|
| 265 |
+
brief: str,
|
| 266 |
+
structure: str,
|
| 267 |
+
language: str,
|
| 268 |
+
tone: str,
|
| 269 |
+
verse_lines: float,
|
| 270 |
+
chorus_lines: float,
|
| 271 |
+
bridge_lines: float,
|
| 272 |
+
rhyme: str,
|
| 273 |
+
temperature: float,
|
| 274 |
+
top_p: float,
|
| 275 |
+
top_k: float,
|
| 276 |
+
max_new_tokens: float,
|
| 277 |
+
seed,
|
| 278 |
+
progress=gr.Progress(track_tqdm=True), # noqa: B008
|
| 279 |
+
):
|
| 280 |
+
"""Lyrics-mode click. Calls ``modes.lyrics(...)`` directly — no ACE-Step
|
| 281 |
+
pipeline is touched. Qwen 2.5 7B is its own lazy singleton inside
|
| 282 |
+
``lyrics_lm``; the first click triggers a ~4 GB MLX download (cached
|
| 283 |
+
afterwards) and ~30 s warm-up before the draft appears.
|
| 284 |
+
"""
|
| 285 |
+
try:
|
| 286 |
+
return modes.lyrics(
|
| 287 |
+
get_backend(),
|
| 288 |
+
params={
|
| 289 |
+
"brief": brief,
|
| 290 |
+
"structure": structure,
|
| 291 |
+
"language": language,
|
| 292 |
+
"tone": tone,
|
| 293 |
+
"verse_lines": int(verse_lines),
|
| 294 |
+
"chorus_lines": int(chorus_lines),
|
| 295 |
+
"bridge_lines": int(bridge_lines),
|
| 296 |
+
"rhyme": rhyme,
|
| 297 |
+
"temperature": float(temperature),
|
| 298 |
+
"top_p": float(top_p),
|
| 299 |
+
"top_k": int(top_k),
|
| 300 |
+
"max_new_tokens": int(max_new_tokens),
|
| 301 |
+
"seed": int(seed) if seed is not None else None,
|
| 302 |
+
},
|
| 303 |
+
)
|
| 304 |
+
except ValueError as e:
|
| 305 |
+
raise gr.Error(str(e)) from e
|
| 306 |
+
|
| 307 |
+
|
| 308 |
def on_edit_click(
|
| 309 |
source_audio,
|
| 310 |
sub_mode: str,
|
|
|
|
| 557 |
outputs=[e["output_audio"], e["output_meta"]],
|
| 558 |
)
|
| 559 |
with gr.Group(visible=False, elem_classes=["ams-tab-pane"]) as pane_lyrics:
|
| 560 |
+
lyr = ui.build_lyrics_tab()
|
| 561 |
+
lyr["draft_btn"].click(
|
| 562 |
+
fn=on_draft_lyrics,
|
| 563 |
+
inputs=[
|
| 564 |
+
lyr["brief"],
|
| 565 |
+
lyr["structure"],
|
| 566 |
+
lyr["language"],
|
| 567 |
+
lyr["tone"],
|
| 568 |
+
lyr["verse_lines"],
|
| 569 |
+
lyr["chorus_lines"],
|
| 570 |
+
lyr["bridge_lines"],
|
| 571 |
+
lyr["rhyme"],
|
| 572 |
+
lyr["temperature"],
|
| 573 |
+
lyr["top_p"],
|
| 574 |
+
lyr["top_k"],
|
| 575 |
+
lyr["max_new_tokens"],
|
| 576 |
+
lyr["seed"],
|
| 577 |
+
],
|
| 578 |
+
outputs=[lyr["lyrics_output"], lyr["meta_output"]],
|
| 579 |
+
)
|
| 580 |
+
# Cross-tab "Use these in Generate" — pipes the drafted
|
| 581 |
+
# text straight into the Generate tab's lyrics textbox.
|
| 582 |
+
# Both panes were declared inside the same gr.Blocks
|
| 583 |
+
# context so referencing g["lyrics"] across panes works.
|
| 584 |
+
lyr["use_in_generate_btn"].click(
|
| 585 |
+
fn=lambda txt: txt,
|
| 586 |
+
inputs=[lyr["lyrics_output"]],
|
| 587 |
+
outputs=[g["lyrics"]],
|
| 588 |
+
)
|
| 589 |
|
| 590 |
panes = [pane_generate, pane_cover, pane_extend, pane_edit, pane_lyrics]
|
| 591 |
|
theme.py
CHANGED
|
@@ -861,6 +861,59 @@ main, .contain {{
|
|
| 861 |
padding:0 12px 12px 12px !important;
|
| 862 |
}}
|
| 863 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 864 |
/* Hide Gradio footer + the floating "Use via API" / settings panel */
|
| 865 |
footer {{ display:none !important; }}
|
| 866 |
.show-api {{ display:none !important; }}
|
|
|
|
| 861 |
padding:0 12px 12px 12px !important;
|
| 862 |
}}
|
| 863 |
|
| 864 |
+
/* ============================================================
|
| 865 |
+
* Lyrics tab (M4)
|
| 866 |
+
* Mono draft textbox + secondary "Use in Generate" CTA. The LM-params
|
| 867 |
+
* accordion reuses the same chrome as the LoRA + experimental
|
| 868 |
+
* accordions so the bordered section header reads consistently.
|
| 869 |
+
* ============================================================ */
|
| 870 |
+
.ams-content .ams-lyrics-output textarea {{
|
| 871 |
+
font-family: {FONT_MONO} !important;
|
| 872 |
+
font-size: 12px !important;
|
| 873 |
+
line-height: 1.6 !important;
|
| 874 |
+
min-height: 280px !important;
|
| 875 |
+
background:{SURFACE_STRONG} !important;
|
| 876 |
+
border:1px solid {BORDER} !important;
|
| 877 |
+
color:{INK} !important;
|
| 878 |
+
}}
|
| 879 |
+
.ams-content .ams-lyrics-output textarea::placeholder {{
|
| 880 |
+
font-style: italic;
|
| 881 |
+
}}
|
| 882 |
+
.ams-content .ams-lyrics-use-btn {{
|
| 883 |
+
margin-top: 6px !important;
|
| 884 |
+
}}
|
| 885 |
+
.ams-content .ams-lm-accordion {{
|
| 886 |
+
border:1px solid {BORDER} !important;
|
| 887 |
+
border-radius:3px !important;
|
| 888 |
+
background:{SURFACE_STRONG} !important;
|
| 889 |
+
margin-top:10px !important;
|
| 890 |
+
padding:0 !important;
|
| 891 |
+
}}
|
| 892 |
+
.ams-content .ams-lm-accordion > .label-wrap,
|
| 893 |
+
.ams-content .ams-lm-accordion summary,
|
| 894 |
+
.ams-content .ams-lm-accordion > button {{
|
| 895 |
+
font-family: {FONT_MONO} !important;
|
| 896 |
+
font-size:10px !important;
|
| 897 |
+
letter-spacing:0.08em !important;
|
| 898 |
+
text-transform:uppercase !important;
|
| 899 |
+
color:{INK_MUTED} !important;
|
| 900 |
+
padding:10px 12px !important;
|
| 901 |
+
background:transparent !important;
|
| 902 |
+
border:none !important;
|
| 903 |
+
}}
|
| 904 |
+
.ams-content .ams-lm-accordion > .label-wrap span,
|
| 905 |
+
.ams-content .ams-lm-accordion summary span,
|
| 906 |
+
.ams-content .ams-lm-accordion > button span {{
|
| 907 |
+
color:{INK_MUTED} !important;
|
| 908 |
+
font-family: {FONT_MONO} !important;
|
| 909 |
+
font-size:10px !important;
|
| 910 |
+
letter-spacing:0.08em !important;
|
| 911 |
+
text-transform:uppercase !important;
|
| 912 |
+
}}
|
| 913 |
+
.ams-content .ams-lm-accordion > div:not(.label-wrap):not(summary) {{
|
| 914 |
+
padding:0 12px 12px 12px !important;
|
| 915 |
+
}}
|
| 916 |
+
|
| 917 |
/* Hide Gradio footer + the floating "Use via API" / settings panel */
|
| 918 |
footer {{ display:none !important; }}
|
| 919 |
.show-api {{ display:none !important; }}
|
ui.py
CHANGED
|
@@ -418,3 +418,137 @@ def build_edit_tab() -> dict[str, gr.components.Component]:
|
|
| 418 |
_build_output_panel(components)
|
| 419 |
|
| 420 |
return components
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
_build_output_panel(components)
|
| 419 |
|
| 420 |
return components
|
| 421 |
+
|
| 422 |
+
|
| 423 |
+
def build_lyrics_tab() -> dict[str, gr.components.Component]:
|
| 424 |
+
"""Lyrics tab body: Qwen 2.5 7B drafts structurally-tagged lyrics.
|
| 425 |
+
|
| 426 |
+
Compact 2-column row: form on the left (brief / structure / language /
|
| 427 |
+
line counts / tone / rhyme + collapsed LM-params accordion), output on
|
| 428 |
+
the right (read-only multi-line textbox + ``Use these in Generate``
|
| 429 |
+
cross-tab CTA + bordered JSON metadata panel).
|
| 430 |
+
|
| 431 |
+
The output textbox carries ``elem_classes=["ams-lyrics-output"]`` so
|
| 432 |
+
the Brutalist Mono treatment in ``theme.CSS`` (mono font, 12 px,
|
| 433 |
+
280 px min-height) applies. The "Use in Generate" button is tagged
|
| 434 |
+
``ams-lyrics-use-btn`` so it gets a small top margin instead of
|
| 435 |
+
sitting flush against the textbox.
|
| 436 |
+
|
| 437 |
+
Does NOT include the LoRA accordion — Qwen-7B has no LoRA picker and
|
| 438 |
+
the audio-mode LoRA semantics don't apply here.
|
| 439 |
+
"""
|
| 440 |
+
c: dict[str, gr.components.Component] = {}
|
| 441 |
+
with gr.Row():
|
| 442 |
+
# --- FORM column (left) ---
|
| 443 |
+
with gr.Column(scale=12):
|
| 444 |
+
c["brief"] = gr.Textbox(
|
| 445 |
+
label="Brief",
|
| 446 |
+
lines=4,
|
| 447 |
+
placeholder=("Describe the song. Tone, mood, references, specific images, lines to avoid…"),
|
| 448 |
+
)
|
| 449 |
+
with gr.Row():
|
| 450 |
+
c["structure"] = gr.Textbox(
|
| 451 |
+
label="Structure",
|
| 452 |
+
value="intro, verse, chorus, verse, chorus, bridge, chorus, outro",
|
| 453 |
+
)
|
| 454 |
+
c["language"] = gr.Dropdown(
|
| 455 |
+
choices=["en", "zh", "ja", "ko", "es", "fr", "de"],
|
| 456 |
+
value="en",
|
| 457 |
+
label="Language",
|
| 458 |
+
)
|
| 459 |
+
with gr.Row():
|
| 460 |
+
c["verse_lines"] = gr.Slider(
|
| 461 |
+
minimum=2,
|
| 462 |
+
maximum=10,
|
| 463 |
+
value=6,
|
| 464 |
+
step=1,
|
| 465 |
+
label="Verse lines",
|
| 466 |
+
)
|
| 467 |
+
c["chorus_lines"] = gr.Slider(
|
| 468 |
+
minimum=2,
|
| 469 |
+
maximum=8,
|
| 470 |
+
value=4,
|
| 471 |
+
step=1,
|
| 472 |
+
label="Chorus lines",
|
| 473 |
+
)
|
| 474 |
+
c["bridge_lines"] = gr.Slider(
|
| 475 |
+
minimum=1,
|
| 476 |
+
maximum=6,
|
| 477 |
+
value=2,
|
| 478 |
+
step=1,
|
| 479 |
+
label="Bridge lines",
|
| 480 |
+
)
|
| 481 |
+
c["tone"] = gr.Textbox(
|
| 482 |
+
label="Tone / mood",
|
| 483 |
+
placeholder="euphoric, hypnotic, transcendent, not cheesy",
|
| 484 |
+
)
|
| 485 |
+
c["rhyme"] = gr.Radio(
|
| 486 |
+
choices=["strict", "loose", "none"],
|
| 487 |
+
value="loose",
|
| 488 |
+
label="Rhyme",
|
| 489 |
+
)
|
| 490 |
+
with gr.Accordion(
|
| 491 |
+
"LM parameters",
|
| 492 |
+
open=False,
|
| 493 |
+
elem_classes=["ams-lm-accordion"],
|
| 494 |
+
):
|
| 495 |
+
c["temperature"] = gr.Slider(
|
| 496 |
+
minimum=0.0,
|
| 497 |
+
maximum=2.0,
|
| 498 |
+
value=0.85,
|
| 499 |
+
step=0.05,
|
| 500 |
+
label="Temperature",
|
| 501 |
+
)
|
| 502 |
+
c["top_p"] = gr.Slider(
|
| 503 |
+
minimum=0.0,
|
| 504 |
+
maximum=1.0,
|
| 505 |
+
value=0.9,
|
| 506 |
+
step=0.05,
|
| 507 |
+
label="Top-p",
|
| 508 |
+
)
|
| 509 |
+
c["top_k"] = gr.Slider(
|
| 510 |
+
minimum=0,
|
| 511 |
+
maximum=200,
|
| 512 |
+
value=40,
|
| 513 |
+
step=1,
|
| 514 |
+
label="Top-k",
|
| 515 |
+
)
|
| 516 |
+
c["max_new_tokens"] = gr.Slider(
|
| 517 |
+
minimum=100,
|
| 518 |
+
maximum=2000,
|
| 519 |
+
value=600,
|
| 520 |
+
step=50,
|
| 521 |
+
label="Max new tokens",
|
| 522 |
+
)
|
| 523 |
+
c["seed"] = gr.Number(
|
| 524 |
+
value=42,
|
| 525 |
+
precision=0,
|
| 526 |
+
label="Seed",
|
| 527 |
+
)
|
| 528 |
+
c["draft_btn"] = gr.Button(
|
| 529 |
+
"▶ Draft lyrics",
|
| 530 |
+
variant="primary",
|
| 531 |
+
)
|
| 532 |
+
|
| 533 |
+
# --- OUTPUT column (right) ---
|
| 534 |
+
with gr.Column(scale=10):
|
| 535 |
+
# NOTE: gr.Textbox in Gradio 6.14 doesn't accept ``show_copy_button``
|
| 536 |
+
# (the kwarg landed in a later 6.x). The Brutalist Mono textbox already
|
| 537 |
+
# exposes a native selection + browser copy via Cmd-A / Cmd-C; the
|
| 538 |
+
# copy-button affordance is therefore a no-op miss here.
|
| 539 |
+
c["lyrics_output"] = gr.Textbox(
|
| 540 |
+
label="Draft",
|
| 541 |
+
lines=14,
|
| 542 |
+
interactive=False,
|
| 543 |
+
elem_classes=["ams-lyrics-output"],
|
| 544 |
+
)
|
| 545 |
+
c["use_in_generate_btn"] = gr.Button(
|
| 546 |
+
"↑ Use these in Generate",
|
| 547 |
+
variant="primary",
|
| 548 |
+
elem_classes=["ams-lyrics-use-btn"],
|
| 549 |
+
)
|
| 550 |
+
c["meta_output"] = gr.JSON(
|
| 551 |
+
label="Metadata",
|
| 552 |
+
elem_classes=["ams-out", "ams-out-meta"],
|
| 553 |
+
)
|
| 554 |
+
return c
|