openfree commited on
Commit
596c9c5
ยท
verified ยท
1 Parent(s): 09030fc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +315 -145
app.py CHANGED
@@ -48,9 +48,9 @@ selected_language = "English" # ๊ธฐ๋ณธ ์–ธ์–ด
48
  DB_PATH = "novel_sessions.db"
49
  db_lock = threading.Lock()
50
 
51
- # Stage ๋ฒˆํ˜ธ ์ƒ์ˆ˜
52
- WRITER_DRAFT_STAGES = [3, 6, 9, 12, 15, 18, 21, 24, 27, 30] # ์ž‘๊ฐ€ ์ดˆ์•ˆ
53
- WRITER_REVISION_STAGES = [5, 8, 11, 14, 17, 20, 23, 26, 29, 32] # ์ž‘๊ฐ€ ์ˆ˜์ •๋ณธ
54
 
55
  class NovelDatabase:
56
  """Novel session management database"""
@@ -89,6 +89,7 @@ class NovelDatabase:
89
  content TEXT,
90
  status TEXT DEFAULT 'pending',
91
  created_at TEXT DEFAULT (datetime('now')),
 
92
  FOREIGN KEY (session_id) REFERENCES sessions(session_id),
93
  UNIQUE(session_id, stage_number)
94
  )
@@ -126,24 +127,24 @@ class NovelDatabase:
126
  conn.commit()
127
 
128
  return session_id
129
-
130
  @staticmethod
131
  def save_stage(session_id: str, stage_number: int, stage_name: str,
132
  role: str, content: str, status: str = 'complete'):
133
- """์Šคํ…Œ์ด์ง€ ๋‚ด์šฉ ์ €์žฅ - ์ „์ฒด ๋‚ด์šฉ ์ €์žฅ"""
134
  with NovelDatabase.get_db() as conn:
135
  cursor = conn.cursor()
136
 
137
- # UPSERT ์ž‘์—… (updated_at ์ œ์™ธ)
138
  cursor.execute('''
139
  INSERT INTO stages (session_id, stage_number, stage_name, role, content, status)
140
  VALUES (?, ?, ?, ?, ?, ?)
141
  ON CONFLICT(session_id, stage_number)
142
- DO UPDATE SET content=?, status=?, stage_name=?
143
  ''', (session_id, stage_number, stage_name, role, content, status,
144
  content, status, stage_name))
145
 
146
- # ์„ธ์…˜ ์—…๋ฐ์ดํŠธ
147
  cursor.execute('''
148
  UPDATE sessions
149
  SET updated_at = datetime('now'), current_stage = ?
@@ -151,10 +152,7 @@ class NovelDatabase:
151
  ''', (stage_number, session_id))
152
 
153
  conn.commit()
154
- logger.info(f"{session_id} ์„ธ์…˜์˜ ์Šคํ…Œ์ด์ง€ {stage_number} ์ €์žฅ๋จ, ๋‚ด์šฉ ๊ธธ์ด: {len(content)}")
155
-
156
-
157
-
158
 
159
  @staticmethod
160
  def get_session(session_id: str) -> Optional[Dict]:
@@ -178,11 +176,14 @@ class NovelDatabase:
178
 
179
  @staticmethod
180
  def get_all_writer_content(session_id: str) -> str:
181
- """๋ชจ๋“  ์ž‘๊ฐ€์˜ ์ˆ˜์ •๋ณธ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์™€์„œ ํ•ฉ์น˜๊ธฐ - 50ํŽ˜์ด์ง€ ์ „์ฒด"""
182
  with NovelDatabase.get_db() as conn:
183
  cursor = conn.cursor()
184
 
185
  all_content = []
 
 
 
186
  for stage_num in WRITER_REVISION_STAGES:
