Forráskód Böngészése

refactor(genres): align profile fallbacks with taxonomy

lingfengQAQ 2 hete
szülő
commit
99b8b53942

+ 6 - 1
webnovel-writer/scripts/data_modules/context_manager.py

@@ -319,7 +319,12 @@ class ContextManager:
         fallback = str(getattr(self.config, "context_genre_profile_fallback", "shuangwen") or "shuangwen")
         project = state.get("project") or {}
         project_info = state.get("project_info") or {}
-        genre_raw = str(project.get("genre") or project_info.get("genre") or fallback)
+        genre_raw = str(
+            project_info.get("genre")
+            or project_info.get("genre_label")
+            or project.get("genre")
+            or fallback
+        )
         genres = self._parse_genre_tokens(genre_raw)
         if not genres:
             genres = [fallback]

+ 4 - 19
webnovel-writer/scripts/data_modules/genre_aliases.py

@@ -6,26 +6,11 @@ Genre alias normalization and profile key mapping.
 
 from __future__ import annotations
 
-
-GENRE_INPUT_ALIASES: dict[str, str] = {
-    "修仙/玄幻": "修仙",
-    "玄幻修仙": "修仙",
-    "玄幻": "修仙",
-    "修真": "修仙",
-    "都市修真": "都市异能",
-    "都市高武": "高武",
-    "都市奇闻": "都市脑洞",
-    "古言脑洞": "古言",
-    "游戏电竞": "电竞",
-    "电竞文": "电竞",
-    "直播": "直播文",
-    "直播带货": "直播文",
-    "主播": "直播文",
-    "克系": "克苏鲁",
-    "克系悬疑": "克苏鲁",
-}
+from genre_taxonomy import normalize_genre_label_for_profile
 
 
+# Fallback profile keys are a legacy markdown-section namespace, not canonical
+# genre values. User-input aliases live in references/taxonomy/genre-index.csv.
 GENRE_PROFILE_KEY_ALIASES: dict[str, str] = {
     "修仙": "xianxia",
     "修仙/玄幻": "xianxia",
@@ -54,7 +39,7 @@ def normalize_genre_token(token: str) -> str:
     value = str(token or "").strip()
     if not value:
         return ""
-    return GENRE_INPUT_ALIASES.get(value, value)
+    return normalize_genre_label_for_profile(value)
 
 
 def to_profile_key(genre: str) -> str:

+ 8 - 1
webnovel-writer/scripts/data_modules/memory_contract_adapter.py

@@ -232,7 +232,14 @@ class MemoryContractAdapter:
             if not genre:
                 sm = self._state_manager()
                 sm._load_state()
-                genre = str(sm._state.get("project", {}).get("genre", "")).strip()
+                project_info = sm._state.get("project_info", {}) or {}
+                legacy_project = sm._state.get("project", {}) or {}
+                genre = str(
+                    project_info.get("genre")
+                    or project_info.get("genre_label")
+                    or legacy_project.get("genre")
+                    or ""
+                ).strip()
             if genre:
                 profile_path = self.config.project_root / ".claude" / "references" / "genre-profiles.md"
                 if profile_path.exists():

+ 3 - 3
webnovel-writer/scripts/data_modules/tests/test_context_manager.py

@@ -861,7 +861,7 @@ def test_context_manager_genre_profile_fallbacks_to_project_info(temp_project):
     assert profile.get("genre") == "xuanhuan"
 
 
-def test_context_manager_genre_profile_prefers_project_over_project_info(temp_project):
+def test_context_manager_genre_profile_prefers_project_info_over_project(temp_project):
     manager = ContextManager(temp_project)
 
     profile = manager._load_genre_profile(
@@ -871,8 +871,8 @@ def test_context_manager_genre_profile_prefers_project_over_project_info(temp_pr
         }
     )
 
-    assert profile.get("genre_raw") == "xuanhuan"
-    assert profile.get("genre") == "xuanhuan"
+    assert profile.get("genre_raw") == "dushi"
+    assert profile.get("genre") == "dushi"
 
 
 def test_context_manager_includes_plot_structure_when_outline_has_nodes(temp_project):

+ 15 - 0
webnovel-writer/scripts/data_modules/tests/test_memory_contract_adapter.py

@@ -338,6 +338,21 @@ class TestLoadContext:
         assert pack.sections["runtime_status"]["primary_write_source"] == "chapter_commit"
         assert pack.sections["latest_commit"]["meta"]["status"] == "accepted"
 
+    def test_load_context_genre_profile_fallback_reads_project_info(self, tmp_path):
+        cfg = _make_project(tmp_path)
+        (cfg.webnovel_dir / "state.json").write_text(
+            json.dumps({"project_info": {"genre": "规则怪谈"}}, ensure_ascii=False),
+            encoding="utf-8",
+        )
+        refs_dir = tmp_path / ".claude" / "references"
+        refs_dir.mkdir(parents=True, exist_ok=True)
+        (refs_dir / "genre-profiles.md").write_text("## 规则怪谈\n- 规则优先", encoding="utf-8")
+
+        adapter = MemoryContractAdapter(cfg)
+        pack = adapter.load_context(1)
+
+        assert "规则优先" in pack.sections["genre_profile_excerpt"]
+
     def test_load_context_prefers_actual_latest_commit_status(self, tmp_path):
         cfg = _make_project(tmp_path)
         story_root = tmp_path / ".story-system"