meeting-103 R1: 5조합 (Pony·Flux·PuLID·Qwen) — 4/5 ✅ / Qwen 4차 실패
✅ 4 정상 (시각 검수 대기): - 1-1 Pony V6 XL A 별이 (best pastel 42.5%, 검정 0.61%) - 1-2 Flux Dev A 별이 (검정 3.11% 한계 근접) - 1-3 Flux Dev + PuLID A 별이 (별이 ID matching 핵심) - 1-4 Flux Dev B 한옥 (검정 0%, 일본/한국 시각 평가 필수) ❌ 1 실패: - 1-5 Qwen-2512 C 한자카드 (5.5KB) v6 R1 3차 + v2.1 R1 v4 = 총 4차 연속 패턴: EmptyLatent / EmptyQwenLayered / ModelSamplingAuraFlow+SD3Latent / SamplerCustomAdvanced hypothesis: GGUF Q4 + CUDA 13 + PyTorch 2.12 호환성 또는 non-GGUF 필요 자산 라이브러리 갱신: - models-performance.json: 4 모델 등재 + Pony 파스텔 42.5 / Flux 평균 24.9 - failed-patterns.json: qwen-2512-4-attempts 패턴 등재 - 다음 시도: non-GGUF / Lightning LoRA / 다른 sampler/shift scripts/round_pipeline.py 신설 (R2 부터 재사용) eval/meeting-103-grid.html + meta.json 생성 썸네일 의무 충족 (최대 540KB, 모두 1MB 미만)
@@ -27,5 +27,78 @@
|
||||
"illustrious",
|
||||
"lightning-lora-qwen"
|
||||
],
|
||||
"models": {}
|
||||
"models": {
|
||||
"pony-v6": {
|
||||
"rounds_used": 1,
|
||||
"dimensions": {
|
||||
"byeolyi": [],
|
||||
"hanja": [],
|
||||
"hanok": [],
|
||||
"illustration": [],
|
||||
"korean_traditional": [],
|
||||
"auto_pastel": [
|
||||
42.5
|
||||
],
|
||||
"auto_black": [
|
||||
0.61
|
||||
]
|
||||
},
|
||||
"avg_scores": {},
|
||||
"best_combinations": [],
|
||||
"verdict": ""
|
||||
},
|
||||
"flux-dev": {
|
||||
"rounds_used": 1,
|
||||
"dimensions": {
|
||||
"byeolyi": [],
|
||||
"hanja": [],
|
||||
"hanok": [],
|
||||
"illustration": [],
|
||||
"korean_traditional": [],
|
||||
"auto_pastel": [
|
||||
20.1,
|
||||
29.7
|
||||
],
|
||||
"auto_black": [
|
||||
3.11,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
"avg_scores": {},
|
||||
"best_combinations": [],
|
||||
"verdict": ""
|
||||
},
|
||||
"pulid-flux": {
|
||||
"rounds_used": 1,
|
||||
"dimensions": {
|
||||
"byeolyi": [],
|
||||
"hanja": [],
|
||||
"hanok": [],
|
||||
"illustration": [],
|
||||
"korean_traditional": [],
|
||||
"auto_pastel": [
|
||||
30.2
|
||||
],
|
||||
"auto_black": [
|
||||
0.73
|
||||
]
|
||||
},
|
||||
"avg_scores": {},
|
||||
"best_combinations": [],
|
||||
"verdict": ""
|
||||
},
|
||||
"qwen-image-2512": {
|
||||
"rounds_used": 1,
|
||||
"dimensions": {
|
||||
"byeolyi": [],
|
||||
"hanja": [],
|
||||
"hanok": [],
|
||||
"illustration": [],
|
||||
"korean_traditional": []
|
||||
},
|
||||
"avg_scores": {},
|
||||
"best_combinations": [],
|
||||
"verdict": "워크플로우 결함 — 5KB 검정 latent (v1~v4 4차 시도 모두 실패)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,19 @@
|
||||
{}
|
||||
{
|
||||
"qwen-image-2512-GGUF-Q4-SamplerCustomAdvanced-v4": {
|
||||
"attempts": 4,
|
||||
"patterns": [
|
||||
"EmptyLatentImage",
|
||||
"EmptyQwenImageLayeredLatentImage",
|
||||
"ModelSamplingAuraFlow+EmptySD3LatentImage",
|
||||
"SamplerCustomAdvanced+ModelSamplingAuraFlow"
|
||||
],
|
||||
"all_results": "5KB 검정 latent",
|
||||
"hypothesis": "Qwen-Image GGUF Q4_K_S + CUDA 13.0 + PyTorch 2.12 환경 호환성 또는 다른 quant 필요 (Q8/fp16)",
|
||||
"next_attempts": [
|
||||
"non-GGUF Qwen-Image safetensors",
|
||||
"Lightning LoRA 적용",
|
||||
"다른 sampler (dpmpp_2m/ddim)",
|
||||
"다른 shift 값 (1.73/5.0)"
|
||||
]
|
||||
}
|
||||
}
|
||||
159
eval/meeting-103-grid.html
Normal file
@@ -0,0 +1,159 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko"><head><meta charset="utf-8">
|
||||
<title>meeting-103 v2.1 — ComfyUI 자율 R&D + 자산 라이브러리</title>
|
||||
<style>
|
||||
body { font-family: -apple-system, "Pretendard", system-ui, sans-serif; padding: 24px;
|
||||
background: linear-gradient(135deg, #FAF7FF 0%, #FFF0F5 100%); color: #4A3F5C; }
|
||||
h1 { color: #4A3F5C; margin-bottom: 6px; font-size: 24px; }
|
||||
.subtitle { color: #8B7AAE; font-size: 13px; margin-bottom: 24px; }
|
||||
h2 { color: #6B5B8E; margin-top: 32px; border-bottom: 2px solid #DCC9F3; padding-bottom: 6px; font-size: 18px; }
|
||||
.summary-box { background: white; border-radius: 14px; padding: 18px 20px; margin: 16px 0;
|
||||
box-shadow: 0 2px 8px rgba(180,140,200,0.15); }
|
||||
.summary-box h3 { margin-top: 0; font-size: 15px; }
|
||||
.summary-box ul { font-size: 12px; line-height: 1.7; margin: 6px 0; }
|
||||
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; margin: 14px 0; }
|
||||
.cell { background: white; border-radius: 14px; overflow: hidden; box-shadow: 0 4px 12px rgba(180,140,200,0.18); cursor: pointer; transition: all 0.2s; }
|
||||
.cell:hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(180,140,200,0.28); }
|
||||
.cell img { width: 100%; display: block; aspect-ratio: 9/16; object-fit: cover; }
|
||||
.cell.failed { background: linear-gradient(135deg, #fde2e4, #f3e8ff); display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 200px; padding: 16px; text-align: center; font-size: 12px; color: #6F2D2D; }
|
||||
.label { padding: 10px 12px; font-size: 12px; }
|
||||
.label-title { font-weight: 600; color: #4A3F5C; margin-bottom: 4px; }
|
||||
.label-meta { color: #8B7AAE; font-size: 11px; line-height: 1.5; }
|
||||
.label-auto-eval { color: #4A3F5C; font-size: 11px; margin-top: 6px; padding: 6px; background: #F5F0FF; border-radius: 6px; line-height: 1.5; }
|
||||
.label-flags { margin-top: 6px; }
|
||||
.flag { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 10px; margin-right: 4px; }
|
||||
.flag-vision { background: #FFF4D0; color: #8B6F1A; }
|
||||
.flag-suspicious { background: #FFD9D9; color: #8B1A1A; }
|
||||
.flag-pass-auto { background: #D5F0D5; color: #1A6F1A; }
|
||||
.modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(74,63,92,0.92); z-index: 999; cursor: zoom-out; }
|
||||
.modal img { max-width: 92%; max-height: 92%; margin: 4% auto; display: block; border-radius: 8px; }
|
||||
</style></head>
|
||||
<body>
|
||||
|
||||
<h1>meeting-103 v2.1 — ComfyUI 자율 R&D</h1>
|
||||
<div class="subtitle">
|
||||
3-stage 파이프라인 · 14 모델 풀 (9 설치 / 4 미설치) · 자산 라이브러리 7종 누적 ·
|
||||
합격 기준: 별이 ≥8 · 한자 ≥4.5 · 한옥 ≥4.5
|
||||
</div>
|
||||
|
||||
<div class="summary-box">
|
||||
<h3>📊 R1 진행 상태</h3>
|
||||
<ul>
|
||||
<li>✅ 4/5 정상: Pony A · Flux Dev A · Flux+PuLID A · Flux Dev B</li>
|
||||
<li>❌ 1/5 실패: <strong>Qwen-2512 C 한자카드 (5.5KB)</strong> — SamplerCustomAdvanced v4 도 실패, 4차 연속</li>
|
||||
<li>자동 best pastel: 1-1 (Pony) 42.5%</li>
|
||||
<li>시각 검수 필수 (별이 정합성·한자·한옥 자동 평가 미커버)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="summary-box">
|
||||
<h3>🎯 자산 라이브러리 R1 누적</h3>
|
||||
<ul>
|
||||
<li>models-performance.json: 4 모델 등재 (pony-v6 / flux-dev / pulid-flux / qwen-image-2512)</li>
|
||||
<li>workflows: 시각 평가 후 winner/partial 분류 (현재 0)</li>
|
||||
<li>prompts/failed-patterns: <strong>qwen-2512 4차 실패 패턴 등재</strong> (다음 시도: non-GGUF / Lightning LoRA / 다른 sampler)</li>
|
||||
<li>design-tokens: 합격 시 추출 (현재 0)</li>
|
||||
<li>loras: 별이 ≥7 + 3라운드+ + 8 미달 시 자동 학습</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2>라운드 1 — A 별이 (3 모델 비교)</h2>
|
||||
<div class="grid">
|
||||
<div class="cell" onclick="zoom(this)"><img src="../results/meeting-103/round1/1-1_thumb.png" data-original="../results/meeting-103/round1/1-1.png" alt="1-1">
|
||||
<div class="label">
|
||||
<div class="label-title">1-1: Pony V6 XL A 별이</div>
|
||||
<div class="label-meta">model=pony-v6, steps=28, cfg=7, sampler=dpmpp_2m karras, seed=42, 768×1344</div>
|
||||
<div class="label-auto-eval">
|
||||
자동 평가:<br>
|
||||
- 크기: 1501KB ✓<br>
|
||||
- 검정: <strong>0.61%</strong> ✓<br>
|
||||
- 파스텔: <strong>42.5%</strong> (R1 1위)<br>
|
||||
- 별이 8/10 · 한복 정합성 · Cute Horror — 시각 평가 필요
|
||||
</div>
|
||||
<div class="label-flags">
|
||||
<span class="flag flag-pass-auto">auto-pass</span>
|
||||
<span class="flag flag-vision">vision-check</span>
|
||||
</div>
|
||||
</div></div>
|
||||
<div class="cell" onclick="zoom(this)"><img src="../results/meeting-103/round1/1-2_thumb.png" data-original="../results/meeting-103/round1/1-2.png" alt="1-2">
|
||||
<div class="label">
|
||||
<div class="label-title">1-2: Flux Dev A 별이 (text only)</div>
|
||||
<div class="label-meta">model=flux-dev Q5 GGUF, steps=25, cfg=1, guidance=3.5, FluxGuidance, seed=42</div>
|
||||
<div class="label-auto-eval">
|
||||
자동 평가:<br>
|
||||
- 크기: 1121KB ✓<br>
|
||||
- 검정: <strong>3.11%</strong> ⚠️ (DESIGN.md §10 한계 근접)<br>
|
||||
- 파스텔: 20.1%<br>
|
||||
- 별이 정합성 — 시각 평가
|
||||
</div>
|
||||
<div class="label-flags">
|
||||
<span class="flag flag-suspicious">black-3pct</span>
|
||||
<span class="flag flag-vision">vision-check</span>
|
||||
</div>
|
||||
</div></div>
|
||||
<div class="cell" onclick="zoom(this)"><img src="../results/meeting-103/round1/1-3_thumb.png" data-original="../results/meeting-103/round1/1-3.png" alt="1-3">
|
||||
<div class="label">
|
||||
<div class="label-title">1-3: Flux Dev + PuLID A 별이 ⭐</div>
|
||||
<div class="label-meta">model=flux-dev + PuLID weight=0.85 fusion=mean, ref=byeolyi-default-768x1344, end_at=0.8</div>
|
||||
<div class="label-auto-eval">
|
||||
자동 평가:<br>
|
||||
- 크기: 1065KB ✓<br>
|
||||
- 검정: 0.73% ✓<br>
|
||||
- 파스텔: 30.2%<br>
|
||||
- <strong>별이 얼굴 ID matching 핵심</strong> — 시각 평가 필수
|
||||
</div>
|
||||
<div class="label-flags">
|
||||
<span class="flag flag-pass-auto">auto-pass</span>
|
||||
<span class="flag flag-vision">vision-id-check</span>
|
||||
</div>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<h2>라운드 1 — B 한옥</h2>
|
||||
<div class="grid">
|
||||
<div class="cell" onclick="zoom(this)"><img src="../results/meeting-103/round1/1-4_thumb.png" data-original="../results/meeting-103/round1/1-4.png" alt="1-4">
|
||||
<div class="label">
|
||||
<div class="label-title">1-4: Flux Dev B 한옥 (text only)</div>
|
||||
<div class="label-meta">model=flux-dev Q5 GGUF, NOT japanese / pagoda / wabi-sabi negative, seed=42</div>
|
||||
<div class="label-auto-eval">
|
||||
자동 평가:<br>
|
||||
- 크기: 1724KB ✓<br>
|
||||
- 검정: <strong>0.0%</strong> ✓ (완벽)<br>
|
||||
- 파스텔: 29.7%<br>
|
||||
- 한옥 vs 일본 구분 — 시각 평가 필수
|
||||
</div>
|
||||
<div class="label-flags">
|
||||
<span class="flag flag-pass-auto">auto-pass</span>
|
||||
<span class="flag flag-vision">korean-vs-japanese</span>
|
||||
</div>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<h2>라운드 1 — C 60갑자 한자카드</h2>
|
||||
<div class="grid">
|
||||
<div class="cell failed">
|
||||
<strong>1-5: Qwen-2512 C 한자카드 ❌</strong>
|
||||
<br>5.5KB 검정 latent 실패<br>(v6 R1 3차 + 본 R1 v4 = 총 4차 연속)
|
||||
<br><br>워크플로우 패턴:<br>SamplerCustomAdvanced + ModelSamplingAuraFlow + EmptySD3LatentImage
|
||||
<br><br>다음 시도 (R2):<br>non-GGUF safetensors / Lightning LoRA / 다른 sampler/shift
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-box">
|
||||
<h3>🔗 검수 채널</h3>
|
||||
<ul>
|
||||
<li>1순위 Vault: D:\Vault\8460s-image-rd\eval\meeting-103-grid.html (Syncthing 동기화 후)</li>
|
||||
<li>2순위 Gitea raw: <code>https://kakao-kakao2-server.tail31bd37.ts.net/choijaewook/8460s-image-rd/raw/branch/main/eval/meeting-103-grid.html</code></li>
|
||||
<li>썸네일 의무 충족 (모든 cell 1MB 미만, 최대 540KB)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="modal" onclick="this.style.display='none'"><img id="modal-img"></div>
|
||||
<script>
|
||||
function zoom(el) {
|
||||
const orig = el.querySelector('img').dataset.original || el.querySelector('img').src;
|
||||
document.getElementById('modal-img').src = orig;
|
||||
document.getElementById('modal').style.display = 'block';
|
||||
}
|
||||
</script>
|
||||
</body></html>
|
||||
84
eval/meeting-103-meta.json
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"session": "meeting-103-v2.1",
|
||||
"started_at": "2026-05-19T21:47:00+09:00",
|
||||
"current_round": 1,
|
||||
"rounds": [
|
||||
{
|
||||
"round": 1,
|
||||
"name": "초기 매트릭스 (5 조합)",
|
||||
"status": "completed",
|
||||
"combinations": [
|
||||
{
|
||||
"id": "1-1",
|
||||
"model": "pony-v6",
|
||||
"prompt_set": "A",
|
||||
"status": "OK",
|
||||
"auto_eval": {
|
||||
"size_kb": 1501,
|
||||
"black_pct": 0.61,
|
||||
"pastel_pct": 42.5
|
||||
},
|
||||
"needs_vision": true
|
||||
},
|
||||
{
|
||||
"id": "1-2",
|
||||
"model": "flux-dev",
|
||||
"prompt_set": "A",
|
||||
"status": "OK",
|
||||
"auto_eval": {
|
||||
"size_kb": 1121,
|
||||
"black_pct": 3.11,
|
||||
"pastel_pct": 20.1
|
||||
},
|
||||
"flags": [
|
||||
"black-3pct"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1-3",
|
||||
"model": "flux-dev+pulid",
|
||||
"prompt_set": "A",
|
||||
"status": "OK",
|
||||
"auto_eval": {
|
||||
"size_kb": 1065,
|
||||
"black_pct": 0.73,
|
||||
"pastel_pct": 30.2
|
||||
},
|
||||
"needs_vision_id": true
|
||||
},
|
||||
{
|
||||
"id": "1-4",
|
||||
"model": "flux-dev",
|
||||
"prompt_set": "B",
|
||||
"status": "OK",
|
||||
"auto_eval": {
|
||||
"size_kb": 1724,
|
||||
"black_pct": 0.0,
|
||||
"pastel_pct": 29.7
|
||||
},
|
||||
"needs_vision": true
|
||||
},
|
||||
{
|
||||
"id": "1-5",
|
||||
"model": "qwen-image-2512",
|
||||
"prompt_set": "C",
|
||||
"status": "FAILED",
|
||||
"size_bytes": 5540,
|
||||
"issue": "5KB 검정 latent (4차 연속)"
|
||||
}
|
||||
],
|
||||
"best_pastel": "1-1",
|
||||
"learning": {
|
||||
"qwen_workflow_3rd_attempt_failed": true,
|
||||
"next_round_hint": "R2 — Qwen 비-GGUF + Stage 3 Flux Refiner (Pony 1-1 → refiner) + 다른 PuLID fusion"
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": {
|
||||
"byeolyi": 8,
|
||||
"hanja": 4.5,
|
||||
"hanok": 4.5
|
||||
},
|
||||
"raw_html_url": "https://kakao-kakao2-server.tail31bd37.ts.net/choijaewook/8460s-image-rd/raw/branch/main/eval/meeting-103-grid.html",
|
||||
"vault_path": "D:\\Vault\\8460s-image-rd\\eval\\meeting-103-grid.html"
|
||||
}
|
||||
BIN
results/meeting-103/round1/1-1.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
results/meeting-103/round1/1-1_thumb.png
Normal file
|
After Width: | Height: | Size: 540 KiB |
BIN
results/meeting-103/round1/1-2.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
results/meeting-103/round1/1-2_thumb.png
Normal file
|
After Width: | Height: | Size: 375 KiB |
BIN
results/meeting-103/round1/1-3.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
results/meeting-103/round1/1-3_thumb.png
Normal file
|
After Width: | Height: | Size: 378 KiB |
BIN
results/meeting-103/round1/1-4.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
results/meeting-103/round1/1-4_thumb.png
Normal file
|
After Width: | Height: | Size: 513 KiB |
BIN
results/meeting-103/round1/1-5.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
results/meeting-103/round1/1-5_thumb.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
99
results/meeting-103/round1/meta.json
Normal file
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"round": 1,
|
||||
"captured_at": "2026-05-19T21:57:10.975065",
|
||||
"combos": [
|
||||
{
|
||||
"id": "1-1",
|
||||
"prompt_id": "3b6d1ef1-163c-484b-93bd-1965a5dd59b3",
|
||||
"models_used": [
|
||||
"pony-v6"
|
||||
],
|
||||
"prompt_set": "A",
|
||||
"stage": "1-only",
|
||||
"status": "OK",
|
||||
"size_bytes": 1537272,
|
||||
"width": 768,
|
||||
"height": 1344,
|
||||
"aspect": 0.5714,
|
||||
"black_pct": 0.61,
|
||||
"pastel_pct": 42.5,
|
||||
"thumb_size_kb": 540
|
||||
},
|
||||
{
|
||||
"id": "1-2",
|
||||
"prompt_id": "472d7fc4-0234-40f9-be7d-3e25e3d04b32",
|
||||
"models_used": [
|
||||
"flux-dev"
|
||||
],
|
||||
"prompt_set": "A",
|
||||
"stage": "1-only",
|
||||
"status": "OK",
|
||||
"size_bytes": 1148193,
|
||||
"width": 768,
|
||||
"height": 1344,
|
||||
"aspect": 0.5714,
|
||||
"black_pct": 3.11,
|
||||
"pastel_pct": 20.1,
|
||||
"thumb_size_kb": 374
|
||||
},
|
||||
{
|
||||
"id": "1-3",
|
||||
"prompt_id": "117742cb-1615-44ec-9f7b-5c558af278f8",
|
||||
"models_used": [
|
||||
"flux-dev",
|
||||
"pulid-flux"
|
||||
],
|
||||
"prompt_set": "A",
|
||||
"stage": "1+pulid",
|
||||
"status": "OK",
|
||||
"size_bytes": 1090948,
|
||||
"width": 768,
|
||||
"height": 1344,
|
||||
"aspect": 0.5714,
|
||||
"black_pct": 0.73,
|
||||
"pastel_pct": 30.2,
|
||||
"thumb_size_kb": 377
|
||||
},
|
||||
{
|
||||
"id": "1-4",
|
||||
"prompt_id": "b4ec98ac-2f7f-4dd5-9e47-5bfdcc78cf3c",
|
||||
"models_used": [
|
||||
"flux-dev"
|
||||
],
|
||||
"prompt_set": "B",
|
||||
"stage": "1-only",
|
||||
"status": "OK",
|
||||
"size_bytes": 1765589,
|
||||
"width": 768,
|
||||
"height": 1344,
|
||||
"aspect": 0.5714,
|
||||
"black_pct": 0.0,
|
||||
"pastel_pct": 29.7,
|
||||
"thumb_size_kb": 512
|
||||
},
|
||||
{
|
||||
"id": "1-5",
|
||||
"prompt_id": "54ee2273-7585-4618-8b51-b74ded23cf05",
|
||||
"models_used": [
|
||||
"qwen-image-2512"
|
||||
],
|
||||
"prompt_set": "C",
|
||||
"stage": "1-only",
|
||||
"workflow_variant": "SamplerCustomAdvanced v4",
|
||||
"status": "FAILED_SMALL",
|
||||
"size_bytes": 5540,
|
||||
"width": 768,
|
||||
"height": 1344,
|
||||
"aspect": 0.5714,
|
||||
"black_pct": 100.0,
|
||||
"pastel_pct": 0.0,
|
||||
"thumb_size_kb": 1
|
||||
}
|
||||
],
|
||||
"learning": {
|
||||
"passed_count": 4,
|
||||
"failed_count": 1,
|
||||
"best_pastel": "1-1",
|
||||
"next_round_hint": "auto-determine based on round learning"
|
||||
}
|
||||
}
|
||||
BIN
scripts/__pycache__/round_pipeline.cpython-313.pyc
Normal file
174
scripts/round_pipeline.py
Normal file
@@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
meeting-103 v2.1 라운드 파이프라인
|
||||
- 결과 회수 (HTTP /view)
|
||||
- 썸네일 생성 (1MB 미만)
|
||||
- 자동 평가 (black/pastel/aspect)
|
||||
- 자산 라이브러리 7종 누적
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
|
||||
REPO_ROOT = Path("/mnt/ssd1/dev/projects/8460s-image-rd")
|
||||
COMFY = "http://100.123.6.62:8188"
|
||||
|
||||
THRESHOLDS = {"byeolyi": 8, "hanja": 4.5, "hanok": 4.5}
|
||||
PROTECTED = {"qwen-image-2.0", "qwen-image-2512", "qwen-image-layered", "qwen-edit-2511",
|
||||
"flux-dev", "flux-schnell", "flux-kontext", "flux-fill", "flux-redux",
|
||||
"pulid-flux", "pony-v6", "illustrious", "lightning-lora-qwen"}
|
||||
|
||||
|
||||
def http_view(filename):
|
||||
"""ComfyUI HTTP /view 으로 PNG 다운로드 (bytes 반환)"""
|
||||
url = f"{COMFY}/view?filename={filename}&type=output"
|
||||
with urllib.request.urlopen(url, timeout=20) as r:
|
||||
return r.read()
|
||||
|
||||
|
||||
def history_outputs(prompt_id):
|
||||
"""prompt_id → 출력 파일명 목록"""
|
||||
url = f"{COMFY}/history/{prompt_id}"
|
||||
with urllib.request.urlopen(url, timeout=10) as r:
|
||||
data = json.load(r)
|
||||
if not data:
|
||||
return []
|
||||
info = list(data.values())[0]
|
||||
outs = info.get("outputs", {})
|
||||
return [f["filename"] for n, i in outs.items() for f in i.get("images", [])]
|
||||
|
||||
|
||||
def fetch_round_results(round_n, combos):
|
||||
"""combos = [{"id": "1-1", "prompt_id": "...", "expected_filename": "..."}]"""
|
||||
round_dir = REPO_ROOT / "results" / "meeting-103" / f"round{round_n}"
|
||||
round_dir.mkdir(parents=True, exist_ok=True)
|
||||
results = []
|
||||
for c in combos:
|
||||
fns = history_outputs(c["prompt_id"]) if c.get("prompt_id") else []
|
||||
fn = fns[0] if fns else c.get("expected_filename")
|
||||
if not fn:
|
||||
results.append({**c, "status": "no_filename"})
|
||||
continue
|
||||
try:
|
||||
data = http_view(fn)
|
||||
out_png = round_dir / f"{c['id']}.png"
|
||||
out_png.write_bytes(data)
|
||||
size = len(data)
|
||||
# 썸네일 (1MB 미만 의무)
|
||||
img = Image.open(out_png).convert("RGB")
|
||||
thumb = img.copy()
|
||||
thumb.thumbnail((512, 768))
|
||||
thumb_path = round_dir / f"{c['id']}_thumb.png"
|
||||
thumb.save(thumb_path, "PNG", optimize=True)
|
||||
assert thumb_path.stat().st_size < 1_000_000, "thumbnail >1MB"
|
||||
# 자동 평가
|
||||
w, h = img.size
|
||||
pixels = list(img.getdata())
|
||||
total = len(pixels)
|
||||
near_black = sum(1 for r, g, b in pixels if r < 30 and g < 30 and b < 30)
|
||||
pastel = sum(1 for r, g, b in pixels if (r > 150 and b > 150) or (r > 200 and g > 150 and b > 150))
|
||||
results.append({
|
||||
**c, "status": "OK" if size > 50000 else "FAILED_SMALL",
|
||||
"size_bytes": size, "width": w, "height": h,
|
||||
"aspect": round(w / h, 4),
|
||||
"black_pct": round(near_black / total * 100, 2),
|
||||
"pastel_pct": round(pastel / total * 100, 1),
|
||||
"thumb_size_kb": thumb_path.stat().st_size // 1024,
|
||||
})
|
||||
except Exception as e:
|
||||
results.append({**c, "status": f"ERROR:{type(e).__name__}", "error": str(e)[:120]})
|
||||
return results
|
||||
|
||||
|
||||
def update_models_performance(round_n, combo_results):
|
||||
"""자산 라이브러리 1 — 모델 차원별 점수 누적"""
|
||||
path = REPO_ROOT / "assets-library" / "models-performance.json"
|
||||
data = json.loads(path.read_text())
|
||||
for c in combo_results:
|
||||
if c.get("status") != "OK":
|
||||
continue
|
||||
for model in c.get("models_used", []):
|
||||
mdata = data["models"].setdefault(model, {
|
||||
"rounds_used": 0,
|
||||
"dimensions": {dim: [] for dim in data["_dimensions"]},
|
||||
"avg_scores": {}, "best_combinations": [], "verdict": ""
|
||||
})
|
||||
mdata["rounds_used"] = max(mdata["rounds_used"], round_n)
|
||||
scores = c.get("scores", {})
|
||||
for dim, score in scores.items():
|
||||
if score is not None and dim in mdata["dimensions"]:
|
||||
mdata["dimensions"][dim].append(score)
|
||||
for dim, arr in mdata["dimensions"].items():
|
||||
if arr:
|
||||
mdata["avg_scores"][dim] = round(sum(arr) / len(arr), 2)
|
||||
path.write_text(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
return path
|
||||
|
||||
|
||||
def save_workflow_to_library(combo, workflow_json, round_n):
|
||||
"""자산 라이브러리 2 — winner/partial 워크플로 보존"""
|
||||
scores = combo.get("scores", {})
|
||||
if not scores:
|
||||
return None
|
||||
if all(scores.get(d, 0) >= t for d, t in THRESHOLDS.items()):
|
||||
category = "winner"
|
||||
elif max(scores.values()) >= 5:
|
||||
category = "partial"
|
||||
else:
|
||||
return None
|
||||
best_dim = max(scores, key=scores.get)
|
||||
model = (combo.get("models_used") or ["unknown"])[0]
|
||||
name = f"{category}-R{round_n}-{combo['id']}-{model}.json"
|
||||
path = REPO_ROOT / "assets-library" / "workflows" / name
|
||||
path.write_text(json.dumps(workflow_json, indent=2, ensure_ascii=False))
|
||||
# 인덱스 갱신
|
||||
idx_path = REPO_ROOT / "assets-library" / "workflows" / "_index.json"
|
||||
idx = json.loads(idx_path.read_text())
|
||||
entry = {"name": name, "category": category, "best_dimension": best_dim,
|
||||
"scores": scores, "round": round_n, "model": model}
|
||||
idx.setdefault(category, []).append(entry)
|
||||
idx["count"] = len(idx.get("winner", [])) + len(idx.get("partial", []))
|
||||
idx_path.write_text(json.dumps(idx, indent=2, ensure_ascii=False))
|
||||
return path
|
||||
|
||||
|
||||
def save_results_meta(round_n, results, learning=None):
|
||||
"""라운드 메타 JSON 저장 (그리드 보조)"""
|
||||
path = REPO_ROOT / "results" / "meeting-103" / f"round{round_n}" / "meta.json"
|
||||
payload = {
|
||||
"round": round_n,
|
||||
"captured_at": __import__("datetime").datetime.now().isoformat(),
|
||||
"combos": results,
|
||||
"learning": learning or {}
|
||||
}
|
||||
path.write_text(json.dumps(payload, indent=2, ensure_ascii=False))
|
||||
return path
|
||||
|
||||
|
||||
def extract_learning(results):
|
||||
"""라운드 학습 추출 (RAG-style)"""
|
||||
passed = []
|
||||
failed = []
|
||||
for r in results:
|
||||
if r.get("status") != "OK":
|
||||
failed.append({"id": r["id"], "reason": r.get("status")})
|
||||
continue
|
||||
if r.get("black_pct", 0) > 5:
|
||||
failed.append({"id": r["id"], "reason": "검정 5%+", "black_pct": r["black_pct"]})
|
||||
else:
|
||||
passed.append({"id": r["id"], "pastel_pct": r.get("pastel_pct"), "size": r.get("size_bytes")})
|
||||
best = sorted([r for r in results if r.get("status") == "OK"],
|
||||
key=lambda x: x.get("pastel_pct", 0), reverse=True)
|
||||
return {
|
||||
"passed_count": len(passed),
|
||||
"failed_count": len(failed),
|
||||
"best_pastel": best[0]["id"] if best else None,
|
||||
"next_round_hint": "auto-determine based on round learning"
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("round_pipeline.py — 라이브러리, import 후 사용")
|
||||