Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,21 +1,23 @@
|
|
| 1 |
-
# CHATGPT APIλ‘ λ³κ²½
|
| 2 |
-
|
| 3 |
# ββββββββββββββββββββββββββββββββ Imports ββββββββββββββββββββββββββββββββ
|
| 4 |
import os, json, re, logging, requests, markdown, time, io
|
| 5 |
from datetime import datetime
|
| 6 |
|
| 7 |
import streamlit as st
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
| 9 |
from gradio_client import Client
|
| 10 |
import pandas as pd
|
| 11 |
import PyPDF2 # For handling PDF files
|
| 12 |
|
| 13 |
# ββββββββββββββββββββββββββββββββ Environment Variables / Constants βββββββββββββββββββββββββ
|
| 14 |
-
ANTHROPIC_KEY
|
|
|
|
| 15 |
BRAVE_KEY = os.getenv("SERPHOUSE_API_KEY", "") # Keep this name
|
| 16 |
BRAVE_ENDPOINT = "https://api.search.brave.com/res/v1/web/search"
|
| 17 |
IMAGE_API_URL = "http://211.233.58.201:7896"
|
| 18 |
-
MAX_TOKENS =
|
| 19 |
|
| 20 |
# Blog template and style definitions (in English)
|
| 21 |
BLOG_TEMPLATES = {
|
|
@@ -45,10 +47,18 @@ EXAMPLE_TOPICS = {
|
|
| 45 |
logging.basicConfig(level=logging.INFO,
|
| 46 |
format="%(asctime)s - %(levelname)s - %(message)s")
|
| 47 |
|
| 48 |
-
# ββββββββββββββββββββββββββββββββ
|
| 49 |
@st.cache_resource
|
| 50 |
-
def
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
# ββββββββββββββββββββββββββββββββ Blog Creation System Prompt βββββββββββββ
|
| 54 |
def get_system_prompt(template="ginigen", tone="professional", word_count=1750, include_search_results=False, include_uploaded_files=False) -> str:
|
|
@@ -514,19 +524,55 @@ def extract_image_prompt(blog_text: str, topic: str):
|
|
| 514 |
Analyze the blog content (blog_text) to generate a one-line English image prompt
|
| 515 |
related to the topic.
|
| 516 |
"""
|
| 517 |
-
|
| 518 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 519 |
try:
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 526 |
)
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 530 |
return f"A professional photo related to {topic}, high quality"
|
| 531 |
|
| 532 |
def md_to_html(md: str, title="Ginigen Blog"):
|
|
@@ -544,7 +590,8 @@ def ginigen_app():
|
|
| 544 |
|
| 545 |
# Set default session state
|
| 546 |
if "ai_model" not in st.session_state:
|
| 547 |
-
|
|
|
|
| 548 |
if "messages" not in st.session_state:
|
| 549 |
st.session_state.messages = []
|
| 550 |
if "auto_save" not in st.session_state:
|
|
@@ -710,7 +757,7 @@ def process_example(topic):
|
|
| 710 |
process_input(topic, [])
|
| 711 |
|
| 712 |
def process_input(prompt: str, uploaded_files):
|
| 713 |
-
# Add user's message
|
| 714 |
if not any(m["role"] == "user" and m["content"] == prompt for m in st.session_state.messages):
|
| 715 |
st.session_state.messages.append({"role": "user", "content": prompt})
|
| 716 |
|
|
@@ -725,7 +772,7 @@ def process_input(prompt: str, uploaded_files):
|
|
| 725 |
has_uploaded_files = bool(uploaded_files) and len(uploaded_files) > 0
|
| 726 |
|
| 727 |
try:
|
| 728 |
-
client =
|
| 729 |
|
| 730 |
# Prepare conversation messages
|
| 731 |
messages = [{"role": m["role"], "content": m["content"]} for m in st.session_state.messages]
|
|
@@ -752,7 +799,7 @@ def process_input(prompt: str, uploaded_files):
|
|
| 752 |
include_uploaded_files=has_uploaded_files
|
| 753 |
)
|
| 754 |
|
| 755 |
-
#
|
| 756 |
if file_content:
|
| 757 |
sys_prompt += (
|
| 758 |
"\n\n"
|
|
@@ -761,7 +808,7 @@ def process_input(prompt: str, uploaded_files):
|
|
| 761 |
"Ensure the file content is accurately reflected in the blog.\n"
|
| 762 |
)
|
| 763 |
|
| 764 |
-
#
|
| 765 |
if has_uploaded_files:
|
| 766 |
extra_user_msg = (
|
| 767 |
f"{prompt}\n\n"
|
|
@@ -770,19 +817,53 @@ def process_input(prompt: str, uploaded_files):
|
|
| 770 |
)
|
| 771 |
messages.append({"role": "user", "content": extra_user_msg})
|
| 772 |
|
| 773 |
-
#
|
| 774 |
-
with
|
| 775 |
-
|
| 776 |
-
|
| 777 |
-
|
| 778 |
-
|
| 779 |
-
|
| 780 |
-
|
| 781 |
-
|
| 782 |
-
|
| 783 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 784 |
|
| 785 |
-
#
|
|
|
|
|
|
|
|
|
|
| 786 |
answer_entry_saved = False
|
| 787 |
if st.session_state.generate_image:
|
| 788 |
with st.spinner("Generating image..."):
|
|
@@ -797,8 +878,8 @@ def process_input(prompt: str, uploaded_files):
|
|
| 797 |
"image_caption": cap
|
| 798 |
})
|
| 799 |
answer_entry_saved = True
|
| 800 |
-
|
| 801 |
-
# Save the answer
|
| 802 |
if not answer_entry_saved:
|
| 803 |
st.session_state.messages.append({"role": "assistant", "content": answer})
|
| 804 |
|
|
@@ -827,16 +908,6 @@ def process_input(prompt: str, uploaded_files):
|
|
| 827 |
except Exception as e:
|
| 828 |
logging.error(f"Auto-save failed: {e}")
|
| 829 |
|
| 830 |
-
except anthropic.BadRequestError as e:
|
| 831 |
-
error_message = str(e)
|
| 832 |
-
if "credit balance is too low" in error_message:
|
| 833 |
-
placeholder.error("β οΈ Insufficient API credits: Please top up your Anthropic API account.")
|
| 834 |
-
ans = "Unable to generate blog due to low API credits. Please recharge and try again."
|
| 835 |
-
else:
|
| 836 |
-
placeholder.error(f"API request error: {error_message}")
|
| 837 |
-
ans = f"An error occurred while calling the API: {error_message}"
|
| 838 |
-
st.session_state.messages.append({"role": "assistant", "content": ans})
|
| 839 |
-
|
| 840 |
except Exception as e:
|
| 841 |
error_message = str(e)
|
| 842 |
placeholder.error(f"An error occurred: {error_message}")
|
|
@@ -848,4 +919,4 @@ def main():
|
|
| 848 |
ginigen_app()
|
| 849 |
|
| 850 |
if __name__ == "__main__":
|
| 851 |
-
main()
|
|
|
|
|
|
|
|
|
|
| 1 |
# ββββββββββββββββββββββββββββββββ Imports ββββββββββββββββββββββββββββββββ
|
| 2 |
import os, json, re, logging, requests, markdown, time, io
|
| 3 |
from datetime import datetime
|
| 4 |
|
| 5 |
import streamlit as st
|
| 6 |
+
# >>> Anthropic λΆλΆ μμ
|
| 7 |
+
# import anthropic
|
| 8 |
+
from openai import OpenAI # 컀μ€ν
λνΌ(λλ λ³λ λΌμ΄λΈλ¬λ¦¬)λΌκ³ κ°μ
|
| 9 |
+
|
| 10 |
from gradio_client import Client
|
| 11 |
import pandas as pd
|
| 12 |
import PyPDF2 # For handling PDF files
|
| 13 |
|
| 14 |
# ββββββββββββββββββββββββββββββββ Environment Variables / Constants βββββββββββββββββββββββββ
|
| 15 |
+
# κΈ°μ‘΄ ANTHROPIC_KEY -> OPENAI_API_KEY λ‘ λ³κ²½
|
| 16 |
+
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
|
| 17 |
BRAVE_KEY = os.getenv("SERPHOUSE_API_KEY", "") # Keep this name
|
| 18 |
BRAVE_ENDPOINT = "https://api.search.brave.com/res/v1/web/search"
|
| 19 |
IMAGE_API_URL = "http://211.233.58.201:7896"
|
| 20 |
+
MAX_TOKENS = 7999
|
| 21 |
|
| 22 |
# Blog template and style definitions (in English)
|
| 23 |
BLOG_TEMPLATES = {
|
|
|
|
| 47 |
logging.basicConfig(level=logging.INFO,
|
| 48 |
format="%(asctime)s - %(levelname)s - %(message)s")
|
| 49 |
|
| 50 |
+
# ββββββββββββββββββββββββββββββββ OpenAI Client ββββββββββββββββββββββββββ
|
| 51 |
@st.cache_resource
|
| 52 |
+
def get_openai_client():
|
| 53 |
+
"""
|
| 54 |
+
컀μ€ν
OpenAI κ°μ²΄λ₯Ό μμ±νλ€κ³ κ°μ ν©λλ€.
|
| 55 |
+
μ€μ λ‘λ openai.api_key = OPENAI_API_KEY λ‘λ§ μ€μ νλ κ²½μ°κ° λ§μ΅λλ€.
|
| 56 |
+
"""
|
| 57 |
+
if not OPENAI_API_KEY:
|
| 58 |
+
raise RuntimeError("β οΈ OPENAI_API_KEY νκ²½ λ³μκ° μ€μ λμ§ μμμ΅λλ€.")
|
| 59 |
+
# μ¬κΈ°μλ μμλ‘μ λ€μκ³Ό κ°μ΄ μ΄κΈ°ν:
|
| 60 |
+
client = OpenAI(api_key=OPENAI_API_KEY)
|
| 61 |
+
return client
|
| 62 |
|
| 63 |
# ββββββββββββββββββββββββββββββββ Blog Creation System Prompt βββββββββββββ
|
| 64 |
def get_system_prompt(template="ginigen", tone="professional", word_count=1750, include_search_results=False, include_uploaded_files=False) -> str:
|
|
|
|
| 524 |
Analyze the blog content (blog_text) to generate a one-line English image prompt
|
| 525 |
related to the topic.
|
| 526 |
"""
|
| 527 |
+
# κΈ°μ‘΄ anthropic ν΄λΌμ΄μΈνΈ μ¬μ© -> OpenAI νΈμΆλ‘ λ체
|
| 528 |
+
client = get_openai_client()
|
| 529 |
+
|
| 530 |
+
system_msg = (
|
| 531 |
+
f"Generate a single-line English image prompt from the following text:\n"
|
| 532 |
+
f"Topic: {topic}\n\n"
|
| 533 |
+
f"---\n"
|
| 534 |
+
f"{blog_text}\n\n"
|
| 535 |
+
f"---\n"
|
| 536 |
+
f"Return only the prompt text, nothing else."
|
| 537 |
+
)
|
| 538 |
+
|
| 539 |
+
# μ€μ λ‘λ openai APIλ₯Ό μ΄λ»κ² νΈμΆνλλμ λ°λΌ λ¬λΌμ§λλ€.
|
| 540 |
+
# μ¬κΈ°μλ μμλ‘ client.responses.create()λ₯Ό μ¬μ©νλ€κ³ κ°μ
|
| 541 |
try:
|
| 542 |
+
response = client.responses.create(
|
| 543 |
+
model="gpt-4.1-mini",
|
| 544 |
+
input=[
|
| 545 |
+
{
|
| 546 |
+
"role": "system",
|
| 547 |
+
"content": [
|
| 548 |
+
{
|
| 549 |
+
"type": "input_text",
|
| 550 |
+
"text": system_msg
|
| 551 |
+
}
|
| 552 |
+
]
|
| 553 |
+
}
|
| 554 |
+
],
|
| 555 |
+
text={"format": {"type": "text"}},
|
| 556 |
+
temperature=1,
|
| 557 |
+
max_output_tokens=80,
|
| 558 |
+
top_p=1
|
| 559 |
)
|
| 560 |
+
# μμ: response κ°μ²΄μμ 첫 λ²μ§Έ μΆλ ₯λ§ κ°μ Έμ¨λ€κ³ κ°μ
|
| 561 |
+
# response ꡬ쑰λ μ¬μ© μ€μΈ λνΌμ λ°λΌ λ€λ¦
λλ€.
|
| 562 |
+
content = ""
|
| 563 |
+
if "responses" in dir(response):
|
| 564 |
+
# κ°μ μμ: response.responses[0].content[0].text
|
| 565 |
+
# λλ response["choices"][0]["text"] ννμΌ μλ μμ
|
| 566 |
+
first_resp = response.responses[0] # κ°μ
|
| 567 |
+
# λ³Έ μμλ "content" νλκ° listλ‘ μκ³ , κ·Έ μ€ [0]["text"]μ κ°μ΄ μλ€κ³ κ°μ
|
| 568 |
+
content = first_resp.content[0]["text"].strip()
|
| 569 |
+
else:
|
| 570 |
+
content = "A professional photo related to the topic, high quality"
|
| 571 |
+
|
| 572 |
+
return content
|
| 573 |
+
|
| 574 |
+
except Exception as e:
|
| 575 |
+
logging.error(f"OpenAI image prompt generation error: {e}")
|
| 576 |
return f"A professional photo related to {topic}, high quality"
|
| 577 |
|
| 578 |
def md_to_html(md: str, title="Ginigen Blog"):
|
|
|
|
| 590 |
|
| 591 |
# Set default session state
|
| 592 |
if "ai_model" not in st.session_state:
|
| 593 |
+
# κΈ°μ‘΄ anthropic λͺ¨λΈλͺ
λμ , gpt-4.1-mini
|
| 594 |
+
st.session_state.ai_model = "gpt-4.1-mini"
|
| 595 |
if "messages" not in st.session_state:
|
| 596 |
st.session_state.messages = []
|
| 597 |
if "auto_save" not in st.session_state:
|
|
|
|
| 757 |
process_input(topic, [])
|
| 758 |
|
| 759 |
def process_input(prompt: str, uploaded_files):
|
| 760 |
+
# Add user's message
|
| 761 |
if not any(m["role"] == "user" and m["content"] == prompt for m in st.session_state.messages):
|
| 762 |
st.session_state.messages.append({"role": "user", "content": prompt})
|
| 763 |
|
|
|
|
| 772 |
has_uploaded_files = bool(uploaded_files) and len(uploaded_files) > 0
|
| 773 |
|
| 774 |
try:
|
| 775 |
+
client = get_openai_client()
|
| 776 |
|
| 777 |
# Prepare conversation messages
|
| 778 |
messages = [{"role": m["role"], "content": m["content"]} for m in st.session_state.messages]
|
|
|
|
| 799 |
include_uploaded_files=has_uploaded_files
|
| 800 |
)
|
| 801 |
|
| 802 |
+
# νμΌ λ΄μ©μ΄ μλ€λ©΄ system promptμ μΆκ°
|
| 803 |
if file_content:
|
| 804 |
sys_prompt += (
|
| 805 |
"\n\n"
|
|
|
|
| 808 |
"Ensure the file content is accurately reflected in the blog.\n"
|
| 809 |
)
|
| 810 |
|
| 811 |
+
# μ¬μ©μκ° μ
λ ₯ν promptμ νμΌ μ°Έκ³ λ©μμ§
|
| 812 |
if has_uploaded_files:
|
| 813 |
extra_user_msg = (
|
| 814 |
f"{prompt}\n\n"
|
|
|
|
| 817 |
)
|
| 818 |
messages.append({"role": "user", "content": extra_user_msg})
|
| 819 |
|
| 820 |
+
# μ΄μ OpenAI clientλ‘ μμ²μ 보λ
λλ€.
|
| 821 |
+
with st.spinner("Generating blog content..."):
|
| 822 |
+
response = client.responses.create(
|
| 823 |
+
model=st.session_state.ai_model,
|
| 824 |
+
# OpenAI λνΌμ λ§μΆ°μ λλ΅μ μΌλ‘ μ¬κ΅¬μ±ν μμ
λλ€.
|
| 825 |
+
input=[
|
| 826 |
+
{
|
| 827 |
+
"role": "system",
|
| 828 |
+
"content": [
|
| 829 |
+
{
|
| 830 |
+
"type": "input_text",
|
| 831 |
+
"text": sys_prompt
|
| 832 |
+
}
|
| 833 |
+
]
|
| 834 |
+
},
|
| 835 |
+
{
|
| 836 |
+
"role": "user",
|
| 837 |
+
"content": [
|
| 838 |
+
{
|
| 839 |
+
"type": "input_text",
|
| 840 |
+
"text": prompt
|
| 841 |
+
}
|
| 842 |
+
]
|
| 843 |
+
}
|
| 844 |
+
# νμνλ©΄ messages μ 체λ₯Ό λ£μ μλ μμ§λ§,
|
| 845 |
+
# μ¬κΈ°μλ promptμ system promptλ§ λ£λ μμ
|
| 846 |
+
],
|
| 847 |
+
text={"format": {"type": "text"}},
|
| 848 |
+
temperature=1,
|
| 849 |
+
max_output_tokens=MAX_TOKENS,
|
| 850 |
+
top_p=1,
|
| 851 |
+
store=True
|
| 852 |
+
)
|
| 853 |
+
|
| 854 |
+
# μμμ response κ°μ²΄μμ μ΅μ’
ν
μ€νΈλ₯Ό κΊΌλ΄λ λ‘μ§
|
| 855 |
+
# μ€μ ꡬ쑰λ λΌμ΄λΈλ¬λ¦¬ ꡬνμ λ°λΌ λ€λ¦
λλ€
|
| 856 |
+
if "responses" in dir(response):
|
| 857 |
+
# κ°μ : response.responses[0].content[0].text νν
|
| 858 |
+
content_blocks = response.responses[0].content
|
| 859 |
+
answer = "\n".join(block["text"] for block in content_blocks if block["type"] == "output_text")
|
| 860 |
+
else:
|
| 861 |
+
answer = "Error: Unable to parse the OpenAI response."
|
| 862 |
|
| 863 |
+
# μ€νΈλ¦¬λ°μ΄ μλλ―λ‘ οΏ½οΏ½οΏ½λ²μ answer νλ ν μΆλ ₯
|
| 864 |
+
placeholder.markdown(answer)
|
| 865 |
+
|
| 866 |
+
# μ΄λ―Έμ§ μμ±
|
| 867 |
answer_entry_saved = False
|
| 868 |
if st.session_state.generate_image:
|
| 869 |
with st.spinner("Generating image..."):
|
|
|
|
| 878 |
"image_caption": cap
|
| 879 |
})
|
| 880 |
answer_entry_saved = True
|
| 881 |
+
|
| 882 |
+
# Save the answer if not saved above
|
| 883 |
if not answer_entry_saved:
|
| 884 |
st.session_state.messages.append({"role": "assistant", "content": answer})
|
| 885 |
|
|
|
|
| 908 |
except Exception as e:
|
| 909 |
logging.error(f"Auto-save failed: {e}")
|
| 910 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 911 |
except Exception as e:
|
| 912 |
error_message = str(e)
|
| 913 |
placeholder.error(f"An error occurred: {error_message}")
|
|
|
|
| 919 |
ginigen_app()
|
| 920 |
|
| 921 |
if __name__ == "__main__":
|
| 922 |
+
main()
|