mirror of https://github.com/Mai-with-u/MaiBot.git
feat(webui): enhance ConfigSchemaGenerator with field_docs and UI metadata
- Add AttrDocBase.get_class_field_docs() classmethod for class-level field docs extraction - Merge json_schema_extra (x-widget, x-icon, step) into schema output - Map Pydantic constraints (ge/le) to minValue/maxValue for frontend compatibility - Add ge=0, le=1 constraints to ChatConfig.talk_value for validation Completes Task 1 (including subtasks 1a, 1b, 1c, 1d) of webui-config-visualization-refactor plan.pull/1496/head
parent
19c9c5a39a
commit
278a084c23
|
|
@ -5,7 +5,7 @@ import types
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
from typing import Union, get_args, get_origin, Tuple, Any, List, Dict, Set, Literal
|
from typing import Any, Dict, List, Literal, Set, Tuple, Union, cast, get_args, get_origin
|
||||||
|
|
||||||
__all__ = ["ConfigBase", "Field", "AttributeData"]
|
__all__ = ["ConfigBase", "Field", "AttributeData"]
|
||||||
|
|
||||||
|
|
@ -44,6 +44,16 @@ class AttrDocBase:
|
||||||
# 从类定义节点中提取字段文档
|
# 从类定义节点中提取字段文档
|
||||||
return self._extract_field_docs(class_node, allow_extra_methods)
|
return self._extract_field_docs(class_node, allow_extra_methods)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_class_field_docs(cls) -> dict[str, str]:
|
||||||
|
class_source = cls._get_class_source()
|
||||||
|
class_node = cls._find_class_node(class_source)
|
||||||
|
return AttrDocBase._extract_field_docs(
|
||||||
|
cast(AttrDocBase, cast(Any, cls)),
|
||||||
|
class_node,
|
||||||
|
allow_extra_methods=False,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_class_source(cls) -> str:
|
def _get_class_source(cls) -> str:
|
||||||
"""获取类定义所在文件的完整源代码"""
|
"""获取类定义所在文件的完整源代码"""
|
||||||
|
|
@ -265,7 +275,7 @@ class ConfigBase(BaseModel, AttrDocBase):
|
||||||
if origin_type in (int, float, str, bool, complex, bytes, Any):
|
if origin_type in (int, float, str, bool, complex, bytes, Any):
|
||||||
continue
|
continue
|
||||||
# 允许嵌套的ConfigBase自定义类
|
# 允许嵌套的ConfigBase自定义类
|
||||||
if inspect.isclass(origin_type) and issubclass(origin_type, ConfigBase): # type: ignore
|
if isinstance(origin_type, type) and issubclass(cast(type, origin_type), ConfigBase):
|
||||||
continue
|
continue
|
||||||
# 只允许 list, set, dict 三类泛型
|
# 只允许 list, set, dict 三类泛型
|
||||||
if origin_type not in (list, set, dict, List, Set, Dict, Literal):
|
if origin_type not in (list, set, dict, List, Set, Dict, Literal):
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,8 @@ class ChatConfig(ConfigBase):
|
||||||
|
|
||||||
talk_value: float = Field(
|
talk_value: float = Field(
|
||||||
default=1,
|
default=1,
|
||||||
|
ge=0,
|
||||||
|
le=1,
|
||||||
json_schema_extra={
|
json_schema_extra={
|
||||||
"x-widget": "slider",
|
"x-widget": "slider",
|
||||||
"x-icon": "message-circle",
|
"x-icon": "message-circle",
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,18 @@ class ConfigSchemaGenerator:
|
||||||
if options:
|
if options:
|
||||||
schema["options"] = options
|
schema["options"] = options
|
||||||
|
|
||||||
|
# Task 1c: Merge json_schema_extra (x-widget, x-icon, step, etc.)
|
||||||
|
if hasattr(field_info, "json_schema_extra") and field_info.json_schema_extra:
|
||||||
|
schema.update(field_info.json_schema_extra)
|
||||||
|
|
||||||
|
# Task 1d: Map Pydantic constraints to minValue/maxValue (frontend naming convention)
|
||||||
|
if hasattr(field_info, "metadata") and field_info.metadata:
|
||||||
|
for constraint in field_info.metadata:
|
||||||
|
if hasattr(constraint, "ge"):
|
||||||
|
schema["minValue"] = constraint.ge
|
||||||
|
if hasattr(constraint, "le"):
|
||||||
|
schema["maxValue"] = constraint.le
|
||||||
|
|
||||||
return schema
|
return schema
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue