Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 6 additions & 92 deletions acestep/ui/gradio/events/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
TrainingWiringContext,
build_mode_ui_outputs,
register_generation_metadata_handlers,
register_generation_mode_handlers,
register_generation_service_handlers,
)
from acestep.ui.gradio.i18n import t
Expand Down Expand Up @@ -42,100 +43,13 @@ def setup_event_handlers(demo, dit_handler, llm_handler, dataset_handler, datase
auto_checkbox_outputs=auto_checkbox_outputs,
)

# ========== Generation Mode Change ==========
generation_section["generation_mode"].change(
fn=lambda mode, prev: gen_h.handle_generation_mode_change(mode, prev, llm_handler),
inputs=[
generation_section["generation_mode"],
generation_section["previous_generation_mode"],
],
outputs=mode_ui_outputs
)

# ========== Extract Mode: Auto-fill caption from track_name ==========
generation_section["track_name"].change(
fn=gen_h.handle_extract_track_name_change,
inputs=[
generation_section["track_name"],
generation_section["generation_mode"],
],
outputs=[generation_section["captions"]],
)

# Validate source audio eagerly so users get immediate feedback on invalid files.
generation_section["src_audio"].change(
fn=lambda src_audio: gen_h.validate_uploaded_audio_file(src_audio, "source"),
inputs=[generation_section["src_audio"]],
outputs=[generation_section["src_audio"]],
register_generation_mode_handlers(
wiring_context,
mode_ui_outputs=mode_ui_outputs,
auto_checkbox_inputs=auto_checkbox_inputs,
auto_checkbox_outputs=auto_checkbox_outputs,
)

# ========== Extract/Lego Mode: Auto-fill audio_duration from src_audio ==========
generation_section["src_audio"].change(
fn=gen_h.handle_extract_src_audio_change,
inputs=[
generation_section["src_audio"],
generation_section["generation_mode"],
],
outputs=[generation_section["audio_duration"]],
)

# ========== Simple Mode Instrumental Checkbox ==========
# When instrumental is checked, disable vocal language and set to ["unknown"]
generation_section["simple_instrumental_checkbox"].change(
fn=gen_h.handle_simple_instrumental_change,
inputs=[generation_section["simple_instrumental_checkbox"]],
outputs=[generation_section["simple_vocal_language"]]
)

# ========== Random Description Button ==========
generation_section["random_desc_btn"].click(
fn=gen_h.load_random_simple_description,
inputs=[],
outputs=[
generation_section["simple_query_input"],
generation_section["simple_instrumental_checkbox"],
generation_section["simple_vocal_language"],
]
)

# ========== Create Sample Button (Simple Mode) ==========
# Note: cfg_scale and negative_prompt are not supported in create_sample mode
generation_section["create_sample_btn"].click(
fn=lambda query, instrumental, vocal_lang, temp, top_k, top_p, debug: gen_h.handle_create_sample(
llm_handler, query, instrumental, vocal_lang, temp, top_k, top_p, debug
),
inputs=[
generation_section["simple_query_input"],
generation_section["simple_instrumental_checkbox"],
generation_section["simple_vocal_language"],
generation_section["lm_temperature"],
generation_section["lm_top_k"],
generation_section["lm_top_p"],
generation_section["constrained_decoding_debug"],
],
outputs=[
generation_section["captions"],
generation_section["lyrics"],
generation_section["bpm"],
generation_section["audio_duration"],
generation_section["key_scale"],
generation_section["vocal_language"],
generation_section["simple_vocal_language"],
generation_section["time_signature"],
generation_section["instrumental_checkbox"],
generation_section["generate_btn"],
generation_section["simple_sample_created"],
generation_section["think_checkbox"],
results_section["is_format_caption_state"],
results_section["status_output"],
generation_section["generation_mode"],
]
).then(
fn=gen_h.uncheck_auto_for_populated_fields,
inputs=auto_checkbox_inputs,
outputs=auto_checkbox_outputs,
)

# ========== Load/Save Metadata ==========
generation_section["load_file"].upload(
fn=lambda file_obj: gen_h.load_metadata(file_obj, llm_handler),
Expand Down
2 changes: 2 additions & 0 deletions acestep/ui/gradio/events/wiring/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
build_mode_ui_outputs,
)
from .generation_metadata_wiring import register_generation_metadata_handlers
from .generation_mode_wiring import register_generation_mode_handlers
from .generation_service_wiring import register_generation_service_handlers

__all__ = [
Expand All @@ -21,5 +22,6 @@
"build_auto_checkbox_outputs",
"build_mode_ui_outputs",
"register_generation_metadata_handlers",
"register_generation_mode_handlers",
"register_generation_service_handlers",
]
27 changes: 20 additions & 7 deletions acestep/ui/gradio/events/wiring/decomposition_contract_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Regression tests for PR2 wiring decomposition contracts.
"""Regression tests for event wiring decomposition contracts.

These tests validate source-level delegation in
``acestep.ui.gradio.events.__init__`` without importing Gradio dependencies.
Expand All @@ -10,6 +10,7 @@


_EVENTS_INIT_PATH = Path(__file__).resolve().parents[1] / "__init__.py"
_MODE_WIRING_PATH = Path(__file__).resolve().with_name("generation_mode_wiring.py")


def _load_setup_event_handlers_node() -> ast.FunctionDef:
Expand All @@ -23,6 +24,17 @@ def _load_setup_event_handlers_node() -> ast.FunctionDef:
raise AssertionError("setup_event_handlers not found")


def _load_generation_mode_wiring_node() -> ast.FunctionDef:
"""Return the AST node for ``register_generation_mode_handlers``."""

source = _MODE_WIRING_PATH.read_text(encoding="utf-8")
module = ast.parse(source)
for node in module.body:
if isinstance(node, ast.FunctionDef) and node.name == "register_generation_mode_handlers":
return node
raise AssertionError("register_generation_mode_handlers not found")


def _call_name(node: ast.AST) -> str | None:
"""Extract a simple function name from a call node target."""

Expand All @@ -34,10 +46,10 @@ def _call_name(node: ast.AST) -> str | None:


class DecompositionContractTests(unittest.TestCase):
"""Verify delegation contracts introduced in PR2 wiring extraction."""
"""Verify delegation contracts introduced in PR2/PR3 wiring extraction."""

def test_setup_event_handlers_uses_generation_wiring_helpers(self):
"""setup_event_handlers should delegate service/metadata registration."""
"""setup_event_handlers should delegate service/metadata/mode registration."""

setup_node = _load_setup_event_handlers_node()
call_names = []
Expand All @@ -49,15 +61,16 @@ def test_setup_event_handlers_uses_generation_wiring_helpers(self):

self.assertIn("register_generation_service_handlers", call_names)
self.assertIn("register_generation_metadata_handlers", call_names)
self.assertIn("register_generation_mode_handlers", call_names)
self.assertIn("build_mode_ui_outputs", call_names)

def test_generation_mode_change_uses_mode_ui_outputs_variable(self):
"""generation_mode change handler should still output mode_ui_outputs."""
def test_generation_mode_wiring_uses_mode_ui_outputs_variable(self):
"""Mode wiring helper should bind generation_mode outputs to mode_ui_outputs."""

setup_node = _load_setup_event_handlers_node()
wiring_node = _load_generation_mode_wiring_node()
found_mode_change_output_binding = False

for node in ast.walk(setup_node):
for node in ast.walk(wiring_node):
if not isinstance(node, ast.Call):
continue
if not isinstance(node.func, ast.Attribute) or node.func.attr != "change":
Expand Down
115 changes: 115 additions & 0 deletions acestep/ui/gradio/events/wiring/generation_mode_wiring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"""Generation mode and simple-mode event wiring helpers.

This module contains mode-switch and simple-mode related handlers to keep
``events.__init__`` as a thin facade.
"""

from typing import Any, Sequence

from .. import generation_handlers as gen_h
from .context import GenerationWiringContext


def register_generation_mode_handlers(
context: GenerationWiringContext,
mode_ui_outputs: Sequence[Any],
auto_checkbox_inputs: Sequence[Any],
auto_checkbox_outputs: Sequence[Any],
) -> None:
"""Register generation mode and simple-mode handlers."""

generation_section = context.generation_section
results_section = context.results_section
llm_handler = context.llm_handler

# ========== Generation Mode Change ==========
generation_section["generation_mode"].change(
fn=lambda mode, prev: gen_h.handle_generation_mode_change(mode, prev, llm_handler),
inputs=[
generation_section["generation_mode"],
generation_section["previous_generation_mode"],
],
outputs=mode_ui_outputs,
)

# ========== Extract Mode: Auto-fill caption from track_name ==========
generation_section["track_name"].change(
fn=gen_h.handle_extract_track_name_change,
inputs=[
generation_section["track_name"],
generation_section["generation_mode"],
],
outputs=[generation_section["captions"]],
)

# Validate source audio eagerly so users get immediate feedback on invalid files.
generation_section["src_audio"].change(
fn=lambda src_audio: gen_h.validate_uploaded_audio_file(src_audio, "source"),
inputs=[generation_section["src_audio"]],
outputs=[generation_section["src_audio"]],
)

# ========== Extract/Lego Mode: Auto-fill audio_duration from src_audio ==========
generation_section["src_audio"].change(
fn=gen_h.handle_extract_src_audio_change,
inputs=[
generation_section["src_audio"],
generation_section["generation_mode"],
],
outputs=[generation_section["audio_duration"]],
)

# ========== Simple Mode Instrumental Checkbox ==========
generation_section["simple_instrumental_checkbox"].change(
fn=gen_h.handle_simple_instrumental_change,
inputs=[generation_section["simple_instrumental_checkbox"]],
outputs=[generation_section["simple_vocal_language"]],
)

# ========== Random Description Button ==========
generation_section["random_desc_btn"].click(
fn=gen_h.load_random_simple_description,
inputs=[],
outputs=[
generation_section["simple_query_input"],
generation_section["simple_instrumental_checkbox"],
generation_section["simple_vocal_language"],
],
)

# ========== Create Sample Button (Simple Mode) ==========
generation_section["create_sample_btn"].click(
fn=lambda query, instrumental, vocal_lang, temp, top_k, top_p, debug: gen_h.handle_create_sample(
llm_handler, query, instrumental, vocal_lang, temp, top_k, top_p, debug
),
inputs=[
generation_section["simple_query_input"],
generation_section["simple_instrumental_checkbox"],
generation_section["simple_vocal_language"],
generation_section["lm_temperature"],
generation_section["lm_top_k"],
generation_section["lm_top_p"],
generation_section["constrained_decoding_debug"],
],
outputs=[
generation_section["captions"],
generation_section["lyrics"],
generation_section["bpm"],
generation_section["audio_duration"],
generation_section["key_scale"],
generation_section["vocal_language"],
generation_section["simple_vocal_language"],
generation_section["time_signature"],
generation_section["instrumental_checkbox"],
generation_section["generate_btn"],
generation_section["simple_sample_created"],
generation_section["think_checkbox"],
results_section["is_format_caption_state"],
results_section["status_output"],
generation_section["generation_mode"],
],
).then(
fn=gen_h.uncheck_auto_for_populated_fields,
inputs=list(auto_checkbox_inputs),
outputs=list(auto_checkbox_outputs),
)