|
""" |
|
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 |
|
|
|
|
|
|
|
|
|
|
|
def _set_korean_font(): |
|
candidates = [ |
|
"Malgun Gothic", |
|
"NanumGothic", |
|
"AppleGothic", |
|
"DejaVu Sans", |
|
] |
|
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() |
|
|
|
|
|
|
|
|
|
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") |
|
if not OPENAI_API_KEY: |
|
raise EnvironmentError("OPENAI_API_KEY ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํ์ธ์.") |
|
|
|
client = OpenAI() |
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
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", ""), |
|
) |
|
|
|
|
|
|
|
|
|
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__": |
|
|
|
demo.launch() |
|
|