""" 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 # ------------------------------------------------- # 0. 한글 폰트 설정 # ------------------------------------------------- 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() # ------------------------------------------------- # 1. OpenAI 클라이언트 # ------------------------------------------------- if not os.getenv("OPENAI_API_KEY"): raise EnvironmentError("OPENAI_API_KEY 환경 변수를 설정하세요.") client = OpenAI() # ------------------------------------------------- # 2. Square Diagram # ------------------------------------------------- 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 # ------------------------------------------------- # 3. LLM Prompt & Call # ------------------------------------------------- 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 # ------------------------------------------------- # 4. Gradio callback # ------------------------------------------------- def generate(seed_word: str): results = call_llm(seed_word) # 1위 도식 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 # ------------------------------------------------- # 5. UI # ------------------------------------------------- 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()