openfree commited on
Commit
5b4cfb6
ยท
verified ยท
1 Parent(s): 2710a10

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +108 -63
app.py CHANGED
@@ -1,42 +1,67 @@
1
  """
2
- Square Theory Generator (Gradio demo)
3
- ------------------------------------
4
- ์ด ์Šคํฌ๋ฆฝํŠธ๋Š” Adam Aaronson์˜ โ€œSquare Theoryโ€ ๊ฐœ๋…์„ ํ•œ๊ตญ์–ด ์นดํ”ผ๋ผ์ดํŒ…ยท๋ธŒ๋žœ๋“œ ๋„ค์ด๋ฐ์—
5
- ์‹คํ—˜์ ์œผ๋กœ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ„๋‹จํ•œ ์›น ์•ฑ์ž…๋‹ˆ๋‹ค.
6
-
7
- ๐Ÿงฉ **๋ฌด์—‡์„ ํ•˜๋‚˜์š”?**
8
- 1. ์‚ฌ์šฉ์ž๊ฐ€ ๋„ค ๊ฐœ์˜ ๋‹จ์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด (์œ—๋ณ€ AยทB, ์•„๋žซ๋ณ€ A'ยทB')
9
- 2. ๋„ค ๋ณ€์ด ์™„์ „ํ•œ ์‚ฌ๊ฐํ˜•์„ ์ด๋ฃจ๋Š”์ง€ ์‹œ๊ฐ์ ์œผ๋กœ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค.
10
- 3. ๋™์‹œ์— ๋‘ ์ค„ ์Šฌ๋กœ๊ฑด, ๋ธŒ๋žœ๋“œ ๋„ค์ž„ ํ›„๋ณด๋ฅผ ์ž๋™ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.
11
-
12
- โœ๏ธ **๊ตฌํ˜„ ํฌ์ธํŠธ**
13
- - **Gradio Blocks**๋ฅผ ์‚ฌ์šฉํ•ด ์ž…๋ ฅยท์ถœ๋ ฅ ๋ ˆ์ด์•„์›ƒ์„ ์ž์œ ๋กญ๊ฒŒ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. (cf. gradio docs)
14
- - **Matplotlib**๋กœ ๊ฐ„๋‹จํ•œ ์‚ฌ๊ฐํ˜• ๋„์‹์„ ๊ทธ๋ ค์„œ ์‹œ๊ฐ์  ์ดํ•ด๋ฅผ ๋•์Šต๋‹ˆ๋‹ค.
15
- - ๋‹จ์–ด ์กฐํ•ฉ ๋กœ์ง์€ ๋ฐ๋ชจ ๋ชฉ์ ์˜ ์‹ฌํ”Œ ๋ฒ„์ „์ž…๋‹ˆ๋‹ค. ํ•„์š”ํ•˜๋ฉด WordNetยท๊ผฌ๊ผฌ๋งˆ ๋“ฑ์œผ๋กœ ํ™•์žฅ ๊ฐ€๋Šฅ.
16
-
17
- Author: ChatGPT (OpenAI)
18
  """
19
 
 
 
20
  import gradio as gr
21
  import matplotlib.pyplot as plt
22
- from matplotlib import patches
23
-
24
-
25
- def draw_square(a: str, b: str, a_alt: str, b_alt: str):
26
- """Return a matplotlib Figure that visualizes the Square Theory diagram."""
27
- # Create figure & axis
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  fig, ax = plt.subplots(figsize=(4, 4))
29
- # Draw square border
30
  square = patches.Rectangle((0, 0), 1, 1, fill=False, linewidth=2)
31
  ax.add_patch(square)
32
 
33
- # Corner labels
34
- ax.text(-0.05, 1.05, a, ha="right", va="bottom", fontsize=14, fontweight="bold")
35
- ax.text(1.05, 1.05, b, ha="left", va="bottom", fontsize=14, fontweight="bold")
36
- ax.text(-0.05, -0.05, a_alt, ha="right", va="top", fontsize=14, fontweight="bold")
37
- ax.text(1.05, -0.05, b_alt, ha="left", va="top", fontsize=14, fontweight="bold")
38
 
