AstrBot 4.5.1__py3-none-any.whl → 4.5.2__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/__init__.py +10 -11
- astrbot/api/event/__init__.py +5 -6
- astrbot/api/event/filter/__init__.py +37 -36
- astrbot/api/platform/__init__.py +7 -8
- astrbot/api/provider/__init__.py +7 -7
- astrbot/api/star/__init__.py +3 -4
- astrbot/api/util/__init__.py +2 -2
- astrbot/cli/__main__.py +5 -5
- astrbot/cli/commands/__init__.py +3 -3
- astrbot/cli/commands/cmd_conf.py +19 -16
- astrbot/cli/commands/cmd_init.py +3 -2
- astrbot/cli/commands/cmd_plug.py +8 -10
- astrbot/cli/commands/cmd_run.py +5 -6
- astrbot/cli/utils/__init__.py +6 -6
- astrbot/cli/utils/basic.py +14 -14
- astrbot/cli/utils/plugin.py +24 -15
- astrbot/cli/utils/version_comparator.py +10 -12
- astrbot/core/__init__.py +8 -6
- astrbot/core/agent/agent.py +3 -2
- astrbot/core/agent/handoff.py +6 -2
- astrbot/core/agent/hooks.py +9 -6
- astrbot/core/agent/mcp_client.py +50 -15
- astrbot/core/agent/message.py +168 -0
- astrbot/core/agent/response.py +2 -1
- astrbot/core/agent/run_context.py +2 -3
- astrbot/core/agent/runners/base.py +10 -13
- astrbot/core/agent/runners/tool_loop_agent_runner.py +52 -51
- astrbot/core/agent/tool.py +60 -41
- astrbot/core/agent/tool_executor.py +9 -3
- astrbot/core/astr_agent_context.py +3 -1
- astrbot/core/astrbot_config_mgr.py +29 -9
- astrbot/core/config/__init__.py +2 -2
- astrbot/core/config/astrbot_config.py +28 -26
- astrbot/core/config/default.py +4 -6
- astrbot/core/conversation_mgr.py +105 -36
- astrbot/core/core_lifecycle.py +68 -54
- astrbot/core/db/__init__.py +33 -18
- astrbot/core/db/migration/helper.py +12 -10
- astrbot/core/db/migration/migra_3_to_4.py +53 -34
- astrbot/core/db/migration/migra_45_to_46.py +1 -1
- astrbot/core/db/migration/shared_preferences_v3.py +2 -1
- astrbot/core/db/migration/sqlite_v3.py +26 -23
- astrbot/core/db/po.py +27 -18
- astrbot/core/db/sqlite.py +74 -45
- astrbot/core/db/vec_db/base.py +10 -14
- astrbot/core/db/vec_db/faiss_impl/document_storage.py +90 -77
- astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +9 -3
- astrbot/core/db/vec_db/faiss_impl/vec_db.py +36 -31
- astrbot/core/event_bus.py +8 -6
- astrbot/core/file_token_service.py +6 -5
- astrbot/core/initial_loader.py +7 -5
- astrbot/core/knowledge_base/chunking/__init__.py +1 -3
- astrbot/core/knowledge_base/chunking/base.py +1 -0
- astrbot/core/knowledge_base/chunking/fixed_size.py +2 -0
- astrbot/core/knowledge_base/chunking/recursive.py +16 -10
- astrbot/core/knowledge_base/kb_db_sqlite.py +50 -48
- astrbot/core/knowledge_base/kb_helper.py +30 -17
- astrbot/core/knowledge_base/kb_mgr.py +6 -7
- astrbot/core/knowledge_base/models.py +10 -4
- astrbot/core/knowledge_base/parsers/__init__.py +3 -5
- astrbot/core/knowledge_base/parsers/base.py +1 -0
- astrbot/core/knowledge_base/parsers/markitdown_parser.py +2 -1
- astrbot/core/knowledge_base/parsers/pdf_parser.py +2 -1
- astrbot/core/knowledge_base/parsers/text_parser.py +1 -0
- astrbot/core/knowledge_base/parsers/util.py +1 -1
- astrbot/core/knowledge_base/retrieval/__init__.py +6 -8
- astrbot/core/knowledge_base/retrieval/manager.py +17 -14
- astrbot/core/knowledge_base/retrieval/rank_fusion.py +7 -3
- astrbot/core/knowledge_base/retrieval/sparse_retriever.py +11 -5
- astrbot/core/log.py +21 -13
- astrbot/core/message/components.py +123 -217
- astrbot/core/message/message_event_result.py +24 -24
- astrbot/core/persona_mgr.py +20 -11
- astrbot/core/pipeline/__init__.py +7 -7
- astrbot/core/pipeline/content_safety_check/stage.py +13 -9
- astrbot/core/pipeline/content_safety_check/strategies/__init__.py +1 -2
- astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +12 -13
- astrbot/core/pipeline/content_safety_check/strategies/keywords.py +1 -0
- astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
- astrbot/core/pipeline/context.py +4 -1
- astrbot/core/pipeline/context_utils.py +77 -7
- astrbot/core/pipeline/preprocess_stage/stage.py +12 -9
- astrbot/core/pipeline/process_stage/method/llm_request.py +125 -72
- astrbot/core/pipeline/process_stage/method/star_request.py +19 -17
- astrbot/core/pipeline/process_stage/stage.py +13 -10
- astrbot/core/pipeline/process_stage/utils.py +6 -5
- astrbot/core/pipeline/rate_limit_check/stage.py +37 -36
- astrbot/core/pipeline/respond/stage.py +23 -20
- astrbot/core/pipeline/result_decorate/stage.py +31 -23
- astrbot/core/pipeline/scheduler.py +12 -8
- astrbot/core/pipeline/session_status_check/stage.py +12 -8
- astrbot/core/pipeline/stage.py +10 -4
- astrbot/core/pipeline/waking_check/stage.py +24 -18
- astrbot/core/pipeline/whitelist_check/stage.py +10 -7
- astrbot/core/platform/__init__.py +6 -6
- astrbot/core/platform/astr_message_event.py +76 -110
- astrbot/core/platform/astrbot_message.py +11 -13
- astrbot/core/platform/manager.py +16 -15
- astrbot/core/platform/message_session.py +5 -3
- astrbot/core/platform/platform.py +16 -24
- astrbot/core/platform/platform_metadata.py +4 -4
- astrbot/core/platform/register.py +8 -8
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +23 -15
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +51 -33
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +42 -27
- astrbot/core/platform/sources/dingtalk/dingtalk_event.py +7 -3
- astrbot/core/platform/sources/discord/client.py +9 -6
- astrbot/core/platform/sources/discord/components.py +18 -14
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +45 -30
- astrbot/core/platform/sources/discord/discord_platform_event.py +38 -30
- astrbot/core/platform/sources/lark/lark_adapter.py +23 -17
- astrbot/core/platform/sources/lark/lark_event.py +21 -14
- astrbot/core/platform/sources/misskey/misskey_adapter.py +107 -67
- astrbot/core/platform/sources/misskey/misskey_api.py +153 -129
- astrbot/core/platform/sources/misskey/misskey_event.py +20 -15
- astrbot/core/platform/sources/misskey/misskey_utils.py +74 -62
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +63 -44
- astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +41 -26
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +36 -17
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_event.py +3 -1
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +12 -7
- astrbot/core/platform/sources/satori/satori_adapter.py +56 -38
- astrbot/core/platform/sources/satori/satori_event.py +34 -25
- astrbot/core/platform/sources/slack/client.py +11 -9
- astrbot/core/platform/sources/slack/slack_adapter.py +52 -36
- astrbot/core/platform/sources/slack/slack_event.py +34 -24
- astrbot/core/platform/sources/telegram/tg_adapter.py +38 -18
- astrbot/core/platform/sources/telegram/tg_event.py +32 -18
- astrbot/core/platform/sources/webchat/webchat_adapter.py +27 -17
- astrbot/core/platform/sources/webchat/webchat_event.py +14 -10
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +115 -120
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +9 -8
- astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +15 -16
- astrbot/core/platform/sources/wecom/wecom_adapter.py +35 -18
- astrbot/core/platform/sources/wecom/wecom_event.py +55 -48
- astrbot/core/platform/sources/wecom/wecom_kf.py +34 -44
- astrbot/core/platform/sources/wecom/wecom_kf_message.py +26 -10
- astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +18 -10
- astrbot/core/platform/sources/wecom_ai_bot/__init__.py +3 -5
- astrbot/core/platform/sources/wecom_ai_bot/ierror.py +0 -1
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +61 -37
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +67 -28
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +8 -9
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +18 -9
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +14 -12
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +22 -12
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +40 -26
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +47 -45
- astrbot/core/platform_message_history_mgr.py +5 -3
- astrbot/core/provider/__init__.py +2 -3
- astrbot/core/provider/entites.py +8 -8
- astrbot/core/provider/entities.py +61 -75
- astrbot/core/provider/func_tool_manager.py +59 -55
- astrbot/core/provider/manager.py +32 -22
- astrbot/core/provider/provider.py +72 -46
- astrbot/core/provider/register.py +7 -7
- astrbot/core/provider/sources/anthropic_source.py +48 -30
- astrbot/core/provider/sources/azure_tts_source.py +17 -13
- astrbot/core/provider/sources/coze_api_client.py +27 -17
- astrbot/core/provider/sources/coze_source.py +104 -87
- astrbot/core/provider/sources/dashscope_source.py +18 -11
- astrbot/core/provider/sources/dashscope_tts.py +36 -23
- astrbot/core/provider/sources/dify_source.py +25 -20
- astrbot/core/provider/sources/edge_tts_source.py +21 -17
- astrbot/core/provider/sources/fishaudio_tts_api_source.py +22 -14
- astrbot/core/provider/sources/gemini_embedding_source.py +12 -13
- astrbot/core/provider/sources/gemini_source.py +72 -58
- astrbot/core/provider/sources/gemini_tts_source.py +8 -6
- astrbot/core/provider/sources/gsv_selfhosted_source.py +17 -14
- astrbot/core/provider/sources/gsvi_tts_source.py +11 -7
- astrbot/core/provider/sources/minimax_tts_api_source.py +50 -40
- astrbot/core/provider/sources/openai_embedding_source.py +6 -8
- astrbot/core/provider/sources/openai_source.py +77 -69
- astrbot/core/provider/sources/openai_tts_api_source.py +14 -6
- astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
- astrbot/core/provider/sources/vllm_rerank_source.py +10 -4
- astrbot/core/provider/sources/volcengine_tts.py +38 -31
- astrbot/core/provider/sources/whisper_api_source.py +14 -12
- astrbot/core/provider/sources/whisper_selfhosted_source.py +15 -11
- astrbot/core/provider/sources/xinference_rerank_source.py +16 -8
- astrbot/core/provider/sources/xinference_stt_provider.py +35 -25
- astrbot/core/star/__init__.py +16 -11
- astrbot/core/star/config.py +10 -15
- astrbot/core/star/context.py +97 -75
- astrbot/core/star/filter/__init__.py +4 -3
- astrbot/core/star/filter/command.py +30 -28
- astrbot/core/star/filter/command_group.py +27 -24
- astrbot/core/star/filter/custom_filter.py +6 -5
- astrbot/core/star/filter/event_message_type.py +4 -2
- astrbot/core/star/filter/permission.py +4 -2
- astrbot/core/star/filter/platform_adapter_type.py +4 -2
- astrbot/core/star/filter/regex.py +4 -2
- astrbot/core/star/register/__init__.py +19 -19
- astrbot/core/star/register/star.py +6 -2
- astrbot/core/star/register/star_handler.py +96 -73
- astrbot/core/star/session_llm_manager.py +48 -14
- astrbot/core/star/session_plugin_manager.py +29 -15
- astrbot/core/star/star.py +1 -2
- astrbot/core/star/star_handler.py +13 -8
- astrbot/core/star/star_manager.py +151 -59
- astrbot/core/star/star_tools.py +44 -37
- astrbot/core/star/updator.py +10 -10
- astrbot/core/umop_config_router.py +10 -4
- astrbot/core/updator.py +13 -5
- astrbot/core/utils/astrbot_path.py +3 -5
- astrbot/core/utils/dify_api_client.py +33 -15
- astrbot/core/utils/io.py +66 -42
- astrbot/core/utils/log_pipe.py +1 -1
- astrbot/core/utils/metrics.py +7 -7
- astrbot/core/utils/path_util.py +15 -16
- astrbot/core/utils/pip_installer.py +5 -5
- astrbot/core/utils/session_waiter.py +19 -20
- astrbot/core/utils/shared_preferences.py +45 -20
- astrbot/core/utils/t2i/__init__.py +4 -1
- astrbot/core/utils/t2i/network_strategy.py +35 -26
- astrbot/core/utils/t2i/renderer.py +11 -5
- astrbot/core/utils/t2i/template_manager.py +14 -15
- astrbot/core/utils/tencent_record_helper.py +19 -13
- astrbot/core/utils/version_comparator.py +10 -13
- astrbot/core/zip_updator.py +43 -40
- astrbot/dashboard/routes/__init__.py +18 -18
- astrbot/dashboard/routes/auth.py +10 -8
- astrbot/dashboard/routes/chat.py +30 -21
- astrbot/dashboard/routes/config.py +92 -75
- astrbot/dashboard/routes/conversation.py +46 -39
- astrbot/dashboard/routes/file.py +4 -2
- astrbot/dashboard/routes/knowledge_base.py +47 -40
- astrbot/dashboard/routes/log.py +9 -4
- astrbot/dashboard/routes/persona.py +19 -16
- astrbot/dashboard/routes/plugin.py +69 -55
- astrbot/dashboard/routes/route.py +3 -1
- astrbot/dashboard/routes/session_management.py +130 -116
- astrbot/dashboard/routes/stat.py +34 -34
- astrbot/dashboard/routes/t2i.py +15 -12
- astrbot/dashboard/routes/tools.py +47 -52
- astrbot/dashboard/routes/update.py +32 -28
- astrbot/dashboard/server.py +30 -26
- astrbot/dashboard/utils.py +8 -4
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/METADATA +2 -1
- astrbot-4.5.2.dist-info/RECORD +261 -0
- astrbot-4.5.1.dist-info/RECORD +0 -260
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/WHEEL +0 -0
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/entry_points.txt +0 -0
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import json
|
|
3
|
-
|
|
2
|
+
import os
|
|
4
3
|
from contextlib import asynccontextmanager
|
|
4
|
+
from datetime import datetime
|
|
5
5
|
|
|
6
|
-
from sqlalchemy import
|
|
6
|
+
from sqlalchemy import Column, Text
|
|
7
7
|
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
|
|
8
8
|
from sqlalchemy.orm import sessionmaker
|
|
9
|
-
from sqlmodel import Field,
|
|
9
|
+
from sqlmodel import Field, MetaData, SQLModel, col, func, select, text
|
|
10
|
+
|
|
10
11
|
from astrbot.core import logger
|
|
11
12
|
|
|
12
13
|
|
|
@@ -20,7 +21,9 @@ class Document(BaseDocModel, table=True):
|
|
|
20
21
|
__tablename__ = "documents" # type: ignore
|
|
21
22
|
|
|
22
23
|
id: int | None = Field(
|
|
23
|
-
default=None,
|
|
24
|
+
default=None,
|
|
25
|
+
primary_key=True,
|
|
26
|
+
sa_column_kwargs={"autoincrement": True},
|
|
24
27
|
)
|
|
25
28
|
doc_id: str = Field(nullable=False)
|
|
26
29
|
text: str = Field(nullable=False)
|
|
@@ -36,7 +39,8 @@ class DocumentStorage:
|
|
|
36
39
|
self.engine: AsyncEngine | None = None
|
|
37
40
|
self.async_session_maker: sessionmaker | None = None
|
|
38
41
|
self.sqlite_init_path = os.path.join(
|
|
39
|
-
os.path.dirname(__file__),
|
|
42
|
+
os.path.dirname(__file__),
|
|
43
|
+
"sqlite_init.sql",
|
|
40
44
|
)
|
|
41
45
|
|
|
42
46
|
async def initialize(self):
|
|
@@ -50,26 +54,26 @@ class DocumentStorage:
|
|
|
50
54
|
await conn.execute(
|
|
51
55
|
text(
|
|
52
56
|
"ALTER TABLE documents ADD COLUMN kb_doc_id TEXT "
|
|
53
|
-
"GENERATED ALWAYS AS (json_extract(metadata, '$.kb_doc_id')) STORED"
|
|
54
|
-
)
|
|
57
|
+
"GENERATED ALWAYS AS (json_extract(metadata, '$.kb_doc_id')) STORED",
|
|
58
|
+
),
|
|
55
59
|
)
|
|
56
60
|
await conn.execute(
|
|
57
61
|
text(
|
|
58
62
|
"ALTER TABLE documents ADD COLUMN user_id TEXT "
|
|
59
|
-
"GENERATED ALWAYS AS (json_extract(metadata, '$.user_id')) STORED"
|
|
60
|
-
)
|
|
63
|
+
"GENERATED ALWAYS AS (json_extract(metadata, '$.user_id')) STORED",
|
|
64
|
+
),
|
|
61
65
|
)
|
|
62
66
|
|
|
63
67
|
# Create indexes
|
|
64
68
|
await conn.execute(
|
|
65
69
|
text(
|
|
66
|
-
"CREATE INDEX IF NOT EXISTS idx_documents_kb_doc_id ON documents(kb_doc_id)"
|
|
67
|
-
)
|
|
70
|
+
"CREATE INDEX IF NOT EXISTS idx_documents_kb_doc_id ON documents(kb_doc_id)",
|
|
71
|
+
),
|
|
68
72
|
)
|
|
69
73
|
await conn.execute(
|
|
70
74
|
text(
|
|
71
|
-
"CREATE INDEX IF NOT EXISTS idx_documents_user_id ON documents(user_id)"
|
|
72
|
-
)
|
|
75
|
+
"CREATE INDEX IF NOT EXISTS idx_documents_user_id ON documents(user_id)",
|
|
76
|
+
),
|
|
73
77
|
)
|
|
74
78
|
except BaseException:
|
|
75
79
|
pass
|
|
@@ -113,10 +117,11 @@ class DocumentStorage:
|
|
|
113
117
|
|
|
114
118
|
Returns:
|
|
115
119
|
list: The list of documents that match the filters.
|
|
120
|
+
|
|
116
121
|
"""
|
|
117
122
|
if self.engine is None:
|
|
118
123
|
logger.warning(
|
|
119
|
-
"Database connection is not initialized, returning empty result"
|
|
124
|
+
"Database connection is not initialized, returning empty result",
|
|
120
125
|
)
|
|
121
126
|
return []
|
|
122
127
|
|
|
@@ -125,7 +130,7 @@ class DocumentStorage:
|
|
|
125
130
|
|
|
126
131
|
for key, val in metadata_filters.items():
|
|
127
132
|
query = query.where(
|
|
128
|
-
text(f"json_extract(metadata, '$.{key}') = :filter_{key}")
|
|
133
|
+
text(f"json_extract(metadata, '$.{key}') = :filter_{key}"),
|
|
129
134
|
).params(**{f"filter_{key}": val})
|
|
130
135
|
|
|
131
136
|
if ids is not None and len(ids) > 0:
|
|
@@ -153,24 +158,27 @@ class DocumentStorage:
|
|
|
153
158
|
|
|
154
159
|
Returns:
|
|
155
160
|
int: The integer ID of the inserted document.
|
|
161
|
+
|
|
156
162
|
"""
|
|
157
163
|
assert self.engine is not None, "Database connection is not initialized."
|
|
158
164
|
|
|
159
|
-
async with self.get_session() as session:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return document.id # type: ignore
|
|
165
|
+
async with self.get_session() as session, session.begin():
|
|
166
|
+
document = Document(
|
|
167
|
+
doc_id=doc_id,
|
|
168
|
+
text=text,
|
|
169
|
+
metadata_=json.dumps(metadata),
|
|
170
|
+
created_at=datetime.now(),
|
|
171
|
+
updated_at=datetime.now(),
|
|
172
|
+
)
|
|
173
|
+
session.add(document)
|
|
174
|
+
await session.flush() # Flush to get the ID
|
|
175
|
+
return document.id # type: ignore
|
|
171
176
|
|
|
172
177
|
async def insert_documents_batch(
|
|
173
|
-
self,
|
|
178
|
+
self,
|
|
179
|
+
doc_ids: list[str],
|
|
180
|
+
texts: list[str],
|
|
181
|
+
metadatas: list[dict],
|
|
174
182
|
) -> list[int]:
|
|
175
183
|
"""Batch insert documents and return their integer IDs.
|
|
176
184
|
|
|
@@ -181,44 +189,44 @@ class DocumentStorage:
|
|
|
181
189
|
|
|
182
190
|
Returns:
|
|
183
191
|
list[int]: List of integer IDs of the inserted documents.
|
|
192
|
+
|
|
184
193
|
"""
|
|
185
194
|
assert self.engine is not None, "Database connection is not initialized."
|
|
186
195
|
|
|
187
|
-
async with self.get_session() as session:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return [doc.id for doc in documents] # type: ignore
|
|
196
|
+
async with self.get_session() as session, session.begin():
|
|
197
|
+
import json
|
|
198
|
+
|
|
199
|
+
documents = []
|
|
200
|
+
for doc_id, text, metadata in zip(doc_ids, texts, metadatas):
|
|
201
|
+
document = Document(
|
|
202
|
+
doc_id=doc_id,
|
|
203
|
+
text=text,
|
|
204
|
+
metadata_=json.dumps(metadata),
|
|
205
|
+
created_at=datetime.now(),
|
|
206
|
+
updated_at=datetime.now(),
|
|
207
|
+
)
|
|
208
|
+
documents.append(document)
|
|
209
|
+
session.add(document)
|
|
210
|
+
|
|
211
|
+
await session.flush() # Flush to get all IDs
|
|
212
|
+
return [doc.id for doc in documents] # type: ignore
|
|
205
213
|
|
|
206
214
|
async def delete_document_by_doc_id(self, doc_id: str):
|
|
207
215
|
"""Delete a document by its doc_id.
|
|
208
216
|
|
|
209
217
|
Args:
|
|
210
218
|
doc_id (str): The doc_id of the document to delete.
|
|
219
|
+
|
|
211
220
|
"""
|
|
212
221
|
assert self.engine is not None, "Database connection is not initialized."
|
|
213
222
|
|
|
214
|
-
async with self.get_session() as session:
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
document = result.scalar_one_or_none()
|
|
223
|
+
async with self.get_session() as session, session.begin():
|
|
224
|
+
query = select(Document).where(col(Document.doc_id) == doc_id)
|
|
225
|
+
result = await session.execute(query)
|
|
226
|
+
document = result.scalar_one_or_none()
|
|
219
227
|
|
|
220
|
-
|
|
221
|
-
|
|
228
|
+
if document:
|
|
229
|
+
await session.delete(document)
|
|
222
230
|
|
|
223
231
|
async def get_document_by_doc_id(self, doc_id: str):
|
|
224
232
|
"""Retrieve a document by its doc_id.
|
|
@@ -228,6 +236,7 @@ class DocumentStorage:
|
|
|
228
236
|
|
|
229
237
|
Returns:
|
|
230
238
|
dict: The document data or None if not found.
|
|
239
|
+
|
|
231
240
|
"""
|
|
232
241
|
assert self.engine is not None, "Database connection is not initialized."
|
|
233
242
|
|
|
@@ -246,46 +255,46 @@ class DocumentStorage:
|
|
|
246
255
|
Args:
|
|
247
256
|
doc_id (str): The doc_id.
|
|
248
257
|
new_text (str): The new text to update the document with.
|
|
258
|
+
|
|
249
259
|
"""
|
|
250
260
|
assert self.engine is not None, "Database connection is not initialized."
|
|
251
261
|
|
|
252
|
-
async with self.get_session() as session:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
document = result.scalar_one_or_none()
|
|
262
|
+
async with self.get_session() as session, session.begin():
|
|
263
|
+
query = select(Document).where(col(Document.doc_id) == doc_id)
|
|
264
|
+
result = await session.execute(query)
|
|
265
|
+
document = result.scalar_one_or_none()
|
|
257
266
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
267
|
+
if document:
|
|
268
|
+
document.text = new_text
|
|
269
|
+
document.updated_at = datetime.now()
|
|
270
|
+
session.add(document)
|
|
262
271
|
|
|
263
272
|
async def delete_documents(self, metadata_filters: dict):
|
|
264
273
|
"""Delete documents by their metadata filters.
|
|
265
274
|
|
|
266
275
|
Args:
|
|
267
276
|
metadata_filters (dict): The metadata filters to apply.
|
|
277
|
+
|
|
268
278
|
"""
|
|
269
279
|
if self.engine is None:
|
|
270
280
|
logger.warning(
|
|
271
|
-
"Database connection is not initialized, skipping delete operation"
|
|
281
|
+
"Database connection is not initialized, skipping delete operation",
|
|
272
282
|
)
|
|
273
283
|
return
|
|
274
284
|
|
|
275
|
-
async with self.get_session() as session:
|
|
276
|
-
|
|
277
|
-
query = select(Document)
|
|
285
|
+
async with self.get_session() as session, session.begin():
|
|
286
|
+
query = select(Document)
|
|
278
287
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
288
|
+
for key, val in metadata_filters.items():
|
|
289
|
+
query = query.where(
|
|
290
|
+
text(f"json_extract(metadata, '$.{key}') = :filter_{key}"),
|
|
291
|
+
).params(**{f"filter_{key}": val})
|
|
283
292
|
|
|
284
|
-
|
|
285
|
-
|
|
293
|
+
result = await session.execute(query)
|
|
294
|
+
documents = result.scalars().all()
|
|
286
295
|
|
|
287
|
-
|
|
288
|
-
|
|
296
|
+
for doc in documents:
|
|
297
|
+
await session.delete(doc)
|
|
289
298
|
|
|
290
299
|
async def count_documents(self, metadata_filters: dict | None = None) -> int:
|
|
291
300
|
"""Count documents in the database.
|
|
@@ -295,6 +304,7 @@ class DocumentStorage:
|
|
|
295
304
|
|
|
296
305
|
Returns:
|
|
297
306
|
int: The count of documents.
|
|
307
|
+
|
|
298
308
|
"""
|
|
299
309
|
if self.engine is None:
|
|
300
310
|
logger.warning("Database connection is not initialized, returning 0")
|
|
@@ -306,7 +316,7 @@ class DocumentStorage:
|
|
|
306
316
|
if metadata_filters:
|
|
307
317
|
for key, val in metadata_filters.items():
|
|
308
318
|
query = query.where(
|
|
309
|
-
text(f"json_extract(metadata, '$.{key}') = :filter_{key}")
|
|
319
|
+
text(f"json_extract(metadata, '$.{key}') = :filter_{key}"),
|
|
310
320
|
).params(**{f"filter_{key}": val})
|
|
311
321
|
|
|
312
322
|
result = await session.execute(query)
|
|
@@ -318,12 +328,13 @@ class DocumentStorage:
|
|
|
318
328
|
|
|
319
329
|
Returns:
|
|
320
330
|
list: A list of user IDs.
|
|
331
|
+
|
|
321
332
|
"""
|
|
322
333
|
assert self.engine is not None, "Database connection is not initialized."
|
|
323
334
|
|
|
324
335
|
async with self.get_session() as session:
|
|
325
336
|
query = text(
|
|
326
|
-
"SELECT DISTINCT user_id FROM documents WHERE user_id IS NOT NULL"
|
|
337
|
+
"SELECT DISTINCT user_id FROM documents WHERE user_id IS NOT NULL",
|
|
327
338
|
)
|
|
328
339
|
result = await session.execute(query)
|
|
329
340
|
rows = result.fetchall()
|
|
@@ -337,6 +348,7 @@ class DocumentStorage:
|
|
|
337
348
|
|
|
338
349
|
Returns:
|
|
339
350
|
dict: The converted dictionary.
|
|
351
|
+
|
|
340
352
|
"""
|
|
341
353
|
return {
|
|
342
354
|
"id": document.id,
|
|
@@ -361,6 +373,7 @@ class DocumentStorage:
|
|
|
361
373
|
dict: The converted dictionary.
|
|
362
374
|
|
|
363
375
|
Note: This method is kept for backward compatibility but is no longer used internally.
|
|
376
|
+
|
|
364
377
|
"""
|
|
365
378
|
return {
|
|
366
379
|
"id": row[0],
|
|
@@ -2,9 +2,10 @@ try:
|
|
|
2
2
|
import faiss
|
|
3
3
|
except ModuleNotFoundError:
|
|
4
4
|
raise ImportError(
|
|
5
|
-
"faiss 未安装。请使用 'pip install faiss-cpu' 或 'pip install faiss-gpu' 安装。"
|
|
5
|
+
"faiss 未安装。请使用 'pip install faiss-cpu' 或 'pip install faiss-gpu' 安装。",
|
|
6
6
|
)
|
|
7
7
|
import os
|
|
8
|
+
|
|
8
9
|
import numpy as np
|
|
9
10
|
|
|
10
11
|
|
|
@@ -27,11 +28,12 @@ class EmbeddingStorage:
|
|
|
27
28
|
id (int): 向量的ID
|
|
28
29
|
Raises:
|
|
29
30
|
ValueError: 如果向量的维度与存储的维度不匹配
|
|
31
|
+
|
|
30
32
|
"""
|
|
31
33
|
assert self.index is not None, "FAISS index is not initialized."
|
|
32
34
|
if vector.shape[0] != self.dimension:
|
|
33
35
|
raise ValueError(
|
|
34
|
-
f"向量维度不匹配, 期望: {self.dimension}, 实际: {vector.shape[0]}"
|
|
36
|
+
f"向量维度不匹配, 期望: {self.dimension}, 实际: {vector.shape[0]}",
|
|
35
37
|
)
|
|
36
38
|
self.index.add_with_ids(vector.reshape(1, -1), np.array([id]))
|
|
37
39
|
await self.save_index()
|
|
@@ -44,11 +46,12 @@ class EmbeddingStorage:
|
|
|
44
46
|
ids (list[int]): 向量的ID列表
|
|
45
47
|
Raises:
|
|
46
48
|
ValueError: 如果向量的维度与存储的维度不匹配
|
|
49
|
+
|
|
47
50
|
"""
|
|
48
51
|
assert self.index is not None, "FAISS index is not initialized."
|
|
49
52
|
if vectors.shape[1] != self.dimension:
|
|
50
53
|
raise ValueError(
|
|
51
|
-
f"向量维度不匹配, 期望: {self.dimension}, 实际: {vectors.shape[1]}"
|
|
54
|
+
f"向量维度不匹配, 期望: {self.dimension}, 实际: {vectors.shape[1]}",
|
|
52
55
|
)
|
|
53
56
|
self.index.add_with_ids(vectors, np.array(ids))
|
|
54
57
|
await self.save_index()
|
|
@@ -61,6 +64,7 @@ class EmbeddingStorage:
|
|
|
61
64
|
k (int): 返回的最相似向量的数量
|
|
62
65
|
Returns:
|
|
63
66
|
tuple: (距离, 索引)
|
|
67
|
+
|
|
64
68
|
"""
|
|
65
69
|
assert self.index is not None, "FAISS index is not initialized."
|
|
66
70
|
faiss.normalize_L2(vector)
|
|
@@ -72,6 +76,7 @@ class EmbeddingStorage:
|
|
|
72
76
|
|
|
73
77
|
Args:
|
|
74
78
|
ids (list[int]): 要删除的向量ID列表
|
|
79
|
+
|
|
75
80
|
"""
|
|
76
81
|
assert self.index is not None, "FAISS index is not initialized."
|
|
77
82
|
id_array = np.array(ids, dtype=np.int64)
|
|
@@ -83,5 +88,6 @@ class EmbeddingStorage:
|
|
|
83
88
|
|
|
84
89
|
Args:
|
|
85
90
|
path (str): 保存索引的路径
|
|
91
|
+
|
|
86
92
|
"""
|
|
87
93
|
faiss.write_index(self.index, self.path)
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import uuid
|
|
2
1
|
import time
|
|
2
|
+
import uuid
|
|
3
|
+
|
|
3
4
|
import numpy as np
|
|
5
|
+
|
|
6
|
+
from astrbot import logger
|
|
7
|
+
from astrbot.core.provider.provider import EmbeddingProvider, RerankProvider
|
|
8
|
+
|
|
9
|
+
from ..base import BaseVecDB, Result
|
|
4
10
|
from .document_storage import DocumentStorage
|
|
5
11
|
from .embedding_storage import EmbeddingStorage
|
|
6
|
-
from ..base import Result, BaseVecDB
|
|
7
|
-
from astrbot.core.provider.provider import EmbeddingProvider
|
|
8
|
-
from astrbot.core.provider.provider import RerankProvider
|
|
9
|
-
from astrbot import logger
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class FaissVecDB(BaseVecDB):
|
|
13
|
-
"""
|
|
14
|
-
A class to represent a vector database.
|
|
15
|
-
"""
|
|
15
|
+
"""A class to represent a vector database."""
|
|
16
16
|
|
|
17
17
|
def __init__(
|
|
18
18
|
self,
|
|
@@ -26,7 +26,8 @@ class FaissVecDB(BaseVecDB):
|
|
|
26
26
|
self.embedding_provider = embedding_provider
|
|
27
27
|
self.document_storage = DocumentStorage(doc_store_path)
|
|
28
28
|
self.embedding_storage = EmbeddingStorage(
|
|
29
|
-
embedding_provider.get_dim(),
|
|
29
|
+
embedding_provider.get_dim(),
|
|
30
|
+
index_store_path,
|
|
30
31
|
)
|
|
31
32
|
self.embedding_provider = embedding_provider
|
|
32
33
|
self.rerank_provider = rerank_provider
|
|
@@ -35,11 +36,12 @@ class FaissVecDB(BaseVecDB):
|
|
|
35
36
|
await self.document_storage.initialize()
|
|
36
37
|
|
|
37
38
|
async def insert(
|
|
38
|
-
self,
|
|
39
|
+
self,
|
|
40
|
+
content: str,
|
|
41
|
+
metadata: dict | None = None,
|
|
42
|
+
id: str | None = None,
|
|
39
43
|
) -> int:
|
|
40
|
-
"""
|
|
41
|
-
插入一条文本和其对应向量,自动生成 ID 并保持一致性。
|
|
42
|
-
"""
|
|
44
|
+
"""插入一条文本和其对应向量,自动生成 ID 并保持一致性。"""
|
|
43
45
|
metadata = metadata or {}
|
|
44
46
|
str_id = id or str(uuid.uuid4()) # 使用 UUID 作为原始 ID
|
|
45
47
|
|
|
@@ -63,11 +65,11 @@ class FaissVecDB(BaseVecDB):
|
|
|
63
65
|
max_retries: int = 3,
|
|
64
66
|
progress_callback=None,
|
|
65
67
|
) -> list[int]:
|
|
66
|
-
"""
|
|
67
|
-
批量插入文本和其对应向量,自动生成 ID 并保持一致性。
|
|
68
|
+
"""批量插入文本和其对应向量,自动生成 ID 并保持一致性。
|
|
68
69
|
|
|
69
70
|
Args:
|
|
70
71
|
progress_callback: 进度回调函数,接收参数 (current, total)
|
|
72
|
+
|
|
71
73
|
"""
|
|
72
74
|
metadatas = metadatas or [{} for _ in contents]
|
|
73
75
|
ids = ids or [str(uuid.uuid4()) for _ in contents]
|
|
@@ -83,12 +85,14 @@ class FaissVecDB(BaseVecDB):
|
|
|
83
85
|
)
|
|
84
86
|
end = time.time()
|
|
85
87
|
logger.debug(
|
|
86
|
-
f"Generated embeddings for {len(contents)} contents in {end - start:.2f} seconds."
|
|
88
|
+
f"Generated embeddings for {len(contents)} contents in {end - start:.2f} seconds.",
|
|
87
89
|
)
|
|
88
90
|
|
|
89
91
|
# 使用 DocumentStorage 的批量插入方法
|
|
90
92
|
int_ids = await self.document_storage.insert_documents_batch(
|
|
91
|
-
ids,
|
|
93
|
+
ids,
|
|
94
|
+
contents,
|
|
95
|
+
metadatas,
|
|
92
96
|
)
|
|
93
97
|
|
|
94
98
|
# 批量插入向量到 FAISS
|
|
@@ -104,8 +108,7 @@ class FaissVecDB(BaseVecDB):
|
|
|
104
108
|
rerank: bool = False,
|
|
105
109
|
metadata_filters: dict | None = None,
|
|
106
110
|
) -> list[Result]:
|
|
107
|
-
"""
|
|
108
|
-
搜索最相似的文档。
|
|
111
|
+
"""搜索最相似的文档。
|
|
109
112
|
|
|
110
113
|
Args:
|
|
111
114
|
query (str): 查询文本
|
|
@@ -116,6 +119,7 @@ class FaissVecDB(BaseVecDB):
|
|
|
116
119
|
|
|
117
120
|
Returns:
|
|
118
121
|
List[Result]: 查询结果
|
|
122
|
+
|
|
119
123
|
"""
|
|
120
124
|
embedding = await self.embedding_provider.get_embedding(query)
|
|
121
125
|
scores, indices = await self.embedding_storage.search(
|
|
@@ -128,7 +132,8 @@ class FaissVecDB(BaseVecDB):
|
|
|
128
132
|
scores[0] = 1.0 - (scores[0] / 2.0)
|
|
129
133
|
# NOTE: maybe the size is less than k.
|
|
130
134
|
fetched_docs = await self.document_storage.get_documents(
|
|
131
|
-
metadata_filters=metadata_filters or {},
|
|
135
|
+
metadata_filters=metadata_filters or {},
|
|
136
|
+
ids=indices[0],
|
|
132
137
|
)
|
|
133
138
|
if not fetched_docs:
|
|
134
139
|
return []
|
|
@@ -149,7 +154,9 @@ class FaissVecDB(BaseVecDB):
|
|
|
149
154
|
documents = [doc.data["text"] for doc in top_k_results]
|
|
150
155
|
reranked_results = await self.rerank_provider.rerank(query, documents)
|
|
151
156
|
reranked_results = sorted(
|
|
152
|
-
reranked_results,
|
|
157
|
+
reranked_results,
|
|
158
|
+
key=lambda x: x.relevance_score,
|
|
159
|
+
reverse=True,
|
|
153
160
|
)
|
|
154
161
|
top_k_results = [
|
|
155
162
|
top_k_results[reranked_result.index]
|
|
@@ -159,9 +166,7 @@ class FaissVecDB(BaseVecDB):
|
|
|
159
166
|
return top_k_results
|
|
160
167
|
|
|
161
168
|
async def delete(self, doc_id: str):
|
|
162
|
-
"""
|
|
163
|
-
删除一条文档块(chunk)
|
|
164
|
-
"""
|
|
169
|
+
"""删除一条文档块(chunk)"""
|
|
165
170
|
# 获得对应的 int id
|
|
166
171
|
result = await self.document_storage.get_document_by_doc_id(doc_id)
|
|
167
172
|
int_id = result["id"] if result else None
|
|
@@ -176,23 +181,23 @@ class FaissVecDB(BaseVecDB):
|
|
|
176
181
|
await self.document_storage.close()
|
|
177
182
|
|
|
178
183
|
async def count_documents(self, metadata_filter: dict | None = None) -> int:
|
|
179
|
-
"""
|
|
180
|
-
计算文档数量
|
|
184
|
+
"""计算文档数量
|
|
181
185
|
|
|
182
186
|
Args:
|
|
183
187
|
metadata_filter (dict | None): 元数据过滤器
|
|
188
|
+
|
|
184
189
|
"""
|
|
185
190
|
count = await self.document_storage.count_documents(
|
|
186
|
-
metadata_filters=metadata_filter or {}
|
|
191
|
+
metadata_filters=metadata_filter or {},
|
|
187
192
|
)
|
|
188
193
|
return count
|
|
189
194
|
|
|
190
195
|
async def delete_documents(self, metadata_filters: dict):
|
|
191
|
-
"""
|
|
192
|
-
根据元数据过滤器删除文档
|
|
193
|
-
"""
|
|
196
|
+
"""根据元数据过滤器删除文档"""
|
|
194
197
|
docs = await self.document_storage.get_documents(
|
|
195
|
-
metadata_filters=metadata_filters,
|
|
198
|
+
metadata_filters=metadata_filters,
|
|
199
|
+
offset=None,
|
|
200
|
+
limit=None,
|
|
196
201
|
)
|
|
197
202
|
doc_ids: list[int] = [doc["id"] for doc in docs]
|
|
198
203
|
await self.embedding_storage.delete(doc_ids)
|
astrbot/core/event_bus.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
事件总线, 用于处理事件的分发和处理
|
|
1
|
+
"""事件总线, 用于处理事件的分发和处理
|
|
3
2
|
事件总线是一个异步队列, 用于接收各种消息事件, 并将其发送到Scheduler调度器进行处理
|
|
4
3
|
其中包含了一个无限循环的调度函数, 用于从事件队列中获取新的事件, 并创建一个新的异步任务来执行管道调度器的处理逻辑
|
|
5
4
|
|
|
@@ -13,10 +12,12 @@ class:
|
|
|
13
12
|
|
|
14
13
|
import asyncio
|
|
15
14
|
from asyncio import Queue
|
|
16
|
-
|
|
15
|
+
|
|
17
16
|
from astrbot.core import logger
|
|
18
|
-
from .platform import AstrMessageEvent
|
|
19
17
|
from astrbot.core.astrbot_config_mgr import AstrBotConfigManager
|
|
18
|
+
from astrbot.core.pipeline.scheduler import PipelineScheduler
|
|
19
|
+
|
|
20
|
+
from .platform import AstrMessageEvent
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class EventBus:
|
|
@@ -46,14 +47,15 @@ class EventBus:
|
|
|
46
47
|
|
|
47
48
|
Args:
|
|
48
49
|
event (AstrMessageEvent): 事件对象
|
|
50
|
+
|
|
49
51
|
"""
|
|
50
52
|
# 如果有发送者名称: [平台名] 发送者名称/发送者ID: 消息概要
|
|
51
53
|
if event.get_sender_name():
|
|
52
54
|
logger.info(
|
|
53
|
-
f"[{conf_name}] [{event.get_platform_id()}({event.get_platform_name()})] {event.get_sender_name()}/{event.get_sender_id()}: {event.get_message_outline()}"
|
|
55
|
+
f"[{conf_name}] [{event.get_platform_id()}({event.get_platform_name()})] {event.get_sender_name()}/{event.get_sender_id()}: {event.get_message_outline()}",
|
|
54
56
|
)
|
|
55
57
|
# 没有发送者名称: [平台名] 发送者ID: 消息概要
|
|
56
58
|
else:
|
|
57
59
|
logger.info(
|
|
58
|
-
f"[{conf_name}] [{event.get_platform_id()}({event.get_platform_name()})] {event.get_sender_id()}: {event.get_message_outline()}"
|
|
60
|
+
f"[{conf_name}] [{event.get_platform_id()}({event.get_platform_name()})] {event.get_sender_id()}: {event.get_message_outline()}",
|
|
59
61
|
)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import os
|
|
3
|
-
import uuid
|
|
4
|
-
import time
|
|
5
|
-
from urllib.parse import urlparse, unquote
|
|
6
3
|
import platform
|
|
4
|
+
import time
|
|
5
|
+
import uuid
|
|
6
|
+
from urllib.parse import unquote, urlparse
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class FileTokenService:
|
|
@@ -40,8 +40,8 @@ class FileTokenService:
|
|
|
40
40
|
|
|
41
41
|
Raises:
|
|
42
42
|
FileNotFoundError: 当路径不存在时抛出
|
|
43
|
-
"""
|
|
44
43
|
|
|
44
|
+
"""
|
|
45
45
|
# 处理 file:///
|
|
46
46
|
try:
|
|
47
47
|
parsed_uri = urlparse(file_path)
|
|
@@ -61,7 +61,7 @@ class FileTokenService:
|
|
|
61
61
|
|
|
62
62
|
if not os.path.exists(local_path):
|
|
63
63
|
raise FileNotFoundError(
|
|
64
|
-
f"文件不存在: {local_path} (原始输入: {file_path})"
|
|
64
|
+
f"文件不存在: {local_path} (原始输入: {file_path})",
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
file_token = str(uuid.uuid4())
|
|
@@ -84,6 +84,7 @@ class FileTokenService:
|
|
|
84
84
|
Raises:
|
|
85
85
|
KeyError: 当令牌不存在或已过期时抛出
|
|
86
86
|
FileNotFoundError: 当文件本身已被删除时抛出
|
|
87
|
+
|
|
87
88
|
"""
|
|
88
89
|
async with self.lock:
|
|
89
90
|
await self._cleanup_expired_tokens()
|
astrbot/core/initial_loader.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
AstrBot 启动器,负责初始化和启动核心组件和仪表板服务器。
|
|
1
|
+
"""AstrBot 启动器,负责初始化和启动核心组件和仪表板服务器。
|
|
3
2
|
|
|
4
3
|
工作流程:
|
|
5
4
|
1. 初始化核心生命周期, 传递数据库和日志代理实例到核心生命周期
|
|
@@ -8,10 +7,10 @@ AstrBot 启动器,负责初始化和启动核心组件和仪表板服务器。
|
|
|
8
7
|
|
|
9
8
|
import asyncio
|
|
10
9
|
import traceback
|
|
11
|
-
|
|
10
|
+
|
|
11
|
+
from astrbot.core import LogBroker, logger
|
|
12
12
|
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
|
|
13
13
|
from astrbot.core.db import BaseDatabase
|
|
14
|
-
from astrbot.core import LogBroker
|
|
15
14
|
from astrbot.dashboard.server import AstrBotDashboard
|
|
16
15
|
|
|
17
16
|
|
|
@@ -39,7 +38,10 @@ class InitialLoader:
|
|
|
39
38
|
webui_dir = self.webui_dir
|
|
40
39
|
|
|
41
40
|
self.dashboard_server = AstrBotDashboard(
|
|
42
|
-
core_lifecycle,
|
|
41
|
+
core_lifecycle,
|
|
42
|
+
self.db,
|
|
43
|
+
core_lifecycle.dashboard_shutdown_event,
|
|
44
|
+
webui_dir,
|
|
43
45
|
)
|
|
44
46
|
|
|
45
47
|
coro = self.dashboard_server.run()
|