Skip to content

Commit 766e0c1

Browse files
committed
Improve first-round diversity and sample reporting
1 parent 9bfec86 commit 766e0c1

9 files changed

Lines changed: 187 additions & 51 deletions

File tree

app/core/schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class StrategyConfig(BaseModel):
5959
feedback_mode: FeedbackType = FeedbackType.scalar_rating
6060
seed_policy: str = "fixed-per-round"
6161
steering_mode: str = "low_dimensional"
62-
candidate_count: int = 4
62+
candidate_count: int = 5
6363
image_size: str = "512x512"
6464
trust_radius: float = 0.35
6565
anchor_strength: float = 0.15

app/core/tracing.py

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ def _render_session_report(
161161
" .events th, .events td { text-align: left; padding: 10px; border-top: 1px solid #e7ddcf; vertical-align: top; }",
162162
" pre { margin: 0; white-space: pre-wrap; word-break: break-word; background: #f7f2ea; border-radius: 12px; padding: 12px; }",
163163
" a { color: #7a3f13; }",
164+
" details.card { padding: 0; overflow: hidden; }",
165+
" details.card > summary { list-style: none; cursor: pointer; padding: 20px; font-weight: 700; }",
166+
" details.card > summary::-webkit-details-marker { display: none; }",
167+
" details.card > summary::after { content: '+'; float: right; color: #7a3f13; }",
168+
" details.card[open] > summary::after { content: '−'; }",
169+
" .card-body { padding: 0 20px 20px; }",
164170
" </style>",
165171
"</head>",
166172
"<body>",
@@ -179,30 +185,37 @@ def _render_session_report(
179185
f' <div class="metric"><h3>Active Device</h3><p>{self._escape(str(diagnostics.get("active_device") or diagnostics.get("configured_device") or "n/a"))}</p></div>',
180186
" </div>",
181187
" </section>",
182-
' <section class="card section">',
183-
" <h2>Artifacts</h2>",
188+
' <details class="card section" open>',
189+
" <summary>Artifacts</summary>",
190+
' <div class="card-body">',
184191
f" <p>Backend event log: <code>{self._escape(str(backend_rel))}</code></p>",
185192
f" <p>Frontend event log: <code>{self._escape(str(frontend_rel))}</code></p>",
186-
" </section>",
187-
' <section class="card section">',
188-
" <h2>Runtime Diagnostics</h2>",
193+
" </div>",
194+
" </details>",
195+
' <details class="card section" open>',
196+
" <summary>Runtime Diagnostics</summary>",
197+
' <div class="card-body">',
189198
f" <pre>{self._escape(json.dumps(diagnostics, indent=2, sort_keys=True))}</pre>",
190-
" </section>",
191-
' <section class="card section">',
192-
" <h2>Run Summary</h2>",
199+
" </div>",
200+
" </details>",
201+
' <details class="card section" open>',
202+
" <summary>Run Summary</summary>",
203+
' <div class="card-body">',
193204
f" <p><strong>Negative prompt:</strong> {self._escape(session.get('negative_prompt') or '(none)')}</p>",
194205
f" <p><strong>Model:</strong> <code>{self._escape(session.get('model_name', 'unknown'))}</code></p>",
195206
f" <p><strong>Feedback mode:</strong> <code>{self._escape(str(session.get('config', {}).get('feedback_mode', 'unknown')))}</code></p>",
196207
f" <p><strong>Sampler:</strong> <code>{self._escape(str(session.get('config', {}).get('sampler', 'unknown')))}</code></p>",
197208
f" <p><strong>Updater:</strong> <code>{self._escape(str(session.get('config', {}).get('updater', 'unknown')))}</code></p>",
198-
" </section>",
209+
" </div>",
210+
" </details>",
199211
]
200212

201213
for round_obj in rounds:
202214
html_parts.extend(
203215
[
204-
' <section class="card section round">',
205-
f" <h2>Round {round_obj.get('round_index')}</h2>",
216+
' <details class="card section round">',
217+
f" <summary>Round {round_obj.get('round_index')}</summary>",
218+
' <div class="card-body">',
206219
f' <span class="pill">Round id: {self._escape(round_obj.get("id", ""))}</span>',
207220
f' <span class="pill">Render status: {self._escape(str(round_obj.get("render_status", "unknown")))}</span>',
208221
f' <span class="pill">Latency: {self._escape(str(round_obj.get("latency_ms", 0)))} ms</span>',
@@ -242,12 +255,13 @@ def _render_session_report(
242255
html_parts.append(
243256
f" <pre>{self._escape(json.dumps(round_obj.get('update_summary', {}), indent=2, sort_keys=True))}</pre>"
244257
)
245-
html_parts.append(" </section>")
258+
html_parts.extend([" </div>", " </details>"])
246259

247260
html_parts.extend(
248261
[
249-
' <section class="card section">',
250-
" <h2>Event Timeline</h2>",
262+
' <details class="card section">',
263+
" <summary>Event Timeline</summary>",
264+
' <div class="card-body">',
251265
' <table class="events">',
252266
" <thead><tr><th>Time</th><th>Source</th><th>Event</th><th>Details</th></tr></thead>",
253267
" <tbody>",
@@ -270,7 +284,8 @@ def _render_session_report(
270284
[
271285
" </tbody>",
272286
" </table>",
273-
" </section>",
287+
" </div>",
288+
" </details>",
274289
"</main>",
275290
"</body>",
276291
"</html>",

app/engine/orchestrator.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
from copy import deepcopy
4+
import math
45

56
from app.core.config import settings
67
from app.core.schema import (
@@ -21,6 +22,7 @@
2122
from app.core.logging import logger
2223
from app.core.tracing import TraceRecorder
2324
from app.feedback.normalization import normalize_feedback
25+
from app.samplers.base import clamp_vector
2426
from app.samplers.exploit_orthogonal import ExploitOrthogonalSampler
2527
from app.samplers.random_local import RandomLocalSampler
2628
from app.samplers.uncertainty import UncertaintyGuidedSampler
@@ -139,6 +141,7 @@ def generate_round(self, session_id: str) -> RoundResponse:
139141
carried_forward = self._build_carried_forward_candidate(session)
140142
baseline_candidate = self._build_baseline_prompt_candidate(session)
141143
proposed_candidates = sampler.propose(session, seed)
144+
proposed_candidates = self._widen_first_round_candidates(session, proposed_candidates)
142145
candidates = self._compose_round_candidates(
143146
pinned_candidate=carried_forward or baseline_candidate,
144147
proposed_candidates=proposed_candidates,
@@ -360,6 +363,35 @@ def _build_baseline_prompt_candidate(session: Session) -> Candidate | None:
360363
},
361364
)
362365

366+
@staticmethod
367+
def _widen_first_round_candidates(session: Session, proposed_candidates: list[Candidate]) -> list[Candidate]:
368+
"""Slightly spread first-round exploratory candidates away from the prompt baseline."""
369+
370+
if session.current_round != 0:
371+
return proposed_candidates
372+
373+
boosted_candidates: list[Candidate] = []
374+
boost_radius = min(session.config.trust_radius * 1.35, 0.6)
375+
min_radius = min(max(session.config.trust_radius * 0.85, 0.18), boost_radius)
376+
for index, candidate in enumerate(proposed_candidates):
377+
scale = 1.2 + (0.12 * index)
378+
boosted_z = clamp_vector([value * scale for value in candidate.z], boost_radius)
379+
length = math.sqrt(sum(value * value for value in boosted_z))
380+
if 0.0 < length < min_radius:
381+
normalization = min_radius / length
382+
boosted_z = clamp_vector([value * normalization for value in boosted_z], boost_radius)
383+
length = math.sqrt(sum(value * value for value in boosted_z))
384+
if length == 0.0:
385+
axis = index % max(1, len(session.current_z))
386+
boosted_z = [0.0 for _ in session.current_z]
387+
boosted_z[axis] = min_radius
388+
candidate.z = boosted_z
389+
candidate.generation_params["first_round_diversity_boost"] = True
390+
candidate.generation_params["first_round_diversity_scale"] = round(scale, 3)
391+
candidate.generation_params["first_round_min_radius"] = round(min_radius, 3)
392+
boosted_candidates.append(candidate)
393+
return boosted_candidates
394+
363395
@staticmethod
364396
def _compose_round_candidates(
365397
*,

docs/configuration_manual.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ updater: winner_average
103103
feedback_mode: scalar_rating
104104
seed_policy: fixed-per-round
105105
steering_mode: low_dimensional
106-
candidate_count: 4
106+
candidate_count: 5
107107
image_size: 512x512
108108
trust_radius: 0.35
109109
anchor_strength: 0.15
@@ -331,7 +331,7 @@ updater: winner_average
331331
feedback_mode: scalar_rating
332332
seed_policy: fixed-per-round
333333
steering_mode: low_dimensional
334-
candidate_count: 4
334+
candidate_count: 5
335335
image_size: 512x512
336336
trust_radius: 0.25
337337
anchor_strength: 0.15

scripts/create_real_e2e_example.py

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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",

site/docs/configuration_manual.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ <h2 id="yaml-template">YAML Template</h2>
111111
feedback_mode: scalar_rating
112112
seed_policy: fixed-per-round
113113
steering_mode: low_dimensional
114-
candidate_count: 4
114+
candidate_count: 5
115115
image_size: 512x512
116116
trust_radius: 0.35
117117
anchor_strength: 0.15
@@ -306,7 +306,7 @@ <h3 id="conservative-comparison-session">Conservative Comparison Session</h3>
306306
feedback_mode: scalar_rating
307307
seed_policy: fixed-per-round
308308
steering_mode: low_dimensional
309-
candidate_count: 4
309+
candidate_count: 5
310310
image_size: 512x512
311311
trust_radius: 0.25
312312
anchor_strength: 0.15

0 commit comments

Comments
 (0)