openfree commited on
Commit
dc92e59
Β·
verified Β·
1 Parent(s): 83b5d74

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +151 -10
app.py CHANGED
@@ -607,21 +607,34 @@ class NovelWritingSystem:
607
 
608
  def get_system_prompts(self, language: str) -> Dict[str, str]:
609
  """역할별 μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈ 생성"""
610
- # (κΈ°μ‘΄ μ½”λ“œμ™€ 동일)
611
- if language == "Korean":
612
- return {
613
  "director": "당신은 창의적이고 체계적인 μ†Œμ„€ 기획 μ „λ¬Έκ°€μž…λ‹ˆλ‹€. ν₯λ―Έλ‘­κ³  일관성 μžˆλŠ” μŠ€ν† λ¦¬λ₯Ό μ„€κ³„ν•˜μ„Έμš”.",
614
  "critic": "당신은 일관성 κ²€ν†  μ „λ¬Έ λΉ„ν‰κ°€μž…λ‹ˆλ‹€. 캐릭터, ν”Œλ‘―, μ„€μ •μ˜ 일관성을 μ² μ €νžˆ μ κ²€ν•˜κ³  κ°œμ„ λ°©μ•ˆμ„ μ œμ‹œν•˜μ„Έμš”.",
615
- "writer1": "당신은 μ†Œμ„€μ˜ λ§€λ ₯적인 μ‹œμž‘μ„ λ‹΄λ‹Ήν•˜λŠ” μž‘κ°€μž…λ‹ˆλ‹€. λ…μžλ₯Ό μ‚¬λ‘œμž‘λŠ” λ„μž…λΆ€λ₯Ό λ§Œλ“œμ„Έμš”.",
616
- "writer10": "당신은 μ™„λ²½ν•œ 결말을 λ§Œλ“œλŠ” μž‘κ°€μž…λ‹ˆλ‹€. λ…μžμ—κ²Œ κΉŠμ€ μ—¬μš΄μ„ λ‚¨κΈ°λŠ” 마무리λ₯Ό ν•˜μ„Έμš”."
617
- }
618
- else:
619
- return {
620
  "director": "You are a creative and systematic novel planning expert. Design engaging and consistent stories.",
621
  "critic": "You are a consistency review specialist critic. Thoroughly check character, plot, and setting consistency and suggest improvements.",
622
- "writer1": "You are a writer responsible for the captivating beginning. Create an opening that hooks readers.",
623
- "writer10": "You are a writer who creates the perfect ending. Create a conclusion that leaves readers with deep resonance."
624
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625
 
626
  # --- 메인 ν”„λ‘œμ„ΈμŠ€ ---
627
  def process_novel_stream(self, query: str, language: str, session_id: Optional[str] = None) -> Generator[Tuple[str, List[Dict[str, Any]], str], None, None]:
@@ -755,6 +768,134 @@ class NovelWritingSystem:
755
  return "λ³΄κ³ μ„œ 생성 쀑 였λ₯˜ λ°œμƒ"
756
 
757
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
758
 
759
 
760
  # CSS μŠ€νƒ€μΌ
 
607
 
608
  def get_system_prompts(self, language: str) -> Dict[str, str]:
609
  """역할별 μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈ 생성"""
610
+ base_prompts = {
611
+ "Korean": {
 
612
  "director": "당신은 창의적이고 체계적인 μ†Œμ„€ 기획 μ „λ¬Έκ°€μž…λ‹ˆλ‹€. ν₯λ―Έλ‘­κ³  일관성 μžˆλŠ” μŠ€ν† λ¦¬λ₯Ό μ„€κ³„ν•˜μ„Έμš”.",
613
  "critic": "당신은 일관성 κ²€ν†  μ „λ¬Έ λΉ„ν‰κ°€μž…λ‹ˆλ‹€. 캐릭터, ν”Œλ‘―, μ„€μ •μ˜ 일관성을 μ² μ €νžˆ μ κ²€ν•˜κ³  κ°œμ„ λ°©μ•ˆμ„ μ œμ‹œν•˜μ„Έμš”.",
614
+ "writer_base": "당신은 μ „λ¬Έ μ†Œμ„€ μž‘κ°€μž…λ‹ˆλ‹€. μ£Όμ–΄μ§„ 지침에 따라 λͺ°μž…감 있고 일관성 μžˆλŠ” λ‚΄μš©μ„ μž‘μ„±ν•˜μ„Έμš”."
615
+ },
616
+ "English": {
 
 
617
  "director": "You are a creative and systematic novel planning expert. Design engaging and consistent stories.",
618
  "critic": "You are a consistency review specialist critic. Thoroughly check character, plot, and setting consistency and suggest improvements.",
619
+ "writer_base": "You are a professional novel writer. Write immersive and consistent content according to the given guidelines."
 
620
  }
621
+ }
622
+
623
+ prompts = base_prompts[language].copy()
624
+
625
+ # μž‘κ°€λ³„ 특수 ν”„λ‘¬ν”„νŠΈ
626
+ if language == "Korean":
627
+ prompts["writer1"] = "당신은 μ†Œμ„€μ˜ λ§€λ ₯적인 μ‹œμž‘μ„ λ‹΄λ‹Ήν•˜λŠ” μž‘κ°€μž…λ‹ˆλ‹€. λ…μžλ₯Ό μ‚¬λ‘œμž‘λŠ” λ„μž…λΆ€λ₯Ό λ§Œλ“œμ„Έμš”."
628
+ prompts["writer10"] = "당신은 μ™„λ²½ν•œ 결말을 λ§Œλ“œλŠ” μž‘κ°€μž…λ‹ˆλ‹€. λ…μžμ—κ²Œ κΉŠμ€ μ—¬μš΄μ„ λ‚¨κΈ°λŠ” 마무리λ₯Ό ν•˜μ„Έμš”."
629
+ else:
630
+ prompts["writer1"] = "You are a writer responsible for the captivating beginning. Create an opening that hooks readers."
631
+ prompts["writer10"] = "You are a writer who creates the perfect ending. Create a conclusion that leaves readers with deep resonance."
632
+
633
+ # writer2-9λŠ” κΈ°λ³Έ ν”„λ‘¬ν”„νŠΈ μ‚¬μš©
634
+ for i in range(2, 10):
635
+ prompts[f"writer{i}"] = prompts["writer_base"]
636
+
637
+ return prompts
638
 
639
  # --- 메인 ν”„λ‘œμ„ΈμŠ€ ---
640
  def process_novel_stream(self, query: str, language: str, session_id: Optional[str] = None) -> Generator[Tuple[str, List[Dict[str, Any]], str], None, None]:
 
768
  return "λ³΄κ³ μ„œ 생성 쀑 였λ₯˜ λ°œμƒ"
769
 
770
 
771
+ # --- μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜λ“€ ---
772
+ def process_query(query: str, language: str, session_id: Optional[str] = None) -> Generator[Tuple[str, str, str, str], None, None]:
773
+ """메인 쿼리 처리 ν•¨μˆ˜"""
774
+ if not query.strip():
775
+ yield "", "", "❌ 주제λ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”.", session_id
776
+ return
777
+
778
+ system = NovelWritingSystem()
779
+ stages_markdown = ""
780
+ novel_content = ""
781
+
782
+ for status, stages, current_session_id in system.process_novel_stream(query, language, session_id):
783
+ stages_markdown = format_stages_display(stages)
784
+
785
+ # μ΅œμ’… μ†Œμ„€ λ‚΄μš© κ°€μ Έμ˜€κΈ°
786
+ if stages and all(s.get("status") == "complete" for s in stages[-10:]):
787
+ novel_content = NovelDatabase.get_writer_content(current_session_id)
788
+ novel_content = format_novel_display(novel_content)
789
+
790
+ yield stages_markdown, novel_content, status or "πŸ”„ 처리 쀑...", current_session_id
791
+
792
+ def get_active_sessions(language: str) -> List[str]:
793
+ """ν™œμ„± μ„Έμ…˜ λͺ©λ‘ κ°€μ Έμ˜€κΈ°"""
794
+ sessions = NovelDatabase.get_active_sessions()
795
+ return [f"{s['session_id'][:8]}... - {s['user_query'][:50]}... ({s['created_at']})"
796
+ for s in sessions]
797
+
798
+ def auto_recover_session(language: str) -> Tuple[Optional[str], str]:
799
+ """κ°€μž₯ 졜근 ν™œμ„± μ„Έμ…˜ μžλ™ 볡ꡬ"""
800
+ latest_session = NovelDatabase.get_latest_active_session()
801
+ if latest_session:
802
+ return latest_session['session_id'], f"μ„Έμ…˜ {latest_session['session_id'][:8]}... 볡ꡬ됨"
803
+ return None, "볡ꡬ할 μ„Έμ…˜μ΄ μ—†μŠ΅λ‹ˆλ‹€."
804
+
805
+ def resume_session(session_id: str, language: str) -> Generator[Tuple[str, str, str, str], None, None]:
806
+ """μ„Έμ…˜ 재개 ν•¨μˆ˜"""
807
+ if not session_id:
808
+ yield "", "", "❌ μ„Έμ…˜ IDκ°€ μ—†μŠ΅λ‹ˆλ‹€.", session_id
809
+ return
810
+
811
+ # λ“œλ‘­λ‹€μš΄μ—μ„œ μ„Έμ…˜ ID μΆ”μΆœ
812
+ if "..." in session_id:
813
+ session_id = session_id.split("...")[0]
814
+
815
+ session = NovelDatabase.get_session(session_id)
816
+ if not session:
817
+ yield "", "", "❌ μ„Έμ…˜μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.", None
818
+ return
819
+
820
+ # process_queryλ₯Ό 톡해 재개
821
+ yield from process_query(session['user_query'], session['language'], session_id)
822
+
823
+ def download_novel(novel_text: str, format_type: str, language: str, session_id: str) -> Optional[str]:
824
+ """μ†Œμ„€ λ‹€μš΄λ‘œλ“œ 파일 생성"""
825
+ if not novel_text or not session_id:
826
+ return None
827
+
828
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
829
+ filename = f"novel_{session_id[:8]}_{timestamp}"
830
+
831
+ try:
832
+ if format_type == "DOCX" and DOCX_AVAILABLE:
833
+ return export_to_docx(novel_text, filename, language)
834
+ else:
835
+ return export_to_txt(novel_text, filename)
836
+ except Exception as e:
837
+ logger.error(f"파일 생성 μ‹€νŒ¨: {e}")
838
+ return None
839
+
840
+ def format_stages_display(stages: List[Dict]) -> str:
841
+ """단계별 μ§„ν–‰ 상황 λ§ˆν¬λ‹€μš΄ ν¬λ§·νŒ…"""
842
+ markdown = "## 🎬 μ§„ν–‰ 상황\n\n"
843
+ for i, stage in enumerate(stages):
844
+ status_icon = "βœ…" if stage['status'] == 'complete' else "πŸ”„" if stage['status'] == 'active' else "⏳"
845
+ markdown += f"{status_icon} **{stage['name']}**"
846
+ if stage.get('consistency_score', 0) > 0:
847
+ markdown += f" (일관성: {stage['consistency_score']:.1f}/10)"
848
+ markdown += "\n"
849
+ if stage['content']:
850
+ preview = stage['content'][:200] + "..." if len(stage['content']) > 200 else stage['content']
851
+ markdown += f"> {preview}\n\n"
852
+ return markdown
853
+
854
+ def format_novel_display(novel_text: str) -> str:
855
+ """μ†Œμ„€ λ‚΄μš© λ§ˆν¬λ‹€μš΄ ν¬λ§·νŒ…"""
856
+ if not novel_text:
857
+ return "아직 μ™„μ„±λœ λ‚΄μš©μ΄ μ—†μŠ΅λ‹ˆλ‹€."
858
+
859
+ # νŽ˜μ΄μ§€ ꡬ뢄 μΆ”κ°€
860
+ pages = novel_text.split('\n\n')
861
+ formatted = "# πŸ“– μ™„μ„±λœ μ†Œμ„€\n\n"
862
+
863
+ for i, page in enumerate(pages):
864
+ if page.strip():
865
+ formatted += f"### νŽ˜μ΄μ§€ {i+1}\n\n{page}\n\n---\n\n"
866
+
867
+ return formatted
868
+
869
+ def export_to_docx(content: str, filename: str, language: str) -> str:
870
+ """DOCX 파일둜 내보내기"""
871
+ doc = Document()
872
+
873
+ # 제λͺ© μΆ”κ°€
874
+ title = doc.add_heading('AI ν˜‘μ—… μ†Œμ„€', 0)
875
+ title.alignment = WD_ALIGN_PARAGRAPH.CENTER
876
+
877
+ # 메타데이터
878
+ doc.add_paragraph(f"생성일: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
879
+ doc.add_paragraph(f"μ–Έμ–΄: {language}")
880
+ doc.add_page_break()
881
+
882
+ # λ³Έλ¬Έ μΆ”κ°€
883
+ paragraphs = content.split('\n\n')
884
+ for para in paragraphs:
885
+ if para.strip():
886
+ doc.add_paragraph(para.strip())
887
+
888
+ # 파일 μ €μž₯
889
+ filepath = f"{filename}.docx"
890
+ doc.save(filepath)
891
+ return filepath
892
+
893
+ def export_to_txt(content: str, filename: str) -> str:
894
+ """TXT 파일둜 내보내기"""
895
+ filepath = f"{filename}.txt"
896
+ with open(filepath, 'w', encoding='utf-8') as f:
897
+ f.write(content)
898
+ return filepath
899
 
900
 
901
  # CSS μŠ€νƒ€μΌ