✨ feat(core): 支持LLM多图片响应,增强UI主题皮肤系统及优化JSON/Markdown处理 (#2062)

- 【LLM服务】
  - `LLMResponse` 模型现在支持 `images: list[bytes]`,允许模型返回多张图片。
  - LLM适配器 (`base.py`, `gemini.py`) 和 API 层 (`api.py`, `service.py`) 已更新以处理多图片响应。
  - 响应验证逻辑已调整,以检查 `images` 列表而非单个 `image_bytes`。
- 【UI渲染服务】
  - 引入组件“皮肤”(variant)概念,允许为同一组件提供不同视觉风格。
  - 改进了 `manifest.json` 的加载、合并和缓存机制,支持基础清单与皮肤清单的递归合并。
  - `ThemeManager` 现在会缓存已加载的清单,并在主题重载时清除缓存。
  - 增强了资源解析器 (`ResourceResolver`),支持 `@` 命名空间路径和更健壮的相对路径处理。
  - 独立模板现在会继承主 Jinja 环境的过滤器。
- 【工具函数】
  - 引入 `dump_json_safely` 工具函数,用于更安全地序列化包含 Pydantic 模型、枚举等复杂类型的对象为 JSON。
  - LLM 服务中的请求体和缓存键生成已改用 `dump_json_safely`。
  - 优化了 `format_usage_for_markdown` 函数,改进了 Markdown 文本的格式化,确保块级元素前有正确换行,并正确处理段落内硬换行。

Co-authored-by: webjoin111 <455457521@qq.com>
This commit is contained in:
Rumio
2025-10-09 08:50:40 +08:00
committed by GitHub
co-authored by webjoin111
parent e7f3c210df
commit 74a9f3a843
10 changed files with 205 additions and 55 deletions
+7 -4
View File
@@ -126,12 +126,15 @@ class SqlUtils:
def format_usage_for_markdown(text: str) -> str:
"""
智能地将Python多行字符串转换为适合Markdown渲染的格式。
- 将单个换行符替换为Markdown的硬换行(行尾加两个空格)。
- 在列表、标题等块级元素前自动插入换行,确保正确解析。
- 将段落内的单个换行符替换为Markdown的硬换行(行尾加两个空格)。
- 保留两个或更多的连续换行符,使其成为Markdown的段落分隔。
"""
if not text:
return ""
text = re.sub(r"\n{2,}", "<<PARAGRAPH_BREAK>>", text)
text = text.replace("\n", " \n")
text = text.replace("<<PARAGRAPH_BREAK>>", "\n\n")
text = re.sub(r"([^\n])\n(\s*[-*] |\s*#+\s|\s*>)", r"\1\n\n\2", text)
text = re.sub(r"(?<!\n)\n(?!\n)", " \n", text)
return text
+28
View File
@@ -5,10 +5,14 @@ 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
from pydantic import VERSION, BaseModel
import ujson as json
T = TypeVar("T", bound=BaseModel)
V = TypeVar("V")
@@ -19,6 +23,7 @@ __all__ = [
"_dump_pydantic_obj",
"_is_pydantic_type",
"compat_computed_field",
"dump_json_safely",
"model_copy",
"model_dump",
"model_json_schema",
@@ -93,3 +98,26 @@ def parse_as(type_: type[V], obj: Any) -> V:
from pydantic import TypeAdapter # type: ignore
return TypeAdapter(type_).validate_python(obj)
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)