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.
Files changed (39) hide show
  1. astrbot/core/agent/agent.py +1 -1
  2. astrbot/core/agent/mcp_client.py +3 -1
  3. astrbot/core/agent/runners/tool_loop_agent_runner.py +6 -27
  4. astrbot/core/agent/tool.py +28 -17
  5. astrbot/core/config/default.py +50 -14
  6. astrbot/core/db/sqlite.py +15 -1
  7. astrbot/core/pipeline/content_safety_check/stage.py +1 -1
  8. astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +1 -1
  9. astrbot/core/pipeline/content_safety_check/strategies/keywords.py +1 -1
  10. astrbot/core/pipeline/context_utils.py +4 -1
  11. astrbot/core/pipeline/process_stage/method/llm_request.py +23 -4
  12. astrbot/core/pipeline/process_stage/method/star_request.py +8 -6
  13. astrbot/core/platform/manager.py +4 -0
  14. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +2 -1
  15. astrbot/core/platform/sources/misskey/misskey_adapter.py +391 -0
  16. astrbot/core/platform/sources/misskey/misskey_api.py +404 -0
  17. astrbot/core/platform/sources/misskey/misskey_event.py +123 -0
  18. astrbot/core/platform/sources/misskey/misskey_utils.py +327 -0
  19. astrbot/core/platform/sources/satori/satori_adapter.py +290 -24
  20. astrbot/core/platform/sources/satori/satori_event.py +9 -0
  21. astrbot/core/platform/sources/telegram/tg_event.py +0 -1
  22. astrbot/core/provider/entities.py +13 -3
  23. astrbot/core/provider/func_tool_manager.py +4 -4
  24. astrbot/core/provider/manager.py +35 -19
  25. astrbot/core/star/context.py +26 -12
  26. astrbot/core/star/filter/command_group.py +4 -4
  27. astrbot/core/star/filter/platform_adapter_type.py +10 -5
  28. astrbot/core/star/register/star.py +3 -1
  29. astrbot/core/star/register/star_handler.py +65 -36
  30. astrbot/core/star/session_plugin_manager.py +3 -0
  31. astrbot/core/star/star_handler.py +4 -4
  32. astrbot/core/star/star_manager.py +10 -4
  33. astrbot/core/star/star_tools.py +6 -2
  34. astrbot/core/star/updator.py +3 -0
  35. {astrbot-4.1.4.dist-info → astrbot-4.1.5.dist-info}/METADATA +6 -7
  36. {astrbot-4.1.4.dist-info → astrbot-4.1.5.dist-info}/RECORD +39 -35
  37. {astrbot-4.1.4.dist-info → astrbot-4.1.5.dist-info}/WHEEL +0 -0
  38. {astrbot-4.1.4.dist-info → astrbot-4.1.5.dist-info}/entry_points.txt +0 -0
  39. {astrbot-4.1.4.dist-info → astrbot-4.1.5.dist-info}/licenses/LICENSE +0 -0
@@ -23,7 +23,7 @@ from .star import star_registry, StarMetadata, star_map
23
23
  from .star_handler import star_handlers_registry, StarHandlerMetadata, EventType
24
24
  from .filter.command import CommandFilter
25
25
  from .filter.regex import RegexFilter
26
- from typing import Awaitable
26
+ from typing import Awaitable, Any, Callable
27
27
  from astrbot.core.conversation_mgr import ConversationManager
