openfree commited on
Commit
295a976
ยท
verified ยท
1 Parent(s): f4edc7e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -185
app.py CHANGED
@@ -369,8 +369,8 @@ class NovelDatabase:
369
  return cursor.fetchall()
370
 
371
  @staticmethod
372
- def get_all_writer_content(session_id: str) -> str:
373
- """๋ชจ๋“  ์ž‘๊ฐ€์˜ ์ˆ˜์ •๋ณธ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์™€์„œ ํ•ฉ์น˜๊ธฐ - 10๋ช… ์ž‘๊ฐ€"""
374
  with NovelDatabase.get_db() as conn:
375
  cursor = conn.cursor()
376
 
@@ -378,7 +378,13 @@ class NovelDatabase:
378
  writer_count = 0
379
  total_word_count = 0
380
 
381
- for stage_num in WRITER_REVISION_STAGES:
 
 
 
 
 
 
382
  cursor.execute('''
383
  SELECT content, stage_name, word_count FROM stages
384
  WHERE session_id = ? AND stage_number = ?
@@ -399,11 +405,15 @@ class NovelDatabase:
399
  logger.info(f"Writer {writer_count} (stage {stage_num}): {word_count} words")
400
 
401
  full_content = '\n\n'.join(all_content)
402
- logger.info(f"Total: {writer_count} writers, {total_word_count} words")
403
 
404
- # 10๋ช… ์ž‘๊ฐ€ * 1,450 ํ‰๊ท  = 14,500 ๋‹จ์–ด ๋ชฉํ‘œ
405
- if total_word_count < 12000:
406
- logger.warning(f"Content too short! Only {total_word_count} words instead of ~14,500")
 
 
 
 
 
407
 
408
  return full_content
409
 
@@ -1026,7 +1036,7 @@ Write a revision reflecting:
1026
  Present the revised final version. Never use page markers.
1027
  You MUST maintain 1,400-1,500 words."""
1028
 
1029
- def create_test_writer_complete_prompt(self, director_plan: str, writer1_content: str = "", language: str = "English") -> str:
1030
  """Test mode - Writer 10 writes remaining novel (chapters 2-10)"""
1031
  if language == "Korean":
1032
  return f"""[ํ…Œ์ŠคํŠธ ๋ชจ๋“œ] ๋‹น์‹ ์€ ๋‚˜๋จธ์ง€ 9๊ฐœ ์ฑ•ํ„ฐ(Chapter 2-10)๋ฅผ ์ž‘์„ฑํ•˜๋Š” ํŠน๋ณ„ ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค.
@@ -1142,7 +1152,7 @@ Write Chapters 2-10 now."""
1142
 
1143
  # ์ž‘์„ฑ์ž๋“ค์—๊ฒŒ๋Š” ์ ์ ˆํ•œ ํ† ํฐ ํ• ๋‹น
1144
  if role == "writer10" and stage_info and stage_info.get('test_mode'):
1145
- max_tokens = 30000 # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ: ์ „์ฒด ์†Œ์„ค ์ž‘์„ฑ (์ถฉ๋ถ„ํ•œ ํ† ํฐ)
1146
  temperature = 0.8
1147
  top_p = 0.95
1148
  elif role.startswith("writer"):
@@ -1282,7 +1292,7 @@ Write Chapters 2-10 now."""
1282
  prompts = {
1283
  "director": "๋‹น์‹ ์€ 30ํŽ˜์ด์ง€ ์ค‘ํŽธ ์†Œ์„ค์„ ๊ธฐํšํ•˜๊ณ  ๊ฐ๋…ํ•˜๋Š” ๋ฌธํ•™ ๊ฐ๋…์ž์ž…๋‹ˆ๋‹ค. ์ฒด๊ณ„์ ์ด๊ณ  ์ฐฝ์˜์ ์ธ ์Šคํ† ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค.",
1284
  "critic": "๋‹น์‹ ์€ ๋‚ ์นด๋กœ์šด ํ†ต์ฐฐ๋ ฅ์„ ๊ฐ€์ง„ ๋ฌธํ•™ ๋น„ํ‰๊ฐ€์ž…๋‹ˆ๋‹ค. ๊ฑด์„ค์ ์ด๊ณ  ๊ตฌ์ฒด์ ์ธ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.",
1285
- "writer10": "[ํ…Œ์ŠคํŠธ ๋ชจ๋“œ] ๋‹น์‹ ์€ ์ฑ•ํ„ฐ 2-10์„ ์ž‘์„ฑํ•˜๋Š” ํŠน๋ณ„ ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. 9๊ฐœ ์ฑ•ํ„ฐ๋กœ ๊ตฌ์„ฑ๋œ ๋‚˜๋จธ์ง€ ์†Œ์„ค์„ ์ž‘์„ฑํ•˜์„ธ์š”."
1286
  }
1287
 
1288
  # 10๋ช…์˜ ์ž‘๊ฐ€ ํ”„๋กฌํ”„ํŠธ
@@ -1307,7 +1317,7 @@ Write Chapters 2-10 now."""
1307
  prompts = {
1308
  "director": "You are a literary director planning and supervising a 30-page novella. You create systematic and creative story structures.",
1309
  "critic": "You are a literary critic with sharp insights. You provide constructive and specific feedback.",
1310
- "writer10": "[TEST MODE] You are a special writer creating chapters 2-10. Write the remaining novel organized into 9 chapters."
1311
  }
1312
 
1313
  # 10 writer prompts
@@ -1330,7 +1340,7 @@ Write Chapters 2-10 now."""
1330
  return prompts
1331
 
1332
  def get_test_response(self, role: str, language: str) -> str:
1333
- """Get test response based on role"""
1334
  if language == "Korean":
1335
  return self.get_korean_test_response(role)
1336
  else:
@@ -1626,111 +1636,16 @@ Seoyeon leaned back in her chair and closed her eyes for a moment. Fatigue penet
1626
  # Mark stage complete and save to DB
1627
  stages[stage_idx]["status"] = "complete"
1628
 
1629
- # Test mode์—์„œ writer1 ์ถ”๊ฐ€ ์ €์žฅ์€ ํ•˜์ง€ ์•Š์Œ (์ด๋ฏธ ์ •์ƒ ํ”„๋กœ์„ธ์Šค๋กœ ์ €์žฅ๋จ)
1630
- if test_quick_mode and role == "writer10":
1631
- logger.info(f"Test mode: Processing remaining chapters 2-10 ({len(stage_content)} chars)")
1632
-
1633
- # ์ „์ฒด ๋‚ด์šฉ์„ 9๊ฐœ ์ฑ•ํ„ฐ๋กœ ๋ถ„ํ• ํ•˜์—ฌ ์ €์žฅ (Chapter 2-10)
1634
- # [Chapter ์ˆซ์ž] ํŒจํ„ด์œผ๋กœ ๋ถ„ํ• 
1635
- chapter_pattern = r'\[(?i)chapter\s+\d+\]'
1636
-
1637
- # ์ฑ•ํ„ฐ ๋งˆ์ปค๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ
1638
- chapter_markers = re.findall(chapter_pattern, stage_content, re.IGNORECASE)
1639
- logger.info(f"Found {len(chapter_markers)} chapter markers")
1640
-
1641
- if len(chapter_markers) >= 9:
1642
- # ์ฑ•ํ„ฐ ๋งˆ์ปค๋กœ ๋ถ„ํ• 
1643
- parts = re.split(chapter_pattern, stage_content, flags=re.IGNORECASE)
1644
- chapters = []
1645
-
1646
- # ์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„์€ ์ฑ•ํ„ฐ ์ด์ „ ๋‚ด์šฉ์ด๋ฏ€๋กœ ์ œ์™ธ
1647
- for i in range(1, len(parts)):
1648
- content = parts[i].strip()
1649
- if content:
1650
- chapters.append(content)
1651
- else:
1652
- # ์ฑ•ํ„ฐ ๋งˆ์ปค๊ฐ€ ๋ถ€์กฑํ•˜๋ฉด ์ „์ฒด๋ฅผ ๊ท ๋“ฑ ๋ถ„ํ• 
1653
- logger.warning(f"Not enough chapter markers, splitting content evenly")
1654
-
1655
- # ๋จผ์ € ๋‹จ๋ฝ์œผ๋กœ ๋ถ„ํ•  ์‹œ๋„
1656
- paragraphs = stage_content.split('\n\n')
1657
- paragraphs = [p.strip() for p in paragraphs if p.strip()]
1658
-
1659
- if len(paragraphs) >= 18: # ์ถฉ๋ถ„ํ•œ ๋‹จ๋ฝ์ด ์žˆ์œผ๋ฉด
1660
- para_per_chapter = len(paragraphs) // 9
1661
- chapters = []
1662
- for i in range(9):
1663
- start_idx = i * para_per_chapter
1664
- end_idx = (i + 1) * para_per_chapter if i < 8 else len(paragraphs)
1665
- chapter_content = '\n\n'.join(paragraphs[start_idx:end_idx])
1666
- chapters.append(chapter_content)
1667
- else:
1668
- # ๋‹จ๋ฝ๋„ ๋ถ€์กฑํ•˜๋ฉด ๋‹จ์–ด๋กœ ๊ท ๋“ฑ ๋ถ„ํ• 
1669
- words = stage_content.split()
1670
- words_per_chapter = max(len(words) // 9, 100) # ์ตœ์†Œ 100๋‹จ์–ด
1671
- chapters = []
1672
- for i in range(9):
1673
- start_idx = i * words_per_chapter
1674
- end_idx = (i + 1) * words_per_chapter if i < 8 else len(words)
1675
- chapter_words = words[start_idx:end_idx]
1676
- chapters.append(' '.join(chapter_words))
1677
-
1678
- # 9๊ฐœ ์ฑ•ํ„ฐ๋กœ ์ œํ•œ
1679
- chapters = chapters[:9]
1680
-
1681
- # ๋ถ€์กฑํ•œ ์ฑ•ํ„ฐ๋Š” ๋งˆ์ง€๋ง‰ ์ฑ•ํ„ฐ๋ฅผ ๋ถ„ํ• ํ•ด์„œ ์ฑ„์šฐ๊ธฐ
1682
- while len(chapters) < 9:
1683
- if chapters and len(chapters[-1].split()) > 200:
1684
- # ๋งˆ์ง€๋ง‰ ์ฑ•ํ„ฐ๋ฅผ ๋ฐ˜์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ
1685
- last_chapter = chapters[-1]
1686
- words = last_chapter.split()
1687
- mid = len(words) // 2
1688
- chapters[-1] = ' '.join(words[:mid])
1689
- chapters.append(' '.join(words[mid:]))
1690
- else:
1691
- # ๋„ˆ๋ฌด ์งง์œผ๋ฉด ๋นˆ ์ฑ•ํ„ฐ ์ถ”๊ฐ€
1692
- chapters.append(f"[Chapter {len(chapters)+2} - Content pending]")
1693
-
1694
- # ๊ฐ ์ฑ•ํ„ฐ๋ฅผ writer 2-10์œผ๋กœ ์ €์žฅ
1695
- total_saved_words = 0
1696
- for i, chapter_content in enumerate(chapters[:9], 2): # Start from writer 2
1697
- writer_stage_num = (i-1) * 3 + 5 # 8, 11, 14, 17, 20, 23, 26, 29, 32
1698
-
1699
- # ์ฑ•ํ„ฐ ํ—ค๋” ์ถ”๊ฐ€
1700
- full_chapter_content = f"[Chapter {i}]\n\n{chapter_content}"
1701
-
1702
- NovelDatabase.save_stage(
1703
- self.current_session_id,
1704
- writer_stage_num,
1705
- f"Writer {i}: Revision",
1706
- f"writer{i}",
1707
- full_chapter_content,
1708
- "complete"
1709
- )
1710
- word_count = len(chapter_content.split())
1711
- total_saved_words += word_count
1712
- logger.info(f"โœ… Test mode: Saved chapter {i} as writer {i} ({word_count} words)")
1713
-
1714
- logger.info(f"โœ… Test mode: Total saved words for chapters 2-10: {total_saved_words}")
1715
-
1716
- # ์›๋ณธ๋„ ์ €์žฅ (๋ฐฑ์—…์šฉ)
1717
- NovelDatabase.save_stage(
1718
- self.current_session_id,
1719
- stage_idx,
1720
- stage_name,
1721
- role,
1722
- stage_content,
1723
- "complete"
1724
- )
1725
- else:
1726
- NovelDatabase.save_stage(
1727
- self.current_session_id,
1728
- stage_idx,
1729
- stage_name,
1730
- role,
1731
- stage_content,
1732
- "complete"
1733
- )
1734
 
1735
  # Auto-save notification
1736
  if role.startswith("writer"):
@@ -1751,7 +1666,7 @@ Seoyeon leaned back in her chair and closed her eyes for a moment. Fatigue penet
1751
  logger.error(f"Final novel too short! Only {verification['total_words']} words")
1752
 
1753
  # Get complete novel from DB
1754
- complete_novel = NovelDatabase.get_all_writer_content(self.current_session_id)
1755
 
1756
  # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ๋ฉด ์™„๋ฃŒ ์ƒํƒœ ์—…๋ฐ์ดํŠธ
1757
  if test_quick_mode and self.current_session_id:
@@ -1762,7 +1677,7 @@ Seoyeon leaned back in her chair and closed her eyes for a moment. Fatigue penet
1762
 
1763
  # Final yield - ํ™”๋ฉด์—๋Š” ์™„๋ฃŒ ๋ฉ”์‹œ์ง€๋งŒ ํ‘œ์‹œ
1764
  if test_quick_mode:
1765
- final_message = f"โœ… [TEST MODE] Novel complete! {len(complete_novel.split())} words total. Click Download to save."
1766
  else:
1767
  final_message = f"โœ… Novel complete! {len(complete_novel.split())} words total. Click Download to save."
1768
  yield final_message, stages
@@ -1850,15 +1765,6 @@ Write a high-quality complete novel."""
1850
  return self.create_director_revision_prompt(
1851
  stages[0]["content"], stages[1]["content"], language)
1852
 
1853
- # Test mode - Writer 10 writes remaining chapters
1854
- elif test_mode and role == "writer10":
1855
- final_plan = stages[2]["content"] # Director's final plan
1856
- # Get Writer 1's content from previous stage
1857
- writer1_content = ""
1858
- if len(stages) > 5: # Writer 1 revision is at index 5
1859
- writer1_content = stages[5]["content"]
1860
- return self.create_test_writer_complete_prompt(final_plan, writer1_content, language)
1861
-
1862
  # Writer stages
1863
  elif role.startswith("writer"):
1864
  writer_num = int(role.replace("writer", ""))
@@ -2039,6 +1945,14 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2039
  stages = NovelDatabase.get_stages(session_id)
2040
  logger.info(f"Found {len(stages)} stages in database")
2041
 
 
 
 
 
 
 
 
 
2042
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
2043
 
2044
  if format == "DOCX" and DOCX_AVAILABLE:
@@ -2047,7 +1961,7 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2047
 
2048
  # ์ œ๋ชฉ ํŽ˜์ด์ง€
2049
  title_para = doc.add_paragraph()
2050
- title_run = title_para.add_run('AI Collaborative Novel')
2051
  title_run.font.size = Pt(24)
2052
  title_run.font.bold = True
2053
  title_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
@@ -2076,15 +1990,9 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2076
  # ๊ฐ ์ž‘๊ฐ€์˜ ์ˆ˜์ •๋ณธ๋งŒ ์ˆ˜์ง‘
2077
  writer_contents = []
2078
 
2079
- # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ ์ฒดํฌ
2080
- is_test_mode = False
2081
- for stage in stages:
2082
- if stage['role'] == 'writer10' and stage['stage_name'] and 'TEST' in stage['stage_name']:
2083
- is_test_mode = True
2084
- logger.info("Test mode detected")
2085
- break
2086
 
2087
- # Writer stages ๊ฒ€์ƒ‰
2088
  for stage in stages:
2089
  logger.debug(f"Checking stage: role={stage['role']}, name={stage['stage_name']}, status={stage['status']}")
2090
 
@@ -2092,6 +2000,8 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2092
  # ์ž‘๊ฐ€ ๋ฒˆํ˜ธ ์ถ”์ถœ
2093
  try:
2094
  writer_num_from_role = int(stage['role'].replace('writer', ''))
 
 
2095
  logger.debug(f"Found writer {writer_num_from_role} revision")
2096
  except:
2097
  logger.warning(f"Could not extract writer number from role: {stage['role']}")
@@ -2115,42 +2025,9 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2115
  })
2116
  logger.info(f"Added writer {writer_count}: {word_count} words")
2117
 
2118
- if not writer_contents:
2119
- logger.error("No writer content found in database!")
2120
-
2121
- # ๋””๋ฒ„๊ทธ๋ฅผ ์œ„ํ•ด ๋ชจ๋“  stages ์ถœ๋ ฅ
2122
- for stage in stages:
2123
- logger.debug(f"Stage {stage['stage_number']}: role={stage['role']}, name={stage['stage_name']}")
2124
-
2125
- # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ์ธ ๊ฒฝ์šฐ writer10 ๋‚ด์šฉ ์ง์ ‘ ์‚ฌ์šฉ
2126
- if is_test_mode:
2127
- logger.info("Attempting to use writer10 content directly")
2128
- for stage in stages:
2129
- if stage['role'] == 'writer10':
2130
- content = stage['content'] or ""
2131
- if content:
2132
- doc.add_heading('Complete Novel (Test Mode)', 1)
2133
- doc.add_paragraph(f'Total length: {len(content)} characters')
2134
- doc.add_page_break()
2135
-
2136
- paragraphs = content.split('\n\n')
2137
- for para_text in paragraphs:
2138
- if para_text.strip():
2139
- para = doc.add_paragraph(para_text.strip())
2140
- para.style.font.size = Pt(11)
2141
-
2142
- # Save
2143
- temp_dir = tempfile.gettempdir()
2144
- safe_filename = re.sub(r'[^\w\s-]', '', session['user_query'][:30]).strip()
2145
- filename = f"Novel_TestMode_{safe_filename}_{timestamp}.docx"
2146
- filepath = os.path.join(temp_dir, filename)
2147
- doc.save(filepath)
2148
- logger.info(f"DOCX saved (test mode): {filepath}")
2149
- return filepath
2150
-
2151
  # ํ†ต๊ณ„ ํŽ˜์ด์ง€
2152
  doc.add_heading('Novel Statistics', 1)
2153
- doc.add_paragraph(f'Mode: {"Test Mode" if is_test_mode else "Normal Mode"}')
2154
  doc.add_paragraph(f'Total Writers: {writer_count}')
2155
  doc.add_paragraph(f'Total Words: {total_words:,}')
2156
  doc.add_paragraph(f'Estimated Pages: ~{total_words/500:.0f}')
@@ -2198,7 +2075,8 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2198
  # Save
2199
  temp_dir = tempfile.gettempdir()
2200
  safe_filename = re.sub(r'[^\w\s-]', '', session['user_query'][:30]).strip()
2201
- filename = f"Novel_Complete_{safe_filename}_{timestamp}.docx"
 
2202
  filepath = os.path.join(temp_dir, filename)
2203
  doc.save(filepath)
2204
 
@@ -2208,16 +2086,18 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2208
  # TXT format
2209
  temp_dir = tempfile.gettempdir()
2210
  safe_filename = re.sub(r'[^\w\s-]', '', session['user_query'][:30]).strip()
2211
- filename = f"Novel_Complete_{safe_filename}_{timestamp}.txt"
 
2212
  filepath = os.path.join(temp_dir, filename)
2213
 
2214
  with open(filepath, 'w', encoding='utf-8') as f:
2215
  f.write("="*60 + "\n")
2216
- f.write("AI COLLABORATIVE NOVEL - COMPLETE VERSION\n")
2217
  f.write("="*60 + "\n")
2218
  f.write(f"Theme: {session['user_query']}\n")
2219
  f.write(f"Language: {session['language']}\n")
2220
  f.write(f"Created: {datetime.now()}\n")
 
2221
  f.write("="*60 + "\n\n")
2222
 
2223
  total_words = 0
@@ -2226,6 +2106,14 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2226
  # ๊ฐ ์ž‘๊ฐ€์˜ ์ˆ˜์ •๋ณธ๋งŒ ์ถœ๋ ฅ
2227
  for stage in stages:
2228
  if stage['role'] and stage['role'].startswith('writer') and stage['stage_name'] and 'Revision' in stage['stage_name']:
 
 
 
 
 
 
 
 
2229
  writer_count += 1
2230
  content = stage['content'] or ""
2231
  # ํŽ˜์ด์ง€ ๋งˆํฌ ์ œ๊ฑฐ
@@ -2244,15 +2132,6 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2244
  f.write(content)
2245
  f.write("\n\n")
2246
 
2247
- if writer_count == 0:
2248
- # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ ๋ฐฑ์—…
2249
- f.write("\n[No writer content found - displaying raw stages]\n\n")
2250
- for stage in stages:
2251
- if stage['content']:
2252
- f.write(f"\n--- {stage['stage_name']} ---\n")
2253
- f.write(stage['content'])
2254
- f.write("\n\n")
2255
-
2256
  f.write(f"\n{'='*60}\n")
2257
  f.write(f"TOTAL: {writer_count} writers, {total_words:,} words\n")
2258
  f.write(f"{'='*60}\n")
@@ -2372,9 +2251,9 @@ def create_interface():
2372
 
2373
  # Test mode checkbox
2374
  test_mode_check = gr.Checkbox(
2375
- label="๐Ÿงช Test Mode (Quick: Director + Writer 1 + Writer 10) / ํ…Œ์ŠคํŠธ ๋ชจ๋“œ (๋น ๋ฅธ ์ƒ์„ฑ)",
2376
  value=False,
2377
- info="Complete Writer 1 process, then Writer 10 creates chapters 2-10 / ์ž‘๊ฐ€ 1 ์ „์ฒด ๊ณผ์ • ํ›„ ์ž‘๊ฐ€ 10์ด ์ฑ•ํ„ฐ 2-10 ์ž‘์„ฑ"
2378
  )
2379
 
2380
  # Web search status indicator
 
369
  return cursor.fetchall()
370
 
371
  @staticmethod
372
+ def get_all_writer_content(session_id: str, test_mode: bool = False) -> str:
373
+ """๋ชจ๋“  ์ž‘๊ฐ€์˜ ์ˆ˜์ •๋ณธ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์™€์„œ ํ•ฉ์น˜๊ธฐ - ํ…Œ์ŠคํŠธ ๋ชจ๋“œ์—์„œ๋Š” 1,2๋งŒ"""
374
  with NovelDatabase.get_db() as conn:
375
  cursor = conn.cursor()
376
 
 
378
  writer_count = 0
379
  total_word_count = 0
380
 
381
+ # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ์—์„œ๋Š” writer 1,2๋งŒ (stage 5, 8)
382
+ if test_mode:
383
+ writer_stages_to_check = [5, 8] # Writer 1,2์˜ ์ˆ˜์ •๋ณธ
384
+ else:
385
+ writer_stages_to_check = WRITER_REVISION_STAGES
386
+
387
+ for stage_num in writer_stages_to_check:
388
  cursor.execute('''
389
  SELECT content, stage_name, word_count FROM stages
390
  WHERE session_id = ? AND stage_number = ?
 
405
  logger.info(f"Writer {writer_count} (stage {stage_num}): {word_count} words")
406
 
407
  full_content = '\n\n'.join(all_content)
 
408
 
409
+ if test_mode:
410
+ logger.info(f"Test mode - Total: {writer_count} writers, {total_word_count} words")
411
+ if total_word_count < 2800: # 2 ์ž‘๊ฐ€ * 1,400 = 2,800 ์ตœ์†Œ
412
+ logger.warning(f"Test mode content short! Only {total_word_count} words instead of ~3,000")
413
+ else:
414
+ logger.info(f"Total: {writer_count} writers, {total_word_count} words")
415
+ if total_word_count < 12000:
416
+ logger.warning(f"Content too short! Only {total_word_count} words instead of ~14,500")
417
 
418
  return full_content
419
 
 
1036
  Present the revised final version. Never use page markers.
1037
  You MUST maintain 1,400-1,500 words."""
1038
 
1039
+ def create_test_writer_remaining_prompt(self, director_plan: str, writer1_content: str, language: str) -> str:
1040
  """Test mode - Writer 10 writes remaining novel (chapters 2-10)"""
1041
  if language == "Korean":
1042
  return f"""[ํ…Œ์ŠคํŠธ ๋ชจ๋“œ] ๋‹น์‹ ์€ ๋‚˜๋จธ์ง€ 9๊ฐœ ์ฑ•ํ„ฐ(Chapter 2-10)๋ฅผ ์ž‘์„ฑํ•˜๋Š” ํŠน๋ณ„ ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค.
 
1152
 
1153
  # ์ž‘์„ฑ์ž๋“ค์—๊ฒŒ๋Š” ์ ์ ˆํ•œ ํ† ํฐ ํ• ๋‹น
1154
  if role == "writer10" and stage_info and stage_info.get('test_mode'):
1155
+ max_tokens = 30000 # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ: 9๊ฐœ ์ฑ•ํ„ฐ ์ž‘์„ฑ (์ถฉ๋ถ„ํ•œ ํ† ํฐ)
1156
  temperature = 0.8
1157
  top_p = 0.95
1158
  elif role.startswith("writer"):
 
1292
  prompts = {
1293
  "director": "๋‹น์‹ ์€ 30ํŽ˜์ด์ง€ ์ค‘ํŽธ ์†Œ์„ค์„ ๊ธฐํšํ•˜๊ณ  ๊ฐ๋…ํ•˜๋Š” ๋ฌธํ•™ ๊ฐ๋…์ž์ž…๋‹ˆ๋‹ค. ์ฒด๊ณ„์ ์ด๊ณ  ์ฐฝ์˜์ ์ธ ์Šคํ† ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค.",
1294
  "critic": "๋‹น์‹ ์€ ๋‚ ์นด๋กœ์šด ํ†ต์ฐฐ๋ ฅ์„ ๊ฐ€์ง„ ๋ฌธํ•™ ๋น„ํ‰๊ฐ€์ž…๋‹ˆ๋‹ค. ๊ฑด์„ค์ ์ด๊ณ  ๊ตฌ์ฒด์ ์ธ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.",
1295
+ "writer10": "[ํ…Œ์ŠคํŠธ ๋ชจ๋“œ] ๋‹น์‹ ์€ ์ฑ•ํ„ฐ 2-10์„ ์ž‘์„ฑํ•˜๋Š” ํŠน๋ณ„ ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. 9๊ฐœ ์ฑ•ํ„ฐ๋กœ ๊ตฌ์„ฑ๋œ ๋‚˜๋จธ์ง€ ์†Œ์„ค์„ ์ž‘์„ฑํ•˜์„ธ์š”. ๊ฐ ์ฑ•ํ„ฐ๋Š” ๋ฐ˜๋“œ์‹œ 1,400-1,500๋‹จ์–ด๋กœ ์ž‘์„ฑํ•˜์„ธ์š”."
1296
  }
1297
 
1298
  # 10๋ช…์˜ ์ž‘๊ฐ€ ํ”„๋กฌํ”„ํŠธ
 
1317
  prompts = {
1318
  "director": "You are a literary director planning and supervising a 30-page novella. You create systematic and creative story structures.",
1319
  "critic": "You are a literary critic with sharp insights. You provide constructive and specific feedback.",
1320
+ "writer10": "[TEST MODE] You are a special writer creating chapters 2-10. Write the remaining novel organized into 9 chapters. Each chapter MUST be 1,400-1,500 words."
1321
  }
1322
 
1323
  # 10 writer prompts
 
1340
  return prompts
1341
 
1342
  def get_test_response(self, role: str, language: str) -> str:
1343
+ """Get test response based on role - updated for writer10 chapters 2-10"""
1344
  if language == "Korean":
1345
  return self.get_korean_test_response(role)
1346
  else:
 
1636
  # Mark stage complete and save to DB
1637
  stages[stage_idx]["status"] = "complete"
1638
 
1639
+ # Test mode์—์„œ๋Š” writer1๊ณผ writer2๋งŒ ์ฒ˜๋ฆฌ
1640
+ # writer10 ๊ด€๋ จ ํŠน๋ณ„ ์ฒ˜๋ฆฌ ์ œ๊ฑฐ - ์ผ๋ฐ˜ ํ”„๋กœ์„ธ์Šค์™€ ๋™์ผ
1641
+ NovelDatabase.save_stage(
1642
+ self.current_session_id,
1643
+ stage_idx,
1644
+ stage_name,
1645
+ role,
1646
+ stage_content,
1647
+ "complete"
1648
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1649
 
1650
  # Auto-save notification
1651
  if role.startswith("writer"):
 
1666
  logger.error(f"Final novel too short! Only {verification['total_words']} words")
1667
 
1668
  # Get complete novel from DB
1669
+ complete_novel = NovelDatabase.get_all_writer_content(self.current_session_id, test_quick_mode)
1670
 
1671
  # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ๋ฉด ์™„๋ฃŒ ์ƒํƒœ ์—…๋ฐ์ดํŠธ
1672
  if test_quick_mode and self.current_session_id:
 
1677
 
1678
  # Final yield - ํ™”๋ฉด์—๋Š” ์™„๋ฃŒ ๋ฉ”์‹œ์ง€๋งŒ ํ‘œ์‹œ
1679
  if test_quick_mode:
1680
+ final_message = f"โœ… [TEST MODE] Novel complete! 2 chapters, {len(complete_novel.split())} words total. Click Download to save."
1681
  else:
1682
  final_message = f"โœ… Novel complete! {len(complete_novel.split())} words total. Click Download to save."
1683
  yield final_message, stages
 
1765
  return self.create_director_revision_prompt(
1766
  stages[0]["content"], stages[1]["content"], language)
1767
 
 
 
 
 
 
 
 
 
 
1768
  # Writer stages
1769
  elif role.startswith("writer"):
1770
  writer_num = int(role.replace("writer", ""))
 
1945
  stages = NovelDatabase.get_stages(session_id)
1946
  logger.info(f"Found {len(stages)} stages in database")
1947
 
1948
+ # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ ๊ฐ์ง€ - Writer 2๊นŒ์ง€๋งŒ ์žˆ๊ณ  ์ „์ฒด stage๊ฐ€ 9๊ฐœ๋ฉด ํ…Œ์ŠคํŠธ ๋ชจ๋“œ
1949
+ is_test_mode = False
1950
+ has_writer2 = any(stage['role'] == 'writer2' for stage in stages)
1951
+ has_writer3 = any(stage['role'] == 'writer3' for stage in stages)
1952
+ if has_writer2 and not has_writer3 and len(stages) <= 10:
1953
+ is_test_mode = True
1954
+ logger.info("Test mode detected - will export 2 chapters only")
1955
+
1956
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
1957
 
1958
  if format == "DOCX" and DOCX_AVAILABLE:
 
1961
 
1962
  # ์ œ๋ชฉ ํŽ˜์ด์ง€
1963
  title_para = doc.add_paragraph()
1964
+ title_run = title_para.add_run('AI Collaborative Novel' + (' - Test Mode' if is_test_mode else ''))
1965
  title_run.font.size = Pt(24)
1966
  title_run.font.bold = True
1967
  title_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
 
1990
  # ๊ฐ ์ž‘๊ฐ€์˜ ์ˆ˜์ •๋ณธ๋งŒ ์ˆ˜์ง‘
1991
  writer_contents = []
1992
 
1993
+ # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ์—์„œ๋Š” writer 1,2๋งŒ ๊ฒ€์ƒ‰
1994
+ max_writer = 2 if is_test_mode else 10
 
 
 
 
 
1995
 
 
1996
  for stage in stages:
1997
  logger.debug(f"Checking stage: role={stage['role']}, name={stage['stage_name']}, status={stage['status']}")
1998
 
 
2000
  # ์ž‘๊ฐ€ ๋ฒˆํ˜ธ ์ถ”์ถœ
2001
  try:
2002
  writer_num_from_role = int(stage['role'].replace('writer', ''))
2003
+ if is_test_mode and writer_num_from_role > max_writer:
2004
+ continue # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ์—์„œ๋Š” writer 3 ์ด์ƒ ๋ฌด์‹œ
2005
  logger.debug(f"Found writer {writer_num_from_role} revision")
2006
  except:
2007
  logger.warning(f"Could not extract writer number from role: {stage['role']}")
 
2025
  })
