k-l-lambda commited on
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

Files changed (2) hide show
  1. app.py +33 -7
  2. 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
- '''Streaming generate callback. Yields updates for (log, editor, file_list, store).
 
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=int(max_patches), temperature=float(temperature),
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
- yield _log_panel(raw), pretty, gr.update(choices=list(store.keys()), value=label), store
 
 
 
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
- /* Belt-and-suspenders: pin currentColor on the actual drawing elements too. */
 
 
 
 
 
 
 
 
 
 
 
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