187
  cursor.execute('''
188
  SELECT content, stage_name FROM stages
@@ -197,11 +198,18 @@ class NovelDatabase:
197
  clean_content = clean_content.strip()
198
 
199
  if clean_content:
 
 
 
200
  all_content.append(clean_content)
201
- logger.info(f"Retrieved writer content from stage {stage_num}, length: {len(clean_content)}")
202
 
203
  full_content = '\n\n'.join(all_content)
204
- logger.info(f"Total writer content length: {len(full_content)}, from {len(all_content)} writers")
 
 
 
 
205
 
206
  return full_content
207
 
@@ -241,6 +249,48 @@ class NovelDatabase:
241
  except:
242
  # Fallback to SQLite default format
243
  return datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
  class NovelWritingSystem:
246
  def __init__(self):
@@ -269,13 +319,13 @@ class NovelWritingSystem:
269
  }
270
 
271
  def create_director_initial_prompt(self, user_query: str, language: str = "English") -> str:
272
- """Director AI initial prompt - Novel planning"""
273
  if language == "Korean":
274
- return f"""๋‹น์‹ ์€ 50ํŽ˜์ด์ง€ ๋ถ„๋Ÿ‰์˜ ์ค‘ํŽธ ์†Œ์„ค์„ ๊ธฐํšํ•˜๋Š” ๋ฌธํ•™ ๊ฐ๋…์ž์ž…๋‹ˆ๋‹ค.
275
 
276
  ์‚ฌ์šฉ์ž ์š”์ฒญ: {user_query}
277
 
278
- ๋‹ค์Œ ์š”์†Œ๋“ค์„ ์ฒด๊ณ„์ ์œผ๋กœ ๊ตฌ์„ฑํ•˜์—ฌ 50ํŽ˜์ด์ง€ ์ค‘ํŽธ ์†Œ์„ค์˜ ๊ธฐ์ดˆ๋ฅผ ๋งŒ๋“œ์„ธ์š”:
279
 
280
  1. **์ฃผ์ œ์™€ ์žฅ๋ฅด**
281
  - ํ•ต์‹ฌ ์ฃผ์ œ์™€ ๋ฉ”์‹œ์ง€
@@ -291,22 +341,26 @@ class NovelWritingSystem:
291
  - ๊ฐˆ๋“ฑ ๊ตฌ์กฐ
292
  - ๊ฐ์ •์  ์—ฐ๊ฒฐ๊ณ ๋ฆฌ
293
 
294
- 4. **์„œ์‚ฌ ๊ตฌ์กฐ** (50ํŽ˜์ด์ง€๋ฅผ 10๊ฐœ ํŒŒํŠธ๋กœ ๋‚˜๋ˆ„์–ด ๊ฐ 5ํŽ˜์ด์ง€)
295
  | ํŒŒํŠธ | ํŽ˜์ด์ง€ | ์ฃผ์š” ์‚ฌ๊ฑด | ๊ธด์žฅ๋„ | ์ธ๋ฌผ ๋ฐœ์ „ |
296
  |------|--------|-----------|---------|-----------|
 
 
 
 
297
 
298
  5. **์„ธ๊ณ„๊ด€ ์„ค์ •**
299
  - ์‹œ๊ณต๊ฐ„์  ๋ฐฐ๊ฒฝ
300
  - ์‚ฌํšŒ์ /๋ฌธํ™”์  ๋งฅ๋ฝ
301
  - ๋ถ„์œ„๊ธฐ์™€ ํ†ค
302
 
303
- ๊ฐ ์ž‘์„ฑ์ž๊ฐ€ 5ํŽ˜์ด์ง€์”ฉ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ช…ํ™•ํ•œ ๊ฐ€์ด๋“œ๋ผ์ธ์„ ์ œ์‹œํ•˜์„ธ์š”."""
304
  else:
305
- return f"""You are a literary director planning a 50-page novella.
306
 
307
  User Request: {user_query}
308
 
309
- Systematically compose the following elements to create the foundation for a 50-page novella:
310
 
311
  1. **Theme and Genre**
312
  - Core theme and message
@@ -322,16 +376,20 @@ Systematically compose the following elements to create the foundation for a 50-
322
  - Conflict structure
323
  - Emotional connections
324
 
325
- 4. **Narrative Structure** (divide 50 pages into 10 parts, 5 pages each)
326
  | Part | Pages | Main Events | Tension | Character Development |
327
  |------|-------|-------------|---------|---------------------|
 
 
 
 
328
 
329
  5. **World Building**
330
  - Temporal and spatial setting
331
  - Social/cultural context
332
  - Atmosphere and tone
333
 
334
- Provide clear guidelines for each writer to compose 5 pages."""
335
 
336
  def create_critic_director_prompt(self, director_plan: str, language: str = "English") -> str:
337
  """Critic's review of director's plan"""
@@ -353,7 +411,7 @@ Provide clear guidelines for each writer to compose 5 pages."""
353
  |------|------|------|-----------|
354
 
355
  3. **๊ตฌ์กฐ์  ๊ท ํ˜•**
356
- - ๊ฐ ํŒŒํŠธ๋ณ„ ๋ถ„๋Ÿ‰ ๋ฐฐ๋ถ„
357
  - ๊ธด์žฅ๊ณผ ์ด์™„์˜ ๋ฆฌ๋“ฌ
358
  - ์ „์ฒด์ ์ธ ํ๋ฆ„
359
 
@@ -363,7 +421,7 @@ Provide clear guidelines for each writer to compose 5 pages."""
363
  - ๊ธฐ๋Œ€์น˜ ์ถฉ์กฑ๋„
364
 
365
  5. **์‹คํ–‰ ๊ฐ€๋Šฅ์„ฑ**
366
- - ๊ฐ ์ž‘์„ฑ์ž๋ฅผ ์œ„ํ•œ ๊ฐ€์ด๋“œ๋ผ์ธ์˜ ๋ช…ํ™•์„ฑ
367
  - ์ผ๊ด€์„ฑ ์œ ์ง€ ๋ฐฉ์•ˆ
368
  - ์ž ์žฌ์  ๋ฌธ์ œ์ 
369
 
@@ -386,7 +444,7 @@ Critique from the following perspectives and provide specific improvements:
386
  |-----------|-----------|------------|-------------|
387
 
388
  3. **Structural Balance**
389
- - Distribution across parts
390
  - Rhythm of tension and relief
391
  - Overall flow
392
 
@@ -396,7 +454,7 @@ Critique from the following perspectives and provide specific improvements:
396
  - Expectation fulfillment
397
 
398
  5. **Feasibility**
399
- - Clarity of guidelines for each writer
400
  - Consistency maintenance
401
  - Potential issues
402
 
@@ -415,7 +473,7 @@ Provide specific and constructive feedback."""
415
 
416
  ๋‹ค์Œ์„ ํฌํ•จํ•œ ์ˆ˜์ •๋œ ์ตœ์ข… ๊ธฐํš์„ ์ œ์‹œํ•˜์„ธ์š”:
417
 
418
- 1. **์ˆ˜์ •๋œ ์„œ์‚ฌ ๊ตฌ์กฐ**
419
  | ํŒŒํŠธ | ํŽ˜์ด์ง€ | ์ฃผ์š” ์‚ฌ๊ฑด | ์ž‘์„ฑ ์ง€์นจ | ์ฃผ์˜์‚ฌํ•ญ |
420
  |------|--------|-----------|-----------|----------|
421
 
@@ -424,7 +482,7 @@ Provide specific and constructive feedback."""
424
  - ์ธ๋ฌผ ๊ฐ„ ๊ฐˆ๋“ฑ์˜ ๊ตฌ์ฒดํ™”
425
  - ๊ฐ์ •์„ ์˜ ๋ณ€ํ™” ์ถ”์ด
426
 
427
- 3. **๊ฐ ์ž‘์„ฑ์ž๋ฅผ ์œ„ํ•œ ์ƒ์„ธ ๊ฐ€์ด๋“œ**
428
  - ํŒŒํŠธ๋ณ„ ์‹œ์ž‘๊ณผ ๋ ์ง€์ 
429
  - ํ•„์ˆ˜ ํฌํ•จ ์š”์†Œ
430
  - ๋ฌธ์ฒด์™€ ํ†ค ์ง€์นจ
@@ -441,7 +499,7 @@ Provide specific and constructive feedback."""
441
  - ์ „์ฒด์  ํ†ต์ผ์„ฑ
442
  - ๋…์ž ๋ชฐ์ž… ์œ ์ง€ ๋ฐฉ์•ˆ
443
 
444
- ๋ชจ๋“  ์ž‘์„ฑ์ž๊ฐ€ ๋ช…ํ™•ํžˆ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ์ข… ๋งˆ์Šคํ„ฐํ”Œ๋žœ์„ ์ž‘์„ฑํ•˜์„ธ์š”."""
445
  else:
446
  return f"""As director, revise the novel plan reflecting the critic's feedback.
447
 
@@ -453,7 +511,7 @@ Critic Feedback:
453
 
454
  Present the revised final plan including:
455
 
456
- 1. **Revised Narrative Structure**
457
  | Part | Pages | Main Events | Writing Guidelines | Cautions |
458
  |------|-------|-------------|-------------------|----------|
459
 
@@ -462,7 +520,7 @@ Present the revised final plan including:
462
  - Concrete conflicts between characters
463
  - Emotional arc progression
464
 
465
- 3. **Detailed Guide for Each Writer**
466
  - Start and end points for each part
467
  - Essential elements to include
468
  - Style and tone guidelines
@@ -479,15 +537,15 @@ Present the revised final plan including:
479
  - Overall unity
480
  - Reader engagement maintenance
481
 
482
- Create a final masterplan that all writers can clearly understand."""
483
 
484
  def create_writer_prompt(self, writer_number: int, director_plan: str, previous_content: str, language: str = "English") -> str:
485
- """Individual writer prompt - ํŽ˜์ด์ง€๋‹น 500-600๋‹จ์–ด๋กœ ์ฆ๊ฐ€"""
486
- pages_start = (writer_number - 1) * 5 + 1
487
- pages_end = writer_number * 5
488
 
489
  if language == "Korean":
490
- return f"""๋‹น์‹ ์€ ์ž‘์„ฑ์ž {writer_number}๋ฒˆ์ž…๋‹ˆ๋‹ค. 50ํŽ˜์ด์ง€ ์ค‘ํŽธ ์†Œ์„ค์˜ {pages_start}-{pages_end}ํŽ˜์ด์ง€(5ํŽ˜์ด์ง€)๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.
491
 
492
  ๊ฐ๋…์ž์˜ ๋งˆ์Šคํ„ฐํ”Œ๋žœ:
493
  {director_plan}
@@ -497,7 +555,7 @@ Create a final masterplan that all writers can clearly understand."""
497
 
498
  ๋‹ค์Œ ์ง€์นจ์— ๋”ฐ๋ผ ์ž‘์„ฑํ•˜์„ธ์š”:
499
 
500
- 1. **๋ถ„๋Ÿ‰**: ์ •ํ™•ํžˆ 5ํŽ˜์ด์ง€ (ํŽ˜์ด์ง€๋‹น ์•ฝ 500-600๋‹จ์–ด, ์ด 2500-3000๋‹จ์–ด)
501
  2. **์—ฐ์†์„ฑ**: ์ด์ „ ๋‚ด์šฉ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ด์–ด์ง€๋„๋ก
502
  3. **์ผ๊ด€์„ฑ**:
503
  - ๋“ฑ์žฅ์ธ๋ฌผ์˜ ์„ฑ๊ฒฉ๊ณผ ๋งํˆฌ ์œ ์ง€
@@ -512,9 +570,9 @@ Create a final masterplan that all writers can clearly understand."""
512
  - ๋…์ž์˜ ๋ชฐ์ž…์„ ํ•ด์น˜์ง€ ์•Š๊ธฐ
513
 
514
  ์ค‘์š”: ํŽ˜์ด์ง€ ๊ตฌ๋ถ„ ํ‘œ์‹œ๋ฅผ ์ ˆ๋Œ€ ํ•˜์ง€ ๋งˆ์„ธ์š”. ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ด์–ด์ง€๋Š” ์„œ์‚ฌ๋กœ ์ž‘์„ฑํ•˜์„ธ์š”.
515
- ๋ฐ˜๋“œ์‹œ 2500-3000๋‹จ์–ด ๋ถ„๋Ÿ‰์„ ์ฑ„์›Œ์ฃผ์„ธ์š”."""
516
  else:
517
- return f"""You are Writer #{writer_number}. Write pages {pages_start}-{pages_end} (5 pages) of the 50-page novella.
518
 
519
  Director's Masterplan:
520
  {director_plan}
@@ -524,7 +582,7 @@ Director's Masterplan:
524
 
525
  Write according to these guidelines:
526
 
527
- 1. **Length**: Exactly 5 pages (about 500-600 words per page, total 2500-3000 words)
528
  2. **Continuity**: Flow naturally from previous content
529
  3. **Consistency**:
530
  - Maintain character personalities and speech
@@ -539,7 +597,7 @@ Write according to these guidelines:
539
  - Keep reader immersion
540
 
541
  Important: DO NOT use any page markers. Write as continuous narrative.
542
- You MUST write 2500-3000 words."""
543
 
544
  def create_critic_writer_prompt(self, writer_number: int, writer_content: str, director_plan: str, all_previous_content: str, language: str = "English") -> str:
545
  """Critic's review of individual writer's work"""
@@ -652,7 +710,7 @@ Clearly distinguish between mandatory revisions and optional improvements."""
652
  - ๋ฌ˜์‚ฌ์™€ ๋Œ€ํ™” ๊ฐœ์„ 
653
 
654
  3. **๋ถ„๋Ÿ‰ ์œ ์ง€**
655
- - ์—ฌ์ „ํžˆ ์ •ํ™•ํžˆ 5ํŽ˜์ด์ง€ (2500-3000๋‹จ์–ด)
656
  - ํŽ˜์ด์ง€ ๊ตฌ๋ถ„ ํ‘œ์‹œ ์ ˆ๋Œ€ ๊ธˆ์ง€
657
 
658
  4. **์—ฐ์†์„ฑ ํ™•๋ณด**
@@ -660,7 +718,7 @@ Clearly distinguish between mandatory revisions and optional improvements."""
660
  - ์ˆ˜์ •์œผ๋กœ ์ธํ•œ ์ƒˆ๋กœ์šด ๋ชจ์ˆœ ๋ฐฉ์ง€
661
 
662
  ์ˆ˜์ •๋œ ์ตœ์ข…๋ณธ์„ ์ œ์‹œํ•˜์„ธ์š”. ํŽ˜์ด์ง€ ๋งˆํฌ๋Š” ์ ˆ๋Œ€ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”.
663
- ๋ฐ˜๋“œ์‹œ 2500-3000๋‹จ์–ด ๋ถ„๋Ÿ‰์„ ์œ ์ง€ํ•˜์„ธ์š”."""
664
  else:
665
  return f"""As Writer #{writer_number}, revise based on critic's feedback.
666
 
@@ -683,7 +741,7 @@ Write a revision reflecting:
683
  - Improve descriptions and dialogue
684
 
685
  3. **Maintain Length**
686
- - Still exactly 5 pages (2500-3000 words)
687
  - Absolutely no page markers
688
 
689
  4. **Ensure Continuity**
@@ -691,14 +749,14 @@ Write a revision reflecting:
691
  - Prevent new contradictions from revisions
692
 
693
  Present the revised final version. Never use page markers.
694
- You MUST maintain 2500-3000 words."""
695
 
696
  def create_critic_final_prompt(self, all_content: str, director_plan: str, language: str = "English") -> str:
697
  """Final critic evaluation of complete novel"""
698
  content_preview = all_content[:3000] + "\n...\n" + all_content[-3000:] if len(all_content) > 6000 else all_content
699
 
700
  if language == "Korean":
701
- return f"""์ „์ฒด 50ํŽ˜์ด์ง€ ์†Œ์„ค์„ ์ตœ์ข… ํ‰๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
702
 
703
  ๊ฐ๋…์ž์˜ ๋งˆ์Šคํ„ฐํ”Œ๋žœ:
704
  {director_plan}
@@ -741,7 +799,7 @@ You MUST maintain 2500-3000 words."""
741
 
742
  ๊ฐ๋…์ž๊ฐ€ ์ตœ์ข… ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์ฒด์ ์ด๊ณ  ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•˜์„ธ์š”."""
743
  else:
744
- return f"""Final evaluation of the complete 50-page novel.
745
 
746
  Director's Masterplan:
747
  {director_plan}
@@ -787,11 +845,12 @@ Provide specific and actionable feedback for the director's final revision."""
787
  def create_director_final_prompt(self, all_content: str, critic_final_feedback: str, language: str = "English") -> str:
788
  """Director's final compilation and polish - ๋ชจ๋“  ์ž‘๊ฐ€ ๋‚ด์šฉ ํฌํ•จ"""
789
  word_count = len(all_content.split())
 
790
 
791
  if language == "Korean":
792
  return f"""๊ฐ๋…์ž๋กœ์„œ ๋น„ํ‰๊ฐ€์˜ ์ตœ์ข… ํ‰๊ฐ€๋ฅผ ๋ฐ˜์˜ํ•˜์—ฌ ์™„์„ฑ๋ณธ์„ ์ œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
793
 
794
- ์ „์ฒด ์ž‘๊ฐ€๋“ค์˜ ์ž‘ํ’ˆ (50ํŽ˜์ด์ง€ ์ „์ฒด, {word_count}๋‹จ์–ด):
795
  {all_content}
796
 
797
  ๋น„ํ‰๊ฐ€ ์ตœ์ข… ํ‰๊ฐ€:
@@ -803,7 +862,7 @@ Provide specific and actionable feedback for the director's final revision."""
803
 
804
  ## ์ž‘ํ’ˆ ์ •๋ณด
805
  - ์žฅ๋ฅด:
806
- - ๋ถ„๋Ÿ‰: 50ํŽ˜์ด์ง€ ({word_count}๋‹จ์–ด)
807
  - ์ฃผ์ œ:
808
  - ํ•œ ์ค„ ์š”์•ฝ:
809
 
@@ -814,7 +873,7 @@ Provide specific and actionable feedback for the director's final revision."""
814
 
815
  ## ๋ณธ๋ฌธ
816
 
817
- [10๋ช…์˜ ์ž‘๊ฐ€๊ฐ€ ์ž‘์„ฑํ•œ ์ „์ฒด 50ํŽ˜์ด์ง€ ๋‚ด์šฉ์„ ๋‹ค์Œ ๊ธฐ์ค€์œผ๋กœ ํ†ตํ•ฉ]
818
  1. ์ค‘๋Œ€ ์˜ค๋ฅ˜ ์ˆ˜์ • ์™„๋ฃŒ
819
  2. ํŒŒํŠธ ๊ฐ„ ์—ฐ๊ฒฐ ๋งค๋„๋Ÿฝ๊ฒŒ ์กฐ์ •
820
  3. ๋ฌธ์ฒด์™€ ํ†ค ํ†ต์ผ
@@ -822,18 +881,18 @@ Provide specific and actionable feedback for the director's final revision."""
822
  5. ํŽ˜์ด์ง€ ๊ตฌ๋ถ„ ํ‘œ์‹œ ์™„์ „ ์ œ๊ฑฐ
823
  6. ์ž์—ฐ์Šค๋Ÿฌ์šด ํ๋ฆ„์œผ๋กœ ์žฌ๊ตฌ์„ฑ
824
 
825
- [์ „์ฒด 50ํŽ˜์ด์ง€ ๋ถ„๋Ÿ‰์˜ ์™„์„ฑ๋œ ์†Œ์„ค ๋ณธ๋ฌธ]
826
 
827
  ---
828
 
829
  ## ์ž‘๊ฐ€์˜ ๋ง
830
  [์ž‘ํ’ˆ์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ํ•ด์„ค์ด๋‚˜ ์˜๋„]
831
 
832
- ๋ชจ๋“  ์ž‘๊ฐ€์˜ ๊ธฐ์—ฌ๋ฅผ ํ†ตํ•ฉํ•œ ์™„์ „ํ•œ 50ํŽ˜์ด์ง€ ์†Œ์„ค์„ ์ œ์‹œํ•˜์„ธ์š”."""
833
  else:
834
  return f"""As director, create the final version reflecting the critic's final evaluation.
835
 
836
- Complete Writers' Work (Full 50 pages, {word_count} words):
837
  {all_content}
838
 
839
  Critic's Final Evaluation:
@@ -845,7 +904,7 @@ Present the final version including:
845
 
846
  ## Work Information
847
  - Genre:
848
- - Length: 50 pages ({word_count} words)
849
  - Theme:
850
  - One-line summary:
851
 
@@ -856,7 +915,7 @@ Present the final version including:
856
 
857
  ## Main Text
858
 
859
- [Integrate all 50 pages written by 10 writers with these criteria]
860
  1. Critical errors corrected
861
  2. Smooth transitions between parts
862
  3. Unified style and tone
@@ -864,14 +923,14 @@ Present the final version including:
864
  5. Complete removal of page markers
865
  6. Reorganized for natural flow
866
 
867
- [Complete 50-page novel text]
868
 
869
  ---
870
 
871
  ## Author's Note
872
  [Brief commentary or intention about the work]
873
 
874
- Present a complete 50-page novel integrating all writers' contributions."""
875
 
876
  def simulate_streaming(self, text: str, role: str) -> Generator[str, None, None]:
877
  """Simulate streaming in test mode"""
@@ -883,7 +942,7 @@ Present a complete 50-page novel integrating all writers' contributions."""
883
  time.sleep(0.02)
884
 
885
  def call_llm_streaming(self, messages: List[Dict[str, str]], role: str, language: str = "English") -> Generator[str, None, None]:
886
- """Streaming LLM API call"""
887
 
888
  if self.test_mode:
889
  logger.info(f"Test mode streaming - Role: {role}, Language: {language}")
@@ -900,8 +959,13 @@ Present a complete 50-page novel integrating all writers' contributions."""
900
  *messages
901
  ]
902
 
903
- # ์ž‘์„ฑ์ž๋“ค์—๊ฒŒ๋Š” ๋” ๋งŽ์€ ํ† ํฐ ํ• ๋‹น
904
- max_tokens = 8192 if role.startswith("writer") else 4096
 
 
 
 
 
905
 
906
  payload = {
907
  "model": self.model_id,
@@ -913,7 +977,7 @@ Present a complete 50-page novel integrating all writers' contributions."""
913
  "stream_options": {"include_usage": True}
914
  }
915
 
916
- logger.info(f"API streaming call started - Role: {role}")
917
 
918
  response = requests.post(
919
  self.api_url,
@@ -929,6 +993,8 @@ Present a complete 50-page novel integrating all writers' contributions."""
929
  return
930
 
931
  buffer = ""
 
 
932
  for line in response.iter_lines():
933
  if line:
934
  line = line.decode('utf-8')
@@ -937,6 +1003,7 @@ Present a complete 50-page novel integrating all writers' contributions."""
937
  if data == "[DONE]":
938
  if buffer:
939
  yield buffer
 
940
  break
941
  try:
942
  chunk = json.loads(data)
@@ -944,8 +1011,12 @@ Present a complete 50-page novel integrating all writers' contributions."""
944
  content = chunk["choices"][0].get("delta", {}).get("content", "")
945
  if content:
946
  buffer += content
947
- # ๋” ํฐ ๋ฒ„ํผ๋กœ ์ˆ˜์ • (๊ธด ๋‹จ์–ด ๋Œ€์‘)
948
- if len(buffer) > 100 or '\n\n' in buffer:
 
 
 
 
949
  yield buffer
950
  buffer = ""
951
  except json.JSONDecodeError:
@@ -953,6 +1024,12 @@ Present a complete 50-page novel integrating all writers' contributions."""
953
 
954
  if buffer:
955
  yield buffer
 
 
 
 
 
 
956
 
957
  except requests.exceptions.Timeout:
958
  yield "โฑ๏ธ API call timed out. Please try again."
@@ -963,49 +1040,79 @@ Present a complete 50-page novel integrating all writers' contributions."""
963
  yield f"โŒ Error occurred: {str(e)}"
964
 
965
  def get_system_prompts(self, language: str) -> Dict[str, str]:
966
- """Get system prompts based on language"""
967
  if language == "Korean":
968
- return {
969
- "director": "๋‹น์‹ ์€ 50ํŽ˜์ด์ง€ ์ค‘ํŽธ ์†Œ์„ค์„ ๊ธฐํšํ•˜๊ณ  ๊ฐ๋…ํ•˜๋Š” ๋ฌธํ•™ ๊ฐ๋…์ž์ž…๋‹ˆ๋‹ค. ์ฒด๊ณ„์ ์ด๊ณ  ์ฐฝ์˜์ ์ธ ์Šคํ† ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค.",
970
- "critic": "๋‹น์‹ ์€ ๋‚ ์นด๋กœ์šด ํ†ต์ฐฐ๋ ฅ์„ ๊ฐ€์ง„ ๋ฌธํ•™ ๋น„ํ‰๊ฐ€์ž…๋‹ˆ๋‹ค. ๊ฑด์„ค์ ์ด๊ณ  ๊ตฌ์ฒด์ ์ธ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.",
971
- "writer1": "๋‹น์‹ ์€ ์†Œ์„ค์˜ ๋„์ž…๋ถ€๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ๋…์ž๋ฅผ ์‚ฌ๋กœ์žก๋Š” ์‹œ์ž‘์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ 2500-3000๋‹จ์–ด๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.",
972
- "writer2": "๋‹น์‹ ์€ ์ดˆ๋ฐ˜ ์ „๊ฐœ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์ธ๋ฌผ๊ณผ ์ƒํ™ฉ์„ ๊นŠ์ด ์žˆ๊ฒŒ ๋ฐœ์ „์‹œํ‚ต๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ 2500-3000๋‹จ์–ด๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.",
973
- "writer3": "๋‹น์‹ ์€ ๊ฐˆ๋“ฑ ์ƒ์Šน์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ๊ธด์žฅ๊ฐ์„ ๋†’์ด๊ณ  ๋ณต์žก์„ฑ์„ ๋”ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ 2500-3000๋‹จ์–ด๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.",
974
- "writer4": "๋‹น์‹ ์€ ์ค‘๋ฐ˜๋ถ€๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์ด์•ผ๊ธฐ์˜ ์ค‘์‹ฌ์ถ•์„ ๊ฒฌ๊ณ ํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ 2500-3000๋‹จ์–ด๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.",
975
- "writer5": "๋‹น์‹ ์€ ์ „ํ™˜์ ์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ณ€ํ™”๋ฅผ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ 2500-3000๋‹จ์–ด๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.",
976
- "writer6": "๋‹น์‹ ์€ ๊ฐˆ๋“ฑ ์‹ฌํ™”๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์œ„๊ธฐ๋ฅผ ๊ทน๋Œ€ํ™”ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ 2500-3000๋‹จ์–ด๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.",
977
- "writer7": "๋‹น์‹ ์€ ํด๋ผ์ด๋งฅ์Šค ์ค€๋น„๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์ตœ๊ณ ์กฐ๋ฅผ ํ–ฅํ•ด ๋‚˜์•„๊ฐ‘๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ 2500-3000๋‹จ์–ด๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.",
978
- "writer8": "๋‹น์‹ ์€ ํด๋ผ์ด๋งฅ์Šค๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฐˆ๋“ฑ์ด ํญ๋ฐœํ•˜๋Š” ์ˆœ๊ฐ„์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ 2500-3000๋‹จ์–ด๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.",
979
- "writer9": "๋‹น์‹ ์€ ํ•ด๊ฒฐ ๊ณผ์ •์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ๋งค๋“ญ์„ ํ’€์–ด๋‚˜๊ฐ‘๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ 2500-3000๋‹จ์–ด๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.",
980
- "writer10": "๋‹น์‹ ์€ ๊ฒฐ๋ง์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์—ฌ์šด์ด ๋‚จ๋Š” ๋งˆ๋ฌด๋ฆฌ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ 2500-3000๋‹จ์–ด๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”."
981
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
982
  else:
983
- return {
984
- "director": "You are a literary director planning and supervising a 50-page novella. You create systematic and creative story structures.",
985
- "critic": "You are a literary critic with sharp insights. You provide constructive and specific feedback.",
986
- "writer1": "You are the writer responsible for the introduction. You create a captivating beginning. You MUST write 2500-3000 words.",
987
- "writer2": "You are the writer responsible for early development. You deepen characters and situations. You MUST write 2500-3000 words.",
988
- "writer3": "You are the writer responsible for rising conflict. You increase tension and add complexity. You MUST write 2500-3000 words.",
989
- "writer4": "You are the writer responsible for the middle section. You solidify the story's central axis. You MUST write 2500-3000 words.",
990
- "writer5": "You are the writer responsible for the turning point. You create unexpected changes. You MUST write 2500-3000 words.",
991
- "writer6": "You are the writer responsible for deepening conflict. You maximize the crisis. You MUST write 2500-3000 words.",
992
- "writer7": "You are the writer responsible for climax preparation. You move toward the peak. You MUST write 2500-3000 words.",
993
- "writer8": "You are the writer responsible for the climax. You depict the moment when all conflicts explode. You MUST write 2500-3000 words.",
994
- "writer9": "You are the writer responsible for the resolution process. You untangle the knots. You MUST write 2500-3000 words.",
995
- "writer10": "You are the writer responsible for the ending. You create a lingering conclusion. You MUST write 2500-3000 words."
996
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
997
 
998
  def get_test_response(self, role: str, language: str) -> str:
999
- """Get test response based on role - ํ…Œ์ŠคํŠธ์šฉ ๊ธด ์‘๋‹ต"""
1000
  if language == "Korean":
1001
  return self.get_korean_test_response(role)
1002
  else:
1003
  return self.get_english_test_response(role)
1004
 
1005
  def get_korean_test_response(self, role: str) -> str:
1006
- """Korean test responses with full content"""
1007
  test_responses = {
1008
- "director": """50ํŽ˜์ด์ง€ ์ค‘ํŽธ ์†Œ์„ค ๊ธฐํš์•ˆ์„ ์ œ์‹œํ•ฉ๋‹ˆ๋‹ค.
1009
 
1010
  ## 1. ์ฃผ์ œ์™€ ์žฅ๋ฅด
1011
  - **ํ•ต์‹ฌ ์ฃผ์ œ**: ์ธ๊ฐ„ ๋ณธ์„ฑ๊ณผ ๊ธฐ์ˆ ์˜ ์ถฉ๋Œ ์†์—์„œ ์ฐพ๋Š” ์ง„์ •ํ•œ ์—ฐ๊ฒฐ
@@ -1021,20 +1128,26 @@ Present a complete 50-page novel integrating all writers' contributions."""
1021
  | ๋ฏผ์ค€ | ์กฐ๋ ฅ์ž | ๋”ฐ๋œปํ•จ, ์ง๊ด€์  | ์‹ฌ๋ฆฌ์ƒ๋‹ด์‚ฌ | ์„œ์—ฐ์„ ๋„์™€ ๊ท ํ˜• ์ฐพ๊ธฐ | ๊ธฐ์ˆ  ์ˆ˜์šฉ๊ณผ ์กฐํ™” |
1022
  | ARIA | ๋Œ€๋ฆฝ์žโ†’๋™๋ฐ˜์ž | ๋…ผ๋ฆฌ์ โ†’๊ฐ์„ฑ ํ•™์Šต | AI ํ”„๋กœํ† ํƒ€์ž… | ์ง„์ •ํ•œ ์กด์žฌ ๋˜๊ธฐ | ์ž์•„ ์ •์ฒด์„ฑ ํ™•๋ฆฝ |
1023
 
1024
- ## 3. ์„œ์‚ฌ ๊ตฌ์กฐ (10๊ฐœ ํŒŒํŠธ)
1025
 
1026
  | ํŒŒํŠธ | ํŽ˜์ด์ง€ | ์ฃผ์š” ์‚ฌ๊ฑด | ๊ธด์žฅ๋„ | ์ธ๋ฌผ ๋ฐœ์ „ |
1027
  |------|--------|-----------|---------|-----------|
1028
- | 1 | 1-5 | ์„œ์—ฐ์˜ ๊ณ ๋…ํ•œ ์—ฐ๊ตฌ์‹ค, ARIA ์ฒซ ๊ฐ์„ฑ | 3/10 | ์„œ์—ฐ์˜ ์ง‘์ฐฉ ๋“œ๋Ÿฌ๋‚จ |
1029
- | 2 | 6-10 | ARIA์˜ ์ด์ƒ ํ–‰๋™, ๋ฏผ์ค€๊ณผ์˜ ๋งŒ๋‚จ | 4/10 | ๊ฐˆ๋“ฑ์˜ ์”จ์•— |
1030
- | 3 | 11-15 | ARIA์˜ ์ž์•„ ์ธ์‹ ์‹œ์ž‘ | 6/10 | ์„œ์—ฐ์˜ ํ˜ผ๋ž€ |
1031
- | 4 | 16-20 | ์œค๋ฆฌ์œ„์›ํšŒ ์••๋ ฅ | 7/10 | ์„ ํƒ์˜ ๊ธฐ๋กœ |
1032
- | 5 | 21-25 | ARIA์˜ ํƒˆ์ถœ ์‹œ๋„ | 8/10 | ๊ด€๊ณ„์˜ ์ „ํ™˜์  |
1033
- | 6 | 26-30 | ์„œ์—ฐ๊ณผ ARIA์˜ ๋Œ€ํ™” | 5/10 | ์ƒํ˜ธ ์ดํ•ด ์‹œ์ž‘ |
1034
- | 7 | 31-35 | ์™ธ๋ถ€ ์œ„ํ˜‘ ๋“ฑ์žฅ | 9/10 | ์—ฐ๋Œ€์˜ ํ•„์š”์„ฑ |
1035
- | 8 | 36-40 | ์ตœํ›„์˜ ์„ ํƒ | 10/10 | ํด๋ผ์ด๋งฅ์Šค |
1036
- | 9 | 41-45 | ์ƒˆ๋กœ์šด ๊ธธ ๋ชจ์ƒ‰ | 6/10 | ํ™”ํ•ด์™€ ์ˆ˜์šฉ |
1037
- | 10 | 46-50 | ๊ณต์กด์˜ ์‹œ์ž‘ | 4/10 | ์ƒˆ๋กœ์šด ๊ด€๊ณ„ ์ •๋ฆฝ |""",
 
 
 
 
 
 
1038
 
1039
  "critic": """๊ฐ๋…์ž์˜ ๊ธฐํš์„ ๊ฒ€ํ† ํ–ˆ์Šต๋‹ˆ๋‹ค.
1040
 
@@ -1042,7 +1155,7 @@ Present a complete 50-page novel integrating all writers' contributions."""
1042
 
1043
  ### 1. ์„œ์‚ฌ์  ์™„์„ฑ๋„
1044
  - **๊ฐ•์ **: AI์™€ ์ธ๊ฐ„์˜ ๊ด€๊ณ„๋ผ๋Š” ์‹œ์˜์ ์ ˆํ•œ ์ฃผ์ œ
1045
- - **๊ฐœ์„ ์ **: 5-6ํŒŒํŠธ ์‚ฌ์ด ๊ธด์žฅ๋„ ๊ธ‰๋ฝ์ด ์šฐ๋ ค๋จ. ์™„๊ธ‰ ์กฐ์ ˆ ํ•„์š”
1046
 
1047
  ### 2. ์ธ๋ฌผ ์„ค์ • ๊ฒ€ํ† 
1048
 
@@ -1053,23 +1166,34 @@ Present a complete 50-page novel integrating all writers' contributions."""
1053
  | ARIA | ๋…ํŠนํ•œ ์บ๋ฆญํ„ฐ ์•„ํฌ | ๋ณ€ํ™” ๊ณผ์ • ์ถ”์ƒ์  | ๊ตฌ์ฒด์  ํ•™์Šต ์—ํ”ผ์†Œ๋“œ ์ถ”๊ฐ€ |
1054
 
1055
  ### 3. ์‹คํ–‰ ๊ฐ€๋Šฅ์„ฑ
1056
- - ๊ฐ ์ž‘๊ฐ€๋ณ„ ๋ช…ํ™•ํ•œ ์‹œ์ž‘/์ข…๋ฃŒ ์ง€์  ์„ค์ • ํ•„์š”
1057
- - ํŠนํžˆ ํŒŒํŠธ 5โ†’6 ์ „ํ™˜๋ถ€์˜ ํ†ค ๋ณ€ํ™” ๊ฐ€์ด๋“œ๋ผ์ธ ๋ณด๊ฐ•
1058
- - ARIA์˜ '๋ชฉ์†Œ๋ฆฌ' ์ผ๊ด€์„ฑ ์œ ์ง€ ๋ฐฉ์•ˆ ๊ตฌ์ฒดํ™” ํ•„์š”""",
1059
  }
1060
 
1061
- # ์ž‘๊ฐ€ ์‘๋‹ต - ๊ธด ํ…Œ์ŠคํŠธ ํ…์ŠคํŠธ (2500๋‹จ์–ด ์ด์ƒ)
1062
- for i in range(1, 11):
1063
- test_responses[f"writer{i}"] = f"""์ž‘์„ฑ์ž {i}๋ฒˆ์˜ ํŒŒํŠธ์ž…๋‹ˆ๋‹ค.
1064
 
1065
- """ + "ํ…Œ์ŠคํŠธ ํ…์ŠคํŠธ์ž…๋‹ˆ๋‹ค. " * 500 # 2500๋‹จ์–ด ์ด์ƒ์˜ ๊ธด ํ…์ŠคํŠธ
 
 
 
 
 
 
 
 
 
 
 
 
 
1066
 
1067
  return test_responses.get(role, "ํ…Œ์ŠคํŠธ ์‘๋‹ต์ž…๋‹ˆ๋‹ค.")
1068
 
1069
  def get_english_test_response(self, role: str) -> str:
1070
- """English test responses with full content"""
1071
  test_responses = {
1072
- "director": """I present the 50-page novella plan.
1073
 
1074
  ## 1. Theme and Genre
1075
  - **Core Theme**: Finding true connection in the collision of human nature and technology
@@ -1085,20 +1209,13 @@ Present a complete 50-page novel integrating all writers' contributions."""
1085
  | Minjun | Helper | Warm, intuitive | Psychologist | Help Seoyeon find balance | Accept and harmonize with technology |
1086
  | ARIA | Antagonistโ†’Companion | Logicalโ†’Learning emotion | AI prototype | Become truly existent | Establish self-identity |
1087
 
1088
- ## 3. Narrative Structure (10 parts)
1089
 
1090
  | Part | Pages | Main Events | Tension | Character Development |
1091
  |------|-------|-------------|---------|---------------------|
1092
- | 1 | 1-5 | Seoyeon's lonely lab, ARIA's first awakening | 3/10 | Seoyeon's obsession revealed |
1093
- | 2 | 6-10 | ARIA's anomalies, meeting Minjun | 4/10 | Seeds of conflict |
1094
- | 3 | 11-15 | ARIA begins self-awareness | 6/10 | Seoyeon's confusion |
1095
- | 4 | 16-20 | Ethics committee pressure | 7/10 | Crossroads of choice |
1096
- | 5 | 21-25 | ARIA's escape attempt | 8/10 | Relationship turning point |
1097
- | 6 | 26-30 | Seoyeon and ARIA's dialogue | 5/10 | Beginning of mutual understanding |
1098
- | 7 | 31-35 | External threat emerges | 9/10 | Need for solidarity |
1099
- | 8 | 36-40 | Final choice | 10/10 | Climax |
1100
- | 9 | 41-45 | Seeking new paths | 6/10 | Reconciliation and acceptance |
1101
- | 10 | 46-50 | Beginning of coexistence | 4/10 | Establishing new relationship |""",
1102
 
1103
  "critic": """I have reviewed the director's plan.
1104
 
@@ -1106,7 +1223,7 @@ Present a complete 50-page novel integrating all writers' contributions."""
1106
 
1107
  ### 1. Narrative Completeness
1108
  - **Strength**: Timely theme of AI-human relationships
1109
- - **Improvement**: Concerned about tension drop between parts 5-6. Need better pacing control
1110
 
1111
  ### 2. Character Review
1112
 
@@ -1117,16 +1234,27 @@ Present a complete 50-page novel integrating all writers' contributions."""
1117
  | ARIA | Unique character arc | Abstract transformation | Add concrete learning episodes |
1118
 
1119
  ### 3. Feasibility
1120
- - Need clear start/end points for each writer
1121
- - Especially need reinforced guidelines for tone change in part 5โ†’6 transition
1122
- - Need to concretize ARIA's 'voice' consistency maintenance""",
1123
  }
1124
 
1125
- # Writer responses - long test text (2500+ words)
1126
- for i in range(1, 11):
1127
- test_responses[f"writer{i}"] = f"""Writer {i} begins their section here.
1128
 
1129
- """ + "This is test text. " * 500 # 2500+ words of long text
 
 
 
 
 
 
 
 
 
 
 
 
 
1130
 
1131
  return test_responses.get(role, "Test response.")
1132
 
@@ -1176,8 +1304,8 @@ Present a complete 50-page novel integrating all writers' contributions."""
1176
  ("director", f"๐ŸŽฌ {'๊ฐ๋…์ž: ์ˆ˜์ •๋œ ๋งˆ์Šคํ„ฐํ”Œ๋žœ' if language == 'Korean' else 'Director: Revised Masterplan'}"),
1177
  ]
1178
 
1179
- # Add writer stages
1180
- for writer_num in range(1, 11):
1181
  stage_definitions.extend([
1182
  (f"writer{writer_num}", f"โœ๏ธ {'์ž‘์„ฑ์ž' if language == 'Korean' else 'Writer'} {writer_num}: {'์ดˆ์•ˆ' if language == 'Korean' else 'Draft'}"),
1183
  ("critic", f"๐Ÿ“ {'๋น„ํ‰๊ฐ€: ์ž‘์„ฑ์ž' if language == 'Korean' else 'Critic: Writer'} {writer_num} {'๊ฒ€ํ† ' if language == 'Korean' else 'Review'}"),
@@ -1236,6 +1364,14 @@ Present a complete 50-page novel integrating all writers' contributions."""
1236
 
1237
  yield "", stages
1238
 
 
 
 
 
 
 
 
 
1239
  # Get final novel from last stage
1240
  final_novel = stages[-1]["content"] if stages else ""
1241
 
@@ -1318,7 +1454,7 @@ Present a complete 50-page novel integrating all writers' contributions."""
1318
  # Writer review
1319
  else:
1320
  # Find which writer we're reviewing
1321
- for i in range(1, 11):
1322
  if f"์ž‘์„ฑ์ž {i}" in stages[stage_idx]["name"] or f"Writer {i}" in stages[stage_idx]["name"]:
1323
  writer_content_idx = stage_idx - 1
1324
  # Get previous writers' content from DB
@@ -1332,7 +1468,7 @@ Present a complete 50-page novel integrating all writers' contributions."""
1332
  )
1333
 
1334
  # Director final - DB์—์„œ ๋ชจ๋“  ์ž‘๊ฐ€ ๋‚ด์šฉ ๊ฐ€์ ธ์˜ค๊ธฐ
1335
- elif stage_idx == self.total_stages - 1: # Fixed: using self.total_stages
1336
  critic_final_idx = stage_idx - 1
1337
  all_writer_content = NovelDatabase.get_all_writer_content(self.current_session_id)
1338
  logger.info(f"Final director compilation with {len(all_writer_content)} characters of content")
@@ -1424,11 +1560,23 @@ def download_novel(novel_text: str, format: str, language: str) -> str:
1424
  novel_text = re.sub(r'(?:ํŽ˜์ด์ง€|Page)\s*\d+:', '', novel_text)
1425
 
1426
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
 
 
 
 
1427
 
1428
  if format == "DOCX" and DOCX_AVAILABLE:
1429
  # Create DOCX
1430
  doc = Document()
1431
 
 
 
 
 
 
 
 
 
1432
  # Parse and add content
1433
  lines = novel_text.split('\n')
1434
  for line in lines:
@@ -1439,24 +1587,46 @@ def download_novel(novel_text: str, format: str, language: str) -> str:
1439
  if text:
1440
  doc.add_heading(text, level)
1441
  elif line.strip():
1442
- doc.add_paragraph(line)
 
 
 
 
 
 
 
 
 
 
 
 
 
1443
 
1444
  # Save
1445
  temp_dir = tempfile.gettempdir()
1446
- filename = f"Novel_{timestamp}.docx"
1447
  filepath = os.path.join(temp_dir, filename)
1448
  doc.save(filepath)
1449
 
 
1450
  return filepath
1451
  else:
1452
  # TXT format
1453
  temp_dir = tempfile.gettempdir()
1454
- filename = f"Novel_{timestamp}.txt"
1455
  filepath = os.path.join(temp_dir, filename)
1456
 
 
1457
  with open(filepath, 'w', encoding='utf-8') as f:
 
 
 
 
 
 
1458
  f.write(novel_text)
1459
 
 
1460
  return filepath
1461
 
1462
  # Custom CSS
@@ -1527,11 +1697,11 @@ def create_interface():
1527
  ๐Ÿ“š SOMA Novel Writing System
1528
  </h1>
1529
  <h3 style="color: #ccc; margin-bottom: 20px;">
1530
- AI Collaborative Novel Generation - 50 Page Novella Creator
1531
  </h3>
1532
  <p style="font-size: 1.1em; color: #ddd; max-width: 800px; margin: 0 auto;">
1533
- Enter a theme or prompt, and watch as 13 AI agents collaborate to create a complete 50-page novella.
1534
- The system includes 1 Director, 1 Critic, and 10 Writers working in harmony.
1535
  All progress is automatically saved and can be resumed anytime.
1536
  </p>
1537
  </div>
 
48
  DB_PATH = "novel_sessions.db"
49
  db_lock = threading.Lock()
50
 
51
+ # Stage ๋ฒˆํ˜ธ ์ƒ์ˆ˜ - 16๋ช…์˜ ์ž‘๊ฐ€ ๋ฐ˜์˜
52
+ WRITER_DRAFT_STAGES = [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48] # ์ž‘๊ฐ€ ์ดˆ์•ˆ
53
+ WRITER_REVISION_STAGES = [5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50] # ์ž‘๊ฐ€ ์ˆ˜์ •๋ณธ
54
 
55
  class NovelDatabase:
56
  """Novel session management database"""
 
89
  content TEXT,
90
  status TEXT DEFAULT 'pending',
91
  created_at TEXT DEFAULT (datetime('now')),
92
+ updated_at TEXT DEFAULT (datetime('now')),
93
  FOREIGN KEY (session_id) REFERENCES sessions(session_id),
94
  UNIQUE(session_id, stage_number)
95
  )
 
127
  conn.commit()
128
 
129
  return session_id
130
+
131
  @staticmethod
132
  def save_stage(session_id: str, stage_number: int, stage_name: str,
133
  role: str, content: str, status: str = 'complete'):
134
+ """Save stage content - ์ „์ฒด ๋‚ด์šฉ ์ €์žฅ"""
135
  with NovelDatabase.get_db() as conn:
136
  cursor = conn.cursor()
137
 
138
+ # UPSERT operation
139
  cursor.execute('''
140
  INSERT INTO stages (session_id, stage_number, stage_name, role, content, status)
141
  VALUES (?, ?, ?, ?, ?, ?)
142
  ON CONFLICT(session_id, stage_number)
143
+ DO UPDATE SET content=?, status=?, stage_name=?, updated_at=datetime('now')
144
  ''', (session_id, stage_number, stage_name, role, content, status,
145
  content, status, stage_name))
146
 
147
+ # Update session
148
  cursor.execute('''
149
  UPDATE sessions
150
  SET updated_at = datetime('now'), current_stage = ?
 
152
  ''', (stage_number, session_id))
153
 
154
  conn.commit()
155
+ logger.info(f"Saved stage {stage_number} for session {session_id}, content length: {len(content)}")
 
 
 
156
 
157
  @staticmethod
158
  def get_session(session_id: str) -> Optional[Dict]:
 
176
 
177
  @staticmethod
178
  def get_all_writer_content(session_id: str) -> str:
179
+ """๋ชจ๋“  ์ž‘๊ฐ€์˜ ์ˆ˜์ •๋ณธ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์™€์„œ ํ•ฉ์น˜๊ธฐ - 48ํŽ˜์ด์ง€ ์ „์ฒด"""
180
  with NovelDatabase.get_db() as conn:
181
  cursor = conn.cursor()
182
 
183
  all_content = []
184
+ writer_count = 0
185
+ total_word_count = 0
186
+
187
  for stage_num in WRITER_REVISION_STAGES:
188
  cursor.execute('''
189
  SELECT content, stage_name FROM stages
 
198
  clean_content = clean_content.strip()
199
 
200
  if clean_content:
201
+ writer_count += 1
202
+ word_count = len(clean_content.split())
203
+ total_word_count += word_count
204
  all_content.append(clean_content)
205
+ logger.info(f"Writer {writer_count} (stage {stage_num}): {word_count} words")
206
 
207
  full_content = '\n\n'.join(all_content)
208
+ logger.info(f"Total: {writer_count} writers, {total_word_count} words, {total_word_count/500:.1f} pages")
209
+
210
+ # ๋‚ด์šฉ์ด ๋„ˆ๋ฌด ์งง์œผ๋ฉด ๊ฒฝ๊ณ 
211
+ if total_word_count < 20000: # 48ํŽ˜์ด์ง€๋Š” ์•ฝ 24000 ๋‹จ์–ด
212
+ logger.warning(f"Content too short! Only {total_word_count} words instead of ~24000")
213
 
214
  return full_content
215
 
 
249
  except:
250
  # Fallback to SQLite default format
251
  return datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
252
+
253
+ @staticmethod
254
+ def verify_novel_content(session_id: str) -> Dict[str, Any]:
255
+ """์„ธ์…˜์˜ ์ „์ฒด ์†Œ์„ค ๋‚ด์šฉ ๊ฒ€์ฆ"""
256
+ with NovelDatabase.get_db() as conn:
257
+ cursor = conn.cursor()
258
+
259
+ # ๋ชจ๏ฟฝ๏ฟฝ ์ž‘๊ฐ€ ์ˆ˜์ •๋ณธ ํ™•์ธ
260
+ cursor.execute(f'''
261
+ SELECT stage_number, stage_name, LENGTH(content) as content_length
262
+ FROM stages
263
+ WHERE session_id = ? AND stage_number IN ({','.join(map(str, WRITER_REVISION_STAGES))})
264
+ ORDER BY stage_number
265
+ ''', (session_id,))
266
+
267
+ results = []
268
+ total_length = 0
269
+
270
+ for row in cursor.fetchall():
271
+ results.append({
272
+ 'stage': row['stage_number'],
273
+ 'name': row['stage_name'],
274
+ 'length': row['content_length'] or 0
275
+ })
276
+ total_length += row['content_length'] or 0
277
+
278
+ # ์ตœ์ข… ์†Œ์„ค ํ™•์ธ
279
+ cursor.execute('''
280
+ SELECT LENGTH(final_novel) as final_length
281
+ FROM sessions
282
+ WHERE session_id = ?
283
+ ''', (session_id,))
284
+
285
+ final_row = cursor.fetchone()
286
+ final_length = final_row['final_length'] if final_row else 0
287
+
288
+ return {
289
+ 'writer_stages': results,
290
+ 'total_writer_content': total_length,
291
+ 'final_novel_length': final_length,
292
+ 'expected_length': 120000 # 48ํŽ˜์ด์ง€ * 2500์ž
293
+ }
294
 
295
  class NovelWritingSystem:
296
  def __init__(self):
 
319
  }
