|
""" |
|
Square Theory Generator (10 best variations) |
|
=========================================== |
|
2025โ05โ28ย v3 โ ํ ์
๋ ฅ โ ์ฐ์๋ ์ 10๊ฐ์ง ๊ฒฐ๊ณผ |
|
------------------------------------------------ |
|
๋ณ๊ฒฝ ์์ฝ |
|
--------- |
|
1. **LLM ํย ํ ํธ์ถ๋ก 10๊ฐ์ ์ฌ๊ฐํ ์ ์** |
|
* JSON ๋ฐฐ์ด ํํ๋ก ๋ฐํ, ํ์ง์ด ๋์ ์์๋ก ์ ๋ ฌ (1ย โย 10) |
|
2. **์ถ๋ ฅ** |
|
* โ 1์ ์ ์์ ์ฌ๊ฐํ ๋์(Plot) |
|
* โก 10๊ฐ ์ ์์ ๋งํฌ๋ค์ด ๋ฆฌ์คํธ๋ก ์ ๋ฆฌํ์ฌ ํ์ |
|
3. **UI ๊ฐ์ํ** : seed ์
๋ ฅย +ย ์คํ ๋ฒํผ + Plot + Markdown |
|
4. **์๋ฌ ์ฒ๋ฆฌ ๊ฐํ** |
|
|
|
์คํ๋ฒ |
|
------ |
|
```bash |
|
pip install --upgrade gradio matplotlib openai |
|
export OPENAI_API_KEY="sk-..." |
|
python square_theory_gradio.py |
|
``` |
|
""" |
|
|
|
import os |
|
import json |
|
import gradio as gr |
|
import matplotlib.pyplot as plt |
|
from matplotlib import patches, font_manager, rcParams |
|
from openai import OpenAI |
|
|
|
|
|
|
|
|
|
|
|
def _set_korean_font(): |
|
for cand in ("Malgun Gothic", "NanumGothic", "AppleGothic", "DejaVu Sans"): |
|
if cand in {f.name for f in font_manager.fontManager.ttflist}: |
|
rcParams["font.family"] = cand |
|
break |
|
rcParams["axes.unicode_minus"] = False |
|
|
|
_set_korean_font() |
|
|
|
|
|
|
|
|
|
if not os.getenv("OPENAI_API_KEY"): |
|
raise EnvironmentError("OPENAI_API_KEY ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํ์ธ์.") |
|
|
|
client = OpenAI() |
|
|
|
|
|
|
|
|
|
|
|
def draw_square(words): |
|
fig, ax = plt.subplots(figsize=(4, 4)) |
|
ax.add_patch(patches.Rectangle((0, 0), 1, 1, fill=False, linewidth=2)) |
|
ax.text(-0.05, 1.05, words["tl"], ha="right", va="bottom", fontsize=14, fontweight="bold") |
|
ax.text(1.05, 1.05, words["tr"], ha="left", va="bottom", fontsize=14, fontweight="bold") |
|
ax.text(1.05, -0.05, words["br"], ha="left", va="top", fontsize=14, fontweight="bold") |
|
ax.text(-0.05, -0.05, words["bl"], ha="right", va="top", fontsize=14, fontweight="bold") |
|
ax.set_xticks([]) |
|
ax.set_yticks([]) |
|
ax.set_xlim(-0.2, 1.2) |
|
ax.set_ylim(-0.2, 1.2) |
|
ax.set_aspect("equal") |
|
return fig |
|
|
|
|
|
|
|
|
|
SYSTEM_PROMPT = ( |
|
"๋๋ ํ๊ตญ์ด ์นดํผยท๋ธ๋๋ ๋ค์ด๋ฐ ์ ๋ฌธ๊ฐ์ด์ Square Theory ๋์ฐ๋ฏธ๋ค. " |
|
"์ฌ์ฉ์๊ฐ ์ค ํ๋์ ๋จ์ด(tl)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ง์ด ๊ฐ์ฅ ๋ฐ์ด๋ ๊ฒ๋ถํฐ 10๊ฐ์ ์ ์์ JSON ๋ฐฐ์ด๋ก ๋ฐํํด๋ผ. " |
|
"๊ฐ ๋ฐฐ์ด ์์๋ tl, tr, br, bl, top_phrase, bottom_phrase, slogan, brand ํ๋๋ฅผ ๊ฐ์ง๋ฉฐ, " |
|
"์ฌ๊ฐํ ๋ค ๊ผญ์ง์ (tl>tr>br>bl)๊ณผ ๋ ํํยท์ฌ๋ก๊ฑดยท๋ธ๋๋ ๋ค์์ด ์์ฐ์ค๋ฝ๊ฒ ์ฐ๊ฒฐ๋ผ์ผ ํ๋ค. " |
|
"๋ฐฐ์ด์ ์ต๊ณ ์ ์ ์์ด index 0, ๊ทธ๋ค์์ด index 1 โฆ 9 ์์์ฌ์ผ ํ๋ค. " |
|
"๊ฒฐ๊ณผ๋ JSON ์ธ ๋ค๋ฅธ ํ
์คํธ๋ฅผ ํฌํจํ๋ฉด ์ ๋๋ค." |
|
) |
|
|
|
|
|
def call_llm(seed: str): |
|
resp = client.chat.completions.create( |
|
model="gpt-4o-mini", |
|
messages=[ |
|
{"role": "system", "content": SYSTEM_PROMPT}, |
|
{"role": "user", "content": seed}, |
|
], |
|
temperature=0.9, |
|
max_tokens=1024, |
|
) |
|
raw = resp.choices[0].message.content.strip() |
|
try: |
|
data = json.loads(raw) |
|
if not isinstance(data, list) or len(data) != 10: |
|
raise ValueError("JSON ๋ฐฐ์ด ๊ธธ์ด๊ฐ 10์ด ์๋") |
|
except Exception as exc: |
|
raise ValueError(f"LLM JSON ํ์ฑ ์คํจ: {exc}\n์๋ฌธ: {raw[:300]} โฆ") |
|
return data |
|
|
|
|
|
|
|
|
|
|
|
def generate(seed_word: str): |
|
results = call_llm(seed_word) |
|
|
|
fig = draw_square({k: results[0][k] for k in ("tl", "tr", "br", "bl")}) |
|
|
|
|
|
md_lines = [] |
|
for idx, item in enumerate(results, 1): |
|
md_lines.append( |
|
f"### {idx}. {item['top_phrase']} / {item['bottom_phrase']}\n" |
|
f"- **์ฌ๋ก๊ฑด**: {item['slogan']}\n" |
|
f"- **๋ธ๋๋ ๋ค์**: {item['brand']}\n" |
|
f"- (tl={item['tl']}, tr={item['tr']}, br={item['br']}, bl={item['bl']})\n" |
|
) |
|
markdown_out = "\n".join(md_lines) |
|
return fig, markdown_out |
|
|
|
|
|
|
|
|
|
with gr.Blocks(title="Square Theory โ Topย 10 ๐ฐ๐ท") as demo: |
|
gr.Markdown("""# ๐ Square Theory ์ ์ Topย 10\n๋จ์ด 1๊ฐ โ LLM์ด ํ๊ฐยท์ ๋ ฌํ 10๊ฐ ์ฌ๊ฐํ/์นดํผ/๋ธ๋๋ ๋ค์""") |
|
seed = gr.Textbox(label="์๋ ๋จ์ด(TL)", placeholder="์: ๊ณจ๋ ") |
|
run = gr.Button("์์ฑ") |
|
fig_out = gr.Plot(label="1์ ์ฌ๊ฐํ") |
|
md_out = gr.Markdown(label="Topย 10 ์ ์") |
|
|
|
run.click(generate, inputs=seed, outputs=[fig_out, md_out]) |
|
|
|
if __name__ == "__main__": |
|
demo.launch() |
|
|