diff --git a/pytests/A_memorix_test/test_legacy_config_migration.py b/pytests/A_memorix_test/test_legacy_config_migration.py index 8a110663..d4022f79 100644 --- a/pytests/A_memorix_test/test_legacy_config_migration.py +++ b/pytests/A_memorix_test/test_legacy_config_migration.py @@ -1,6 +1,16 @@ 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(): payload = { "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(): payload = {"visual": {"multimodal_planner": True}} diff --git a/src/common/message_server/server.py b/src/common/message_server/server.py index c4c58669..58708bf9 100644 --- a/src/common/message_server/server.py +++ b/src/common/message_server/server.py @@ -19,7 +19,7 @@ class Server: def __init__(self, host: Optional[str] = None, port: Optional[int] = None, app_name: str = "MaiMCore"): self.app = FastAPI(title=app_name) self._host: str = "127.0.0.1" - self._port: int = 8080 + self._port: int = 8000 self._server: Optional[UvicornServer] = None self.set_address(host, port) diff --git a/src/config/legacy_migration.py b/src/config/legacy_migration.py index 5e7e9441..e7d9f85c 100644 --- a/src/config/legacy_migration.py +++ b/src/config/legacy_migration.py @@ -107,9 +107,9 @@ def _parse_enable_disable(v: Any) -> Optional[bool]: if isinstance(v, str): normalized_value = v.strip().lower() - if normalized_value == "enable": + if normalized_value in {"enable", "true"}: return True - if normalized_value == "disable": + if normalized_value in {"disable", "false"}: return False return None @@ -174,7 +174,21 @@ def _migrate_expression_groups(expr: dict[str, Any]) -> bool: """ 将旧版 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: return False 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 +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: """将旧版 `.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"): migrated_any = True 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 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 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")) if expr is not None: 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 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) return MigrationResult(data=data, migrated=migrated_any, reason=reason) diff --git a/src/config/official_configs.py b/src/config/official_configs.py index 6ffaa9a7..d68e707d 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -1205,7 +1205,7 @@ class MaimMessageConfig(ConfigBase): """旧版基于WS的服务器主机地址""" ws_server_port: int = Field( - default=8080, + default=8000, json_schema_extra={ "x-widget": "input", "x-icon": "hash",