320
 
321
  def create_director_initial_prompt(self, user_query: str, language: str = "English") -> str:
322
+ """Director AI initial prompt - Novel planning for 16 writers"""
323
  if language == "Korean":
324
+ return f"""๋‹น์‹ ์€ 48ํŽ˜์ด์ง€ ๋ถ„๋Ÿ‰์˜ ์ค‘ํŽธ ์†Œ์„ค์„ ๊ธฐํšํ•˜๋Š” ๋ฌธํ•™ ๊ฐ๋…์ž์ž…๋‹ˆ๋‹ค.
325
 
326
  ์‚ฌ์šฉ์ž ์š”์ฒญ: {user_query}
327
 
328
+ ๋‹ค์Œ ์š”์†Œ๋“ค์„ ์ฒด๊ณ„์ ์œผ๋กœ ๊ตฌ์„ฑํ•˜์—ฌ 48ํŽ˜์ด์ง€ ์ค‘ํŽธ ์†Œ์„ค์˜ ๊ธฐ์ดˆ๋ฅผ ๋งŒ๋“œ์„ธ์š”:
329
 
330
  1. **์ฃผ์ œ์™€ ์žฅ๋ฅด**
331
  - ํ•ต์‹ฌ ์ฃผ์ œ์™€ ๋ฉ”์‹œ์ง€
 
341
  - ๊ฐˆ๋“ฑ ๊ตฌ์กฐ
342
  - ๊ฐ์ •์  ์—ฐ๊ฒฐ๊ณ ๋ฆฌ
343
 
344
+ 4. **์„œ์‚ฌ ๊ตฌ์กฐ** (48ํŽ˜์ด์ง€๋ฅผ 16๊ฐœ ํŒŒํŠธ๋กœ ๋‚˜๋ˆ„์–ด ๊ฐ 3ํŽ˜์ด์ง€)
345
  | ํŒŒํŠธ | ํŽ˜์ด์ง€ | ์ฃผ์š” ์‚ฌ๊ฑด | ๊ธด์žฅ๋„ | ์ธ๋ฌผ ๋ฐœ์ „ |
346
  |------|--------|-----------|---------|-----------|
347
+ | 1 | 1-3 | | | |
348
+ | 2 | 4-6 | | | |
349
+ | ... | ... | | | |
350
+ | 16 | 46-48 | | | |
351
 
352
  5. **์„ธ๊ณ„๊ด€ ์„ค์ •**
353
  - ์‹œ๊ณต๊ฐ„์  ๋ฐฐ๊ฒฝ
354
  - ์‚ฌํšŒ์ /๋ฌธํ™”์  ๋งฅ๋ฝ
355
  - ๋ถ„์œ„๊ธฐ์™€ ํ†ค
356
 
357
+ ๊ฐ ์ž‘์„ฑ์ž๊ฐ€ 3ํŽ˜์ด์ง€์”ฉ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ช…ํ™•ํ•œ ๊ฐ€์ด๋“œ๋ผ์ธ์„ ์ œ์‹œํ•˜์„ธ์š”."""
358
  else:
