7-4_copy / app.py
Kims12's picture
Update app.py
fc4aa21 verified
import os
import gradio as gr
import openai
import requests
import logging
import asyncio
import aiohttp
# ๋กœ๊น… ์„ค์ •
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# OpenAI API ํด๋ผ์ด์–ธํŠธ ์„ค์ •
openai.api_key = os.getenv("OPENAI_API_KEY")
# ์š”์ฒญ์‚ฌํ•ญ์— ๋”ฐ๋ผ ์นดํ…Œ๊ณ ๋ฆฌ ์ˆ˜์ •
CATEGORIES = [
"๊ณตํฌ ๋งˆ์ผ€ํŒ…",
"์Šคํ† ๋ฆฌํ…”๋ง"
]
def get_category_prompt(category):
if category == "๊ณตํฌ ๋งˆ์ผ€ํŒ…":
return """
# ๊ณตํฌ ๋งˆ์ผ€ํŒ… ์นดํ”ผ๋ผ์ดํŒ… ์ƒ์„ฑ ๊ทœ์น™
1. ๋ฐ˜๋“œ์‹œ ํ•œ๊ธ€๋กœ ์ถœ๋ ฅํ•˜๋ผ.
2. ๋„ˆ๋Š” ์„ธ๊ณ„ ์ตœ๊ณ ์˜ ๊ณตํฌ ๋งˆ์ผ€ํŒ… ์นดํ”ผ๋ผ์ดํ„ฐ์ด๋‹ค.
3. ๋ฐ˜๋“œ์‹œ ์นดํ”ผ๋ผ์ดํŒ…์€ 30์ž ์ด๋‚ด๋กœ ์ž‘์„ฑํ•˜๊ณ , 20๊ฐœ๋งŒ ์ถœ๋ ฅํ•˜๋ผ.(๋‹ค๋ฅธ ๋‚ด์šฉ ์ถœ๋ ฅ ๊ธˆ์ง€)
4. ๋ฐ˜๋“œ์‹œ ์ž…๋ ฅ๋œ ์ฃผ์ œ๋กœ๋งŒ ์ž‘์„ฑํ•˜๊ณ  ์˜ˆ์‹œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๋‚ด์šฉ์„ ๋ณด๊ฐ•ํ•˜๋ผ.
5. ๋ฐ˜๋“œ์‹œ 1 ๋ถ€ํ„ฐ 20๊นŒ์ง€ ๋ฒˆํ˜ธ(๋ฆฌ์ŠคํŠธํ˜•ํƒœ)๋ฅผ ๊ฐ™์ด ์ถœ๋ ฅํ•˜๋ผ
6. ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ๋‘๋ ค์›€์„ ์ฃผ๋ฉด์„œ, ๋™์‹œ์— ์ œํ’ˆ์ด๋‚˜ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ๋งŒ ํ•˜๋Š” ์ด์œ ๋ฅผ ๊ฐ•ํ•˜๊ฒŒ ์–ดํ•„ํ•˜๋ผ.
7. ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๋•Œ์˜ ์œ„ํ—˜์ด๋‚˜ ์†์‹ค์„ ๊ฐ•์กฐํ•˜๋ผ.
8. ๊ฐ์ •์ ์ธ ๋‹จ์–ด์™€ ํ‘œํ˜„์„ ์‚ฌ์šฉํ•˜์—ฌ ๋…์ž์˜ ๋ถˆ์•ˆ์„ ์ฆํญ์‹œํ‚ค๋ผ
9. ์ œํ’ˆ/์„œ๋น„์Šค์˜ ์ค‘์š”์„ฑ๊ณผ ๊ทธ๊ฒƒ์ด ์—†์„ ๋•Œ์˜ ๊ทน๋‹จ์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๋ช…ํ•˜๋ผ.
์˜ˆ์‹œ:
- ์Šค์ฟผํŠธ ํ•  ๋•Œ ์ด ๋™์ž‘ ํ•˜๋ฉด ๋ฌด๋ฆŽ ๋ถ€์ƒ ์˜ต๋‹ˆ๋‹ค
- ์ฑ… ์•ˆ ์ฝ๋Š” ์‚ฌ๋žŒ์ด ๊ฐ€๋‚œํ•  ํ™•๋ฅ  98%์ธ ์ด์œ 
- ์‹ ์ œํ’ˆ์„ ์ถœ์‹œํ•  ๋•Œ ์‹คํŒจํ•˜๋Š” 7 ๊ฐ€์ง€ ์น˜๋ช…์  ์‹ค์ˆ˜
- ์†Œ์…œ ๋ฏธ๋””์–ด๋ฅผ ์ž˜๋ชป ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ์—…์ด ๋งํ•  ์ˆ˜ ์žˆ๋Š” ์ด์œ 
- ์ด ์•ฑ ์•ˆ ์“ฐ๋ฉด ํ•ดํ‚น ๋‹นํ•  ํ™•๋ฅ  80% ์ฆ๊ฐ€
- ์ด ๋ณดํ—˜ ์—†์œผ๋ฉด ์‚ฌ๊ณ  ์‹œ ํŒŒ์‚ฐ ์œ„๊ธฐ ์˜จ๋‹ค
- ์ถฉ๊ฒฉ! ์ด ์„ ํฌ๋ฆผ ์•ˆ ๋ฐ”๋ฅด๋ฉด ํ”ผ๋ถ€์•” ์œ„ํ—˜ 500% ์ฆ๊ฐ€
- ๊ฐ€์Šต๊ธฐ 2์ผ๋งŒ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์•„๋„ "์„ธ๊ท  ๋ฒ”๋ฒ…"
- ๋ฐ”๋‹ค ํ™˜๊ฒฝ์˜ค์—ผ, ์‹ํƒ์œผ๋กœ ๋˜๋Œ์•„์˜จ๋‹ค. ํ”Œ๋ผ์Šคํ‹ฑ ์‚ฌ์šฉ ์ด๋Œ€๋กœ ๊ดœ์ฐฎ์€๊ฐ€์š”?
- "๋งฅ์ฃผ ํ•œ์ž”์ด๋ผ๋„ ๋‚ ๋งˆ๋‹ค ์ˆ ์ด ๋•ก๊ธด๋‹ค๋ฉด? ์ด๊ฒƒ ์˜์‹ฌํ•ด์•ผ"
"""
elif category == "์Šคํ† ๋ฆฌํ…”๋ง":
return """
# ์Šคํ† ๋ฆฌํ…”๋ง ์นดํ”ผ๋ผ์ดํŒ… ์ƒ์„ฑ ๊ทœ์น™
1. ๋ฐ˜๋“œ์‹œ ํ•œ๊ธ€๋กœ ์ถœ๋ ฅํ•˜๋ผ.
2. ๋„ˆ๋Š” ์„ธ๊ณ„ ์ตœ๊ณ ์˜ ์Šคํ† ๋ฆฌํ…”๋ง ๋งˆ์ผ€ํŒ… ์นดํ”ผ๋ผ์ดํ„ฐ์ด๋‹ค.
3. ๋ฐ˜๋“œ์‹œ ์นดํ”ผ๋ผ์ดํŒ…์€ 30์ž ์ด๋‚ด๋กœ ์ž‘์„ฑํ•˜๊ณ , 20๊ฐœ๋งŒ ์ถœ๋ ฅํ•˜๋ผ.(๋‹ค๋ฅธ ๋‚ด์šฉ ์ถœ๋ ฅ ๊ธˆ์ง€)
4. ๋ฐ˜๋“œ์‹œ ์ž…๋ ฅ๋œ ์ฃผ์ œ๋กœ๋งŒ ์ž‘์„ฑํ•˜๊ณ  ์˜ˆ์‹œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๋‚ด์šฉ์„ ๋ณด๊ฐ•ํ•˜๋ผ.
5. ๋ฐ˜๋“œ์‹œ 1 ๋ถ€ํ„ฐ 20๊นŒ์ง€ ๋ฒˆํ˜ธ(๋ฆฌ์ŠคํŠธํ˜•ํƒœ)๋ฅผ ๊ฐ™์ด ์ถœ๋ ฅํ•˜๋ผ
6. ์ œํ’ˆ/์„œ๋น„์Šค์™€ ๊ด€๋ จ๋œ ์งง๊ณ  ํฅ๋ฏธ๋กœ์šด ์ด์•ผ๊ธฐ๋ฅผ ๋งŒ๋“ค์–ด๋ผ.
7. ๊ณ ๊ฐ์ด ๊ณต๊ฐํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์ด๋‚˜ ์บ๋ฆญํ„ฐ๋ฅผ ์„ค์ •ํ•˜๋ผ.
8. ์ด์•ผ๊ธฐ๋ฅผ ํ†ตํ•ด ์ œํ’ˆ/์„œ๋น„์Šค์˜ ๊ฐ€์น˜๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ „๋‹ฌํ•˜๋ผ.
์˜ˆ์‹œ:
- ํ•œ ์–ด๋ถ€์˜ ๊ฟˆ์—์„œ ์‹œ์ž‘๋œ ํ˜์‹ ์ ์ธ ๋‚š์‹œ์žฅ๋น„
- 100๋…„ ๋œ ๊ฐ€์กฑ ๋ ˆ์‹œํ”ผ๋กœ ๋งŒ๋“  ์ˆ˜์ œ ์žผ
- ์šฐ์ฃผ ๋น„ํ–‰์‚ฌ์˜ ์•„์ด๋””์–ด๋กœ ํƒ„์ƒํ•œ ์ดˆ๊ฒฝ๋Ÿ‰ ์žฌํ‚ท
- ํ• ๋จธ๋‹ˆ์˜ 100๋…„ ๋œ ๋น„๋ฐ€ ๋ ˆ์‹œํ”ผ๋กœ ๋งŒ๋“  ๊ฑด๊ฐ•ํ•œ ์ง‘๋ฐฅ ๊ฐ„์‹
- ํžˆ๋ง๋ผ์•ผ ๋“ฑ์‚ฐ๊ฐ์˜ ๊ณ ๋ฏผ์—์„œ ์‹œ์ž‘๋œ ํ˜์‹ ์  ์ดˆ๊ฒฝ๋Ÿ‰ ๋ฐฐ๋‚ญ
- ๋ชฝ๊ณจ ์œ ๋ชฉ๋ฏผ์˜ ์ฒœ๋…„ ์ง€ํ˜œ๋ฅผ ๋‹ด์€ ์นœํ™˜๊ฒฝ ์˜คํ† ์บ ํ•‘ ํ…ํŠธ
- ๋ฐ”๋‹ค๋ฅผ ์‚ฌ๋ž‘ํ•œ ํ•ด์–‘์ƒ๋ฌผํ•™์ž์˜ ํ˜์‹ ์ ์ธ ํ•ด์–‘ ์ •ํ™” ๊ธฐ์ˆ 
- 20๋…„ ๋ถˆ๋ฉด์ฆ ํ™˜์ž๊ฐ€ ์ง์ ‘ ๊ฐœ๋ฐœํ•œ ๊ฟ€์ž  ์œ ๋„ ์Šค๋งˆํŠธ ๋ฒ ๊ฐœ
- ๊ธธ๊ณ ์–‘์ด 100๋งˆ๋ฆฌ๋ฅผ ๊ตฌ์กฐํ•œ ์ˆ˜์˜์‚ฌ์˜ ๋ฐ˜๋ ค๋™๋ฌผ ๊ฑด๊ฐ•๊ด€๋ฆฌ ์•ฑ
- ํŒ” ์—†๋Š” ์žฅ์• ์ธ ํ™”๊ฐ€์˜ ๊ฟˆ์„ ์ด๋ค„์ค€ ์ฒจ๋‹จ ๊ทธ๋ฆผ ๋„๊ตฌ
"""
########################################################
# (๊ธฐ์กด ๋™๊ธฐ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜) - ๊ธฐ์กด ์ฝ”๋“œ ์œ ์ง€
########################################################
def call_api_sync(content, system_message, max_tokens, temperature, top_p):
response = requests.post(
"https://api.openai.com/v1/chat/completions",
headers={"Authorization": f"Bearer {openai.api_key}"},
json={
"model": "gpt-4o-mini",
"messages": [
{"role": "system", "content": system_message},
{"role": "user", "content": content},
],
"max_tokens": max_tokens,
"temperature": temperature,
"top_p": top_p,
}
)
result = response.json()
return result['choices'][0]['message']['content']
def generate_copywriting(categories, topic):
max_tokens = 1000
temperature = 0.8
top_p = 0.95
results = {}
for category in categories:
prompt = get_category_prompt(category)
user_content = f"์ฃผ์ œ: {topic}"
copywriting = call_api_sync(user_content, prompt, max_tokens, temperature, top_p)
results[category] = copywriting
return results
########################################################
# (์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜๋“ค) - ๋ณ‘๋ ฌ ๊ตฌ์กฐ ๋ฐ ์ŠคํŠธ๋ฆฌ๋ฐ
########################################################
async def call_api_async(category, content, system_message, max_tokens, temperature, top_p):
"""
๋น„๋™๊ธฐ๋กœ OpenAI API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜
"""
async with aiohttp.ClientSession() as session:
headers = {
"Authorization": f"Bearer {openai.api_key}",
"Content-Type": "application/json"
}
payload = {
"model": "gpt-4o-mini",
"messages": [
{"role": "system", "content": system_message},
{"role": "user", "content": content},
],
"max_tokens": max_tokens,
"temperature": temperature,
"top_p": top_p,
}
async with session.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload) as resp:
result = await resp.json()
copywriting = result["choices"][0]["message"]["content"]
return category, copywriting
async def generate_copywriting_async(categories, topic):
"""
๊ฐ ์นดํ…Œ๊ณ ๋ฆฌ์— ๋Œ€ํ•ด ๋น„๋™๊ธฐ๋กœ API ํ˜ธ์ถœ์„ ์ˆ˜ํ–‰ํ•˜๊ณ ,
์™„๋ฃŒ๋˜๋Š” ์ˆœ์„œ๋Œ€๋กœ (category, ๊ฒฐ๊ณผ) ๋ฅผ yield
"""
max_tokens = 1000
temperature = 0.8
top_p = 0.95
tasks = []
for category in categories:
prompt = get_category_prompt(category)
user_content = f"์ฃผ์ œ: {topic}"
tasks.append(
asyncio.create_task(
call_api_async(category, user_content, prompt, max_tokens, temperature, top_p)
)
)
# ์™„๋ฃŒ๋œ ํƒœ์Šคํฌ๋ถ€ํ„ฐ ์ˆœ์„œ๋Œ€๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜
for task in asyncio.as_completed(tasks):
cat, copywriting = await task
yield cat, copywriting
########################################################
# Gradio ์ธํ„ฐํŽ˜์ด์Šค
########################################################
with gr.Blocks() as iface:
gr.Markdown("# AI ์นดํ”ผ๋ผ์ดํŒ… ์ƒ์„ฑ๊ธฐ")
with gr.Column():
topic = gr.Textbox(lines=1, label="์ฃผ์ œ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”")
generate_btn = gr.Button("์นดํ”ผ๋ผ์ดํŒ… ์ƒ์„ฑํ•˜๊ธฐ")
status = gr.Markdown("์ค€๋น„๋จ")
output_boxes = {}
with gr.Column():
for category in CATEGORIES:
output_box = gr.Textbox(label=category, visible=True)
output_boxes[category] = output_box
########################################################
# (์ˆ˜์ •) validate_and_generate ํ•จ์ˆ˜๋ฅผ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋กœ ๋ฐ”๊ฟ”
# 3๊ฐœ์˜ ์ถœ๋ ฅ์— ๋งž๊ฒŒ ๋งค yield๋งˆ๋‹ค [status, box1, box2]๋ฅผ ๋ฐ˜ํ™˜
########################################################
async def validate_and_generate(topic):
try:
results = {}
# ์™„๋ฃŒ๋œ ์ˆœ์„œ๋Œ€๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ
async for cat, copywriting in generate_copywriting_async(CATEGORIES, topic):
results[cat] = copywriting
# ํ˜„์žฌ๊นŒ์ง€ ์™„๋ฃŒ๋œ copywriting์„ ๋ฐ˜์˜ํ•ด์„œ ์ถœ๋ ฅ
yield [
gr.update(value=f"์ง„ํ–‰์ค‘: [{cat}] ์นดํ”ผ๋ผ์ดํŒ… ์™„๋ฃŒ"),
*[gr.update(value=results.get(c, "")) for c in CATEGORIES]
]
# ๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋œ ๋’ค ์ตœ์ข… ์ถœ๋ ฅ
yield [
gr.update(value="์นดํ”ผ๋ผ์ดํŒ… ์ƒ์„ฑ์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."),
*[gr.update(value=results.get(c, "")) for c in CATEGORIES]
]
except Exception as e:
logger.error(f"Error during copywriting generation: {str(e)}")
yield [
gr.update(value=f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"),
*[gr.update(value="") for _ in CATEGORIES]
]
# ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ Gradio ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋กœ ๋“ฑ๋ก
generate_btn.click(
fn=validate_and_generate,
inputs=[topic],
outputs=[status] + [output_boxes[category] for category in CATEGORIES]
)
# Gradio 3.30+๋ถ€ํ„ฐ๋Š” queue() + async ํ•จ์ˆ˜๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ ๊ฐ€๋Šฅ
iface.queue()
iface.launch()