Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -45,13 +45,19 @@ logging.basicConfig(level=logging.INFO,
|
|
45 |
format="%(asctime)s - %(levelname)s - %(message)s")
|
46 |
|
47 |
# ──────────────────────────────── OpenAI Client ──────────────────────────
|
|
|
|
|
48 |
@st.cache_resource
|
49 |
def get_openai_client():
|
50 |
-
"""Create an OpenAI client."""
|
51 |
if not OPENAI_API_KEY:
|
52 |
raise RuntimeError("⚠️ OPENAI_API_KEY 환경 변수가 설정되지 않았습니다.")
|
53 |
-
return OpenAI(
|
54 |
-
|
|
|
|
|
|
|
|
|
55 |
# ──────────────────────────────── Blog Creation System Prompt ─────────────
|
56 |
def get_system_prompt(template="ginigen", tone="professional", word_count=1750, include_search_results=False, include_uploaded_files=False) -> str:
|
57 |
"""
|
@@ -788,64 +794,126 @@ def process_input(prompt: str, uploaded_files):
|
|
788 |
# 사용자 메시지 추가
|
789 |
api_messages.append({"role": "user", "content": user_content})
|
790 |
|
791 |
-
# OpenAI API 호출
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
810 |
|
811 |
# 결과 표시
|
812 |
placeholder.markdown(answer)
|
813 |
|
814 |
# 이미지 생성
|
815 |
answer_entry_saved = False
|
816 |
-
if st.session_state.generate_image:
|
817 |
with st.spinner("Generating image..."):
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
|
|
|
|
|
|
|
|
829 |
|
830 |
# Save the answer if not saved above
|
831 |
-
if not answer_entry_saved:
|
832 |
st.session_state.messages.append({"role": "assistant", "content": answer})
|
833 |
|
834 |
# Download buttons
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
|
|
849 |
|
850 |
# Auto save
|
851 |
if st.session_state.auto_save and st.session_state.messages:
|
|
|
45 |
format="%(asctime)s - %(levelname)s - %(message)s")
|
46 |
|
47 |
# ──────────────────────────────── OpenAI Client ──────────────────────────
|
48 |
+
|
49 |
+
# OpenAI 클라이언트에 타임아웃과 재시도 로직 추가
|
50 |
@st.cache_resource
|
51 |
def get_openai_client():
|
52 |
+
"""Create an OpenAI client with timeout and retry settings."""
|
53 |
if not OPENAI_API_KEY:
|
54 |
raise RuntimeError("⚠️ OPENAI_API_KEY 환경 변수가 설정되지 않았습니다.")
|
55 |
+
return OpenAI(
|
56 |
+
api_key=OPENAI_API_KEY,
|
57 |
+
timeout=60.0, # 타임아웃 60초로 설정
|
58 |
+
max_retries=3 # 재시도 횟수 3회로 설정
|
59 |
+
)
|
60 |
+
|
61 |
# ──────────────────────────────── Blog Creation System Prompt ─────────────
|
62 |
def get_system_prompt(template="ginigen", tone="professional", word_count=1750, include_search_results=False, include_uploaded_files=False) -> str:
|
63 |
"""
|
|
|
794 |
# 사용자 메시지 추가
|
795 |
api_messages.append({"role": "user", "content": user_content})
|
796 |
|
797 |
+
# OpenAI API 호출 - 재시도 로직 추가
|
798 |
+
max_retries = 3
|
799 |
+
retry_delay = 2 # 초 단위
|
800 |
+
answer = None
|
801 |
+
used_model = st.session_state.ai_model
|
802 |
+
|
803 |
+
for retry in range(max_retries):
|
804 |
+
try:
|
805 |
+
status.update(label=f"Generating blog with {used_model} (attempt {retry+1}/{max_retries})...")
|
806 |
+
response = client.chat.completions.create(
|
807 |
+
model=used_model,
|
808 |
+
messages=api_messages,
|
809 |
+
temperature=1,
|
810 |
+
max_tokens=MAX_TOKENS,
|
811 |
+
top_p=1,
|
812 |
+
request_timeout=90 # 타임아웃 90초로 설정
|
813 |
+
)
|
814 |
+
|
815 |
+
# 응답 추출
|
816 |
+
answer = response.choices[0].message.content
|
817 |
+
status.update(label=f"Blog generated successfully with {used_model}!", state="complete")
|
818 |
+
break # 성공하면 반복문 종료
|
819 |
+
|
820 |
+
except Exception as api_error:
|
821 |
+
error_message = str(api_error)
|
822 |
+
logging.error(f"OpenAI API error (attempt {retry+1}/{max_retries}): {error_message}")
|
823 |
+
|
824 |
+
if retry < max_retries - 1:
|
825 |
+
wait_time = retry_delay * (retry + 1) # 점점 길어지는 대기 시간
|
826 |
+
status.update(label=f"API Error: {error_message}. Retrying in {wait_time}s... ({retry+1}/{max_retries})")
|
827 |
+
time.sleep(wait_time)
|
828 |
+
else:
|
829 |
+
# 마지막 시도에서도 실패하면 대체 모델 시도
|
830 |
+
fallback_models = ["gpt-3.5-turbo", "gpt-3.5-turbo-16k"]
|
831 |
+
if used_model in fallback_models:
|
832 |
+
fallback_models.remove(used_model)
|
833 |
+
|
834 |
+
# 대체 모델 시도
|
835 |
+
if fallback_models:
|
836 |
+
status.update(label="Primary model failed. Trying fallback models...")
|
837 |
+
for fb_model in fallback_models:
|
838 |
+
try:
|
839 |
+
status.update(label=f"Trying with fallback model: {fb_model}...")
|
840 |
+
response = client.chat.completions.create(
|
841 |
+
model=fb_model,
|
842 |
+
messages=api_messages,
|
843 |
+
temperature=1,
|
844 |
+
max_tokens=min(MAX_TOKENS, 4096 if "16k" not in fb_model else 16000),
|
845 |
+
top_p=1,
|
846 |
+
request_timeout=90
|
847 |
+
)
|
848 |
+
answer = response.choices[0].message.content
|
849 |
+
used_model = fb_model
|
850 |
+
status.update(label=f"Blog generated with fallback model: {used_model}", state="complete")
|
851 |
+
break
|
852 |
+
except Exception as fb_error:
|
853 |
+
logging.error(f"Fallback model {fb_model} also failed: {str(fb_error)}")
|
854 |
+
|
855 |
+
# 대체 모델 중 하나가 성공했는지 확인
|
856 |
+
if answer:
|
857 |
+
break
|
858 |
+
|
859 |
+
# 모든 모델이 실패한 경우
|
860 |
+
if not answer:
|
861 |
+
status.update(label="All models failed to generate content", state="error")
|
862 |
+
user_message = "OpenAI API 연결 문제가 발생했습니다. 인터넷 연결을 확인하거나 나중에 다시 시도해 주세요."
|
863 |
+
if "rate limit" in error_message.lower():
|
864 |
+
user_message = "OpenAI API 속도 제한에 도달했습니다. 잠시 후 다시 시도해 주세요."
|
865 |
+
elif "invalid api key" in error_message.lower():
|
866 |
+
user_message = "유효하지 않은 API 키입니다. API 키를 확인해 주세요."
|
867 |
+
|
868 |
+
raise Exception(user_message)
|
869 |
+
|
870 |
+
# 결과가 생성되지 않은 경우 오류 발생
|
871 |
+
if not answer:
|
872 |
+
raise Exception("블로그 콘텐츠를 생성하지 못했습니다.")
|
873 |
|
874 |
# 결과 표시
|
875 |
placeholder.markdown(answer)
|
876 |
|
877 |
# 이미지 생성
|
878 |
answer_entry_saved = False
|
879 |
+
if st.session_state.generate_image and answer:
|
880 |
with st.spinner("Generating image..."):
|
881 |
+
try:
|
882 |
+
ip = extract_image_prompt(answer, prompt)
|
883 |
+
img, cap = generate_image(ip)
|
884 |
+
if img:
|
885 |
+
st.image(img, caption=cap)
|
886 |
+
st.session_state.messages.append({
|
887 |
+
"role": "assistant",
|
888 |
+
"content": answer,
|
889 |
+
"image": img,
|
890 |
+
"image_caption": cap
|
891 |
+
})
|
892 |
+
answer_entry_saved = True
|
893 |
+
except Exception as img_error:
|
894 |
+
logging.error(f"Image generation error: {str(img_error)}")
|
895 |
+
st.warning("이미지 생성에 실패했습니다. 블로그 콘텐츠만 저장됩니다.")
|
896 |
|
897 |
# Save the answer if not saved above
|
898 |
+
if not answer_entry_saved and answer:
|
899 |
st.session_state.messages.append({"role": "assistant", "content": answer})
|
900 |
|
901 |
# Download buttons
|
902 |
+
if answer:
|
903 |
+
st.subheader("Download This Blog")
|
904 |
+
c1, c2 = st.columns(2)
|
905 |
+
c1.download_button(
|
906 |
+
"Markdown",
|
907 |
+
data=answer,
|
908 |
+
file_name=f"{prompt[:30]}.md",
|
909 |
+
mime="text/markdown"
|
910 |
+
)
|
911 |
+
c2.download_button(
|
912 |
+
"HTML",
|
913 |
+
data=md_to_html(answer, prompt[:30]),
|
914 |
+
file_name=f"{prompt[:30]}.html",
|
915 |
+
mime="text/html"
|
916 |
+
)
|
917 |
|
918 |
# Auto save
|
919 |
if st.session_state.auto_save and st.session_state.messages:
|