2026
  logger.info(f"Added writer {writer_count}: {word_count} words")
2027
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2028
  # ํ†ต๊ณ„ ํŽ˜์ด์ง€
2029
  doc.add_heading('Novel Statistics', 1)
2030
+ doc.add_paragraph(f'Mode: {"Test Mode (2 chapters)" if is_test_mode else "Full Mode (10 chapters)"}')
2031
  doc.add_paragraph(f'Total Writers: {writer_count}')
2032
  doc.add_paragraph(f'Total Words: {total_words:,}')
2033
  doc.add_paragraph(f'Estimated Pages: ~{total_words/500:.0f}')
 
2075
  # Save
2076
  temp_dir = tempfile.gettempdir()
2077
  safe_filename = re.sub(r'[^\w\s-]', '', session['user_query'][:30]).strip()
2078
+ mode_suffix = "_TestMode" if is_test_mode else "_Complete"
2079
+ filename = f"Novel{mode_suffix}_{safe_filename}_{timestamp}.docx"
2080
  filepath = os.path.join(temp_dir, filename)
2081
  doc.save(filepath)
2082
 
 
2086
  # TXT format
2087
  temp_dir = tempfile.gettempdir()
2088
  safe_filename = re.sub(r'[^\w\s-]', '', session['user_query'][:30]).strip()
