Compare commits

...

4 Commits

7 changed files with 51 additions and 17 deletions

View File

@@ -1,4 +1,4 @@
import logging
__version__ = "4.26.0"
__version__ = "4.26.1"
logger = logging.getLogger("astrbot")

View File

@@ -210,7 +210,8 @@ class InternalAgentSubStage(Stage):
await event.send_typing()
except Exception:
logger.warning("send_typing failed", exc_info=True)
await call_event_hook(event, EventType.OnWaitingLLMRequestEvent)
if await call_event_hook(event, EventType.OnWaitingLLMRequestEvent):
return
async with session_lock_manager.acquire_lock(event.unified_msg_origin):
logger.debug("acquired session lock for llm request")

View File

@@ -25,7 +25,7 @@ from tenacity import (
from astrbot.api import logger
from astrbot.api.event import AstrMessageEvent, MessageChain
from astrbot.api.message_components import At, File, Image, Plain, Record, Video
from astrbot.api.message_components import File, Image, Plain, Record, Video
from astrbot.api.platform import AstrBotMessage, PlatformMetadata
from astrbot.core.utils.media_utils import MediaResolver, file_uri_to_path, is_file_uri
@@ -747,10 +747,6 @@ class QQOfficialMessageEvent(AstrMessageEvent):
file_source = file_path
elif i.url:
file_source = i.url
elif isinstance(i, At):
qq_id = getattr(i, "qq", "")
if qq_id and qq_id != "all":
plain_text += f"<@{qq_id}>"
else:
logger.debug(f"qq_official 忽略 {i.type}")
return (

View File

@@ -249,9 +249,10 @@ class _PermissionGuardedTool(FunctionTool):
if error is not None:
return error
# Delegate to handler first (plugin tools).
# @filter.llm_tool decorated tools have a handler attribute, which is the actual callable.
if self._wrapped.handler is not None:
result = self._wrapped.handler(context, **kwargs)
event = context.context.event
result = self._wrapped.handler(event, **kwargs)
if _inspect.isasyncgen(result):
last: Any = None
async for item in result:
@@ -261,11 +262,12 @@ class _PermissionGuardedTool(FunctionTool):
return await result
return result
# Fall back to overridden call() on subclasses (e.g. MCPTool).
# If the tool has a "call" method that is not the default FunctionTool.call, invoke it.
call_override = getattr(type(self._wrapped), "call", None)
if call_override is not None and call_override is not FunctionTool.call:
return await self._wrapped.call(context, **kwargs)
# Compatibility fallback: if the tool has a "run" method, invoke it. This is for legacy tools that don't use the new handler/call interface.
run = getattr(self._wrapped, "run", None)
if run is not None:
event = context.context.event

30
changelogs/v4.26.1.md Normal file
View File

@@ -0,0 +1,30 @@
- [更新日志(简体中文)](#chinese)
- [Changelog(English)](#english)
<a id="chinese"></a>
## What's Changed
### 修复
- 修复插件注册的 LLM Tool 在受保护工具权限处理场景下可能无法正常调用的问题。([#9001](https://github.com/AstrBotDevs/AstrBot/pull/9001))
- 修复等待 LLM 请求事件在获取会话锁前未及时处理停止信号的问题。([#8935](https://github.com/AstrBotDevs/AstrBot/pull/8935))
- 回滚 QQ 官方平台发送消息时保留 At 组件的相关变更,避免引入兼容性问题。([#9004](https://github.com/AstrBotDevs/AstrBot/pull/9004))
### 其他
- 补充 Cloudflare Workers 静态资源相关配置。([commit](https://github.com/AstrBotDevs/AstrBot/commit/44df70d2e), [commit](https://github.com/AstrBotDevs/AstrBot/commit/348fe8172))
<a id="english"></a>
## What's Changed (EN)
### Bug Fixes
- Fixed a case where plugin-registered LLM tools could fail to invoke when guarded tool permission handling was applied. ([#9001](https://github.com/AstrBotDevs/AstrBot/pull/9001))
- Fixed waiting LLM request events so stop signals are checked before acquiring the session lock. ([#8935](https://github.com/AstrBotDevs/AstrBot/pull/8935))
- Reverted the QQ Official platform At component preservation change to avoid compatibility regressions. ([#9004](https://github.com/AstrBotDevs/AstrBot/pull/9004))
### Other
- Added Cloudflare Workers static asset configuration. ([commit](https://github.com/AstrBotDevs/AstrBot/commit/44df70d2e), [commit](https://github.com/AstrBotDevs/AstrBot/commit/348fe8172))

View File

@@ -1,6 +1,6 @@
[project]
name = "AstrBot"
version = "4.26.0"
version = "4.26.1"
description = "Easy-to-use multi-platform LLM chatbot and development framework"
readme = "README.md"
license = { text = "AGPL-3.0-or-later" }

View File

@@ -174,16 +174,19 @@ async def test_check_permission_passes_for_member_when_configured_member():
@pytest.mark.asyncio
async def test_guarded_tool_delegates_when_permission_passes():
async def test_guarded_tool_delegates_handler_with_event_when_permission_passes():
_clear_tool_permissions()
mgr = FunctionToolManager()
called = False
received_event = None
async def handler(ctx, **kw):
async def handler(event, **kw):
nonlocal called
nonlocal received_event
called = True
return "ok"
received_event = event
return f"ok:{event.get_sender_id()}:{kw['value']}"
wrapped = FunctionTool(
name="delegated",
@@ -194,9 +197,10 @@ async def test_guarded_tool_delegates_when_permission_passes():
guarded = _PermissionGuardedTool(wrapped, mgr)
context = _make_context(role="member")
result = await guarded.call(context)
result = await guarded.call(context, value="sentinel")
assert called
assert result == "ok"
assert received_event is context.context.event
assert result == "ok:user_123:sentinel"
@pytest.mark.asyncio
@@ -280,7 +284,8 @@ async def test_guarded_tool_handles_async_generator_handler():
_clear_tool_permissions()
mgr = FunctionToolManager()
async def gen_handler(ctx, **kw): # type: ignore[misc]
async def gen_handler(event, **kw): # type: ignore[misc]
assert event is context.context.event
yield "A"
yield "B"
yield "C"