AstrBot 4.1.4__py3-none-any.whl → 4.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- astrbot/core/agent/agent.py +1 -1
- astrbot/core/agent/mcp_client.py +3 -1
- astrbot/core/agent/runners/tool_loop_agent_runner.py +6 -27
- astrbot/core/agent/tool.py +28 -17
- astrbot/core/config/default.py +50 -14
- astrbot/core/db/sqlite.py +15 -1
- astrbot/core/pipeline/content_safety_check/stage.py +1 -1
- astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +1 -1
- astrbot/core/pipeline/content_safety_check/strategies/keywords.py +1 -1
- astrbot/core/pipeline/context_utils.py +4 -1
- astrbot/core/pipeline/process_stage/method/llm_request.py +23 -4
- astrbot/core/pipeline/process_stage/method/star_request.py +8 -6
- astrbot/core/platform/manager.py +4 -0
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +2 -1
- astrbot/core/platform/sources/misskey/misskey_adapter.py +391 -0
- astrbot/core/platform/sources/misskey/misskey_api.py +404 -0
- astrbot/core/platform/sources/misskey/misskey_event.py +123 -0
- astrbot/core/platform/sources/misskey/misskey_utils.py +327 -0
- astrbot/core/platform/sources/satori/satori_adapter.py +290 -24
- astrbot/core/platform/sources/satori/satori_event.py +9 -0
- astrbot/core/platform/sources/telegram/tg_event.py +0 -1
- astrbot/core/provider/entities.py +13 -3
- astrbot/core/provider/func_tool_manager.py +4 -4
- astrbot/core/provider/manager.py +35 -19
- astrbot/core/star/context.py +26 -12
- astrbot/core/star/filter/command_group.py +4 -4
- astrbot/core/star/filter/platform_adapter_type.py +10 -5
- astrbot/core/star/register/star.py +3 -1
- astrbot/core/star/register/star_handler.py +65 -36
- astrbot/core/star/session_plugin_manager.py +3 -0
- astrbot/core/star/star_handler.py +4 -4
- astrbot/core/star/star_manager.py +10 -4
- astrbot/core/star/star_tools.py +6 -2
- astrbot/core/star/updator.py +3 -0
- {astrbot-4.1.4.dist-info → astrbot-4.1.5.dist-info}/METADATA +6 -7
- {astrbot-4.1.4.dist-info → astrbot-4.1.5.dist-info}/RECORD +39 -35
- {astrbot-4.1.4.dist-info → astrbot-4.1.5.dist-info}/WHEEL +0 -0
- {astrbot-4.1.4.dist-info → astrbot-4.1.5.dist-info}/entry_points.txt +0 -0
- {astrbot-4.1.4.dist-info → astrbot-4.1.5.dist-info}/licenses/LICENSE +0 -0
astrbot/core/agent/agent.py
CHANGED
|
@@ -9,5 +9,5 @@ from .hooks import BaseAgentRunHooks
|
|
|
9
9
|
class Agent(Generic[TContext]):
|
|
10
10
|
name: str
|
|
11
11
|
instructions: str | None = None
|
|
12
|
-
tools: list[str
|
|
12
|
+
tools: list[str | FunctionTool] | None = None
|
|
13
13
|
run_hooks: BaseAgentRunHooks[TContext] | None = None
|
astrbot/core/agent/mcp_client.py
CHANGED
|
@@ -92,7 +92,7 @@ class MCPClient:
|
|
|
92
92
|
self.session: Optional[mcp.ClientSession] = None
|
|
93
93
|
self.exit_stack = AsyncExitStack()
|
|
94
94
|
|
|
95
|
-
self.name = None
|
|
95
|
+
self.name: str | None = None
|
|
96
96
|
self.active: bool = True
|
|
97
97
|
self.tools: list[mcp.Tool] = []
|
|
98
98
|
self.server_errlogs: list[str] = []
|
|
@@ -198,6 +198,8 @@ class MCPClient:
|
|
|
198
198
|
|
|
199
199
|
async def list_tools_and_save(self) -> mcp.ListToolsResult:
|
|
200
200
|
"""List all tools from the server and save them to self.tools"""
|
|
201
|
+
if not self.session:
|
|
202
|
+
raise Exception("MCP Client is not initialized")
|
|
201
203
|
response = await self.session.list_tools()
|
|
202
204
|
self.tools = response.tools
|
|
203
205
|
return response
|
|
@@ -269,17 +269,6 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]):
|
|
|
269
269
|
)
|
|
270
270
|
yield MessageChain().message("返回的数据类型不受支持。")
|
|
271
271
|
|
|
272
|
-
try:
|
|
273
|
-
await self.agent_hooks.on_tool_end(
|
|
274
|
-
self.run_context,
|
|
275
|
-
func_tool_name,
|
|
276
|
-
func_tool_args,
|
|
277
|
-
resp,
|
|
278
|
-
)
|
|
279
|
-
except Exception as e:
|
|
280
|
-
logger.error(
|
|
281
|
-
f"Error in on_tool_end hook: {e}", exc_info=True
|
|
282
|
-
)
|
|
283
272
|
elif resp is None:
|
|
284
273
|
# Tool 直接请求发送消息给用户
|
|
285
274
|
# 这里我们将直接结束 Agent Loop。
|
|
@@ -289,27 +278,17 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]):
|
|
|
289
278
|
yield MessageChain(
|
|
290
279
|
chain=res.chain, type="tool_direct_result"
|
|
291
280
|
)
|
|
292
|
-
try:
|
|
293
|
-
await self.agent_hooks.on_tool_end(
|
|
294
|
-
self.run_context, func_tool_name, func_tool_args, None
|
|
295
|
-
)
|
|
296
|
-
except Exception as e:
|
|
297
|
-
logger.error(
|
|
298
|
-
f"Error in on_tool_end hook: {e}", exc_info=True
|
|
299
|
-
)
|
|
300
281
|
else:
|
|
301
282
|
logger.warning(
|
|
302
283
|
f"Tool 返回了不支持的类型: {type(resp)},将忽略。"
|
|
303
284
|
)
|
|
304
285
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
f"Error in on_tool_end hook: {e}", exc_info=True
|
|
312
|
-
)
|
|
286
|
+
try:
|
|
287
|
+
await self.agent_hooks.on_tool_end(
|
|
288
|
+
self.run_context, func_tool, func_tool_args, None
|
|
289
|
+
)
|
|
290
|
+
except Exception as e:
|
|
291
|
+
logger.error(f"Error in on_tool_end hook: {e}", exc_info=True)
|
|
313
292
|
|
|
314
293
|
self.run_context.event.clear_result()
|
|
315
294
|
except Exception as e:
|
astrbot/core/agent/tool.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from deprecated import deprecated
|
|
3
|
-
from typing import Awaitable, Literal, Any, Optional
|
|
3
|
+
from typing import Awaitable, Callable, Literal, Any, Optional
|
|
4
4
|
from .mcp_client import MCPClient
|
|
5
5
|
|
|
6
6
|
|
|
@@ -8,10 +8,10 @@ from .mcp_client import MCPClient
|
|
|
8
8
|
class FunctionTool:
|
|
9
9
|
"""A class representing a function tool that can be used in function calling."""
|
|
10
10
|
|
|
11
|
-
name: str
|
|
11
|
+
name: str
|
|
12
12
|
parameters: dict | None = None
|
|
13
13
|
description: str | None = None
|
|
14
|
-
handler: Awaitable | None = None
|
|
14
|
+
handler: Callable[..., Awaitable[Any]] | None = None
|
|
15
15
|
"""处理函数, 当 origin 为 mcp 时,这个为空"""
|
|
16
16
|
handler_module_path: str | None = None
|
|
17
17
|
"""处理函数的模块路径,当 origin 为 mcp 时,这个为空
|
|
@@ -51,7 +51,7 @@ class ToolSet:
|
|
|
51
51
|
This class provides methods to add, remove, and retrieve tools, as well as
|
|
52
52
|
convert the tools to different API formats (OpenAI, Anthropic, Google GenAI)."""
|
|
53
53
|
|
|
54
|
-
def __init__(self, tools: list[FunctionTool] = None):
|
|
54
|
+
def __init__(self, tools: list[FunctionTool] | None = None):
|
|
55
55
|
self.tools: list[FunctionTool] = tools or []
|
|
56
56
|
|
|
57
57
|
def empty(self) -> bool:
|
|
@@ -79,7 +79,13 @@ class ToolSet:
|
|
|
79
79
|
return None
|
|
80
80
|
|
|
81
81
|
@deprecated(reason="Use add_tool() instead", version="4.0.0")
|
|
82
|
-
def add_func(
|
|
82
|
+
def add_func(
|
|
83
|
+
self,
|
|
84
|
+
name: str,
|
|
85
|
+
func_args: list,
|
|
86
|
+
desc: str,
|
|
87
|
+
handler: Callable[..., Awaitable[Any]],
|
|
88
|
+
):
|
|
83
89
|
"""Add a function tool to the set."""
|
|
84
90
|
params = {
|
|
85
91
|
"type": "object", # hard-coded here
|
|
@@ -104,7 +110,7 @@ class ToolSet:
|
|
|
104
110
|
self.remove_tool(name)
|
|
105
111
|
|
|
106
112
|
@deprecated(reason="Use get_tool() instead", version="4.0.0")
|
|
107
|
-
def get_func(self, name: str) ->
|
|
113
|
+
def get_func(self, name: str) -> FunctionTool | None:
|
|
108
114
|
"""Get all function tools."""
|
|
109
115
|
return self.get_tool(name)
|
|
110
116
|
|
|
@@ -125,7 +131,11 @@ class ToolSet:
|
|
|
125
131
|
},
|
|
126
132
|
}
|
|
127
133
|
|
|
128
|
-
if
|
|
134
|
+
if (
|
|
135
|
+
tool.parameters
|
|
136
|
+
and tool.parameters.get("properties")
|
|
137
|
+
or not omit_empty_parameter_field
|
|
138
|
+
):
|
|
129
139
|
func_def["function"]["parameters"] = tool.parameters
|
|
130
140
|
|
|
131
141
|
result.append(func_def)
|
|
@@ -135,14 +145,14 @@ class ToolSet:
|
|
|
135
145
|
"""Convert tools to Anthropic API format."""
|
|
136
146
|
result = []
|
|
137
147
|
for tool in self.tools:
|
|
148
|
+
input_schema = {"type": "object"}
|
|
149
|
+
if tool.parameters:
|
|
150
|
+
input_schema["properties"] = tool.parameters.get("properties", {})
|
|
151
|
+
input_schema["required"] = tool.parameters.get("required", [])
|
|
138
152
|
tool_def = {
|
|
139
153
|
"name": tool.name,
|
|
140
154
|
"description": tool.description,
|
|
141
|
-
"input_schema":
|
|
142
|
-
"type": "object",
|
|
143
|
-
"properties": tool.parameters.get("properties", {}),
|
|
144
|
-
"required": tool.parameters.get("required", []),
|
|
145
|
-
},
|
|
155
|
+
"input_schema": input_schema,
|
|
146
156
|
}
|
|
147
157
|
result.append(tool_def)
|
|
148
158
|
return result
|
|
@@ -210,14 +220,15 @@ class ToolSet:
|
|
|
210
220
|
|
|
211
221
|
return result
|
|
212
222
|
|
|
213
|
-
tools = [
|
|
214
|
-
|
|
223
|
+
tools = []
|
|
224
|
+
for tool in self.tools:
|
|
225
|
+
d = {
|
|
215
226
|
"name": tool.name,
|
|
216
227
|
"description": tool.description,
|
|
217
|
-
"parameters": convert_schema(tool.parameters),
|
|
218
228
|
}
|
|
219
|
-
|
|
220
|
-
|
|
229
|
+
if tool.parameters:
|
|
230
|
+
d["parameters"] = convert_schema(tool.parameters)
|
|
231
|
+
tools.append(d)
|
|
221
232
|
|
|
222
233
|
declarations = {}
|
|
223
234
|
if tools:
|
astrbot/core/config/default.py
CHANGED
|
@@ -6,7 +6,7 @@ import os
|
|
|
6
6
|
|
|
7
7
|
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
8
8
|
|
|
9
|
-
VERSION = "4.1.
|
|
9
|
+
VERSION = "4.1.5"
|
|
10
10
|
DB_PATH = os.path.join(get_astrbot_data_path(), "data_v4.db")
|
|
11
11
|
|
|
12
12
|
# 默认配置
|
|
@@ -236,6 +236,16 @@ CONFIG_METADATA_2 = {
|
|
|
236
236
|
"discord_guild_id_for_debug": "",
|
|
237
237
|
"discord_activity_name": "",
|
|
238
238
|
},
|
|
239
|
+
"Misskey": {
|
|
240
|
+
"id": "misskey",
|
|
241
|
+
"type": "misskey",
|
|
242
|
+
"enable": False,
|
|
243
|
+
"misskey_instance_url": "https://misskey.example",
|
|
244
|
+
"misskey_token": "",
|
|
245
|
+
"misskey_default_visibility": "public",
|
|
246
|
+
"misskey_local_only": False,
|
|
247
|
+
"misskey_enable_chat": True,
|
|
248
|
+
},
|
|
239
249
|
"Slack": {
|
|
240
250
|
"id": "slack",
|
|
241
251
|
"type": "slack",
|
|
@@ -253,7 +263,7 @@ CONFIG_METADATA_2 = {
|
|
|
253
263
|
"type": "satori",
|
|
254
264
|
"enable": False,
|
|
255
265
|
"satori_api_base_url": "http://localhost:5140/satori/v1",
|
|
256
|
-
"satori_endpoint": "ws://
|
|
266
|
+
"satori_endpoint": "ws://localhost:5140/satori/v1/events",
|
|
257
267
|
"satori_token": "",
|
|
258
268
|
"satori_auto_reconnect": True,
|
|
259
269
|
"satori_heartbeat_interval": 10,
|
|
@@ -262,34 +272,34 @@ CONFIG_METADATA_2 = {
|
|
|
262
272
|
},
|
|
263
273
|
"items": {
|
|
264
274
|
"satori_api_base_url": {
|
|
265
|
-
"description": "Satori API
|
|
275
|
+
"description": "Satori API 终结点",
|
|
266
276
|
"type": "string",
|
|
267
|
-
"hint": "
|
|
277
|
+
"hint": "Satori API 的基础地址。",
|
|
268
278
|
},
|
|
269
279
|
"satori_endpoint": {
|
|
270
|
-
"description": "Satori WebSocket
|
|
280
|
+
"description": "Satori WebSocket 终结点",
|
|
271
281
|
"type": "string",
|
|
272
|
-
"hint": "
|
|
282
|
+
"hint": "Satori 事件的 WebSocket 端点。",
|
|
273
283
|
},
|
|
274
284
|
"satori_token": {
|
|
275
|
-
"description": "Satori
|
|
285
|
+
"description": "Satori 令牌",
|
|
276
286
|
"type": "string",
|
|
277
|
-
"hint": "
|
|
287
|
+
"hint": "用于 Satori API 身份验证的令牌。",
|
|
278
288
|
},
|
|
279
289
|
"satori_auto_reconnect": {
|
|
280
|
-
"description": "
|
|
290
|
+
"description": "启用自动重连",
|
|
281
291
|
"type": "bool",
|
|
282
|
-
"hint": "
|
|
292
|
+
"hint": "断开连接时是否自动重新连接 WebSocket。",
|
|
283
293
|
},
|
|
284
294
|
"satori_heartbeat_interval": {
|
|
285
|
-
"description": "Satori
|
|
295
|
+
"description": "Satori 心跳间隔",
|
|
286
296
|
"type": "int",
|
|
287
|
-
"hint": "
|
|
297
|
+
"hint": "发送心跳消息的间隔(秒)。",
|
|
288
298
|
},
|
|
289
299
|
"satori_reconnect_delay": {
|
|
290
|
-
"description": "Satori
|
|
300
|
+
"description": "Satori 重连延迟",
|
|
291
301
|
"type": "int",
|
|
292
|
-
"hint": "
|
|
302
|
+
"hint": "尝试重新连接前的延迟时间(秒)。",
|
|
293
303
|
},
|
|
294
304
|
"slack_connection_mode": {
|
|
295
305
|
"description": "Slack Connection Mode",
|
|
@@ -337,6 +347,32 @@ CONFIG_METADATA_2 = {
|
|
|
337
347
|
"type": "string",
|
|
338
348
|
"hint": "如果你的网络环境为中国大陆,请在 `其他配置` 处设置代理或更改 api_base。",
|
|
339
349
|
},
|
|
350
|
+
"misskey_instance_url": {
|
|
351
|
+
"description": "Misskey 实例 URL",
|
|
352
|
+
"type": "string",
|
|
353
|
+
"hint": "例如 https://misskey.example,填写 Bot 账号所在的 Misskey 实例地址",
|
|
354
|
+
},
|
|
355
|
+
"misskey_token": {
|
|
356
|
+
"description": "Misskey Access Token",
|
|
357
|
+
"type": "string",
|
|
358
|
+
"hint": "连接服务设置生成的 API 鉴权访问令牌(Access token)",
|
|
359
|
+
},
|
|
360
|
+
"misskey_default_visibility": {
|
|
361
|
+
"description": "默认帖子可见性",
|
|
362
|
+
"type": "string",
|
|
363
|
+
"options": ["public", "home", "followers"],
|
|
364
|
+
"hint": "机器人发帖时的默认可见性设置。public:公开,home:主页时间线,followers:仅关注者。",
|
|
365
|
+
},
|
|
366
|
+
"misskey_local_only": {
|
|
367
|
+
"description": "仅限本站(不参与联合)",
|
|
368
|
+
"type": "bool",
|
|
369
|
+
"hint": "启用后,机器人发出的帖子将仅在本实例可见,不会联合到其他实例",
|
|
370
|
+
},
|
|
371
|
+
"misskey_enable_chat": {
|
|
372
|
+
"description": "启用聊天消息响应",
|
|
373
|
+
"type": "bool",
|
|
374
|
+
"hint": "启用后,机器人将会监听和响应私信聊天消息",
|
|
375
|
+
},
|
|
340
376
|
"telegram_command_register": {
|
|
341
377
|
"description": "Telegram 命令注册",
|
|
342
378
|
"type": "bool",
|
astrbot/core/db/sqlite.py
CHANGED
|
@@ -18,6 +18,7 @@ from astrbot.core.db.po import (
|
|
|
18
18
|
from sqlalchemy import select, update, delete, text
|
|
19
19
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
20
20
|
from sqlalchemy.sql import func
|
|
21
|
+
from sqlalchemy import or_
|
|
21
22
|
|
|
22
23
|
NOT_GIVEN = T.TypeVar("NOT_GIVEN")
|
|
23
24
|
|
|
@@ -153,8 +154,21 @@ class SQLiteDatabase(BaseDatabase):
|
|
|
153
154
|
ConversationV2.platform_id.in_(platform_ids)
|
|
154
155
|
)
|
|
155
156
|
if search_query:
|
|
157
|
+
search_query = search_query.encode("unicode_escape").decode("utf-8")
|
|
156
158
|
base_query = base_query.where(
|
|
157
|
-
|
|
159
|
+
or_(
|
|
160
|
+
ConversationV2.title.ilike(f"%{search_query}%"),
|
|
161
|
+
ConversationV2.content.ilike(f"%{search_query}%"),
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
if "message_types" in kwargs and len(kwargs["message_types"]) > 0:
|
|
165
|
+
for msg_type in kwargs["message_types"]:
|
|
166
|
+
base_query = base_query.where(
|
|
167
|
+
ConversationV2.user_id.ilike(f"%:{msg_type}:%")
|
|
168
|
+
)
|
|
169
|
+
if "platforms" in kwargs and len(kwargs["platforms"]) > 0:
|
|
170
|
+
base_query = base_query.where(
|
|
171
|
+
ConversationV2.platform_id.in_(kwargs["platforms"])
|
|
158
172
|
)
|
|
159
173
|
|
|
160
174
|
# Get total count matching the filters
|
|
@@ -19,7 +19,7 @@ class ContentSafetyCheckStage(Stage):
|
|
|
19
19
|
self.strategy_selector = StrategySelector(config)
|
|
20
20
|
|
|
21
21
|
async def process(
|
|
22
|
-
self, event: AstrMessageEvent, check_text: str = None
|
|
22
|
+
self, event: AstrMessageEvent, check_text: str | None = None
|
|
23
23
|
) -> Union[None, AsyncGenerator[None, None]]:
|
|
24
24
|
"""检查内容安全"""
|
|
25
25
|
text = check_text if check_text else event.get_message_str()
|
|
@@ -13,7 +13,7 @@ class BaiduAipStrategy(ContentSafetyStrategy):
|
|
|
13
13
|
self.secret_key = sk
|
|
14
14
|
self.client = AipContentCensor(self.app_id, self.api_key, self.secret_key)
|
|
15
15
|
|
|
16
|
-
def check(self, content: str):
|
|
16
|
+
def check(self, content: str) -> tuple[bool, str]:
|
|
17
17
|
res = self.client.textCensorUserDefined(content)
|
|
18
18
|
if "conclusionType" not in res:
|
|
19
19
|
return False, ""
|
|
@@ -16,7 +16,7 @@ class KeywordsStrategy(ContentSafetyStrategy):
|
|
|
16
16
|
# json.loads(base64.b64decode(f.read()).decode("utf-8"))["keywords"]
|
|
17
17
|
# )
|
|
18
18
|
|
|
19
|
-
def check(self, content: str) -> bool:
|
|
19
|
+
def check(self, content: str) -> tuple[bool, str]:
|
|
20
20
|
for keyword in self.keywords:
|
|
21
21
|
if re.search(keyword, content):
|
|
22
22
|
return False, "内容安全检查不通过,匹配到敏感词。"
|
|
@@ -10,7 +10,7 @@ from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
|
|
10
10
|
|
|
11
11
|
async def call_handler(
|
|
12
12
|
event: AstrMessageEvent,
|
|
13
|
-
handler: T.Awaitable,
|
|
13
|
+
handler: T.Callable[..., T.Awaitable[T.Any]],
|
|
14
14
|
*args,
|
|
15
15
|
**kwargs,
|
|
16
16
|
) -> T.AsyncGenerator[T.Any, None]:
|
|
@@ -36,6 +36,9 @@ async def call_handler(
|
|
|
36
36
|
except TypeError:
|
|
37
37
|
logger.error("处理函数参数不匹配,请检查 handler 的定义。", exc_info=True)
|
|
38
38
|
|
|
39
|
+
if not ready_to_call:
|
|
40
|
+
return
|
|
41
|
+
|
|
39
42
|
if inspect.isasyncgen(ready_to_call):
|
|
40
43
|
_has_yielded = False
|
|
41
44
|
try:
|
|
@@ -7,6 +7,7 @@ import copy
|
|
|
7
7
|
import json
|
|
8
8
|
import traceback
|
|
9
9
|
from typing import AsyncGenerator, Union
|
|
10
|
+
from astrbot.core.conversation_mgr import Conversation
|
|
10
11
|
from astrbot.core import logger
|
|
11
12
|
from astrbot.core.message.components import Image
|
|
12
13
|
from astrbot.core.message.message_event_result import (
|
|
@@ -133,6 +134,15 @@ class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
|
|
133
134
|
|
|
134
135
|
if agent_runner.done():
|
|
135
136
|
llm_response = agent_runner.get_final_llm_resp()
|
|
137
|
+
|
|
138
|
+
if not llm_response:
|
|
139
|
+
text_content = mcp.types.TextContent(
|
|
140
|
+
type="text",
|
|
141
|
+
text=f"error when deligate task to {tool.agent.name}",
|
|
142
|
+
)
|
|
143
|
+
yield mcp.types.CallToolResult(content=[text_content])
|
|
144
|
+
return
|
|
145
|
+
|
|
136
146
|
logger.debug(
|
|
137
147
|
f"Agent {tool.agent.name} 任务完成, response: {llm_response.completion_text}"
|
|
138
148
|
)
|
|
@@ -148,7 +158,7 @@ class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
|
|
148
158
|
)
|
|
149
159
|
yield mcp.types.CallToolResult(content=[text_content])
|
|
150
160
|
else:
|
|
151
|
-
|
|
161
|
+
text_content = mcp.types.TextContent(
|
|
152
162
|
type="text",
|
|
153
163
|
text=f"error when deligate task to {tool.agent.name}",
|
|
154
164
|
)
|
|
@@ -200,7 +210,11 @@ class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
|
|
200
210
|
):
|
|
201
211
|
if not tool.mcp_client:
|
|
202
212
|
raise ValueError("MCP client is not available for MCP function tools.")
|
|
203
|
-
|
|
213
|
+
|
|
214
|
+
session = tool.mcp_client.session
|
|
215
|
+
if not session:
|
|
216
|
+
raise ValueError("MCP session is not available for MCP function tools.")
|
|
217
|
+
res = await session.call_tool(
|
|
204
218
|
name=tool.name,
|
|
205
219
|
arguments=tool_args,
|
|
206
220
|
)
|
|
@@ -325,7 +339,7 @@ class LLMRequestSubStage(Stage):
|
|
|
325
339
|
|
|
326
340
|
return _ctx.get_using_provider(umo=event.unified_msg_origin)
|
|
327
341
|
|
|
328
|
-
async def _get_session_conv(self, event: AstrMessageEvent):
|
|
342
|
+
async def _get_session_conv(self, event: AstrMessageEvent) -> Conversation:
|
|
329
343
|
umo = event.unified_msg_origin
|
|
330
344
|
conv_mgr = self.conv_manager
|
|
331
345
|
|
|
@@ -337,6 +351,8 @@ class LLMRequestSubStage(Stage):
|
|
|
337
351
|
if not conversation:
|
|
338
352
|
cid = await conv_mgr.new_conversation(umo, event.get_platform_id())
|
|
339
353
|
conversation = await conv_mgr.get_conversation(umo, cid)
|
|
354
|
+
if not conversation:
|
|
355
|
+
raise RuntimeError("无法创建新的对话。")
|
|
340
356
|
return conversation
|
|
341
357
|
|
|
342
358
|
async def process(
|
|
@@ -444,7 +460,10 @@ class LLMRequestSubStage(Stage):
|
|
|
444
460
|
if event.plugins_name is not None and req.func_tool:
|
|
445
461
|
new_tool_set = ToolSet()
|
|
446
462
|
for tool in req.func_tool.tools:
|
|
447
|
-
|
|
463
|
+
mp = tool.handler_module_path
|
|
464
|
+
if not mp:
|
|
465
|
+
continue
|
|
466
|
+
plugin = star_map.get(mp)
|
|
448
467
|
if not plugin:
|
|
449
468
|
continue
|
|
450
469
|
if plugin.name in event.plugins_name or plugin.reserved:
|
|
@@ -34,12 +34,14 @@ class StarRequestSubStage(Stage):
|
|
|
34
34
|
|
|
35
35
|
for handler in activated_handlers:
|
|
36
36
|
params = handlers_parsed_params.get(handler.handler_full_name, {})
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
f"plugin -> {star_map.get(handler.handler_module_path).name} - {handler.handler_name}"
|
|
37
|
+
md = star_map.get(handler.handler_module_path)
|
|
38
|
+
if not md:
|
|
39
|
+
logger.warning(
|
|
40
|
+
f"Cannot find plugin for given handler module path: {handler.handler_module_path}"
|
|
42
41
|
)
|
|
42
|
+
continue
|
|
43
|
+
logger.debug(f"plugin -> {md.name} - {handler.handler_name}")
|
|
44
|
+
try:
|
|
43
45
|
wrapper = call_handler(event, handler.handler, **params)
|
|
44
46
|
async for ret in wrapper:
|
|
45
47
|
yield ret
|
|
@@ -49,7 +51,7 @@ class StarRequestSubStage(Stage):
|
|
|
49
51
|
logger.error(f"Star {handler.handler_full_name} handle error: {e}")
|
|
50
52
|
|
|
51
53
|
if event.is_at_or_wake_command:
|
|
52
|
-
ret = f":(\n\n在调用插件 {
|
|
54
|
+
ret = f":(\n\n在调用插件 {md.name} 的处理函数 {handler.handler_name} 时出现异常:{e}"
|
|
53
55
|
event.set_result(MessageEventResult().message(ret))
|
|
54
56
|
yield
|
|
55
57
|
event.clear_result()
|
astrbot/core/platform/manager.py
CHANGED
|
@@ -90,6 +90,10 @@ class PlatformManager:
|
|
|
90
90
|
from .sources.discord.discord_platform_adapter import (
|
|
91
91
|
DiscordPlatformAdapter, # noqa: F401
|
|
92
92
|
)
|
|
93
|
+
case "misskey":
|
|
94
|
+
from .sources.misskey.misskey_adapter import (
|
|
95
|
+
MisskeyPlatformAdapter, # noqa: F401
|
|
96
|
+
)
|
|
93
97
|
case "slack":
|
|
94
98
|
from .sources.slack.slack_adapter import SlackAdapter # noqa: F401
|
|
95
99
|
case "satori":
|
|
@@ -182,7 +182,8 @@ class AiocqhttpAdapter(Platform):
|
|
|
182
182
|
abm = AstrBotMessage()
|
|
183
183
|
abm.self_id = str(event.self_id)
|
|
184
184
|
abm.sender = MessageMember(
|
|
185
|
-
str(event.sender["user_id"]),
|
|
185
|
+
str(event.sender["user_id"]),
|
|
186
|
+
event.sender.get("card") or event.sender.get("nickname", "N/A"),
|
|
186
187
|
)
|
|
187
188
|
if event["message_type"] == "group":
|
|
188
189
|
abm.type = MessageType.GROUP_MESSAGE
|