Spaces:
Sleeping
Sleeping
import os | |
import random | |
import time | |
import re | |
import json | |
import requests | |
from bs4 import BeautifulSoup | |
from requests.adapters import HTTPAdapter | |
from requests.packages.urllib3.util.retry import Retry | |
import openai | |
import gradio as gr | |
from fpdf import FPDF as FPDF2 | |
from datetime import datetime | |
from zoneinfo import ZoneInfo | |
import sys | |
import logging | |
# API ํค ์ค์ | |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") | |
# OpenAI ์ค์ | |
openai.api_key = OPENAI_API_KEY | |
def setup_session(): | |
try: | |
session = requests.Session() | |
retries = Retry(total=5, backoff_factor=1, status_forcelist=[502, 503, 504]) | |
session.mount('https://', HTTPAdapter(max_retries=retries)) | |
return session | |
except Exception as e: | |
return None | |
def generate_naver_search_url(query): | |
base_url = "https://search.naver.com/search.naver?" | |
params = {"ssc": "tab.blog.all", "sm": "tab_jum", "query": query} | |
url = base_url + "&".join(f"{key}={value}" for key, value in params.items()) | |
return url | |
def crawl_blog_content(url, session): | |
try: | |
headers = { | |
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", | |
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", | |
"Accept-Language": "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7", | |
"Accept-Encoding": "gzip, deflate, br", | |
"Connection": "keep-alive", | |
"Referer": "https://search.naver.com/search.naver", | |
} | |
delay = random.uniform(1, 2) | |
time.sleep(delay) | |
response = session.get(url, headers=headers) | |
if response.status_code != 200: | |
return "" | |
soup = BeautifulSoup(response.content, "html.parser") | |
content = soup.find("div", attrs={'class': 'se-main-container'}) | |
if content: | |
return clean_text(content.get_text()) | |
else: | |
return "" | |
except Exception as e: | |
return "" | |
def crawl_naver_search_results(url, session): | |
try: | |
headers = { | |
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", | |
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", | |
"Accept-Language": "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7", | |
"Accept-Encoding": "gzip, deflate, br", | |
"Connection": "keep-alive", | |
"Referer": "https://search.naver.com/search.naver", | |
} | |
response = session.get(url, headers=headers) | |
if response.status_code != 200: | |
return [] | |
soup = BeautifulSoup(response.content, "html.parser") | |
results = [] | |
count = 0 | |
for li in soup.find_all("li", class_=re.compile("bx.*")): | |
if count >= 10: | |
break | |
for div in li.find_all("div", class_="detail_box"): | |
for div2 in div.find_all("div", class_="title_area"): | |
title = div2.text.strip() | |
for a in div2.find_all("a", href=True): | |
link = a["href"] | |
if "blog.naver" in link: | |
link = link.replace("https://", "https://m.") | |
results.append({"์ ๋ชฉ": title, "๋งํฌ": link}) | |
count += 1 | |
if count >= 10: | |
break | |
if count >= 10: | |
break | |
if count >= 10: | |
break | |
return results | |
except Exception as e: | |
return [] | |
def clean_text(text): | |
text = re.sub(r'\s+', ' ', text).strip() | |
return text | |
def fetch_references(topic): | |
search_url = generate_naver_search_url(topic) | |
session = setup_session() | |
if session is None: | |
return ["์ธ์ ์ค์ ์คํจ"] * 3 | |
results = crawl_naver_search_results(search_url, session) | |
if len(results) < 3: | |
return ["์ถฉ๋ถํ ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค."] * 3 | |
selected_results = random.sample(results, 3) | |
references = [] | |
for result in selected_results: | |
content = crawl_blog_content(result['๋งํฌ'], session) | |
references.append(f"์ ๋ชฉ: {result['์ ๋ชฉ']}\n๋ด์ฉ: {content}") | |
return references | |
def fetch_crawl_results(query): | |
references = fetch_references(query) | |
return references[0], references[1], references[2] | |
def get_style_prompt(style="์น๊ทผํ"): | |
prompts = { | |
"์น๊ทผํ": """ | |
[์น๊ทผํ ํฌ์คํ ์คํ์ผ ๊ฐ์ด๋] | |
1. ํค๊ณผ ์ด์กฐ | |
- ๋ํํ๋ฏ ํธ์ํ๊ณ ์น๊ทผํ ๋งํฌ ์ฌ์ฉ | |
2. ๋ฌธ์ฅ ๋ฐ ์ดํฌ | |
- ๋ฐ๋์ 'ํด์์ฒด'๋ก ์์ฑ, ์ ๋ '์ต๋๋ค'์ฒด๋ฅผ ์ฌ์ฉํ์ง ๋ง ๊ฒ. | |
- '~์'๋ก ๋๋๋๋ก ์์ฑ, '~๋ค'๋ก ๋๋์ง ์๊ฒ ํ๋ผ | |
- ๊ตฌ์ด์ฒด ํํ ์ฌ์ฉ (์: "~ํ์ด์", "~์ธ ๊ฒ ๊ฐ์์") | |
3. ์ฉ์ด ๋ฐ ์ค๋ช ๋ฐฉ์ | |
- ์ ๋ฌธ ์ฉ์ด ๋์ ์ฌ์ด ๋จ์ด๋ก ํ์ด์ ์ค๋ช | |
- ๋น์ ๋ ์์ ๋ฅผ ํ์ฉํ์ฌ ๋ณต์กํ ๊ฐ๋ ์ค๋ช | |
- ์์ฌ์๋ฌธ๋ฌธ ํ์ฉํ์ฌ ๋ ์์ ์ํตํ๋ ๋๋ ์ฃผ๊ธฐ | |
4. ๋ ์์์ ์ํธ์์ฉ | |
- ๋ ์์ ์๊ฒฌ์ ๋ฌผ์ด๋ณด๋ ์ง๋ฌธ ํฌํจ | |
- ๋๊ธ ๋ฌ๊ธฐ๋ฅผ ๋ ๋ คํ๋ ๋ฌธ๊ตฌ ์ฌ์ฉ | |
์ฃผ์์ฌํญ: ๋๋ฌด ๊ฐ๋ฒผ์ด ํค์ ์ง์ํ๊ณ , ์ฃผ์ ์ ์ค์์ฑ์ ํด์น์ง ์๋ ์ ์์ ์น๊ทผํจ ์ ์ง | |
(์์: ์๋๋ค~ ์ค๋ ์ค ์ฝ์นด์ฝ๋ผ๋ง์ด์๋ก ์ถ์๊ฐ ๋๋ค๋๊ฑฐ ์๊ณ ๊ณ์ จ๋์?!ใ ์ค๋ ์ค ์ฝ์นด์ฝ๋ผ๋ง์ ์ด๋ค์ง ์์งํ๊ณผ๊ตฌ๋งค์ ๋ณด, ๊ฐ๊ฒฉ, ์นผ๋ก๋ฆฌ ๋ฑ์ ๋ํด ์์ธ~ ํ ์ ์ด๋ณด๋๋ก ํ ๊ป์! ์ค๋ ์ค๋ฅผ ์ข์ํ๋ ์๋ค์๊ฒ๊ฐ์์ผ๋ก ์ค๋ ์ค ์ฝ์นด์ฝ๋ผ๋ง์ ์คฌ๋๋๋ง์๋ค๊ณ ์ข์ํ๋๋ผ๊ตฌ์. ์ฝ๋ผํฅ์ด ๋์ ๋ ๋ง์์ ๋ ๋ค๋ฉฐใ ๊ฐ์ธ์ ์ผ๋ก๋ ๋ณ โญ๏ธโญ๏ธโญ๏ธ.์๊ฑด ๊ฐ์ธ์ฐจ๊ฐ ์์๊ฑฐ ๊ฐ์์~) | |
""", | |
"์ผ๋ฐ": """ | |
#์ผ๋ฐ์ ์ธ ๋ธ๋ก๊ทธ ํฌ์คํ ์คํ์ผ ๊ฐ์ด๋ | |
1. ํค๊ณผ ์ด์กฐ | |
- ์ค๋ฆฝ์ ์ด๊ณ ๊ฐ๊ด์ ์ธ ํค ์ ์ง | |
- ์ ์ ํ ์กด๋๋ง ์ฌ์ฉ (์: "~ํฉ๋๋ค", "~์ ๋๋ค") | |
2. ๋ด์ฉ ๊ตฌ์กฐ ๋ฐ ์ ๊ฐ | |
- ๋ช ํํ ์ฃผ์ ์ ์๋ก ์์ | |
- ๋ ผ๋ฆฌ์ ์ธ ์์๋ก ์ ๋ณด ์ ๊ฐ | |
- ์ฃผ์ ํฌ์ธํธ๋ฅผ ๊ฐ์กฐํ๋ ์์ ๋ชฉ ํ์ฉ | |
- ์ ์ ํ ๊ธธ์ด์ ๋จ๋ฝ์ผ๋ก ๊ตฌ์ฑ | |
3. ์ฉ์ด ๋ฐ ์ค๋ช ๋ฐฉ์ | |
- ์ผ๋ฐ์ ์ผ๋ก ์ดํดํ๊ธฐ ์ฌ์ด ์ฉ์ด ์ ํ | |
- ํ์์ ๊ฐ๋จํ ์ค๋ช ์ถ๊ฐ | |
- ๊ฐ๊ด์ ์ธ ์ ๋ณด ์ ๊ณต์ ์ค์ | |
4. ํ ์คํธ ๊ตฌ์กฐํ | |
- ๋ถ๋ฆฟ ํฌ์ธํธ๋ ๋ฒํธ ๋งค๊ธฐ๊ธฐ๋ฅผ ํ์ฉํ์ฌ ์ ๋ณด ๊ตฌ์กฐํ | |
- ์ค์ํ ์ ๋ณด๋ ๊ตต์ ๊ธ์จ๋ ๊ธฐ์ธ์๊ผด๋ก ๊ฐ์กฐ | |
5. ๋ ์ ์ํธ์์ฉ | |
- ์ ์ ํ ๋ ์์ ์๊ฐ์ ๋ฌป๋ ์ง๋ฌธ ํฌํจ | |
- ์ถ๊ฐ ์ ๋ณด๋ฅผ ์ฐพ์ ์ ์๋ ํค์๋ ์ ์ | |
6. ๋ง๋ฌด๋ฆฌ | |
- ์ฃผ์ ๋ด์ฉ ๊ฐ๋จํ ์์ฝ | |
- ์ถ๊ฐ ์ ๋ณด์ ๋ํ ์๋ด ์ ๊ณต | |
์ฃผ์์ฌํญ: ๋๋ฌด ๋ฑ๋ฑํ๊ฑฐ๋ ์ง๋ฃจํ์ง ์๋๋ก ๊ท ํ ์ ์ง | |
""", | |
"์ ๋ฌธ์ ์ธ": """ | |
#์ ๋ฌธ์ ์ธ ๋ธ๋ก๊ทธ ํฌ์คํ ์คํ์ผ ๊ฐ์ด๋ | |
1. ํค๊ณผ ๊ตฌ์กฐ | |
- ๊ณต์์ ์ด๊ณ ํ์ ์ ์ธ ํค ์ฌ์ฉ | |
- ๊ฐ๊ด์ ์ด๊ณ ๋ถ์์ ์ธ ์ ๊ทผ ์ ์ง | |
- ๋ช ํํ ์๋ก , ๋ณธ๋ก , ๊ฒฐ๋ก ๊ตฌ์กฐ | |
- ์ฒด๊ณ์ ์ธ ๋ ผ์ ์ ๊ฐ | |
- ์ธ๋ถ ์น์ ์ ์ํ ๋ช ํํ ์์ ๋ชฉ ์ฌ์ฉ | |
2. ๋ด์ฉ ๊ตฌ์ฑ ๋ฐ ์ ๊ฐ | |
- ๋ณต์กํ ๊ฐ๋ ์ ์ ํํ ์ ๋ฌํ ์ ์๋ ๋ฌธ์ฅ ๊ตฌ์กฐ ์ฌ์ฉ | |
- ๋ ผ๋ฆฌ์ ์ฐ๊ฒฐ์ ์ํ ์ ํ์ด ํ์ฉ | |
- ํด๋น ๋ถ์ผ์ ์ ๋ฌธ ์ฉ์ด ์ ๊ทน ํ์ฉ (ํ์์ ๊ฐ๋ตํ ์ค๋ช ์ ๊ณต) | |
- ์ฌ์ธต์ ์ธ ๋ถ์๊ณผ ๋นํ์ ์ฌ๊ณ ์ ๊ฐ | |
- ๋ค์ํ ๊ด์ ์ ์ ๋ฐ ๋น๊ต | |
3. ๋ฐ์ดํฐ ๋ฐ ๊ทผ๊ฑฐ ํ์ฉ | |
- ํต๊ณ, ์ฐ๊ตฌ ๊ฒฐ๊ณผ, ์ ๋ฌธ๊ฐ ์๊ฒฌ ๋ฑ ์ ๋ขฐํ ์ ์๋ ์ถ์ฒ ์ธ์ฉ | |
- ํ์์ ๊ฐ์ฃผ๋ ์ฐธ๊ณ ๋ฌธํ ๋ชฉ๋ก ํฌํจ | |
- ์์น ๋ฐ์ดํฐ๋ ํ ์คํธ๋ก ๋ช ํํ ์ค๋ช | |
4. ํ ์คํธ ๊ตฌ์กฐํ | |
- ๋ ผ๋ฆฌ์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์กฐํ๊ธฐ ์ํด ๋ฒํธ ๋งค๊ธฐ๊ธฐ ์ฌ์ฉ | |
- ํต์ฌ ๊ฐ๋ ์ด๋ ์ฉ์ด๋ ๊ธฐ์ธ์๊ผด๋ก ๊ฐ์กฐ | |
- ๊ธด ์ธ์ฉ๋ฌธ์ ๋ค์ฌ์ฐ๊ธฐ๋ก ๊ตฌ๋ถ | |
5. ๋ง๋ฌด๋ฆฌ | |
- ํต์ฌ ๋ ผ์ ์ฌ๊ฐ์กฐ | |
- ํฅํ ์ฐ๊ตฌ ๋ฐฉํฅ์ด๋ ์ค๋ฌด์ ํจ์ ์ ์ | |
์ฃผ์์ฌํญ: ์ ๋ฌธ์ฑ์ ์ ์งํ๋, ์์ ํ ์ดํดํ๊ธฐ ์ด๋ ค์ด ์์ค์ ์ง์ | |
""" | |
} | |
return prompts.get(style, prompts["์น๊ทผํ"]) | |
def remove_unwanted_phrases(text): | |
unwanted_phrases = [ | |
'์ฌ๋ฌ๋ถ', '์ต๊ทผ', '๋ง์ง๋ง์ผ๋ก', '๊ฒฐ๋ก ์ ์ผ๋ก', '๊ฒฐ๊ตญ', | |
'์ข ํฉ์ ์ผ๋ก', '๋ฐ๋ผ์', '๋ง๋ฌด๋ฆฌ', '๋์ผ๋ก', '์์ฝ' | |
] | |
words = re.findall(r'\S+|\n', text) | |
result_words = [word for word in words if not any(phrase in word for phrase in unwanted_phrases)] | |
return ' '.join(result_words).replace(' \n ', '\n').replace(' \n', '\n').replace('\n ', '\n') | |
def generate_blog_post(query, prompt_template, style="์น๊ทผํ"): | |
try: | |
# ๋ชฉํ ๊ธ์์ ์ค์ (๋ฌธ์ ์) | |
target_char_length = 3000 | |
max_attempts = 2 # ์ต๋ ์๋ ํ์ | |
# ์ฐธ๊ณ ๊ธ ๊ฐ์ ธ์ค๊ธฐ | |
references = fetch_references(query) | |
ref1, ref2, ref3 = references | |
# OpenAI API ์ค์ | |
model_name = "gpt-4o-mini" | |
temperature = 0.85 | |
max_tokens = 15000 | |
top_p = 0.9 | |
frequency_penalty = 0.5 | |
presence_penalty = 0.3 | |
# ์คํ์ผ ํ๋กฌํํธ ๊ฐ์ ธ์ค๊ธฐ | |
style_prompt = get_style_prompt(style) | |
# ์ด๊ธฐ ํ๋กฌํํธ ๊ตฌ์ฑ | |
initial_prompt = f""" | |
{prompt_template} | |
{style_prompt} | |
์ฃผ์ : {query} | |
์ฐธ๊ณ ๊ธ 1: {ref1} | |
์ฐธ๊ณ ๊ธ 2: {ref2} | |
์ฐธ๊ณ ๊ธ 3: {ref3} | |
๋ชฉํ ๊ธ์์: {target_char_length} | |
""" | |
# ์ฒซ ๋ฒ์งธ ์๋ | |
messages = [{"role": "user", "content": initial_prompt}] | |
response = openai.ChatCompletion.create( | |
model=model_name, | |
messages=messages, | |
temperature=temperature, | |
max_tokens=max_tokens, | |
top_p=top_p, | |
frequency_penalty=frequency_penalty, | |
presence_penalty=presence_penalty, | |
) | |
first_attempt = response['choices'][0]['message']['content'].strip() | |
# ๋ถํ์ํ ํํ ์ ๊ฑฐ ๋ฐ ๊ธ์์ ํ์ธ | |
first_attempt_cleaned = remove_unwanted_phrases(first_attempt) | |
first_attempt_length = len(first_attempt_cleaned) | |
# ์ฒซ ๋ฒ์งธ ์๋์์ ๋ชฉํ ๊ธ์์ ์ถฉ์กฑ ์ | |
if first_attempt_length >= target_char_length: | |
final_post = f"์ฃผ์ : {query}\n\n{first_attempt_cleaned}" | |
return final_post, ref1, ref2, ref3, first_attempt_length | |
# ๊ฐ์ฅ ๊ธด ์ฐธ๊ณ ๊ธ ์ ํ | |
longest_ref = max([ref1, ref2, ref3], key=len) | |
# ๋ ๋ฒ์งธ ์๋ (ํด๊ณ )๋ฅผ ์ํ ์ถ๊ฐ ํ๋กฌํํธ | |
revision_prompt = f""" | |
์ด์ ์ ์์ฑ๋ ๊ธ์ ๊ธฐ๋ฐ์ผ๋ก ๋ค์ ์ง์นจ์ ๋ฐ๋์ ๋ฐ๋ผ์ ๊ธ์ ํด๊ณ (revision)ํ๋ผ: | |
1. ๋ฐ๋์ ์ด์ ๊ธ์ ๊ตฌ์กฐ์ ๋ด์ฉ์ ์ ์งํ๋ฉด์ ์ฐธ๊ณ ๊ธ์ ๋ด์ฉ์ผ๋ก๋ง ๋ณด์ํ๋ผ | |
2. ๋ฐ๋์ ๋ชฉํ ๊ธ์์({target_char_length}์)๋ฅผ ์ถฉ์กฑํ๋๋ก ๋ด์ฉ์ ๋ณด์ํ๋ผ | |
- ์ ๋ ๊ธ ํ๋จ๋ถ์ ๋จ์ ์ฒจ๊ฐ์ ๊ธ์์ ๋๋ฆฌ๊ธฐ ๊ธ์ง | |
- ์ ๋ ๊ธ ๋ง๋ฌด๋ฆฌ์ ์๋ฏธ์๋ ๊ฐ์ ์ ์ธ ๋ด์ฉ ์ถ๊ฐ ๊ธ์ง | |
- ๋ฐ๋์ ์ด์ ๊ธ์ ๋ณธ๋ก ๋ถ์ ์์ฐ์ค๋ฝ๊ฒ ๋ณด์ํ๋ผ | |
3. ๋ฐ๋์ ๋งํฌ๋ค์ด ํ์์ด ์๋ ์์ํ ํ ์คํธ๋ก๋ง ์ถ๋ ฅํ์ธ์. | |
4. ๋ฐ๋์ ์ด ํํ๋ค์ ์ฌ์ฉํ์ง ๋ง์ธ์: ์ฌ๋ฌ๋ถ, ์ต๊ทผ, ๋ง์ง๋ง์ผ๋ก, ๊ฒฐ๋ก ์ ์ผ๋ก, ๊ฒฐ๊ตญ, ์ข ํฉ์ ์ผ๋ก, ๋ฐ๋ผ์, ๋ง๋ฌด๋ฆฌ, ์์ฝ. | |
5. ๊ธ์ ํ๋ฆ์ ์์ฐ์ค๋ฝ๊ฒ ๋ง๋ค๊ณ , ๊ฐ ๋จ๋ฝ ๊ฐ์ ์ฐ๊ฒฐ์ ๋ถ๋๋ฝ๊ฒ ํด์ฃผ์ธ์. | |
6. ์ ํ๋ ํฌ์คํ ์คํ์ผ์ ๋ฐ๋์ ์ ์ฉํ์ธ์. | |
์ด์ ๊ธ: | |
{first_attempt_cleaned} | |
์ฐธ๊ณ ๊ธ: {longest_ref} | |
ํฌ์คํ ์คํ์ผ: | |
{style_prompt} | |
""" | |
# ๋ ๋ฒ์งธ ์๋ (ํด๊ณ ) | |
messages = [{"role": "user", "content": revision_prompt}] | |
response = openai.ChatCompletion.create( | |
model=model_name, | |
messages=messages, | |
temperature=temperature, | |
max_tokens=max_tokens, | |
top_p=top_p, | |
frequency_penalty=frequency_penalty, | |
presence_penalty=presence_penalty, | |
) | |
revised_attempt = response['choices'][0]['message']['content'].strip() | |
# ๋ถํ์ํ ํํ ์ ๊ฑฐ | |
final_post = remove_unwanted_phrases(revised_attempt) | |
# ์ต์ข ๊ฒฐ๊ณผ๋ฌผ ๊ตฌ์ฑ | |
final_post = f"์ฃผ์ : {query}\n\n{final_post}" | |
actual_char_length = len(final_post) | |
return final_post, ref1, ref2, ref3, actual_char_length | |
except Exception as e: | |
return f"๋ธ๋ก๊ทธ ๊ธ ์์ฑ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}", "", "", "", 0 | |
# PDF ํด๋์ค ๋ฐ ๊ด๋ จ ํจ์ ์ ์ | |
class PDF(FPDF2): | |
def __init__(self): | |
super().__init__() | |
current_dir = os.path.dirname(__file__) | |
self.add_font("NanumGothic", "", os.path.join(current_dir, "NanumGothic.ttf")) | |
self.add_font("NanumGothic", "B", os.path.join(current_dir, "NanumGothicBold.ttf")) | |
self.add_font("NanumGothicExtraBold", "", os.path.join(current_dir, "NanumGothicExtraBold.ttf")) | |
self.add_font("NanumGothicLight", "", os.path.join(current_dir, "NanumGothicLight.ttf")) | |
def header(self): | |
self.set_font('NanumGothic', '', 10) | |
def footer(self): | |
self.set_y(-15) | |
self.set_font('NanumGothic', '', 8) | |
self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C') | |
def save_to_pdf(blog_post, user_topic): | |
pdf = PDF() | |
pdf.add_page() | |
lines = blog_post.split('\n') | |
title = lines[0].strip() | |
content = '\n'.join(lines[1:]).strip() | |
# ํ์ฌ ๋ ์ง์ ์๊ฐ์ ๊ฐ์ ธ์ต๋๋ค (๋ํ๋ฏผ๊ตญ ์๊ฐ ๊ธฐ์ค) | |
now = datetime.now(ZoneInfo("Asia/Seoul")) | |
date_str = now.strftime("%y%m%d") | |
time_str = now.strftime("%H%M") | |
# ํ์ผ๋ช ์์ฑ | |
filename = f"{date_str}_{time_str}_{format_filename(user_topic)}.pdf" | |
pdf.set_font("NanumGothic", 'B', size=14) | |
pdf.cell(0, 10, title, ln=True, align='C') | |
pdf.ln(10) | |
pdf.set_font("NanumGothic", '', size=11) | |
pdf.multi_cell(0, 5, content) | |
print(f"Saving PDF as: {filename}") | |
pdf.output(filename) | |
return filename | |
def format_filename(text): | |
text = re.sub(r'[^\w\s-]', '', text) | |
return text[:50].strip() | |
def save_content_to_pdf(blog_post, user_topic): | |
return save_to_pdf(blog_post, user_topic) | |
# ๊ธฐ๋ณธ ํ๋กฌํํธ ํ ํ๋ฆฟ | |
DEFAULT_PROMPT_TEMPLATE = """ | |
[๋ธ๋ก๊ทธ ๊ธ ์์ฑ ๊ธฐ๋ณธ ๊ท์น] | |
1. ๋ฐ๋์ ํ๊ธ๋ก ์์ฑํ๋ผ | |
2. ์ฃผ์ด์ง ์ฐธ๊ณ ๊ธ์ ๋ฐํ์ผ๋ก ์ฌํ ๋ธ๋ก๊ทธ๋ฅผ ์์ฑ | |
3. ๊ธ์ ์ ๋ชฉ์ ์ฌํ ๋ธ๋ก๊ทธ ํํ์ ๋ง๋ ์ ์ ํ ์ ๋ชฉ์ผ๋ก ์ถ๋ ฅ | |
- ์ฐธ๊ณ ๊ธ์ ์ ๋ชฉ๋ ์ฐธ๊ณ ํ๋, ๋์ผํ๊ฒ ์์ฑํ์ง ๋ง ๊ฒ | |
4. ๋ฐ๋์ ๋งํฌ๋ค์ด ํ์์ด ์๋ ์์ํ ํ ์คํธ๋ก๋ง ์ถ๋ ฅํ๋ผ | |
5. ๋ฐ๋์ 3000์ ์ด์ ์์ฑํ๋ผ | |
6. ์ฃผ์ ์ ์ฐธ๊ณ ๊ธ์ ๋ณด๊ณ ์ฌํ ์คํ์ผ(๋๋ฒ ์ด, ๊ฐ์กฑ(์์ด, ๋ถ๋ชจ๋), ์ปคํ, ์๋ก ๋ฑ)์ ํ๊ฐ์ง ์ ์ ํ์ฌ ์์ฑํ๋ผ | |
7. ์ดํฌ๋ ์ฐธ๊ณ ๊ธ์ ์ดํฌ๋ฅผ ๋ฐ์ํ๋ ์ฌํ์ ๋ํ ์ค๋ ์์ด ๋ด๊ธด ์ดํฌ๋ฅผ ์ฌ์ฉํ๋ผ | |
* ๋ชจ๋ ๋ด์ฉ๋ค์ ์น์ ์ ๊ตฌ๋ถํ์ง ๋ง๊ณ ์์ฐ์ค๋ฝ๊ฒ ์ด์ฐ๋ฌ์ง๊ฒ ์์ฑํ๋ผ | |
[์ฌํ ๊ธ ์์ฑ ์ธ๋ถ ๊ท์น] | |
1. ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ์ฃผ์ ์ ์ฃผ์ด์ง ์ฐธ๊ณ ๊ธ์ ๋ฐํ์ผ๋ก ์ฌํ ๋ธ๋ก๊ทธ ๊ธ 1๊ฐ๋ฅผ ์์ฑํ๋ผ | |
2. ๊ธ์ ์ฃผ์ ๋ ์ ๋ ฅ๋ ์ฃผ์ ์ ์ฐธ๊ณ ๊ธ์ ๋ง๊ฒ ๋ค์ํ ํํ๋ก ๊ธ์ ์์ฑํ๋ผ | |
- ์ฝ์ค, ์ผ์ ๋ฑ์ ํํ(2๋ฐ3์ผ ์ฌํ ์ฝ์ค, ๋ฐ์ดํธ ์ฝ์ค ๋ฑ) | |
- ํ๋ ์ด์ ํํ(์ฌํ์ง ์ถ์ฒ Best5 ๋ฑ, ๋จ ์ฌํ์ง๋ ์ต๋ 5๊ณณ) | |
- ๋ง์ถคํ ์ฌํ์ง ์ถ์ฒ(์ปคํ, ๋ฐ์ดํธ, ๊ฐ์กฑ์ฌํ, ์์ด์ ํจ๊ปํ๋ ์ฌํ, ๋ถ๋ชจ๋๊ณผ ์ฌํ ๋ฑ) | |
- ๋จ์ ์ฌํ์ง ๋์ด ๊ธ์ง | |
- ์ผ์ ์ด๋ ์ฝ์ค์ ๋ฐ๋ฅธ ์น์ ๊ตฌ๋ถ ๊ธ์ง | |
3. ๋ ์๊ฐ ์ง์ ์ฒดํํ๋ ๊ฒ์ฒ๋ผ ์์ํ๊ฒ ์ ๋ฌํ๋ผ | |
4. ๊ฐ์ธ์ ์ธ ๊ฒฝํ๊ณผ ์ ๋ณด ์ ๊ณต์ ๊ท ํ์ ๋ง์ถฐ, ๋ ์๋ค์ด ์ ๋ณด๋ฅผ ์ป์ ์ ์๋๋ก ์์ฑ | |
5. ์ฌํ์ ์ฃผ์ ํ๋(๊ด๊ด, ์ฒดํ, ๋ง์ง ํ๋ฐฉ ๋ฑ)์ ์์ฑ | |
6. ๊ฐ ํ๋์์ ๊ฒช์ ๊ฐ์ธ์ ์ธ ๊ฒฝํ(๋๊ธฐ ์๊ฐ, ๊ตํต, ๋ ์จ ๋ฑ)์ ๊ตฌ์ฒด์ ์ผ๋ก ์ค๋ช ํ๋ผ | |
7. ์ฌํ ์ค ๋จน์ ์์์ด๋ ์ฒดํ์ ์ค์ฌ์ผ๋ก, ๊ฒฝํ๊ณผ ๋๋๋ฑ์ ์ถ๊ฐํ๊ณ ๊ตฌ์ฒด์ ์ธ ์ ๋ณด(๋ฉ๋ด, ๊ฐ๊ฒฉ, ์์น ๋ฑ)๋ฅผ ์์ฑ | |
8. ์ฌํ๊ณผ ํ๋์ ๋ํ ๊ฐ์ข ์ ๋ณด๋ฅผ ํฌํจํ๋ผ | |
[์ฌํ๊ณผ ๊ด๋ จ๋ ๊ฐ์ข ์ ๋ณด] | |
1. ์ ์ฅ๋ฃ, ์ค๋น๋ฌผ, ์๊ฐ, ์ฃผ์ฐจ, ๊ตํต์๋จ, ํ์ฌ, ์ผ์ , ๊ฐ๊ฒฉ, ๋ง์ง์ ๋ณด, ๊ฟํ, ์์ ์ ํ ๊ธฐ์ค, ์ฃผ๋ณ ํ๊ฒฝ ๋ฑ | |
2. ๊ณ์ ๋ณ๋ก ๋ฌ๋ผ์ง๋ ๊ด๊ด์ง์ ๋ชจ์ต, ์ฆ๊ธธ ๊ฑฐ๋ฆฌ, ์ฃผ์์ฌํญ ๋ฑ | |
3. ์ฌํ์ง์ ๋ํ์ ์ธ ํน์ฐ๋ฌผ์ ์ ๋์ ๋ง์ ํน์ง | |
4. ์ฌํ ์ ์ค๋น ๊ณผ์ , ์์ฝ ํ, ํ์ ์ค๋น๋ฌผ ๋ฑ | |
5. ์ธ์คํ๊ทธ๋จ์ด๋ SNS์ ์ฌ๋ฆฌ๊ธฐ ์ข์ ์ฅ์๋ ํฌํ ์คํ ๋ฑ | |
6. ํ์ง์ธ๋ค์ด ์์ฃผ ๊ฐ๋ ์จ์ ๋ง์ง์ด๋ ๋ช ์ | |
7. ๋์ค๊ตํต, ๋ ํฐ์นด ๋ฑ ์ด๋ ์๋จ์ ๋ฐ๋ฅธ ์ฌํ ํ | |
8. ์ฌํ ์ค ๊ฒช์ ์ ์๋ ์ด๋ ค์(์: ์จ์ดํ , ๋ ์จ ๋ณํ)๊ณผ ๋์ฒ ๋ฐฉ๋ฒ ๋ฑ | |
9. ์ฌํ์ง์ ์ญ์ฌ๋ ๋ฌธํ์ ๋ฐฐ๊ฒฝ์ ๊ฐ๋จํ ์๊ฐ | |
[๋ฐ๋์ ์ ์ธํด์ผ ํ ํํ] | |
1. ๋ฐ๋์ ์ฐธ๊ณ ๊ธ์ ํฌํจ๋ ๋งํฌ(URL)๋ ์ ์ธ | |
2. ์ฐธ๊ณ ๊ธ์์ '๋งํฌ๋ฅผ ํ์ธํด์ฃผ์ธ์'์ ๊ฐ์ ๋งํฌ ์ด๋์ ๋ฌธ๊ตฌ๋ ์ ์ธ | |
3. ์ฐธ๊ณ ๊ธ์ ์๋ ์์ฑ์, ํ์, ์ ํ๋ฒ, ๊ธฐ์(Writer, speaker, YouTuber, reporter)์ ์ด๋ฆ, ์ ์นญ, ๋๋ค์(Name, Nkickname)์ ๋ฐ๋์ ์ ์ธ | |
4. '์ ์ฒด๋ก ๋ถํฐ ์ ๊ณต ๋ฐ์์ ์์ฑ', '์ฟ ํก ํํธ๋์ค'๋ฑ์ ํํ์ ๋ฐ๋์ ์ ์ธํ๋ผ. | |
""" | |
# Gradio ์ฑ ์์ฑ | |
with gr.Blocks() as iface: | |
gr.Markdown("# ๋ธ๋ก๊ทธ ๊ธ ์์ฑ๊ธฐ_์ฌํ(์ฝ์คํ) ๋ธ๋ก๊ทธ") | |
gr.Markdown("์ฃผ์ ๋ฅผ ์ ๋ ฅํ๊ณ ๋ธ๋ก๊ทธ ๊ธ ์์ฑ ๋ฒํผ์ ๋๋ฅด๋ฉด ์๋์ผ๋ก ๋ธ๋ก๊ทธ ๊ธ์ ์์ฑํฉ๋๋ค.") | |
query_input = gr.Textbox(lines=1, placeholder="ํค์๋๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์...", label="ํค์๋") | |
style_input = gr.Radio(["์น๊ทผํ", "์ผ๋ฐ", "์ ๋ฌธ์ ์ธ"], label="ํฌ์คํ ์คํ์ผ", value="์น๊ทผํ") | |
prompt_input = gr.Textbox(lines=10, value=DEFAULT_PROMPT_TEMPLATE, label="ํ๋กฌํํธ ํ ํ๋ฆฟ", visible=False) | |
generate_button = gr.Button("๋ธ๋ก๊ทธ ๊ธ ์์ฑ") | |
output_text = gr.Textbox(label="์์ฑ๋ ๋ธ๋ก๊ทธ ๊ธ") | |
ref1_text = gr.Textbox(label="์ฐธ๊ณ ๊ธ 1", lines=10, visible=False) | |
ref2_text = gr.Textbox(label="์ฐธ๊ณ ๊ธ 2", lines=10, visible=False) | |
ref3_text = gr.Textbox(label="์ฐธ๊ณ ๊ธ 3", lines=10, visible=False) | |
save_pdf_button = gr.Button("PDF๋ก ์ ์ฅ") | |
pdf_output = gr.File(label="์์ฑ๋ PDF ํ์ผ") | |
generate_button.click( | |
generate_blog_post, | |
inputs=[query_input, prompt_input, style_input], | |
outputs=[output_text, ref1_text, ref2_text, ref3_text], | |
show_progress=True | |
) | |
save_pdf_button.click( | |
save_content_to_pdf, | |
inputs=[output_text, query_input], | |
outputs=[pdf_output], | |
show_progress=True | |
) | |
# Gradio ์ฑ ์คํ | |
if __name__ == "__main__": | |
iface.launch() |