File size: 5,728 Bytes
26ca8c8 5b4cfb6 26ca8c8 5b4cfb6 26ca8c8 5b4cfb6 26ca8c8 5b4cfb6 26ca8c8 5b4cfb6 26ca8c8 5b4cfb6 26ca8c8 5b4cfb6 26ca8c8 5b4cfb6 26ca8c8 5b4cfb6 26ca8c8 5b4cfb6 26ca8c8 5b4cfb6 26ca8c8 5b4cfb6 26ca8c8 5b4cfb6 26ca8c8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
"""
Square Theory Generator (Koreanโfriendly, LLMโpowered)
-----------------------------------------------------
๋ณ๊ฒฝ ์ฌํญ
0) Matplotlib ํ๊ธ ํฐํธ ์๋ ์ค์ ์ผ๋ก ๊นจ์ง ํด๊ฒฐ
1) ์
๋ ฅ ํ๋กฌํํธ๋ฅผ ํ๋๋ง ๋๊ณ , ๋๋จธ์ง 3๋จ์ด๋ LLM์ด ์์ฑ
2) OpenAI ๊ณต์ Python SDK (`openai`) ์ฌ์ฉ โ ์ฌ์ฉ์๊ฐ ์ ๊ณตํ `client.responses.create` ์์ ๋ฐ์
3) LLM์ด ์ฌ๊ฐํ ๋จ์ด + ๋ ์ค ์นดํผยท๋ธ๋๋ ๋ค์๊น์ง JSON์ผ๋ก ๋ฐํ โ ์๋ ์ถ๋ ฅ
โ ๏ธ **OPENAI_API_KEY** ํ๊ฒฝ ๋ณ์๋ฅผ ๋ฏธ๋ฆฌ ์ค์ ํด ๋์ธ์.
"""
import os
import json
import gradio as gr
import matplotlib.pyplot as plt
from matplotlib import patches, font_manager, rcParams
from openai import OpenAI
# -------------------------------------------------
# 0. ํ๊ธ ํฐํธ ์๋ ํ์ & ์ค์ (Malgun, Nanum ๋ฑ)
# -------------------------------------------------
def _set_korean_font():
candidates = [
"Malgun Gothic", # Windows
"NanumGothic", # Linux (apt install fonts-nanum)
"AppleGothic", # macOS
"DejaVu Sans", # fallback (๊ธฐ๋ณธ ํฐํธ์ง๋ง ํ๊ธ ์ผ๋ถ ์ง์)
]
available = {f.name for f in font_manager.fontManager.ttflist}
for cand in candidates:
if cand in available:
rcParams["font.family"] = cand
break
rcParams["axes.unicode_minus"] = False # ์์ ๋ถํธ ๊นจ์ง ๋ฐฉ์ง
_set_korean_font()
# -------------------------------------------------
# 1. OpenAI ํด๋ผ์ด์ธํธ ์ด๊ธฐํ
# -------------------------------------------------
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
if not OPENAI_API_KEY:
raise EnvironmentError("OPENAI_API_KEY ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํ์ธ์.")
client = OpenAI()
# -------------------------------------------------
# 2. Square Theory ๋์ ๊ทธ๋ฆฌ๊ธฐ
# -------------------------------------------------
def draw_square(words):
"""words = dict with keys tl, tr, br, bl"""
fig, ax = plt.subplots(figsize=(4, 4))
# ์ฌ๊ฐํ ํ๋ ์
square = patches.Rectangle((0, 0), 1, 1, fill=False, linewidth=2)
ax.add_patch(square)
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
# -------------------------------------------------
# 3. LLM ํ๋กฌํํธ & ์๋ต ํ์ฑ
# -------------------------------------------------
SYSTEM_PROMPT = """๋๋ ํ๊ตญ์ด ์นดํผยท๋ธ๋๋ ๋ค์ด๋ฐ ์ ๋ฌธ๊ฐ์ด์ Square Theory(์ฌ๊ฐํ ์ด๋ก ) ๋์ฐ๋ฏธ๋ค.\n์ฌ์ฉ์๊ฐ ์ ์ํ ํ๋์ ๋จ์ด(TL)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก, ๋ค์ JSON ํ์์ผ๋ก ์ฌ๊ฐํ์ ์์ฑํ๋ผ.\n- "tl": ์
๋ ฅ ๋จ์ด ๊ทธ๋๋ก\n- "tr": TL๊ณผ ์๋ฏธยท์์ด ๋์๋๋ ๋ค๋ฅธ ๋จ์ด\n- "br": TR๊ณผ ์๋ฏธยท์์ด ๋์๋๋ ๋ค๋ฅธ ๋จ์ด\n- "bl": BR๊ณผ ์๋ฏธยท์์ด ๋์๋๋ ๋ค๋ฅธ ๋จ์ด๋ก TL๊ณผ๋ ์ฐ๊ฒฐ์ด ์์ด์ผ ํ๋ค\n๋ํ, "top_phrase"(tl+tr), "bottom_phrase"(bl+br) ๋ ํํ์ ์์ฐ์ค๋ฌ์ด ํ๊ตญ์ด ๊ด์ฉ๊ตฌ๋ก ์ ์ํ๊ณ ,\n"slogan"(๋ ์ค ์นดํผ)์ "brand"(๋ธ๋๋ ๋ค์ ํ๋ณด) ํ๋๋ฅผ ํจ๊ป ํฌํจํ๋ผ.\n๊ฒฐ๊ณผ๋ ๋ฐ๋์ **ํ๊ธ UTFโ8**๋ก ์ธ์ฝ๋ฉ๋ JSON๋ง ๋ฐํํ๋ค."""
def call_llm(seed):
"""Returns parsed dict from LLM JSON output."""
response = client.responses.create(
model="gpt-4.1-mini",
input=[
{"role": "system", "content": [{"type": "input_text", "text": SYSTEM_PROMPT}]},
{"role": "user", "content": [{"type": "input_text", "text": seed}]},
],
text={"format": {"type": "text"}},
temperature=0.9,
max_output_tokens=512,
)
raw = response.choices[0].message.content[0]["text"]
try:
data = json.loads(raw)
except json.JSONDecodeError as e:
raise ValueError(f"LLM ์๋ต์ด JSON ํ์์ด ์๋๋๋ค: {e}\n์๋ณธ: {raw}")
return data
# -------------------------------------------------
# 4. Gradio ์ฝ๋ฐฑ
# -------------------------------------------------
def generate(seed_word):
data = call_llm(seed_word)
words = {
"tl": data["tl"],
"tr": data["tr"],
"br": data["br"],
"bl": data["bl"],
}
fig = draw_square(words)
return (
fig,
data.get("top_phrase", ""),
data.get("bottom_phrase", ""),
data.get("slogan", ""),
data.get("brand", ""),
)
# -------------------------------------------------
# 5. Gradio UI
# -------------------------------------------------
with gr.Blocks(title="Square Theory Generator โ LLM Powered ๐ฐ๐ท") as demo:
gr.Markdown("""# ๐ง Square Theory Generator\n๋จ์ด ํ๋๋ง ์
๋ ฅํ๋ฉด, LLM์ด ์ฌ๊ฐํ ๊ตฌ์กฐยท์นดํผยท๋ธ๋๋ ๋ค์์ ์๋์ผ๋ก ์ ์ํฉ๋๋ค.""")
seed = gr.Textbox(label="์๋ ๋จ์ด(TL)", placeholder="์: ๊ณจ๋ ")
run_btn = gr.Button("์ฌ๊ฐํ ์์ฑ")
fig_out = gr.Plot(label="์ฌ๊ฐํ ๋ค์ด์ด๊ทธ๋จ")
top_out = gr.Textbox(label="์๋ณ ํํ")
bottom_out = gr.Textbox(label="์๋ซ๋ณ ํํ")
slogan_out = gr.Textbox(label="๋ ์ค ์ฌ๋ก๊ฑด")
brand_out = gr.Textbox(label="๋ธ๋๋ ๋ค์ ์ ์")
run_btn.click(fn=generate, inputs=seed, outputs=[fig_out, top_out, bottom_out, slogan_out, brand_out])
if __name__ == "__main__":
# launch(): share=True ํ๋ฉด ์ธ๋ถ์์ ์ ์ ๊ฐ๋ฅ
demo.launch()
|