diff --git a/astrbot/dashboard/routes/chat.py b/astrbot/dashboard/routes/chat.py index 56946550a..b50a4bab0 100644 --- a/astrbot/dashboard/routes/chat.py +++ b/astrbot/dashboard/routes/chat.py @@ -3,6 +3,7 @@ import json import os import uuid from contextlib import asynccontextmanager +from typing import cast from quart import Response as QuartResponse from quart import g, make_response, request @@ -233,15 +234,19 @@ class ChatRoute(Route): ), ) - response = await make_response( - stream(), - { - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - }, + response = cast( + QuartResponse, + await make_response( + stream(), + { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + }, + ), ) + response.timeout = None # fix SSE auto disconnect issue return response @@ -312,7 +317,7 @@ class ChatRoute(Route): # remove content conversations_ = [] for conv in conversations: - conv.history = None + conv.history = None # type: ignore[attr-defined] conversations_.append(conv) return Response().ok(data=conversations_).__dict__ diff --git a/astrbot/dashboard/routes/config.py b/astrbot/dashboard/routes/config.py index b947d26f2..827f5e8da 100644 --- a/astrbot/dashboard/routes/config.py +++ b/astrbot/dashboard/routes/config.py @@ -2,6 +2,7 @@ import asyncio import inspect import os import traceback +from typing import Any, cast from quart import request @@ -26,7 +27,7 @@ from astrbot.core.utils.astrbot_path import get_astrbot_path from .route import Response, Route, RouteContext -def try_cast(value: str, type_: str): +def try_cast(value: Any, type_: str): if type_ == "int": try: return int(value) @@ -133,7 +134,9 @@ def save_config(post_config: dict, config: AstrBotConfig, is_core: bool = False) is_core, ) else: - errors, post_config = validate_config(post_config, config.schema, is_core) + errors, post_config = validate_config( + post_config, cast(dict, config.schema), is_core + ) except BaseException as e: logger.error(traceback.format_exc()) logger.warning(f"验证配置时出现异常: {e}") @@ -287,6 +290,7 @@ class ConfigRoute(Route): .ok({"config": abconf, "metadata": CONFIG_METADATA_3_SYSTEM}) .__dict__ ) + assert abconf_id is not None abconf = self.acm.confs[abconf_id] return ( Response() @@ -598,7 +602,9 @@ class ConfigRoute(Route): return Response().error("缺少参数 provider_id").__dict__ prov_mgr = self.core_lifecycle.provider_manager - provider: Provider | None = prov_mgr.inst_map.get(provider_id, None) + provider: Provider | None = cast( + Provider, prov_mgr.inst_map.get(provider_id, None) + ) if not provider: return Response().error(f"未找到 ID 为 {provider_id} 的提供商").__dict__ @@ -651,9 +657,9 @@ class ConfigRoute(Route): if not isinstance(inst, EmbeddingProvider): return Response().error("提供商不是 EmbeddingProvider 类型").__dict__ - # 初始化 - if getattr(inst, "initialize", None): - await inst.initialize() + init_fn = getattr(inst, "initialize", None) + if inspect.iscoroutinefunction(init_fn): + await init_fn() # 获取嵌入向量维度 vec = await inst.get_embedding("echo") @@ -906,7 +912,7 @@ class ConfigRoute(Route): return {"metadata": CONFIG_METADATA_2, "config": config} async def _get_plugin_config(self, plugin_name: str): - ret = {"metadata": None, "config": None} + ret: dict = {"metadata": None, "config": None} for plugin_md in star_registry: if plugin_md.name == plugin_name: diff --git a/astrbot/dashboard/routes/log.py b/astrbot/dashboard/routes/log.py index eb02fdf40..86cc8c6ca 100644 --- a/astrbot/dashboard/routes/log.py +++ b/astrbot/dashboard/routes/log.py @@ -1,6 +1,8 @@ import asyncio import json +from typing import cast +from quart import Response as QuartResponse from quart import make_response from astrbot.core import LogBroker, logger @@ -39,14 +41,17 @@ class LogRoute(Route): if queue: self.log_broker.unregister(queue) - response = await make_response( - stream(), - { - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - "Connection": "keep-alive", - "Transfer-Encoding": "chunked", - }, + response = cast( + QuartResponse, + await make_response( + stream(), + { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + "Connection": "keep-alive", + "Transfer-Encoding": "chunked", + }, + ), ) response.timeout = None return response diff --git a/astrbot/dashboard/routes/plugin.py b/astrbot/dashboard/routes/plugin.py index 597a245d4..40a6df7c3 100644 --- a/astrbot/dashboard/routes/plugin.py +++ b/astrbot/dashboard/routes/plugin.py @@ -486,6 +486,7 @@ class PluginRoute(Route): logger.warning(f"插件 {plugin_name} 不存在") return Response().error(f"插件 {plugin_name} 不存在").__dict__ + assert plugin_obj.root_dir_name is not None plugin_dir = os.path.join( self.plugin_manager.plugin_store_path, plugin_obj.root_dir_name, diff --git a/astrbot/dashboard/routes/route.py b/astrbot/dashboard/routes/route.py index 1105b69a7..01ab292d4 100644 --- a/astrbot/dashboard/routes/route.py +++ b/astrbot/dashboard/routes/route.py @@ -12,6 +12,8 @@ class RouteContext: class Route: + routes: list | dict + def __init__(self, context: RouteContext): self.app = context.app self.config = context.config diff --git a/astrbot/dashboard/server.py b/astrbot/dashboard/server.py index 84976f2ba..171a777f6 100644 --- a/astrbot/dashboard/server.py +++ b/astrbot/dashboard/server.py @@ -2,9 +2,12 @@ import asyncio import logging import os import socket +from typing import cast import jwt import psutil +from flask.json.provider import DefaultJSONProvider +from psutil._common import addr as psutil_addr from quart import Quart, g, jsonify, request from quart.logging import default_handler @@ -20,7 +23,7 @@ from .routes.route import Response, RouteContext from .routes.session_management import SessionManagementRoute from .routes.t2i import T2iRoute -APP: Quart = None +APP: Quart class AstrBotDashboard: @@ -47,6 +50,7 @@ class AstrBotDashboard: self.app.config["MAX_CONTENT_LENGTH"] = ( 128 * 1024 * 1024 ) # 将 Flask 允许的最大上传文件体大小设置为 128 MB + assert isinstance(self.app.json, DefaultJSONProvider) self.app.json.sort_keys = False self.app.before_request(self.auth_middleware) # token 用于验证请求 @@ -145,7 +149,7 @@ class AstrBotDashboard: """获取占用端口的进程详细信息""" try: for conn in psutil.net_connections(kind="inet"): - if conn.laddr.port == port: + if cast(psutil_addr, conn.laddr).port == port: try: process = psutil.Process(conn.pid) # 获取详细信息