mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-29 00:32:06 +08:00
* ♻️ refactor(core): 重构 AI 能力与定时任务调度系统 - 【AI 能力与工具】重构 Capability 注册与管理机制,引入 CapabilityManager 统一管理 - 移除全局能力注册表,改用声明式装饰器 `@capability` 进行解耦注册 - 重构工具解析器链,使用统一的 BaseToolResolver 代替原有的多个特定解析器 - 增强工具查询过滤,支持通配符匹配、工具箱过滤和排除标签 - 【定时任务调度】重构定时任务管理器,引入 SchedulerRegistry 统一管理任务元数据 - 引入 JobConfig 聚合定时任务配置,支持用户维度的定时任务调度 - 重构执行分发器,支持并发限制、串行间隔和随机延迟打散 - 【运行上下文】引入 ScheduledDeps 以支持后台和定时任务环境下的依赖注入 - 优化 RunContext,支持从定时任务上下文快速构造,并提供 emit 辅助方法 - 【日志与监控】引入 AILoggerProxy,实现 AI 各模块的专属日志输出 - 将各模块的全局 logger 替换为对应的模块专属日志代理 - 【其他优化】修复 Pydantic V1 兼容层中 model_validator 的装饰器兼容性问题 - 在非交互式环境(如定时任务)中自动隐藏 HITL 交互工具以节省 Token * ♻️ refactor(core): 优化内部导入路径并提升 Pydantic 兼容性 - 【重构】将 `services/ai` 模块内的绝对导入重构为相对导入,优化包结构 - 【重构】移除不必要的 `if TYPE_CHECKING` 保护,通过 `from __future__ import annotations` 直接导入类型 - 【清理】清理 `core/messages/types.py` 中未使用的 `AssistantContentUnion` 等联合类型定义 - 【优化】在 `utils/pydantic_compat.py` 中新增 `model_rebuild` 兼容函数,统一 Pydantic V1/V2 的模型重建逻辑 - 【优化】将部分函数内部的延迟导入提升至模块顶部,规范代码结构 * ♻️ refactor(imports): 优化导入路径为相对导入并清理冗余导入 - 【重构】将 AI 服务相关模块中的绝对导入路径修改为相对导入,提升模块内聚性与可移植性 - 【清理】移除多处函数内部或类方法中未使用的冗余导入,避免循环引用和资源浪费 - 【格式化】微调部分工具装饰器和返回语句的格式与尾随逗号 * 🚨 auto fix by pre-commit hooks --------- Co-authored-by: webjoin111 <455457521@qq.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
200 lines
5.3 KiB
Python
200 lines
5.3 KiB
Python
"""
|
||
Pydantic V1 & V2 兼容层模块
|
||
|
||
为 Pydantic V1 与 V2 版本提供统一的便捷函数与类,
|
||
包括 model_dump, model_copy, model_json_schema, parse_as 等。
|
||
"""
|
||
|
||
from datetime import datetime
|
||
from enum import Enum
|
||
from pathlib import Path
|
||
from typing import Any, TypeVar, get_args, get_origin
|
||
|
||
from nonebot.compat import (
|
||
PYDANTIC_V2,
|
||
model_dump,
|
||
model_fields,
|
||
type_validate_json,
|
||
type_validate_python,
|
||
)
|
||
from pydantic import BaseModel
|
||
import ujson as json
|
||
|
||
T = TypeVar("T", bound=BaseModel)
|
||
V = TypeVar("V")
|
||
|
||
|
||
import typing
|
||
|
||
if typing.TYPE_CHECKING:
|
||
_T_TA = TypeVar("_T_TA")
|
||
|
||
class TypeAdapter(typing.Generic[_T_TA]):
|
||
def __init__(self, type_: Any, **kwargs: Any): ...
|
||
def validate_python(self, obj: Any) -> _T_TA: ...
|
||
|
||
def model_validator(*args: Any, **kwargs: Any) -> Any: ...
|
||
else:
|
||
try:
|
||
from pydantic import TypeAdapter, model_validator
|
||
except ImportError:
|
||
|
||
class TypeAdapter:
|
||
def __init__(self, type_: Any, **kwargs: Any):
|
||
self.type_ = type_
|
||
|
||
def validate_python(self, obj: Any) -> Any:
|
||
from nonebot.compat import type_validate_python
|
||
|
||
return type_validate_python(self.type_, obj)
|
||
|
||
from pydantic import root_validator
|
||
|
||
def model_validator(*args: Any, **kwargs: Any) -> Any:
|
||
mode = kwargs.get("mode", "after")
|
||
pre = mode == "before"
|
||
|
||
def decorator(func: Any) -> Any:
|
||
return root_validator(pre=pre, allow_reuse=True)(func)
|
||
|
||
return decorator
|
||
|
||
|
||
__all__ = [
|
||
"PYDANTIC_V2",
|
||
"TypeAdapter",
|
||
"_dump_pydantic_obj",
|
||
"_is_pydantic_type",
|
||
"compat_computed_field",
|
||
"dump_json_safely",
|
||
"model_construct",
|
||
"model_copy",
|
||
"model_dump",
|
||
"model_dump_json",
|
||
"model_fields",
|
||
"model_json_schema",
|
||
"model_rebuild",
|
||
"model_validate",
|
||
"model_validator",
|
||
"parse_as",
|
||
"type_validate_json",
|
||
"type_validate_python",
|
||
]
|
||
|
||
|
||
def model_copy(
|
||
model: T, *, update: dict[str, Any] | None = None, deep: bool = False
|
||
) -> T:
|
||
"""
|
||
Pydantic `model.copy()` (v1) 和 `model.model_copy()` (v2) 的兼容函数。
|
||
"""
|
||
if PYDANTIC_V2:
|
||
return model.model_copy(update=update, deep=deep)
|
||
else:
|
||
update_dict = update or {}
|
||
return model.copy(update=update_dict, deep=deep)
|
||
|
||
|
||
def model_construct(model_class: type[T], **kwargs: Any) -> T:
|
||
"""
|
||
Pydantic `model_construct` (v2) 与 `construct` (v1) 的兼容函数。
|
||
"""
|
||
if PYDANTIC_V2:
|
||
return model_class.model_construct(**kwargs)
|
||
else:
|
||
return model_class.construct(**kwargs)
|
||
|
||
|
||
def model_validate(model_class: type[T], obj: Any) -> T:
|
||
"""
|
||
Pydantic 模型验证兼容函数。
|
||
"""
|
||
return type_validate_python(model_class, obj)
|
||
|
||
|
||
def model_dump_json(model: BaseModel, **kwargs: Any) -> str:
|
||
"""
|
||
Pydantic `model.json()` (v1) 和 `model.model_dump_json()` (v2) 的兼容函数。
|
||
"""
|
||
if PYDANTIC_V2:
|
||
return model.model_dump_json(**kwargs)
|
||
return model.json(**kwargs)
|
||
|
||
|
||
if PYDANTIC_V2:
|
||
from pydantic import computed_field as compat_computed_field
|
||
else:
|
||
compat_computed_field = property
|
||
|
||
|
||
def model_json_schema(model_class: type[BaseModel], **kwargs: Any) -> dict[str, Any]:
|
||
"""
|
||
Pydantic `Model.schema()` (v1) 和 `Model.model_json_schema()` (v2) 的兼容函数。
|
||
"""
|
||
if PYDANTIC_V2:
|
||
return model_class.model_json_schema(**kwargs)
|
||
return model_class.schema(by_alias=kwargs.get("by_alias", True))
|
||
|
||
|
||
def _is_pydantic_type(t: Any) -> bool:
|
||
"""
|
||
递归检查一个类型注解是否与 Pydantic BaseModel 相关。
|
||
"""
|
||
if t is None:
|
||
return False
|
||
origin = get_origin(t)
|
||
if origin:
|
||
return any(_is_pydantic_type(arg) for arg in get_args(t))
|
||
return isinstance(t, type) and issubclass(t, BaseModel)
|
||
|
||
|
||
def _dump_pydantic_obj(obj: Any) -> Any:
|
||
"""
|
||
递归地将一个对象内部的 Pydantic BaseModel 实例转换为字典。
|
||
支持单个实例、实例列表、实例字典等情况。
|
||
"""
|
||
if isinstance(obj, BaseModel):
|
||
return model_dump(obj)
|
||
if isinstance(obj, list):
|
||
return [_dump_pydantic_obj(item) for item in obj]
|
||
if isinstance(obj, dict):
|
||
return {key: _dump_pydantic_obj(value) for key, value in obj.items()}
|
||
return obj
|
||
|
||
|
||
parse_as = type_validate_python
|
||
|
||
|
||
def dump_json_safely(obj: Any, **kwargs) -> str:
|
||
"""
|
||
安全地将可能包含 Pydantic 特定类型 (如 Enum) 的对象序列化为 JSON 字符串。
|
||
"""
|
||
|
||
def default_serializer(o):
|
||
if isinstance(o, Enum):
|
||
return o.value
|
||
if isinstance(o, datetime):
|
||
return o.isoformat()
|
||
if isinstance(o, Path):
|
||
return str(o.as_posix())
|
||
if isinstance(o, set):
|
||
return list(o)
|
||
if isinstance(o, BaseModel):
|
||
return model_dump(o)
|
||
raise TypeError(
|
||
f"Object of type {o.__class__.__name__} is not JSON serializable"
|
||
)
|
||
|
||
return json.dumps(obj, default=default_serializer, **kwargs)
|
||
|
||
|
||
def model_rebuild(model_class: type[BaseModel], **kwargs: Any) -> None:
|
||
"""
|
||
Pydantic V1/V2 兼容的前向引用重建函数。
|
||
V2 调用 `model_rebuild()`,V1 调用 `update_forward_refs()`。
|
||
"""
|
||
if PYDANTIC_V2:
|
||
model_class.model_rebuild(**kwargs)
|
||
else:
|
||
model_class.update_forward_refs(**kwargs)
|