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