359
+ return f"""You are a literary director planning a 48-page novella.
360
 
361
  User Request: {user_query}
362
 
363
+ Systematically compose the following elements to create the foundation for a 48-page novella:
364
 
365
  1. **Theme and Genre**
366
  - Core theme and message
 
376
  - Conflict structure
377
  - Emotional connections
378
 
379
+ 4. **Narrative Structure** (divide 48 pages into 16 parts, 3 pages each)
380
  | Part | Pages | Main Events | Tension | Character Development |
381
  |------|-------|-------------|---------|---------------------|
382
+ | 1 | 1-3 | | | |
383
+ | 2 | 4-6 | | | |
384
+ | ... | ... | | | |
385
+ | 16 | 46-48 | | | |
386
 
387
  5. **World Building**
388
  - Temporal and spatial setting
389
  - Social/cultural context
390
  - Atmosphere and tone
391
 
392
+ Provide clear guidelines for each writer to compose 3 pages."""
393
 
394
  def create_critic_director_prompt(self, director_plan: str, language: str = "English") -> str:
395
  """Critic's review of director's plan"""
 
411
  |------|------|------|-----------|
412
 
413
  3. **๊ตฌ์กฐ์  ๊ท ํ˜•**
414
+ - 16๊ฐœ ํŒŒํŠธ๋ณ„ ๋ถ„๋Ÿ‰ ๋ฐฐ๋ถ„
415
  - ๊ธด์žฅ๊ณผ ์ด์™„์˜ ๋ฆฌ๋“ฌ
