openfree commited on
Commit
8866e62
ยท
verified ยท
1 Parent(s): b3ccac9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +298 -28
app.py CHANGED
@@ -486,16 +486,29 @@ class ScreenplayGenerationSystem:
486
  }
487
 
488
  def call_llm_streaming(self, messages: List[Dict[str, str]], max_tokens: int = 8000) -> Generator[str, None, None]:
 
489
  try:
 
 
 
 
 
 
 
 
 
 
 
 
490
  payload = {
491
  "model": self.model_id,
492
  "messages": messages,
493
  "max_tokens": max_tokens,
494
- "temperature": 0.7,
495
- "top_p": 0.9,
496
- "top_k": 40,
497
- "presence_penalty": 0.3,
498
- "frequency_penalty": 0.3,
499
  "stream": True
500
  }
501
 
@@ -514,6 +527,8 @@ class ScreenplayGenerationSystem:
514
  return
515
 
516
  buffer = ""
 
 
517
  for line in response.iter_lines():
518
  if not line:
519
  continue
@@ -525,20 +540,41 @@ class ScreenplayGenerationSystem:
525
 
526
  data_str = line_str[6:]
527
  if data_str == "[DONE]":
 
 
 
 
 
528
  break
529
 
530
  data = json.loads(data_str)
531
  if "choices" in data and len(data["choices"]) > 0:
532
  content = data["choices"][0].get("delta", {}).get("content", "")
533
  if content:
 
 
 
 
 
 
 
 
534
  buffer += content
535
- if len(buffer) >= 100 or '\n' in buffer:
 
 
 
 
536
  yield buffer
537
  buffer = ""
538
- except:
 
 
539
  continue
540
 
 
541
  if buffer:
 
542
  yield buffer
543
 
544
  except Exception as e:
@@ -705,11 +741,17 @@ class ScreenplayGenerationSystem:
705
 
706
  def generate_act(self, session_id: str, act_name: str, planning_data: Dict,
707
  previous_acts: Dict) -> Generator[Tuple[ActProgress, str], None, None]:
708
- """๋ง‰๋ณ„ ์‹œ๋‚˜๋ฆฌ์˜ค ์ƒ์„ฑ"""
 
709
  try:
710
  self.current_session_id = session_id
711
  self.planning_data = planning_data
712
 
 
 
 
 
 
713
  stages = ACT_WRITING_STAGES.get(act_name, [])