28
28
  from astrbot.core.star.filter.platform_adapter_type import (
29
29
  PlatformAdapterType,
@@ -105,7 +105,10 @@ class Context:
105
105
 
106
106
  def get_provider_by_id(self, provider_id: str) -> Provider | None:
107
107
  """通过 ID 获取对应的 LLM Provider(Chat_Completion 类型)。"""
108
- return self.provider_manager.inst_map.get(provider_id)
108
+ prov = self.provider_manager.inst_map.get(provider_id)
109
+ if prov and not isinstance(prov, Provider):
110
+ raise ValueError("返回的 Provider 不是 Provider 类型")
111
+ return prov
109
112
 
110
113
  def get_all_providers(self) -> List[Provider]:
111
114
  """获取所有用于文本生成任务的 LLM Provider(Chat_Completion 类型)。"""
@@ -130,34 +133,43 @@ class Context:
130
133
  Args:
131
134
  umo(str): unified_message_origin 值,如果传入并且用户启用了提供商会话隔离,则使用该会话偏好的提供商。
132
135
  """
133
- return self.provider_manager.get_using_provider(
136
+ prov = self.provider_manager.get_using_provider(
134
137
  provider_type=ProviderType.CHAT_COMPLETION,
135
138
  umo=umo,
136
139
  )
140
+ if prov and not isinstance(prov, Provider):
141
+ raise ValueError("返回的 Provider 不是 Provider 类型")
142
+ return prov
137
143
 
138
- def get_using_tts_provider(self, umo: str | None = None) -> TTSProvider:
144
+ def get_using_tts_provider(self, umo: str | None = None) -> TTSProvider | None:
139
145
  """
140
146
  获取当前使用的用于 TTS 任务的 Provider。
141
147
 
142
148
  Args:
143
149
  umo(str): unified_message_origin 值,如果传入,则使用该会话偏好的提供商。
144
150
  """
145
- return self.provider_manager.get_using_provider(
151
+ prov = self.provider_manager.get_using_provider(
146
152
  provider_type=ProviderType.TEXT_TO_SPEECH,
147
153
  umo=umo,
148
154
  )
155
+ if prov and not isinstance(prov, TTSProvider):
156
+ raise ValueError("返回的 Provider 不是 TTSProvider 类型")
157
+ return prov
149
158
 
150
- def get_using_stt_provider(self, umo: str | None = None) -> STTProvider:
159
+ def get_using_stt_provider(self, umo: str | None = None) -> STTProvider | None:
151
160
  """
152
161
  获取当前使用的用于 STT 任务的 Provider。
153
162
 
154
163
  Args:
155
164
  umo(str): unified_message_origin 值,如果传入,则使用该会话偏好的提供商。
156
165
  """
157
- return self.provider_manager.get_using_provider(
166
+ prov = self.provider_manager.get_using_provider(
158
167
  provider_type=ProviderType.SPEECH_TO_TEXT,
159
168
  umo=umo,
160
169
  )
170
+ if prov and not isinstance(prov, STTProvider):
171
+ raise ValueError("返回的 Provider 不是 STTProvider 类型")
172
+ return prov
161
173
 
162
174
  def get_config(self, umo: str | None = None) -> AstrBotConfig:
163
175
  """获取 AstrBot 的配置。"""
@@ -245,7 +257,11 @@ class Context:
245
257
  """
246
258
 
247
259
  def register_llm_tool(
248
- self, name: str, func_args: list, desc: str, func_obj: Awaitable
260
+ self,
261
+ name: str,
262
+ func_args: list,
263
+ desc: str,
264
+ func_obj: Callable[..., Awaitable[Any]],
249
265
  ) -> None:
250
266
  """
251
267
  为函数调用(function-calling / tools-use)添加工具。
@@ -267,9 +283,7 @@ class Context:
267
283
  desc=desc,
268
284
  )
269
285
  star_handlers_registry.append(md)
270
- self.provider_manager.llm_tools.add_func(
271
- name, func_args, desc, func_obj, func_obj
272
- )
286
+ self.provider_manager.llm_tools.add_func(name, func_args, desc, func_obj)
273
287
 
274
288
  def unregister_llm_tool(self, name: str) -> None:
275
289
  """删除一个函数调用工具。如果再要启用,需要重新注册。"""
@@ -281,7 +295,7 @@ class Context:
281
295
  command_name: str,
282
296
  desc: str,
283
297
  priority: int,
284
- awaitable: Awaitable,
298
+ awaitable: Callable[..., Awaitable[Any]],
285
299
  use_regex=False,
286
300
  ignore_prefix=False,
287
301
  ):
@@ -13,8 +13,8 @@ class CommandGroupFilter(HandlerFilter):
13
13
  def __init__(
14
14
  self,
15
15
  group_name: str,
16
- alias: set = None,
17
- parent_group: CommandGroupFilter = None,
16
+ alias: set | None = None,
17
+ parent_group: CommandGroupFilter | None = None,
18
18
  ):
19
19
  self.group_name = group_name
20
20
  self.alias = alias if alias else set()
@@ -54,8 +54,8 @@ class CommandGroupFilter(HandlerFilter):
54
54
  self,
55
55
  sub_command_filters: List[Union[CommandFilter, CommandGroupFilter]],
56
56
  prefix: str = "",
57
- event: AstrMessageEvent = None,
58
- cfg: AstrBotConfig = None,
57
+ event: AstrMessageEvent | None = None,
58
+ cfg: AstrBotConfig | None = None,
59
59
  ) -> str:
60
60
  result = ""
61
61
  for sub_filter in sub_command_filters:
@@ -2,7 +2,6 @@ import enum
2
2
  from . import HandlerFilter
3
3
  from astrbot.core.platform.astr_message_event import AstrMessageEvent
4
4
  from astrbot.core.config import AstrBotConfig
5
- from typing import Union
6
5
 
7
6
 
8
7
  class PlatformAdapterType(enum.Flag):
@@ -19,6 +18,7 @@ class PlatformAdapterType(enum.Flag):
19
18
  VOCECHAT = enum.auto()
20
19
  WEIXIN_OFFICIAL_ACCOUNT = enum.auto()
21
20
  SATORI = enum.auto()
21
+ MISSKEY = enum.auto()
22
22
  ALL = (
23
23
  AIOCQHTTP
24
24
  | QQOFFICIAL
@@ -33,6 +33,7 @@ class PlatformAdapterType(enum.Flag):
33
33
  | VOCECHAT
34
34
  | WEIXIN_OFFICIAL_ACCOUNT
35
35
  | SATORI
36
+ | MISSKEY
36
37
  )
37
38
 
38
39
 
@@ -50,15 +51,19 @@ ADAPTER_NAME_2_TYPE = {
50
51
  "vocechat": PlatformAdapterType.VOCECHAT,
51
52
  "weixin_official_account": PlatformAdapterType.WEIXIN_OFFICIAL_ACCOUNT,
52
53
  "satori": PlatformAdapterType.SATORI,
54
+ "misskey": PlatformAdapterType.MISSKEY,
53
55
  }
54
56
 
55
57
 
56
58
  class PlatformAdapterTypeFilter(HandlerFilter):
57
- def __init__(self, platform_adapter_type_or_str: Union[PlatformAdapterType, str]):
58
- self.type_or_str = platform_adapter_type_or_str
59
+ def __init__(self, platform_adapter_type_or_str: PlatformAdapterType | str):
60
+ if isinstance(platform_adapter_type_or_str, str):
61
+ self.platform_type = ADAPTER_NAME_2_TYPE.get(platform_adapter_type_or_str)
62
+ else:
63
+ self.platform_type = platform_adapter_type_or_str
59
64
 
60
65
  def filter(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool:
61
66
  adapter_name = event.get_platform_name()
62
- if adapter_name in ADAPTER_NAME_2_TYPE:
63
- return ADAPTER_NAME_2_TYPE[adapter_name] & self.type_or_str
67
+ if adapter_name in ADAPTER_NAME_2_TYPE and self.platform_type is not None:
68
+ return bool(ADAPTER_NAME_2_TYPE[adapter_name] & self.platform_type)
64
69
  return False
@@ -5,7 +5,9 @@ from astrbot.core.star import StarMetadata, star_map
5
5
  _warned_register_star = False
6
6
 
7
7
 
8
- def register_star(name: str, author: str, desc: str, version: str, repo: str = None):
8
+ def register_star(
9
+ name: str, author: str, desc: str, version: str, repo: str | None = None
10
+ ):
9
11
  """注册一个插件(Star)。
10
12
 
11
13
  [DEPRECATED] 该装饰器已废弃,将在未来版本中移除。
@@ -12,7 +12,7 @@ from ..filter.platform_adapter_type import (
12
12
  from ..filter.permission import PermissionTypeFilter, PermissionType
13
13
  from ..filter.custom_filter import CustomFilterAnd, CustomFilterOr
14
14
  from ..filter.regex import RegexFilter
15
- from typing import Awaitable
15
+ from typing import Awaitable, Any, Callable
16
16
  from astrbot.core.provider.func_tool_manager import SUPPORTED_TYPES
17
17
  from astrbot.core.provider.register import llm_tools
18
18
  from astrbot.core.agent.agent import Agent
@@ -20,15 +20,19 @@ from astrbot.core.agent.tool import FunctionTool
20
20
  from astrbot.core.agent.handoff import HandoffTool
21
21
  from astrbot.core.agent.hooks import BaseAgentRunHooks
22
22
  from astrbot.core.astr_agent_context import AstrAgentContext
23
+ from astrbot.core import logger
23
24
 
24
25
 
25
- def get_handler_full_name(awaitable: Awaitable) -> str:
26
+ def get_handler_full_name(awaitable: Callable[..., Awaitable[Any]]) -> str:
26
27
  """获取 Handler 的全名"""
27
28
  return f"{awaitable.__module__}_{awaitable.__name__}"
28
29
 
29
30
 
30
31
  def get_handler_or_create(
31
- handler: Awaitable, event_type: EventType, dont_add=False, **kwargs
32
+ handler: Callable[..., Awaitable[Any]],
33
+ event_type: EventType,
34
+ dont_add=False,
35
+ **kwargs,
32
36
  ) -> StarHandlerMetadata:
33
37
  """获取 Handler 或者创建一个新的 Handler"""
34
38
  handler_full_name = get_handler_full_name(handler)
@@ -59,22 +63,35 @@ def get_handler_or_create(
59
63
 
60
64
 
61
65
  def register_command(
62
- command_name: str = None, sub_command: str = None, alias: set = None, **kwargs
66
+ command_name: str | None = None,
67
+ sub_command: str | None = None,
68
+ alias: set | None = None,
69
+ **kwargs,
63
70
  ):
64
71
  """注册一个 Command."""
65
72
  new_command = None
66
73
  add_to_event_filters = False
67
74
  if isinstance(command_name, RegisteringCommandable):
68
75
  # 子指令
69
- parent_command_names = command_name.parent_group.get_complete_command_names()
70
- new_command = CommandFilter(
71
- sub_command, alias, None, parent_command_names=parent_command_names
72
- )
73
- command_name.parent_group.add_sub_command_filter(new_command)
76
+ if sub_command is not None:
77
+ parent_command_names = (
78
+ command_name.parent_group.get_complete_command_names()
79
+ )
80
+ new_command = CommandFilter(
81
+ sub_command, alias, None, parent_command_names=parent_command_names
82
+ )
83
+ command_name.parent_group.add_sub_command_filter(new_command)
84
+ else:
85
+ logger.warning(
86
+ f"注册指令{command_name} 的子指令时未提供 sub_command 参数。"
87
+ )
74
88
  else:
75
89
  # 裸指令
76
- new_command = CommandFilter(command_name, alias, None)
77
- add_to_event_filters = True
90
+ if command_name is None:
91
+ logger.warning("注册裸指令时未提供 command_name 参数。")
92
+ else:
93
+ new_command = CommandFilter(command_name, alias, None)
94
+ add_to_event_filters = True
78
95
 
79
96
  def decorator(awaitable):
80
97
  if not add_to_event_filters:
@@ -84,8 +101,9 @@ def register_command(
84
101
  handler_md = get_handler_or_create(
85
102
  awaitable, EventType.AdapterMessageEvent, **kwargs
86
103
  )
87
- new_command.init_handler_md(handler_md)
88
- handler_md.event_filters.append(new_command)
104
+ if new_command:
105
+ new_command.init_handler_md(handler_md)
106
+ handler_md.event_filters.append(new_command)
89
107
  return awaitable
90
108
 
91
109
  return decorator
@@ -163,26 +181,38 @@ def register_custom_filter(custom_type_filter, *args, **kwargs):
163
181
 
164
182
 
165
183
  def register_command_group(
166
- command_group_name: str = None, sub_command: str = None, alias: set = None, **kwargs
184
+ command_group_name: str | None = None,
185
+ sub_command: str | None = None,
186
+ alias: set | None = None,
187
+ **kwargs,
167
188
  ):
168
189
  """注册一个 CommandGroup"""
169
190
  new_group = None
170
191
  if isinstance(command_group_name, RegisteringCommandable):
171
192
  # 子指令组
172
- new_group = CommandGroupFilter(
173
- sub_command, alias, parent_group=command_group_name.parent_group
174
- )
175
- command_group_name.parent_group.add_sub_command_filter(new_group)
193
+ if sub_command is None:
194
+ logger.warning(f"{command_group_name} 指令组的子指令组 sub_command 未指定")
195
+ else:
196
+ new_group = CommandGroupFilter(
197
+ sub_command, alias, parent_group=command_group_name.parent_group
198
+ )
199
+ command_group_name.parent_group.add_sub_command_filter(new_group)
176
200
  else:
177
201
  # 根指令组
178
- new_group = CommandGroupFilter(command_group_name, alias)
202
+ if command_group_name is None:
203
+ logger.warning("根指令组的名称未指定")
204
+ else:
205
+ new_group = CommandGroupFilter(command_group_name, alias)
179
206
 
180
207
  def decorator(obj):
181
208
  # 根指令组
182
- handler_md = get_handler_or_create(obj, EventType.AdapterMessageEvent, **kwargs)
183
- handler_md.event_filters.append(new_group)
209
+ if new_group:
210
+ handler_md = get_handler_or_create(
211
+ obj, EventType.AdapterMessageEvent, **kwargs
212
+ )
213
+ handler_md.event_filters.append(new_group)
184
214
 
185
- return RegisteringCommandable(new_group)
215
+ return RegisteringCommandable(new_group)
186
216
 
187
217
  return decorator
188
218
 
@@ -323,7 +353,7 @@ def register_on_llm_response(**kwargs):
323
353
  return decorator
324
354
 
325
355
 
326
- def register_llm_tool(name: str = None, **kwargs):
356
+ def register_llm_tool(name: str | None = None, **kwargs):
327
357
  """为函数调用(function-calling / tools-use)添加工具。
328
358
 
329
359
  请务必按照以下格式编写一个工具(包括函数注释,AstrBot 会尝试解析该函数注释)
@@ -361,9 +391,10 @@ def register_llm_tool(name: str = None, **kwargs):
361
391
  if kwargs.get("registering_agent"):
362
392
  registering_agent = kwargs["registering_agent"]
363
393
 
364
- def decorator(awaitable: Awaitable):
394
+ def decorator(awaitable: Callable[..., Awaitable[Any]]):
365
395
  llm_tool_name = name_ if name_ else awaitable.__name__
366
- docstring = docstring_parser.parse(awaitable.__doc__)
396
+ func_doc = awaitable.__doc__ or ""
397
+ docstring = docstring_parser.parse(func_doc)
367
398
  args = []
368
399
  for arg in docstring.params:
369
400
  if arg.type_name not in SUPPORTED_TYPES:
@@ -379,20 +410,18 @@ def register_llm_tool(name: str = None, **kwargs):
379
410
  )
380
411
  # print(llm_tool_name, registering_agent)
381
412
  if not registering_agent:
413
+ doc_desc = docstring.description.strip() if docstring.description else ""
382
414
  md = get_handler_or_create(awaitable, EventType.OnCallingFuncToolEvent)
383
- llm_tools.add_func(
384
- llm_tool_name, args, docstring.description.strip(), md.handler
385
- )
415
+ llm_tools.add_func(llm_tool_name, args, doc_desc, md.handler)
386
416
  else:
387
417
  assert isinstance(registering_agent, RegisteringAgent)
388
418
  # print(f"Registering tool {llm_tool_name} for agent", registering_agent._agent.name)
389
419
  if registering_agent._agent.tools is None:
390
420
  registering_agent._agent.tools = []
391
- registering_agent._agent.tools.append(
392
- llm_tools.spec_to_func(
393
- llm_tool_name, args, docstring.description.strip(), awaitable
394
- )
395
- )
421
+
422
+ desc = docstring.description.strip() if docstring.description else ""
423
+ tool = llm_tools.spec_to_func(llm_tool_name, args, desc, awaitable)
424
+ registering_agent._agent.tools.append(tool)
396
425
 
397
426
  return awaitable
398
427
 
@@ -413,8 +442,8 @@ class RegisteringAgent:
413
442
  def register_agent(
414
443
  name: str,
415
444
  instruction: str,
416
- tools: list[str | FunctionTool] = None,
417
- run_hooks: BaseAgentRunHooks[AstrAgentContext] = None,
445
+ tools: list[str | FunctionTool] | None = None,
446
+ run_hooks: BaseAgentRunHooks[AstrAgentContext] | None = None,
418
447
  ):
419
448
  """注册一个 Agent
420
449
 
@@ -426,7 +455,7 @@ def register_agent(
426
455
  """
427
456
  tools_ = tools or []
428
457
 
429
- def decorator(awaitable: Awaitable):
458
+ def decorator(awaitable: Callable[..., Awaitable[Any]]):
430
459
  AstrAgent = Agent[AstrAgentContext]
431
460
  agent = AstrAgent(
432
461
  name=name,
@@ -140,6 +140,9 @@ class SessionPluginManager:
140
140
  filtered_handlers.append(handler)
141
141
  continue
142
142
 
143
+ if plugin.name is None:
144
+ continue
145
+
143
146
  # 检查插件是否在当前会话中启用
144
147
  if SessionPluginManager.is_plugin_enabled_for_session(
145
148
  session_id, plugin.name
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
  import enum
3
3
  from dataclasses import dataclass, field
4
- from typing import Awaitable, List, Dict, TypeVar, Generic
4
+ from typing import Callable, Awaitable, Any, List, Dict, TypeVar, Generic
5
5
  from .filter import HandlerFilter
6
6
  from .star import star_map
7
7
 
@@ -60,7 +60,7 @@ class StarHandlerRegistry(Generic[T]):
60
60
  handlers.append(handler)
61
61
  return handlers
62
62
 
63
- def get_handler_by_full_name(self, full_name: str) -> StarHandlerMetadata:
63
+ def get_handler_by_full_name(self, full_name: str) -> StarHandlerMetadata | None:
64
64
  return self.star_handlers_map.get(full_name, None)
65
65
 
66
66
  def get_handlers_by_module_name(
@@ -87,7 +87,7 @@ class StarHandlerRegistry(Generic[T]):
87
87
  return len(self._handlers)
88
88
 
89
89
 
90
- star_handlers_registry = StarHandlerRegistry()
90
+ star_handlers_registry = StarHandlerRegistry() # type: ignore
91
91
 
92
92
 
93
93
  class EventType(enum.Enum):
@@ -123,7 +123,7 @@ class StarHandlerMetadata:
123
123
  handler_module_path: str
124
124
  """Handler 所在的模块路径。"""
125
125
 
126
- handler: Awaitable
126
+ handler: Callable[..., Awaitable[Any]]
127
127
  """Handler 的函数对象,应当是一个异步函数"""
128
128
 
129
129
  event_filters: List[HandlerFilter]
@@ -43,7 +43,7 @@ class PluginManager:
43
43
  self.updator = PluginUpdator()
44
44
 
45
45
  self.context = context
46
- self.context._star_manager = self
46
+ self.context._star_manager = self # type: ignore
47
47
 
48
48
  self.config = config
49
49
  self.plugin_store_path = get_astrbot_plugin_path()
@@ -478,9 +478,10 @@ class PluginManager:
478
478
  if isinstance(func_tool, HandoffTool):
479
479
  need_apply = []
480
480
  sub_tools = func_tool.agent.tools
481
- for sub_tool in sub_tools:
482
- if isinstance(sub_tool, FunctionTool):
483
- need_apply.append(sub_tool)
481
+ if sub_tools:
482
+ for sub_tool in sub_tools:
483
+ if isinstance(sub_tool, FunctionTool):
484
+ need_apply.append(sub_tool)
484
485
  else:
485
486
  need_apply = [func_tool]
486
487
 
@@ -686,6 +687,9 @@ class PluginManager:
686
687
  )
687
688
 
688
689
  # 从 star_registry 和 star_map 中删除
690
+ if plugin.module_path is None or root_dir_name is None:
691
+ raise Exception(f"插件 {plugin_name} 数据不完整,无法卸载。")
692
+
689
693
  await self._unbind_plugin(plugin_name, plugin.module_path)
690
694
 
691
695
  try:
@@ -800,6 +804,8 @@ class PluginManager:
800
804
 
801
805
  async def turn_on_plugin(self, plugin_name: str):
802
806
  plugin = self.context.get_registered_star(plugin_name)
807
+ if plugin is None:
808
+ raise Exception(f"插件 {plugin_name} 不存在。")
803
809
  inactivated_plugins: list = await sp.global_get("inactivated_plugins", [])
804
810
  inactivated_llm_tools: list = await sp.global_get("inactivated_llm_tools", [])
805
811
  if plugin.module_path in inactivated_plugins:
@@ -22,7 +22,7 @@ import inspect
22
22
  import os
23
23
  import uuid
24
24
  from pathlib import Path
25
- from typing import Union, Awaitable, List, Optional, ClassVar
25
+ from typing import Union, Awaitable, Callable, Any, List, Optional, ClassVar
26
26
  from astrbot.core.message.components import BaseMessageComponent
27
27
  from astrbot.core.message.message_event_result import MessageChain
28
28
  from astrbot.api.platform import MessageMember, AstrBotMessage, MessageType
@@ -221,7 +221,11 @@ class StarTools:
221
221
 
222
222
  @classmethod
223
223
  def register_llm_tool(
224
- cls, name: str, func_args: list, desc: str, func_obj: Awaitable
224
+ cls,
225
+ name: str,
226
+ func_args: list,
227
+ desc: str,
228
+ func_obj: Callable[..., Awaitable[Any]],
225
229
  ) -> None:
226
230
  """
227
231
  为函数调用(function-calling/tools-use)添加工具
@@ -32,6 +32,9 @@ class PluginUpdator(RepoZipUpdator):
32
32
  if not repo_url:
33
33
  raise Exception(f"插件 {plugin.name} 没有指定仓库地址。")
34
34
 
35
+ if not plugin.root_dir_name:
36
+ raise Exception(f"插件 {plugin.name} 的根目录名未指定。")
37
+
35
38
  plugin_path = os.path.join(self.plugin_store_path, plugin.root_dir_name)
36
39
 
37
40
  logger.info(f"正在更新插件,路径: {plugin_path},仓库地址: {repo_url}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AstrBot
3
- Version: 4.1.4
3
+ Version: 4.1.5
4
4
  Summary: 易上手的多平台 LLM 聊天机器人及开发框架
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.10
@@ -10,9 +10,11 @@ Requires-Dist: aiohttp>=3.11.18
10
10
  Requires-Dist: aiosqlite>=0.21.0
11
11
  Requires-Dist: anthropic>=0.51.0
12
12
  Requires-Dist: apscheduler>=3.11.0
13
+ Requires-Dist: audioop-lts; python_full_version >= '3.13'
13
14
  Requires-Dist: beautifulsoup4>=4.13.4
14
15
  Requires-Dist: certifi>=2025.4.26
15
16
  Requires-Dist: chardet~=5.1.0
17
+ Requires-Dist: click>=8.2.1
16
18
  Requires-Dist: colorlog>=6.9.0
17
19
  Requires-Dist: cryptography>=44.0.3
18
20
  Requires-Dist: dashscope>=1.23.2
@@ -162,7 +164,6 @@ uv run main.py
162
164
 
163
165
  <a href="https://discord.gg/hAVk6tgV36"><img alt="Discord_community" src="https://img.shields.io/badge/Discord-AstrBot-purple?style=for-the-badge&color=76bad9"></a>
164
166
 
165
-
166
167
  ## ⚡ 消息平台支持情况
167
168
 
168
169
  | 平台 | 支持性 |
@@ -179,6 +180,8 @@ uv run main.py
179
180
  | Discord | ✔ |
180
181
  | [KOOK](https://github.com/wuyan1003/astrbot_plugin_kook_adapter) | ✔ |
181
182
  | [VoceChat](https://github.com/HikariFroya/astrbot_plugin_vocechat) | ✔ |
183
+ | Satori | ✔ |
184
+ | Misskey | ✔ |
182
185
 
183
186
  ## ⚡ 提供商支持情况
184
187
 
@@ -224,7 +227,6 @@ pip install pre-commit
224
227
  pre-commit install
225
228
  ```
226
229
 
227
-
228
230
  ## ❤️ Special Thanks
229
231
 
230
232
  特别感谢所有 Contributors 和插件开发者对 AstrBot 的贡献 ❤️
@@ -252,14 +254,11 @@ pre-commit install
252
254
  > 如果本项目对您的生活 / 工作产生了帮助,或者您关注本项目的未来发展,请给项目 Star,这是我维护这个开源项目的动力 <3
253
255
 
254
256
  <div align="center">
255
-
257
+
256
258
  [![Star History Chart](https://api.star-history.com/svg?repos=soulter/astrbot&type=Date)](https://star-history.com/#soulter/astrbot&Date)
257
259
 
258
260
  </div>
259
261
 
260
-
261
262
  </details>
262
263
 
263
-
264
264
  _私は、高性能ですから!_
265
-