Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import gradio as gr
|
3 |
+
import openai
|
4 |
+
import requests
|
5 |
+
import logging
|
6 |
+
import asyncio
|
7 |
+
import aiohttp
|
8 |
+
|
9 |
+
# ๋ก๊น
์ค์
|
10 |
+
logging.basicConfig(level=logging.INFO)
|
11 |
+
logger = logging.getLogger(__name__)
|
12 |
+
|
13 |
+
# OpenAI API ํด๋ผ์ด์ธํธ ์ค์
|
14 |
+
openai.api_key = os.getenv("OPENAI_API_KEY")
|
15 |
+
|
16 |
+
# ์์ฒญ์ฌํญ์ ๋ฐ๋ผ ์นดํ
๊ณ ๋ฆฌ ์์
|
17 |
+
CATEGORIES = [
|
18 |
+
"๊ณตํฌ ๋ง์ผํ
",
|
19 |
+
"์คํ ๋ฆฌํ
๋ง"
|
20 |
+
]
|
21 |
+
|
22 |
+
def get_category_prompt(category):
|
23 |
+
if category == "๊ณตํฌ ๋ง์ผํ
":
|
24 |
+
return """
|
25 |
+
# ๊ณตํฌ ๋ง์ผํ
์นดํผ๋ผ์ดํ
์์ฑ ๊ท์น
|
26 |
+
1. ๋ฐ๋์ ํ๊ธ๋ก ์ถ๋ ฅํ๋ผ.
|
27 |
+
2. ๋๋ ์ธ๊ณ ์ต๊ณ ์ ๊ณตํฌ ๋ง์ผํ
์นดํผ๋ผ์ดํฐ์ด๋ค.
|
28 |
+
3. ๋ฐ๋์ ์นดํผ๋ผ์ดํ
์ 30์ ์ด๋ด๋ก ์์ฑํ๊ณ , 20๊ฐ๋ง ์ถ๋ ฅํ๋ผ.(๋ค๋ฅธ ๋ด์ฉ ์ถ๋ ฅ ๊ธ์ง)
|
29 |
+
4. ๋ฐ๋์ ์
๋ ฅ๋ ์ฃผ์ ๋ก๋ง ์์ฑํ๊ณ ์์๋ฅผ ์ฐธ๊ณ ํ์ฌ ๋ด์ฉ์ ๋ณด๊ฐํ๋ผ.
|
30 |
+
5. ๋ฐ๋์ 1 ๋ถํฐ 20๊น์ง ๋ฒํธ(๋ฆฌ์คํธํํ)๋ฅผ ๊ฐ์ด ์ถ๋ ฅํ๋ผ
|
31 |
+
6. ์ฌ๋๋ค์๊ฒ ๋๋ ค์์ ์ฃผ๋ฉด์, ๋์์ ์ ํ์ด๋ ์๋น์ค๋ฅผ ์ฌ์ฉํด์ผ๋ง ํ๋ ์ด์ ๋ฅผ ๊ฐํ๊ฒ ์ดํํ๋ผ.
|
32 |
+
7. ์ฌ์ฉํ์ง ์์ ๋์ ์ํ์ด๋ ์์ค์ ๊ฐ์กฐํ๋ผ.
|
33 |
+
8. ๊ฐ์ ์ ์ธ ๋จ์ด์ ํํ์ ์ฌ์ฉํ์ฌ ๋
์์ ๋ถ์์ ์ฆํญ์ํค๋ผ
|
34 |
+
9. ์ ํ/์๋น์ค์ ์ค์์ฑ๊ณผ ๊ทธ๊ฒ์ด ์์ ๋์ ๊ทน๋จ์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ๊ตฌ์ฒด์ ์ผ๋ก ์ค๋ช
ํ๋ผ.
|
35 |
+
์์:
|
36 |
+
- ์ค์ฟผํธ ํ ๋ ์ด ๋์ ํ๋ฉด ๋ฌด๋ฆ ๋ถ์ ์ต๋๋ค
|
37 |
+
- ์ฑ
์ ์ฝ๋ ์ฌ๋์ด ๊ฐ๋ํ ํ๋ฅ 98%์ธ ์ด์
|
38 |
+
- ์ ์ ํ์ ์ถ์ํ ๋ ์คํจํ๋ 7 ๊ฐ์ง ์น๋ช
์ ์ค์
|
39 |
+
- ์์
๋ฏธ๋์ด๋ฅผ ์๋ชป ์ฌ์ฉํ๋ฉด ๊ธฐ์
์ด ๋งํ ์ ์๋ ์ด์
|
40 |
+
- ์ด ์ฑ ์ ์ฐ๋ฉด ํดํน ๋นํ ํ๋ฅ 80% ์ฆ๊ฐ
|
41 |
+
- ์ด ๋ณดํ ์์ผ๋ฉด ์ฌ๊ณ ์ ํ์ฐ ์๊ธฐ ์จ๋ค
|
42 |
+
- ์ถฉ๊ฒฉ! ์ด ์ ํฌ๋ฆผ ์ ๋ฐ๋ฅด๋ฉด ํผ๋ถ์ ์ํ 500% ์ฆ๊ฐ
|
43 |
+
- ๊ฐ์ต๊ธฐ 2์ผ๋ง ๊ด๋ฆฌํ์ง ์์๋ "์ธ๊ท ๋ฒ๋ฒ
"
|
44 |
+
- ๋ฐ๋ค ํ๊ฒฝ์ค์ผ, ์ํ์ผ๋ก ๋๋์์จ๋ค. ํ๋ผ์คํฑ ์ฌ์ฉ ์ด๋๋ก ๊ด์ฐฎ์๊ฐ์?
|
45 |
+
- "๋งฅ์ฃผ ํ์์ด๋ผ๋ ๋ ๋ง๋ค ์ ์ด ๋ก๊ธด๋ค๋ฉด? ์ด๊ฒ ์์ฌํด์ผ"
|
46 |
+
"""
|
47 |
+
|
48 |
+
elif category == "์คํ ๋ฆฌํ
๋ง":
|
49 |
+
return """
|
50 |
+
# ์คํ ๋ฆฌํ
๋ง ์นดํผ๋ผ์ดํ
์์ฑ ๊ท์น
|
51 |
+
1. ๋ฐ๋์ ํ๊ธ๋ก ์ถ๋ ฅํ๋ผ.
|
52 |
+
2. ๋๋ ์ธ๊ณ ์ต๊ณ ์ ์คํ ๋ฆฌํ
๋ง ๋ง์ผํ
์นดํผ๋ผ์ดํฐ์ด๋ค.
|
53 |
+
3. ๋ฐ๋์ ์นดํผ๋ผ์ดํ
์ 30์ ์ด๋ด๋ก ์์ฑํ๊ณ , 20๊ฐ๋ง ์ถ๋ ฅํ๋ผ.(๋ค๋ฅธ ๋ด์ฉ ์ถ๋ ฅ ๊ธ์ง)
|
54 |
+
4. ๋ฐ๋์ ์
๋ ฅ๋ ์ฃผ์ ๋ก๋ง ์์ฑํ๊ณ ์์๋ฅผ ์ฐธ๊ณ ํ์ฌ ๋ด์ฉ์ ๋ณด๊ฐํ๋ผ.
|
55 |
+
5. ๋ฐ๋์ 1 ๋ถํฐ 20๊น์ง ๋ฒํธ(๋ฆฌ์คํธํํ)๋ฅผ ๊ฐ์ด ์ถ๋ ฅํ๋ผ
|
56 |
+
6. ์ ํ/์๋น์ค์ ๊ด๋ จ๋ ์งง๊ณ ํฅ๋ฏธ๋ก์ด ์ด์ผ๊ธฐ๋ฅผ ๋ง๋ค์ด๋ผ.
|
57 |
+
7. ๊ณ ๊ฐ์ด ๊ณต๊ฐํ ์ ์๋ ์ํฉ์ด๋ ์บ๋ฆญํฐ๋ฅผ ์ค์ ํ๋ผ.
|
58 |
+
8. ์ด์ผ๊ธฐ๋ฅผ ํตํด ์ ํ/์๋น์ค์ ๊ฐ์น๋ฅผ ์์ฐ์ค๋ฝ๊ฒ ์ ๋ฌํ๋ผ.
|
59 |
+
์์:
|
60 |
+
- ํ ์ด๋ถ์ ๊ฟ์์ ์์๋ ํ์ ์ ์ธ ๋์์ฅ๋น
|
61 |
+
- 100๋
๋ ๊ฐ์กฑ ๋ ์ํผ๋ก ๋ง๋ ์์ ์ผ
|
62 |
+
- ์ฐ์ฃผ ๋นํ์ฌ์ ์์ด๋์ด๋ก ํ์ํ ์ด๊ฒฝ๋ ์ฌํท
|
63 |
+
- ํ ๋จธ๋์ 100๋
๋ ๋น๋ฐ ๋ ์ํผ๋ก ๋ง๋ ๊ฑด๊ฐํ ์ง๋ฐฅ ๊ฐ์
|
64 |
+
- ํ๋ง๋ผ์ผ ๋ฑ์ฐ๊ฐ์ ๊ณ ๋ฏผ์์ ์์๋ ํ์ ์ ์ด๊ฒฝ๋ ๋ฐฐ๋ญ
|
65 |
+
- ๋ชฝ๊ณจ ์ ๋ชฉ๋ฏผ์ ์ฒ๋
์งํ๋ฅผ ๋ด์ ์นํ๊ฒฝ ์คํ ์บ ํ ํ
ํธ
|
66 |
+
- ๋ฐ๋ค๋ฅผ ์ฌ๋ํ ํด์์๋ฌผํ์์ ํ์ ์ ์ธ ํด์ ์ ํ ๊ธฐ์
|
67 |
+
- 20๋
๋ถ๋ฉด์ฆ ํ์๊ฐ ์ง์ ๊ฐ๋ฐํ ๊ฟ์ ์ ๋ ์ค๋งํธ ๋ฒ ๊ฐ
|
68 |
+
- ๊ธธ๊ณ ์์ด 100๋ง๋ฆฌ๋ฅผ ๊ตฌ์กฐํ ์์์ฌ์ ๋ฐ๋ ค๋๋ฌผ ๊ฑด๊ฐ๊ด๋ฆฌ ์ฑ
|
69 |
+
- ํ ์๋ ์ฅ์ ์ธ ํ๊ฐ์ ๊ฟ์ ์ด๋ค์ค ์ฒจ๋จ ๊ทธ๋ฆผ ๋๊ตฌ
|
70 |
+
"""
|
71 |
+
|
72 |
+
########################################################
|
73 |
+
# (๊ธฐ์กด ๋๊ธฐ ์ฒ๋ฆฌ ํจ์) - ๊ธฐ์กด ์ฝ๋ ์ ์ง
|
74 |
+
########################################################
|
75 |
+
def call_api_sync(content, system_message, max_tokens, temperature, top_p):
|
76 |
+
response = requests.post(
|
77 |
+
"https://api.openai.com/v1/chat/completions",
|
78 |
+
headers={"Authorization": f"Bearer {openai.api_key}"},
|
79 |
+
json={
|
80 |
+
"model": "gpt-4o-mini",
|
81 |
+
"messages": [
|
82 |
+
{"role": "system", "content": system_message},
|
83 |
+
{"role": "user", "content": content},
|
84 |
+
],
|
85 |
+
"max_tokens": max_tokens,
|
86 |
+
"temperature": temperature,
|
87 |
+
"top_p": top_p,
|
88 |
+
}
|
89 |
+
)
|
90 |
+
result = response.json()
|
91 |
+
return result['choices'][0]['message']['content']
|
92 |
+
|
93 |
+
def generate_copywriting(categories, topic):
|
94 |
+
max_tokens = 1000
|
95 |
+
temperature = 0.8
|
96 |
+
top_p = 0.95
|
97 |
+
|
98 |
+
results = {}
|
99 |
+
for category in categories:
|
100 |
+
prompt = get_category_prompt(category)
|
101 |
+
user_content = f"์ฃผ์ : {topic}"
|
102 |
+
copywriting = call_api_sync(user_content, prompt, max_tokens, temperature, top_p)
|
103 |
+
results[category] = copywriting
|
104 |
+
return results
|
105 |
+
|
106 |
+
|
107 |
+
########################################################
|
108 |
+
# (์๋ก ์ถ๊ฐ๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ ํจ์๋ค) - ๋ณ๋ ฌ ๊ตฌ์กฐ ๋ฐ ์คํธ๋ฆฌ๋ฐ
|
109 |
+
########################################################
|
110 |
+
async def call_api_async(category, content, system_message, max_tokens, temperature, top_p):
|
111 |
+
"""
|
112 |
+
๋น๋๊ธฐ๋ก OpenAI API๋ฅผ ํธ์ถํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ
|
113 |
+
"""
|
114 |
+
async with aiohttp.ClientSession() as session:
|
115 |
+
headers = {
|
116 |
+
"Authorization": f"Bearer {openai.api_key}",
|
117 |
+
"Content-Type": "application/json"
|
118 |
+
}
|
119 |
+
payload = {
|
120 |
+
"model": "gpt-4o-mini",
|
121 |
+
"messages": [
|
122 |
+
{"role": "system", "content": system_message},
|
123 |
+
{"role": "user", "content": content},
|
124 |
+
],
|
125 |
+
"max_tokens": max_tokens,
|
126 |
+
"temperature": temperature,
|
127 |
+
"top_p": top_p,
|
128 |
+
}
|
129 |
+
async with session.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload) as resp:
|
130 |
+
result = await resp.json()
|
131 |
+
copywriting = result["choices"][0]["message"]["content"]
|
132 |
+
return category, copywriting
|
133 |
+
|
134 |
+
async def generate_copywriting_async(categories, topic):
|
135 |
+
"""
|
136 |
+
๊ฐ ์นดํ
๊ณ ๋ฆฌ์ ๋ํด ๋น๋๊ธฐ๋ก API ํธ์ถ์ ์ํํ๊ณ ,
|
137 |
+
์๋ฃ๋๋ ์์๋๋ก (category, ๊ฒฐ๊ณผ) ๋ฅผ yield
|
138 |
+
"""
|
139 |
+
max_tokens = 1000
|
140 |
+
temperature = 0.8
|
141 |
+
top_p = 0.95
|
142 |
+
|
143 |
+
tasks = []
|
144 |
+
for category in categories:
|
145 |
+
prompt = get_category_prompt(category)
|
146 |
+
user_content = f"์ฃผ์ : {topic}"
|
147 |
+
tasks.append(
|
148 |
+
asyncio.create_task(
|
149 |
+
call_api_async(category, user_content, prompt, max_tokens, temperature, top_p)
|
150 |
+
)
|
151 |
+
)
|
152 |
+
|
153 |
+
# ์๋ฃ๋ ํ์คํฌ๋ถํฐ ์์๋๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ
|
154 |
+
for task in asyncio.as_completed(tasks):
|
155 |
+
cat, copywriting = await task
|
156 |
+
yield cat, copywriting
|
157 |
+
|
158 |
+
|
159 |
+
########################################################
|
160 |
+
# Gradio ์ธํฐํ์ด์ค
|
161 |
+
########################################################
|
162 |
+
with gr.Blocks() as iface:
|
163 |
+
gr.Markdown("# AI ์นดํผ๋ผ์ดํ
์์ฑ๊ธฐ")
|
164 |
+
|
165 |
+
with gr.Column():
|
166 |
+
topic = gr.Textbox(lines=1, label="์ฃผ์ ๋ฅผ ์
๋ ฅํ์ธ์")
|
167 |
+
|
168 |
+
generate_btn = gr.Button("์นดํผ๋ผ์ดํ
์์ฑํ๊ธฐ")
|
169 |
+
status = gr.Markdown("์ค๋น๋จ")
|
170 |
+
|
171 |
+
output_boxes = {}
|
172 |
+
with gr.Column():
|
173 |
+
for category in CATEGORIES:
|
174 |
+
output_box = gr.Textbox(label=category, visible=True)
|
175 |
+
output_boxes[category] = output_box
|
176 |
+
|
177 |
+
########################################################
|
178 |
+
# ๊ธฐ์กด validate_and_generate ํจ์ ์ด๋ฆ/์ถ๋ ฅ ํ์ ์ ์ง
|
179 |
+
# โ ๋ด๋ถ์์ ๋น๋๊ธฐ ์ฒ๋ฆฌ + Gradio ์คํธ๋ฆฌ๋ฐ(yield) ์ฌ์ฉ
|
180 |
+
########################################################
|
181 |
+
def validate_and_generate(topic):
|
182 |
+
async def async_gen():
|
183 |
+
try:
|
184 |
+
# ๋น๋๊ธฐ๋ก ์นดํผ๋ผ์ดํ
์์ฑ
|
185 |
+
results = {}
|
186 |
+
|
187 |
+
# ์์ฐจ๊ฐ ์๋, '์๋ฃ๋ ์์๋๋ก' ๊ฒฐ๊ณผ๋ฅผ yield
|
188 |
+
async for cat, copywriting in generate_copywriting_async(CATEGORIES, topic):
|
189 |
+
results[cat] = copywriting
|
190 |
+
# ํ์ฌ๊น์ง ์๋ฃ๋ ๋ด์ฉ๋ง ์
๋ฐ์ดํธ
|
191 |
+
yield [
|
192 |
+
gr.update(value=f"์งํ์ค: [{cat}] ์นดํผ๋ผ์ดํ
์๋ฃ"),
|
193 |
+
*[gr.update(value=results.get(c, "")) for c in CATEGORIES]
|
194 |
+
]
|
195 |
+
|
196 |
+
# ๋ชจ๋ ์นดํ
๊ณ ๋ฆฌ ์๋ฃ ํ ์ต์ข
๋ฉ์ธ์ง
|
197 |
+
yield [
|
198 |
+
gr.update(value="์นดํผ๋ผ์ดํ
์์ฑ์ด ์๋ฃ๋์์ต๋๋ค."),
|
199 |
+
*[gr.update(value=results.get(c, "")) for c in CATEGORIES]
|
200 |
+
]
|
201 |
+
|
202 |
+
except Exception as e:
|
203 |
+
logger.error(f"Error during copywriting generation: {str(e)}")
|
204 |
+
yield [
|
205 |
+
gr.update(value=f"์ค๋ฅ ๋ฐ์: {str(e)}"),
|
206 |
+
*[gr.update(value="") for _ in CATEGORIES]
|
207 |
+
]
|
208 |
+
|
209 |
+
# Gradio์์ ์คํธ๋ฆฌ๋ฐ ์ถ๋ ฅ์ ์ํด async_gen ํจ์ ์์ฒด๋ฅผ ๋ฐํ
|
210 |
+
return async_gen()
|
211 |
+
|
212 |
+
generate_btn.click(
|
213 |
+
fn=validate_and_generate,
|
214 |
+
inputs=[topic],
|
215 |
+
outputs=[status] + [output_boxes[category] for category in CATEGORIES]
|
216 |
+
)
|
217 |
+
|
218 |
+
# ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ํด queue()๋ฅผ ์ฌ์ฉ(Gradio 3.30+ ๊ธฐ์ค)
|
219 |
+
iface.queue()
|
220 |
+
iface.launch()
|