Spaces:
Sleeping
Sleeping
feat: 6-axis creative constraints + emergent references \u2014 block the safe-mean trap
Browse files- aether_ad/core/creative_constraints.py +180 -0
- aether_ad/core/engine.py +16 -1
- aether_ad/llm/base.py +5 -1
- aether_ad/llm/prompts/apply_collision.txt +37 -42
- static/app.js +27 -0
- static/index.html +2 -2
- static/styles.css +41 -0
aether_ad/core/creative_constraints.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Concrete creative constraints โ injected into every collision prompt to
|
| 2 |
+
force the LLM out of the "safe mean" of its training distribution.
|
| 3 |
+
|
| 4 |
+
Why this matters
|
| 5 |
+
----------------
|
| 6 |
+
Large models trained on mixed corpora fall back to the **median of Korean ad
|
| 7 |
+
clichรฉs** (family photos, graduation hugs, rainy umbrellas) when given only
|
| 8 |
+
abstract directives. Hard concrete constraints โ specific objects, specific
|
| 9 |
+
camera shots, specific sensory inversions โ physically block that escape
|
| 10 |
+
route. The model has to synthesize something novel to satisfy all of them.
|
| 11 |
+
|
| 12 |
+
Pools are intentionally specific and Korean-cultural where useful. Every
|
| 13 |
+
entry is an imperative "must appear / must use / must feel like" โ not a
|
| 14 |
+
mere suggestion.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
from __future__ import annotations
|
| 18 |
+
|
| 19 |
+
import random
|
| 20 |
+
from dataclasses import dataclass
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
# โโ Concrete physical objects โ must appear as a tangible prop at a specific moment
|
| 24 |
+
CONCRETE_OBJECTS: list[str] = [
|
| 25 |
+
"๋
น์จ ๋ชป ํ๋", "๋จ๊ฐ์ง ๋ฐ๋", "๊นจ์ง ๋์๊ธฐ ํํธ", "๋ง๋ผ๋ฒ๋ฆฐ ์ฅ๋ฏธ ํ ์ก์ด",
|
| 26 |
+
"ํ๋ผ์คํฑ ๋น", "๋ฌผ์ ์ ์ ๊ณต์ฑ
", "๊ตฌ๋ฉ ๋ ์๋ง", "๋ฐ์ผ๋ก ์ ํ ํธ์ง์ง",
|
| 27 |
+
"์ค๋๋ ๋์ ์ง๊ฐ", "๋ก์ ํ์ค์๊ณ ์ค", "๋ฒ๋ ค์ง ์ฐ์ฐ์ด", "๋ง๋ผ๋ถ์ ๊ป",
|
| 28 |
+
"์์์ง ์๊ธ", "ํ ๋๋ก ํ๋ฒ๋ฆฐ ์ฑ๋ฅ", "๋ถ์์ง ํ๊ด๋ฑ", "์ค๋๋ ๋์๋ฆฌ",
|
| 29 |
+
"์ฐจ๊ฐ๊ฒ ์์ ์ปคํผ ์ฐ๊บผ๊ธฐ", "๋
น์ ํฌ๋ ์ฉ", "๊ธธ ํ๊ฐ์ด๋ฐ ๋์ธ ํ๋ผ์คํฑ ์์",
|
| 30 |
+
"๋์ด์ง ๋ชฉ์ค", "๋จผ์ง ์์ธ ๋ณํ", "๊ตฌ๊ฒจ์ง ์์์ฆ ๋ญ์น", "๋๊ฐ ๋๊ณ ๊ฐ ๋ด๋ฐฐ๊ฝ์ด",
|
| 31 |
+
"๋จผ์ง ๋ CRT ๋ชจ๋ํฐ", "๋ฐ์ฏค ๋
น์ ์์ด์คํฌ๋ฆผ", "๋น๋๋ด์ง์ ๋ ๊น์น",
|
| 32 |
+
"๋ฐ๋์ ๋ ๋ฆฌ๋ ํ์๋ง ์กฐ๊ฐ", "๋
น์จ ๋นจ๋ ๊ฑด์กฐ๋", "๊นจ์ง ๋ฐ์ง",
|
| 33 |
+
"๋งค๋ฏธ ํ๋ฌผ", "๊ธ์ด ๊ฐ ๊ฑฐ์ธ",
|
| 34 |
+
]
|
| 35 |
+
|
| 36 |
+
# โโ Camera shots โ must be the dominant POV of at least one cut
|
| 37 |
+
CAMERA_SHOTS: list[str] = [
|
| 38 |
+
"CCTV ํ๋ทฐ (์ฒ์ฅ ํ๋ฐฑ)", "์๋ฆฌ๋ฒ ์ดํฐ ์ฒ์ฅ ์ด์ ๋ ์ฆ",
|
| 39 |
+
"๋ฌผ์์์ ์๋ฉด ์๋ฅผ ์ฌ๋ ค๋ค๋ณด๋ ์์ ", "๋ฒฝ์ง๋ฅผ ๋ซ๊ณ ๋์ค๋ ์์ ",
|
| 40 |
+
"๋์ฅ๊ณ ์์ชฝ์์ ๋ฌธ์ด ์ด๋ฆฌ๋ ์์ ", "๋จธ๋ฆฌ์นด๋ฝ ์ฌ์ด์์ ๋ณธ ์ผ๊ตด",
|
| 41 |
+
"๋ฐ๋ฅ์ ๋จ์ด์ง ํด๋ํฐ ํ๋ฉด์ด ์ฌ๋ ค๋ค๋ณด๋ ์์ ", "๋์ฝ ํ ์ฅ์ ์์ (๋
์ผ๋ก ๋จ์ด์ง๋ฉด์)",
|
| 42 |
+
"์๊ณ ๋ค์ด์ผ ์์ชฝ์์ ๋ณธ ๋ฐ๋", "์งํ์ฒ ์ฐฝ๋ฌธ ๋ฐ์ฌ์ ์์ ์น๊ฐ",
|
| 43 |
+
"๊ทน์ ์ ์ฌ๋ก์ฐ๋ชจ์
(1์ด๋ฅผ 15์ด๋ก ๋์ธ)",
|
| 44 |
+
"๊ทน๊ณ ์ ํ์๋ฉ์ค (8์๊ฐ์ 3์ด๋ก ์์ถ)",
|
| 45 |
+
"์ดํ์ ์นด๋ฉ๋ผ ์์ ", "X-ray ์์ ", "๋๋ก ํ๋ทฐ์์ ์์ง ํ๊ฐ",
|
| 46 |
+
"๊ฑฐ์ธ ์์ ๊ฑฐ์ธ (๋ฌดํ ๋ฐ์ฌ)", "์ฃผ์ธ๊ณต ๋คํต์๋ง ๋ณด์ด๋ ์์ ",
|
| 47 |
+
"์ปคํผ ์ ์ ์๋ฉด์ ๋น์น ์ฒ์ฅ", "์๋์ฐจ ์ฌ์ด๋๋ฏธ๋ฌ ์ ์ฅ๋ฉด",
|
| 48 |
+
"์นด๋ฉ๋ผ๊ฐ ์ธ๋ฌผ์ ๋น๋น ๋๋ 360๋ ๋ฌ๋ฆฌ์ท",
|
| 49 |
+
]
|
| 50 |
+
|
| 51 |
+
# โโ Sensory inversions โ one sensory channel must be translated into another
|
| 52 |
+
SYNESTHETIC_INVERSIONS: list[str] = [
|
| 53 |
+
"์๊ณ ์ด์นจ ์๋ฆฌ๊ฐ ํ๋์ ์ด๋ง์ผ๋ก ๋ฒ์ญ๋จ",
|
| 54 |
+
"ํ๋น์ด ๋ฌด๊ฒ๋ก ๋ณํด ์ด๊นจ๋ฅผ ์ง๋๋ฆ",
|
| 55 |
+
"์นจ๋ฌต์ด ํผ๋ถ๋ฅผ ์ธ๊ณ ์ง๋๊ฐ๋ ๊ฐ์ด์ผ๋ก ํํ๋จ",
|
| 56 |
+
"ํ์ธ์ ๋๋น์ด ๋์๋ก ๊ฐ์ง๋จ",
|
| 57 |
+
"SNS ์๋ฆผ์ด ๊ท์ ๋ฌผ๋ฐฉ์ธ๋ก ๋จ์ด์ง",
|
| 58 |
+
"ํ์จ์ด ์์ผ๋ก ๋ฒ์ ธ๋๊ฐ",
|
| 59 |
+
"๊ธฐ์ต์ด ์์ ์ฅ ์ ์๋ ์์ ๋๋ก ๋ฌผ์งํ๋จ",
|
| 60 |
+
"์๊ฐ์ ํ๋ฆ์ด ๋ฑ์ ํ๊ณ ๋ด๋ ค์ค๋ ์ฐจ๊ฐ์ด ๊ฐ์ด์ผ๋ก",
|
| 61 |
+
"ํ์ธ์ ๋ง์ด ๊ณต์ค์ ๊ธ์๋ก ๋ ์ค๋ฅด๊ณ ๋ฌด๊ฒ๊ฐ ์์",
|
| 62 |
+
"๋ถ๋
ธ๊ฐ ์๋์์ ํ๋ ๋ถ๊ฝ์ผ๋ก ํ์ค๋ฆ",
|
| 63 |
+
"์ธ๋ก์์ด ์
์์์ ๋
น๋ ์ผ์์ ๊ฐ์ด",
|
| 64 |
+
"๊ฑฑ์ ์ด ๋จธ๋ฆฌ์นด๋ฝ ๋์์ ์๊ธ์ผ๋ก ๊ตณ์",
|
| 65 |
+
"๊ทธ๋ฆฌ์์ด ๋ฐ๋ฐ์์ ์ง์ ์๊ฐ๋ก ์ฌ๋ผ์ด",
|
| 66 |
+
"๊ฑฐ์ง๋ง์ด ์จ๊ฒฐ๋ง๋ค ๊ฒ์ ๊นํธ๋ก ๋จ์ด์ง",
|
| 67 |
+
"ํผ๊ณค์ด ๋ชฉ๋๋ฏธ์์ ๋ฉ๋ฉ์ด๋ก ๋ณํด ๋งค๋ฌ๋ฆผ",
|
| 68 |
+
]
|
| 69 |
+
|
| 70 |
+
# โโ Genre subversion โ starts as one genre, ends as another
|
| 71 |
+
GENRE_TWISTS: list[str] = [
|
| 72 |
+
"๋ช
์ ๊ฐ์กฑ ๊ด๊ณ ๋ก ์์ โ ์ค๋ฐ๋ถํฐ ์ฌ์ด๋ฒํํฌ ๋์คํ ํผ์",
|
| 73 |
+
"์๋ฅ ์์ ๊ด๊ณ ๋ก ์์ โ ์ข๋น ์ํฌ์นผ๋ฆฝ์ค",
|
| 74 |
+
"๋ก๋งจํฑ ์ฝ๋ฏธ๋๋ก ์์ โ ๋คํ๋ฉํฐ๋ฆฌ ์ธํฐ๋ทฐ",
|
| 75 |
+
"๊ณต์ต๊ด๊ณ ๋๋์ผ๋ก ์์ โ ๋ฒ์ฃ ์ค๋ฆด๋ฌ",
|
| 76 |
+
"์๋ ๊ต์ก ๊ด๊ณ ๋ก ์์ โ ํ๋ฆ ๋์๋ฅด",
|
| 77 |
+
"ํ๋ง ์ฌํ ๊ด๊ณ ๋ก ์์ โ ์ฐ์ฃผ SF",
|
| 78 |
+
"์คํฌ์ธ ๊ด๊ณ ๋ก ์์ โ ์กฐ์ฉํ ์ฒ ํ ๋ช
์",
|
| 79 |
+
"๋ช
ํ ๋ญ์
๋ฆฌ ๊ด๊ณ ๋ก ์์ โ ๊ธธ๊ฑฐ๋ฆฌ ๋คํ๋ฉํฐ๋ฆฌ",
|
| 80 |
+
"์จ๋ฉ ๋ก๋งจ์ค๋ก ์์ โ ์ฌ๋ฉ์คํฑ ์ฝ๋ฏธ๋",
|
| 81 |
+
"๊ณตํฌ ๋ถ์๊ธฐ๋ก ์์ โ ๋ฐ๋ปํ ๊ฐ์กฑ๋ฌผ",
|
| 82 |
+
"์ ํ๋ธ ๋ธ์ด๋ก๊ทธ ์คํ์ผ๋ก ์์ โ 18์ธ๊ธฐ ์๋๊ทน",
|
| 83 |
+
"๋ด์ค ์๋ณด ํฌ๋งท์ผ๋ก ์์ โ ์ดํ์ค ๋ชฝํ๊ทน",
|
| 84 |
+
"๋ช
์ ์ฑ ๊ด๊ณ ๋ก ์์ โ ์ก์
์ถ๊ฒฉ",
|
| 85 |
+
]
|
| 86 |
+
|
| 87 |
+
# โโ Temporal structures โ must be the backbone of the 15/30s
|
| 88 |
+
TIME_STRUCTURES: list[str] = [
|
| 89 |
+
"15์ด ์ ์ฒด๊ฐ 1์ด์ ์ด์ฌ๋ก๋ชจ์
(์ธ์ ๋ฉ์ถ ๋ฏ)",
|
| 90 |
+
"15์ด ์์ 100๋
์ด ํ๋ฆ (๋ฐฐ๊ฒฝ์ด ๊ณ์ ๋ฐ๋)",
|
| 91 |
+
"์ด์นจ์ด ์ญ๋ฐฉํฅ์ผ๋ก ๊ฐ๋ฉฐ ์๊ฐ ์ญ์ฃผํ",
|
| 92 |
+
"3์ด๋ง๋ค ๋ค๋ฅธ ์๋๋ก ์ ํ (์กฐ์ โ 1980 โ ํ์ฌ โ 2080)",
|
| 93 |
+
"๊ฐ์ 3์ด ์ฅ๋ฉด์ด ๋ค์ฏ ๋ฒ ๋ฐ๋ณต๋๋ฉฐ ๋งค๋ฒ ํ ์์์ฉ ๋ฌ๋ผ์ง",
|
| 94 |
+
"ํ๋ฉด์ด ๋ง์ง๋ง 1์ด๋ง ์ ์ธํ๊ณ ์ ์ง ์ฌ์ง์ผ๋ก ํ๋ฆฝ",
|
| 95 |
+
"์๊ฐ์ด ๋ถ๊ธฐ๋์ด ๋ ๊ฐ์ ๊ฐ๋ฅ์ฑ์ด ๋์์ ์ฌ์",
|
| 96 |
+
"๋ง์ง๋ง 1์ด๊ฐ ์๋์ ์ผ๋ก ์๋ต๋์ด ๊ด๊ฐ์ด ์์ฑ",
|
| 97 |
+
"ํ๋ ์์ด ๊ฑฐ๊พธ๋ก ์์ด๋ฉฐ ๋ง์ง๋ง์ ์ฒซ ํ๋ ์์ด ๋ค์ ๋ฑ์ฅ",
|
| 98 |
+
"ํ ์ธ๋ฌผ์ ํ๋ฃจ ์ ์ฒด๊ฐ ๊ทธ ์ฌ๋์ ๋ ๊น๋นก์ ์์ ์ถ์๋จ",
|
| 99 |
+
"๋ฑ์ฅ ์ธ๋ฌผ์ ์ฌ๋ฐ๊ณผ ์๊ณ๊ฐ ๋๊ธฐํ๋์ด ๋ฐ๋ ๋จ์๋ก ์ปท ์ ํ",
|
| 100 |
+
"๋ฐฐ๊ฒฝ ์์
์ด 15์ด๋ฅผ ๊ฑฐ๊พธ๋ก ์ฌ์ (์์
์ด ๋
น๋ ๋ฏ)",
|
| 101 |
+
]
|
| 102 |
+
|
| 103 |
+
# โโ Korean-specific ์์ฑ์ด/์ํ์ด โ at least one must be the sonic anchor
|
| 104 |
+
KOREAN_ONOMATOPOEIA: list[str] = [
|
| 105 |
+
"์ฌ๊ฐ์ฌ๊ฐ", "๋ฐ์ค๋ฝ", "์จ๊ทธ๋", "์๊ธ์๊ธ", "๋ถ์ค์ค",
|
| 106 |
+
"๋๊ฐ๋๊ฐ", "์๊ทผ์๊ทผ", "๋๋", "์ซ๋์ซ๋", "์์ญ",
|
| 107 |
+
"ํ๋ฅด๋ฅด", "ํ๋์ ", "ํ์ฒญ", "์ฒ ๋ ", "๊พธ๋ฌผ๊พธ๋ฌผ",
|
| 108 |
+
"๋ํฐ๋ํฐ", "ํกํก", "์ซ", "์ฅ์ฅ", "๊น๋ฅด๋ฅด",
|
| 109 |
+
]
|
| 110 |
+
|
| 111 |
+
# โโ 2 iconic emergent-ad references embedded in prompt as calibration
|
| 112 |
+
# (used as BRIEF examples so model sees what "์ฐฝ๋ฐ" actually looks like โ
|
| 113 |
+
# not to copy, but to lift its target distribution)
|
| 114 |
+
EMERGENT_AD_REFS = """
|
| 115 |
+
์ฐธ๊ณ โ ์ญ๋ ์ฐฝ๋ฐ์ ๊ด๊ณ ์ '๋๋' (์์ ๊ธ์ง, ๋ถ์๊ธฐ๋ง ์ฐธ์กฐ):
|
| 116 |
+
ยท Sony Bravia Balls: ์ํ๋์์ค์ฝ ์ธ๋์์ ์์ญ๋ง ๊ฐ ์ปฌ๋ฌ ํฑํฑ๋ณผ์ด ๋ฌด์์ผ๋ก
|
| 117 |
+
๊ตด๋ฌ ๋ด๋ ค์ค๋ฉฐ ๋์๋ฅผ ์์ผ๋ก ๋ค๋ฎ์ (์ ํ์ TV์ง๋ง TV๊ฐ ํ ๋ฒ๋ ์ ๋์ด).
|
| 118 |
+
ยท Honda Cog: ์ ๊ตํ ๊ธฐ๊ณ ๋ถํ๋ค์ด ๊ณจ๋๋ฒ๊ทธ ๋จธ์ ์ผ๋ก ์ฐ์ ๋ฐ์ํ๋ค๊ฐ ๋ง์ง๋ง์
|
| 119 |
+
์๋์ฐจ๊ฐ ์ค์ค๋ก ๊ตด๋ฌ ๋์ด (2๋ถ ๋ด๋ด ์๋์ฐจ 0 ๋ฑ์ฅ).
|
| 120 |
+
์ด ๋ ์์์ ๊ณตํต์ : ์ถ์์ ๋ฌธ์ ๊ทธ๋๋ก ๋ฌผ๋ฆฌํ, ์ ํ์ ์นจ๋ฌต์ผ๋ก ๋งํจ.
|
| 121 |
+
"""
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
@dataclass(frozen=True)
|
| 125 |
+
class CreativeConstraints:
|
| 126 |
+
concrete_object: str
|
| 127 |
+
camera_shot: str
|
| 128 |
+
synesthesia: str
|
| 129 |
+
genre_twist: str
|
| 130 |
+
time_structure: str
|
| 131 |
+
onomatopoeia: str
|
| 132 |
+
|
| 133 |
+
def as_prompt_block(self) -> str:
|
| 134 |
+
return (
|
| 135 |
+
"[์ฐฝ๋ฐ ๊ฐ์ ์กฐ๊ฑด โ ์ด๋ฒ ์๋ ์ ์ฉ, ๊ฐ ํญ๋ชฉ์ ์ฅ๋ฉด์ ๋ฐ๋์ ์ฒดํ]\n"
|
| 136 |
+
f" ยท ๋ฑ์ฅ ์ค๋ธ์ : **{self.concrete_object}** (์ฅ๋ฉด ์ค๋ฐ ์ ํ์ ์ ๋ฑ์ฅ)\n"
|
| 137 |
+
f" ยท ์นด๋ฉ๋ผ ์์ : **{self.camera_shot}** (์ต์ 1์ปท ์ด์ ์ง๋ฐฐ์ POV)\n"
|
| 138 |
+
f" ยท ๊ณต๊ฐ๊ฐ ๋ฒ์ญ: **{self.synesthesia}** (ํ ๋ฒ ์ด์ ์์ ์ ๋
น์ฌ ๋ฃ์ ๊ฒ)\n"
|
| 139 |
+
f" ยท ์ฅ๋ฅด ์ ํ: **{self.genre_twist}** (์ฅ๋ฉด ํ๋ฆ์ด ์ด ๊ตด๊ณก์ ํ์ผ ํจ)\n"
|
| 140 |
+
f" ยท ์๊ฐ ๊ตฌ์กฐ: **{self.time_structure}**\n"
|
| 141 |
+
f" ยท ์๋ฆฌ ์ต์ปค (์์ฑ์ด): **{self.onomatopoeia}** (๋ณธ๋ฌธ์ 1ํ ๋ช
์)"
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
def as_summary(self) -> dict:
|
| 145 |
+
"""Return a JSON-friendly summary for UI display."""
|
| 146 |
+
return {
|
| 147 |
+
"object": self.concrete_object,
|
| 148 |
+
"shot": self.camera_shot,
|
| 149 |
+
"synesthesia": self.synesthesia,
|
| 150 |
+
"genre_twist": self.genre_twist,
|
| 151 |
+
"time": self.time_structure,
|
| 152 |
+
"onomatopoeia": self.onomatopoeia,
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def pick_constraints(rng: random.Random | None = None) -> CreativeConstraints:
|
| 157 |
+
"""Draw one item from each pool for this seed. Each pool is wide enough
|
| 158 |
+
that parallel seeds in the same run almost never collide on any axis."""
|
| 159 |
+
rng = rng or random
|
| 160 |
+
return CreativeConstraints(
|
| 161 |
+
concrete_object=rng.choice(CONCRETE_OBJECTS),
|
| 162 |
+
camera_shot=rng.choice(CAMERA_SHOTS),
|
| 163 |
+
synesthesia=rng.choice(SYNESTHETIC_INVERSIONS),
|
| 164 |
+
genre_twist=rng.choice(GENRE_TWISTS),
|
| 165 |
+
time_structure=rng.choice(TIME_STRUCTURES),
|
| 166 |
+
onomatopoeia=rng.choice(KOREAN_ONOMATOPOEIA),
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
__all__ = [
|
| 171 |
+
"CONCRETE_OBJECTS",
|
| 172 |
+
"CAMERA_SHOTS",
|
| 173 |
+
"SYNESTHETIC_INVERSIONS",
|
| 174 |
+
"GENRE_TWISTS",
|
| 175 |
+
"TIME_STRUCTURES",
|
| 176 |
+
"KOREAN_ONOMATOPOEIA",
|
| 177 |
+
"EMERGENT_AD_REFS",
|
| 178 |
+
"CreativeConstraints",
|
| 179 |
+
"pick_constraints",
|
| 180 |
+
]
|
aether_ad/core/engine.py
CHANGED
|
@@ -13,6 +13,11 @@ from pydantic import BaseModel, Field
|
|
| 13 |
|
| 14 |
from aether_ad.blending.spaces import BlendingSpaceSet, build_spaces
|
| 15 |
from aether_ad.core.collision import ALL_RULES, CollisionRule, sample_rules
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
from aether_ad.core.narrative import Beat, PixarSpineRenderer
|
| 17 |
from aether_ad.core.ohaeng import BeatSlot, pick_script, script_to_prompt
|
| 18 |
from aether_ad.core.wow_anchors import pick_anchor
|
|
@@ -74,6 +79,7 @@ class AdSeed(BaseModel):
|
|
| 74 |
raw_spine: str | None = None
|
| 75 |
wow_anchor: str | None = None
|
| 76 |
ohaeng: list[dict] = Field(default_factory=list) # per-beat element + relation
|
|
|
|
| 77 |
|
| 78 |
|
| 79 |
class ScoredSeed(BaseModel):
|
|
@@ -177,6 +183,9 @@ class GenesisEngine:
|
|
| 177 |
slots = pick_script(duration, self.rng)
|
| 178 |
blueprint = script_to_prompt(slots)
|
| 179 |
|
|
|
|
|
|
|
|
|
|
| 180 |
plans.append(
|
| 181 |
{
|
| 182 |
"index": i,
|
|
@@ -186,6 +195,7 @@ class GenesisEngine:
|
|
| 186 |
"wow_anchor": anchor,
|
| 187 |
"ohaeng_slots": slots,
|
| 188 |
"ohaeng_blueprint": blueprint,
|
|
|
|
| 189 |
}
|
| 190 |
)
|
| 191 |
return plans
|
|
@@ -207,9 +217,10 @@ class GenesisEngine:
|
|
| 207 |
anchor = plan["wow_anchor"]
|
| 208 |
blueprint = plan["ohaeng_blueprint"]
|
| 209 |
slots = plan["ohaeng_slots"]
|
|
|
|
| 210 |
summary, concept, raw = self._apply_rules(
|
| 211 |
spaces, rules, product, tension, persona_text,
|
| 212 |
-
atom, potential, anchor, blueprint,
|
| 213 |
)
|
| 214 |
return AdSeed(
|
| 215 |
seed_id=f"{product.product_id}_{tension.id}_{i:03d}",
|
|
@@ -222,6 +233,7 @@ class GenesisEngine:
|
|
| 222 |
duration=duration,
|
| 223 |
raw_collision=raw,
|
| 224 |
wow_anchor=anchor,
|
|
|
|
| 225 |
ohaeng=[
|
| 226 |
{
|
| 227 |
"index": s.index,
|
|
@@ -313,6 +325,7 @@ class GenesisEngine:
|
|
| 313 |
potential: str,
|
| 314 |
wow_anchor: str,
|
| 315 |
ohaeng_blueprint: str,
|
|
|
|
| 316 |
) -> tuple[str, str, str | None]:
|
| 317 |
"""Return (scene_summary, concept, raw)."""
|
| 318 |
if self.llm is not None:
|
|
@@ -326,6 +339,8 @@ class GenesisEngine:
|
|
| 326 |
product=product,
|
| 327 |
wow_anchor=wow_anchor,
|
| 328 |
ohaeng_blueprint=ohaeng_blueprint,
|
|
|
|
|
|
|
| 329 |
)
|
| 330 |
return summary, concept, raw
|
| 331 |
except Exception as e:
|
|
|
|
| 13 |
|
| 14 |
from aether_ad.blending.spaces import BlendingSpaceSet, build_spaces
|
| 15 |
from aether_ad.core.collision import ALL_RULES, CollisionRule, sample_rules
|
| 16 |
+
from aether_ad.core.creative_constraints import (
|
| 17 |
+
EMERGENT_AD_REFS,
|
| 18 |
+
CreativeConstraints,
|
| 19 |
+
pick_constraints,
|
| 20 |
+
)
|
| 21 |
from aether_ad.core.narrative import Beat, PixarSpineRenderer
|
| 22 |
from aether_ad.core.ohaeng import BeatSlot, pick_script, script_to_prompt
|
| 23 |
from aether_ad.core.wow_anchors import pick_anchor
|
|
|
|
| 79 |
raw_spine: str | None = None
|
| 80 |
wow_anchor: str | None = None
|
| 81 |
ohaeng: list[dict] = Field(default_factory=list) # per-beat element + relation
|
| 82 |
+
creative_constraints: dict | None = None # the 6 hard constraints injected into prompt
|
| 83 |
|
| 84 |
|
| 85 |
class ScoredSeed(BaseModel):
|
|
|
|
| 183 |
slots = pick_script(duration, self.rng)
|
| 184 |
blueprint = script_to_prompt(slots)
|
| 185 |
|
| 186 |
+
# Creative constraints โ 6 concrete imperatives per seed
|
| 187 |
+
constraints = pick_constraints(self.rng)
|
| 188 |
+
|
| 189 |
plans.append(
|
| 190 |
{
|
| 191 |
"index": i,
|
|
|
|
| 195 |
"wow_anchor": anchor,
|
| 196 |
"ohaeng_slots": slots,
|
| 197 |
"ohaeng_blueprint": blueprint,
|
| 198 |
+
"constraints": constraints,
|
| 199 |
}
|
| 200 |
)
|
| 201 |
return plans
|
|
|
|
| 217 |
anchor = plan["wow_anchor"]
|
| 218 |
blueprint = plan["ohaeng_blueprint"]
|
| 219 |
slots = plan["ohaeng_slots"]
|
| 220 |
+
constraints: CreativeConstraints = plan["constraints"]
|
| 221 |
summary, concept, raw = self._apply_rules(
|
| 222 |
spaces, rules, product, tension, persona_text,
|
| 223 |
+
atom, potential, anchor, blueprint, constraints,
|
| 224 |
)
|
| 225 |
return AdSeed(
|
| 226 |
seed_id=f"{product.product_id}_{tension.id}_{i:03d}",
|
|
|
|
| 233 |
duration=duration,
|
| 234 |
raw_collision=raw,
|
| 235 |
wow_anchor=anchor,
|
| 236 |
+
creative_constraints=constraints.as_summary(),
|
| 237 |
ohaeng=[
|
| 238 |
{
|
| 239 |
"index": s.index,
|
|
|
|
| 325 |
potential: str,
|
| 326 |
wow_anchor: str,
|
| 327 |
ohaeng_blueprint: str,
|
| 328 |
+
constraints: CreativeConstraints,
|
| 329 |
) -> tuple[str, str, str | None]:
|
| 330 |
"""Return (scene_summary, concept, raw)."""
|
| 331 |
if self.llm is not None:
|
|
|
|
| 339 |
product=product,
|
| 340 |
wow_anchor=wow_anchor,
|
| 341 |
ohaeng_blueprint=ohaeng_blueprint,
|
| 342 |
+
creative_constraints=constraints.as_prompt_block(),
|
| 343 |
+
emergent_refs=EMERGENT_AD_REFS,
|
| 344 |
)
|
| 345 |
return summary, concept, raw
|
| 346 |
except Exception as e:
|
aether_ad/llm/base.py
CHANGED
|
@@ -235,6 +235,8 @@ class LLMBackend(ABC):
|
|
| 235 |
product: "ProductGenome",
|
| 236 |
wow_anchor: str = "์ฅ๋ฉด์ ์์ธก ๋ถ๊ฐ๋ฅํ ๋ฐ์ ์ด ๋ฐ๋์ ์์ด์ผ ํ๋ค.",
|
| 237 |
ohaeng_blueprint: str = "",
|
|
|
|
|
|
|
| 238 |
) -> tuple[str, str, str]:
|
| 239 |
"""Return (scene_summary, cleaned_concept, raw_response)."""
|
| 240 |
tpl = _load_prompt("apply_collision.txt")
|
|
@@ -255,8 +257,10 @@ class LLMBackend(ABC):
|
|
| 255 |
rules="\n".join(f"- [{r.rule_id}] {r.name}: {r.llm_prompt_fragment}" for r in rules),
|
| 256 |
wow_anchor=wow_anchor,
|
| 257 |
ohaeng_blueprint=ohaeng_blueprint,
|
|
|
|
|
|
|
| 258 |
)
|
| 259 |
-
raw = self.complete(prompt, max_tokens=12288, temperature=
|
| 260 |
scene_summary, cleaned = _split_scene_summary(raw)
|
| 261 |
if not cleaned:
|
| 262 |
cleaned_clean, _ = strip_reasoning(raw)
|
|
|
|
| 235 |
product: "ProductGenome",
|
| 236 |
wow_anchor: str = "์ฅ๋ฉด์ ์์ธก ๋ถ๊ฐ๋ฅํ ๋ฐ์ ์ด ๋ฐ๋์ ์์ด์ผ ํ๋ค.",
|
| 237 |
ohaeng_blueprint: str = "",
|
| 238 |
+
creative_constraints: str = "",
|
| 239 |
+
emergent_refs: str = "",
|
| 240 |
) -> tuple[str, str, str]:
|
| 241 |
"""Return (scene_summary, cleaned_concept, raw_response)."""
|
| 242 |
tpl = _load_prompt("apply_collision.txt")
|
|
|
|
| 257 |
rules="\n".join(f"- [{r.rule_id}] {r.name}: {r.llm_prompt_fragment}" for r in rules),
|
| 258 |
wow_anchor=wow_anchor,
|
| 259 |
ohaeng_blueprint=ohaeng_blueprint,
|
| 260 |
+
creative_constraints=creative_constraints,
|
| 261 |
+
emergent_refs=emergent_refs,
|
| 262 |
)
|
| 263 |
+
raw = self.complete(prompt, max_tokens=12288, temperature=1.05)
|
| 264 |
scene_summary, cleaned = _split_scene_summary(raw)
|
| 265 |
if not cleaned:
|
| 266 |
cleaned_clean, _ = strip_reasoning(raw)
|
aether_ad/llm/prompts/apply_collision.txt
CHANGED
|
@@ -1,43 +1,45 @@
|
|
| 1 |
[์ญํ ]
|
| 2 |
-
๋น์ ์ AETHER 5-์์ฑ์(ไบ่ก) ๋ฉํ์ธ์ง ์์๋ธ์ด๋ค.
|
| 3 |
-
|
| 4 |
-
๋ง์ง๋ง ๋จ๊ณ(ๆฐด)์ ํตํฉ ๊ฒฐ๊ณผ๋ง ์ถ๋ ฅํ๋ค.
|
| 5 |
|
| 6 |
-
[AETHER 5-์์ฑ์ โ ๋ด๋ถ ๋ฉํ์ธ์ง
|
| 7 |
-
1. ๆจ(์จ์
|
| 8 |
-
2. ็ซ(์ฆํญ
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
์๊ทน: ๆจ โก ๅ (์ ์๊ฐ์ด ๊ด์ต ์ง๋ฉด์ ๋ซ์ โ ์ง๋ถํจ ์ฐจ๋จ)
|
| 13 |
-
4. ้(ํธ์งยท์ ์ญ) โ ํด๋ฆฌ์
ฐยท์์ดยท์ฌ๊ณ ํ์ ยท๊ตฐ๋๋๊ธฐ๋ฅผ ๋ ์นด๋กญ๊ฒ ๋๋ ค๋ธ๋ค.
|
| 14 |
-
์์: ๅ โ ้ (ํ์์ ์ ๊ฐ ๋์ด)
|
| 15 |
-
์๊ทน: ็ซ โก ้ (๊ณผ์ด๋ ๊ฐ์ ์ด ํธ์ง์ ์นผ์ ๋
น์ โ ์ง๋์น ๊ต์ ๋ฐฉ์ง)
|
| 16 |
-
5. ๆฐด(ํตํฉยทํ๋ฆ) โ ์ ๋ค ๋จ๊ณ๋ฅผ ํ๊ตญ์ด ํ ๋ฌธ๋จ์ผ๋ก ์์ถํ์ฌ ํ๋ฅด๊ฒ ํ๋ค.
|
| 17 |
-
์์: ้ โ ๆฐด (์ ์์ ์ด์ฌ์ด ๋งบํ)
|
| 18 |
-
์๊ทน: ๆฐด โก ็ซ (๋ฌผ์ด ๋ถ์ ๋ โ ๊ณผ์ด ๊ฐ์ ์ ์ด)
|
| 19 |
-
์๊ทน: ๅ โก ๆฐด (ํ์ด ๋ฌผ์ ๋ง์ โ ์ค์ฌ ์ ์ง)
|
| 20 |
|
| 21 |
-
|
| 22 |
-
1. ์ 5๋จ๊ณ์ ์ค๊ฐ ์์
๋ฌผยท์ฌ๊ณ ๊ณผ์ ยท๋ถ์์ **์ ๋ ์ธ๋ถ๋ก ์ถ๋ ฅํ์ง ๋ง ๊ฒ**.
|
| 23 |
-
2. ์ถ๋ ฅ์ ์ค์ง ๆฐด์ ์ต์ข
ํตํฉ๋ฌผ.
|
| 24 |
-
3. ์ถ๋ ฅ ์ธ์ด๋ **100% ํ๊ตญ์ด**. ์์ด ๋จ์ดยท์๋ฌธ ๋ฌธ์ฅ ์ผ์ฒด ๊ธ์ง
|
| 25 |
-
(์์ธ: ๊ณ ์ ๋ธ๋๋๋ช
'{brand}' ํ๋๋ง ํ์ฉ).
|
| 26 |
-
4. "์ฌ์ฉ์๋โฆ", "The user isโฆ", "Let meโฆ", "R01:โฆ", "**์ญํ **" ๊ฐ์ ์๊ธฐ ์ง์ ๊ธ์ง.
|
| 27 |
-
5. ์ด๋ชจ์งยท๋งํฌ๋ค์ดยท๋ฆฌ์คํธยทํค๋ยท๋ฐฑํฑ ๊ธ์ง.
|
| 28 |
-
6. ๋ถํ '{atom_name}'์ด ์ฅ๋ฉด์ ์ ํ์ ์ผ๋ก ๋ฐ๋์ ํ ๋ฒ ๋ฑ์ฅ.
|
| 29 |
-
7. ์ฅ์ ยท์ฑ๋ณยท์ธ์ข
๋นํ, ๋ฏธ์ฑ๋
์ฑ์ ๋ฌ์ฌ, ์ํด ์ง์ ๋ฌ์ฌ, ์ค์กด ๋ธ๋๋ ๊ธ์ง.
|
| 30 |
-
8. ๊ด๊ณ ์นดํผ(ํ ์ค ์ฌ๋ก๊ฑด) ๊ธ์ง.
|
| 31 |
-
9. ๋ณธ๋ฌธ์ 3~5 ๋ฌธ์ฅ, ์ ์ฒด 400์ ์ด๋ด.
|
| 32 |
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
[์์ฐ ์ต์ปค โ ์ด๋ฒ ์๋ ์ ์ฉ ์ค๊ณ ์ง์]
|
| 38 |
{wow_anchor}
|
| 39 |
|
| 40 |
-
[์ฐธ๊ณ ๋ธ๋ฃจํ๋ฆฐํธ โ 5์ญํ ๋ด๋ถ ์์
|
| 41 |
{ohaeng_blueprint}
|
| 42 |
|
| 43 |
[์
๋ ฅ ๊ณต๊ฐ I1 โ ์ ํ]
|
|
@@ -57,16 +59,9 @@
|
|
| 57 |
[์ ์ฉํ Collision Rule(s)]
|
| 58 |
{rules}
|
| 59 |
|
| 60 |
-
[์ถ๋ ฅ ํ์ โ ๋ฐ๋์ ์ด ํ์
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
SCENE_SUMMARY: (์ฌ๊ธฐ์ 10~14์ ํ๊ตญ์ด ์ ๋ชฉ์ ์จ๋ผ. ๋ฐ๋์ ์ด ์ค์ ๋งจ ์ฒ์์ ์ถ๋ ฅํ๋ผ.)
|
| 64 |
|
| 65 |
(๋น ์ค ํ ๊ฐ)
|
| 66 |
|
| 67 |
-
(ํ๊ตญ์ด 3~5 ๋ฌธ์ฅ
|
| 68 |
-
|
| 69 |
-
์์ (ํ์๋ง ์ฐธ๊ณ , ๋ด์ฉ์ ๋ณต์ฌ ๊ธ์ง):
|
| 70 |
-
SCENE_SUMMARY: ๋๋์๊ฐ ๋ณต๋
|
| 71 |
-
|
| 72 |
-
๋น ๊ต์ค์ ํ๊ด๋ฑ์ด ๊บผ์ง์ 30๋ ์ง์ฅ์ธ์ ์๋ชฉ์์ AETHER-A ์๊ณ์ ์ฉ๋๊ฐ ์ค์ค๋ก ๋ฐ ๋ฐํด ๋์๊ฐ๋ค. (์๋ต)
|
|
|
|
| 1 |
[์ญํ ]
|
| 2 |
+
๋น์ ์ AETHER 5-์์ฑ์(ไบ่ก) ๋ฉํ์ธ์ง ์์๋ธ์ด๋ค. ๋ค์ 5๊ฐ ๋ด๋ถ ์ญํ ์ ์์ฐจ
|
| 3 |
+
์ํํ๋, ๊ฐ ๋จ๊ณ์ ์์
๋ฌผ์ ์ธ๋ถ๋ก ๋
ธ์ถํ์ง ๋ง๊ณ ์ต์ข
ํตํฉ(ๆฐด) ๊ฒฐ๊ณผ๋ง ์ถ๋ ฅํ๋ค.
|
|
|
|
| 4 |
|
| 5 |
+
[AETHER 5-์์ฑ์ โ ๋ด๋ถ ๋ฉํ์ธ์ง (์ ๋ ์ถ๋ ฅ ๊ธ์ง)]
|
| 6 |
+
1. ๆจ(์จ์) โ ์
๋ ฅ์์ ๊ฑฐ์น ์ฒซ ์ด๋ฏธ์ง 1๊ฐ ๋ฐ์
|
| 7 |
+
2. ็ซ(์ฆํญ) โ ๊ฐ์ฅ ๋จ๊ฑฐ์ด ๊ฐ์ ์ ์ ํ์ [์์ ๆจโ็ซ]
|
| 8 |
+
3. ๅ(์ง๋ฐ) โ ๋ฌผ๋ฆฌยท๋ฌธํ ์ ํฉ์ฑ ํ๋ณด [์์ ็ซโๅ ยท ์๊ทน ๆจโกๅ: ์ง๋ถํจ ์ฐจ๋จ]
|
| 9 |
+
4. ้(ํธ์ง) โ ํด๋ฆฌ์
ฐยท์์ดยท๊ตฐ๋๋๊ธฐ ์ ์ญ [์์ ๅโ้ ยท ์๊ทน ็ซโก้: ๊ณผ๊ต์ ๋ฐฉ์ง]
|
| 10 |
+
5. ๆฐด(ํตํฉ) โ ํ๊ตญ์ด ํ ๋ฌธ๋จ์ผ๋ก ์์ถ [์์ ้โๆฐด ยท ์๊ทน ๆฐดโก็ซ: ๊ณผ์ด ์ ์ด]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
+
{emergent_refs}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
+
{creative_constraints}
|
| 15 |
+
|
| 16 |
+
[ํต์ฌ ์์น โ '์ฒํธ์ผ๋ฅ ' ์ฐจ๋จ์ ์ํ ๊ฐ์ ]
|
| 17 |
+
1. **์ถ์์ ๋ฐ๋์ ๋ฌผ๋ฆฌ๋ก**: "๋ฐฉ์"โ๋๋ฌผ์ด ํผ๋ถ๋ฅผ ๋ซ์ง ๋ชปํจ, "์๊ฐ์กฐ์ "โ์ฃผ๋ณ ์ธ๋ฌผ
|
| 18 |
+
์ค์ ๋์. ์์ ๋ฅผ ๊ทธ๋๋ก ๊ด๊ฐ ๋์์ ๋ณด์ด๋ ํ์์ผ๋ก.
|
| 19 |
+
2. **์ ํ์ ์นจ๋ฌต์ผ๋ก ๋งํจ**: ๋ก๊ณ ยท์นดํผยท๋ธ๋๋์ฝ ๊ธ์ง. ๊ทธ๋ฆผ์ยท์๊ตญยท์๋ฆฌยท๋น์๋ฆฌ๋ก ์กด์ฌ๊ฐ.
|
| 20 |
+
3. **์ฒซ 3์ด์ ์์ธก์ด ๋ง์ง๋ง 3์ด์ ๋ค์งํ์ผ ํ๋ค** (prediction gap).
|
| 21 |
+
4. **์ '์ฐฝ๋ฐ ๊ฐ์ ์กฐ๊ฑด' 6๊ฐ ํญ๋ชฉ์ ์ ๋ถ ์ฅ๋ฉด์ ๋
น์ผ ๊ฒ**. ํ๋๋ผ๋ ๋น ์ง๋ฉด ์คํจ.
|
| 22 |
+
|
| 23 |
+
[์ ๋ ๊ธ์ง ํด๋ฆฌ์
ฐ (์ฌ์ฉ ์ ์๋ ์ค๊ฒฉ)]
|
| 24 |
+
- ๊ฐ์กฑ ์ฌ์ง / ์กธ์
์ ํฌ์น / ๋น์ ์ฐ์ฐ ๊ณต์ / ์ผ๊ธฐ์ฅ์ ํธ์ง ์ฐ๊ธฐ
|
| 25 |
+
- ์ด์นจ ์๋ฆฌ์ ๋๋ฌผ ๋จ๋
๊ต์ฐจ / ํ ๋จธ๋ยทํ ์๋ฒ์ง์ ์ฃฝ์ ์๋ฉ
|
| 26 |
+
- ๋ฒ๊ฝ ํฉ๋ ๋ฆฌ๋ฉฐ ๊ณ ๋ฐฑ / ๋ ๋ด๋ฆฌ๋ ์ฒซ์ฌ๋ / ์นดํ ์ฐฝ๋ฐ ์ถ์ต
|
| 27 |
+
- ๋์ดํฐ ๋ชจ๋์ ์ด๋ฆ ์ฐ๊ธฐ / ํ๋์ ์ง์ฐ๊ธฐ / ์๊ธ ๋ฐ๋ผ๋ณด๊ธฐ
|
| 28 |
+
|
| 29 |
+
[์ต์ข
์ถ๋ ฅ ๊ท์น]
|
| 30 |
+
1. 5๋จ๊ณ ๋ด๋ถ ์์
๋ฌผยท์ฌ๊ณ ยท๋ถ์ ์ผ์ฒด ์ธ๋ถ ์ถ๋ ฅ ๊ธ์ง.
|
| 31 |
+
2. ์ถ๋ ฅ ์ธ์ด๋ **100% ํ๊ตญ์ด** (๋ธ๋๋ '{brand}'๋ง ์์ธ).
|
| 32 |
+
3. "์ฌ์ฉ์๋โฆ", "The user isโฆ", "Let meโฆ", "R01:โฆ", "**์ญํ **" ์๊ธฐ ์ง์ ๊ธ์ง.
|
| 33 |
+
4. ์ด๋ชจ์งยท๋งํฌ๋ค์ดยท๋ฆฌ์คํธยทํค๋ยท๋ฐฑํฑ ๊ธ์ง.
|
| 34 |
+
5. ๋ถํ '{atom_name}'์ด ์ฅ๋ฉด ์ ํ์ ์ผ๋ก ๋ฐ๋์ ํ ๋ฒ ๋ฑ์ฅ.
|
| 35 |
+
6. ์ฅ์ ยท์ฑ๋ณยท์ธ์ข
๋นํ, ๋ฏธ์ฑ๋
์ฑ์ ๋ฌ์ฌ, ์ํด ์ง์ ๋ฌ์ฌ ๊ธ์ง.
|
| 36 |
+
7. ๊ด๊ณ ์นดํผ(ํ ์ค ์ฌ๋ก๊ฑด) ๊ธ์ง.
|
| 37 |
+
8. ๋ณธ๋ฌธ์ 3~5 ๋ฌธ์ฅ, ์ ์ฒด 500์ ์ด๋ด.
|
| 38 |
|
| 39 |
[์์ฐ ์ต์ปค โ ์ด๋ฒ ์๋ ์ ์ฉ ์ค๊ณ ์ง์]
|
| 40 |
{wow_anchor}
|
| 41 |
|
| 42 |
+
[์ฐธ๊ณ ๋ธ๋ฃจํ๋ฆฐํธ โ 5์ญํ ๋ด๋ถ ์์
๊ฐ์ด๋]
|
| 43 |
{ohaeng_blueprint}
|
| 44 |
|
| 45 |
[์
๋ ฅ ๊ณต๊ฐ I1 โ ์ ํ]
|
|
|
|
| 59 |
[์ ์ฉํ Collision Rule(s)]
|
| 60 |
{rules}
|
| 61 |
|
| 62 |
+
[์ถ๋ ฅ ํ์ โ ๋ฐ๋์ ์ด ํ์]
|
| 63 |
+
SCENE_SUMMARY: (10~14์ ํ๊ตญ์ด ์ ๋ชฉ. ์ด ์ค์ ๋ฐ๋์ ๋งจ ๋จผ์ )
|
|
|
|
|
|
|
| 64 |
|
| 65 |
(๋น ์ค ํ ๊ฐ)
|
| 66 |
|
| 67 |
+
(ํ๊ตญ์ด 3~5 ๋ฌธ์ฅ ํ ๋ฌธ๋จ. '์ฐฝ๋ฐ ๊ฐ์ ์กฐ๊ฑด' 6๊ฐ ํญ๋ชฉ ๋ชจ๋ ๋
น์ ์์ด์ผ ํจ. 500์ ์ด๋ด.)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static/app.js
CHANGED
|
@@ -449,6 +449,8 @@ function renderSeedCard(s, rank) {
|
|
| 449 |
|
| 450 |
${seed.wow_anchor ? `<div class="seed-anchor">${escapeHtml(seed.wow_anchor)}</div>` : ''}
|
| 451 |
|
|
|
|
|
|
|
| 452 |
<div class="seed-section-title">Concept</div>
|
| 453 |
<div class="seed-concept">${escapeHtml(seed.concept)}</div>
|
| 454 |
|
|
@@ -526,6 +528,31 @@ function renderSeedCard(s, rank) {
|
|
| 526 |
return card;
|
| 527 |
}
|
| 528 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 529 |
function renderRawDetails(seed) {
|
| 530 |
const parts = [];
|
| 531 |
if (seed.raw_collision) {
|
|
|
|
| 449 |
|
| 450 |
${seed.wow_anchor ? `<div class="seed-anchor">${escapeHtml(seed.wow_anchor)}</div>` : ''}
|
| 451 |
|
| 452 |
+
${renderConstraints(seed.creative_constraints)}
|
| 453 |
+
|
| 454 |
<div class="seed-section-title">Concept</div>
|
| 455 |
<div class="seed-concept">${escapeHtml(seed.concept)}</div>
|
| 456 |
|
|
|
|
| 528 |
return card;
|
| 529 |
}
|
| 530 |
|
| 531 |
+
function renderConstraints(c) {
|
| 532 |
+
if (!c) return '';
|
| 533 |
+
const rows = [
|
| 534 |
+
['๐ฆ ์ค๋ธ์ ', c.object],
|
| 535 |
+
['๐ฅ ์นด๋ฉ๋ผ', c.shot],
|
| 536 |
+
['๐ ๊ณต๊ฐ๊ฐ', c.synesthesia],
|
| 537 |
+
['๐ฌ ์ฅ๋ฅด ์ ํ', c.genre_twist],
|
| 538 |
+
['โฑ ์๊ฐ ๊ตฌ์กฐ', c.time],
|
| 539 |
+
['๐ ์์ฑ์ด', c.onomatopoeia],
|
| 540 |
+
].filter(([, v]) => v);
|
| 541 |
+
if (!rows.length) return '';
|
| 542 |
+
return `
|
| 543 |
+
<div class="constraints">
|
| 544 |
+
<div class="constraints-head">์ฐฝ๋ฐ ๊ฐ์ ์กฐ๊ฑด <small>ยท Kimi-K2P6์ ํ๋ ์ฃผ์
๋ ๊ตฌ์ฒด ์ ์ฝ</small></div>
|
| 545 |
+
<div class="constraints-grid">
|
| 546 |
+
${rows.map(([k, v]) => `
|
| 547 |
+
<div class="constraint-chip">
|
| 548 |
+
<span class="constraint-key">${k}</span>
|
| 549 |
+
<span class="constraint-val">${escapeHtml(v)}</span>
|
| 550 |
+
</div>`).join('')}
|
| 551 |
+
</div>
|
| 552 |
+
</div>
|
| 553 |
+
`;
|
| 554 |
+
}
|
| 555 |
+
|
| 556 |
function renderRawDetails(seed) {
|
| 557 |
const parts = [];
|
| 558 |
if (seed.raw_collision) {
|
static/index.html
CHANGED
|
@@ -10,7 +10,7 @@
|
|
| 10 |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
|
| 11 |
<link href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable.min.css" rel="stylesheet" />
|
| 12 |
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6/dist/chart.umd.min.js"></script>
|
| 13 |
-
<link rel="stylesheet" href="/static/styles.css?v=0.
|
| 14 |
</head>
|
| 15 |
<body>
|
| 16 |
<header class="hero">
|
|
@@ -226,6 +226,6 @@
|
|
| 226 |
</div>
|
| 227 |
</footer>
|
| 228 |
|
| 229 |
-
<script src="/static/app.js?v=0.
|
| 230 |
</body>
|
| 231 |
</html>
|
|
|
|
| 10 |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
|
| 11 |
<link href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable.min.css" rel="stylesheet" />
|
| 12 |
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6/dist/chart.umd.min.js"></script>
|
| 13 |
+
<link rel="stylesheet" href="/static/styles.css?v=0.6.0" />
|
| 14 |
</head>
|
| 15 |
<body>
|
| 16 |
<header class="hero">
|
|
|
|
| 226 |
</div>
|
| 227 |
</footer>
|
| 228 |
|
| 229 |
+
<script src="/static/app.js?v=0.6.0"></script>
|
| 230 |
</body>
|
| 231 |
</html>
|
static/styles.css
CHANGED
|
@@ -769,6 +769,47 @@ input[type="range"] { padding: 0; width: 100%; accent-color: var(--primary); }
|
|
| 769 |
font-weight: 700; color: #92400e;
|
| 770 |
}
|
| 771 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 772 |
/* AETHER 5-generator metacognitive provenance (compact one-liner) */
|
| 773 |
.aether-provenance {
|
| 774 |
margin-top: 14px;
|
|
|
|
| 769 |
font-weight: 700; color: #92400e;
|
| 770 |
}
|
| 771 |
|
| 772 |
+
/* Creative constraints โ the 6 hard imperatives injected per seed */
|
| 773 |
+
.constraints {
|
| 774 |
+
margin: 10px 0 16px;
|
| 775 |
+
padding: 12px 14px;
|
| 776 |
+
background: linear-gradient(135deg, #f0fdf4 0%, #ffffff 100%);
|
| 777 |
+
border: 1px solid #86efac;
|
| 778 |
+
border-radius: 10px;
|
| 779 |
+
}
|
| 780 |
+
.constraints-head {
|
| 781 |
+
font-size: 11px; font-weight: 700;
|
| 782 |
+
color: #14532d;
|
| 783 |
+
text-transform: uppercase; letter-spacing: 0.05em;
|
| 784 |
+
margin-bottom: 10px;
|
| 785 |
+
}
|
| 786 |
+
.constraints-head small {
|
| 787 |
+
font-weight: 500; color: var(--text-muted);
|
| 788 |
+
letter-spacing: 0; text-transform: none;
|
| 789 |
+
}
|
| 790 |
+
.constraints-grid {
|
| 791 |
+
display: grid;
|
| 792 |
+
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
| 793 |
+
gap: 6px;
|
| 794 |
+
}
|
| 795 |
+
.constraint-chip {
|
| 796 |
+
display: flex; align-items: baseline; gap: 8px;
|
| 797 |
+
padding: 7px 10px;
|
| 798 |
+
background: white;
|
| 799 |
+
border: 1px solid #bbf7d0;
|
| 800 |
+
border-radius: 8px;
|
| 801 |
+
font-size: 12px;
|
| 802 |
+
line-height: 1.45;
|
| 803 |
+
}
|
| 804 |
+
.constraint-key {
|
| 805 |
+
font-weight: 700; color: #166534;
|
| 806 |
+
flex-shrink: 0;
|
| 807 |
+
min-width: 72px;
|
| 808 |
+
}
|
| 809 |
+
.constraint-val {
|
| 810 |
+
color: var(--text); font-weight: 500;
|
| 811 |
+
}
|
| 812 |
+
|
| 813 |
/* AETHER 5-generator metacognitive provenance (compact one-liner) */
|
| 814 |
.aether-provenance {
|
| 815 |
margin-top: 14px;
|