@@ -33,10 +33,13 @@ def _pick_feedback(round_payload: dict, round_index: int) -> tuple[dict[str, int
3333 candidate_ids [0 ]: 2 ,
3434 candidate_ids [1 ]: 5 ,
3535 candidate_ids [2 ]: 4 ,
36+ candidate_ids [3 ]: 3 ,
37+ candidate_ids [4 ]: 1 ,
3638 }
3739 critique = (
3840 "The unmodified prompt baseline is promising, but candidate 2 has the clearest silhouette, "
39- "the strongest sunrise rim light, and the most premium hero-shot composition."
41+ "the strongest sunrise rim light, and the most premium hero-shot composition. "
42+ "The extra alternatives are useful, but they do not beat the top two."
4043 )
4144 return ratings , critique
4245
@@ -45,6 +48,8 @@ def _pick_feedback(round_payload: dict, round_index: int) -> tuple[dict[str, int
4548 candidate_ids [0 ]: 5 ,
4649 candidate_ids [1 ]: 4 ,
4750 candidate_ids [2 ]: 2 ,
51+ candidate_ids [3 ]: 3 ,
52+ candidate_ids [4 ]: 1 ,
4853 }
4954 critique = "Keep the carried-forward winner, but push harder toward cleaner panel detail and a stronger studio-product framing."
5055 return ratings , critique
@@ -54,6 +59,8 @@ def _pick_feedback(round_payload: dict, round_index: int) -> tuple[dict[str, int
5459 candidate_ids [0 ]: 4 ,
5560 candidate_ids [1 ]: 5 ,
5661 candidate_ids [2 ]: 3 ,
62+ candidate_ids [3 ]: 2 ,
63+ candidate_ids [4 ]: 1 ,
5764 }
5865 critique = "Candidate 2 improves the bodywork and surfacing. It feels more like a premium launch image while staying true to the concept."
5966 return ratings , critique
@@ -63,6 +70,8 @@ def _pick_feedback(round_payload: dict, round_index: int) -> tuple[dict[str, int
6370 candidate_ids [0 ]: 4 ,
6471 candidate_ids [1 ]: 5 ,
6572 candidate_ids [2 ]: 3 ,
73+ candidate_ids [3 ]: 2 ,
74+ candidate_ids [4 ]: 1 ,
6675 }
6776 critique = "Candidate 2 has the best balance of dramatic lighting and realistic product geometry. Keep that direction for the next phase."
6877 return ratings , critique
@@ -71,6 +80,8 @@ def _pick_feedback(round_payload: dict, round_index: int) -> tuple[dict[str, int
7180 candidate_ids [0 ]: 5 ,
7281 candidate_ids [1 ]: 4 ,
7382 candidate_ids [2 ]: 3 ,
83+ candidate_ids [3 ]: 2 ,
84+ candidate_ids [4 ]: 1 ,
7485 }
7586 critique = "The incumbent now reads like the strongest finished hero image, so preserve it as the final preferred direction."
7687 return ratings , critique
@@ -233,6 +244,12 @@ def _render_html(
233244 " pre { white-space: pre-wrap; word-break: break-word; background: #f7f1e9; border-radius: 14px; padding: 14px; font-size: 0.88rem; }" ,
234245 " table { width: 100%; border-collapse: collapse; }" ,
235246 " th, td { text-align: left; vertical-align: top; padding: 10px; border-top: 1px solid #e7dbce; }" ,
247+ " details.card { padding: 0; overflow: hidden; }" ,
248+ " details.card > summary { list-style: none; cursor: pointer; padding: 22px 24px; font-family: 'Segoe UI', sans-serif; font-weight: 700; }" ,
249+ " details.card > summary::-webkit-details-marker { display: none; }" ,
250+ " details.card > summary::after { content: '+'; float: right; color: #87562a; }" ,
251+ " details.card[open] > summary::after { content: '−'; }" ,
252+ " .card-body { padding: 0 24px 24px; }" ,
236253 " </style>" ,
237254 "</head>" ,
238255 "<body>" ,
@@ -251,12 +268,14 @@ def _render_html(
251268 f' <div class="metric"><strong>Unique images</strong><span>{ escape (str (visual_checks ["summary" ]["unique_image_count" ]))} </span></div>' ,
252269 " </div>" ,
253270 " </section>" ,
254- ' <section class="card stack">' ,
255- ' <div class="callout">' ,
271+ ' <details class="card" open>' ,
272+ " <summary>Demonstration Overview</summary>" ,
273+ ' <div class="card-body stack">' ,
274+ ' <div class="callout">' ,
256275 " <h2>Demonstration Objective</h2>" ,
257276 f" <p>{ escape (objective )} </p>" ,
258- " </div>" ,
259- ' <div class="columns">' ,
277+ " </div>" ,
278+ ' <div class="columns">' ,
260279 " <div>" ,
261280 " <h3>Success Criteria</h3>" ,
262281 " <ul>" ,
@@ -270,29 +289,35 @@ def _render_html(
270289 " </ol>" ,
271290 " </div>" ,
272291 " </div>" ,
273- " </section>" ,
274- ' <section class="card stack">' ,
275- " <div>" ,
292+ " </div>" ,
293+ " </details>" ,
294+ ' <details class="card" open>' ,
295+ " <summary>Run Setup</summary>" ,
296+ ' <div class="card-body stack">' ,
297+ " <div>" ,
276298 " <h2>Run Setup</h2>" ,
277299 f" <p><strong>Prompt:</strong> { escape (session ['prompt' ])} </p>" ,
278300 f" <p><strong>Negative prompt:</strong> { escape (session .get ('negative_prompt' ) or '(none)' )} </p>" ,
279301 f" <p><strong>Sampler:</strong> <code>{ escape (session ['config' ]['sampler' ])} </code> | <strong>Updater:</strong> <code>{ escape (session ['config' ]['updater' ])} </code> | <strong>Feedback mode:</strong> <code>{ escape (session ['config' ]['feedback_mode' ])} </code></p>" ,
280302 f" <p><strong>Output file:</strong> <code>{ escape (str (output_path ))} </code></p>" ,
303+ " </div>" ,
304+ f" <pre>{ escape (json .dumps (diagnostics , indent = 2 , sort_keys = True ))} </pre>" ,
281305 " </div>" ,
282- f" <pre>{ escape (json .dumps (diagnostics , indent = 2 , sort_keys = True ))} </pre>" ,
283- " </section>" ,
284- ' <section class="card stack">' ,
285- " <div>" ,
306+ " </details>" ,
307+ ' <details class="card" open>' ,
308+ " <summary>Automated Visual Checks</summary>" ,
309+ ' <div class="card-body stack">' ,
310+ " <div>" ,
286311 " <h2>Automated Visual Checks</h2>" ,
287312 " <p>These checks do not prove semantic correctness, but they do catch common generation failures such as blank-looking outputs, unreadable files, low-detail artifacts, and accidental duplication.</p>" ,
288- " </div>" ,
289- ' <div class="metrics">' ,
313+ " </div>" ,
314+ ' <div class="metrics">' ,
290315 f' <div class="metric"><strong>Unique image files</strong><span>{ escape (str (visual_checks ["summary" ]["unique_image_count" ]))} </span></div>' ,
291316 f' <div class="metric"><strong>Reused candidates</strong><span>{ escape (str (visual_checks ["summary" ]["reused_candidate_count" ]))} </span></div>' ,
292317 f' <div class="metric"><strong>Flagged images</strong><span>{ escape (str (visual_checks ["summary" ]["failing_image_count" ]))} </span></div>' ,
293318 f' <div class="metric"><strong>Duplicate groups</strong><span>{ escape (str (visual_checks ["summary" ]["duplicate_image_group_count" ]))} </span></div>' ,
294- " </div>" ,
295- " <table>" ,
319+ " </div>" ,
320+ " <table>" ,
296321 " <thead><tr><th>Image</th><th>Candidates</th><th>Size</th><th>Entropy</th><th>Contrast</th><th>Edges</th><th>Status</th></tr></thead>" ,
297322 " <tbody>" ,
298323 * [
@@ -310,21 +335,23 @@ def _render_html(
310335 " </tbody>" ,
311336 " </table>" ,
312337 (
313- f" <pre>{ escape (json .dumps (visual_checks , indent = 2 , sort_keys = True ))} </pre>"
338+ f" <pre>{ escape (json .dumps (visual_checks , indent = 2 , sort_keys = True ))} </pre>"
314339 if visual_checks ["failing_images" ] or visual_checks ["duplicate_groups" ]
315- else " <p>All automated visual sanity checks passed for the unique generated images in this bundle.</p>"
340+ else " <p>All automated visual sanity checks passed for the unique generated images in this bundle.</p>"
316341 ),
317- " </section>" ,
342+ " </div>" ,
343+ " </details>" ,
318344 ]
319345
320346 for round_obj in rounds :
321347 feedback = round_obj ["feedback_events" ][0 ] if round_obj ["feedback_events" ] else None
322348 winner_id = round_obj .get ("update_summary" , {}).get ("winner_candidate_id" )
323349 sections .extend (
324350 [
325- ' <section class="card round">' ,
351+ ' <details class="card round">' ,
352+ f" <summary>Phase { round_obj ['round_index' ]} : propose, inspect, choose, update</summary>" ,
353+ ' <div class="card-body">' ,
326354 f" <p class=\" eyebrow\" >Round { round_obj ['round_index' ]} </p>" ,
327- f" <h2>Phase { round_obj ['round_index' ]} : propose, inspect, choose, update</h2>" ,
328355 f" <p><span class=\" pill\" >Round id: { escape (round_obj ['id' ])} </span><span class=\" pill\" >Latency: { escape (str (round_obj .get ('latency_ms' , 0 )))} ms</span><span class=\" pill\" >Incumbent z before update: { escape (json .dumps (round_obj .get ('incumbent_z' , [])))} </span></p>" ,
329356 ' <div class="candidate-grid">' ,
330357 ]
@@ -369,16 +396,19 @@ def _render_html(
369396 f" <pre>{ escape (json .dumps (round_obj ['update_summary' ], indent = 2 , sort_keys = True ))} </pre>" ,
370397 ]
371398 )
372- sections .append ( " </section>" )
399+ sections .extend ([ " </div>" , " </details>" ] )
373400
374401 sections .extend (
375402 [
376- ' <section class="card">' ,
403+ ' <details class="card" open>' ,
404+ " <summary>Outcome</summary>" ,
405+ ' <div class="card-body">' ,
377406 " <h2>Outcome</h2>" ,
378407 f" <p>The session ended at round <strong>{ len (rounds )} </strong> with incumbent candidate <code>{ escape (str (session .get ('incumbent_candidate_id' ) or 'n/a' ))} </code>.</p>" ,
379408 " <p>This report demonstrates system value by showing that the workflow does not stop at one prompt and one image. It keeps the user in a controlled compare-select-update loop with visible evidence for what was proposed, what was preferred, and how the next state was chosen.</p>" ,
380409 f" <p>Backend session traces and the auto-generated audit report also exist under <code>{ escape (str ((output_path .parent / 'data' / 'traces' ).resolve ()))} </code>.</p>" ,
381- " </section>" ,
410+ " </div>" ,
411+ " </details>" ,
382412 "</main>" ,
383413 "</body>" ,
384414 "</html>" ,
@@ -422,7 +452,7 @@ def main() -> int:
422452 name = "Premium product hero image demo" ,
423453 description = "A scripted user steering walkthrough that demonstrates creative refinement on the real Diffusers backend." ,
424454 config = StrategyConfig (
425- candidate_count = 3 ,
455+ candidate_count = 5 ,
426456 image_size = "512x512" ,
427457 sampler = "random_local" ,
428458 updater = "winner_average" ,
0 commit comments