Fix regen: single-event trigger + generator-safe early-exit
Browse filesTwo bugs causing regeneration to never reach Python:
1. clear-then-set fired TWO .change() events. The first (empty string)
caused the generator to hit a bare `return` — but Python generators
with a bare return raise StopIteration immediately, which Gradio 5
SSR interprets as an SSE stream error. This errored state blocked
the second (real value) call from completing.
Fix: remove the clear step entirely; use a timestamp suffix in the
trigger value so repeat clicks always produce a unique string and
React's change detection always fires.
2. Early-exit paths used `return tuple` inside a generator function.
In Python, any function containing `yield` is always a generator —
`return value` inside a generator is silently dropped (only
StopIteration is raised). Gradio 5 SSR then sees an empty stream.
Fix: change all early-exit paths to `yield ...; return` so Gradio
receives a proper final update before the stream closes.
Trigger format is now: "slot_id|seg_idx|timestamp|{stateJSON}"
All three handlers (TARO/MMA/HF) updated accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@@ -1568,7 +1568,10 @@ _GLOBAL_JS = """
|
|
| 1568 |
}
|
| 1569 |
|
| 1570 |
// Use native setter to bypass React's controlled-input tracking.
|
| 1571 |
-
//
|
|
|
|
|
|
|
|
|
|
| 1572 |
const desc = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')
|
| 1573 |
|| Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
|
| 1574 |
function setNative(val) {
|
|
@@ -1577,13 +1580,10 @@ _GLOBAL_JS = """
|
|
| 1577 |
input.dispatchEvent(new Event('input', {bubbles: true}));
|
| 1578 |
input.dispatchEvent(new Event('change', {bubbles: true}));
|
| 1579 |
}
|
| 1580 |
-
// Encode: "slot_id|seg_idx|{stateJSON}"
|
| 1581 |
-
const triggerVal = slot_id + '|' + idx + '|' + stateJson;
|
| 1582 |
-
setNative(
|
| 1583 |
-
|
| 1584 |
-
setNative(triggerVal);
|
| 1585 |
-
console.log('[fireRegen] fired trigger for', slot_id, 'seg', idx);
|
| 1586 |
-
}, 50);
|
| 1587 |
|
| 1588 |
const lbl = document.getElementById('wf_seglabel_' + slot_id);
|
| 1589 |
if (lbl) lbl.textContent = 'Regenerating Seg ' + (idx + 1) + '...';
|
|
@@ -1689,14 +1689,14 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 1689 |
print(f"[regen TARO] trigger_val_len={len(trigger_val) if trigger_val else 0} video={video!r}")
|
| 1690 |
if not trigger_val:
|
| 1691 |
print(f"[regen TARO] early-exit: trigger_val empty")
|
| 1692 |
-
|
| 1693 |
-
# Trigger format: "slot_id|seg_idx|{stateJSON}"
|
| 1694 |
-
parts = trigger_val.split("|",
|
| 1695 |
-
if len(parts) !=
|
| 1696 |
-
print(f"[regen TARO] early-exit: parts
|
| 1697 |
-
|
| 1698 |
seg_idx = int(parts[1])
|
| 1699 |
-
state_json = parts[
|
| 1700 |
print(f"[regen TARO] slot={_sid} seg_idx={seg_idx} state_json_len={len(state_json)}")
|
| 1701 |
lock = _get_slot_lock(_sid)
|
| 1702 |
with lock:
|
|
@@ -1781,14 +1781,14 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 1781 |
print(f"[regen MMA] trigger_val_len={len(trigger_val) if trigger_val else 0} video={video!r}")
|
| 1782 |
if not trigger_val:
|
| 1783 |
print(f"[regen MMA] early-exit: trigger_val empty")
|
| 1784 |
-
|
| 1785 |
-
# Trigger format: "slot_id|seg_idx|{stateJSON}"
|
| 1786 |
-
parts = trigger_val.split("|",
|
| 1787 |
-
if len(parts) !=
|
| 1788 |
-
print(f"[regen MMA] early-exit: parts
|
| 1789 |
-
|
| 1790 |
seg_idx = int(parts[1])
|
| 1791 |
-
state_json = parts[
|
| 1792 |
print(f"[regen MMA] slot={_sid} seg_idx={seg_idx} state_json_len={len(state_json)}")
|
| 1793 |
lock = _get_slot_lock(_sid)
|
| 1794 |
with lock:
|
|
@@ -1874,14 +1874,14 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 1874 |
print(f"[regen HF] trigger_val_len={len(trigger_val) if trigger_val else 0} video={video!r}")
|
| 1875 |
if not trigger_val:
|
| 1876 |
print(f"[regen HF] early-exit: trigger_val empty")
|
| 1877 |
-
|
| 1878 |
-
# Trigger format: "slot_id|seg_idx|{stateJSON}"
|
| 1879 |
-
parts = trigger_val.split("|",
|
| 1880 |
-
if len(parts) !=
|
| 1881 |
-
print(f"[regen HF] early-exit: parts
|
| 1882 |
-
|
| 1883 |
seg_idx = int(parts[1])
|
| 1884 |
-
state_json = parts[
|
| 1885 |
print(f"[regen HF] slot={_sid} seg_idx={seg_idx} state_json_len={len(state_json)}")
|
| 1886 |
lock = _get_slot_lock(_sid)
|
| 1887 |
with lock:
|
|
|
|
| 1568 |
}
|
| 1569 |
|
| 1570 |
// Use native setter to bypass React's controlled-input tracking.
|
| 1571 |
+
// We do NOT clear to '' first — that would fire a spurious .change() event
|
| 1572 |
+
// which (as a generator function) causes an SSE stream error that blocks
|
| 1573 |
+
// the real call. Instead we use a timestamp suffix to ensure uniqueness
|
| 1574 |
+
// so repeat clicks on the same segment always look like a new value.
|
| 1575 |
const desc = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')
|
| 1576 |
|| Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
|
| 1577 |
function setNative(val) {
|
|
|
|
| 1580 |
input.dispatchEvent(new Event('input', {bubbles: true}));
|
| 1581 |
input.dispatchEvent(new Event('change', {bubbles: true}));
|
| 1582 |
}
|
| 1583 |
+
// Encode: "slot_id|seg_idx|ts|{stateJSON}" (ts = timestamp for uniqueness)
|
| 1584 |
+
const triggerVal = slot_id + '|' + idx + '|' + Date.now() + '|' + stateJson;
|
| 1585 |
+
setNative(triggerVal);
|
| 1586 |
+
console.log('[fireRegen] fired trigger for', slot_id, 'seg', idx);
|
|
|
|
|
|
|
|
|
|
| 1587 |
|
| 1588 |
const lbl = document.getElementById('wf_seglabel_' + slot_id);
|
| 1589 |
if (lbl) lbl.textContent = 'Regenerating Seg ' + (idx + 1) + '...';
|
|
|
|
| 1689 |
print(f"[regen TARO] trigger_val_len={len(trigger_val) if trigger_val else 0} video={video!r}")
|
| 1690 |
if not trigger_val:
|
| 1691 |
print(f"[regen TARO] early-exit: trigger_val empty")
|
| 1692 |
+
yield gr.update(), gr.update(), gr.update(); return
|
| 1693 |
+
# Trigger format: "slot_id|seg_idx|timestamp|{stateJSON}"
|
| 1694 |
+
parts = trigger_val.split("|", 3)
|
| 1695 |
+
if len(parts) != 4 or parts[0] != _sid:
|
| 1696 |
+
print(f"[regen TARO] early-exit: parts={parts[:2]} expected slot={_sid!r}")
|
| 1697 |
+
yield gr.update(), gr.update(), gr.update(); return
|
| 1698 |
seg_idx = int(parts[1])
|
| 1699 |
+
state_json = parts[3]
|
| 1700 |
print(f"[regen TARO] slot={_sid} seg_idx={seg_idx} state_json_len={len(state_json)}")
|
| 1701 |
lock = _get_slot_lock(_sid)
|
| 1702 |
with lock:
|
|
|
|
| 1781 |
print(f"[regen MMA] trigger_val_len={len(trigger_val) if trigger_val else 0} video={video!r}")
|
| 1782 |
if not trigger_val:
|
| 1783 |
print(f"[regen MMA] early-exit: trigger_val empty")
|
| 1784 |
+
yield gr.update(), gr.update(), gr.update(); return
|
| 1785 |
+
# Trigger format: "slot_id|seg_idx|timestamp|{stateJSON}"
|
| 1786 |
+
parts = trigger_val.split("|", 3)
|
| 1787 |
+
if len(parts) != 4 or parts[0] != _sid:
|
| 1788 |
+
print(f"[regen MMA] early-exit: parts={parts[:2]} expected slot={_sid!r}")
|
| 1789 |
+
yield gr.update(), gr.update(), gr.update(); return
|
| 1790 |
seg_idx = int(parts[1])
|
| 1791 |
+
state_json = parts[3]
|
| 1792 |
print(f"[regen MMA] slot={_sid} seg_idx={seg_idx} state_json_len={len(state_json)}")
|
| 1793 |
lock = _get_slot_lock(_sid)
|
| 1794 |
with lock:
|
|
|
|
| 1874 |
print(f"[regen HF] trigger_val_len={len(trigger_val) if trigger_val else 0} video={video!r}")
|
| 1875 |
if not trigger_val:
|
| 1876 |
print(f"[regen HF] early-exit: trigger_val empty")
|
| 1877 |
+
yield gr.update(), gr.update(), gr.update(); return
|
| 1878 |
+
# Trigger format: "slot_id|seg_idx|timestamp|{stateJSON}"
|
| 1879 |
+
parts = trigger_val.split("|", 3)
|
| 1880 |
+
if len(parts) != 4 or parts[0] != _sid:
|
| 1881 |
+
print(f"[regen HF] early-exit: parts={parts[:2]} expected slot={_sid!r}")
|
| 1882 |
+
yield gr.update(), gr.update(), gr.update(); return
|
| 1883 |
seg_idx = int(parts[1])
|
| 1884 |
+
state_json = parts[3]
|
| 1885 |
print(f"[regen HF] slot={_sid} seg_idx={seg_idx} state_json_len={len(state_json)}")
|
| 1886 |
lock = _get_slot_lock(_sid)
|
| 1887 |
with lock:
|