714
  act_progress = ActProgress(
715
  act_name=act_name,
@@ -732,29 +774,49 @@ class ScreenplayGenerationSystem:
732
  # ์—ญํ• ๋ณ„ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
733
  prompt = self._create_act_prompt(role, act_name, act_content, planning_data, previous_acts, stage_type)
734
 
735
- # LLM ํ˜ธ์ถœ
 
 
 
 
 
 
 
 
 
 
736
  messages = [
737
- {"role": "system", "content": f"๋‹น์‹ ์€ {role}์ž…๋‹ˆ๋‹ค. {EXPERT_ROLES[role]['description']}"},
738
  {"role": "user", "content": prompt}
739
  ]
740
 
741
  expert_output = ""
742
- for chunk in self.call_llm_streaming(messages, max_tokens=10000):
 
 
743
  if chunk and not chunk.startswith("โŒ"):
744
- expert_output += chunk
 
 
 
 
745
  # ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ
746
- yield act_progress, f"โœ๏ธ {role}: ์ž‘์—… ์ค‘..."
747
 
748
  # ์›น ๊ฒ€์ƒ‰ ๊ณ ์ฆ
749
  if role == "๊ณ ์ฆ์ „๋ฌธ๊ฐ€" and self.web_searcher.enabled:
750
  verification = self.web_searcher.verify_facts(act_content, act_name)
751
  expert_output += f"\n\nใ€์›น ๊ฒ€์ฆ ๊ฒฐ๊ณผใ€‘\n{json.dumps(verification, ensure_ascii=False, indent=2)}"
752
 
 
 
 
 
753
  # ํ”ผ๋“œ๋ฐฑ ์ €์žฅ
754
  feedback = ExpertFeedback(
755
  role=role,
756
  stage=f"{act_name}_{stage_type}",
757
- feedback=expert_output[:500],
758
  suggestions=self._extract_suggestions(expert_output),
759
  score=85.0 + random.uniform(-5, 10)
760
  )
@@ -767,7 +829,7 @@ class ScreenplayGenerationSystem:
767
  elif stage_type == "์ดˆ๊ณ ":
768
  act_content = expert_output
769
 
770
- yield act_progress, f"โœ… {role} ์ž‘์—… ์™„๋ฃŒ"
771
 
772
  time.sleep(0.5)
773
 
@@ -776,50 +838,258 @@ class ScreenplayGenerationSystem:
776
  break
777
  except Exception as e:
778
  logger.error(f"Error in act stage {idx}: {e}")
 
779
  continue
780
 
781
  # ๋ง‰ ์™„์„ฑ
782
  act_progress.status = "complete"
783
  ScreenplayDatabase.save_act_content(session_id, act_name, act_content)
784
 
785
- yield act_progress, f"โœ… {act_name} ์™„์„ฑ!"
 
 
786
 
787
  except Exception as e:
788
  logger.error(f"Act generation error: {e}\n{traceback.format_exc()}")
789
  act_progress.status = "error"
790
  yield act_progress, f"โŒ ์˜ค๋ฅ˜: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
791
 
792
  def _create_act_prompt(self, role: str, act_name: str, current_content: str,
793
  planning_data: Dict, previous_acts: Dict, stage_type: str) -> str:
794
- """๋ง‰ ์ž‘์„ฑ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
795
 
796
  if role == "์Šคํ† ๋ฆฌ์ž‘๊ฐ€":
797
  if stage_type == "์ดˆ๊ณ ":
798
  return f"""ใ€{act_name} ์ดˆ๊ณ  ์ž‘์„ฑใ€‘
799
- ์›๋ณธ ์š”์ฒญ: {self.original_query}
800
 
801
- ๊ธฐํš์•ˆ ์š”์•ฝ:
802
- {self._summarize_planning(planning_data)}
 
803
 
804
- ์ด์ „ ๋ง‰:
 
 
 
805
  {self._summarize_previous_acts(previous_acts)}
806
 
807
- ๋ชฉํ‘œ: 1000์ค„ ์ด์ƒ์˜ {act_name} ์ดˆ๊ณ ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.
808
- ํ‘œ์ค€ ์‹œ๋‚˜๋ฆฌ์˜ค ํฌ๋งท์„ ์‚ฌ์šฉํ•˜์„ธ์š”."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
809
  else: # ์™„์„ฑ
810
  return f"""ใ€{act_name} ์ตœ์ข… ์™„์„ฑใ€‘
 
 
811
  ์ด์ „ ์ „๋ฌธ๊ฐ€๋“ค์˜ ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ˜์˜ํ•˜์—ฌ ์ตœ์ข…๋ณธ์„ ์ž‘์„ฑํ•˜์„ธ์š”.
812
- ๋ชฉํ‘œ: 1200์ค„ ์ด์ƒ"""
 
 
 
 
 
 
 
813
 
814
  elif role == "๊ณ ์ฆ์ „๋ฌธ๊ฐ€":
815
  return f"""ใ€{act_name} ์‚ฌ์‹ค ํ™•์ธใ€‘
816
- ๋‹ค์Œ ๋‚ด์šฉ์„ ๊ฒ€ํ† ํ•˜๊ณ  ์‚ฌ์‹ค ์˜ค๋ฅ˜๋ฅผ ์ฐพ์œผ์„ธ์š”:
 
 
817
  {current_content[:2000]}...
818
 
819
- ์—ญ์‚ฌ์ , ๊ณผํ•™์ , ๋ฌธํ™”์  ์ •ํ™•์„ฑ์„ ํ™•์ธํ•˜์„ธ์š”."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
820
 
821
- # ๋‹ค๋ฅธ ์—ญํ• ๋“ค...
822
- return f"ใ€{role} ์ž‘์—…ใ€‘\n{act_name}์„(๋ฅผ) ๊ฒ€ํ† ํ•˜๊ณ  ๊ฐœ์„ ํ•˜์„ธ์š”."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
823
 
824
  def _summarize_planning(self, planning_data: Dict) -> str:
825
  """๊ธฐํš์•ˆ ์š”์•ฝ"""
 
486
  }
487
 
488
  def call_llm_streaming(self, messages: List[Dict[str, str]], max_tokens: int = 8000) -> Generator[str, None, None]:
489
+ """LLM ํ˜ธ์ถœ - ๊ฐœ์„ ๋œ ๋ฒ„์ „"""
490
  try:
491
+ # ํ•œ๊ตญ์–ด ๊ฐ•์ œ ๋ฐ ์™„์ „์„ฑ ๋ณด์žฅ์„ ์œ„ํ•œ ์‹œ์Šคํ…œ ๋ฉ”์‹œ์ง€ ๊ฐ•ํ™”
492
+ if messages and messages[0].get("role") == "system":
493
+ messages[0]["content"] = messages[0]["content"] + """
494
+
495
+ ใ€์ ˆ๋Œ€ ์ค€์ˆ˜ ์‚ฌํ•ญใ€‘
496
+ 1. ๋ชจ๋“  ์‘๋‹ต์„ ์™„์ „ํ•œ ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•˜์„ธ์š”.
497
+ 2. ์˜์–ด ๋‹จ์–ด๋‚˜ ๋ฌธ์žฅ์„ ์ ˆ๋Œ€ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”.
498
+ 3. ... ์ด๋‚˜ ๋ฏธ์™„์„ฑ ๋ฌธ์žฅ์„ ์ž‘์„ฑํ•˜์ง€ ๋งˆ์„ธ์š”.
499
+ 4. ๋ชจ๋“  ๋ฌธ์žฅ์„ ์™„์ „ํ•˜๊ฒŒ ๋งˆ๋ฌด๋ฆฌํ•˜์„ธ์š”.
500
+ 5. ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” ์˜ํ™” ํฌ๋งท์ด๋ฉฐ, ์—ฐ๊ทน์ด ์•„๋‹™๋‹ˆ๋‹ค.
501
+ """
502
+
503
  payload = {
504
  "model": self.model_id,
505
  "messages": messages,
506
  "max_tokens": max_tokens,
507
+ "temperature": 0.6, # ๋” ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ๋‚ฎ์ถค
508
+ "top_p": 0.85, # ๋” ๋ณด์ˆ˜์ ์œผ๋กœ
509
+ "top_k": 30, # ๋” ์ œํ•œ์ ์œผ๋กœ
510
+ "presence_penalty": 0.4, # ๋ฐ˜๋ณต ๋ฐฉ์ง€ ๊ฐ•ํ™”
511
+ "frequency_penalty": 0.4,
512
  "stream": True
513
  }
514
 
 
527
  return
528
 
529
  buffer = ""
530
+ incomplete_check = 0
531
+
532
  for line in response.iter_lines():
533
  if not line:
534
  continue
 
540
 
541
  data_str = line_str[6:]
542
  if data_str == "[DONE]":
543
+ # ๋งˆ์ง€๋ง‰์— ๋ถˆ์™„์ „ํ•œ ๋‚ด์šฉ ์ฒดํฌ
544
+ if buffer and "..." in buffer[-100:]:
545
+ buffer = buffer.replace("...", "")
546
+ if buffer:
547
+ yield buffer
548
  break
549
 
550
  data = json.loads(data_str)
551
  if "choices" in data and len(data["choices"]) > 0:
552
  content = data["choices"][0].get("delta", {}).get("content", "")
553
  if content:
554
+ # ์˜์–ด ๊ฐ์ง€ ๋ฐ ํ•„ํ„ฐ๋ง
555
+ if any(ord(c) < 128 and c.isalpha() for c in content):
556
+ # ์˜์–ด๊ฐ€ ํฌํ•จ๋œ ๊ฒฝ์šฐ ์Šคํ‚ตํ•˜๊ฑฐ๋‚˜ ๊ฒฝ๊ณ 
557
+ logger.warning(f"English detected in content: {content[:50]}")
558
+ # INT. EXT. ๊ฐ™์€ ์‹œ๋‚˜๋ฆฌ์˜ค ํฌ๋งท์€ ํ—ˆ์šฉ
559
+ if not any(term in content for term in ["INT.", "EXT.", "CUT TO", "FADE"]):
560
+ continue
561
+
562
  buffer += content
563
+
564
+ # ์ถฉ๋ถ„ํ•œ ๋ฒ„ํผ๊ฐ€ ์Œ“์ด๋ฉด yield
565
+ if len(buffer) >= 200 or '\n\n' in buffer:
566
+ # ๋ถˆ์™„์ „ํ•œ ... ์ œ๊ฑฐ
567
+ buffer = buffer.replace("......", "").replace(".....", "")
568
  yield buffer
569
  buffer = ""
570
+
571
+ except Exception as e:
572
+ logger.error(f"Line processing error: {e}")
573
  continue
574
 
575
+ # ๋‚จ์€ ๋ฒ„ํผ ์ฒ˜๋ฆฌ
576
  if buffer:
577
+ buffer = buffer.replace("......", "").replace(".....", "")
578
  yield buffer
579
 
580
  except Exception as e:
 
741
 
742
  def generate_act(self, session_id: str, act_name: str, planning_data: Dict,
743
  previous_acts: Dict) -> Generator[Tuple[ActProgress, str], None, None]:
744
+ """๋ง‰๋ณ„ ์‹œ๋‚˜๋ฆฌ์˜ค ์ƒ์„ฑ - ๊ฐœ์„ ๋œ ๋ฒ„์ „"""
745
+
746
  try:
747
  self.current_session_id = session_id
748
  self.planning_data = planning_data
749
 
750
+ # ์„ธ์…˜ ์ •๋ณด ๋ณต์›
751
+ session_data = ScreenplayDatabase.get_session_data(session_id)
752
+ if session_data:
753
+ self.original_query = session_data.get('user_query', '')
754
+
755
  stages = ACT_WRITING_STAGES.get(act_name, [])
756
  act_progress = ActProgress(
757
  act_name=act_name,
 
774
  # ์—ญํ• ๋ณ„ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
775
  prompt = self._create_act_prompt(role, act_name, act_content, planning_data, previous_acts, stage_type)
776
 
777
+ # ํ•œ๊ตญ์–ด ๋ฐ ์˜ํ™” ํฌ๋งท ๊ฐ•์กฐ
778
+ system_message = f"""๋‹น์‹ ์€ {role}์ž…๋‹ˆ๋‹ค.
779
+ {EXPERT_ROLES[role]['description']}
780
+
781
+ ใ€์ ˆ๋Œ€ ๊ทœ์น™ใ€‘
782
+ 1. ๋ชจ๋“  ๋‚ด์šฉ์„ ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•˜์„ธ์š”.
783
+ 2. ์˜ํ™” ์‹œ๋‚˜๋ฆฌ์˜ค ํฌ๋งท์„ ์‚ฌ์šฉํ•˜์„ธ์š” (์—ฐ๊ทน ์•„๋‹˜).
784
+ 3. ๊ธฐํš์•ˆ ๋‚ด์šฉ์„ 100% ๋ฐ˜์˜ํ•˜์„ธ์š”.
785
+ 4. ์˜์–ด ์‚ฌ์šฉ ๊ธˆ์ง€ (INT. EXT. ๋“ฑ ํฌ๋งท ์šฉ์–ด ์ œ์™ธ).
786
+ 5. ์™„์ „ํ•œ ๋ฌธ์žฅ์œผ๋กœ ์ž‘์„ฑ (... ์‚ฌ์šฉ ๊ธˆ์ง€)."""
787
+
788
  messages = [
789
+ {"role": "system", "content": system_message},
790
  {"role": "user", "content": prompt}
791
  ]
792
 
793
  expert_output = ""
794
+ line_count = 0
795
+
796
+ for chunk in self.call_llm_streaming(messages, max_tokens=12000):
797
  if chunk and not chunk.startswith("โŒ"):
798
+ # ์˜์–ด ํ•„ํ„ฐ๋ง (ํฌ๋งท ์šฉ์–ด ์ œ์™ธ)
799
+ cleaned_chunk = self._clean_output(chunk)
800
+ expert_output += cleaned_chunk
801
+ line_count = len(expert_output.split('\n'))
802
+
803
  # ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ
804
+ yield act_progress, f"โœ๏ธ {role}: ์ž‘์—… ์ค‘... ({line_count}์ค„ ์ž‘์„ฑ)"
805
 
806
  # ์›น ๊ฒ€์ƒ‰ ๊ณ ์ฆ
807
  if role == "๊ณ ์ฆ์ „๋ฌธ๊ฐ€" and self.web_searcher.enabled:
808
  verification = self.web_searcher.verify_facts(act_content, act_name)
809
  expert_output += f"\n\nใ€์›น ๊ฒ€์ฆ ๊ฒฐ๊ณผใ€‘\n{json.dumps(verification, ensure_ascii=False, indent=2)}"
810
 
811
+ # ์ถœ๋ ฅ ๊ฒ€์ฆ ๋ฐ ์ •๋ฆฌ
812
+ if stage_type in ["์ดˆ๊ณ ", "์™„์„ฑ"]:
813
+ expert_output = self._validate_and_clean_screenplay(expert_output)
814
+
815
  # ํ”ผ๋“œ๋ฐฑ ์ €์žฅ
816
  feedback = ExpertFeedback(
817
  role=role,
818
  stage=f"{act_name}_{stage_type}",
819
+ feedback=f"{role} ์ž‘์—… ์™„๋ฃŒ. {line_count}์ค„ ์ž‘์„ฑ.",
820
  suggestions=self._extract_suggestions(expert_output),
821
  score=85.0 + random.uniform(-5, 10)
822
  )
 
829
  elif stage_type == "์ดˆ๊ณ ":
830
  act_content = expert_output
831
 
832
+ yield act_progress, f"โœ… {role} ์ž‘์—… ์™„๋ฃŒ ({line_count}์ค„)"
833
 
834
  time.sleep(0.5)
835
 
 
838
  break
839
  except Exception as e:
840
  logger.error(f"Error in act stage {idx}: {e}")
841
+ yield act_progress, f"โš ๏ธ {role} ์ž‘์—… ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ"
842
  continue
843
 
844
  # ๋ง‰ ์™„์„ฑ
845
  act_progress.status = "complete"
846
  ScreenplayDatabase.save_act_content(session_id, act_name, act_content)
847
 
848
+ # ์ตœ์ข… ํŽ˜์ด์ง€ ์ˆ˜ ๊ณ„์‚ฐ
849
+ page_count = len(act_content.split('\n')) / 58
850
+ yield act_progress, f"โœ… {act_name} ์™„์„ฑ! (์•ฝ {page_count:.1f}ํŽ˜์ด์ง€)"
851
 
852
  except Exception as e:
853
  logger.error(f"Act generation error: {e}\n{traceback.format_exc()}")
854
  act_progress.status = "error"
855
  yield act_progress, f"โŒ ์˜ค๋ฅ˜: {str(e)}"
856
+
857
+ def _clean_output(self, text: str) -> str:
858
+ """์ถœ๋ ฅ ํ…์ŠคํŠธ ์ •๋ฆฌ"""
859
+ # ์˜์–ด ๋‹จ์–ด ์ œ๊ฑฐ (์‹œ๋‚˜๋ฆฌ์˜ค ํฌ๋งท ์ œ์™ธ)
860
+ format_terms = ["INT.", "EXT.", "CUT TO:", "FADE IN:", "FADE OUT:", "DISSOLVE TO:"]
861
+
862
+ lines = text.split('\n')
863
+ cleaned_lines = []
864
+
865
+ for line in lines:
866
+ # ํฌ๋งท ์šฉ์–ด๋Š” ์œ ์ง€
867
+ if any(term in line for term in format_terms):
868
+ cleaned_lines.append(line)
869
+ else:
870
+ # ์˜์–ด ๊ฐ์ง€ ๋ฐ ์ œ๊ฑฐ
871
+ if not self._contains_english(line):
872
+ # ๋ถˆ์™„์ „ํ•œ ๋ถ€๋ถ„ ์ œ๊ฑฐ
873
+ line = line.replace("......", "").replace(".....", "").replace("...", "")
874
+ cleaned_lines.append(line)
875
+ else:
876
+ # ์˜์–ด๊ฐ€ ํฌํ•จ๋œ ์ค„์€ ์Šคํ‚ตํ•˜๊ฑฐ๋‚˜ ๊ฒฝ๊ณ 
877
+ logger.warning(f"Skipping English line: {line[:50]}")
878
+
879
+ return '\n'.join(cleaned_lines)
880
+
881
+ def _contains_english(self, text: str) -> bool:
882
+ """์˜์–ด ํฌํ•จ ์—ฌ๋ถ€ ํ™•์ธ (ํฌ๋งท ์šฉ์–ด ์ œ์™ธ)"""
883
+ format_terms = ["INT", "EXT", "CUT", "FADE", "DISSOLVE"]
884
+
885
+ # ํฌ๋งท ์šฉ์–ด ์ œ๊ฑฐ ํ›„ ํ™•์ธ
886
+ test_text = text
887
+ for term in format_terms:
888
+ test_text = test_text.replace(term, "")
889
+
890
+ # ์•ŒํŒŒ๋ฒณ ์—ฐ์† 3๊ฐœ ์ด์ƒ์ด๋ฉด ์˜์–ด๋กœ ํŒ๋‹จ
891
+ import re
892
+ english_pattern = re.compile(r'[a-zA-Z]{3,}')
893
+ return bool(english_pattern.search(test_text))
894
+
895
+ def _validate_and_clean_screenplay(self, content: str) -> str:
896
+ """์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒ€์ฆ ๋ฐ ์ •๋ฆฌ"""
897
+ if not content:
898
+ return ""
899
+
900
+ lines = content.split('\n')
901
+ cleaned = []
902
+
903
+ for line in lines:
904
+ # ๋นˆ ์ค„์€ ์œ ์ง€
905
+ if not line.strip():
906
+ cleaned.append(line)
907
+ continue
908
+
909
+ # ๋ถˆ์™„์ „ํ•œ ๋ถ€๋ถ„ ์ œ๊ฑฐ
910
+ if "..." in line and not line.strip().endswith('.'):
911
+ continue
912
+
913
+ # ์˜์–ด ์ฒดํฌ (ํฌ๋งท ์ œ์™ธ)
914
+ if self._contains_english(line):
915
+ logger.warning(f"Removing English line: {line[:50]}")
916
+ continue
917
+
918
+ cleaned.append(line)
919
+
920
+ return '\n'.join(cleaned)
921
+
922
+ @staticmethod
923
+ def get_session_data(session_id: str) -> Optional[Dict]:
924
+ """์„ธ์…˜ ๋ฐ์ดํ„ฐ ์กฐํšŒ"""
925
+ with ScreenplayDatabase.get_db() as conn:
926
+ row = conn.cursor().execute(
927
+ 'SELECT * FROM screenplay_sessions WHERE session_id = ?',
928
+ (session_id,)
929
+ ).fetchone()
930
+ if row:
931
+ return dict(row)
932
+ return None
933
 
934
  def _create_act_prompt(self, role: str, act_name: str, current_content: str,
935
  planning_data: Dict, previous_acts: Dict, stage_type: str) -> str:
936
+ """๋ง‰ ์ž‘์„ฑ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ - ๊ฐœ์„ ๋œ ๋ฒ„์ „"""
937
+
938
+ # ๊ธฐํš์•ˆ์—์„œ ํ•ต์‹ฌ ์ •๋ณด ์ถ”์ถœ
939
+ title = ""
940
+ genre = ""
941
+ screenplay_type = ""
942
+ logline = ""
943
+ synopsis = ""
944
+ characters = ""
945
+
946
+ for key, value in planning_data.items():
947
+ if "ํ”„๋กœ๋“€์„œ" in key and value:
948
+ # ์ œ๋ชฉ๊ณผ ๋กœ๊ทธ๋ผ์ธ ์ถ”์ถœ
949
+ if "์ œ๋ชฉ" in value:
950
+ title_match = re.search(r'์ œ๋ชฉ[:\s]*([^\n]+)', value)
951
+ if title_match:
952
+ title = title_match.group(1).strip()
953
+ if "๋กœ๊ทธ๋ผ์ธ" in value:
954
+ logline_match = re.search(r'๋กœ๊ทธ๋ผ์ธ[:\s]*([^\n]+)', value)
955
+ if logline_match:
956
+ logline = logline_match.group(1).strip()
957
+ elif "์Šคํ† ๋ฆฌ" in key and value:
958
+ synopsis = value[:1000]
959
+ elif "์บ๋ฆญํ„ฐ" in key and value:
960
+ characters = value[:1000]
961
+
962
+ # ์›๋ณธ ์š”์ฒญ ์ •๋ณด ๊ฐ•์กฐ
963
+ core_info = f"""
964
+ ใ€ํ•ต์‹ฌ ์ •๋ณด - ์ ˆ๋Œ€ ์ค€์ˆ˜ใ€‘
965
+ ์›๋ณธ ์š”์ฒญ: {self.original_query}
966
+ ์ œ๋ชฉ: {title}
967
+ ๋กœ๊ทธ๋ผ์ธ: {logline}
968
+ ์žฅ๋ฅด: {genre if genre else '๋“œ๋ผ๋งˆ'}
969
+ ํ˜•์‹: {screenplay_type if screenplay_type else '์˜ํ™”'}
970
+
971
+ โš ๏ธ ์ค‘์š”: ์œ„ ์ •๋ณด๋ฅผ ์ ˆ๋Œ€์ ์œผ๋กœ ์ค€์ˆ˜ํ•˜์„ธ์š”. ๋‹ค๋ฅธ ์ด์•ผ๊ธฐ๋กœ ๋ฐ”๊พธ์ง€ ๋งˆ์„ธ์š”.
972
+ โš ๏ธ ์ค‘์š”: ๋ชจ๋“  ๋‚ด์šฉ์„ ํ•œ๊ตญ์–ด๋กœ๋งŒ ์ž‘์„ฑํ•˜์„ธ์š”. ์˜์–ด ์‚ฌ์šฉ ๊ธˆ์ง€.
973
+ โš ๏ธ ์ค‘์š”: ํ‘œ์ค€ ์‹œ๋‚˜๋ฆฌ์˜ค ํฌ๋งท์„ ์ •ํ™•ํžˆ ์ค€์ˆ˜ํ•˜์„ธ์š”.
974
+ """
975
 
976
  if role == "์Šคํ† ๋ฆฌ์ž‘๊ฐ€":
977
  if stage_type == "์ดˆ๊ณ ":
978
  return f"""ใ€{act_name} ์ดˆ๊ณ  ์ž‘์„ฑใ€‘
979
+ {core_info}
980
 
981
+ ใ€๊ธฐํš์•ˆ ๋‚ด์šฉใ€‘
982
+ ์‹œ๋†‰์‹œ์Šค:
983
+ {synopsis}
984
 
985
+ ์บ๋ฆญํ„ฐ:
986
+ {characters}
987
+
988
+ ใ€์ด์ „ ๋ง‰ ๋‚ด์šฉใ€‘
989
  {self._summarize_previous_acts(previous_acts)}
990
 
991
+ ใ€์ž‘์„ฑ ์š”๊ตฌ์‚ฌํ•ญใ€‘
992
+ 1. ๋ฐ˜๋“œ์‹œ ํ•œ๊ตญ์–ด๋กœ๋งŒ ์ž‘์„ฑ
993
+ 2. ํ‘œ์ค€ ์‹œ๋‚˜๋ฆฌ์˜ค ํฌ๋งท:
994
+ INT. ์žฅ์†Œ - ์‹œ๊ฐ„ (๋˜๋Š” EXT. ์žฅ์†Œ - ์‹œ๊ฐ„)
995
+
996
+ ์žฅ๋ฉด ์„ค๋ช…์€ ํ˜„์žฌํ˜•์œผ๋กœ ์ž‘์„ฑ.
997
+
998
+ ์บ๋ฆญํ„ฐ๋ช… (๋Œ€๋ฌธ์ž)
999
+ ๋Œ€์‚ฌ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ํ•œ๊ตญ์–ด๋กœ.
1000
+
1001
+ 3. ๋ถ„๋Ÿ‰: ์ตœ์†Œ 1000์ค„ ์ด์ƒ
1002
+ 4. ๊ฐ ์”ฌ: 5-7ํŽ˜์ด์ง€ ๋ถ„๋Ÿ‰
1003
+ 5. ๊ธฐํš์•ˆ ๋‚ด์šฉ 100% ๋ฐ˜์˜
1004
+
1005
+ ์ ˆ๋Œ€ ์˜์–ด๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”.
1006
+ ์ ˆ๋Œ€ ๋‹ค๋ฅธ ์ด์•ผ๊ธฐ๋กœ ๋ฐ”๊พธ์ง€ ๋งˆ์„ธ์š”.
1007
+ ์ ˆ๋Œ€ ์—ฐ๊ทน์ด๋‚˜ ๋‹ค๋ฅธ ํ˜•์‹์œผ๋กœ ์ž‘์„ฑํ•˜์ง€ ๋งˆ์„ธ์š”.
1008
+
1009
+ {act_name}์„ ์™„์ „ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค ํ˜•์‹์œผ๋กœ ์ž‘์„ฑํ•˜์„ธ์š”."""
1010
+
1011
  else: # ์™„์„ฑ
1012
  return f"""ใ€{act_name} ์ตœ์ข… ์™„์„ฑใ€‘
1013
+ {core_info}
1014
+
1015
  ์ด์ „ ์ „๋ฌธ๊ฐ€๋“ค์˜ ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ˜์˜ํ•˜์—ฌ ์ตœ์ข…๋ณธ์„ ์ž‘์„ฑํ•˜์„ธ์š”.
1016
+
1017
+ ใ€ํ•„์ˆ˜ ์‚ฌํ•ญใ€‘
1018
+ 1. ํ•œ๊ตญ์–ด๋กœ๋งŒ ์ž‘์„ฑ
1019
+ 2. ์˜ํ™” ์‹œ๋‚˜๋ฆฌ์˜ค ํฌ๋งท ์ค€์ˆ˜
1020
+ 3. ๊ธฐํš์•ˆ ์Šคํ† ๋ฆฌ ์œ ์ง€
1021
+ 4. 1200์ค„ ์ด์ƒ
1022
+
1023
+ ๊นจ์ง„ ๋ถ€๋ถ„์ด๋‚˜ ... ๊ฐ™์€ ๋ฏธ์™„์„ฑ ๋ถ€๋ถ„ ์—†์ด ์™„์ „ํ•˜๊ฒŒ ์ž‘์„ฑํ•˜์„ธ์š”."""
1024
 
1025
  elif role == "๊ณ ์ฆ์ „๋ฌธ๊ฐ€":
1026
  return f"""ใ€{act_name} ์‚ฌ์‹ค ํ™•์ธใ€‘
1027
+ {core_info}
1028
+
1029
+ ๋‹ค์Œ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ฒ€ํ† ํ•˜์„ธ์š”:
1030
  {current_content[:2000]}...
1031
 
1032
+ ใ€๊ฒ€ํ†  ์‚ฌํ•ญใ€‘
1033
+ 1. ์‚ฌ์‹ค ์˜ค๋ฅ˜ ํ™•์ธ
1034
+ 2. ์‹œ๋Œ€ ๊ณ ์ฆ
1035
+ 3. ์˜์–ด ์‚ฌ์šฉ ๋ถ€๋ถ„์„ ํ•œ๊ตญ์–ด๋กœ ์ˆ˜์ •
1036
+ 4. ๊นจ์ง„ ํ…์ŠคํŠธ๋‚˜ ... ๋ถ€๋ถ„ ์ˆ˜์ •
1037
+
1038
+ ๋ชจ๋“  ์ˆ˜์ • ์‚ฌํ•ญ์„ ํ•œ๊ตญ์–ด๋กœ ์ œ์‹œํ•˜์„ธ์š”."""
1039
+
1040
+ elif role == "ํŽธ์ง‘์ž":
1041
+ return f"""ใ€{act_name} ํŽธ์ง‘ใ€‘
1042
+ {core_info}
1043
+
1044
+ ใ€ํŽธ์ง‘ ์ง€์นจใ€‘
1045
+ 1. ์˜์–ด๊ฐ€ ์žˆ๋‹ค๋ฉด ๋ชจ๋‘ ํ•œ๊ตญ์–ด๋กœ ๋ณ€๊ฒฝ
1046
+ 2. ๊นจ์ง„ ๋ถ€๋ถ„(...) ์™„์„ฑ
1047
+ 3. ๊ธฐํš์•ˆ๊ณผ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ๋‚ด์šฉ ์ˆ˜์ •
1048
+ 4. ์‹œ๋‚˜๋ฆฌ์˜ค ํฌ๋งท ๊ต์ •
1049
+
1050
+ ํŽธ์ง‘๋œ ๋ฒ„์ „์„ ์™„์ „ํ•œ ํ•œ๊ตญ์–ด๋กœ ์ œ์‹œํ•˜์„ธ์š”."""
1051
+
1052
+ elif role == "๊ฐ๋…":
1053
+ return f"""ใ€{act_name} ์—ฐ์ถœ ๋…ธํŠธใ€‘
1054
+ {core_info}
1055
+
1056
+ ใ€์—ฐ์ถœ ๊ฐ•ํ™”ใ€‘
1057
+ 1. ์‹œ๊ฐ์  ๋””ํ…Œ์ผ ์ถ”๊ฐ€
1058
+ 2. ์นด๋ฉ”๋ผ ์•ต๊ธ€ ์ œ์•ˆ
1059
+ 3. ๋ถ„์œ„๊ธฐ ๋ฌ˜์‚ฌ ๊ฐ•ํ™”
1060
+
1061
+ ๋ชจ๋“  ์—ฐ์ถœ ๋…ธํŠธ๋ฅผ ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•˜์„ธ์š”.
1062
+ ์˜ํ™” ์‹œ๋‚˜๋ฆฌ์˜ค์ž„์„ ๋ช…ํ™•ํžˆ ํ•˜์„ธ์š”."""
1063
+
1064
+ elif role == "๋Œ€ํ™”์ „๋ฌธ๊ฐ€":
1065
+ return f"""ใ€{act_name} ๋Œ€์‚ฌ ๊ฐœ์„ ใ€‘
1066
+ {core_info}
1067
+
1068
+ ใ€๋Œ€์‚ฌ ์ˆ˜์ •ใ€‘
1069
+ 1. ๋ชจ๋“  ๋Œ€์‚ฌ๋ฅผ ์ž์—ฐ์Šค๋Ÿฌ์šด ํ•œ๊ตญ์–ด๋กœ
1070
+ 2. ์˜์–ด ๋Œ€์‚ฌ๊ฐ€ ์žˆ๋‹ค๋ฉด ํ•œ๊ตญ์–ด๋กœ ๋ฒˆ์—ญ
1071
+ 3. ์บ๋ฆญํ„ฐ๋ณ„ ๋งํˆฌ ์ฐจ๋ณ„ํ™”
1072
+ 4. ์–ด์ƒ‰ํ•œ ๋ฒˆ์—ญํˆฌ ์ œ๊ฑฐ
1073
+
1074
+ ์™„์ „ํ•œ ํ•œ๊ตญ์–ด ๋Œ€์‚ฌ๋กœ ์ˆ˜์ •ํ•˜์„ธ์š”."""
1075
 
1076
+ elif role == "๋น„ํ‰๊ฐ€":
1077
+ return f"""ใ€{act_name} ๊ฒ€ํ† ใ€‘
1078
+ {core_info}
1079
+
1080
+ ใ€ํ‰๊ฐ€ ํ•ญ๋ชฉใ€‘
1081
+ 1. ๊ธฐํš์•ˆ๊ณผ์˜ ์ผ์น˜๋„ (์ตœ์šฐ์„ )
1082
+ 2. ํ•œ๊ตญ์–ด ์‚ฌ์šฉ (์˜์–ด ํ˜ผ์šฉ ๊ธˆ์ง€)
1083
+ 3. ์‹œ๋‚˜๋ฆฌ์˜ค ํฌ๋งท ์ค€์ˆ˜
1084
+ 4. ์Šคํ† ๋ฆฌ ์™„์„ฑ๋„
1085
+
1086
+ ๋ฌธ์ œ์ ๊ณผ ๊ฐœ์„  ์‚ฌํ•ญ์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์ œ์‹œํ•˜์„ธ์š”."""
1087
+
1088
+ return f"""ใ€{role} ์ž‘์—…ใ€‘
1089
+ {core_info}
1090
+
1091
+ {act_name}์„(๋ฅผ) ๊ฒ€ํ† ํ•˜๊ณ  ๊ฐœ์„ ํ•˜์„ธ์š”.
1092
+ ๋ฐ˜๋“œ์‹œ ํ•œ๊ตญ์–ด๋กœ๋งŒ ์ž‘์„ฑํ•˜๊ณ , ๊ธฐํš์•ˆ ๋‚ด์šฉ์„ ์ถฉ์‹คํžˆ ๋ฐ˜์˜ํ•˜์„ธ์š”."""
1093
 
1094
  def _summarize_planning(self, planning_data: Dict) -> str:
1095
  """๊ธฐํš์•ˆ ์š”์•ฝ"""