fix:修复配置文件迁移失败问题
This commit is contained in:
@@ -1,6 +1,16 @@
|
|||||||
from src.config.legacy_migration import try_migrate_legacy_bot_config_dict
|
from src.config.legacy_migration import try_migrate_legacy_bot_config_dict
|
||||||
|
|
||||||
|
|
||||||
|
def test_legacy_empty_qq_account_is_migrated_to_zero():
|
||||||
|
payload = {"bot": {"qq_account": ""}}
|
||||||
|
|
||||||
|
result = try_migrate_legacy_bot_config_dict(payload)
|
||||||
|
|
||||||
|
assert result.migrated is True
|
||||||
|
assert "bot.qq_account_empty" in result.reason
|
||||||
|
assert result.data["bot"]["qq_account"] == 0
|
||||||
|
|
||||||
|
|
||||||
def test_legacy_learning_list_with_numeric_fourth_column_is_migrated():
|
def test_legacy_learning_list_with_numeric_fourth_column_is_migrated():
|
||||||
payload = {
|
payload = {
|
||||||
"expression": {
|
"expression": {
|
||||||
@@ -35,6 +45,72 @@ def test_legacy_learning_list_with_numeric_fourth_column_is_migrated():
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_legacy_learning_list_with_string_bool_fourth_column_is_migrated():
|
||||||
|
payload = {"expression": {"learning_list": [["qq::group", "enable", "enable", "true"]]}}
|
||||||
|
|
||||||
|
result = try_migrate_legacy_bot_config_dict(payload)
|
||||||
|
|
||||||
|
assert result.migrated is True
|
||||||
|
assert "expression.learning_list" in result.reason
|
||||||
|
assert result.data["expression"]["learning_list"] == [
|
||||||
|
{
|
||||||
|
"platform": "qq",
|
||||||
|
"item_id": "",
|
||||||
|
"rule_type": "group",
|
||||||
|
"use_expression": True,
|
||||||
|
"enable_learning": True,
|
||||||
|
"enable_jargon_learning": True,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_legacy_expression_groups_empty_string_is_migrated():
|
||||||
|
payload = {"expression": {"expression_groups": ""}}
|
||||||
|
|
||||||
|
result = try_migrate_legacy_bot_config_dict(payload)
|
||||||
|
|
||||||
|
assert result.migrated is True
|
||||||
|
assert "expression.expression_groups" in result.reason
|
||||||
|
assert result.data["expression"]["expression_groups"] == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_legacy_expression_groups_global_marker_is_migrated():
|
||||||
|
payload = {"expression": {"expression_groups": [["*"]]}}
|
||||||
|
|
||||||
|
result = try_migrate_legacy_bot_config_dict(payload)
|
||||||
|
|
||||||
|
assert result.migrated is True
|
||||||
|
assert "expression.expression_groups" in result.reason
|
||||||
|
assert result.data["expression"]["expression_groups"] == [
|
||||||
|
{
|
||||||
|
"expression_groups": [
|
||||||
|
{
|
||||||
|
"platform": "*",
|
||||||
|
"item_id": "*",
|
||||||
|
"rule_type": "group",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_empty_keyword_rules_are_dropped():
|
||||||
|
payload = {
|
||||||
|
"keyword_reaction": {
|
||||||
|
"keyword_rules": [
|
||||||
|
{"keywords": [], "reaction": ""},
|
||||||
|
{"keywords": ["test"], "reaction": "ok"},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = try_migrate_legacy_bot_config_dict(payload)
|
||||||
|
|
||||||
|
assert result.migrated is True
|
||||||
|
assert "keyword_reaction.keyword_rules_empty" in result.reason
|
||||||
|
assert result.data["keyword_reaction"]["keyword_rules"] == [{"keywords": ["test"], "reaction": "ok"}]
|
||||||
|
|
||||||
|
|
||||||
def test_visual_multimodal_planner_is_migrated_to_planner_mode():
|
def test_visual_multimodal_planner_is_migrated_to_planner_mode():
|
||||||
payload = {"visual": {"multimodal_planner": True}}
|
payload = {"visual": {"multimodal_planner": True}}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class Server:
|
|||||||
def __init__(self, host: Optional[str] = None, port: Optional[int] = None, app_name: str = "MaiMCore"):
|
def __init__(self, host: Optional[str] = None, port: Optional[int] = None, app_name: str = "MaiMCore"):
|
||||||
self.app = FastAPI(title=app_name)
|
self.app = FastAPI(title=app_name)
|
||||||
self._host: str = "127.0.0.1"
|
self._host: str = "127.0.0.1"
|
||||||
self._port: int = 8080
|
self._port: int = 8000
|
||||||
self._server: Optional[UvicornServer] = None
|
self._server: Optional[UvicornServer] = None
|
||||||
self.set_address(host, port)
|
self.set_address(host, port)
|
||||||
|
|
||||||
|
|||||||
@@ -107,9 +107,9 @@ def _parse_enable_disable(v: Any) -> Optional[bool]:
|
|||||||
|
|
||||||
if isinstance(v, str):
|
if isinstance(v, str):
|
||||||
normalized_value = v.strip().lower()
|
normalized_value = v.strip().lower()
|
||||||
if normalized_value == "enable":
|
if normalized_value in {"enable", "true"}:
|
||||||
return True
|
return True
|
||||||
if normalized_value == "disable":
|
if normalized_value in {"disable", "false"}:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return None
|
return None
|
||||||
@@ -174,7 +174,21 @@ def _migrate_expression_groups(expr: dict[str, Any]) -> bool:
|
|||||||
"""
|
"""
|
||||||
将旧版 expression.expression_groups 转成当前结构。
|
将旧版 expression.expression_groups 转成当前结构。
|
||||||
"""
|
"""
|
||||||
expression_groups = _as_list(expr.get("expression_groups"))
|
raw_expression_groups = expr.get("expression_groups")
|
||||||
|
if isinstance(raw_expression_groups, str):
|
||||||
|
normalized_value = raw_expression_groups.strip()
|
||||||
|
if not normalized_value:
|
||||||
|
expr["expression_groups"] = []
|
||||||
|
return True
|
||||||
|
|
||||||
|
parsed = _parse_expression_group_target(normalized_value)
|
||||||
|
if parsed is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
expr["expression_groups"] = [{"expression_groups": [parsed]}]
|
||||||
|
return True
|
||||||
|
|
||||||
|
expression_groups = _as_list(raw_expression_groups)
|
||||||
if expression_groups is None:
|
if expression_groups is None:
|
||||||
return False
|
return False
|
||||||
if expression_groups and all(isinstance(item, dict) for item in expression_groups):
|
if expression_groups and all(isinstance(item, dict) for item in expression_groups):
|
||||||
@@ -220,6 +234,35 @@ def _migrate_target_item_list(parent: dict[str, Any], key: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _drop_empty_keyword_rules(keyword_reaction: dict[str, Any], key: str) -> bool:
|
||||||
|
raw = _as_list(keyword_reaction.get(key))
|
||||||
|
if raw is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
cleaned_rules: list[Any] = []
|
||||||
|
dropped_any = False
|
||||||
|
for item in raw:
|
||||||
|
item_dict = _as_dict(item)
|
||||||
|
if item_dict is None:
|
||||||
|
cleaned_rules.append(item)
|
||||||
|
continue
|
||||||
|
|
||||||
|
keywords = _as_list(item_dict.get("keywords")) or []
|
||||||
|
regex = _as_list(item_dict.get("regex")) or []
|
||||||
|
reaction = item_dict.get("reaction")
|
||||||
|
if not keywords and not regex and (reaction is None or str(reaction).strip() == ""):
|
||||||
|
dropped_any = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
cleaned_rules.append(item)
|
||||||
|
|
||||||
|
if not dropped_any:
|
||||||
|
return False
|
||||||
|
|
||||||
|
keyword_reaction[key] = cleaned_rules
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
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` 中的绑定地址迁移到主配置结构。"""
|
||||||
|
|
||||||
@@ -236,7 +279,7 @@ def migrate_legacy_bind_env_to_bot_config_dict(data: dict[str, Any]) -> Migratio
|
|||||||
if maim_message is not None and _migrate_env_value(maim_message, "ws_server_host", main_host_env, "127.0.0.1"):
|
if maim_message is not None and _migrate_env_value(maim_message, "ws_server_host", main_host_env, "127.0.0.1"):
|
||||||
migrated_any = True
|
migrated_any = True
|
||||||
reasons.append("HOST->maim_message.ws_server_host")
|
reasons.append("HOST->maim_message.ws_server_host")
|
||||||
if maim_message is not None and _migrate_env_value(maim_message, "ws_server_port", main_port_env, 8080):
|
if maim_message is not None and _migrate_env_value(maim_message, "ws_server_port", main_port_env, 8000):
|
||||||
migrated_any = True
|
migrated_any = True
|
||||||
reasons.append("PORT->maim_message.ws_server_port")
|
reasons.append("PORT->maim_message.ws_server_port")
|
||||||
|
|
||||||
@@ -267,6 +310,12 @@ def try_migrate_legacy_bot_config_dict(data: dict[str, Any]) -> MigrationResult:
|
|||||||
migrated_any = False
|
migrated_any = False
|
||||||
reasons: list[str] = []
|
reasons: list[str] = []
|
||||||
|
|
||||||
|
bot = _as_dict(data.get("bot"))
|
||||||
|
if bot is not None and isinstance(bot.get("qq_account"), str) and not bot["qq_account"].strip():
|
||||||
|
bot["qq_account"] = 0
|
||||||
|
migrated_any = True
|
||||||
|
reasons.append("bot.qq_account_empty")
|
||||||
|
|
||||||
expr = _as_dict(data.get("expression"))
|
expr = _as_dict(data.get("expression"))
|
||||||
if expr is not None:
|
if expr is not None:
|
||||||
if _migrate_expression_learning_list(expr):
|
if _migrate_expression_learning_list(expr):
|
||||||
@@ -312,5 +361,14 @@ def try_migrate_legacy_bot_config_dict(data: dict[str, Any]) -> MigrationResult:
|
|||||||
migrated_any = True
|
migrated_any = True
|
||||||
reasons.append("memory.global_memory_blacklist")
|
reasons.append("memory.global_memory_blacklist")
|
||||||
|
|
||||||
|
keyword_reaction = _as_dict(data.get("keyword_reaction"))
|
||||||
|
if keyword_reaction is not None:
|
||||||
|
if _drop_empty_keyword_rules(keyword_reaction, "keyword_rules"):
|
||||||
|
migrated_any = True
|
||||||
|
reasons.append("keyword_reaction.keyword_rules_empty")
|
||||||
|
if _drop_empty_keyword_rules(keyword_reaction, "regex_rules"):
|
||||||
|
migrated_any = True
|
||||||
|
reasons.append("keyword_reaction.regex_rules_empty")
|
||||||
|
|
||||||
reason = ",".join(reasons)
|
reason = ",".join(reasons)
|
||||||
return MigrationResult(data=data, migrated=migrated_any, reason=reason)
|
return MigrationResult(data=data, migrated=migrated_any, reason=reason)
|
||||||
|
|||||||
@@ -1205,7 +1205,7 @@ class MaimMessageConfig(ConfigBase):
|
|||||||
"""旧版基于WS的服务器主机地址"""
|
"""旧版基于WS的服务器主机地址"""
|
||||||
|
|
||||||
ws_server_port: int = Field(
|
ws_server_port: int = Field(
|
||||||
default=8080,
|
default=8000,
|
||||||
json_schema_extra={
|
json_schema_extra={
|
||||||
"x-widget": "input",
|
"x-widget": "input",
|
||||||
"x-icon": "hash",
|
"x-icon": "hash",
|
||||||
|
|||||||
Reference in New Issue
Block a user