AstrBot 4.0.0b5__py3-none-any.whl → 4.1.0__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/api/event/filter/__init__.py +2 -0
- astrbot/core/config/default.py +73 -3
- astrbot/core/initial_loader.py +4 -1
- astrbot/core/message/components.py +59 -50
- astrbot/core/pipeline/result_decorate/stage.py +5 -1
- astrbot/core/platform/manager.py +25 -3
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +11 -4
- astrbot/core/platform/sources/satori/satori_adapter.py +482 -0
- astrbot/core/platform/sources/satori/satori_event.py +221 -0
- astrbot/core/platform/sources/telegram/tg_adapter.py +0 -1
- astrbot/core/provider/sources/openai_source.py +12 -5
- astrbot/core/provider/sources/vllm_rerank_source.py +6 -0
- astrbot/core/star/__init__.py +7 -5
- astrbot/core/star/filter/command.py +9 -3
- astrbot/core/star/filter/platform_adapter_type.py +3 -0
- astrbot/core/star/register/__init__.py +2 -0
- astrbot/core/star/register/star_handler.py +18 -4
- astrbot/core/star/star_handler.py +9 -1
- astrbot/core/star/star_tools.py +116 -21
- astrbot/core/utils/t2i/network_strategy.py +11 -18
- astrbot/core/utils/t2i/renderer.py +8 -2
- astrbot/core/utils/t2i/template/astrbot_powershell.html +184 -0
- astrbot/core/utils/t2i/template_manager.py +112 -0
- astrbot/dashboard/routes/chat.py +6 -1
- astrbot/dashboard/routes/config.py +10 -49
- astrbot/dashboard/routes/route.py +19 -2
- astrbot/dashboard/routes/t2i.py +230 -0
- astrbot/dashboard/server.py +13 -4
- {astrbot-4.0.0b5.dist-info → astrbot-4.1.0.dist-info}/METADATA +39 -52
- {astrbot-4.0.0b5.dist-info → astrbot-4.1.0.dist-info}/RECORD +33 -28
- {astrbot-4.0.0b5.dist-info → astrbot-4.1.0.dist-info}/WHEEL +0 -0
- {astrbot-4.0.0b5.dist-info → astrbot-4.1.0.dist-info}/entry_points.txt +0 -0
- {astrbot-4.0.0b5.dist-info → astrbot-4.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
from astrbot.api import logger
|
|
3
|
+
from astrbot.api.event import AstrMessageEvent, MessageChain
|
|
4
|
+
from astrbot.api.platform import AstrBotMessage, PlatformMetadata
|
|
5
|
+
from astrbot.api.message_components import Plain, Image, At, File, Record
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from .satori_adapter import SatoriPlatformAdapter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SatoriPlatformEvent(AstrMessageEvent):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
message_str: str,
|
|
15
|
+
message_obj: AstrBotMessage,
|
|
16
|
+
platform_meta: PlatformMetadata,
|
|
17
|
+
session_id: str,
|
|
18
|
+
adapter: "SatoriPlatformAdapter",
|
|
19
|
+
):
|
|
20
|
+
super().__init__(message_str, message_obj, platform_meta, session_id)
|
|
21
|
+
self.adapter = adapter
|
|
22
|
+
self.platform = None
|
|
23
|
+
self.user_id = None
|
|
24
|
+
if (
|
|
25
|
+
hasattr(message_obj, "raw_message")
|
|
26
|
+
and message_obj.raw_message
|
|
27
|
+
and isinstance(message_obj.raw_message, dict)
|
|
28
|
+
):
|
|
29
|
+
login = message_obj.raw_message.get("login", {})
|
|
30
|
+
self.platform = login.get("platform")
|
|
31
|
+
user = login.get("user", {})
|
|
32
|
+
self.user_id = user.get("id") if user else None
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
async def send_with_adapter(
|
|
36
|
+
cls, adapter: "SatoriPlatformAdapter", message: MessageChain, session_id: str
|
|
37
|
+
):
|
|
38
|
+
try:
|
|
39
|
+
content_parts = []
|
|
40
|
+
|
|
41
|
+
for component in message.chain:
|
|
42
|
+
if isinstance(component, Plain):
|
|
43
|
+
text = (
|
|
44
|
+
component.text.replace("&", "&")
|
|
45
|
+
.replace("<", "<")
|
|
46
|
+
.replace(">", ">")
|
|
47
|
+
)
|
|
48
|
+
content_parts.append(text)
|
|
49
|
+
|
|
50
|
+
elif isinstance(component, At):
|
|
51
|
+
if component.qq:
|
|
52
|
+
content_parts.append(f'<at id="{component.qq}"/>')
|
|
53
|
+
elif component.name:
|
|
54
|
+
content_parts.append(f'<at name="{component.name}"/>')
|
|
55
|
+
|
|
56
|
+
elif isinstance(component, Image):
|
|
57
|
+
try:
|
|
58
|
+
image_base64 = await component.convert_to_base64()
|
|
59
|
+
if image_base64:
|
|
60
|
+
content_parts.append(
|
|
61
|
+
f'<img src="data:image/jpeg;base64,{image_base64}"/>'
|
|
62
|
+
)
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.error(f"图片转换为base64失败: {e}")
|
|
65
|
+
|
|
66
|
+
elif isinstance(component, File):
|
|
67
|
+
content_parts.append(
|
|
68
|
+
f'<file src="{component.file}" name="{component.name or "文件"}"/>'
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
elif isinstance(component, Record):
|
|
72
|
+
try:
|
|
73
|
+
record_base64 = await component.convert_to_base64()
|
|
74
|
+
if record_base64:
|
|
75
|
+
content_parts.append(
|
|
76
|
+
f'<audio src="data:audio/wav;base64,{record_base64}"/>'
|
|
77
|
+
)
|
|
78
|
+
except Exception as e:
|
|
79
|
+
logger.error(f"语音转换为base64失败: {e}")
|
|
80
|
+
|
|
81
|
+
content = "".join(content_parts)
|
|
82
|
+
channel_id = session_id
|
|
83
|
+
data = {"channel_id": channel_id, "content": content}
|
|
84
|
+
|
|
85
|
+
platform = None
|
|
86
|
+
user_id = None
|
|
87
|
+
|
|
88
|
+
if hasattr(adapter, "logins") and adapter.logins:
|
|
89
|
+
current_login = adapter.logins[0]
|
|
90
|
+
platform = current_login.get("platform", "")
|
|
91
|
+
user = current_login.get("user", {})
|
|
92
|
+
user_id = user.get("id", "") if user else ""
|
|
93
|
+
|
|
94
|
+
result = await adapter.send_http_request(
|
|
95
|
+
"POST", "/message.create", data, platform, user_id
|
|
96
|
+
)
|
|
97
|
+
if result:
|
|
98
|
+
return result
|
|
99
|
+
else:
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
except Exception as e:
|
|
103
|
+
logger.error(f"Satori 消息发送异常: {e}")
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
async def send(self, message: MessageChain):
|
|
107
|
+
platform = getattr(self, "platform", None)
|
|
108
|
+
user_id = getattr(self, "user_id", None)
|
|
109
|
+
|
|
110
|
+
if not platform or not user_id:
|
|
111
|
+
if hasattr(self.adapter, "logins") and self.adapter.logins:
|
|
112
|
+
current_login = self.adapter.logins[0]
|
|
113
|
+
platform = current_login.get("platform", "")
|
|
114
|
+
user = current_login.get("user", {})
|
|
115
|
+
user_id = user.get("id", "") if user else ""
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
content_parts = []
|
|
119
|
+
|
|
120
|
+
for component in message.chain:
|
|
121
|
+
if isinstance(component, Plain):
|
|
122
|
+
text = (
|
|
123
|
+
component.text.replace("&", "&")
|
|
124
|
+
.replace("<", "<")
|
|
125
|
+
.replace(">", ">")
|
|
126
|
+
)
|
|
127
|
+
content_parts.append(text)
|
|
128
|
+
|
|
129
|
+
elif isinstance(component, At):
|
|
130
|
+
if component.qq:
|
|
131
|
+
content_parts.append(f'<at id="{component.qq}"/>')
|
|
132
|
+
elif component.name:
|
|
133
|
+
content_parts.append(f'<at name="{component.name}"/>')
|
|
134
|
+
|
|
135
|
+
elif isinstance(component, Image):
|
|
136
|
+
try:
|
|
137
|
+
image_base64 = await component.convert_to_base64()
|
|
138
|
+
if image_base64:
|
|
139
|
+
content_parts.append(
|
|
140
|
+
f'<img src="data:image/jpeg;base64,{image_base64}"/>'
|
|
141
|
+
)
|
|
142
|
+
except Exception as e:
|
|
143
|
+
logger.error(f"图片转换为base64失败: {e}")
|
|
144
|
+
|
|
145
|
+
elif isinstance(component, File):
|
|
146
|
+
content_parts.append(
|
|
147
|
+
f'<file src="{component.file}" name="{component.name or "文件"}"/>'
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
elif isinstance(component, Record):
|
|
151
|
+
try:
|
|
152
|
+
record_base64 = await component.convert_to_base64()
|
|
153
|
+
if record_base64:
|
|
154
|
+
content_parts.append(
|
|
155
|
+
f'<audio src="data:audio/wav;base64,{record_base64}"/>'
|
|
156
|
+
)
|
|
157
|
+
except Exception as e:
|
|
158
|
+
logger.error(f"语音转换为base64失败: {e}")
|
|
159
|
+
|
|
160
|
+
content = "".join(content_parts)
|
|
161
|
+
channel_id = self.session_id
|
|
162
|
+
data = {"channel_id": channel_id, "content": content}
|
|
163
|
+
|
|
164
|
+
result = await self.adapter.send_http_request(
|
|
165
|
+
"POST", "/message.create", data, platform, user_id
|
|
166
|
+
)
|
|
167
|
+
if not result:
|
|
168
|
+
logger.error("Satori 消息发送失败")
|
|
169
|
+
except Exception as e:
|
|
170
|
+
logger.error(f"Satori 消息发送异常: {e}")
|
|
171
|
+
|
|
172
|
+
await super().send(message)
|
|
173
|
+
|
|
174
|
+
async def send_streaming(self, generator, use_fallback: bool = False):
|
|
175
|
+
try:
|
|
176
|
+
content_parts = []
|
|
177
|
+
|
|
178
|
+
async for chain in generator:
|
|
179
|
+
if isinstance(chain, MessageChain):
|
|
180
|
+
if chain.type == "break":
|
|
181
|
+
if content_parts:
|
|
182
|
+
content = "".join(content_parts)
|
|
183
|
+
temp_chain = MessageChain([Plain(text=content)])
|
|
184
|
+
await self.send(temp_chain)
|
|
185
|
+
content_parts = []
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
for component in chain.chain:
|
|
189
|
+
if isinstance(component, Plain):
|
|
190
|
+
content_parts.append(component.text)
|
|
191
|
+
elif isinstance(component, Image):
|
|
192
|
+
if content_parts:
|
|
193
|
+
content = "".join(content_parts)
|
|
194
|
+
temp_chain = MessageChain([Plain(text=content)])
|
|
195
|
+
await self.send(temp_chain)
|
|
196
|
+
content_parts = []
|
|
197
|
+
try:
|
|
198
|
+
image_base64 = await component.convert_to_base64()
|
|
199
|
+
if image_base64:
|
|
200
|
+
img_chain = MessageChain(
|
|
201
|
+
[
|
|
202
|
+
Plain(
|
|
203
|
+
text=f'<img src="data:image/jpeg;base64,{image_base64}"/>'
|
|
204
|
+
)
|
|
205
|
+
]
|
|
206
|
+
)
|
|
207
|
+
await self.send(img_chain)
|
|
208
|
+
except Exception as e:
|
|
209
|
+
logger.error(f"图片转换为base64失败: {e}")
|
|
210
|
+
else:
|
|
211
|
+
content_parts.append(str(component))
|
|
212
|
+
|
|
213
|
+
if content_parts:
|
|
214
|
+
content = "".join(content_parts)
|
|
215
|
+
temp_chain = MessageChain([Plain(text=content)])
|
|
216
|
+
await self.send(temp_chain)
|
|
217
|
+
|
|
218
|
+
except Exception as e:
|
|
219
|
+
logger.error(f"Satori 流式消息发送异常: {e}")
|
|
220
|
+
|
|
221
|
+
return await super().send_streaming(generator, use_fallback)
|
|
@@ -99,12 +99,13 @@ class ProviderOpenAIOfficial(Provider):
|
|
|
99
99
|
for key in to_del:
|
|
100
100
|
del payloads[key]
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if
|
|
105
|
-
extra_body
|
|
102
|
+
# 读取并合并 custom_extra_body 配置
|
|
103
|
+
custom_extra_body = self.provider_config.get("custom_extra_body", {})
|
|
104
|
+
if isinstance(custom_extra_body, dict):
|
|
105
|
+
extra_body.update(custom_extra_body)
|
|
106
|
+
|
|
106
107
|
# 针对 deepseek 模型的特殊处理:deepseek-reasoner调用必须移除 tools ,否则将被切换至 deepseek-chat
|
|
107
|
-
|
|
108
|
+
if model == "deepseek-reasoner" and "tools" in payloads:
|
|
108
109
|
del payloads["tools"]
|
|
109
110
|
|
|
110
111
|
completion = await self.client.chat.completions.create(
|
|
@@ -137,6 +138,12 @@ class ProviderOpenAIOfficial(Provider):
|
|
|
137
138
|
|
|
138
139
|
# 不在默认参数中的参数放在 extra_body 中
|
|
139
140
|
extra_body = {}
|
|
141
|
+
|
|
142
|
+
# 读取并合并 custom_extra_body 配置
|
|
143
|
+
custom_extra_body = self.provider_config.get("custom_extra_body", {})
|
|
144
|
+
if isinstance(custom_extra_body, dict):
|
|
145
|
+
extra_body.update(custom_extra_body)
|
|
146
|
+
|
|
140
147
|
to_del = []
|
|
141
148
|
for key in payloads.keys():
|
|
142
149
|
if key not in self.default_params:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import aiohttp
|
|
2
|
+
from astrbot import logger
|
|
2
3
|
from ..provider import RerankProvider
|
|
3
4
|
from ..register import register_provider_adapter
|
|
4
5
|
from ..entities import ProviderType, RerankResult
|
|
@@ -44,6 +45,11 @@ class VLLMRerankProvider(RerankProvider):
|
|
|
44
45
|
response_data = await response.json()
|
|
45
46
|
results = response_data.get("results", [])
|
|
46
47
|
|
|
48
|
+
if not results:
|
|
49
|
+
logger.warning(
|
|
50
|
+
f"Rerank API 返回了空的列表数据。原始响应: {response_data}"
|
|
51
|
+
)
|
|
52
|
+
|
|
47
53
|
return [
|
|
48
54
|
RerankResult(
|
|
49
55
|
index=result["index"],
|
astrbot/core/star/__init__.py
CHANGED
|
@@ -27,14 +27,16 @@ class Star(CommandParserMixin):
|
|
|
27
27
|
star_map[cls.__module__].star_cls_type = cls
|
|
28
28
|
star_map[cls.__module__].module_path = cls.__module__
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
async def text_to_image(text: str, return_url=True) -> str:
|
|
30
|
+
async def text_to_image(self, text: str, return_url=True) -> str:
|
|
32
31
|
"""将文本转换为图片"""
|
|
33
|
-
return await html_renderer.render_t2i(
|
|
32
|
+
return await html_renderer.render_t2i(
|
|
33
|
+
text,
|
|
34
|
+
return_url=return_url,
|
|
35
|
+
template_name=self.context._config.get("t2i_active_template"),
|
|
36
|
+
)
|
|
34
37
|
|
|
35
|
-
@staticmethod
|
|
36
38
|
async def html_render(
|
|
37
|
-
tmpl: str, data: dict, return_url=True, options: dict | None = None
|
|
39
|
+
self, tmpl: str, data: dict, return_url=True, options: dict | None = None
|
|
38
40
|
) -> str:
|
|
39
41
|
"""渲染 HTML"""
|
|
40
42
|
return await html_renderer.render_custom_template(
|
|
@@ -7,7 +7,6 @@ from astrbot.core.config import AstrBotConfig
|
|
|
7
7
|
from .custom_filter import CustomFilter
|
|
8
8
|
from ..star_handler import StarHandlerMetadata
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
class GreedyStr(str):
|
|
12
11
|
"""标记指令完成其他参数接收后的所有剩余文本。"""
|
|
13
12
|
|
|
@@ -153,10 +152,17 @@ class CommandFilter(HandlerFilter):
|
|
|
153
152
|
_full = f"{parent_command_name} {candidate}"
|
|
154
153
|
else:
|
|
155
154
|
_full = candidate
|
|
156
|
-
if message_str
|
|
157
|
-
|
|
155
|
+
if message_str == _full:
|
|
156
|
+
# 完全等于命令名 → 没参数
|
|
157
|
+
message_str = ""
|
|
158
|
+
ok = True
|
|
159
|
+
break
|
|
160
|
+
elif message_str.startswith(_full):
|
|
161
|
+
# 命令名后面无论是空格还是直接连参数都可以
|
|
162
|
+
message_str = message_str[len(_full):].lstrip()
|
|
158
163
|
ok = True
|
|
159
164
|
break
|
|
165
|
+
|
|
160
166
|
if not ok:
|
|
161
167
|
return False
|
|
162
168
|
|
|
@@ -18,6 +18,7 @@ class PlatformAdapterType(enum.Flag):
|
|
|
18
18
|
KOOK = enum.auto()
|
|
19
19
|
VOCECHAT = enum.auto()
|
|
20
20
|
WEIXIN_OFFICIAL_ACCOUNT = enum.auto()
|
|
21
|
+
SATORI = enum.auto()
|
|
21
22
|
ALL = (
|
|
22
23
|
AIOCQHTTP
|
|
23
24
|
| QQOFFICIAL
|
|
@@ -31,6 +32,7 @@ class PlatformAdapterType(enum.Flag):
|
|
|
31
32
|
| KOOK
|
|
32
33
|
| VOCECHAT
|
|
33
34
|
| WEIXIN_OFFICIAL_ACCOUNT
|
|
35
|
+
| SATORI
|
|
34
36
|
)
|
|
35
37
|
|
|
36
38
|
|
|
@@ -47,6 +49,7 @@ ADAPTER_NAME_2_TYPE = {
|
|
|
47
49
|
"wechatpadpro": PlatformAdapterType.WECHATPADPRO,
|
|
48
50
|
"vocechat": PlatformAdapterType.VOCECHAT,
|
|
49
51
|
"weixin_official_account": PlatformAdapterType.WEIXIN_OFFICIAL_ACCOUNT,
|
|
52
|
+
"satori": PlatformAdapterType.SATORI,
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
|
|
@@ -8,6 +8,7 @@ from .star_handler import (
|
|
|
8
8
|
register_permission_type,
|
|
9
9
|
register_custom_filter,
|
|
10
10
|
register_on_astrbot_loaded,
|
|
11
|
+
register_on_platform_loaded,
|
|
11
12
|
register_on_llm_request,
|
|
12
13
|
register_on_llm_response,
|
|
13
14
|
register_llm_tool,
|
|
@@ -26,6 +27,7 @@ __all__ = [
|
|
|
26
27
|
"register_permission_type",
|
|
27
28
|
"register_custom_filter",
|
|
28
29
|
"register_on_astrbot_loaded",
|
|
30
|
+
"register_on_platform_loaded",
|
|
29
31
|
"register_on_llm_request",
|
|
30
32
|
"register_on_llm_response",
|
|
31
33
|
"register_llm_tool",
|
|
@@ -267,6 +267,18 @@ def register_on_astrbot_loaded(**kwargs):
|
|
|
267
267
|
return decorator
|
|
268
268
|
|
|
269
269
|
|
|
270
|
+
def register_on_platform_loaded(**kwargs):
|
|
271
|
+
"""
|
|
272
|
+
当平台加载完成时
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
def decorator(awaitable):
|
|
276
|
+
_ = get_handler_or_create(awaitable, EventType.OnPlatformLoadedEvent, **kwargs)
|
|
277
|
+
return awaitable
|
|
278
|
+
|
|
279
|
+
return decorator
|
|
280
|
+
|
|
281
|
+
|
|
270
282
|
def register_on_llm_request(**kwargs):
|
|
271
283
|
"""当有 LLM 请求时的事件
|
|
272
284
|
|
|
@@ -376,9 +388,11 @@ def register_llm_tool(name: str = None, **kwargs):
|
|
|
376
388
|
# print(f"Registering tool {llm_tool_name} for agent", registering_agent._agent.name)
|
|
377
389
|
if registering_agent._agent.tools is None:
|
|
378
390
|
registering_agent._agent.tools = []
|
|
379
|
-
registering_agent._agent.tools.append(
|
|
380
|
-
|
|
381
|
-
|
|
391
|
+
registering_agent._agent.tools.append(
|
|
392
|
+
llm_tools.spec_to_func(
|
|
393
|
+
llm_tool_name, args, docstring.description.strip(), awaitable
|
|
394
|
+
)
|
|
395
|
+
)
|
|
382
396
|
|
|
383
397
|
return awaitable
|
|
384
398
|
|
|
@@ -421,7 +435,7 @@ def register_agent(
|
|
|
421
435
|
run_hooks=run_hooks or BaseAgentRunHooks[AstrAgentContext](),
|
|
422
436
|
)
|
|
423
437
|
handoff_tool = HandoffTool(agent=agent)
|
|
424
|
-
handoff_tool.handler=awaitable
|
|
438
|
+
handoff_tool.handler = awaitable
|
|
425
439
|
llm_tools.func_list.append(handoff_tool)
|
|
426
440
|
return RegisteringAgent(agent)
|
|
427
441
|
|
|
@@ -34,19 +34,26 @@ class StarHandlerRegistry(Generic[T]):
|
|
|
34
34
|
) -> List[StarHandlerMetadata]:
|
|
35
35
|
handlers = []
|
|
36
36
|
for handler in self._handlers:
|
|
37
|
+
# 过滤事件类型
|
|
37
38
|
if handler.event_type != event_type:
|
|
38
39
|
continue
|
|
40
|
+
# 过滤启用状态
|
|
39
41
|
if only_activated:
|
|
40
42
|
plugin = star_map.get(handler.handler_module_path)
|
|
41
43
|
if not (plugin and plugin.activated):
|
|
42
44
|
continue
|
|
45
|
+
# 过滤插件白名单
|
|
43
46
|
if plugins_name is not None and plugins_name != ["*"]:
|
|
44
47
|
plugin = star_map.get(handler.handler_module_path)
|
|
45
48
|
if not plugin:
|
|
46
49
|
continue
|
|
47
50
|
if (
|
|
48
51
|
plugin.name not in plugins_name
|
|
49
|
-
and event_type
|
|
52
|
+
and event_type
|
|
53
|
+
not in (
|
|
54
|
+
EventType.OnAstrBotLoadedEvent,
|
|
55
|
+
EventType.OnPlatformLoadedEvent,
|
|
56
|
+
)
|
|
50
57
|
and not plugin.reserved
|
|
51
58
|
):
|
|
52
59
|
continue
|
|
@@ -90,6 +97,7 @@ class EventType(enum.Enum):
|
|
|
90
97
|
"""
|
|
91
98
|
|
|
92
99
|
OnAstrBotLoadedEvent = enum.auto() # AstrBot 加载完成
|
|
100
|
+
OnPlatformLoadedEvent = enum.auto() # 平台加载完成
|
|
93
101
|
|
|
94
102
|
AdapterMessageEvent = enum.auto() # 收到适配器发来的消息
|
|
95
103
|
OnLLMRequestEvent = enum.auto() # 收到 LLM 请求(可以是用户也可以是插件)
|