39
- # Hide axes
40
  ax.set_xticks([])
41
  ax.set_yticks([])
42
  ax.set_xlim(-0.2, 1.2)
@@ -44,51 +69,71 @@ def draw_square(a: str, b: str, a_alt: str, b_alt: str):
44
  ax.set_aspect("equal")
45
  return fig
46
 
 
 
 
47
 
48
- def generate_square(a: str, b: str, a_alt: str, b_alt: str):
49
- """Core callback: returns diagram + text outputs."""
50
- # Basic phrases
51
- top_phrase = f"{a} {b}"
52
- bottom_phrase = f"{a_alt} {b_alt}"
53
-
54
- # Simple 2โ€‘line slogan
55
- slogan = f"{top_phrase}!\n{bottom_phrase}!"
56
-
57
- # Brand suggestions (kor+eng)
58
- brand_korean = f"{a_alt}{b_alt}"
59
- brand_english = f"{a.capitalize()}{b.capitalize()}"
60
- brand_combo = f"{brand_korean} / {brand_english}"
61
-
62
- # Figure
63
- fig = draw_square(a, b, a_alt, b_alt)
64
 
65
- return fig, top_phrase, bottom_phrase, slogan, brand_combo
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
- with gr.Blocks(title="Square Theory Generator ๐Ÿ‡ฐ๐Ÿ‡ท") as demo:
69
- gr.Markdown("""# โœจ Square Theory Generator\n๋„ค ๊ฐœ์˜ ๋‹จ์–ด๋กœ ์‚ฌ๊ฐํ˜•์„ ์™„์„ฑํ•ด ์นดํ”ผยท๋ธŒ๋žœ๋“œ ์•„์ด๋””์–ด๋ฅผ ๋งŒ๋“ค์–ด ๋ณด์„ธ์š”.""")
70
-
71
- with gr.Row():
72
- a = gr.Textbox(label="๋‹จ์–ด 1 (์™ผ์ชฝ ์œ„)")
73
- b = gr.Textbox(label="๋‹จ์–ด 2 (์˜ค๋ฅธ์ชฝ ์œ„)")
74
- with gr.Row():
75
- a_alt = gr.Textbox(label="๋Œ€์‘ ๋‹จ์–ด 1 (์™ผ์ชฝ ์•„๋ž˜)")
76
- b_alt = gr.Textbox(label="๋Œ€์‘ ๋‹จ์–ด 2 (์˜ค๋ฅธ์ชฝ ์•„๋ž˜)")
77
 
78
- btn = gr.Button("์‚ฌ๊ฐํ˜• ์ƒ์„ฑ")
 
79
 
80
  fig_out = gr.Plot(label="์‚ฌ๊ฐํ˜• ๋‹ค์ด์–ด๊ทธ๋žจ")
81
  top_out = gr.Textbox(label="์œ—๋ณ€ ํ‘œํ˜„")
82
  bottom_out = gr.Textbox(label="์•„๋žซ๋ณ€ ํ‘œํ˜„")
83
- slogan_out = gr.Textbox(label="2ํ–‰ ์Šฌ๋กœ๊ฑด ์ œ์•ˆ")
84
  brand_out = gr.Textbox(label="๋ธŒ๋žœ๋“œ ๋„ค์ž„ ์ œ์•ˆ")
85
 
86
- btn.click(
87
- fn=generate_square,
88
- inputs=[a, b, a_alt, b_alt],
89
- outputs=[fig_out, top_out, bottom_out, slogan_out, brand_out],
90
- )
91
 
92
  if __name__ == "__main__":
93
- # For local testing; in production, set share=True or host on Spaces.
94
  demo.launch()
 
