openfree commited on
Commit
1f8ce9c
Β·
verified Β·
1 Parent(s): e067209

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -3
app.py CHANGED
@@ -756,14 +756,25 @@ Provide specific, practical improvements."""
756
  def generate_random_webnovel_theme(genre: str, language: str) -> str:
757
  """Generate random web novel theme using novel_themes.json and LLM"""
758
  try:
759
- # Load novel_themes.json
760
  json_path = Path("novel_themes.json")
761
  if not json_path.exists():
762
  logger.warning("novel_themes.json not found, using fallback")
763
  return generate_fallback_theme(genre, language)
764
 
765
- with open(json_path, 'r', encoding='utf-8') as f:
766
- themes_data = json.load(f)
 
 
 
 
 
 
 
 
 
 
 
767
 
768
  # Map genres to theme data
769
  genre_mapping = {
@@ -968,6 +979,88 @@ def generate_fallback_theme(genre: str, language: str) -> str:
968
  # Simple translation logic
969
  return selected.replace("νšŒκ·€", "regression").replace("λΉ™μ˜", "transmigration")
970
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
971
  # --- UI functions ---
972
  def format_episodes_display(episodes: List[Dict], current_episode: int = 0) -> str:
973
  """Format episodes for display"""
 
756
  def generate_random_webnovel_theme(genre: str, language: str) -> str:
757
  """Generate random web novel theme using novel_themes.json and LLM"""
758
  try:
759
+ # Load novel_themes.json with error handling
760
  json_path = Path("novel_themes.json")
761
  if not json_path.exists():
762
  logger.warning("novel_themes.json not found, using fallback")
763
  return generate_fallback_theme(genre, language)
764
 
765
+ try:
766
+ with open(json_path, 'r', encoding='utf-8') as f:
767
+ content = f.read()
768
+ # Try to fix common JSON errors
769
+ content = content.replace("'", '"') # Replace single quotes with double quotes
770
+ content = re.sub(r',\s*}', '}', content) # Remove trailing commas before }
771
+ content = re.sub(r',\s*]', ']', content) # Remove trailing commas before ]
772
+ themes_data = json.loads(content)
773
+ except json.JSONDecodeError as e:
774
+ logger.error(f"JSON parsing error: {e}")
775
+ logger.error(f"Error at position: {e.pos if hasattr(e, 'pos') else 'unknown'}")
776
+ # If JSON parsing fails, use LLM with genre-specific prompt
777
+ return generate_theme_with_llm_only(genre, language)
778
 
779
  # Map genres to theme data
780
  genre_mapping = {
 
979
  # Simple translation logic
980
  return selected.replace("νšŒκ·€", "regression").replace("λΉ™μ˜", "transmigration")
981
 
982
+ def generate_theme_with_llm_only(genre: str, language: str) -> str:
983
+ """Generate theme using only LLM when JSON is not available or has errors"""
984
+ system = WebNovelSystem()
985
+
986
+ # Genre-specific prompts based on popular web novel trends
987
+ genre_prompts = {
988
+ "둜맨슀": {
989
+ "elements": ["κ³„μ•½κ²°ν˜Ό", "재벌", "이혼", "μ²«μ‚¬λž‘", "운λͺ…적 λ§Œλ‚¨", "μ˜€ν•΄μ™€ ν™”ν•΄"],
990
+ "hooks": ["기얡상싀", "정체 숨기기", "κ°€μ§œ 연인", "μ›λ‚˜μž‡ ν›„ 재회"]
991
+ },
992
+ "둜판": {
993
+ "elements": ["λΉ™μ˜", "νšŒκ·€", "μ•…λ…€", "ν™©λ…€", "κ³΅μž‘", "μ›μž‘ 파괴"],
994
+ "hooks": ["μ²˜ν˜• 직전", "파혼 μ„ μ–Έ", "독살 μ‹œλ„", "νμœ„ μœ„κΈ°"]
995
+ },
996
+ "νŒνƒ€μ§€": {
997
+ "elements": ["μ‹œμŠ€ν…œ", "각성", "λ˜μ „", "νšŒκ·€", "탑 λ“±λ°˜", "SSSκΈ‰"],
998
+ "hooks": ["FκΈ‰μ—μ„œ μ‹œμž‘", "μˆ¨κ²¨μ§„ 클래슀", "유일무이 μŠ€ν‚¬", "죽음 ν›„ 각성"]
999
+ },
1000
+ "ν˜„νŒ": {
1001
+ "elements": ["ν—Œν„°", "게이트", "κ°μ„±μž", "κΈΈλ“œ", "μ•„μ΄ν…œ", "λž­ν‚Ή"],
1002
+ "hooks": ["λŠ¦μ€ 각성", "재λŠ₯ μž¬ν‰κ°€", "SκΈ‰ 게이트", "μ‹œμŠ€ν…œ 였λ₯˜"]
1003
+ },
1004
+ "λ¬΄ν˜‘": {
1005
+ "elements": ["νšŒκ·€", "천재", "마ꡐ", "λΉ„κΈ‰", "볡수", "ν™˜μƒ"],
1006
+ "hooks": ["νκΈ‰μ—μ„œ μ΅œκ°•", "λ°°μ‹  ν›„ 각성", "μˆ¨κ²¨μ§„ ν˜ˆν†΅", "κΈ°μ—° νšλ“"]
1007
+ },
1008
+ "λ―ΈμŠ€ν„°λ¦¬": {
1009
+ "elements": ["탐정", "연쇄살인", "νƒ€μž„λ£¨ν”„", "초λŠ₯λ ₯", "과거의 λΉ„λ°€"],
1010
+ "hooks": ["λ°€μ‹€ 살인", "예고 살인", "κΈ°μ–΅ μ‘°μž‘", "μ‹œκ°„ μ—­ν–‰"]
1011
+ },
1012
+ "λΌμ΄νŠΈλ…Έλ²¨": {
1013
+ "elements": ["학원", "이세계", "히둜인", "κ²Œμž„", "일상", "νŒνƒ€μ§€"],
1014
+ "hooks": ["전학생 정체", "κ²Œμž„ ν˜„μ‹€ν™”", "평행세계", "μˆ¨κ²¨μ§„ λŠ₯λ ₯"]
1015
+ }
1016
+ }
1017
+
1018
+ genre_info = genre_prompts.get(genre, genre_prompts["둜맨슀"])
1019
+
1020
+ if language == "Korean":
1021
+ prompt = f"""ν•œκ΅­ μ›Ήμ†Œμ„€ {genre} μž₯λ₯΄μ˜ 쀑독성 μžˆλŠ” ν…Œλ§ˆλ₯Ό μƒμ„±ν•˜μ„Έμš”.
1022
+
1023
+ λ‹€μŒ 인기 μš”μ†Œλ“€μ„ μ°Έκ³ ν•˜μ„Έμš”:
1024
+ - 핡심 μš”μ†Œ: {', '.join(genre_info['elements'])}
1025
+ - 인기 ν›…: {', '.join(genre_info['hooks'])}
1026
+
1027
+ μš”κ΅¬μ‚¬ν•­:
1028
+ 1. 200자 λ‚΄μ™Έμ˜ μ§§κ³  μž„νŒ©νŠΈ μžˆλŠ” μ„€μ •
1029
+ 2. 첫 λ¬Έμž₯은 λ…μžλ₯Ό μ‚¬λ‘œμž‘λŠ” κ°•λ ¬ν•œ ν›…
1030
+ 3. 주인곡의 νŠΉλ³„ν•œ λŠ₯λ ₯μ΄λ‚˜ 상황 λͺ…μ‹œ
1031
+ 4. κ°ˆλ“±κ³Ό λͺ©ν‘œκ°€ λͺ…ν™•νžˆ λ“œλŸ¬λ‚˜λ„λ‘
1032
+ 5. 제λͺ©μ΄ λ– μ˜€λ₯Ό μ •λ„λ‘œ κ΅¬μ²΄μ μ΄λ©΄μ„œ ν₯미둜운 μ„€μ •
1033
+
1034
+ μ˜ˆμ‹œ ν˜•μ‹:
1035
+ "[κ°•λ ¬ν•œ 첫 λ¬Έμž₯]"
1036
+ [주인곡 μ†Œκ°œμ™€ νŠΉλ³„ν•œ 상황]
1037
+ [핡심 κ°ˆλ“±κ³Ό λͺ©ν‘œ]
1038
+ [λ…μžμ˜ κΈ°λŒ€κ°μ„ μžκ·Ήν•˜λŠ” 마무리]"""
1039
+ else:
1040
+ prompt = f"""Generate an addictive Korean web novel theme for {genre} genre.
1041
+
1042
+ Reference these popular elements:
1043
+ - Core elements: {', '.join(genre_info['elements'])}
1044
+ - Popular hooks: {', '.join(genre_info['hooks'])}
1045
+
1046
+ Requirements:
1047
+ 1. Around 200 characters, short and impactful
1048
+ 2. First sentence must be a powerful hook
1049
+ 3. Clear special ability or situation for protagonist
1050
+ 4. Clear conflict and goal
1051
+ 5. Specific and intriguing enough to suggest a title
1052
+
1053
+ Example format:
1054
+ "[Powerful opening sentence]"
1055
+ [Protagonist introduction and special situation]
1056
+ [Core conflict and goal]
1057
+ [Ending that sparks reader anticipation]"""
1058
+
1059
+ messages = [{"role": "user", "content": prompt}]
1060
+ generated_theme = system.call_llm_sync(messages, "writer", language)
1061
+
1062
+ return generated_theme
1063
+
1064
  # --- UI functions ---
1065
  def format_episodes_display(episodes: List[Dict], current_episode: int = 0) -> str:
1066
  """Format episodes for display"""