Update app.py
Browse files
app.py
CHANGED
@@ -1,14 +1,25 @@
|
|
1 |
"""
|
2 |
Square Theory Generator (Markdownโonly, up to 20 ideas)
|
3 |
=====================================================
|
4 |
-
2025โ05โ28ย
|
5 |
---------------------------------------------------
|
6 |
๋ณ๊ฒฝ ์์ฝ
|
7 |
---------
|
8 |
-
1.
|
9 |
-
|
10 |
-
|
11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
์คํ๋ฒ
|
14 |
------
|
@@ -22,7 +33,7 @@ python square_theory_gradio.py
|
|
22 |
import os
|
23 |
import json
|
24 |
import gradio as gr
|
25 |
-
from openai import OpenAI
|
26 |
|
27 |
# -------------------------------------------------
|
28 |
# 0. OpenAI ํด๋ผ์ด์ธํธ
|
@@ -42,8 +53,11 @@ SYSTEM_PROMPT = (
|
|
42 |
"๋ฐฐ์ด ์ฒซ ๋ฒ์งธ ์์๊ฐ ๊ฐ์ฅ ์ฐ์ํด์ผ ํ๋ค. JSON ์ธ ๋ฌธ์๋ ๊ธ์งํ๋ค."
|
43 |
)
|
44 |
|
|
|
45 |
|
46 |
-
|
|
|
|
|
47 |
text = text.strip()
|
48 |
if text.startswith("```"):
|
49 |
text = text.split("\n", 1)[1] if "\n" in text else text[3:]
|
@@ -52,163 +66,74 @@ def clean_json_block(text: str) -> str:
|
|
52 |
return text.strip()
|
53 |
|
54 |
|
55 |
-
def
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
data
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
|
|
78 |
|
79 |
# -------------------------------------------------
|
80 |
# 2. Gradio callback
|
81 |
# -------------------------------------------------
|
82 |
|
83 |
def generate(seed_word: str):
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
|
95 |
# -------------------------------------------------
|
96 |
# 3. UI
|
97 |
# -------------------------------------------------
|
98 |
with gr.Blocks(title="Square Theory โ ์ต๋ 20๊ฐ ๐ฐ๐ท") as demo:
|
99 |
-
gr.Markdown("""# ๐ง Square Theory ์ ์ (์ต๋ 20๊ฐ)\n๋จ์ด ํ๋ ์
๋ ฅ โ LLM์ด
|
100 |
seed = gr.Textbox(label="์๋ ๋จ์ด(TL)", placeholder="์: ๊ณจ๋ ")
|
101 |
run = gr.Button("์์ฑ")
|
102 |
-
md_out = gr.Markdown(
|
103 |
|
104 |
run.click(generate, inputs=seed, outputs=md_out)
|
105 |
|
106 |
-
|
107 |
-
demo.launch()
|
108 |
-
"""
|
109 |
-
Square Theory Generator (Markdownโonly, up to 20 ideas)
|
110 |
-
=====================================================
|
111 |
-
2025โ05โ28ย v7 โ ๋ฐ์ค ๋ค์ด์ด๊ทธ๋จ ์ ๊ฑฐ & ์ ์ย 20๊ฐ๋ก ํ์ฅ
|
112 |
-
---------------------------------------------------
|
113 |
-
๋ณ๊ฒฝ ์์ฝ
|
114 |
-
---------
|
115 |
-
1. **์ฌ๊ฐํ ์๊ฐํ ์ ๊ฑฐ** โ ์ถ๋ ฅ/์์กด์์ `matplotlib` ์ ๋ถ ์ญ์ .
|
116 |
-
2. **์ ์ ๊ฐ์ ์ํฅ** โ LLM์ด ์ต๋ **20๊ฐ** ์์ด๋์ด ๋ฐํ.
|
117 |
-
3. **์ ์ฐ ๊ธธ์ด ํ์ฉ** โ 1โฏโคโฏNโฏโคโฏ20 ๋ฆฌ์คํธ๋ฉด OK.
|
118 |
-
4. **UI ๋จ์ํ** โ ์๋ ์
๋ ฅ โ ๋ฒํผ โ ๋งํฌ๋ค์ด ๊ฒฐ๊ณผ.
|
119 |
-
|
120 |
-
์คํ๋ฒ
|
121 |
-
------
|
122 |
-
```bash
|
123 |
-
pip install --upgrade gradio openai
|
124 |
-
export OPENAI_API_KEY="sk-..."
|
125 |
-
python square_theory_gradio.py
|
126 |
-
```
|
127 |
-
"""
|
128 |
-
|
129 |
-
import os
|
130 |
-
import json
|
131 |
-
import gradio as gr
|
132 |
-
from openai import OpenAI
|
133 |
|
134 |
# -------------------------------------------------
|
135 |
-
#
|
136 |
# -------------------------------------------------
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
client = OpenAI()
|
141 |
-
|
142 |
-
# -------------------------------------------------
|
143 |
-
# 1. LLM Prompt & Utilities
|
144 |
-
# -------------------------------------------------
|
145 |
-
SYSTEM_PROMPT = (
|
146 |
-
"๋๋ ํ๊ตญ์ด ์นดํผยท๋ธ๋๋ ๋ค์ด๋ฐ ์ ๋ฌธ๊ฐ์ด์ Square Theory ๋์ฐ๋ฏธ๋ค. "
|
147 |
-
"์ฌ์ฉ์๊ฐ ์
๋ ฅํ ํ๋์ ๋จ์ด(tl)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ง์ด ๋ฐ์ด๋ ์์๋๋ก ์ต๋ 20๊ฐ์ ์ ์์ JSON ๋ฐฐ์ด๋ก ๋ฐํํด๋ผ. "
|
148 |
-
"๊ฐ ์์๋ tl, tr, br, bl, top_phrase, bottom_phrase, slogan, brand ํ๋๋ฅผ ๊ฐ์ง๋ค. "
|
149 |
-
"๋ฐฐ์ด ์ฒซ ๋ฒ์งธ ์์๊ฐ ๊ฐ์ฅ ์ฐ์ํด์ผ ํ๋ค. JSON ์ธ ๋ฌธ์๋ ๊ธ์งํ๋ค."
|
150 |
-
)
|
151 |
-
|
152 |
-
|
153 |
-
def clean_json_block(text: str) -> str:
|
154 |
-
text = text.strip()
|
155 |
-
if text.startswith("```"):
|
156 |
-
text = text.split("\n", 1)[1] if "\n" in text else text[3:]
|
157 |
-
if text.endswith("```"):
|
158 |
-
text = text[:-3]
|
159 |
-
return text.strip()
|
160 |
-
|
161 |
-
|
162 |
-
def call_llm(seed: str):
|
163 |
-
resp = client.chat.completions.create(
|
164 |
-
model="gpt-4o-mini",
|
165 |
-
messages=[
|
166 |
-
{"role": "system", "content": SYSTEM_PROMPT},
|
167 |
-
{"role": "user", "content": seed},
|
168 |
-
],
|
169 |
-
temperature=0.9,
|
170 |
-
max_tokens=2048,
|
171 |
-
)
|
172 |
-
raw = resp.choices[0].message.content
|
173 |
-
cleaned = clean_json_block(raw)
|
174 |
-
try:
|
175 |
-
data = json.loads(cleaned)
|
176 |
-
if isinstance(data, dict):
|
177 |
-
data = [data]
|
178 |
-
if not isinstance(data, list):
|
179 |
-
raise TypeError("LLM ์๋ต์ด ๋ฆฌ์คํธ๊ฐ ์๋")
|
180 |
-
if not 1 <= len(data) <= 20:
|
181 |
-
raise ValueError("์๋ต ๋ฆฌ์คํธ ๊ธธ์ด๊ฐ 1~20 ๋ฒ์๋ฅผ ๋ฒ์ด๋จ")
|
182 |
-
except Exception as exc:
|
183 |
-
raise ValueError(f"LLM JSON ํ์ฑ ์คํจ: {exc}\n์๋ฌธ ์ผ๋ถ: {cleaned[:300]} โฆ")
|
184 |
-
return data
|
185 |
-
|
186 |
-
# -------------------------------------------------
|
187 |
-
# 2. Gradio callback
|
188 |
-
# -------------------------------------------------
|
189 |
-
|
190 |
-
def generate(seed_word: str):
|
191 |
-
results = call_llm(seed_word)
|
192 |
-
md_lines = [f"## ์ด {len(results)}๊ฐ ์ ์\n"]
|
193 |
-
for idx, item in enumerate(results, 1):
|
194 |
-
md_lines.append(
|
195 |
-
f"### {idx}. {item['top_phrase']} / {item['bottom_phrase']}\n"
|
196 |
-
f"- **์ฌ๋ก๊ฑด**: {item['slogan']}\n"
|
197 |
-
f"- **๋ธ๋๋ ๋ค์**: {item['brand']}\n"
|
198 |
-
f"- (tl={item['tl']}, tr={item['tr']}, br={item['br']}, bl={item['bl']})\n"
|
199 |
-
)
|
200 |
-
return "\n".join(md_lines)
|
201 |
-
|
202 |
-
# -------------------------------------------------
|
203 |
-
# 3. UI
|
204 |
-
# -------------------------------------------------
|
205 |
-
with gr.Blocks(title="Square Theory โ ์ต๋ 20๊ฐ ๐ฐ๐ท") as demo:
|
206 |
-
gr.Markdown("""# ๐ง Square Theory ์ ์ (์ต๋ 20๊ฐ)\n๋จ์ด ํ๋ ์
๋ ฅ โ LLM์ด ์ ์ํยท์ ๋ ฌํ ์ฌ๊ฐํ/์นดํผ/๋ธ๋๋ ๋ค์""")
|
207 |
-
seed = gr.Textbox(label="์๋ ๋จ์ด(TL)", placeholder="์: ๊ณจ๋ ")
|
208 |
-
run = gr.Button("์์ฑ")
|
209 |
-
md_out = gr.Markdown(label="์ ์ ๋ชฉ๋ก")
|
210 |
-
|
211 |
-
run.click(generate, inputs=seed, outputs=md_out)
|
212 |
-
|
213 |
-
if __name__ == "__main__":
|
214 |
-
demo.launch()
|
|
|
1 |
"""
|
2 |
Square Theory Generator (Markdownโonly, up to 20 ideas)
|
3 |
=====================================================
|
4 |
+
2025โ05โ28ย v9 โ ์ถ๋ ฅ ํฌ๋งท ๊ฐ์ & ํ๋ ๊ตฌ๋ถ ๋ช
ํํ
|
5 |
---------------------------------------------------
|
6 |
๋ณ๊ฒฝ ์์ฝ
|
7 |
---------
|
8 |
+
1. **์ฌ๋ก๊ฑด ยท ๋ธ๋๋ ๋ค์ ๊ตฌํ** โ Markdown ํ
ํ๋ฆฟ์ ๋ค์ ๊ตฌ์กฐ๋ก ๊ฐ์
|
9 |
+
```md
|
10 |
+
### 1. ๋ธ๋๋ ๋ค์: ๋ชจ๋์ปคํผ
|
11 |
+
**๋ฉ์ธ ์นดํผ**
|
12 |
+
โข ์๋จ: ํ๋ฏธ๋ฅผ ๋ด์ ์ปคํผ
|
13 |
+
โข ํ๋จ: ๋น์ ์ ํ๋ฃจ๋ฅผ ํน๋ณํ๊ฒ
|
14 |
+
|
15 |
+
**์ฌ๋ก๊ฑด**
|
16 |
+
> ์ปคํผ ํ์์ ์ฌ์
|
17 |
+
|
18 |
+
**์ฌ๊ฐํ ํค์๋**
|
19 |
+
TL: ํ๋ฏธ ยท TR: ์ปคํผ ยท BR: ํฅ๊ธฐ ยท BL: ํ์
|
20 |
+
```
|
21 |
+
2. **ํ๋กฌํํธ ๋ช
์ธ ์ ์ง** โ JSON ๊ตฌ์กฐ ๋ณ๋์ ์์(ํธํ).
|
22 |
+
3. **์ถ๊ฐ ๊ฐ์ ์ ์** โ ์ฝ๋ ๋ด ์ฃผ์์ผ๋ก โ์ถ๊ฐ ํ๋ยท๋ญํน ๊ธฐ์คยทCSV ๋ค์ด๋ก๋โ ์์ด๋์ด ์ฝ์
.
|
23 |
|
24 |
์คํ๋ฒ
|
25 |
------
|
|
|
33 |
import os
|
34 |
import json
|
35 |
import gradio as gr
|
36 |
+
from openai import OpenAI, error as oai_err
|
37 |
|
38 |
# -------------------------------------------------
|
39 |
# 0. OpenAI ํด๋ผ์ด์ธํธ
|
|
|
53 |
"๋ฐฐ์ด ์ฒซ ๋ฒ์งธ ์์๊ฐ ๊ฐ์ฅ ์ฐ์ํด์ผ ํ๋ค. JSON ์ธ ๋ฌธ์๋ ๊ธ์งํ๋ค."
|
54 |
)
|
55 |
|
56 |
+
FALLBACK_MODELS = ["gpt-4o-mini", "gpt-4o", "gpt-4o-preview", "gpt-4-turbo"]
|
57 |
|
58 |
+
|
59 |
+
def _clean_json_block(text: str) -> str:
|
60 |
+
"""Strip ```json fences if present."""
|
61 |
text = text.strip()
|
62 |
if text.startswith("```"):
|
63 |
text = text.split("\n", 1)[1] if "\n" in text else text[3:]
|
|
|
66 |
return text.strip()
|
67 |
|
68 |
|
69 |
+
def _call_llm(seed: str):
|
70 |
+
last_exc = None
|
71 |
+
for model in FALLBACK_MODELS:
|
72 |
+
try:
|
73 |
+
resp = client.chat.completions.create(
|
74 |
+
model=model,
|
75 |
+
messages=[
|
76 |
+
{"role": "system", "content": SYSTEM_PROMPT},
|
77 |
+
{"role": "user", "content": seed},
|
78 |
+
],
|
79 |
+
temperature=0.9,
|
80 |
+
max_tokens=2048,
|
81 |
+
)
|
82 |
+
cleaned = _clean_json_block(resp.choices[0].message.content)
|
83 |
+
data = json.loads(cleaned) if cleaned else []
|
84 |
+
if isinstance(data, dict):
|
85 |
+
data = [data]
|
86 |
+
if not isinstance(data, list) or not (1 <= len(data) <= 20):
|
87 |
+
raise ValueError("LLM ์๋ต์ด ์ฌ๋ฐ๋ฅธ 1โ20๊ฐ ๋ฆฌ์คํธ๊ฐ ์๋")
|
88 |
+
return data
|
89 |
+
except (oai_err.OpenAIError, json.JSONDecodeError, ValueError, TypeError) as exc:
|
90 |
+
last_exc = exc
|
91 |
+
continue
|
92 |
+
raise RuntimeError(f"LLM ํธ์ถ ์คํจ: {last_exc}")
|
93 |
|
94 |
# -------------------------------------------------
|
95 |
# 2. Gradio callback
|
96 |
# -------------------------------------------------
|
97 |
|
98 |
def generate(seed_word: str):
|
99 |
+
seed_word = seed_word.strip()
|
100 |
+
if not seed_word:
|
101 |
+
return "โ ๏ธ **์๋ ๋จ์ด๋ฅผ ์
๋ ฅํด ์ฃผ์ธ์.**"
|
102 |
+
try:
|
103 |
+
results = _call_llm(seed_word)
|
104 |
+
md = [f"## ์ด {len(results)}๊ฐ ์ ์"]
|
105 |
+
for idx, item in enumerate(results, 1):
|
106 |
+
md.append(
|
107 |
+
f"### {idx}. ๋ธ๋๋ ๋ค์: {item['brand']}\n"
|
108 |
+
f"**๋ฉ์ธ ์นดํผ** \
|
109 |
+
โข ์๋จ: {item['top_phrase']} \
|
110 |
+
โข ํ๋จ: {item['bottom_phrase']}\n\n"
|
111 |
+
f"**์ฌ๋ก๊ฑด** \
|
112 |
+
> {item['slogan']}\n\n"
|
113 |
+
f"**์ฌ๊ฐํ ํค์๋** \
|
114 |
+
TL: {item['tl']} ยท TR: {item['tr']} ยท BR: {item['br']} ยท BL: {item['bl']}\n"
|
115 |
+
)
|
116 |
+
return "\n".join(md)
|
117 |
+
except Exception as exc:
|
118 |
+
return f"โ **์ค๋ฅ:** {exc}"
|
119 |
|
120 |
# -------------------------------------------------
|
121 |
# 3. UI
|
122 |
# -------------------------------------------------
|
123 |
with gr.Blocks(title="Square Theory โ ์ต๋ 20๊ฐ ๐ฐ๐ท") as demo:
|
124 |
+
gr.Markdown("""# ๐ง Square Theory ์ ์ (์ต๋ 20๊ฐ)\n๋จ์ด ํ๋ ์
๋ ฅ โ LLM์ด ์ ๋ ฌํ ์ฌ๊ฐํ/์นดํผ/๋ธ๋๋ ๋ค์""")
|
125 |
seed = gr.Textbox(label="์๋ ๋จ์ด(TL)", placeholder="์: ๊ณจ๋ ")
|
126 |
run = gr.Button("์์ฑ")
|
127 |
+
md_out = gr.Markdown()
|
128 |
|
129 |
run.click(generate, inputs=seed, outputs=md_out)
|
130 |
|
131 |
+
# queue(): ๋ก๋ฉ ์ธ๋์ผ์ดํฐ & ๋์ ์ ์ ์์ ํ
|
132 |
+
demo.queue().launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
|
134 |
# -------------------------------------------------
|
135 |
+
# 4. (Optional) ์ถ๊ฐ ๊ธฐ๋ฅ ์์ด๋์ด
|
136 |
# -------------------------------------------------
|
137 |
+
# - JSON์ "why"(์ ์ ์ด์ ) ํ๋ ์์ฒญ โ ์์ด๋์ด ์ค๋๋ ฅ ๊ฐํ
|
138 |
+
# - ์ฑ๋๋ณ ํ๊ทธ๋ผ์ธ ๋ณํ(OOHยทSNSยทTV) ์๋ ์์ฑ ์ต์
|
139 |
+
# - ๊ฒฐ๊ณผ CSV ๋ค์ด๋ก๋ ๋ฒํผ ์ถ๊ฐ(๊ทธ๋ผ๋์ค File component)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|