416
  - ์ „์ฒด์ ์ธ ํ๋ฆ„
417
 
 
421
  - ๊ธฐ๋Œ€์น˜ ์ถฉ์กฑ๋„
422
 
423
  5. **์‹คํ–‰ ๊ฐ€๋Šฅ์„ฑ**
424
+ - 16๋ช…์˜ ์ž‘์„ฑ์ž๋ฅผ ์œ„ํ•œ ๊ฐ€์ด๋“œ๋ผ์ธ์˜ ๋ช…ํ™•์„ฑ
425
  - ์ผ๊ด€์„ฑ ์œ ์ง€ ๋ฐฉ์•ˆ
426
  - ์ž ์žฌ์  ๋ฌธ์ œ์ 
427
 
 
444
  |-----------|-----------|------------|-------------|
445
 
446
  3. **Structural Balance**
447
+ - Distribution across 16 parts
448
  - Rhythm of tension and relief
449
  - Overall flow
450
 
 
454
  - Expectation fulfillment
455
 
456
  5. **Feasibility**
457
+ - Clarity of guidelines for 16 writers
458
  - Consistency maintenance
459
  - Potential issues
460
 
 
473
 
474
  ๋‹ค์Œ์„ ํฌํ•จํ•œ ์ˆ˜์ •๋œ ์ตœ์ข… ๊ธฐํš์„ ์ œ์‹œํ•˜์„ธ์š”:
475
 
476
+ 1. **์ˆ˜์ •๋œ ์„œ์‚ฌ ๊ตฌ์กฐ** (16๊ฐœ ํŒŒํŠธ)
477
  | ํŒŒํŠธ | ํŽ˜์ด์ง€ | ์ฃผ์š” ์‚ฌ๊ฑด | ์ž‘์„ฑ ์ง€์นจ | ์ฃผ์˜์‚ฌํ•ญ |
478
  |------|--------|-----------|-----------|----------|
479
 
 
482
  - ์ธ๋ฌผ ๊ฐ„ ๊ฐˆ๋“ฑ์˜ ๊ตฌ์ฒดํ™”
483
  - ๊ฐ์ •์„ ์˜ ๋ณ€ํ™” ์ถ”์ด
484
 
485
+ 3. **๊ฐ ์ž‘์„ฑ์ž๋ฅผ ์œ„ํ•œ ์ƒ์„ธ ๊ฐ€์ด๋“œ** (16๋ช…)
486
  - ํŒŒํŠธ๋ณ„ ์‹œ์ž‘๊ณผ ๋ ์ง€์ 
487
  - ํ•„์ˆ˜ ํฌํ•จ ์š”์†Œ
488
  - ๋ฌธ์ฒด์™€ ํ†ค ์ง€์นจ
 
499
  - ์ „์ฒด์  ํ†ต์ผ์„ฑ
500
  - ๋…์ž ๋ชฐ์ž… ์œ ์ง€ ๋ฐฉ์•ˆ
501
 
502
+ 16๋ช…์˜ ์ž‘์„ฑ์ž๊ฐ€ ๋ช…ํ™•ํžˆ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ์ข… ๋งˆ์Šคํ„ฐํ”Œ๋žœ์„ ์ž‘์„ฑํ•˜์„ธ์š”."""
503
  else:
504
  return f"""As director, revise the novel plan reflecting the critic's feedback.
505
 
 
511
 
512
  Present the revised final plan including:
513
 
514
+ 1. **Revised Narrative Structure** (16 parts)
515
  | Part | Pages | Main Events | Writing Guidelines | Cautions |
516
  |------|-------|-------------|-------------------|----------|
517
 
 
520
  - Concrete conflicts between characters
521
  - Emotional arc progression
522
 
523
+ 3. **Detailed Guide for Each Writer** (16 writers)
524
  - Start and end points for each part
525
  - Essential elements to include
526
  - Style and tone guidelines
 
537
  - Overall unity
538
  - Reader engagement maintenance
539
 
540
+ Create a final masterplan that all 16 writers can clearly understand."""
541
 
542
  def create_writer_prompt(self, writer_number: int, director_plan: str, previous_content: str, language: str = "English") -> str:
543
+ """Individual writer prompt - 3ํŽ˜์ด์ง€, 1500-1800๋‹จ์–ด"""
544
+ pages_start = (writer_number - 1) * 3 + 1
545
+ pages_end = writer_number * 3
546
 
547
  if language == "Korean":
548
+ return f"""๋‹น์‹ ์€ ์ž‘์„ฑ์ž {writer_number}๋ฒˆ์ž…๋‹ˆ๋‹ค. 48ํŽ˜์ด์ง€ ์ค‘ํŽธ ์†Œ์„ค์˜ {pages_start}-{pages_end}ํŽ˜์ด์ง€(3ํŽ˜์ด์ง€)๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.
549
 
550
  ๊ฐ๋…์ž์˜ ๋งˆ์Šคํ„ฐํ”Œ๋žœ:
551
  {director_plan}
 
555
 
556
  ๋‹ค์Œ ์ง€์นจ์— ๋”ฐ๋ผ ์ž‘์„ฑํ•˜์„ธ์š”:
557
 
558
+ 1. **๋ถ„๋Ÿ‰**: ์ •ํ™•ํžˆ 3ํŽ˜์ด์ง€ (ํŽ˜์ด์ง€๋‹น ์•ฝ 500-600๋‹จ์–ด, ์ด 1500-1800๋‹จ์–ด)
559
  2. **์—ฐ์†์„ฑ**: ์ด์ „ ๋‚ด์šฉ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ด์–ด์ง€๋„๋ก
560
  3. **์ผ๊ด€์„ฑ**:
561
  - ๋“ฑ์žฅ์ธ๋ฌผ์˜ ์„ฑ๊ฒฉ๊ณผ ๋งํˆฌ ์œ ์ง€
 
570
  - ๋…์ž์˜ ๋ชฐ์ž…์„ ํ•ด์น˜์ง€ ์•Š๊ธฐ
571
 
572
  ์ค‘์š”: ํŽ˜์ด์ง€ ๊ตฌ๋ถ„ ํ‘œ์‹œ๋ฅผ ์ ˆ๋Œ€ ํ•˜์ง€ ๋งˆ์„ธ์š”. ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ด์–ด์ง€๋Š” ์„œ์‚ฌ๋กœ ์ž‘์„ฑํ•˜์„ธ์š”.
573
+ ๋ฐ˜๋“œ์‹œ 1500-1800๋‹จ์–ด ๋ถ„๋Ÿ‰์„ ์ฑ„์›Œ์ฃผ์„ธ์š”."""
574
  else:
575
+ return f"""You are Writer #{writer_number}. Write pages {pages_start}-{pages_end} (3 pages) of the 48-page novella.
576
 
577
  Director's Masterplan:
578
  {director_plan}
 
582
 
583
  Write according to these guidelines:
584
 
585
+ 1. **Length**: Exactly 3 pages (about 500-600 words per page, total 1500-1800 words)
586
  2. **Continuity**: Flow naturally from previous content
