Spaces:
Running
Running
Update app.py
Browse files
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 |
-
|
766 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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"""
|