AstrBot 4.7.3__py3-none-any.whl → 4.8.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/cli/__init__.py +1 -1
- astrbot/core/agent/message.py +21 -5
- astrbot/core/astr_agent_run_util.py +15 -1
- astrbot/core/config/default.py +113 -1
- astrbot/core/db/__init__.py +30 -1
- astrbot/core/db/sqlite.py +55 -1
- astrbot/core/message/components.py +6 -1
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +64 -5
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/third_party.py +1 -1
- astrbot/core/platform/manager.py +67 -9
- astrbot/core/platform/platform.py +99 -2
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +19 -5
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +5 -7
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +1 -2
- astrbot/core/platform/sources/lark/lark_adapter.py +1 -3
- astrbot/core/platform/sources/misskey/misskey_adapter.py +1 -2
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +2 -0
- astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +1 -3
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +32 -9
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +13 -1
- astrbot/core/platform/sources/satori/satori_adapter.py +1 -2
- astrbot/core/platform/sources/slack/client.py +50 -39
- astrbot/core/platform/sources/slack/slack_adapter.py +21 -7
- astrbot/core/platform/sources/slack/slack_event.py +3 -3
- astrbot/core/platform/sources/telegram/tg_adapter.py +4 -3
- astrbot/core/platform/sources/webchat/webchat_adapter.py +95 -29
- astrbot/core/platform/sources/webchat/webchat_event.py +33 -33
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +1 -2
- astrbot/core/platform/sources/wecom/wecom_adapter.py +51 -9
- astrbot/core/platform/sources/wecom/wecom_event.py +1 -1
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +26 -9
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +27 -5
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +52 -11
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +1 -1
- astrbot/core/platform_message_history_mgr.py +3 -3
- astrbot/core/provider/provider.py +35 -0
- astrbot/core/provider/sources/whisper_api_source.py +43 -11
- astrbot/core/utils/file_extract.py +23 -0
- astrbot/core/utils/tencent_record_helper.py +1 -1
- astrbot/core/utils/webhook_utils.py +47 -0
- astrbot/dashboard/routes/__init__.py +2 -0
- astrbot/dashboard/routes/chat.py +300 -70
- astrbot/dashboard/routes/config.py +32 -165
- astrbot/dashboard/routes/knowledge_base.py +1 -1
- astrbot/dashboard/routes/platform.py +100 -0
- astrbot/dashboard/routes/plugin.py +65 -6
- astrbot/dashboard/server.py +3 -1
- {astrbot-4.7.3.dist-info → astrbot-4.8.0.dist-info}/METADATA +48 -37
- {astrbot-4.7.3.dist-info → astrbot-4.8.0.dist-info}/RECORD +52 -49
- {astrbot-4.7.3.dist-info → astrbot-4.8.0.dist-info}/WHEEL +0 -0
- {astrbot-4.7.3.dist-info → astrbot-4.8.0.dist-info}/entry_points.txt +0 -0
- {astrbot-4.7.3.dist-info → astrbot-4.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import sys
|
|
3
3
|
import uuid
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
import quart
|
|
6
7
|
from requests import Response
|
|
@@ -22,6 +23,7 @@ from astrbot.api.platform import (
|
|
|
22
23
|
)
|
|
23
24
|
from astrbot.core import logger
|
|
24
25
|
from astrbot.core.platform.astr_message_event import MessageSesion
|
|
26
|
+
from astrbot.core.utils.webhook_utils import log_webhook_info
|
|
25
27
|
|
|
26
28
|
from .weixin_offacc_event import WeixinOfficialAccountPlatformEvent
|
|
27
29
|
|
|
@@ -31,7 +33,7 @@ else:
|
|
|
31
33
|
from typing_extensions import override
|
|
32
34
|
|
|
33
35
|
|
|
34
|
-
class
|
|
36
|
+
class WeixinOfficialAccountServer:
|
|
35
37
|
def __init__(self, event_queue: asyncio.Queue, config: dict):
|
|
36
38
|
self.server = quart.Quart(__name__)
|
|
37
39
|
self.port = int(config.get("port"))
|
|
@@ -57,9 +59,21 @@ class WecomServer:
|
|
|
57
59
|
self.shutdown_event = asyncio.Event()
|
|
58
60
|
|
|
59
61
|
async def verify(self):
|
|
60
|
-
|
|
62
|
+
"""内部服务器的 GET 验证入口"""
|
|
63
|
+
return await self.handle_verify(quart.request)
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
async def handle_verify(self, request) -> str:
|
|
66
|
+
"""处理验证请求,可被统一 webhook 入口复用
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
request: Quart 请求对象
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
验证响应
|
|
73
|
+
"""
|
|
74
|
+
logger.info(f"验证请求有效性: {request.args}")
|
|
75
|
+
|
|
76
|
+
args = request.args
|
|
63
77
|
if not args.get("signature", None):
|
|
64
78
|
logger.error("未知的响应,请检查回调地址是否填写正确。")
|
|
65
79
|
return "err"
|
|
@@ -77,10 +91,22 @@ class WecomServer:
|
|
|
77
91
|
return "err"
|
|
78
92
|
|
|
79
93
|
async def callback_command(self):
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
"""内部服务器的 POST 回调入口"""
|
|
95
|
+
return await self.handle_callback(quart.request)
|
|
96
|
+
|
|
97
|
+
async def handle_callback(self, request) -> str:
|
|
98
|
+
"""处理回调请求,可被统一 webhook 入口复用
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
request: Quart 请求对象
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
响应内容
|
|
105
|
+
"""
|
|
106
|
+
data = await request.get_data()
|
|
107
|
+
msg_signature = request.args.get("msg_signature")
|
|
108
|
+
timestamp = request.args.get("timestamp")
|
|
109
|
+
nonce = request.args.get("nonce")
|
|
84
110
|
try:
|
|
85
111
|
xml = self.crypto.decrypt_message(data, msg_signature, timestamp, nonce)
|
|
86
112
|
except InvalidSignatureException:
|
|
@@ -123,8 +149,7 @@ class WeixinOfficialAccountPlatformAdapter(Platform):
|
|
|
123
149
|
platform_settings: dict,
|
|
124
150
|
event_queue: asyncio.Queue,
|
|
125
151
|
) -> None:
|
|
126
|
-
super().__init__(event_queue)
|
|
127
|
-
self.config = platform_config
|
|
152
|
+
super().__init__(platform_config, event_queue)
|
|
128
153
|
self.settingss = platform_settings
|
|
129
154
|
self.client_self_id = uuid.uuid4().hex[:8]
|
|
130
155
|
self.api_base_url = platform_config.get(
|
|
@@ -132,6 +157,7 @@ class WeixinOfficialAccountPlatformAdapter(Platform):
|
|
|
132
157
|
"https://api.weixin.qq.com/cgi-bin/",
|
|
133
158
|
)
|
|
134
159
|
self.active_send_mode = self.config.get("active_send_mode", False)
|
|
160
|
+
self.unified_webhook_mode = platform_config.get("unified_webhook_mode", False)
|
|
135
161
|
|
|
136
162
|
if not self.api_base_url:
|
|
137
163
|
self.api_base_url = "https://api.weixin.qq.com/cgi-bin/"
|
|
@@ -143,7 +169,7 @@ class WeixinOfficialAccountPlatformAdapter(Platform):
|
|
|
143
169
|
if not self.api_base_url.endswith("/"):
|
|
144
170
|
self.api_base_url += "/"
|
|
145
171
|
|
|
146
|
-
self.server =
|
|
172
|
+
self.server = WeixinOfficialAccountServer(self._event_queue, self.config)
|
|
147
173
|
|
|
148
174
|
self.client = WeChatClient(
|
|
149
175
|
self.config["appid"].strip(),
|
|
@@ -202,7 +228,22 @@ class WeixinOfficialAccountPlatformAdapter(Platform):
|
|
|
202
228
|
|
|
203
229
|
@override
|
|
204
230
|
async def run(self):
|
|
205
|
-
|
|
231
|
+
# 如果启用统一 webhook 模式,则不启动独立服务器
|
|
232
|
+
webhook_uuid = self.config.get("webhook_uuid")
|
|
233
|
+
if self.unified_webhook_mode and webhook_uuid:
|
|
234
|
+
log_webhook_info(f"{self.meta().id}(微信公众平台)", webhook_uuid)
|
|
235
|
+
# 保持运行状态,等待 shutdown
|
|
236
|
+
await self.server.shutdown_event.wait()
|
|
237
|
+
else:
|
|
238
|
+
await self.server.start_polling()
|
|
239
|
+
|
|
240
|
+
async def webhook_callback(self, request: Any) -> Any:
|
|
241
|
+
"""统一 Webhook 回调入口"""
|
|
242
|
+
# 根据请求方法分发到不同的处理函数
|
|
243
|
+
if request.method == "GET":
|
|
244
|
+
return await self.server.handle_verify(request)
|
|
245
|
+
else:
|
|
246
|
+
return await self.server.handle_callback(request)
|
|
206
247
|
|
|
207
248
|
async def convert_message(
|
|
208
249
|
self,
|
|
@@ -10,12 +10,12 @@ class PlatformMessageHistoryManager:
|
|
|
10
10
|
self,
|
|
11
11
|
platform_id: str,
|
|
12
12
|
user_id: str,
|
|
13
|
-
content:
|
|
13
|
+
content: dict, # TODO: parse from message chain
|
|
14
14
|
sender_id: str | None = None,
|
|
15
15
|
sender_name: str | None = None,
|
|
16
|
-
):
|
|
16
|
+
) -> PlatformMessageHistory:
|
|
17
17
|
"""Insert a new platform message history record."""
|
|
18
|
-
await self.db.insert_platform_message_history(
|
|
18
|
+
return await self.db.insert_platform_message_history(
|
|
19
19
|
platform_id=platform_id,
|
|
20
20
|
user_id=user_id,
|
|
21
21
|
content=content,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import asyncio
|
|
3
|
+
import os
|
|
3
4
|
from collections.abc import AsyncGenerator
|
|
4
5
|
|
|
5
6
|
from astrbot.core.agent.message import Message
|
|
@@ -11,6 +12,7 @@ from astrbot.core.provider.entities import (
|
|
|
11
12
|
ToolCallsResult,
|
|
12
13
|
)
|
|
13
14
|
from astrbot.core.provider.register import provider_cls_map
|
|
15
|
+
from astrbot.core.utils.astrbot_path import get_astrbot_path
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class AbstractProvider(abc.ABC):
|
|
@@ -43,6 +45,14 @@ class AbstractProvider(abc.ABC):
|
|
|
43
45
|
)
|
|
44
46
|
return meta
|
|
45
47
|
|
|
48
|
+
async def test(self):
|
|
49
|
+
"""test the provider is a
|
|
50
|
+
|
|
51
|
+
raises:
|
|
52
|
+
Exception: if the provider is not available
|
|
53
|
+
"""
|
|
54
|
+
...
|
|
55
|
+
|
|
46
56
|
|
|
47
57
|
class Provider(AbstractProvider):
|
|
48
58
|
"""Chat Provider"""
|
|
@@ -165,6 +175,12 @@ class Provider(AbstractProvider):
|
|
|
165
175
|
|
|
166
176
|
return dicts
|
|
167
177
|
|
|
178
|
+
async def test(self, timeout: float = 45.0):
|
|
179
|
+
await asyncio.wait_for(
|
|
180
|
+
self.text_chat(prompt="REPLY `PONG` ONLY"),
|
|
181
|
+
timeout=timeout,
|
|
182
|
+
)
|
|
183
|
+
|
|
168
184
|
|
|
169
185
|
class STTProvider(AbstractProvider):
|
|
170
186
|
def __init__(self, provider_config: dict, provider_settings: dict) -> None:
|
|
@@ -177,6 +193,14 @@ class STTProvider(AbstractProvider):
|
|
|
177
193
|
"""获取音频的文本"""
|
|
178
194
|
raise NotImplementedError
|
|
179
195
|
|
|
196
|
+
async def test(self):
|
|
197
|
+
sample_audio_path = os.path.join(
|
|
198
|
+
get_astrbot_path(),
|
|
199
|
+
"samples",
|
|
200
|
+
"stt_health_check.wav",
|
|
201
|
+
)
|
|
202
|
+
await self.get_text(sample_audio_path)
|
|
203
|
+
|
|
180
204
|
|
|
181
205
|
class TTSProvider(AbstractProvider):
|
|
182
206
|
def __init__(self, provider_config: dict, provider_settings: dict) -> None:
|
|
@@ -189,6 +213,9 @@ class TTSProvider(AbstractProvider):
|
|
|
189
213
|
"""获取文本的音频,返回音频文件路径"""
|
|
190
214
|
raise NotImplementedError
|
|
191
215
|
|
|
216
|
+
async def test(self):
|
|
217
|
+
await self.get_audio("hi")
|
|
218
|
+
|
|
192
219
|
|
|
193
220
|
class EmbeddingProvider(AbstractProvider):
|
|
194
221
|
def __init__(self, provider_config: dict, provider_settings: dict) -> None:
|
|
@@ -211,6 +238,9 @@ class EmbeddingProvider(AbstractProvider):
|
|
|
211
238
|
"""获取向量的维度"""
|
|
212
239
|
...
|
|
213
240
|
|
|
241
|
+
async def test(self):
|
|
242
|
+
await self.get_embedding("astrbot")
|
|
243
|
+
|
|
214
244
|
async def get_embeddings_batch(
|
|
215
245
|
self,
|
|
216
246
|
texts: list[str],
|
|
@@ -294,3 +324,8 @@ class RerankProvider(AbstractProvider):
|
|
|
294
324
|
) -> list[RerankResult]:
|
|
295
325
|
"""获取查询和文档的重排序分数"""
|
|
296
326
|
...
|
|
327
|
+
|
|
328
|
+
async def test(self):
|
|
329
|
+
result = await self.rerank("Apple", documents=["apple", "banana"])
|
|
330
|
+
if not result:
|
|
331
|
+
raise Exception("Rerank provider test failed, no results returned")
|
|
@@ -6,7 +6,10 @@ from openai import NOT_GIVEN, AsyncOpenAI
|
|
|
6
6
|
from astrbot.core import logger
|
|
7
7
|
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
8
8
|
from astrbot.core.utils.io import download_file
|
|
9
|
-
from astrbot.core.utils.tencent_record_helper import
|
|
9
|
+
from astrbot.core.utils.tencent_record_helper import (
|
|
10
|
+
convert_to_pcm_wav,
|
|
11
|
+
tencent_silk_to_wav,
|
|
12
|
+
)
|
|
10
13
|
|
|
11
14
|
from ..entities import ProviderType
|
|
12
15
|
from ..provider import STTProvider
|
|
@@ -35,18 +38,28 @@ class ProviderOpenAIWhisperAPI(STTProvider):
|
|
|
35
38
|
|
|
36
39
|
self.set_model(provider_config.get("model"))
|
|
37
40
|
|
|
38
|
-
async def
|
|
41
|
+
async def _get_audio_format(self, file_path):
|
|
42
|
+
# 定义要检测的头部字节
|
|
39
43
|
silk_header = b"SILK"
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
amr_header = b"#!AMR"
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
with open(file_path, "rb") as f:
|
|
48
|
+
file_header = f.read(8)
|
|
49
|
+
except FileNotFoundError:
|
|
50
|
+
return None
|
|
42
51
|
|
|
43
52
|
if silk_header in file_header:
|
|
44
|
-
return
|
|
45
|
-
|
|
53
|
+
return "silk"
|
|
54
|
+
|
|
55
|
+
if amr_header in file_header:
|
|
56
|
+
return "amr"
|
|
57
|
+
return None
|
|
46
58
|
|
|
47
59
|
async def get_text(self, audio_url: str) -> str:
|
|
48
60
|
"""Only supports mp3, mp4, mpeg, m4a, wav, webm"""
|
|
49
61
|
is_tencent = False
|
|
62
|
+
output_path = None
|
|
50
63
|
|
|
51
64
|
if audio_url.startswith("http"):
|
|
52
65
|
if "multimedia.nt.qq.com.cn" in audio_url:
|
|
@@ -62,16 +75,35 @@ class ProviderOpenAIWhisperAPI(STTProvider):
|
|
|
62
75
|
raise FileNotFoundError(f"文件不存在: {audio_url}")
|
|
63
76
|
|
|
64
77
|
if audio_url.endswith(".amr") or audio_url.endswith(".silk") or is_tencent:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
78
|
+
file_format = await self._get_audio_format(audio_url)
|
|
79
|
+
|
|
80
|
+
# 判断是否需要转换
|
|
81
|
+
if file_format in ["silk", "amr"]:
|
|
68
82
|
temp_dir = os.path.join(get_astrbot_data_path(), "temp")
|
|
69
83
|
output_path = os.path.join(temp_dir, str(uuid.uuid4()) + ".wav")
|
|
70
|
-
|
|
84
|
+
|
|
85
|
+
if file_format == "silk":
|
|
86
|
+
logger.info(
|
|
87
|
+
"Converting silk file to wav using tencent_silk_to_wav..."
|
|
88
|
+
)
|
|
89
|
+
await tencent_silk_to_wav(audio_url, output_path)
|
|
90
|
+
elif file_format == "amr":
|
|
91
|
+
logger.info(
|
|
92
|
+
"Converting amr file to wav using convert_to_pcm_wav..."
|
|
93
|
+
)
|
|
94
|
+
await convert_to_pcm_wav(audio_url, output_path)
|
|
95
|
+
|
|
71
96
|
audio_url = output_path
|
|
72
97
|
|
|
73
98
|
result = await self.client.audio.transcriptions.create(
|
|
74
99
|
model=self.model_name,
|
|
75
|
-
file=open(audio_url, "rb"),
|
|
100
|
+
file=("audio.wav", open(audio_url, "rb")),
|
|
76
101
|
)
|
|
102
|
+
|
|
103
|
+
# remove temp file
|
|
104
|
+
if output_path and os.path.exists(output_path):
|
|
105
|
+
try:
|
|
106
|
+
os.remove(audio_url)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
logger.error(f"Failed to remove temp file {audio_url}: {e}")
|
|
77
109
|
return result.text
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from openai import AsyncOpenAI
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def extract_file_moonshotai(file_path: str, api_key: str) -> str:
|
|
7
|
+
"""Extract text from a file using Moonshot AI API"""
|
|
8
|
+
"""
|
|
9
|
+
Args:
|
|
10
|
+
file_path: The path to the file to extract text from
|
|
11
|
+
api_key: The API key to use to extract text from the file
|
|
12
|
+
Returns:
|
|
13
|
+
The text extracted from the file
|
|
14
|
+
"""
|
|
15
|
+
client = AsyncOpenAI(
|
|
16
|
+
api_key=api_key,
|
|
17
|
+
base_url="https://api.moonshot.cn/v1",
|
|
18
|
+
)
|
|
19
|
+
file_object = await client.files.create(
|
|
20
|
+
file=Path(file_path),
|
|
21
|
+
purpose="file-extract", # type: ignore
|
|
22
|
+
)
|
|
23
|
+
return (await client.files.content(file_id=file_object.id)).text
|
|
@@ -36,7 +36,7 @@ async def wav_to_tencent_silk(wav_path: str, output_path: str) -> int:
|
|
|
36
36
|
import pilk
|
|
37
37
|
except (ImportError, ModuleNotFoundError) as _:
|
|
38
38
|
raise Exception(
|
|
39
|
-
"pilk
|
|
39
|
+
"pilk 模块未安装,请前往管理面板->平台日志->安装pip库 安装 pilk 这个库",
|
|
40
40
|
)
|
|
41
41
|
# with wave.open(wav_path, 'rb') as wav:
|
|
42
42
|
# wav_data = wav.readframes(wav.getnframes())
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from astrbot.core import astrbot_config, logger
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _get_callback_api_base() -> str:
|
|
5
|
+
try:
|
|
6
|
+
return astrbot_config.get("callback_api_base", "").rstrip("/")
|
|
7
|
+
except Exception as e:
|
|
8
|
+
logger.error(f"获取 callback_api_base 失败: {e!s}")
|
|
9
|
+
return ""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _get_dashboard_port() -> int:
|
|
13
|
+
try:
|
|
14
|
+
return astrbot_config.get("dashboard", {}).get("port", 6185)
|
|
15
|
+
except Exception as e:
|
|
16
|
+
logger.error(f"获取 dashboard 端口失败: {e!s}")
|
|
17
|
+
return 6185
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def log_webhook_info(platform_name: str, webhook_uuid: str):
|
|
21
|
+
"""打印美观的 webhook 信息日志
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
platform_name: 平台名称
|
|
25
|
+
webhook_uuid: webhook 的 UUID
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
callback_base = _get_callback_api_base()
|
|
29
|
+
|
|
30
|
+
if not callback_base:
|
|
31
|
+
callback_base = "http(s)://<your-astrbot-domain>"
|
|
32
|
+
|
|
33
|
+
if not callback_base.startswith("http"):
|
|
34
|
+
callback_base = f"http(s)://{callback_base}"
|
|
35
|
+
|
|
36
|
+
callback_base = callback_base.rstrip("/")
|
|
37
|
+
webhook_url = f"{callback_base}/api/platform/webhook/{webhook_uuid}"
|
|
38
|
+
|
|
39
|
+
display_log = (
|
|
40
|
+
"\n====================\n"
|
|
41
|
+
f"🔗 机器人平台 {platform_name} 已启用统一 Webhook 模式\n"
|
|
42
|
+
f"📍 Webhook 回调地址: \n"
|
|
43
|
+
f" ➜ http://<your-ip>:{_get_dashboard_port()}/api/platform/webhook/{webhook_uuid}\n"
|
|
44
|
+
f" ➜ {webhook_url}\n"
|
|
45
|
+
"====================\n"
|
|
46
|
+
)
|
|
47
|
+
logger.info(display_log)
|
|
@@ -6,6 +6,7 @@ from .file import FileRoute
|
|
|
6
6
|
from .knowledge_base import KnowledgeBaseRoute
|
|
7
7
|
from .log import LogRoute
|
|
8
8
|
from .persona import PersonaRoute
|
|
9
|
+
from .platform import PlatformRoute
|
|
9
10
|
from .plugin import PluginRoute
|
|
10
11
|
from .session_management import SessionManagementRoute
|
|
11
12
|
from .stat import StatRoute
|
|
@@ -22,6 +23,7 @@ __all__ = [
|
|
|
22
23
|
"KnowledgeBaseRoute",
|
|
23
24
|
"LogRoute",
|
|
24
25
|
"PersonaRoute",
|
|
26
|
+
"PlatformRoute",
|
|
25
27
|
"PluginRoute",
|
|
26
28
|
"SessionManagementRoute",
|
|
27
29
|
"StatRoute",
|