AstrBot 3.5.6__py3-none-any.whl → 4.7.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/__init__.py +16 -4
- astrbot/api/all.py +2 -1
- astrbot/api/event/__init__.py +5 -6
- astrbot/api/event/filter/__init__.py +37 -34
- astrbot/api/platform/__init__.py +7 -8
- astrbot/api/provider/__init__.py +8 -7
- astrbot/api/star/__init__.py +3 -4
- astrbot/api/util/__init__.py +2 -2
- astrbot/cli/__init__.py +1 -0
- astrbot/cli/__main__.py +18 -197
- astrbot/cli/commands/__init__.py +6 -0
- astrbot/cli/commands/cmd_conf.py +209 -0
- astrbot/cli/commands/cmd_init.py +56 -0
- astrbot/cli/commands/cmd_plug.py +245 -0
- astrbot/cli/commands/cmd_run.py +62 -0
- astrbot/cli/utils/__init__.py +18 -0
- astrbot/cli/utils/basic.py +76 -0
- astrbot/cli/utils/plugin.py +246 -0
- astrbot/cli/utils/version_comparator.py +90 -0
- astrbot/core/__init__.py +17 -19
- astrbot/core/agent/agent.py +14 -0
- astrbot/core/agent/handoff.py +38 -0
- astrbot/core/agent/hooks.py +30 -0
- astrbot/core/agent/mcp_client.py +385 -0
- astrbot/core/agent/message.py +175 -0
- astrbot/core/agent/response.py +14 -0
- astrbot/core/agent/run_context.py +22 -0
- astrbot/core/agent/runners/__init__.py +3 -0
- astrbot/core/agent/runners/base.py +65 -0
- astrbot/core/agent/runners/coze/coze_agent_runner.py +367 -0
- astrbot/core/agent/runners/coze/coze_api_client.py +324 -0
- astrbot/core/agent/runners/dashscope/dashscope_agent_runner.py +403 -0
- astrbot/core/agent/runners/dify/dify_agent_runner.py +336 -0
- astrbot/core/agent/runners/dify/dify_api_client.py +195 -0
- astrbot/core/agent/runners/tool_loop_agent_runner.py +400 -0
- astrbot/core/agent/tool.py +285 -0
- astrbot/core/agent/tool_executor.py +17 -0
- astrbot/core/astr_agent_context.py +19 -0
- astrbot/core/astr_agent_hooks.py +36 -0
- astrbot/core/astr_agent_run_util.py +80 -0
- astrbot/core/astr_agent_tool_exec.py +246 -0
- astrbot/core/astrbot_config_mgr.py +275 -0
- astrbot/core/config/__init__.py +2 -2
- astrbot/core/config/astrbot_config.py +60 -20
- astrbot/core/config/default.py +1972 -453
- astrbot/core/config/i18n_utils.py +110 -0
- astrbot/core/conversation_mgr.py +285 -75
- astrbot/core/core_lifecycle.py +167 -62
- astrbot/core/db/__init__.py +305 -102
- astrbot/core/db/migration/helper.py +69 -0
- astrbot/core/db/migration/migra_3_to_4.py +357 -0
- astrbot/core/db/migration/migra_45_to_46.py +44 -0
- astrbot/core/db/migration/migra_webchat_session.py +131 -0
- astrbot/core/db/migration/shared_preferences_v3.py +48 -0
- astrbot/core/db/migration/sqlite_v3.py +497 -0
- astrbot/core/db/po.py +259 -55
- astrbot/core/db/sqlite.py +773 -528
- astrbot/core/db/vec_db/base.py +73 -0
- astrbot/core/db/vec_db/faiss_impl/__init__.py +3 -0
- astrbot/core/db/vec_db/faiss_impl/document_storage.py +392 -0
- astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +93 -0
- astrbot/core/db/vec_db/faiss_impl/sqlite_init.sql +17 -0
- astrbot/core/db/vec_db/faiss_impl/vec_db.py +204 -0
- astrbot/core/event_bus.py +26 -22
- astrbot/core/exceptions.py +9 -0
- astrbot/core/file_token_service.py +98 -0
- astrbot/core/initial_loader.py +19 -10
- astrbot/core/knowledge_base/chunking/__init__.py +9 -0
- astrbot/core/knowledge_base/chunking/base.py +25 -0
- astrbot/core/knowledge_base/chunking/fixed_size.py +59 -0
- astrbot/core/knowledge_base/chunking/recursive.py +161 -0
- astrbot/core/knowledge_base/kb_db_sqlite.py +301 -0
- astrbot/core/knowledge_base/kb_helper.py +642 -0
- astrbot/core/knowledge_base/kb_mgr.py +330 -0
- astrbot/core/knowledge_base/models.py +120 -0
- astrbot/core/knowledge_base/parsers/__init__.py +13 -0
- astrbot/core/knowledge_base/parsers/base.py +51 -0
- astrbot/core/knowledge_base/parsers/markitdown_parser.py +26 -0
- astrbot/core/knowledge_base/parsers/pdf_parser.py +101 -0
- astrbot/core/knowledge_base/parsers/text_parser.py +42 -0
- astrbot/core/knowledge_base/parsers/url_parser.py +103 -0
- astrbot/core/knowledge_base/parsers/util.py +13 -0
- astrbot/core/knowledge_base/prompts.py +65 -0
- astrbot/core/knowledge_base/retrieval/__init__.py +14 -0
- astrbot/core/knowledge_base/retrieval/hit_stopwords.txt +767 -0
- astrbot/core/knowledge_base/retrieval/manager.py +276 -0
- astrbot/core/knowledge_base/retrieval/rank_fusion.py +142 -0
- astrbot/core/knowledge_base/retrieval/sparse_retriever.py +136 -0
- astrbot/core/log.py +21 -15
- astrbot/core/message/components.py +413 -287
- astrbot/core/message/message_event_result.py +35 -24
- astrbot/core/persona_mgr.py +192 -0
- astrbot/core/pipeline/__init__.py +14 -14
- 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 +13 -14
- astrbot/core/pipeline/content_safety_check/strategies/keywords.py +2 -1
- astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
- astrbot/core/pipeline/context.py +7 -1
- astrbot/core/pipeline/context_utils.py +107 -0
- astrbot/core/pipeline/preprocess_stage/stage.py +63 -36
- astrbot/core/pipeline/process_stage/method/agent_request.py +48 -0
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +464 -0
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/third_party.py +202 -0
- astrbot/core/pipeline/process_stage/method/star_request.py +26 -32
- astrbot/core/pipeline/process_stage/stage.py +21 -15
- astrbot/core/pipeline/process_stage/utils.py +125 -0
- astrbot/core/pipeline/rate_limit_check/stage.py +34 -36
- astrbot/core/pipeline/respond/stage.py +142 -101
- astrbot/core/pipeline/result_decorate/stage.py +124 -57
- astrbot/core/pipeline/scheduler.py +21 -16
- astrbot/core/pipeline/session_status_check/stage.py +37 -0
- astrbot/core/pipeline/stage.py +11 -76
- astrbot/core/pipeline/waking_check/stage.py +69 -33
- astrbot/core/pipeline/whitelist_check/stage.py +10 -7
- astrbot/core/platform/__init__.py +6 -6
- astrbot/core/platform/astr_message_event.py +107 -129
- astrbot/core/platform/astrbot_message.py +32 -12
- astrbot/core/platform/manager.py +62 -18
- astrbot/core/platform/message_session.py +30 -0
- astrbot/core/platform/platform.py +16 -24
- astrbot/core/platform/platform_metadata.py +9 -4
- astrbot/core/platform/register.py +12 -7
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +136 -60
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +126 -46
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +63 -31
- astrbot/core/platform/sources/dingtalk/dingtalk_event.py +30 -26
- astrbot/core/platform/sources/discord/client.py +129 -0
- astrbot/core/platform/sources/discord/components.py +139 -0
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +473 -0
- astrbot/core/platform/sources/discord/discord_platform_event.py +313 -0
- astrbot/core/platform/sources/lark/lark_adapter.py +27 -18
- astrbot/core/platform/sources/lark/lark_event.py +39 -13
- astrbot/core/platform/sources/misskey/misskey_adapter.py +770 -0
- astrbot/core/platform/sources/misskey/misskey_api.py +964 -0
- astrbot/core/platform/sources/misskey/misskey_event.py +163 -0
- astrbot/core/platform/sources/misskey/misskey_utils.py +550 -0
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +149 -33
- 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 +14 -8
- astrbot/core/platform/sources/satori/satori_adapter.py +792 -0
- astrbot/core/platform/sources/satori/satori_event.py +432 -0
- astrbot/core/platform/sources/slack/client.py +164 -0
- astrbot/core/platform/sources/slack/slack_adapter.py +416 -0
- astrbot/core/platform/sources/slack/slack_event.py +253 -0
- astrbot/core/platform/sources/telegram/tg_adapter.py +100 -43
- astrbot/core/platform/sources/telegram/tg_event.py +136 -36
- astrbot/core/platform/sources/webchat/webchat_adapter.py +72 -22
- astrbot/core/platform/sources/webchat/webchat_event.py +46 -22
- astrbot/core/platform/sources/webchat/webchat_queue_mgr.py +35 -0
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +926 -0
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +178 -0
- astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +159 -0
- astrbot/core/platform/sources/wecom/wecom_adapter.py +169 -27
- astrbot/core/platform/sources/wecom/wecom_event.py +162 -77
- astrbot/core/platform/sources/wecom/wecom_kf.py +279 -0
- astrbot/core/platform/sources/wecom/wecom_kf_message.py +196 -0
- astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +297 -0
- astrbot/core/platform/sources/wecom_ai_bot/__init__.py +15 -0
- astrbot/core/platform/sources/wecom_ai_bot/ierror.py +19 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +472 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +417 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +152 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +153 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +168 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +209 -0
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +306 -0
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +186 -0
- astrbot/core/platform_message_history_mgr.py +49 -0
- astrbot/core/provider/__init__.py +2 -3
- astrbot/core/provider/entites.py +8 -8
- astrbot/core/provider/entities.py +154 -98
- astrbot/core/provider/func_tool_manager.py +446 -458
- astrbot/core/provider/manager.py +345 -207
- astrbot/core/provider/provider.py +188 -73
- astrbot/core/provider/register.py +9 -7
- astrbot/core/provider/sources/anthropic_source.py +295 -115
- astrbot/core/provider/sources/azure_tts_source.py +224 -0
- astrbot/core/provider/sources/bailian_rerank_source.py +236 -0
- astrbot/core/provider/sources/dashscope_tts.py +138 -14
- astrbot/core/provider/sources/edge_tts_source.py +24 -19
- astrbot/core/provider/sources/fishaudio_tts_api_source.py +58 -13
- astrbot/core/provider/sources/gemini_embedding_source.py +61 -0
- astrbot/core/provider/sources/gemini_source.py +310 -132
- astrbot/core/provider/sources/gemini_tts_source.py +81 -0
- astrbot/core/provider/sources/groq_source.py +15 -0
- astrbot/core/provider/sources/gsv_selfhosted_source.py +151 -0
- astrbot/core/provider/sources/gsvi_tts_source.py +14 -7
- astrbot/core/provider/sources/minimax_tts_api_source.py +159 -0
- astrbot/core/provider/sources/openai_embedding_source.py +40 -0
- astrbot/core/provider/sources/openai_source.py +241 -145
- astrbot/core/provider/sources/openai_tts_api_source.py +18 -7
- astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
- astrbot/core/provider/sources/vllm_rerank_source.py +71 -0
- astrbot/core/provider/sources/volcengine_tts.py +115 -0
- astrbot/core/provider/sources/whisper_api_source.py +18 -13
- astrbot/core/provider/sources/whisper_selfhosted_source.py +19 -12
- astrbot/core/provider/sources/xinference_rerank_source.py +116 -0
- astrbot/core/provider/sources/xinference_stt_provider.py +197 -0
- astrbot/core/provider/sources/zhipu_source.py +6 -73
- astrbot/core/star/__init__.py +43 -11
- astrbot/core/star/config.py +17 -18
- astrbot/core/star/context.py +362 -138
- astrbot/core/star/filter/__init__.py +4 -3
- astrbot/core/star/filter/command.py +111 -35
- astrbot/core/star/filter/command_group.py +46 -34
- 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 +45 -12
- astrbot/core/star/filter/regex.py +4 -2
- astrbot/core/star/register/__init__.py +19 -15
- astrbot/core/star/register/star.py +41 -13
- astrbot/core/star/register/star_handler.py +236 -86
- astrbot/core/star/session_llm_manager.py +280 -0
- astrbot/core/star/session_plugin_manager.py +170 -0
- astrbot/core/star/star.py +36 -43
- astrbot/core/star/star_handler.py +47 -85
- astrbot/core/star/star_manager.py +442 -260
- astrbot/core/star/star_tools.py +167 -45
- astrbot/core/star/updator.py +17 -20
- astrbot/core/umop_config_router.py +106 -0
- astrbot/core/updator.py +38 -13
- astrbot/core/utils/astrbot_path.py +39 -0
- astrbot/core/utils/command_parser.py +1 -1
- astrbot/core/utils/io.py +119 -60
- astrbot/core/utils/log_pipe.py +1 -1
- astrbot/core/utils/metrics.py +11 -10
- astrbot/core/utils/migra_helper.py +73 -0
- astrbot/core/utils/path_util.py +63 -62
- astrbot/core/utils/pip_installer.py +37 -15
- astrbot/core/utils/session_lock.py +29 -0
- astrbot/core/utils/session_waiter.py +19 -20
- astrbot/core/utils/shared_preferences.py +174 -34
- astrbot/core/utils/t2i/__init__.py +4 -1
- astrbot/core/utils/t2i/local_strategy.py +386 -238
- astrbot/core/utils/t2i/network_strategy.py +109 -49
- astrbot/core/utils/t2i/renderer.py +29 -14
- astrbot/core/utils/t2i/template/astrbot_powershell.html +184 -0
- astrbot/core/utils/t2i/template_manager.py +111 -0
- astrbot/core/utils/tencent_record_helper.py +115 -1
- astrbot/core/utils/version_comparator.py +10 -13
- astrbot/core/zip_updator.py +112 -65
- astrbot/dashboard/routes/__init__.py +20 -13
- astrbot/dashboard/routes/auth.py +20 -9
- astrbot/dashboard/routes/chat.py +297 -141
- astrbot/dashboard/routes/config.py +652 -55
- astrbot/dashboard/routes/conversation.py +107 -37
- astrbot/dashboard/routes/file.py +26 -0
- astrbot/dashboard/routes/knowledge_base.py +1244 -0
- astrbot/dashboard/routes/log.py +27 -2
- astrbot/dashboard/routes/persona.py +202 -0
- astrbot/dashboard/routes/plugin.py +197 -139
- astrbot/dashboard/routes/route.py +27 -7
- astrbot/dashboard/routes/session_management.py +354 -0
- astrbot/dashboard/routes/stat.py +85 -18
- astrbot/dashboard/routes/static_file.py +5 -2
- astrbot/dashboard/routes/t2i.py +233 -0
- astrbot/dashboard/routes/tools.py +184 -120
- astrbot/dashboard/routes/update.py +59 -36
- astrbot/dashboard/server.py +96 -36
- astrbot/dashboard/utils.py +165 -0
- astrbot-4.7.0.dist-info/METADATA +294 -0
- astrbot-4.7.0.dist-info/RECORD +274 -0
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/WHEEL +1 -1
- astrbot/core/db/plugin/sqlite_impl.py +0 -112
- astrbot/core/db/sqlite_init.sql +0 -50
- astrbot/core/pipeline/platform_compatibility/stage.py +0 -56
- astrbot/core/pipeline/process_stage/method/llm_request.py +0 -606
- astrbot/core/platform/sources/gewechat/client.py +0 -806
- astrbot/core/platform/sources/gewechat/downloader.py +0 -55
- astrbot/core/platform/sources/gewechat/gewechat_event.py +0 -255
- astrbot/core/platform/sources/gewechat/gewechat_platform_adapter.py +0 -103
- astrbot/core/platform/sources/gewechat/xml_data_parser.py +0 -110
- astrbot/core/provider/sources/dashscope_source.py +0 -203
- astrbot/core/provider/sources/dify_source.py +0 -281
- astrbot/core/provider/sources/llmtuner_source.py +0 -132
- astrbot/core/rag/embedding/openai_source.py +0 -20
- astrbot/core/rag/knowledge_db_mgr.py +0 -94
- astrbot/core/rag/store/__init__.py +0 -9
- astrbot/core/rag/store/chroma_db.py +0 -42
- astrbot/core/utils/dify_api_client.py +0 -152
- astrbot-3.5.6.dist-info/METADATA +0 -249
- astrbot-3.5.6.dist-info/RECORD +0 -158
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/entry_points.txt +0 -0
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"""本地 Agent 模式的 AstrBot 插件调用 Stage"""
|
|
2
|
+
|
|
3
|
+
import traceback
|
|
4
|
+
from collections.abc import AsyncGenerator
|
|
5
|
+
from typing import Any
|
|
4
6
|
|
|
5
|
-
from ...context import PipelineContext
|
|
6
|
-
from ..stage import Stage
|
|
7
|
-
from typing import Dict, Any, List, AsyncGenerator, Union
|
|
8
|
-
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
|
9
|
-
from astrbot.core.message.message_event_result import MessageEventResult
|
|
10
7
|
from astrbot.core import logger
|
|
11
|
-
from astrbot.core.
|
|
8
|
+
from astrbot.core.message.message_event_result import MessageEventResult
|
|
9
|
+
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
|
12
10
|
from astrbot.core.star.star import star_map
|
|
13
|
-
import
|
|
11
|
+
from astrbot.core.star.star_handler import StarHandlerMetadata
|
|
12
|
+
|
|
13
|
+
from ...context import PipelineContext, call_handler
|
|
14
|
+
from ..stage import Stage
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class StarRequestSubStage(Stage):
|
|
@@ -21,36 +22,29 @@ class StarRequestSubStage(Stage):
|
|
|
21
22
|
self.ctx = ctx
|
|
22
23
|
|
|
23
24
|
async def process(
|
|
24
|
-
self,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
self,
|
|
26
|
+
event: AstrMessageEvent,
|
|
27
|
+
) -> AsyncGenerator[None, None]:
|
|
28
|
+
activated_handlers: list[StarHandlerMetadata] = event.get_extra(
|
|
29
|
+
"activated_handlers",
|
|
28
30
|
)
|
|
29
|
-
handlers_parsed_params:
|
|
30
|
-
"handlers_parsed_params"
|
|
31
|
+
handlers_parsed_params: dict[str, dict[str, Any]] = event.get_extra(
|
|
32
|
+
"handlers_parsed_params",
|
|
31
33
|
)
|
|
32
34
|
if not handlers_parsed_params:
|
|
33
35
|
handlers_parsed_params = {}
|
|
34
36
|
|
|
35
37
|
for handler in activated_handlers:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
logger.debug(
|
|
42
|
-
f"处理器 {handler.handler_name} 在当前平台不兼容,跳过执行"
|
|
38
|
+
params = handlers_parsed_params.get(handler.handler_full_name, {})
|
|
39
|
+
md = star_map.get(handler.handler_module_path)
|
|
40
|
+
if not md:
|
|
41
|
+
logger.warning(
|
|
42
|
+
f"Cannot find plugin for given handler module path: {handler.handler_module_path}",
|
|
43
43
|
)
|
|
44
44
|
continue
|
|
45
|
-
|
|
46
|
-
params = handlers_parsed_params.get(handler.handler_full_name, {})
|
|
45
|
+
logger.debug(f"plugin -> {md.name} - {handler.handler_name}")
|
|
47
46
|
try:
|
|
48
|
-
|
|
49
|
-
continue
|
|
50
|
-
logger.debug(
|
|
51
|
-
f"plugin -> {star_map.get(handler.handler_module_path).name} - {handler.handler_name}"
|
|
52
|
-
)
|
|
53
|
-
wrapper = self._call_handler(self.ctx, event, handler.handler, **params)
|
|
47
|
+
wrapper = call_handler(event, handler.handler, **params)
|
|
54
48
|
async for ret in wrapper:
|
|
55
49
|
yield ret
|
|
56
50
|
event.clear_result() # 清除上一个 handler 的结果
|
|
@@ -59,7 +53,7 @@ class StarRequestSubStage(Stage):
|
|
|
59
53
|
logger.error(f"Star {handler.handler_full_name} handle error: {e}")
|
|
60
54
|
|
|
61
55
|
if event.is_at_or_wake_command:
|
|
62
|
-
ret = f":(\n\n在调用插件 {
|
|
56
|
+
ret = f":(\n\n在调用插件 {md.name} 的处理函数 {handler.handler_name} 时出现异常:{e}"
|
|
63
57
|
event.set_result(MessageEventResult().message(ret))
|
|
64
58
|
yield
|
|
65
59
|
event.clear_result()
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from
|
|
4
|
-
from .method.llm_request import LLMRequestSubStage
|
|
5
|
-
from .method.star_request import StarRequestSubStage
|
|
1
|
+
from collections.abc import AsyncGenerator
|
|
2
|
+
|
|
3
|
+
from astrbot.core import logger
|
|
6
4
|
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
|
7
|
-
from astrbot.core.star.star_handler import StarHandlerMetadata
|
|
8
5
|
from astrbot.core.provider.entities import ProviderRequest
|
|
9
|
-
from astrbot.core import
|
|
6
|
+
from astrbot.core.star.star_handler import StarHandlerMetadata
|
|
7
|
+
|
|
8
|
+
from ..context import PipelineContext
|
|
9
|
+
from ..stage import Stage, register_stage
|
|
10
|
+
from .method.agent_request import AgentRequestSubStage
|
|
11
|
+
from .method.star_request import StarRequestSubStage
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
@register_stage
|
|
@@ -15,18 +17,22 @@ class ProcessStage(Stage):
|
|
|
15
17
|
self.ctx = ctx
|
|
16
18
|
self.config = ctx.astrbot_config
|
|
17
19
|
self.plugin_manager = ctx.plugin_manager
|
|
18
|
-
self.llm_request_sub_stage = LLMRequestSubStage()
|
|
19
|
-
await self.llm_request_sub_stage.initialize(ctx)
|
|
20
20
|
|
|
21
|
+
# initialize agent sub stage
|
|
22
|
+
self.agent_sub_stage = AgentRequestSubStage()
|
|
23
|
+
await self.agent_sub_stage.initialize(ctx)
|
|
24
|
+
|
|
25
|
+
# initialize star request sub stage
|
|
21
26
|
self.star_request_sub_stage = StarRequestSubStage()
|
|
22
27
|
await self.star_request_sub_stage.initialize(ctx)
|
|
23
28
|
|
|
24
29
|
async def process(
|
|
25
|
-
self,
|
|
26
|
-
|
|
30
|
+
self,
|
|
31
|
+
event: AstrMessageEvent,
|
|
32
|
+
) -> None | AsyncGenerator[None, None]:
|
|
27
33
|
"""处理事件"""
|
|
28
|
-
activated_handlers:
|
|
29
|
-
"activated_handlers"
|
|
34
|
+
activated_handlers: list[StarHandlerMetadata] = event.get_extra(
|
|
35
|
+
"activated_handlers",
|
|
30
36
|
)
|
|
31
37
|
# 有插件 Handler 被激活
|
|
32
38
|
if activated_handlers:
|
|
@@ -36,7 +42,7 @@ class ProcessStage(Stage):
|
|
|
36
42
|
# Handler 的 LLM 请求
|
|
37
43
|
event.set_extra("provider_request", resp)
|
|
38
44
|
_t = False
|
|
39
|
-
async for _ in self.
|
|
45
|
+
async for _ in self.agent_sub_stage.process(event):
|
|
40
46
|
_t = True
|
|
41
47
|
yield
|
|
42
48
|
if not _t:
|
|
@@ -64,5 +70,5 @@ class ProcessStage(Stage):
|
|
|
64
70
|
logger.info("未找到可用的 LLM 提供商,请先前往配置服务提供商。")
|
|
65
71
|
return
|
|
66
72
|
|
|
67
|
-
async for _ in self.
|
|
73
|
+
async for _ in self.agent_sub_stage.process(event):
|
|
68
74
|
yield
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
from pydantic.dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
from astrbot.api import logger, sp
|
|
5
|
+
from astrbot.core.agent.run_context import ContextWrapper
|
|
6
|
+
from astrbot.core.agent.tool import FunctionTool, ToolExecResult
|
|
7
|
+
from astrbot.core.astr_agent_context import AstrAgentContext
|
|
8
|
+
from astrbot.core.star.context import Context
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class KnowledgeBaseQueryTool(FunctionTool[AstrAgentContext]):
|
|
13
|
+
name: str = "astr_kb_search"
|
|
14
|
+
description: str = (
|
|
15
|
+
"Query the knowledge base for facts or relevant context. "
|
|
16
|
+
"Use this tool when the user's question requires factual information, "
|
|
17
|
+
"definitions, background knowledge, or previously indexed content. "
|
|
18
|
+
"Only send short keywords or a concise question as the query."
|
|
19
|
+
)
|
|
20
|
+
parameters: dict = Field(
|
|
21
|
+
default_factory=lambda: {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"properties": {
|
|
24
|
+
"query": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "A concise keyword query for the knowledge base.",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
"required": ["query"],
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
async def call(
|
|
34
|
+
self, context: ContextWrapper[AstrAgentContext], **kwargs
|
|
35
|
+
) -> ToolExecResult:
|
|
36
|
+
query = kwargs.get("query", "")
|
|
37
|
+
if not query:
|
|
38
|
+
return "error: Query parameter is empty."
|
|
39
|
+
result = await retrieve_knowledge_base(
|
|
40
|
+
query=kwargs.get("query", ""),
|
|
41
|
+
umo=context.context.event.unified_msg_origin,
|
|
42
|
+
context=context.context.context,
|
|
43
|
+
)
|
|
44
|
+
if not result:
|
|
45
|
+
return "No relevant knowledge found."
|
|
46
|
+
return result
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def retrieve_knowledge_base(
|
|
50
|
+
query: str,
|
|
51
|
+
umo: str,
|
|
52
|
+
context: Context,
|
|
53
|
+
) -> str | None:
|
|
54
|
+
"""Inject knowledge base context into the provider request
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
umo: Unique message object (session ID)
|
|
58
|
+
p_ctx: Pipeline context
|
|
59
|
+
"""
|
|
60
|
+
kb_mgr = context.kb_manager
|
|
61
|
+
config = context.get_config(umo=umo)
|
|
62
|
+
|
|
63
|
+
# 1. 优先读取会话级配置
|
|
64
|
+
session_config = await sp.session_get(umo, "kb_config", default={})
|
|
65
|
+
|
|
66
|
+
if session_config and "kb_ids" in session_config:
|
|
67
|
+
# 会话级配置
|
|
68
|
+
kb_ids = session_config.get("kb_ids", [])
|
|
69
|
+
|
|
70
|
+
# 如果配置为空列表,明确表示不使用知识库
|
|
71
|
+
if not kb_ids:
|
|
72
|
+
logger.info(f"[知识库] 会话 {umo} 已被配置为不使用知识库")
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
top_k = session_config.get("top_k", 5)
|
|
76
|
+
|
|
77
|
+
# 将 kb_ids 转换为 kb_names
|
|
78
|
+
kb_names = []
|
|
79
|
+
invalid_kb_ids = []
|
|
80
|
+
for kb_id in kb_ids:
|
|
81
|
+
kb_helper = await kb_mgr.get_kb(kb_id)
|
|
82
|
+
if kb_helper:
|
|
83
|
+
kb_names.append(kb_helper.kb.kb_name)
|
|
84
|
+
else:
|
|
85
|
+
logger.warning(f"[知识库] 知识库不存在或未加载: {kb_id}")
|
|
86
|
+
invalid_kb_ids.append(kb_id)
|
|
87
|
+
|
|
88
|
+
if invalid_kb_ids:
|
|
89
|
+
logger.warning(
|
|
90
|
+
f"[知识库] 会话 {umo} 配置的以下知识库无效: {invalid_kb_ids}",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
if not kb_names:
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
logger.debug(f"[知识库] 使用会话级配置,知识库数量: {len(kb_names)}")
|
|
97
|
+
else:
|
|
98
|
+
kb_names = config.get("kb_names", [])
|
|
99
|
+
top_k = config.get("kb_final_top_k", 5)
|
|
100
|
+
logger.debug(f"[知识库] 使用全局配置,知识库数量: {len(kb_names)}")
|
|
101
|
+
|
|
102
|
+
top_k_fusion = config.get("kb_fusion_top_k", 20)
|
|
103
|
+
|
|
104
|
+
if not kb_names:
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
logger.debug(f"[知识库] 开始检索知识库,数量: {len(kb_names)}, top_k={top_k}")
|
|
108
|
+
kb_context = await kb_mgr.retrieve(
|
|
109
|
+
query=query,
|
|
110
|
+
kb_names=kb_names,
|
|
111
|
+
top_k_fusion=top_k_fusion,
|
|
112
|
+
top_m_final=top_k,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if not kb_context:
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
formatted = kb_context.get("context_text", "")
|
|
119
|
+
if formatted:
|
|
120
|
+
results = kb_context.get("results", [])
|
|
121
|
+
logger.debug(f"[知识库] 为会话 {umo} 注入了 {len(results)} 条相关知识块")
|
|
122
|
+
return formatted
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
KNOWLEDGE_BASE_QUERY_TOOL = KnowledgeBaseQueryTool()
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from datetime import datetime, timedelta
|
|
3
2
|
from collections import defaultdict, deque
|
|
4
|
-
from
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
|
3
|
+
from collections.abc import AsyncGenerator
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
|
+
|
|
8
6
|
from astrbot.core import logger
|
|
9
7
|
from astrbot.core.config.astrbot_config import RateLimitStrategy
|
|
8
|
+
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
|
9
|
+
|
|
10
|
+
from ..context import PipelineContext
|
|
11
|
+
from ..stage import Stage, register_stage
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
@register_stage
|
|
13
15
|
class RateLimitStage(Stage):
|
|
14
|
-
"""
|
|
15
|
-
检查是否需要限制消息发送的限流器。
|
|
16
|
+
"""检查是否需要限制消息发送的限流器。
|
|
16
17
|
|
|
17
18
|
使用 Fixed Window 算法。
|
|
18
19
|
如果触发限流,将 stall 流水线,直到下一个时间窗口来临时自动唤醒。
|
|
@@ -20,32 +21,30 @@ class RateLimitStage(Stage):
|
|
|
20
21
|
|
|
21
22
|
def __init__(self):
|
|
22
23
|
# 存储每个会话的请求时间队列
|
|
23
|
-
self.event_timestamps:
|
|
24
|
+
self.event_timestamps: defaultdict[str, deque[datetime]] = defaultdict(deque)
|
|
24
25
|
# 为每个会话设置一个锁,避免并发冲突
|
|
25
|
-
self.locks:
|
|
26
|
+
self.locks: defaultdict[str, asyncio.Lock] = defaultdict(asyncio.Lock)
|
|
26
27
|
# 限流参数
|
|
27
28
|
self.rate_limit_count: int = 0
|
|
28
29
|
self.rate_limit_time: timedelta = timedelta(0)
|
|
29
30
|
|
|
30
31
|
async def initialize(self, ctx: PipelineContext) -> None:
|
|
31
|
-
"""
|
|
32
|
-
初始化限流器,根据配置设置限流参数。
|
|
33
|
-
"""
|
|
32
|
+
"""初始化限流器,根据配置设置限流参数。"""
|
|
34
33
|
self.rate_limit_count = ctx.astrbot_config["platform_settings"]["rate_limit"][
|
|
35
34
|
"count"
|
|
36
35
|
]
|
|
37
36
|
self.rate_limit_time = timedelta(
|
|
38
|
-
seconds=ctx.astrbot_config["platform_settings"]["rate_limit"]["time"]
|
|
37
|
+
seconds=ctx.astrbot_config["platform_settings"]["rate_limit"]["time"],
|
|
39
38
|
)
|
|
40
39
|
self.rl_strategy = ctx.astrbot_config["platform_settings"]["rate_limit"][
|
|
41
40
|
"strategy"
|
|
42
41
|
] # stall or discard
|
|
43
42
|
|
|
44
43
|
async def process(
|
|
45
|
-
self,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
检查并处理限流逻辑。如果触发限流,流水线会 stall 并在窗口期后自动恢复。
|
|
44
|
+
self,
|
|
45
|
+
event: AstrMessageEvent,
|
|
46
|
+
) -> None | AsyncGenerator[None, None]:
|
|
47
|
+
"""检查并处理限流逻辑。如果触发限流,流水线会 stall 并在窗口期后自动恢复。
|
|
49
48
|
|
|
50
49
|
Args:
|
|
51
50
|
event (AstrMessageEvent): 当前消息事件。
|
|
@@ -53,48 +52,47 @@ class RateLimitStage(Stage):
|
|
|
53
52
|
|
|
54
53
|
Returns:
|
|
55
54
|
MessageEventResult: 继续或停止事件处理的结果。
|
|
55
|
+
|
|
56
56
|
"""
|
|
57
57
|
session_id = event.session_id
|
|
58
58
|
now = datetime.now()
|
|
59
59
|
|
|
60
60
|
async with self.locks[session_id]: # 确保同一会话不会并发修改队列
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
# 检查并处理限流,可能需要多次检查直到满足条件
|
|
62
|
+
while True:
|
|
63
|
+
timestamps = self.event_timestamps[session_id]
|
|
64
|
+
self._remove_expired_timestamps(timestamps, now)
|
|
65
|
+
|
|
66
|
+
if len(timestamps) < self.rate_limit_count:
|
|
67
|
+
timestamps.append(now)
|
|
68
|
+
break
|
|
67
69
|
next_window_time = timestamps[0] + self.rate_limit_time
|
|
68
|
-
stall_duration = (next_window_time - now).total_seconds()
|
|
70
|
+
stall_duration = (next_window_time - now).total_seconds() + 0.3
|
|
69
71
|
|
|
70
72
|
match self.rl_strategy:
|
|
71
73
|
case RateLimitStrategy.STALL.value:
|
|
72
74
|
logger.info(
|
|
73
|
-
f"会话 {session_id} 被限流。根据限流策略,此会话处理将被暂停 {stall_duration:.2f} 秒。"
|
|
75
|
+
f"会话 {session_id} 被限流。根据限流策略,此会话处理将被暂停 {stall_duration:.2f} 秒。",
|
|
74
76
|
)
|
|
75
77
|
await asyncio.sleep(stall_duration)
|
|
78
|
+
now = datetime.now()
|
|
76
79
|
case RateLimitStrategy.DISCARD.value:
|
|
77
|
-
# event.set_result(MessageEventResult().message(f"会话 {session_id} 被限流。根据限流策略,此请求已被丢弃,直到您的限额于 {stall_duration:.2f} 秒后重置。"))
|
|
78
80
|
logger.info(
|
|
79
|
-
f"会话 {session_id} 被限流。根据限流策略,此请求已被丢弃,直到限额于 {stall_duration:.2f} 秒后重置。"
|
|
81
|
+
f"会话 {session_id} 被限流。根据限流策略,此请求已被丢弃,直到限额于 {stall_duration:.2f} 秒后重置。",
|
|
80
82
|
)
|
|
81
83
|
return event.stop_event()
|
|
82
84
|
|
|
83
|
-
self._remove_expired_timestamps(
|
|
84
|
-
timestamps, now + timedelta(seconds=stall_duration)
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
timestamps.append(now)
|
|
88
|
-
|
|
89
85
|
def _remove_expired_timestamps(
|
|
90
|
-
self,
|
|
86
|
+
self,
|
|
87
|
+
timestamps: deque[datetime],
|
|
88
|
+
now: datetime,
|
|
91
89
|
) -> None:
|
|
92
|
-
"""
|
|
93
|
-
移除时间窗口外的时间戳。
|
|
90
|
+
"""移除时间窗口外的时间戳。
|
|
94
91
|
|
|
95
92
|
Args:
|
|
96
93
|
timestamps (Deque[datetime]): 当前会话的时间戳队列。
|
|
97
94
|
now (datetime): 当前时间,用于计算过期时间。
|
|
95
|
+
|
|
98
96
|
"""
|
|
99
97
|
expiry_threshold: datetime = now - self.rate_limit_time
|
|
100
98
|
while timestamps and timestamps[0] < expiry_threshold:
|