fix:恢复旧配置迁移逻辑
恢复 planner/replyer 旧字段到新 schema 的迁移逻辑。 补充回归测试,确保旧版 bot 配置仍可正常加载。
This commit is contained in:
@@ -33,3 +33,47 @@ def test_legacy_learning_list_with_numeric_fourth_column_is_migrated():
|
|||||||
"enable_jargon_learning": False,
|
"enable_jargon_learning": False,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_visual_multimodal_planner_is_migrated_to_planner_mode():
|
||||||
|
payload = {"visual": {"multimodal_planner": True}}
|
||||||
|
|
||||||
|
result = try_migrate_legacy_bot_config_dict(payload)
|
||||||
|
|
||||||
|
assert result.migrated is True
|
||||||
|
assert "visual.multimodal_planner_moved_to_visual.planner_mode" in result.reason
|
||||||
|
assert result.data["visual"]["planner_mode"] == "multimodal"
|
||||||
|
assert "multimodal_planner" not in result.data["visual"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_chat_multimodal_planner_is_migrated_to_visual_planner_mode():
|
||||||
|
payload = {"chat": {"multimodal_planner": True}}
|
||||||
|
|
||||||
|
result = try_migrate_legacy_bot_config_dict(payload)
|
||||||
|
|
||||||
|
assert result.migrated is True
|
||||||
|
assert "chat.multimodal_planner_moved_to_visual.planner_mode" in result.reason
|
||||||
|
assert result.data["visual"]["planner_mode"] == "multimodal"
|
||||||
|
assert "multimodal_planner" not in result.data["chat"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_visual_multimodal_replyer_is_migrated_to_replyer_mode():
|
||||||
|
payload = {"visual": {"multimodal_replyer": True}}
|
||||||
|
|
||||||
|
result = try_migrate_legacy_bot_config_dict(payload)
|
||||||
|
|
||||||
|
assert result.migrated is True
|
||||||
|
assert "visual.multimodal_replyer_moved_to_visual.replyer_mode" in result.reason
|
||||||
|
assert result.data["visual"]["replyer_mode"] == "multimodal"
|
||||||
|
assert "multimodal_replyer" not in result.data["visual"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_chat_replyer_generator_type_is_migrated_to_replyer_mode():
|
||||||
|
payload = {"chat": {"replyer_generator_type": "legacy"}}
|
||||||
|
|
||||||
|
result = try_migrate_legacy_bot_config_dict(payload)
|
||||||
|
|
||||||
|
assert result.migrated is True
|
||||||
|
assert "chat.replyer_generator_type_moved_to_visual.replyer_mode" in result.reason
|
||||||
|
assert result.data["visual"]["replyer_mode"] == "text"
|
||||||
|
assert "replyer_generator_type" not in result.data["chat"]
|
||||||
|
|||||||
@@ -206,6 +206,40 @@ def _migrate_target_item_list(parent: dict[str, Any], key: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_planner_mode(value: Any) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
兼容旧 planner 配置到当前 visual.planner_mode。
|
||||||
|
"""
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return "multimodal" if value else "text"
|
||||||
|
|
||||||
|
if not isinstance(value, str):
|
||||||
|
return None
|
||||||
|
|
||||||
|
normalized_value = value.strip().lower()
|
||||||
|
if normalized_value in {"text", "multimodal", "auto"}:
|
||||||
|
return normalized_value
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_replyer_mode(value: Any) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
兼容旧 replyer 配置到当前 visual.replyer_mode。
|
||||||
|
"""
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return "multimodal" if value else "text"
|
||||||
|
|
||||||
|
if not isinstance(value, str):
|
||||||
|
return None
|
||||||
|
|
||||||
|
normalized_value = value.strip().lower()
|
||||||
|
if normalized_value in {"text", "multimodal", "auto"}:
|
||||||
|
return normalized_value
|
||||||
|
if normalized_value == "legacy":
|
||||||
|
return "text"
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def migrate_legacy_bind_env_to_bot_config_dict(data: dict[str, Any]) -> MigrationResult:
|
def migrate_legacy_bind_env_to_bot_config_dict(data: dict[str, Any]) -> MigrationResult:
|
||||||
"""将旧版 `.env` 中的绑定地址迁移到主配置结构。"""
|
"""将旧版 `.env` 中的绑定地址迁移到主配置结构。"""
|
||||||
|
|
||||||
@@ -280,9 +314,13 @@ def try_migrate_legacy_bot_config_dict(data: dict[str, Any]) -> MigrationResult:
|
|||||||
migrated_any = True
|
migrated_any = True
|
||||||
reasons.append("expression.manual_reflect_operator_id_empty")
|
reasons.append("expression.manual_reflect_operator_id_empty")
|
||||||
|
|
||||||
|
chat = _as_dict(data.get("chat"))
|
||||||
personality = _as_dict(data.get("personality"))
|
personality = _as_dict(data.get("personality"))
|
||||||
visual = _as_dict(data.get("visual"))
|
visual = _as_dict(data.get("visual"))
|
||||||
if visual is None and personality is not None and "visual_style" in personality:
|
if visual is None and (
|
||||||
|
(personality is not None and "visual_style" in personality)
|
||||||
|
or (chat is not None and ("multimodal_planner" in chat or "replyer_generator_type" in chat))
|
||||||
|
):
|
||||||
visual = {}
|
visual = {}
|
||||||
data["visual"] = visual
|
data["visual"] = visual
|
||||||
|
|
||||||
@@ -293,6 +331,42 @@ def try_migrate_legacy_bot_config_dict(data: dict[str, Any]) -> MigrationResult:
|
|||||||
migrated_any = True
|
migrated_any = True
|
||||||
reasons.append("personality.visual_style_moved_to_visual.visual_style")
|
reasons.append("personality.visual_style_moved_to_visual.visual_style")
|
||||||
|
|
||||||
|
if visual is not None and "multimodal_planner" in visual:
|
||||||
|
planner_mode = _parse_planner_mode(visual.get("multimodal_planner"))
|
||||||
|
if "planner_mode" not in visual and planner_mode is not None:
|
||||||
|
visual["planner_mode"] = planner_mode
|
||||||
|
if "planner_mode" in visual:
|
||||||
|
visual.pop("multimodal_planner", None)
|
||||||
|
migrated_any = True
|
||||||
|
reasons.append("visual.multimodal_planner_moved_to_visual.planner_mode")
|
||||||
|
|
||||||
|
if visual is not None and chat is not None and "multimodal_planner" in chat:
|
||||||
|
planner_mode = _parse_planner_mode(chat.get("multimodal_planner"))
|
||||||
|
if "planner_mode" not in visual and planner_mode is not None:
|
||||||
|
visual["planner_mode"] = planner_mode
|
||||||
|
if "planner_mode" in visual:
|
||||||
|
chat.pop("multimodal_planner", None)
|
||||||
|
migrated_any = True
|
||||||
|
reasons.append("chat.multimodal_planner_moved_to_visual.planner_mode")
|
||||||
|
|
||||||
|
if visual is not None and "multimodal_replyer" in visual:
|
||||||
|
replyer_mode = _parse_replyer_mode(visual.get("multimodal_replyer"))
|
||||||
|
if "replyer_mode" not in visual and replyer_mode is not None:
|
||||||
|
visual["replyer_mode"] = replyer_mode
|
||||||
|
if "replyer_mode" in visual:
|
||||||
|
visual.pop("multimodal_replyer", None)
|
||||||
|
migrated_any = True
|
||||||
|
reasons.append("visual.multimodal_replyer_moved_to_visual.replyer_mode")
|
||||||
|
|
||||||
|
if visual is not None and chat is not None and "replyer_generator_type" in chat:
|
||||||
|
replyer_mode = _parse_replyer_mode(chat.get("replyer_generator_type"))
|
||||||
|
if "replyer_mode" not in visual and replyer_mode is not None:
|
||||||
|
visual["replyer_mode"] = replyer_mode
|
||||||
|
if "replyer_mode" in visual:
|
||||||
|
chat.pop("replyer_generator_type", None)
|
||||||
|
migrated_any = True
|
||||||
|
reasons.append("chat.replyer_generator_type_moved_to_visual.replyer_mode")
|
||||||
|
|
||||||
memory = _as_dict(data.get("memory"))
|
memory = _as_dict(data.get("memory"))
|
||||||
if memory is not None and _migrate_target_item_list(memory, "global_memory_blacklist"):
|
if memory is not None and _migrate_target_item_list(memory, "global_memory_blacklist"):
|
||||||
migrated_any = True
|
migrated_any = True
|
||||||
|
|||||||
Reference in New Issue
Block a user