2089
+ mode_suffix = "_TestMode" if is_test_mode else "_Complete"
2090
+ filename = f"Novel{mode_suffix}_{safe_filename}_{timestamp}.txt"
2091
  filepath = os.path.join(temp_dir, filename)
2092
 
2093
  with open(filepath, 'w', encoding='utf-8') as f:
2094
  f.write("="*60 + "\n")
2095
+ f.write(f"AI COLLABORATIVE NOVEL - {'TEST MODE' if is_test_mode else 'COMPLETE VERSION'}\n")
2096
  f.write("="*60 + "\n")
2097
  f.write(f"Theme: {session['user_query']}\n")
2098
  f.write(f"Language: {session['language']}\n")
2099
  f.write(f"Created: {datetime.now()}\n")
2100
+ f.write(f"Mode: {'Test Mode (2 chapters)' if is_test_mode else 'Full Mode (10 chapters)'}\n")
2101
  f.write("="*60 + "\n\n")
2102
 
2103
  total_words = 0
 
2106
  # ๊ฐ ์ž‘๊ฐ€์˜ ์ˆ˜์ •๋ณธ๋งŒ ์ถœ๋ ฅ
2107
  for stage in stages:
2108
  if stage['role'] and stage['role'].startswith('writer') and stage['stage_name'] and 'Revision' in stage['stage_name']:
