feat:支持高级配置
This commit is contained in:
@@ -38,6 +38,7 @@ export interface FieldSchema {
|
|||||||
properties?: ConfigSchema
|
properties?: ConfigSchema
|
||||||
'x-widget'?: XWidgetType
|
'x-widget'?: XWidgetType
|
||||||
'x-icon'?: string
|
'x-icon'?: string
|
||||||
|
advanced?: boolean
|
||||||
step?: number
|
step?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from src.config.official_configs import ChatConfig, MessageReceiveConfig
|
from src.config.official_configs import ChatConfig, MessageReceiveConfig
|
||||||
from src.config.config import Config
|
from src.config.config import Config
|
||||||
|
from src.config.config_base import ConfigBase, Field
|
||||||
from src.webui.config_schema import ConfigSchemaGenerator
|
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["type"] == "array"
|
||||||
assert ban_words["items"]["type"] == "string"
|
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
|
||||||
|
|||||||
@@ -179,6 +179,8 @@ class ModelConfig(ConfigBase):
|
|||||||
class ConfigManager:
|
class ConfigManager:
|
||||||
"""总配置管理类"""
|
"""总配置管理类"""
|
||||||
|
|
||||||
|
VLM_NOT_CONFIGURED_WARNING: str = "未配置视觉识图模型,部分图片理解可能受限,请在webui或model_config中配置"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.bot_config_path: Path = BOT_CONFIG_PATH
|
self.bot_config_path: Path = BOT_CONFIG_PATH
|
||||||
self.model_config_path: Path = MODEL_CONFIG_PATH
|
self.model_config_path: Path = MODEL_CONFIG_PATH
|
||||||
@@ -205,8 +207,15 @@ class ConfigManager:
|
|||||||
)
|
)
|
||||||
if global_updated or model_updated:
|
if global_updated or model_updated:
|
||||||
sys.exit(0) # 配置已自动升级,退出一次让用户确认新配置后再启动
|
sys.exit(0) # 配置已自动升级,退出一次让用户确认新配置后再启动
|
||||||
|
self._warn_if_vlm_not_configured(self.model_config)
|
||||||
logger.info(t("config.loaded"))
|
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:
|
def load_global_config(self) -> Config:
|
||||||
config, updated = load_config_from_file(Config, self.bot_config_path, CONFIG_VERSION)
|
config, updated = load_config_from_file(Config, self.bot_config_path, CONFIG_VERSION)
|
||||||
if updated:
|
if updated:
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ class PersonalityConfig(ConfigBase):
|
|||||||
"带点翻译腔,但不要太长",
|
"带点翻译腔,但不要太长",
|
||||||
],
|
],
|
||||||
json_schema_extra={
|
json_schema_extra={
|
||||||
|
"advanced": True,
|
||||||
"x-widget": "custom",
|
"x-widget": "custom",
|
||||||
"x-icon": "list",
|
"x-icon": "list",
|
||||||
},
|
},
|
||||||
@@ -112,6 +113,7 @@ class PersonalityConfig(ConfigBase):
|
|||||||
ge=0,
|
ge=0,
|
||||||
le=1,
|
le=1,
|
||||||
json_schema_extra={
|
json_schema_extra={
|
||||||
|
"advanced": True,
|
||||||
"x-widget": "slider",
|
"x-widget": "slider",
|
||||||
"x-icon": "percent",
|
"x-icon": "percent",
|
||||||
"step": 0.1,
|
"step": 0.1,
|
||||||
@@ -876,6 +878,7 @@ class EmojiConfig(ConfigBase):
|
|||||||
content_filtration: bool = Field(
|
content_filtration: bool = Field(
|
||||||
default=False,
|
default=False,
|
||||||
json_schema_extra={
|
json_schema_extra={
|
||||||
|
"advanced": True,
|
||||||
"x-widget": "switch",
|
"x-widget": "switch",
|
||||||
"x-icon": "filter",
|
"x-icon": "filter",
|
||||||
},
|
},
|
||||||
@@ -885,6 +888,7 @@ class EmojiConfig(ConfigBase):
|
|||||||
filtration_prompt: str = Field(
|
filtration_prompt: str = Field(
|
||||||
default="符合公序良俗",
|
default="符合公序良俗",
|
||||||
json_schema_extra={
|
json_schema_extra={
|
||||||
|
"advanced": True,
|
||||||
"x-widget": "input",
|
"x-widget": "input",
|
||||||
"x-icon": "shield",
|
"x-icon": "shield",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
from typing import Any, Dict, List, get_args, get_origin
|
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
|
from typing import Any, Dict, List, get_args, get_origin
|
||||||
|
|
||||||
from pydantic_core import PydanticUndefined
|
from pydantic_core import PydanticUndefined
|
||||||
|
|
||||||
@@ -20,6 +19,8 @@ class ConfigSchemaGenerator:
|
|||||||
for field_name, field_info in config_class.model_fields.items():
|
for field_name, field_info in config_class.model_fields.items():
|
||||||
if field_name in {"field_docs", "_validate_any", "suppress_any_warning"}:
|
if field_name in {"field_docs", "_validate_any", "suppress_any_warning"}:
|
||||||
continue
|
continue
|
||||||
|
if cls._is_advanced_field(field_info):
|
||||||
|
continue
|
||||||
|
|
||||||
field_schema = cls._build_field_schema(config_class, field_name, field_info.annotation, field_info)
|
field_schema = cls._build_field_schema(config_class, field_name, field_info.annotation, field_info)
|
||||||
fields.append(field_schema)
|
fields.append(field_schema)
|
||||||
@@ -49,6 +50,13 @@ class ConfigSchemaGenerator:
|
|||||||
|
|
||||||
return schema
|
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
|
@classmethod
|
||||||
def _build_nested_schema(cls, annotation: Any) -> Dict[str, Any] | None:
|
def _build_nested_schema(cls, annotation: Any) -> Dict[str, Any] | None:
|
||||||
origin = get_origin(annotation)
|
origin = get_origin(annotation)
|
||||||
|
|||||||
Reference in New Issue
Block a user