1
  """
2
+ Square Theory Generator (Koreanโ€‘friendly, LLMโ€‘powered)
3
+ -----------------------------------------------------
4
+ ๋ณ€๊ฒฝ ์‚ฌํ•ญ
5
+ 0) Matplotlib ํ•œ๊ธ€ ํฐํŠธ ์ž๋™ ์„ค์ •์œผ๋กœ ๊นจ์ง ํ•ด๊ฒฐ
6
+ 1) ์ž…๋ ฅ ํ”„๋กฌํ”„ํŠธ๋ฅผ ํ•˜๋‚˜๋งŒ ๋‘๊ณ , ๋‚˜๋จธ์ง€ 3๋‹จ์–ด๋Š” LLM์ด ์ƒ์„ฑ
7
+ 2) OpenAI ๊ณต์‹ Python SDK (`openai`) ์‚ฌ์šฉ โ€“ ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ `client.responses.create` ์˜ˆ์‹œ ๋ฐ˜์˜
8
+ 3) LLM์ด ์‚ฌ๊ฐํ˜• ๋‹จ์–ด + ๋‘ ์ค„ ์นดํ”ผยท๋ธŒ๋žœ๋“œ ๋„ค์ž„๊นŒ์ง€ JSON์œผ๋กœ ๋ฐ˜ํ™˜ โ†’ ์ž๋™ ์ถœ๋ ฅ
9
+
10
+ โš ๏ธ **OPENAI_API_KEY** ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ๋ฏธ๋ฆฌ ์„ค์ •ํ•ด ๋‘์„ธ์š”.
 
 
 
 
 
 
 
11
  """
12
 
13
+ import os
14
+ import json
15
  import gradio as gr
16
  import matplotlib.pyplot as plt
17
+ from matplotlib import patches, font_manager, rcParams
18
+ from openai import OpenAI
19
+
20
+ # -------------------------------------------------
21
+ # 0. ํ•œ๊ธ€ ํฐํŠธ ์ž๋™ ํƒ์ƒ‰ & ์„ค์ • (Malgun, Nanum ๋“ฑ)
22
+ # -------------------------------------------------
23
+
24
+ def _set_korean_font():
25
+ candidates = [
26
+ "Malgun Gothic", # Windows
27
+ "NanumGothic", # Linux (apt install fonts-nanum)
28
+ "AppleGothic", # macOS
29
+ "DejaVu Sans", # fallback (๊ธฐ๋ณธ ํฐํŠธ์ง€๋งŒ ํ•œ๊ธ€ ์ผ๋ถ€ ์ง€์›)
30
+ ]
31
+ available = {f.name for f in font_manager.fontManager.ttflist}
32
+ for cand in candidates:
33
+ if cand in available:
34
+ rcParams["font.family"] = cand
35
+ break
36
+ rcParams["axes.unicode_minus"] = False # ์Œ์ˆ˜ ๋ถ€ํ˜ธ ๊นจ์ง ๋ฐฉ์ง€
37
+
38
+ _set_korean_font()
39
+
40
+ # -------------------------------------------------
41
+ # 1. OpenAI ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
42
+ # -------------------------------------------------
43
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
44
+ if not OPENAI_API_KEY:
45
+ raise EnvironmentError("OPENAI_API_KEY ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜์„ธ์š”.")
46
+
47
+ client = OpenAI()
48
+
49
+ # -------------------------------------------------
50
+ # 2. Square Theory ๋„์‹ ๊ทธ๋ฆฌ๊ธฐ
51
+ # -------------------------------------------------
52
+
53
+ def draw_square(words):
54
+ """words = dict with keys tl, tr, br, bl"""
55
  fig, ax = plt.subplots(figsize=(4, 4))
56
+ # ์‚ฌ๊ฐํ˜• ํ”„๋ ˆ์ž„
57
  square = patches.Rectangle((0, 0), 1, 1, fill=False, linewidth=2)
58
  ax.add_patch(square)
59
 
60
+ ax.text(-0.05, 1.05, words["tl"], ha="right", va="bottom", fontsize=14, fontweight="bold")
61
+ ax.text(1.05, 1.05, words["tr"], ha="left", va="bottom", fontsize=14, fontweight="bold")
62
+ ax.text(1.05, -0.05, words["br"], ha="left", va="top", fontsize=14, fontweight="bold")
63
+ ax.text(-0.05, -0.05, words["bl"], ha="right", va="top", fontsize=14, fontweight="bold")
 
64
 
 
65
  ax.set_xticks([])
66
  ax.set_yticks([])
67
  ax.set_xlim(-0.2, 1.2)
 