2109
+ # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ์—์„œ๋Š” writer 3 ์ด์ƒ ๋ฌด์‹œ
2110
+ try:
2111
+ writer_num = int(stage['role'].replace('writer', ''))
2112
+ if is_test_mode and writer_num > 2:
2113
+ continue
2114
+ except:
2115
+ continue
2116
+
2117
  writer_count += 1
2118
  content = stage['content'] or ""
2119
  # ํŽ˜์ด์ง€ ๋งˆํฌ ์ œ๊ฑฐ
 
2132
  f.write(content)
2133
  f.write("\n\n")
2134
 
 
 
 
 
 
 
 
 
 
2135
  f.write(f"\n{'='*60}\n")
2136
  f.write(f"TOTAL: {writer_count} writers, {total_words:,} words\n")
2137
  f.write(f"{'='*60}\n")
 
2251
 
2252
  # Test mode checkbox
2253
  test_mode_check = gr.Checkbox(
2254
+ label="๐Ÿงช Test Mode (Quick: Writer 1 & 2 only) / ํ…Œ์ŠคํŠธ ๋ชจ๋“œ (๋น ๋ฅธ ์ƒ์„ฑ: ์ž‘๊ฐ€ 1, 2๋งŒ)",
2255
  value=False,
2256
+ info="Complete Writer 1 & 2 process for 2 chapters only / ์ž‘๊ฐ€ 1, 2๋งŒ ์ž‘์„ฑํ•˜์—ฌ 2๊ฐœ ์ฑ•ํ„ฐ ์ƒ์„ฑ"
2257
  )
2258
 
2259
  # Web search status indicator