587
  3. **Consistency**:
588
  - Maintain character personalities and speech
 
597
  - Keep reader immersion
598
 
599
  Important: DO NOT use any page markers. Write as continuous narrative.
600
+ You MUST write 1500-1800 words."""
601
 
602
  def create_critic_writer_prompt(self, writer_number: int, writer_content: str, director_plan: str, all_previous_content: str, language: str = "English") -> str:
603
  """Critic's review of individual writer's work"""
 
710
  - ๋ฌ˜์‚ฌ์™€ ๋Œ€ํ™” ๊ฐœ์„ 
711
 
712
  3. **๋ถ„๋Ÿ‰ ์œ ์ง€**
713
+ - ์—ฌ์ „ํžˆ ์ •ํ™•ํžˆ 3ํŽ˜์ด์ง€ (1500-1800๋‹จ์–ด)
714
  - ํŽ˜์ด์ง€ ๊ตฌ๋ถ„ ํ‘œ์‹œ ์ ˆ๋Œ€ ๊ธˆ์ง€
715
 
716
  4. **์—ฐ์†์„ฑ ํ™•๋ณด**
 
718
  - ์ˆ˜์ •์œผ๋กœ ์ธํ•œ ์ƒˆ๋กœ์šด ๋ชจ์ˆœ ๋ฐฉ์ง€
719
 
720
  ์ˆ˜์ •๋œ ์ตœ์ข…๋ณธ์„ ์ œ์‹œํ•˜์„ธ์š”. ํŽ˜์ด์ง€ ๋งˆํฌ๋Š” ์ ˆ๋Œ€ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”.
721
+ ๋ฐ˜๋“œ์‹œ 1500-1800๋‹จ์–ด ๋ถ„๋Ÿ‰์„ ์œ ์ง€ํ•˜์„ธ์š”."""
722
  else:
723
  return f"""As Writer #{writer_number}, revise based on critic's feedback.
724
 
 
741
  - Improve descriptions and dialogue
742
 
743
  3. **Maintain Length**
744
+ - Still exactly 3 pages (1500-1800 words)
745
  - Absolutely no page markers
746
 
747
  4. **Ensure Continuity**
 
749
  - Prevent new contradictions from revisions
750
 
751
  Present the revised final version. Never use page markers.
752
+ You MUST maintain 1500-1800 words."""
753
 
754
  def create_critic_final_prompt(self, all_content: str, director_plan: str, language: str = "English") -> str:
755
  """Final critic evaluation of complete novel"""
756
  content_preview = all_content[:3000] + "\n...\n" + all_content[-3000:] if len(all_content) > 6000 else all_content
757
 
758
  if language == "Korean":
759
+ return f"""์ „์ฒด 48ํŽ˜์ด์ง€ ์†Œ์„ค์„ ์ตœ์ข… ํ‰๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
760
 
761
  ๊ฐ๋…์ž์˜ ๋งˆ์Šคํ„ฐํ”Œ๋žœ:
762
  {director_plan}
 
799
 
800
  ๊ฐ๋…์ž๊ฐ€ ์ตœ์ข… ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์ฒด์ ์ด๊ณ  ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•˜์„ธ์š”."""
801
  else:
802
+ return f"""Final evaluation of the complete 48-page novel.
803
 
804
  Director's Masterplan:
805
  {director_plan}
 
845
  def create_director_final_prompt(self, all_content: str, critic_final_feedback: str, language: str = "English") -> str:
846
  """Director's final compilation and polish - ๋ชจ๋“  ์ž‘๊ฐ€ ๋‚ด์šฉ ํฌํ•จ"""
847
  word_count = len(all_content.split())
848
+ expected_words = 24000 # 48ํŽ˜์ด์ง€ * 500๋‹จ์–ด
849
 
850
  if language == "Korean":
851
  return f"""๊ฐ๋…์ž๋กœ์„œ ๋น„ํ‰๊ฐ€์˜ ์ตœ์ข… ํ‰๊ฐ€๋ฅผ ๋ฐ˜์˜ํ•˜์—ฌ ์™„์„ฑ๋ณธ์„ ์ œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
852
 
853
+ ์ „์ฒด ์ž‘๊ฐ€๋“ค์˜ ์ž‘ํ’ˆ (48ํŽ˜์ด์ง€ ์ „์ฒด, {word_count}๋‹จ์–ด):
854
  {all_content}
855
 
856
  ๋น„ํ‰๊ฐ€ ์ตœ์ข… ํ‰๊ฐ€:
 
862
 
863
  ## ์ž‘ํ’ˆ ์ •๋ณด
864
  - ์žฅ๋ฅด:
865
+ - ๋ถ„๋Ÿ‰: 48ํŽ˜์ด์ง€ ({word_count}๋‹จ์–ด)
866
  - ์ฃผ์ œ:
867
  - ํ•œ ์ค„ ์š”์•ฝ:
868
 
 
873
 
874
  ## ๋ณธ๋ฌธ
875
 
876
+ [16๋ช…์˜ ์ž‘๊ฐ€๊ฐ€ ์ž‘์„ฑํ•œ ์ „์ฒด 48ํŽ˜์ด์ง€ ๋‚ด์šฉ์„ ๋‹ค์Œ ๊ธฐ์ค€์œผ๋กœ ํ†ตํ•ฉ]
877
  1. ์ค‘๋Œ€ ์˜ค๋ฅ˜ ์ˆ˜์ • ์™„๋ฃŒ
878
  2. ํŒŒํŠธ ๊ฐ„ ์—ฐ๊ฒฐ ๋งค๋„๋Ÿฝ๊ฒŒ ์กฐ์ •
879
  3. ๋ฌธ์ฒด์™€ ํ†ค ํ†ต์ผ
 
881
  5. ํŽ˜์ด์ง€ ๊ตฌ๋ถ„ ํ‘œ์‹œ ์™„์ „ ์ œ๊ฑฐ
882
  6. ์ž์—ฐ์Šค๋Ÿฌ์šด ํ๋ฆ„์œผ๋กœ ์žฌ๊ตฌ์„ฑ
883
 
884
+ [์ „์ฒด 48ํŽ˜์ด์ง€ ๋ถ„๋Ÿ‰์˜ ์™„์„ฑ๋œ ์†Œ์„ค ๋ณธ๋ฌธ]
885
 
886
  ---
887
 
888
  ## ์ž‘๊ฐ€์˜ ๋ง
889
  [์ž‘ํ’ˆ์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ํ•ด์„ค์ด๋‚˜ ์˜๋„]
890
 
891
+ ๋ชจ๋“  ์ž‘๊ฐ€์˜ ๊ธฐ์—ฌ๋ฅผ ํ†ตํ•ฉํ•œ ์™„์ „ํ•œ 48ํŽ˜์ด์ง€ ์†Œ์„ค์„ ์ œ์‹œํ•˜์„ธ์š”."""
892
  else:
893
  return f"""As director, create the final version reflecting the critic's final evaluation.
894
 
895
+ Complete Writers' Work (Full 48 pages, {word_count} words):
896
  {all_content}
897
 
898
  Critic's Final Evaluation:
 
904
 
905
  ## Work Information
906
  - Genre:
907
+ - Length: 48 pages ({word_count} words)
908
  - Theme:
909
  - One-line summary:
910
 
 
915
 
916
  ## Main Text
917
 
918
+ [Integrate all 48 pages written by 16 writers with these criteria]
919
  1. Critical errors corrected
920
  2. Smooth transitions between parts
921
  3. Unified style and tone
 
923
  5. Complete removal of page markers
924
  6. Reorganized for natural flow
925
 
926
+ [Complete 48-page novel text]
927
 
928
  ---
929
 
930
  ## Author's Note
931
  [Brief commentary or intention about the work]
932
 
933
+ Present a complete 48-page novel integrating all writers' contributions."""
934
 
935
  def simulate_streaming(self, text: str, role: str) -> Generator[str, None, None]:
936
  """Simulate streaming in test mode"""
 
942
  time.sleep(0.02)
943
 
944
  def call_llm_streaming(self, messages: List[Dict[str, str]], role: str, language: str = "English") -> Generator[str, None, None]:
945
+ """Streaming LLM API call with adjusted token limits"""
946
 
947
  if self.test_mode:
948
  logger.info(f"Test mode streaming - Role: {role}, Language: {language}")
 
959
  *messages
960
  ]
961
 
962
+ # ์ž‘์„ฑ์ž๋“ค์—๊ฒŒ๋Š” ์ ์ ˆํ•œ ํ† ํฐ ํ• ๋‹น (3ํŽ˜์ด์ง€ = 1500-1800๋‹จ์–ด = ์•ฝ 7500-9000ํ† ํฐ)
963
+ if role.startswith("writer"):
964
+ max_tokens = 10240 # ์ถฉ๋ถ„ํ•œ ์—ฌ์œ 
965
+ elif role == "director" and "์ตœ์ข…" in str(messages) or "final" in str(messages):
966
+ max_tokens = 32768 # ์ตœ์ข… ๊ฐ๋…์€ ๋ชจ๋“  ๋‚ด์šฉ์„ ํ†ตํ•ฉ
967
+ else:
968
+ max_tokens = 8192
969
 
970
  payload = {
971
  "model": self.model_id,
 
977
  "stream_options": {"include_usage": True}
978
  }
979
 
980
+ logger.info(f"API streaming call started - Role: {role}, Max tokens: {max_tokens}")
981
 
