Spaces:
Running
Running
Commit ·
e30c292
1
Parent(s): ba48acb
add generation progress bar, seed randomization, dark-theme score colors
Browse files- run_generation: gr.Progress bar (by measures or patch budget), show_progress=minimal
- randomize the seed slider after each generation; output file keeps the seed used
- date-prefixed output filenames (YYYYMMDD_HHMMSS)
- score colors: force dark on the whole svg subtree to beat Gradio's .prose * rule
(covers staff/barLine/ledgerLines/grpSym), fixing pale lines under the dark theme
- app.py +33 -7
- web/score-player.css +14 -8
app.py
CHANGED
|
@@ -18,6 +18,7 @@ import os
|
|
| 18 |
import re
|
| 19 |
import time
|
| 20 |
import json
|
|
|
|
| 21 |
import logging
|
| 22 |
from collections import deque
|
| 23 |
|
|
@@ -202,13 +203,20 @@ def _log_panel (raw=''):
|
|
| 202 |
return sys_log
|
| 203 |
|
| 204 |
|
| 205 |
-
def run_generation (prompt, measures, temperature, max_patches, seed, store, top_k=0, top_p=0.9
|
| 206 |
-
|
|
|
|
| 207 |
|
| 208 |
store: {label: lyl_text} dict held in gr.State; the produced document is added
|
| 209 |
to it under a timestamped label once generation finishes.
|
| 210 |
|
| 211 |
top_k / top_p have fixed defaults (no UI controls); pass them explicitly to override.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
'''
|
| 213 |
meas = int(measures) if measures and int(measures) > 0 else None
|
| 214 |
store = dict(store or {})
|
|
@@ -217,27 +225,38 @@ def run_generation (prompt, measures, temperature, max_patches, seed, store, top
|
|
| 217 |
|
| 218 |
raw = pretty = ''
|
| 219 |
n_yields = 0
|
|
|
|
| 220 |
t0 = time.perf_counter()
|
|
|
|
| 221 |
try:
|
| 222 |
gen = get_generator()
|
| 223 |
# drop a marker in the log timeline; _log_panel expands it to the live output,
|
| 224 |
# so subsequent log lines (timing/done) land *after* the generation text.
|
| 225 |
LOG.info(_GEN_MARKER)
|
| 226 |
for raw, pretty, done in gen.generate_stream(
|
| 227 |
-
prompt_text=prompt or '', max_patches=
|
| 228 |
top_k=int(top_k), top_p=float(top_p), measures=meas, seed=int(seed)):
|
| 229 |
if not done:
|
| 230 |
n_yields += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
# The log streams every patch (raw text). The editor, however, must stay
|
| 232 |
# syntactically valid: only sync it at a measure boundary — i.e. when the
|
| 233 |
# accumulated text ends with the measure separator `|` (so it never shows a
|
| 234 |
# half-generated, incomplete measure). `done` forces a final sync.
|
| 235 |
at_boundary = raw.rstrip().endswith('|')
|
| 236 |
editor_update = pretty if (at_boundary or done) else gr.update()
|
| 237 |
-
yield _log_panel(raw), editor_update, gr.update(), store
|
| 238 |
except Exception as e:
|
| 239 |
LOG.exception('generation failed: %s', e)
|
| 240 |
-
yield _log_panel(raw), pretty, gr.update(), store
|
| 241 |
return
|
| 242 |
|
| 243 |
# timing: the stream yields once for prefill + once per generated patch, so the
|
|
@@ -256,7 +275,10 @@ def run_generation (prompt, measures, temperature, max_patches, seed, store, top
|
|
| 256 |
with open(out_path, 'w', encoding='utf-8') as f:
|
| 257 |
f.write(pretty)
|
| 258 |
LOG.info('generation done: %d chars -> %s', len(pretty), os.path.basename(out_path))
|
| 259 |
-
|
|
|
|
|
|
|
|
|
|
| 260 |
|
| 261 |
|
| 262 |
def load_file (label, store):
|
|
@@ -443,8 +465,12 @@ def build_ui ():
|
|
| 443 |
gen_event = gen_btn.click(
|
| 444 |
run_generation,
|
| 445 |
inputs=[prompt, measures, temperature, max_patches, seed, store],
|
| 446 |
-
outputs=[log, editor, file_list, store],
|
| 447 |
js=_JS_GEN_START,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
)
|
| 449 |
gen_event.then(None, None, None, js=_JS_GEN_END)
|
| 450 |
|
|
|
|
| 18 |
import re
|
| 19 |
import time
|
| 20 |
import json
|
| 21 |
+
import random
|
| 22 |
import logging
|
| 23 |
from collections import deque
|
| 24 |
|
|
|
|
| 203 |
return sys_log
|
| 204 |
|
| 205 |
|
| 206 |
+
def run_generation (prompt, measures, temperature, max_patches, seed, store, top_k=0, top_p=0.9,
|
| 207 |
+
progress=gr.Progress()):
|
| 208 |
+
'''Streaming generate callback. Yields updates for (log, editor, file_list, store, seed).
|
| 209 |
|
| 210 |
store: {label: lyl_text} dict held in gr.State; the produced document is added
|
| 211 |
to it under a timestamped label once generation finishes.
|
| 212 |
|
| 213 |
top_k / top_p have fixed defaults (no UI controls); pass them explicitly to override.
|
| 214 |
+
|
| 215 |
+
A gr.Progress bar tracks generation: by completed measures when a measure count
|
| 216 |
+
is requested, else by generated patches out of max_patches.
|
| 217 |
+
|
| 218 |
+
The output file is named with the seed used for THIS generation; on completion
|
| 219 |
+
the seed slider is randomized (final yield) so the next click uses a fresh seed.
|
| 220 |
'''
|
| 221 |
meas = int(measures) if measures and int(measures) > 0 else None
|
| 222 |
store = dict(store or {})
|
|
|
|
| 225 |
|
| 226 |
raw = pretty = ''
|
| 227 |
n_yields = 0
|
| 228 |
+
mp = int(max_patches)
|
| 229 |
t0 = time.perf_counter()
|
| 230 |
+
progress(0.0, desc='Starting…')
|
| 231 |
try:
|
| 232 |
gen = get_generator()
|
| 233 |
# drop a marker in the log timeline; _log_panel expands it to the live output,
|
| 234 |
# so subsequent log lines (timing/done) land *after* the generation text.
|
| 235 |
LOG.info(_GEN_MARKER)
|
| 236 |
for raw, pretty, done in gen.generate_stream(
|
| 237 |
+
prompt_text=prompt or '', max_patches=mp, temperature=float(temperature),
|
| 238 |
top_k=int(top_k), top_p=float(top_p), measures=meas, seed=int(seed)):
|
| 239 |
if not done:
|
| 240 |
n_yields += 1
|
| 241 |
+
# progress: by measures (count completed `|` separators vs target) when a
|
| 242 |
+
# measure count was requested, else by patch budget. clamp to [0,1).
|
| 243 |
+
if meas:
|
| 244 |
+
frac = raw.count('|') / meas
|
| 245 |
+
desc = 'measure %d/%d' % (min(raw.count('|'), meas), meas)
|
| 246 |
+
else:
|
| 247 |
+
frac = max(0, n_yields - 1) / mp
|
| 248 |
+
desc = 'patch %d/%d' % (max(0, n_yields - 1), mp)
|
| 249 |
+
progress(min(frac, 0.99) if not done else 1.0, desc=desc)
|
| 250 |
# The log streams every patch (raw text). The editor, however, must stay
|
| 251 |
# syntactically valid: only sync it at a measure boundary — i.e. when the
|
| 252 |
# accumulated text ends with the measure separator `|` (so it never shows a
|
| 253 |
# half-generated, incomplete measure). `done` forces a final sync.
|
| 254 |
at_boundary = raw.rstrip().endswith('|')
|
| 255 |
editor_update = pretty if (at_boundary or done) else gr.update()
|
| 256 |
+
yield _log_panel(raw), editor_update, gr.update(), store, gr.update()
|
| 257 |
except Exception as e:
|
| 258 |
LOG.exception('generation failed: %s', e)
|
| 259 |
+
yield _log_panel(raw), pretty, gr.update(), store, gr.update()
|
| 260 |
return
|
| 261 |
|
| 262 |
# timing: the stream yields once for prefill + once per generated patch, so the
|
|
|
|
| 275 |
with open(out_path, 'w', encoding='utf-8') as f:
|
| 276 |
f.write(pretty)
|
| 277 |
LOG.info('generation done: %d chars -> %s', len(pretty), os.path.basename(out_path))
|
| 278 |
+
# randomize the seed slider for the next run (the file above already used the
|
| 279 |
+
# seed this generation ran with, so naming is unaffected)
|
| 280 |
+
next_seed = random.randint(0, 2147483647)
|
| 281 |
+
yield _log_panel(raw), pretty, gr.update(choices=list(store.keys()), value=label), store, gr.update(value=next_seed)
|
| 282 |
|
| 283 |
|
| 284 |
def load_file (label, store):
|
|
|
|
| 465 |
gen_event = gen_btn.click(
|
| 466 |
run_generation,
|
| 467 |
inputs=[prompt, measures, temperature, max_patches, seed, store],
|
| 468 |
+
outputs=[log, editor, file_list, store, seed],
|
| 469 |
js=_JS_GEN_START,
|
| 470 |
+
# "minimal" = a small runtime/progress indicator only (top-right), NOT a
|
| 471 |
+
# spinner covering the output area. "full" overlays the Logs/editor
|
| 472 |
+
# outputs and, since we yield every patch, flickers badly.
|
| 473 |
+
show_progress='minimal',
|
| 474 |
)
|
| 475 |
gen_event.then(None, None, None, js=_JS_GEN_END)
|
| 476 |
|
web/score-player.css
CHANGED
|
@@ -28,12 +28,6 @@
|
|
| 28 |
|
| 29 |
.ls-svg {
|
| 30 |
background: #fff;
|
| 31 |
-
/* Verovio draws staff lines / notes with stroke|fill="currentColor", which
|
| 32 |
-
inherits the CSS `color`. Gradio's dark theme sets a light body color (with
|
| 33 |
-
enough specificity to beat a plain class rule), so the score would render as
|
| 34 |
-
pale lines on the white box. Force a dark ink color with !important so it
|
| 35 |
-
holds regardless of the active Gradio theme. */
|
| 36 |
-
color: #1a1a1a !important;
|
| 37 |
display: inline-block;
|
| 38 |
min-width: 100%;
|
| 39 |
}
|
|
@@ -41,10 +35,22 @@
|
|
| 41 |
max-width: 100%;
|
| 42 |
height: auto;
|
| 43 |
}
|
| 44 |
-
/*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
.ls-svg svg .staff path,
|
| 46 |
.ls-svg svg .barLine path,
|
| 47 |
-
.ls-svg svg .ledgerLines path
|
|
|
|
| 48 |
stroke: #1a1a1a;
|
| 49 |
}
|
| 50 |
|
|
|
|
| 28 |
|
| 29 |
.ls-svg {
|
| 30 |
background: #fff;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
display: inline-block;
|
| 32 |
min-width: 100%;
|
| 33 |
}
|
|
|
|
| 35 |
max-width: 100%;
|
| 36 |
height: auto;
|
| 37 |
}
|
| 38 |
+
/* Verovio draws everything with stroke|fill="currentColor", which resolves to the
|
| 39 |
+
computed `color`. We can't rely on inheritance from a container `color`: Gradio's
|
| 40 |
+
`.prose * { color: var(--body-text-color) }` sets color DIRECTLY on every
|
| 41 |
+
descendant (so it wins over an inherited value), which under the dark theme is a
|
| 42 |
+
light grey — leaving staff lines, bar lines and the per-system left brace/bracket
|
| 43 |
+
(grpSym) too pale. Force a dark `color` on the whole svg subtree (high enough
|
| 44 |
+
specificity + !important to beat `.prose *`), and pin stroke on the line elements
|
| 45 |
+
as belt-and-suspenders. */
|
| 46 |
+
.ls-svg svg,
|
| 47 |
+
.ls-svg svg * {
|
| 48 |
+
color: #1a1a1a !important;
|
| 49 |
+
}
|
| 50 |
.ls-svg svg .staff path,
|
| 51 |
.ls-svg svg .barLine path,
|
| 52 |
+
.ls-svg svg .ledgerLines path,
|
| 53 |
+
.ls-svg svg .grpSym path {
|
| 54 |
stroke: #1a1a1a;
|
| 55 |
}
|
| 56 |
|