diff --git a/dashboard/src/types/config-schema.ts b/dashboard/src/types/config-schema.ts index c48f7411..c5d1856f 100644 --- a/dashboard/src/types/config-schema.ts +++ b/dashboard/src/types/config-schema.ts @@ -38,6 +38,7 @@ export interface FieldSchema { properties?: ConfigSchema 'x-widget'?: XWidgetType 'x-icon'?: string + advanced?: boolean step?: number } diff --git a/pytests/webui/test_config_schema.py b/pytests/webui/test_config_schema.py index 8ec256af..498c6965 100644 --- a/pytests/webui/test_config_schema.py +++ b/pytests/webui/test_config_schema.py @@ -1,5 +1,6 @@ from src.config.official_configs import ChatConfig, MessageReceiveConfig from src.config.config import Config +from src.config.config_base import ConfigBase, Field from src.webui.config_schema import ConfigSchemaGenerator @@ -127,3 +128,20 @@ def test_set_field_is_mapped_as_array(): assert ban_words["type"] == "array" assert ban_words["items"]["type"] == "string" + + +def test_advanced_fields_are_hidden_from_webui_schema(): + """advanced=True 的字段不应出现在 WebUI 配置 schema 中,未声明时默认展示。""" + + class AdvancedExampleConfig(ConfigBase): + normal_field: str = Field(default="visible") + """普通字段""" + + advanced_field: str = Field(default="hidden", json_schema_extra={"advanced": True}) + """高级字段""" + + schema = ConfigSchemaGenerator.generate_schema(AdvancedExampleConfig) + field_names = {field["name"] for field in schema["fields"]} + + assert "normal_field" in field_names + assert "advanced_field" not in field_names diff --git a/src/config/config.py b/src/config/config.py index 1be32621..df728eef 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -179,6 +179,8 @@ class ModelConfig(ConfigBase): class ConfigManager: """总配置管理类""" + VLM_NOT_CONFIGURED_WARNING: str = "未配置视觉识图模型,部分图片理解可能受限,请在webui或model_config中配置" + def __init__(self): self.bot_config_path: Path = BOT_CONFIG_PATH self.model_config_path: Path = MODEL_CONFIG_PATH @@ -205,8 +207,15 @@ class ConfigManager: ) if global_updated or model_updated: sys.exit(0) # 配置已自动升级,退出一次让用户确认新配置后再启动 + self._warn_if_vlm_not_configured(self.model_config) logger.info(t("config.loaded")) + @classmethod + def _warn_if_vlm_not_configured(cls, model_config: ModelConfig) -> None: + if any(model_name.strip() for model_name in model_config.model_task_config.vlm.model_list): + return + logger.warning(cls.VLM_NOT_CONFIGURED_WARNING) + def load_global_config(self) -> Config: config, updated = load_config_from_file(Config, self.bot_config_path, CONFIG_VERSION) if updated: diff --git a/src/config/official_configs.py b/src/config/official_configs.py index 2291d28c..5fb3eced 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -101,6 +101,7 @@ class PersonalityConfig(ConfigBase): "带点翻译腔,但不要太长", ], json_schema_extra={ + "advanced": True, "x-widget": "custom", "x-icon": "list", }, @@ -112,6 +113,7 @@ class PersonalityConfig(ConfigBase): ge=0, le=1, json_schema_extra={ + "advanced": True, "x-widget": "slider", "x-icon": "percent", "step": 0.1, @@ -876,6 +878,7 @@ class EmojiConfig(ConfigBase): content_filtration: bool = Field( default=False, json_schema_extra={ + "advanced": True, "x-widget": "switch", "x-icon": "filter", }, @@ -885,6 +888,7 @@ class EmojiConfig(ConfigBase): filtration_prompt: str = Field( default="符合公序良俗", json_schema_extra={ + "advanced": True, "x-widget": "input", "x-icon": "shield", }, diff --git a/src/webui/config_schema.py b/src/webui/config_schema.py index 1f11faa2..5d50a42d 100644 --- a/src/webui/config_schema.py +++ b/src/webui/config_schema.py @@ -1,6 +1,5 @@ -from typing import Any, Dict, List, get_args, get_origin - import inspect +from typing import Any, Dict, List, get_args, get_origin from pydantic_core import PydanticUndefined @@ -20,6 +19,8 @@ class ConfigSchemaGenerator: for field_name, field_info in config_class.model_fields.items(): if field_name in {"field_docs", "_validate_any", "suppress_any_warning"}: continue + if cls._is_advanced_field(field_info): + continue field_schema = cls._build_field_schema(config_class, field_name, field_info.annotation, field_info) fields.append(field_schema) @@ -49,6 +50,13 @@ class ConfigSchemaGenerator: return schema + @staticmethod + def _is_advanced_field(field_info: Any) -> bool: + extra = getattr(field_info, "json_schema_extra", None) + if not isinstance(extra, dict): + return False + return extra.get("advanced", False) is True + @classmethod def _build_nested_schema(cls, annotation: Any) -> Dict[str, Any] | None: origin = get_origin(annotation)