Spaces:
Running
Running
Update app.py
Browse files
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 |
-
"""λͺ¨λ μκ°μ μμ λ³Έ λ΄μ©μ κ°μ Έμμ ν©μΉκΈ° -
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
405 |
-
|
406 |
-
|
|
|
|
|
|
|
|
|
|
|
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
|
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
|
1630 |
-
|
1631 |
-
|
1632 |
-
|
1633 |
-
|
1634 |
-
|
1635 |
-
|
1636 |
-
|
1637 |
-
|
1638 |
-
|
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 |
-
|
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 "
|
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 |
-
|
|
|
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 |
-
|
|
|
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:
|
2376 |
value=False,
|
2377 |
-
info="Complete Writer 1
|
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
|