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 ์Šคํƒ€์ผ