69
  ax.set_aspect("equal")
70
  return fig
71
 
72
+ # -------------------------------------------------
73
+ # 3. LLM ํ”„๋กฌํ”„ํŠธ & ์‘๋‹ต ํŒŒ์‹ฑ
74
+ # -------------------------------------------------
75
 
76
+ 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๋งŒ ๋ฐ˜ํ™˜ํ•œ๋‹ค."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
 
78
 
79
+ def call_llm(seed):
80
+ """Returns parsed dict from LLM JSON output."""
81
+ response = client.responses.create(
82
+ model="gpt-4.1-mini",
83
+ input=[
84
+ {"role": "system", "content": [{"type": "input_text", "text": SYSTEM_PROMPT}]},
85
+ {"role": "user", "content": [{"type": "input_text", "text": seed}]},
86
+ ],
87
+ text={"format": {"type": "text"}},
88
+ temperature=0.9,
89
+ max_output_tokens=512,
90
+ )
91
+ raw = response.choices[0].message.content[0]["text"]
92
+ try:
93
+ data = json.loads(raw)
94
+ except json.JSONDecodeError as e:
95
+ raise ValueError(f"LLM ์‘๋‹ต์ด JSON ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค: {e}\n์›๋ณธ: {raw}")
96
+ return data
97
+
98
+ # -------------------------------------------------
99
+ # 4. Gradio ์ฝœ๋ฐฑ
100
+ # -------------------------------------------------
101
+
102
+ def generate(seed_word):
103
+ data = call_llm(seed_word)
104
+
105
+ words = {
106
+ "tl": data["tl"],
107
+ "tr": data["tr"],
108
+ "br": data["br"],
109
+ "bl": data["bl"],
110
+ }
111
+ fig = draw_square(words)
112
+ return (
113
+ fig,
114
+ data.get("top_phrase", ""),
115
+ data.get("bottom_phrase", ""),
116
+ data.get("slogan", ""),
117
+ data.get("brand", ""),
118
+ )
119
 
120
+ # -------------------------------------------------
121
+ # 5. Gradio UI
122
+ # -------------------------------------------------
123
+ with gr.Blocks(title="Square Theory Generator โ€“ LLM Powered ๐Ÿ‡ฐ๐Ÿ‡ท") as demo:
124
+ gr.Markdown("""# ๐ŸŸง Square Theory Generator\n๋‹จ์–ด ํ•˜๋‚˜๋งŒ ์ž…๋ ฅํ•˜๋ฉด, LLM์ด ์‚ฌ๊ฐํ˜• ๊ตฌ์กฐยท์นดํ”ผยท๋ธŒ๋žœ๋“œ ๋„ค์ž„์„ ์ž๋™์œผ๋กœ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.""")
 
 
 
 
125
 
126
+ seed = gr.Textbox(label="์‹œ๋“œ ๋‹จ์–ด(TL)", placeholder="์˜ˆ: ๊ณจ๋“ ")
127
+ run_btn = gr.Button("์‚ฌ๊ฐํ˜• ์ƒ์„ฑ")
128
 
129
  fig_out = gr.Plot(label="์‚ฌ๊ฐํ˜• ๋‹ค์ด์–ด๊ทธ๋žจ")
130
  top_out = gr.Textbox(label="์œ—๋ณ€ ํ‘œํ˜„")
131
  bottom_out = gr.Textbox(label="์•„๋žซ๋ณ€ ํ‘œํ˜„")
132
+ slogan_out = gr.Textbox(label="๋‘ ์ค„ ์Šฌ๋กœ๊ฑด")
133
  brand_out = gr.Textbox(label="๋ธŒ๋žœ๋“œ ๋„ค์ž„ ์ œ์•ˆ")
134
 
135
+ run_btn.click(fn=generate, inputs=seed, outputs=[fig_out, top_out, bottom_out, slogan_out, brand_out])
 
 
 
 
136
 
137
  if __name__ == "__main__":
138
+ # launch(): share=True ํ•˜๋ฉด ์™ธ๋ถ€์—์„œ ์ ‘์† ๊ฐ€๋Šฅ
139
  demo.launch()