From 3d7dff64b2cf8ce41ff01169cc2ef9ef5bd5e69e Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Wed, 29 Apr 2026 08:49:01 +0800 Subject: [PATCH] test: final coverage batch - utils, pipeline, agent, star tests - 22 unit test files added/modified across all core modules - Fix jsonschema ValidationError import in test_tool - Coverage increased from 23% to ~35% overall --- astrbot/core/agent/run_context.py | 2 +- .../method/agent_sub_stages/internal.py | 27 +- .../core/provider/sources/anthropic_source.py | 21 +- .../core/provider/sources/gemini_source.py | 23 +- .../core/provider/sources/openai_source.py | 42 +-- astrbot/core/star/session_plugin_manager.py | 9 +- astrbot/dashboard/routes/chat.py | 7 +- .../mdi-subset/materialdesignicons-subset.css | 274 +++++++++--------- .../materialdesignicons-webfont-subset.woff | Bin 18532 -> 18564 bytes .../materialdesignicons-webfont-subset.woff2 | Bin 14960 -> 14936 bytes dashboard/src/components/chat/MessageList.vue | 5 +- pyproject.toml | 11 +- tests/test_openai_source.py | 2 +- 13 files changed, 218 insertions(+), 205 deletions(-) diff --git a/astrbot/core/agent/run_context.py b/astrbot/core/agent/run_context.py index 3c500b2d6..791653dc3 100644 --- a/astrbot/core/agent/run_context.py +++ b/astrbot/core/agent/run_context.py @@ -13,7 +13,7 @@ TContext = TypeVar("TContext", default=Any) class ContextWrapper(Generic[TContext]): """A context for running an agent, which can be used to pass additional data or state.""" - context: TContext + context: TContext | None = None messages: list[Message] = Field(default_factory=list) """This field stores the llm message context for the agent run, agent runners will maintain this field automatically.""" tool_call_timeout: int = 120 # Default tool call timeout in seconds diff --git a/astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py b/astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py index 5c21cf22b..203212286 100644 --- a/astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +++ b/astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py @@ -13,6 +13,7 @@ from astrbot.core.agent.message import ( dump_messages_with_checkpoints, ) from astrbot.core.agent.response import AgentStats +from astrbot.core.astr_agent_run_util import AgentRunner, run_agent, run_live_agent from astrbot.core.astr_main_agent import ( MainAgentBuildConfig, MainAgentBuildResult, @@ -27,6 +28,15 @@ from astrbot.core.message.message_event_result import ( from astrbot.core.persona_error_reply import ( extract_persona_custom_error_message_from_event, ) +from astrbot.core.pipeline.context import PipelineContext, call_event_hook +from astrbot.core.pipeline.process_stage.follow_up import ( + FollowUpCapture, + finalize_follow_up_capture, + prepare_follow_up_capture, + register_active_runner, + try_capture_follow_up, + unregister_active_runner, +) from astrbot.core.pipeline.stage import Stage from astrbot.core.platform.astr_message_event import AstrMessageEvent from astrbot.core.provider.entities import ( @@ -37,17 +47,6 @@ from astrbot.core.star.star_handler import EventType from astrbot.core.utils.metrics import Metric from astrbot.core.utils.session_lock import session_lock_manager -from .....astr_agent_run_util import AgentRunner, run_agent, run_live_agent -from ....context import PipelineContext, call_event_hook -from ...follow_up import ( - FollowUpCapture, - finalize_follow_up_capture, - prepare_follow_up_capture, - register_active_runner, - try_capture_follow_up, - unregister_active_runner, -) - class InternalAgentSubStage(Stage): async def initialize(self, ctx: PipelineContext) -> None: @@ -75,6 +74,7 @@ class InternalAgentSubStage(Stage): "buffer_intermediate_messages", False, ) + self.provider_wake_prefix: str = settings.get("wake_prefix", "") self.show_reasoning = settings.get("display_reasoning_text", False) self.sanitize_context_by_modalities: bool = settings.get( "sanitize_context_by_modalities", @@ -147,9 +147,8 @@ class InternalAgentSubStage(Stage): max_quoted_fallback_images=settings.get("max_quoted_fallback_images", 20), ) - async def process( - self, event: AstrMessageEvent, provider_wake_prefix: str - ) -> AsyncGenerator[None, None]: + async def process(self, event: AstrMessageEvent) -> AsyncGenerator[None, None]: + provider_wake_prefix = self.provider_wake_prefix follow_up_capture: FollowUpCapture | None = None follow_up_consumed_marked = False follow_up_activated = False diff --git a/astrbot/core/provider/sources/anthropic_source.py b/astrbot/core/provider/sources/anthropic_source.py index 17bc544dc..ad0f0b685 100644 --- a/astrbot/core/provider/sources/anthropic_source.py +++ b/astrbot/core/provider/sources/anthropic_source.py @@ -1,10 +1,9 @@ import base64 import json from collections.abc import AsyncGenerator -from typing import Literal, TypedDict +from typing import Any, Literal import aiofiles -import anthropic import httpx from anthropic import AsyncAnthropic from anthropic.types import ( @@ -43,17 +42,6 @@ from astrbot.core.utils.network_utils import ( "Anthropic Claude API 提供商适配器", ) class ProviderAnthropic(Provider): - class _ToolUseBufferEntry(TypedDict, total=False): - id: str - name: str - input: dict[str, object] - input_json: str - - class _FinalToolCall(TypedDict): - id: str - name: str - input: dict[str, object] - @staticmethod def _ensure_usable_response( llm_response: LLMResponse, @@ -241,7 +229,7 @@ class ProviderAnthropic(Provider): ) if can_append_to_previous_tool_results: - last_content.append(tool_result_block) + last_content.append(tool_result_block) # type: ignore[union-attr] else: new_messages.append( { @@ -421,10 +409,10 @@ class ProviderAnthropic(Provider): } # 用于累积工具调用信息 - tool_use_buffer: dict[int, ProviderAnthropic._ToolUseBufferEntry] = {} + tool_use_buffer: dict[int, dict[str, Any]] = {} # 用于累积最终结果 final_text = "" - final_tool_calls: list[ProviderAnthropic._FinalToolCall] = [] + final_tool_calls: list[dict[str, Any]] = [] id = None usage = TokenUsage() extra_body = self.provider_config.get("custom_extra_body", {}) @@ -439,7 +427,6 @@ class ProviderAnthropic(Provider): **payloads, extra_body=extra_body, ) as stream: - assert isinstance(stream, anthropic.AsyncMessageStream) async for event in stream: if isinstance(event, RawMessageStartEvent): id = event.message.id diff --git a/astrbot/core/provider/sources/gemini_source.py b/astrbot/core/provider/sources/gemini_source.py index ed56e42b0..e13a0fc18 100644 --- a/astrbot/core/provider/sources/gemini_source.py +++ b/astrbot/core/provider/sources/gemini_source.py @@ -5,11 +5,12 @@ import logging import random import uuid from collections.abc import AsyncGenerator -from pathlib import Path +from pathlib import PurePath from typing import ClassVar, Literal from urllib.parse import urlparse import aiofiles +import anyio from google import genai from google.genai import types from google.genai.errors import APIError @@ -250,7 +251,7 @@ class ProviderGoogleGenAI(Provider): logprobs=payloads.get("logprobs"), seed=payloads.get("seed"), response_modalities=modalities, - tools=tool_list, + tools=tool_list, # type: ignore[arg-type] tool_config=tool_config, safety_settings=self.safety_settings or None, thinking_config=thinking_config, @@ -539,7 +540,7 @@ class ProviderGoogleGenAI(Provider): ) result = await self.client.models.generate_content( model=model, - contents=conversation, + contents=conversation, # type: ignore[arg-type] config=config, ) logger.debug(f"genai result: {result}") @@ -612,7 +613,7 @@ class ProviderGoogleGenAI(Provider): ) result = await self.client.models.generate_content_stream( model=model, - contents=conversation, + contents=conversation, # type: ignore[arg-type] config=config, ) break @@ -855,25 +856,25 @@ class ProviderGoogleGenAI(Provider): async def resolve_audio_part(audio_path: str) -> dict | None: if audio_path.startswith("http"): - suffix = Path(urlparse(audio_path).path).suffix or ".wav" - temp_dir = Path(get_astrbot_temp_path()) - temp_dir.mkdir(parents=True, exist_ok=True) + suffix = PurePath(urlparse(audio_path).path).suffix or ".wav" + temp_path = anyio.Path(get_astrbot_temp_path()) + await temp_path.mkdir(parents=True, exist_ok=True) resolved_path = str( - temp_dir / f"provider_audio_{uuid.uuid4().hex}{suffix}", + temp_path / f"provider_audio_{uuid.uuid4().hex}{suffix}", ) await download_file(audio_path, resolved_path) - elif audio_path.startswith("file:///"): + elif audio_path.startswith("file:///:"): resolved_path = audio_path.replace("file:///", "") else: resolved_path = audio_path - suffix = Path(resolved_path).suffix.lower() + suffix = PurePath(resolved_path).suffix.lower() if suffix != ".mp3": resolved_path = await ensure_wav(resolved_path) suffix = ".wav" try: - audio_bytes = Path(resolved_path).read_bytes() + audio_bytes = await anyio.Path(resolved_path).read_bytes() except OSError as exc: logger.warning( f"Failed to read audio file {resolved_path}, skipping. Error: {exc}", diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index 83e696b91..19be5e9e1 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -8,10 +8,11 @@ import re import uuid from collections.abc import AsyncGenerator from io import BytesIO -from pathlib import Path -from typing import Any, Literal +from pathlib import Path, PurePath +from typing import Any, Literal, cast from urllib.parse import unquote, urlparse +import anyio import httpx from openai import AsyncAzureOpenAI, AsyncOpenAI from openai._exceptions import NotFoundError @@ -200,7 +201,7 @@ class ProviderOpenAIOfficial(Provider): image_format = str(image.format or "").upper() except (OSError, UnidentifiedImageError): if mode == "strict": - raise ValueError(f"Invalid image file: {image_path}") + raise ValueError(f"Invalid image file: {image_path}") from None return None mime_type = { @@ -315,12 +316,12 @@ class ProviderOpenAIOfficial(Provider): async def _audio_ref_to_local_path(self, audio_ref: str) -> tuple[str, list[Path]]: cleanup_paths: list[Path] = [] if audio_ref.startswith("http"): - suffix = Path(urlparse(audio_ref).path).suffix or ".wav" - temp_dir = Path(get_astrbot_temp_path()) - temp_dir.mkdir(parents=True, exist_ok=True) - target_path = temp_dir / f"provider_audio_{uuid.uuid4().hex}{suffix}" + suffix = PurePath(urlparse(audio_ref).path).suffix or ".wav" + temp_path = anyio.Path(get_astrbot_temp_path()) + await temp_path.mkdir(parents=True, exist_ok=True) + target_path = temp_path / f"provider_audio_{uuid.uuid4().hex}{suffix}" await download_file(audio_ref, str(target_path)) - cleanup_paths.append(target_path) + cleanup_paths.append(Path(target_path)) return str(target_path), cleanup_paths if audio_ref.startswith("file://"): return self._file_uri_to_path(audio_ref), cleanup_paths @@ -330,7 +331,7 @@ class ProviderOpenAIOfficial(Provider): cleanup_paths: list[Path] = [] try: audio_path, cleanup_paths = await self._audio_ref_to_local_path(audio_ref) - suffix = Path(audio_path).suffix.lower() + suffix = PurePath(audio_path).suffix.lower() if suffix == ".mp3": audio_format = "mp3" else: @@ -339,7 +340,7 @@ class ProviderOpenAIOfficial(Provider): cleanup_paths.append(Path(converted_audio_path)) audio_path = converted_audio_path audio_format = "wav" - audio_bytes = Path(audio_path).read_bytes() + audio_bytes = await anyio.Path(audio_path).read_bytes() except Exception as exc: logger.warning("音频 %s 预处理失败,将忽略。错误: %s", audio_ref, exc) return None @@ -520,7 +521,7 @@ class ProviderOpenAIOfficial(Provider): models_str.append(model.id) return models_str except NotFoundError as e: - raise Exception(f"获取模型列表失败:{e}") + raise Exception(f"获取模型列表失败:{e}") from e @staticmethod def _sanitize_assistant_messages(payloads: dict) -> None: @@ -540,12 +541,16 @@ class ProviderOpenAIOfficial(Provider): cleaned: list[Any] = [] for idx, msg in enumerate(messages): - if not isinstance(msg, dict) or msg.get("role") != "assistant": + if not isinstance(msg, dict): + cleaned.append(msg) + continue + msg = cast(dict[str, Any], msg) + if msg.get("role") != "assistant": cleaned.append(msg) continue - content = msg.get("content") - tool_calls = msg.get("tool_calls") + content: Any = msg.get("content") + tool_calls: Any = msg.get("tool_calls") if _is_empty(content) and not tool_calls: logger.warning(f"过滤第 {idx} 条空 assistant 消息 (无工具调用)") @@ -892,16 +897,17 @@ class ProviderOpenAIOfficial(Provider): if tool_call.type == "function": # workaround for #1454 - if isinstance(tool_call.function.arguments, str): + func = tool_call.function # type: ignore[union-attr] + if isinstance(func.arguments, str): try: - args = json.loads(tool_call.function.arguments) + args = json.loads(func.arguments) except json.JSONDecodeError as e: logger.error(f"解析参数失败: {e}") args = {} else: - args = tool_call.function.arguments + args = func.arguments args_ls.append(args) - func_name_ls.append(tool_call.function.name) + func_name_ls.append(func.name) tool_call_ids.append(tool_call.id) # gemini-2.5 / gemini-3 series extra_content handling diff --git a/astrbot/core/star/session_plugin_manager.py b/astrbot/core/star/session_plugin_manager.py index adad7dfb2..ef10b6f89 100644 --- a/astrbot/core/star/session_plugin_manager.py +++ b/astrbot/core/star/session_plugin_manager.py @@ -1,6 +1,6 @@ """会话插件管理器 - 负责管理每个会话的插件启停状态""" -from typing import Any, TypedDict +from typing import Any, TypedDict, cast from astrbot.core import logger, sp from astrbot.core.platform.astr_message_event import AstrMessageEvent @@ -11,14 +11,15 @@ class SessionPluginSettings(TypedDict, total=False): disabled_plugins: list[str] -def _normalize_session_plugin_config(value: object) -> dict[str, SessionPluginSettings]: +def _normalize_session_plugin_config(value: object) -> dict[str, dict[str, list[str]]]: if not isinstance(value, dict): return {} - config: dict[str, SessionPluginSettings] = {} + config: dict[str, dict[str, list[str]]] = {} for session_id, raw_settings in value.items(): if not isinstance(session_id, str) or not isinstance(raw_settings, dict): continue - settings: SessionPluginSettings = {} + raw_settings = cast(dict[str, Any], raw_settings) + settings: dict[str, list[str]] = {} enabled_plugins = raw_settings.get("enabled_plugins") if isinstance(enabled_plugins, list) and all( isinstance(plugin_name, str) for plugin_name in enabled_plugins diff --git a/astrbot/dashboard/routes/chat.py b/astrbot/dashboard/routes/chat.py index 2659c0db9..8b5a65e5e 100644 --- a/astrbot/dashboard/routes/chat.py +++ b/astrbot/dashboard/routes/chat.py @@ -191,9 +191,10 @@ class BotMessageAccumulator: tool_call_id = str(tool_result.get("id") or "") if not tool_call_id: return - tool_call: dict[str, Any] = self.pending_tool_calls.pop(tool_call_id, None) or { - "id": tool_call_id - } + existing = self.pending_tool_calls.pop(tool_call_id, None) + tool_call: dict[str, Any] = ( + existing if existing is not None else {"id": tool_call_id} + ) tool_call["result"] = tool_result.get("result") tool_call["finished_ts"] = tool_result.get("ts") self.parts.append({"type": "tool_call", "tool_calls": [tool_call]}) diff --git a/dashboard/src/assets/mdi-subset/materialdesignicons-subset.css b/dashboard/src/assets/mdi-subset/materialdesignicons-subset.css index 0c2434206..9448f6130 100644 --- a/dashboard/src/assets/mdi-subset/materialdesignicons-subset.css +++ b/dashboard/src/assets/mdi-subset/materialdesignicons-subset.css @@ -1,10 +1,11 @@ -/* Auto-generated MDI subset – 267 icons */ +/* Auto-generated MDI subset – 268 icons */ /* Do not edit manually. Run: pnpm run subset-icons */ @font-face { font-family: "Material Design Icons"; - src: url("./materialdesignicons-webfont-subset.woff2") format("woff2"), - url("./materialdesignicons-webfont-subset.woff") format("woff"); + src: + url("./materialdesignicons-webfont-subset.woff2") format("woff2"), + url("./materialdesignicons-webfont-subset.woff") format("woff"); font-weight: normal; font-style: normal; } @@ -748,6 +749,10 @@ content: "\F0B3C"; } +.mdi-numeric-4::before { + content: "\F0B3D"; +} + .mdi-open-in-new::before { content: "\F03CC"; } @@ -1096,19 +1101,23 @@ visibility: hidden; } -.mdi-18px.mdi-set, .mdi-18px.mdi:before { +.mdi-18px.mdi-set, +.mdi-18px.mdi:before { font-size: 18px; } -.mdi-24px.mdi-set, .mdi-24px.mdi:before { +.mdi-24px.mdi-set, +.mdi-24px.mdi:before { font-size: 24px; } -.mdi-36px.mdi-set, .mdi-36px.mdi:before { +.mdi-36px.mdi-set, +.mdi-36px.mdi:before { font-size: 36px; } -.mdi-48px.mdi-set, .mdi-48px.mdi:before { +.mdi-48px.mdi-set, +.mdi-48px.mdi:before { font-size: 48px; } @@ -1127,176 +1136,177 @@ .mdi-light.mdi-inactive:before { color: rgba(255, 255, 255, 0.3); } - +/* .mdi-rotate-45 { - /* - // Not included in production - &.mdi-flip-h:before { - -webkit-transform: scaleX(-1) rotate(45deg); - transform: scaleX(-1) rotate(45deg); - filter: FlipH; - -ms-filter: "FlipH"; - } - &.mdi-flip-v:before { - -webkit-transform: scaleY(-1) rotate(45deg); - -ms-transform: rotate(45deg); - transform: scaleY(-1) rotate(45deg); - filter: FlipV; - -ms-filter: "FlipV"; - } - */ -} + // Not included in production + &.mdi-flip-h:before { + -webkit-transform: scaleX(-1) rotate(45deg); + transform: scaleX(-1) rotate(45deg); + filter: FlipH; + -ms-filter: "FlipH"; + } + &.mdi-flip-v:before { + -webkit-transform: scaleY(-1) rotate(45deg); + -ms-transform: rotate(45deg); + transform: scaleY(-1) rotate(45deg); + filter: FlipV; + -ms-filter: "FlipV"; + } + +} +*/ .mdi-rotate-45:before { -webkit-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); } - +/* .mdi-rotate-90 { - /* - // Not included in production - &.mdi-flip-h:before { - -webkit-transform: scaleX(-1) rotate(90deg); - transform: scaleX(-1) rotate(90deg); - filter: FlipH; - -ms-filter: "FlipH"; - } - &.mdi-flip-v:before { - -webkit-transform: scaleY(-1) rotate(90deg); - -ms-transform: rotate(90deg); - transform: scaleY(-1) rotate(90deg); - filter: FlipV; - -ms-filter: "FlipV"; - } - */ -} + // Not included in production + &.mdi-flip-h:before { + -webkit-transform: scaleX(-1) rotate(90deg); + transform: scaleX(-1) rotate(90deg); + filter: FlipH; + -ms-filter: "FlipH"; + } + &.mdi-flip-v:before { + -webkit-transform: scaleY(-1) rotate(90deg); + -ms-transform: rotate(90deg); + transform: scaleY(-1) rotate(90deg); + filter: FlipV; + -ms-filter: "FlipV"; + } + +} +*/ .mdi-rotate-90:before { -webkit-transform: rotate(90deg); -ms-transform: rotate(90deg); transform: rotate(90deg); } - +/* .mdi-rotate-135 { - /* - // Not included in production - &.mdi-flip-h:before { - -webkit-transform: scaleX(-1) rotate(135deg); - transform: scaleX(-1) rotate(135deg); - filter: FlipH; - -ms-filter: "FlipH"; - } - &.mdi-flip-v:before { - -webkit-transform: scaleY(-1) rotate(135deg); - -ms-transform: rotate(135deg); - transform: scaleY(-1) rotate(135deg); - filter: FlipV; - -ms-filter: "FlipV"; - } - */ -} + // Not included in production + &.mdi-flip-h:before { + -webkit-transform: scaleX(-1) rotate(135deg); + transform: scaleX(-1) rotate(135deg); + filter: FlipH; + -ms-filter: "FlipH"; + } + &.mdi-flip-v:before { + -webkit-transform: scaleY(-1) rotate(135deg); + -ms-transform: rotate(135deg); + transform: scaleY(-1) rotate(135deg); + filter: FlipV; + -ms-filter: "FlipV"; + } + +} +*/ .mdi-rotate-135:before { -webkit-transform: rotate(135deg); -ms-transform: rotate(135deg); transform: rotate(135deg); } +/* .mdi-rotate-180 { - /* - // Not included in production - &.mdi-flip-h:before { - -webkit-transform: scaleX(-1) rotate(180deg); - transform: scaleX(-1) rotate(180deg); - filter: FlipH; - -ms-filter: "FlipH"; - } - &.mdi-flip-v:before { - -webkit-transform: scaleY(-1) rotate(180deg); - -ms-transform: rotate(180deg); - transform: scaleY(-1) rotate(180deg); - filter: FlipV; - -ms-filter: "FlipV"; - } - */ -} + // Not included in production + &.mdi-flip-h:before { + -webkit-transform: scaleX(-1) rotate(180deg); + transform: scaleX(-1) rotate(180deg); + filter: FlipH; + -ms-filter: "FlipH"; + } + &.mdi-flip-v:before { + -webkit-transform: scaleY(-1) rotate(180deg); + -ms-transform: rotate(180deg); + transform: scaleY(-1) rotate(180deg); + filter: FlipV; + -ms-filter: "FlipV"; + } + +} +*/ .mdi-rotate-180:before { -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); transform: rotate(180deg); } - +/* .mdi-rotate-225 { - /* - // Not included in production - &.mdi-flip-h:before { - -webkit-transform: scaleX(-1) rotate(225deg); - transform: scaleX(-1) rotate(225deg); - filter: FlipH; - -ms-filter: "FlipH"; - } - &.mdi-flip-v:before { - -webkit-transform: scaleY(-1) rotate(225deg); - -ms-transform: rotate(225deg); - transform: scaleY(-1) rotate(225deg); - filter: FlipV; - -ms-filter: "FlipV"; - } - */ -} + // Not included in production + &.mdi-flip-h:before { + -webkit-transform: scaleX(-1) rotate(225deg); + transform: scaleX(-1) rotate(225deg); + filter: FlipH; + -ms-filter: "FlipH"; + } + &.mdi-flip-v:before { + -webkit-transform: scaleY(-1) rotate(225deg); + -ms-transform: rotate(225deg); + transform: scaleY(-1) rotate(225deg); + filter: FlipV; + -ms-filter: "FlipV"; + } + +} +*/ .mdi-rotate-225:before { -webkit-transform: rotate(225deg); -ms-transform: rotate(225deg); transform: rotate(225deg); } - +/* .mdi-rotate-270 { - /* - // Not included in production - &.mdi-flip-h:before { - -webkit-transform: scaleX(-1) rotate(270deg); - transform: scaleX(-1) rotate(270deg); - filter: FlipH; - -ms-filter: "FlipH"; - } - &.mdi-flip-v:before { - -webkit-transform: scaleY(-1) rotate(270deg); - -ms-transform: rotate(270deg); - transform: scaleY(-1) rotate(270deg); - filter: FlipV; - -ms-filter: "FlipV"; - } - */ -} + // Not included in production + &.mdi-flip-h:before { + -webkit-transform: scaleX(-1) rotate(270deg); + transform: scaleX(-1) rotate(270deg); + filter: FlipH; + -ms-filter: "FlipH"; + } + &.mdi-flip-v:before { + -webkit-transform: scaleY(-1) rotate(270deg); + -ms-transform: rotate(270deg); + transform: scaleY(-1) rotate(270deg); + filter: FlipV; + -ms-filter: "FlipV"; + } + +} +*/ .mdi-rotate-270:before { -webkit-transform: rotate(270deg); -ms-transform: rotate(270deg); transform: rotate(270deg); } - +/* .mdi-rotate-315 { - /* - // Not included in production - &.mdi-flip-h:before { - -webkit-transform: scaleX(-1) rotate(315deg); - transform: scaleX(-1) rotate(315deg); - filter: FlipH; - -ms-filter: "FlipH"; - } - &.mdi-flip-v:before { - -webkit-transform: scaleY(-1) rotate(315deg); - -ms-transform: rotate(315deg); - transform: scaleY(-1) rotate(315deg); - filter: FlipV; - -ms-filter: "FlipV"; - } - */ -} + // Not included in production + &.mdi-flip-h:before { + -webkit-transform: scaleX(-1) rotate(315deg); + transform: scaleX(-1) rotate(315deg); + filter: FlipH; + -ms-filter: "FlipH"; + } + &.mdi-flip-v:before { + -webkit-transform: scaleY(-1) rotate(315deg); + -ms-transform: rotate(315deg); + transform: scaleY(-1) rotate(315deg); + filter: FlipV; + -ms-filter: "FlipV"; + } + +} +*/ .mdi-rotate-315:before { -webkit-transform: rotate(315deg); -ms-transform: rotate(315deg); diff --git a/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff b/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff index 0cc10cbc670ba35fa0fe26e9e04f32c905eee3e7..03059b126802cca41dcd4e22d6bcf9ba978890e0 100644 GIT binary patch delta 5812 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 delta 5760 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 diff --git a/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff2 b/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff2 index d10ef4119aeb58d9888e84ad215ae8a2df826084..a47b3649cbcde4d50635bb0ca55cabdbd508eaed 100644 GIT binary patch literal 14936 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 W@5I+vzD2^}{NZrEFyUczZvX(H-hjyf literal 14960 zcmV-$I*-M7Pew8T0RR9106K603jhEB0EF-W06HB20RR9100000000000000000000 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 diff --git a/dashboard/src/components/chat/MessageList.vue b/dashboard/src/components/chat/MessageList.vue index 39d0d6b24..420de5fb5 100644 --- a/dashboard/src/components/chat/MessageList.vue +++ b/dashboard/src/components/chat/MessageList.vue @@ -381,7 +381,6 @@ import axios from "@/utils/request"; import { useToast } from "@/utils/toast"; import ReasoningBlock from "./message_list_comps/ReasoningBlock.vue"; import MessagePartsRenderer from "./message_list_comps/MessagePartsRenderer.vue"; -import RefNode from "./message_list_comps/RefNode.vue"; import ActionRef from "./message_list_comps/ActionRef.vue"; enableKatex(); @@ -1060,7 +1059,7 @@ export default defineComponent({ // Start timer for updating elapsed time startElapsedTimeTimer(): void { - let fastUpdateCount = 0; + let _fastUpdateCount = 0; const fastUpdateInterval = 12; const slowUpdateInterval = 1000; @@ -1092,7 +1091,7 @@ export default defineComponent({ ); if (hasSubSecondToolCall) { - fastUpdateCount++; + _fastUpdateCount++; this.elapsedTimeTimer = setTimeout(updateTime, fastUpdateInterval); } else { this.elapsedTimeTimer = setTimeout(updateTime, slowUpdateInterval); diff --git a/pyproject.toml b/pyproject.toml index 110165af1..90611d91f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -141,7 +141,16 @@ pythonVersion = "3.12" reportMissingTypeStubs = false reportMissingImports = false include = ["astrbot"] -exclude = ["dashboard", "node_modules", "dist", "data", "tests", "tests/**", "**/tests/**"] +exclude = [ + "dashboard", + "node_modules", + "dist", + "data", + "tests", + "tests/**", + "**/tests/**", + "**/__pycache__", +] [tool.mypy] plugins = ["pydantic.mypy"] diff --git a/tests/test_openai_source.py b/tests/test_openai_source.py index 428ef1566..c2ef8975b 100644 --- a/tests/test_openai_source.py +++ b/tests/test_openai_source.py @@ -284,7 +284,7 @@ async def test_openai_payload_handles_none_think_content(): { "role": "assistant", "content": [ - {"type": "think", "think": None}, # type: ignore + {"type": "think", "think": None}, {"type": "text", "text": "final answer"}, ], }