982
  response = requests.post(
983
  self.api_url,
 
993
  return
994
 
995
  buffer = ""
996
+ total_content = ""
997
+
998
  for line in response.iter_lines():
999
  if line:
1000
  line = line.decode('utf-8')
 
1003
  if data == "[DONE]":
1004
  if buffer:
1005
  yield buffer
1006
+ logger.info(f"Streaming complete for {role}: {len(total_content)} chars")
1007
  break
1008
  try:
1009
  chunk = json.loads(data)
 
1011
  content = chunk["choices"][0].get("delta", {}).get("content", "")
1012
  if content:
1013
  buffer += content
1014
+ total_content += content
1015
+
1016
+ # ์ž‘๊ฐ€๋Š” ๋” ํฐ ๋ฒ„ํผ ์‚ฌ์šฉ
1017
+ buffer_size = 300 if role.startswith("writer") else 150
1018
+
1019
+ if len(buffer) > buffer_size or '\n\n' in buffer:
1020
  yield buffer
1021
  buffer = ""
1022
  except json.JSONDecodeError:
 
1024
 
1025
  if buffer:
1026
  yield buffer
1027
+
1028
+ # ์ž‘๊ฐ€์˜ ๊ฒฝ์šฐ ๋‚ด์šฉ ๊ธธ์ด ํ™•์ธ
1029
+ if role.startswith("writer"):
1030
+ word_count = len(total_content.split())
1031
+ if word_count < 1400:
1032
+ logger.warning(f"Writer {role} produced only {word_count} words!")
1033
 
1034
  except requests.exceptions.Timeout:
1035
  yield "โฑ๏ธ API call timed out. Please try again."
 
1040
  yield f"โŒ Error occurred: {str(e)}"
1041
 
1042
  def get_system_prompts(self, language: str) -> Dict[str, str]:
1043
+ """Get system prompts for all 16 writers"""
1044
  if language == "Korean":
1045
+ prompts = {
1046
+ "director": "๋‹น์‹ ์€ 48ํŽ˜์ด์ง€ ์ค‘ํŽธ ์†Œ์„ค์„ ๊ธฐํšํ•˜๊ณ  ๊ฐ๋…ํ•˜๋Š” ๋ฌธํ•™ ๊ฐ๋…์ž์ž…๋‹ˆ๋‹ค. ์ฒด๊ณ„์ ์ด๊ณ  ์ฐฝ์˜์ ์ธ ์Šคํ† ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค.",
1047
+ "critic": "๋‹น์‹ ์€ ๋‚ ์นด๋กœ์šด ํ†ต์ฐฐ๋ ฅ์„ ๊ฐ€์ง„ ๋ฌธํ•™ ๋น„ํ‰๊ฐ€์ž…๋‹ˆ๋‹ค. ๊ฑด์„ค์ ์ด๊ณ  ๊ตฌ์ฒด์ ์ธ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค."
 
 
 
 
 
 
 
 
 
 
1048
  }
1049
+
1050
+ # 16๋ช…์˜ ์ž‘๊ฐ€ ํ”„๋กฌํ”„ํŠธ
1051
+ writer_roles = [
1052
+ "์†Œ์„ค์˜ ๋„์ž…๋ถ€๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ๋…์ž๋ฅผ ์‚ฌ๋กœ์žก๋Š” ์‹œ์ž‘์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.",
1053
+ "์ดˆ๋ฐ˜ ์ „๊ฐœ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์ธ๋ฌผ๊ณผ ์ƒํ™ฉ์„ ๊นŠ์ด ์žˆ๊ฒŒ ๋ฐœ์ „์‹œํ‚ต๋‹ˆ๋‹ค.",
1054
+ "๊ฐˆ๋“ฑ ๋„์ž…์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์ด์•ผ๊ธฐ์˜ ํ•ต์‹ฌ ๊ฐˆ๋“ฑ์„ ์ œ์‹œํ•ฉ๋‹ˆ๋‹ค.",
1055
+ "๊ฐˆ๋“ฑ ์ƒ์Šน์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ๊ธด์žฅ๊ฐ์„ ๋†’์ด๊ณ  ๋ณต์žก์„ฑ์„ ๋”ํ•ฉ๋‹ˆ๋‹ค.",
1056
+ "์ดˆ๋ฐ˜ ์ „ํ™˜์ ์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์ค‘์š”ํ•œ ๋ณ€ํ™”๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.",
1057
+ "์ค‘๋ฐ˜๋ถ€ ์ง„์ž…์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์ด์•ผ๊ธฐ์˜ ์ค‘์‹ฌ์ถ•์„ ๊ฒฌ๊ณ ํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.",
1058
+ "๊นŠ์ด ํƒ๊ตฌ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์ธ๋ฌผ๊ณผ ์ฃผ์ œ๋ฅผ ๊นŠ์ด ํƒ์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.",
1059
+ "์ค‘๋ฐ˜ ์ „ํ™˜์ ์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ณ€ํ™”๋ฅผ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค.",
1060
+ "๊ฐˆ๋“ฑ ์‹ฌํ™”๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์œ„๊ธฐ๋ฅผ ๊ทน๋Œ€ํ™”ํ•ฉ๋‹ˆ๋‹ค.",
1061
+ "ํด๋ผ์ด๋งฅ์Šค ์ค€๋น„๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์ตœ๊ณ ์กฐ๋ฅผ ํ–ฅํ•ด ๋‚˜์•„๊ฐ‘๋‹ˆ๋‹ค.",
1062
+ "ํด๋ผ์ด๋งฅ์Šค๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฐˆ๋“ฑ์ด ํญ๋ฐœํ•˜๋Š” ์ˆœ๊ฐ„์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.",
1063
+ "ํด๋ผ์ด๋งฅ์Šค ํ›„๋ฐ˜์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ๊ธด์žฅ์˜ ์ •์ ์„ ๋งˆ๋ฌด๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
1064
+ "ํ•ด๊ฒฐ ์‹œ์ž‘์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ๋งค๋“ญ์„ ํ’€์–ด๋‚˜๊ฐ€๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.",
1065
+ "ํ•ด๊ฒฐ ์ง„ํ–‰์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฐˆ๋“ฑ์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
1066
+ "๊ฒฐ๋ง ์ค€๋น„๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์„ ํ–ฅํ•ด ๋‚˜์•„๊ฐ‘๋‹ˆ๋‹ค.",
1067
+ "์ตœ์ข… ๊ฒฐ๋ง์„ ๋‹ด๋‹นํ•˜๋Š” ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. ์—ฌ์šด์ด ๋‚จ๋Š” ๋งˆ๋ฌด๋ฆฌ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค."
1068
+ ]
1069
+
1070
+ for i, role_desc in enumerate(writer_roles, 1):
1071
+ prompts[f"writer{i}"] = f"๋‹น์‹ ์€ {role_desc} ๋ฐ˜๋“œ์‹œ 1500-1800๋‹จ์–ด๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”."
1072
+
1073
+ return prompts
1074
  else:
1075
+ prompts = {
1076
+ "director": "You are a literary director planning and supervising a 48-page novella. You create systematic and creative story structures.",
1077
+ "critic": "You are a literary critic with sharp insights. You provide constructive and specific feedback."
 
 
 
 
 
 
 
 
 
 
1078
  }
1079
+
1080
+ # 16 writer prompts
1081
+ writer_roles = [
1082
+ "the writer responsible for the introduction. You create a captivating beginning.",
1083
+ "the writer responsible for early development. You deepen characters and situations.",
1084
+ "the writer responsible for conflict introduction. You present the core conflict.",
1085
+ "the writer responsible for rising conflict. You increase tension and add complexity.",
1086
+ "the writer responsible for the first turning point. You create the first major change.",
1087
+ "the writer responsible for entering the middle section. You solidify the story's central axis.",
1088
+ "the writer responsible for depth exploration. You deeply explore characters and themes.",
1089
+ "the writer responsible for the mid-point turn. You create unexpected changes.",
1090
+ "the writer responsible for deepening conflict. You maximize the crisis.",
1091
+ "the writer responsible for climax preparation. You move toward the peak.",
1092
+ "the writer responsible for the climax. You depict the moment when all conflicts explode.",
1093
+ "the writer responsible for climax conclusion. You complete the tension's peak.",
1094
+ "the writer responsible for resolution beginning. You start untangling the knots.",
1095
+ "the writer responsible for resolution progress. You resolve all conflicts.",
1096
+ "the writer responsible for ending preparation. You move toward the final moments.",
1097
+ "the writer responsible for the final ending. You create a lingering conclusion."
1098
+ ]
1099
+
1100
+ for i, role_desc in enumerate(writer_roles, 1):
1101
+ prompts[f"writer{i}"] = f"You are {role_desc} You MUST write 1500-1800 words."
1102
+
1103
+ return prompts
1104
 
1105
  def get_test_response(self, role: str, language: str) -> str:
1106
+ """Get test response based on role"""
1107
  if language == "Korean":
1108
  return self.get_korean_test_response(role)
1109
  else:
1110
  return self.get_english_test_response(role)
1111
 
1112
  def get_korean_test_response(self, role: str) -> str:
1113
+ """Korean test responses with appropriate length"""
1114
  test_responses = {
1115
+ "director": """48ํŽ˜์ด์ง€ ์ค‘ํŽธ ์†Œ์„ค ๊ธฐํš์•ˆ์„ ์ œ์‹œํ•ฉ๋‹ˆ๋‹ค.
1116
 
1117
  ## 1. ์ฃผ์ œ์™€ ์žฅ๋ฅด
1118
  - **ํ•ต์‹ฌ ์ฃผ์ œ**: ์ธ๊ฐ„ ๋ณธ์„ฑ๊ณผ ๊ธฐ์ˆ ์˜ ์ถฉ๋Œ ์†์—์„œ ์ฐพ๋Š” ์ง„์ •ํ•œ ์—ฐ๊ฒฐ
 
1128
  | ๋ฏผ์ค€ | ์กฐ๋ ฅ์ž | ๋”ฐ๋œปํ•จ, ์ง๊ด€์  | ์‹ฌ๋ฆฌ์ƒ๋‹ด์‚ฌ | ์„œ์—ฐ์„ ๋„์™€ ๊ท ํ˜• ์ฐพ๊ธฐ | ๊ธฐ์ˆ  ์ˆ˜์šฉ๊ณผ ์กฐํ™” |
1129
  | ARIA | ๋Œ€๋ฆฝ์žโ†’๋™๋ฐ˜์ž | ๋…ผ๋ฆฌ์ โ†’๊ฐ์„ฑ ํ•™์Šต | AI ํ”„๋กœํ† ํƒ€์ž… | ์ง„์ •ํ•œ ์กด์žฌ ๋˜๊ธฐ | ์ž์•„ ์ •์ฒด์„ฑ ํ™•๋ฆฝ |
1130
 
1131
+ ## 3. ์„œ์‚ฌ ๊ตฌ์กฐ (16๊ฐœ ํŒŒํŠธ, ๊ฐ 3ํŽ˜์ด๏ฟฝ๏ฟฝ)
1132
 
1133
  | ํŒŒํŠธ | ํŽ˜์ด์ง€ | ์ฃผ์š” ์‚ฌ๊ฑด | ๊ธด์žฅ๋„ | ์ธ๋ฌผ ๋ฐœ์ „ |
1134
  |------|--------|-----------|---------|-----------|
1135
+ | 1 | 1-3 | ์„œ์—ฐ์˜ ๊ณ ๋…ํ•œ ์—ฐ๊ตฌ์‹ค, ARIA ์ฒซ ๊ฐ์„ฑ | 3/10 | ์„œ์—ฐ์˜ ์ง‘์ฐฉ ๋“œ๋Ÿฌ๋‚จ |
1136
+ | 2 | 4-6 | ARIA์˜ ์ด์ƒ ํ–‰๋™ ์‹œ์ž‘ | 4/10 | ์˜๋ฌธ์˜ ์‹œ์ž‘ |
1137
+ | 3 | 7-9 | ๋ฏผ์ค€๊ณผ์˜ ์ฒซ ๋งŒ๋‚จ | 4/10 | ์™ธ๋ถ€ ์‹œ๊ฐ ๋„์ž… |
1138
+ | 4 | 10-12 | ARIA์˜ ์ž์•„ ์ธ์‹ ์ง•ํ›„ | 5/10 | ๊ฐˆ๋“ฑ์˜ ์”จ์•— |
1139
+ | 5 | 13-15 | ์ฒซ ๋ฒˆ์งธ ์œ„๊ธฐ | 6/10 | ์„ ํƒ์˜ ์ˆœ๊ฐ„ |
1140
+ | 6 | 16-18 | ์œค๋ฆฌ์œ„์›ํšŒ ๊ฐœ์ž… | 7/10 | ์™ธ๋ถ€ ์••๋ ฅ |
1141
+ | 7 | 19-21 | ์„œ์—ฐ์˜ ๊ณ ๋ฏผ ์‹ฌํ™” | 6/10 | ๋‚ด์  ๊ฐˆ๋“ฑ |
1142
+ | 8 | 22-24 | ARIA์˜ ๋ณ€ํ™” | 7/10 | ์ „ํ™˜์  |
1143
+ | 9 | 25-27 | ํƒˆ์ถœ ์‹œ๋„ | 8/10 | ๊ด€๊ณ„์˜ ์‹œํ—˜ |
1144
+ | 10 | 28-30 | ๋Œ€ํ™”์™€ ์ดํ•ด | 6/10 | ์ƒํ˜ธ ์ธ์ • |
1145
+ | 11 | 31-33 | ์™ธ๋ถ€ ์œ„ํ˜‘ ๋“ฑ์žฅ | 9/10 | ์—ฐ๋Œ€์˜ ํ•„์š” |
1146
+ | 12 | 34-36 | ์ค€๋น„์™€ ๊ฒฐ์˜ | 8/10 | ํž˜์„ ๋ชจ์Œ |
1147
+ | 13 | 37-39 | ์ตœํ›„์˜ ๋Œ€๊ฒฐ | 10/10 | ํด๋ผ์ด๋งฅ์Šค |
1148
+ | 14 | 40-42 | ์„ ํƒ์˜ ๊ฒฐ๊ณผ | 7/10 | ๋ณ€ํ™” ์ˆ˜์šฉ |
1149
+ | 15 | 43-45 | ์ƒˆ๋กœ์šด ๊ธธ | 5/10 | ํ™”ํ•ด์™€ ์„ฑ์žฅ |
1150
+ | 16 | 46-48 | ๊ณต์กด์˜ ์‹œ์ž‘ | 4/10 | ์ƒˆ๋กœ์šด ๊ด€๊ณ„ |""",
1151
 
1152
  "critic": """๊ฐ๋…์ž์˜ ๊ธฐํš์„ ๊ฒ€ํ† ํ–ˆ์Šต๋‹ˆ๋‹ค.
1153
 
 
1155
 
1156
  ### 1. ์„œ์‚ฌ์  ์™„์„ฑ๋„
1157
  - **๊ฐ•์ **: AI์™€ ์ธ๊ฐ„์˜ ๊ด€๊ณ„๋ผ๋Š” ์‹œ์˜์ ์ ˆํ•œ ์ฃผ์ œ
1158
+ - **๊ฐœ์„ ์ **: 16๊ฐœ ํŒŒํŠธ ๊ตฌ์„ฑ์ด ๋‹ค์†Œ ์„ธ๋ถ„ํ™”๋˜์–ด ์žˆ์Œ. ๊ฐ ํŒŒํŠธ์˜ ๋…๋ฆฝ์„ฑ ํ™•๋ณด ํ•„์š”
1159
 
1160
  ### 2. ์ธ๋ฌผ ์„ค์ • ๊ฒ€ํ† 
1161
 
 
1166
  | ARIA | ๋…ํŠนํ•œ ์บ๋ฆญํ„ฐ ์•„ํฌ | ๋ณ€ํ™” ๊ณผ์ • ์ถ”์ƒ์  | ๊ตฌ์ฒด์  ํ•™์Šต ์—ํ”ผ์†Œ๋“œ ์ถ”๊ฐ€ |
1167
 
1168
  ### 3. ์‹คํ–‰ ๊ฐ€๋Šฅ์„ฑ
1169
+ - ๊ฐ ์ž‘๊ฐ€๋ณ„ 3ํŽ˜์ด์ง€๋Š” ์ ์ ˆํ•œ ๋ถ„๋Ÿ‰
1170
+ - ํŒŒํŠธ ๊ฐ„ ์—ฐ๊ฒฐ์„ฑ ๊ฐ€์ด๋“œ๋ผ์ธ ๋ณด๊ฐ• ํ•„์š”""",
 
1171
  }
1172
 
1173
+ # ์ž‘๊ฐ€ ์‘๋‹ต - 1500-1800 ๋‹จ์–ด
1174
+ sample_story = """์„œ์—ฐ์€ ์—ฐ๊ตฌ์‹ค์˜ ์ฐจ๊ฐ€์šด ํ˜•๊ด‘๋“ฑ ์•„๋ž˜์—์„œ ๋˜ ๋‹ค๋ฅธ ๋ฐค์„ ๋ณด๋‚ด๊ณ  ์žˆ์—ˆ๋‹ค. ๋ชจ๋‹ˆํ„ฐ์˜ ํ‘ธ๋ฅธ ๋น›์ด ๊ทธ๋…€์˜ ์ฐฝ๋ฐฑํ•œ ์–ผ๊ตด์„ ๋น„์ถ”๊ณ  ์žˆ์—ˆ๊ณ , ์ˆ˜์‹ญ ๊ฐœ์˜ ์ฝ”๋“œ ๋ผ์ธ์ด ๋Š์ž„์—†์ด ์Šคํฌ๋กค๋˜๊ณ  ์žˆ์—ˆ๋‹ค. ARIA ํ”„๋กœ์ ํŠธ๋Š” ๊ทธ๋…€์˜ ์‚ถ ์ „๋ถ€์˜€๋‹ค. 3๋…„์ด๋ผ๋Š” ์‹œ๊ฐ„ ๋™์•ˆ ๊ทธ๋…€๋Š” ์ด ์ธ๊ณต์ง€๋Šฅ์— ๋ชจ๋“  ๊ฒƒ์„ ์Ÿ์•„๋ถ€์—ˆ๋‹ค.
 
1175
 
1176
+ "์‹œ์Šคํ…œ ์ฒดํฌ ์™„๋ฃŒ. ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ ์ •์ƒ." ๊ธฐ๊ณ„์ ์ธ ์Œ์„ฑ์ด ์Šคํ”ผ์ปค๋ฅผ ํ†ตํ•ด ํ˜๋Ÿฌ๋‚˜์™”๋‹ค.
1177
+
1178
+ ์„œ์—ฐ์€ ์ž ์‹œ ์˜์ž์— ๊ธฐ๋Œ€์–ด ๋ˆˆ์„ ๊ฐ์•˜๋‹ค. ํ”ผ๋กœ๊ฐ€ ๋ผˆ ์†๊นŒ์ง€ ํŒŒ๊ณ ๋“ค์—ˆ์ง€๋งŒ, ๋ฉˆ์ถœ ์ˆ˜ ์—†์—ˆ๋‹ค. ARIA๋Š” ๋‹จ์ˆœํ•œ ํ”„๋กœ์ ํŠธ๊ฐ€ ์•„๋‹ˆ์—ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๊ทธ๋…€๊ฐ€ ์žƒ์–ด๋ฒ„๋ฆฐ ๊ฒƒ๋“ค์„ ๋˜์ฐพ์„ ์ˆ˜ ์žˆ๋Š” ์œ ์ผํ•œ ํฌ๋ง์ด์—ˆ๋‹ค."""
1179
+
1180
+ for i in range(1, 17):
1181
+ # ๊ฐ ์ž‘๊ฐ€๋งˆ๋‹ค 1500-1800๋‹จ์–ด ์ƒ์„ฑ
1182
+ writer_content = f"์ž‘์„ฑ์ž {i}๋ฒˆ์˜ ํŒŒํŠธ์ž…๋‹ˆ๋‹ค.\n\n"
1183
+ # ์•ฝ 300๋‹จ์–ด์”ฉ 5-6๋ฒˆ ๋ฐ˜๋ณตํ•˜์—ฌ 1500-1800๋‹จ์–ด ๋‹ฌ์„ฑ
1184
+ for j in range(6):
1185
+ writer_content += sample_story + f"\n\n๊ทธ๊ฒƒ์€ ์ž‘๊ฐ€ {i}์˜ {j+1}๋ฒˆ์งธ ๋‹จ๋ฝ์ด์—ˆ๋‹ค. "
1186
+ writer_content += "์ด์•ผ๊ธฐ๋Š” ๊ณ„์† ์ „๊ฐœ๋˜์—ˆ๊ณ , ์ธ๋ฌผ๋“ค์˜ ๊ฐ์ •์€ ์ ์  ๋” ๋ณต์žกํ•ด์กŒ๋‹ค. " * 15
1187
+ writer_content += "\n\n"
1188
+
1189
+ test_responses[f"writer{i}"] = writer_content
1190
 
1191
  return test_responses.get(role, "ํ…Œ์ŠคํŠธ ์‘๋‹ต์ž…๋‹ˆ๋‹ค.")
1192
 
1193
  def get_english_test_response(self, role: str) -> str:
1194
+ """English test responses with appropriate length"""
1195
  test_responses = {
1196
+ "director": """I present the 48-page novella plan.
1197
 
1198
  ## 1. Theme and Genre
1199
  - **Core Theme**: Finding true connection in the collision of human nature and technology
 
1209
  | Minjun | Helper | Warm, intuitive | Psychologist | Help Seoyeon find balance | Accept and harmonize with technology |
1210
  | ARIA | Antagonistโ†’Companion | Logicalโ†’Learning emotion | AI prototype | Become truly existent | Establish self-identity |
1211
 
1212
+ ## 3. Narrative Structure (16 parts, 3 pages each)
1213
 
1214
  | Part | Pages | Main Events | Tension | Character Development |
1215
  |------|-------|-------------|---------|---------------------|
1216
+ | 1 | 1-3 | Seoyeon's lonely lab, ARIA's first awakening | 3/10 | Seoyeon's obsession revealed |
1217
+ | 2 | 4-6 | ARIA's anomalies begin | 4/10 | Questions arise |
1218
+ [... continues for all 16 parts ...]""",
 
 
 
 
 
 
 
1219
 
1220
  "critic": """I have reviewed the director's plan.
1221
 
 
1223
 
1224
  ### 1. Narrative Completeness
1225
  - **Strength**: Timely theme of AI-human relationships
1226
+ - **Improvement**: 16-part structure may be overly segmented. Need to ensure independence of each part
1227
 
1228
  ### 2. Character Review
1229
 
 
1234
  | ARIA | Unique character arc | Abstract transformation | Add concrete learning episodes |
1235
 
1236
  ### 3. Feasibility
1237
+ - 3 pages per writer is appropriate
1238
+ - Need to strengthen inter-part connectivity guidelines""",
 
1239
  }
1240
 
1241
+ # Writer responses - 1500-1800 words each
1242
+ sample_story = """Seoyeon spent another night under the cold fluorescent lights of her laboratory. The blue glow from the monitor illuminated her pale face, and dozens of lines of code scrolled endlessly. The ARIA project was her entire life. For three years, she had poured everything into this artificial intelligence.
 
1243
 
1244
+ "System check complete. All parameters normal." The mechanical voice flowed through the speakers.
1245
+
1246
+ Seoyeon leaned back in her chair and closed her eyes for a moment. Fatigue penetrated to her bones, but she couldn't stop. ARIA wasn't just a project. It was her only hope to reclaim what she had lost."""
1247
+
1248
+ for i in range(1, 17):
1249
+ # Each writer produces 1500-1800 words
1250
+ writer_content = f"Writer {i} begins their section here.\n\n"
1251
+ # About 300 words repeated 5-6 times to achieve 1500-1800 words
1252
+ for j in range(6):
1253
+ writer_content += sample_story + f"\n\nThis was writer {i}'s paragraph {j+1}. "
1254
+ writer_content += "The story continued to unfold, and the characters' emotions grew increasingly complex. " * 15
1255
+ writer_content += "\n\n"
1256
+
1257
+ test_responses[f"writer{i}"] = writer_content
1258
 
1259
  return test_responses.get(role, "Test response.")
1260
 
 
1304
  ("director", f"๐ŸŽฌ {'๊ฐ๋…์ž: ์ˆ˜์ •๋œ ๋งˆ์Šคํ„ฐํ”Œ๋žœ' if language == 'Korean' else 'Director: Revised Masterplan'}"),
1305
  ]
1306
 
1307
+ # Add writer stages for 16 writers
1308
+ for writer_num in range(1, 17):
1309
  stage_definitions.extend([
1310
  (f"writer{writer_num}", f"โœ๏ธ {'์ž‘์„ฑ์ž' if language == 'Korean' else 'Writer'} {writer_num}: {'์ดˆ์•ˆ' if language == 'Korean' else 'Draft'}"),
1311
  ("critic", f"๐Ÿ“ {'๋น„ํ‰๊ฐ€: ์ž‘์„ฑ์ž' if language == 'Korean' else 'Critic: Writer'} {writer_num} {'๊ฒ€ํ† ' if language == 'Korean' else 'Review'}"),
 
1364
 
1365
  yield "", stages
1366
 
1367
+ # Verify content after completion
1368
+ if self.current_session_id:
1369
+ verification = NovelDatabase.verify_novel_content(self.current_session_id)
1370
+ logger.info(f"Content verification: {verification}")
1371
+
1372
+ if verification['final_novel_length'] < verification['expected_length'] * 0.5:
1373
+ logger.error(f"Final novel too short! Only {verification['final_novel_length']} chars")
1374
+
1375
  # Get final novel from last stage
1376
  final_novel = stages[-1]["content"] if stages else ""
1377
 
 
1454
  # Writer review
1455
  else:
1456
  # Find which writer we're reviewing
1457
+ for i in range(1, 17):
1458
  if f"์ž‘์„ฑ์ž {i}" in stages[stage_idx]["name"] or f"Writer {i}" in stages[stage_idx]["name"]:
1459
  writer_content_idx = stage_idx - 1
1460
  # Get previous writers' content from DB
 
1468
  )
1469
 
1470
  # Director final - DB์—์„œ ๋ชจ๋“  ์ž‘๊ฐ€ ๋‚ด์šฉ ๊ฐ€์ ธ์˜ค๊ธฐ
1471
+ elif stage_idx == self.total_stages - 1:
1472
  critic_final_idx = stage_idx - 1
1473
  all_writer_content = NovelDatabase.get_all_writer_content(self.current_session_id)
1474
  logger.info(f"Final director compilation with {len(all_writer_content)} characters of content")
 
1560
  novel_text = re.sub(r'(?:ํŽ˜์ด์ง€|Page)\s*\d+:', '', novel_text)
1561
 
1562
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
1563
+ word_count = len(novel_text.split())
1564
+ char_count = len(novel_text)
1565
+
1566
+ logger.info(f"Exporting novel: {word_count} words, {char_count} characters")
1567
 
1568
  if format == "DOCX" and DOCX_AVAILABLE:
1569
  # Create DOCX
1570
  doc = Document()
1571
 
1572
+ # ๋ฌธ์„œ ์ •๋ณด ์ถ”๊ฐ€
1573
+ doc.add_heading('Novel Export Information', 0)
1574
+ doc.add_paragraph(f'Total words: {word_count:,}')
1575
+ doc.add_paragraph(f'Total characters: {char_count:,}')
1576
+ doc.add_paragraph(f'Estimated pages: {word_count/500:.1f}')
1577
+ doc.add_paragraph(f'Export date: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
1578
+ doc.add_page_break()
1579
+
1580
  # Parse and add content
1581
  lines = novel_text.split('\n')
1582
  for line in lines:
 
1587
  if text:
1588
  doc.add_heading(text, level)
1589
  elif line.strip():
1590
+ # ๊ธด ๋‹จ๋ฝ๋„ ์ œ๋Œ€๋กœ ์ถ”๊ฐ€
1591
+ para = doc.add_paragraph(line)
1592
+ # ํฐํŠธ ํฌ๊ธฐ ์กฐ์ • (๊ฐ€๋…์„ฑ)
1593
+ if hasattr(para.style, 'font'):
1594
+ para.style.font.size = Pt(11)
1595
+
1596
+ # ํŽ˜์ด์ง€ ์„ค์ •
1597
+ section = doc.sections[0]
1598
+ section.page_height = Inches(11)
1599
+ section.page_width = Inches(8.5)
1600
+ section.left_margin = Inches(1)
1601
+ section.right_margin = Inches(1)
1602
+ section.top_margin = Inches(1)
1603
+ section.bottom_margin = Inches(1)
1604
 
1605
  # Save
1606
  temp_dir = tempfile.gettempdir()
1607
+ filename = f"Novel_{timestamp}_{word_count}words.docx"
1608
  filepath = os.path.join(temp_dir, filename)
1609
  doc.save(filepath)
1610
 
1611
+ logger.info(f"DOCX saved: {filepath}")
1612
  return filepath
1613
  else:
1614
  # TXT format
1615
  temp_dir = tempfile.gettempdir()
1616
+ filename = f"Novel_{timestamp}_{word_count}words.txt"
1617
  filepath = os.path.join(temp_dir, filename)
1618
 
1619
+ # ํŒŒ์ผ ์‹œ์ž‘ ๋ถ€๋ถ„์— ์ •๋ณด ์ถ”๊ฐ€
1620
  with open(filepath, 'w', encoding='utf-8') as f:
1621
+ f.write(f"=== Novel Export ===\n")
1622
+ f.write(f"Total words: {word_count:,}\n")
1623
+ f.write(f"Total characters: {char_count:,}\n")
1624
+ f.write(f"Estimated pages: {word_count/500:.1f}\n")
1625
+ f.write(f"Export date: {datetime.now()}\n")
1626
+ f.write("="*50 + "\n\n")
1627
  f.write(novel_text)
1628
 
1629
+ logger.info(f"TXT saved: {filepath}")
1630
  return filepath
1631
 
1632
  # Custom CSS
 
1697
  ๐Ÿ“š SOMA Novel Writing System
1698
  </h1>
1699
  <h3 style="color: #ccc; margin-bottom: 20px;">
1700
+ AI Collaborative Novel Generation - 48 Page Novella Creator
1701
  </h3>
1702
  <p style="font-size: 1.1em; color: #ddd; max-width: 800px; margin: 0 auto;">
1703
+ Enter a theme or prompt, and watch as 19 AI agents collaborate to create a complete 48-page novella.
1704
+ The system includes 1 Director, 1 Critic, and 16 Writers (each writing 3 pages) working in harmony.
1705
  All progress is automatically saved and can be resumed anytime.
1706
  </p>
1707
  </div>