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
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
from quart import request
|
|
2
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
3
|
+
from sqlmodel import col, select
|
|
4
|
+
|
|
5
|
+
from astrbot.core import logger, sp
|
|
6
|
+
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
|
|
7
|
+
from astrbot.core.db import BaseDatabase
|
|
8
|
+
from astrbot.core.db.po import ConversationV2, Preference
|
|
9
|
+
from astrbot.core.provider.entities import ProviderType
|
|
10
|
+
|
|
11
|
+
from .route import Response, Route, RouteContext
|
|
12
|
+
|
|
13
|
+
AVAILABLE_SESSION_RULE_KEYS = [
|
|
14
|
+
"session_service_config",
|
|
15
|
+
"session_plugin_config",
|
|
16
|
+
"kb_config",
|
|
17
|
+
f"provider_perf_{ProviderType.CHAT_COMPLETION.value}",
|
|
18
|
+
f"provider_perf_{ProviderType.SPEECH_TO_TEXT.value}",
|
|
19
|
+
f"provider_perf_{ProviderType.TEXT_TO_SPEECH.value}",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SessionManagementRoute(Route):
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
context: RouteContext,
|
|
27
|
+
db_helper: BaseDatabase,
|
|
28
|
+
core_lifecycle: AstrBotCoreLifecycle,
|
|
29
|
+
) -> None:
|
|
30
|
+
super().__init__(context)
|
|
31
|
+
self.db_helper = db_helper
|
|
32
|
+
self.routes = {
|
|
33
|
+
"/session/list-rule": ("GET", self.list_session_rule),
|
|
34
|
+
"/session/update-rule": ("POST", self.update_session_rule),
|
|
35
|
+
"/session/delete-rule": ("POST", self.delete_session_rule),
|
|
36
|
+
"/session/batch-delete-rule": ("POST", self.batch_delete_session_rule),
|
|
37
|
+
"/session/active-umos": ("GET", self.list_umos),
|
|
38
|
+
}
|
|
39
|
+
self.conv_mgr = core_lifecycle.conversation_manager
|
|
40
|
+
self.core_lifecycle = core_lifecycle
|
|
41
|
+
self.register_routes()
|
|
42
|
+
|
|
43
|
+
async def _get_umo_rules(
|
|
44
|
+
self, page: int = 1, page_size: int = 10, search: str = ""
|
|
45
|
+
) -> tuple[dict, int]:
|
|
46
|
+
"""获取所有带有自定义规则的 umo 及其规则内容(支持分页和搜索)。
|
|
47
|
+
|
|
48
|
+
如果某个 umo 在 preference 中有以下字段,则表示有自定义规则:
|
|
49
|
+
|
|
50
|
+
1. session_service_config (包含了 是否启用这个umo, 这个umo是否启用 llm, 这个umo是否启用tts, umo自定义名称。)
|
|
51
|
+
2. session_plugin_config (包含了 这个 umo 的 plugin set)
|
|
52
|
+
3. provider_perf_{ProviderType.value} (包含了这个 umo 所选择使用的 provider 信息)
|
|
53
|
+
4. kb_config (包含了这个 umo 的知识库相关配置)
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
page: 页码,从 1 开始
|
|
57
|
+
page_size: 每页数量
|
|
58
|
+
search: 搜索关键词,匹配 umo 或 custom_name
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
tuple[dict, int]: (umo_rules, total) - 分页后的 umo 规则和总数
|
|
62
|
+
"""
|
|
63
|
+
umo_rules = {}
|
|
64
|
+
async with self.db_helper.get_db() as session:
|
|
65
|
+
session: AsyncSession
|
|
66
|
+
result = await session.execute(
|
|
67
|
+
select(Preference).where(
|
|
68
|
+
col(Preference.scope) == "umo",
|
|
69
|
+
col(Preference.key).in_(AVAILABLE_SESSION_RULE_KEYS),
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
prefs = result.scalars().all()
|
|
73
|
+
for pref in prefs:
|
|
74
|
+
umo_id = pref.scope_id
|
|
75
|
+
if umo_id not in umo_rules:
|
|
76
|
+
umo_rules[umo_id] = {}
|
|
77
|
+
umo_rules[umo_id][pref.key] = pref.value["val"]
|
|
78
|
+
|
|
79
|
+
# 搜索过滤
|
|
80
|
+
if search:
|
|
81
|
+
search_lower = search.lower()
|
|
82
|
+
filtered_rules = {}
|
|
83
|
+
for umo_id, rules in umo_rules.items():
|
|
84
|
+
# 匹配 umo
|
|
85
|
+
if search_lower in umo_id.lower():
|
|
86
|
+
filtered_rules[umo_id] = rules
|
|
87
|
+
continue
|
|
88
|
+
# 匹配 custom_name
|
|
89
|
+
svc_config = rules.get("session_service_config", {})
|
|
90
|
+
custom_name = svc_config.get("custom_name", "") if svc_config else ""
|
|
91
|
+
if custom_name and search_lower in custom_name.lower():
|
|
92
|
+
filtered_rules[umo_id] = rules
|
|
93
|
+
umo_rules = filtered_rules
|
|
94
|
+
|
|
95
|
+
# 获取总数
|
|
96
|
+
total = len(umo_rules)
|
|
97
|
+
|
|
98
|
+
# 分页处理
|
|
99
|
+
all_umo_ids = list(umo_rules.keys())
|
|
100
|
+
start_idx = (page - 1) * page_size
|
|
101
|
+
end_idx = start_idx + page_size
|
|
102
|
+
paginated_umo_ids = all_umo_ids[start_idx:end_idx]
|
|
103
|
+
|
|
104
|
+
# 只返回分页后的数据
|
|
105
|
+
paginated_rules = {umo_id: umo_rules[umo_id] for umo_id in paginated_umo_ids}
|
|
106
|
+
|
|
107
|
+
return paginated_rules, total
|
|
108
|
+
|
|
109
|
+
async def list_session_rule(self):
|
|
110
|
+
"""获取所有自定义的规则(支持分页和搜索)
|
|
111
|
+
|
|
112
|
+
返回已配置规则的 umo 列表及其规则内容,以及可用的 personas 和 providers
|
|
113
|
+
|
|
114
|
+
Query 参数:
|
|
115
|
+
page: 页码,默认为 1
|
|
116
|
+
page_size: 每页数量,默认为 10
|
|
117
|
+
search: 搜索关键词,匹配 umo 或 custom_name
|
|
118
|
+
"""
|
|
119
|
+
try:
|
|
120
|
+
# 获取分页和搜索参数
|
|
121
|
+
page = request.args.get("page", 1, type=int)
|
|
122
|
+
page_size = request.args.get("page_size", 10, type=int)
|
|
123
|
+
search = request.args.get("search", "", type=str).strip()
|
|
124
|
+
|
|
125
|
+
# 参数校验
|
|
126
|
+
if page < 1:
|
|
127
|
+
page = 1
|
|
128
|
+
if page_size < 1:
|
|
129
|
+
page_size = 10
|
|
130
|
+
if page_size > 100:
|
|
131
|
+
page_size = 100
|
|
132
|
+
|
|
133
|
+
umo_rules, total = await self._get_umo_rules(
|
|
134
|
+
page=page, page_size=page_size, search=search
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# 构建规则列表
|
|
138
|
+
rules_list = []
|
|
139
|
+
for umo, rules in umo_rules.items():
|
|
140
|
+
rule_info = {
|
|
141
|
+
"umo": umo,
|
|
142
|
+
"rules": rules,
|
|
143
|
+
}
|
|
144
|
+
# 解析 umo 格式: 平台:消息类型:会话ID
|
|
145
|
+
parts = umo.split(":")
|
|
146
|
+
if len(parts) >= 3:
|
|
147
|
+
rule_info["platform"] = parts[0]
|
|
148
|
+
rule_info["message_type"] = parts[1]
|
|
149
|
+
rule_info["session_id"] = parts[2]
|
|
150
|
+
rules_list.append(rule_info)
|
|
151
|
+
|
|
152
|
+
# 获取可用的 providers 和 personas
|
|
153
|
+
provider_manager = self.core_lifecycle.provider_manager
|
|
154
|
+
persona_mgr = self.core_lifecycle.persona_mgr
|
|
155
|
+
|
|
156
|
+
available_personas = [
|
|
157
|
+
{"name": p["name"], "prompt": p.get("prompt", "")}
|
|
158
|
+
for p in persona_mgr.personas_v3
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
available_chat_providers = [
|
|
162
|
+
{
|
|
163
|
+
"id": p.meta().id,
|
|
164
|
+
"name": p.meta().id,
|
|
165
|
+
"model": p.meta().model,
|
|
166
|
+
}
|
|
167
|
+
for p in provider_manager.provider_insts
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
available_stt_providers = [
|
|
171
|
+
{
|
|
172
|
+
"id": p.meta().id,
|
|
173
|
+
"name": p.meta().id,
|
|
174
|
+
"model": p.meta().model,
|
|
175
|
+
}
|
|
176
|
+
for p in provider_manager.stt_provider_insts
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
available_tts_providers = [
|
|
180
|
+
{
|
|
181
|
+
"id": p.meta().id,
|
|
182
|
+
"name": p.meta().id,
|
|
183
|
+
"model": p.meta().model,
|
|
184
|
+
}
|
|
185
|
+
for p in provider_manager.tts_provider_insts
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
Response()
|
|
190
|
+
.ok(
|
|
191
|
+
{
|
|
192
|
+
"rules": rules_list,
|
|
193
|
+
"total": total,
|
|
194
|
+
"page": page,
|
|
195
|
+
"page_size": page_size,
|
|
196
|
+
"available_personas": available_personas,
|
|
197
|
+
"available_chat_providers": available_chat_providers,
|
|
198
|
+
"available_stt_providers": available_stt_providers,
|
|
199
|
+
"available_tts_providers": available_tts_providers,
|
|
200
|
+
"available_rule_keys": AVAILABLE_SESSION_RULE_KEYS,
|
|
201
|
+
}
|
|
202
|
+
)
|
|
203
|
+
.__dict__
|
|
204
|
+
)
|
|
205
|
+
except Exception as e:
|
|
206
|
+
logger.error(f"获取规则列表失败: {e!s}")
|
|
207
|
+
return Response().error(f"获取规则列表失败: {e!s}").__dict__
|
|
208
|
+
|
|
209
|
+
async def update_session_rule(self):
|
|
210
|
+
"""更新某个 umo 的自定义规则
|
|
211
|
+
|
|
212
|
+
请求体:
|
|
213
|
+
{
|
|
214
|
+
"umo": "平台:消息类型:会话ID",
|
|
215
|
+
"rule_key": "session_service_config" | "session_plugin_config" | "kb_config" | "provider_perf_xxx",
|
|
216
|
+
"rule_value": {...} // 规则值,具体结构根据 rule_key 不同而不同
|
|
217
|
+
}
|
|
218
|
+
"""
|
|
219
|
+
try:
|
|
220
|
+
data = await request.get_json()
|
|
221
|
+
umo = data.get("umo")
|
|
222
|
+
rule_key = data.get("rule_key")
|
|
223
|
+
rule_value = data.get("rule_value")
|
|
224
|
+
|
|
225
|
+
if not umo:
|
|
226
|
+
return Response().error("缺少必要参数: umo").__dict__
|
|
227
|
+
if not rule_key:
|
|
228
|
+
return Response().error("缺少必要参数: rule_key").__dict__
|
|
229
|
+
if rule_key not in AVAILABLE_SESSION_RULE_KEYS:
|
|
230
|
+
return Response().error(f"不支持的规则键: {rule_key}").__dict__
|
|
231
|
+
|
|
232
|
+
# 使用 shared preferences 更新规则
|
|
233
|
+
await sp.session_put(umo, rule_key, rule_value)
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
Response()
|
|
237
|
+
.ok({"message": f"规则 {rule_key} 已更新", "umo": umo})
|
|
238
|
+
.__dict__
|
|
239
|
+
)
|
|
240
|
+
except Exception as e:
|
|
241
|
+
logger.error(f"更新会话规则失败: {e!s}")
|
|
242
|
+
return Response().error(f"更新会话规则失败: {e!s}").__dict__
|
|
243
|
+
|
|
244
|
+
async def delete_session_rule(self):
|
|
245
|
+
"""删除某个 umo 的自定义规则
|
|
246
|
+
|
|
247
|
+
请求体:
|
|
248
|
+
{
|
|
249
|
+
"umo": "平台:消息类型:会话ID",
|
|
250
|
+
"rule_key": "session_service_config" | "session_plugin_config" | ... (可选,不传则删除所有规则)
|
|
251
|
+
}
|
|
252
|
+
"""
|
|
253
|
+
try:
|
|
254
|
+
data = await request.get_json()
|
|
255
|
+
umo = data.get("umo")
|
|
256
|
+
rule_key = data.get("rule_key")
|
|
257
|
+
|
|
258
|
+
if not umo:
|
|
259
|
+
return Response().error("缺少必要参数: umo").__dict__
|
|
260
|
+
|
|
261
|
+
if rule_key:
|
|
262
|
+
# 删除单个规则
|
|
263
|
+
if rule_key not in AVAILABLE_SESSION_RULE_KEYS:
|
|
264
|
+
return Response().error(f"不支持的规则键: {rule_key}").__dict__
|
|
265
|
+
await sp.session_remove(umo, rule_key)
|
|
266
|
+
return (
|
|
267
|
+
Response()
|
|
268
|
+
.ok({"message": f"规则 {rule_key} 已删除", "umo": umo})
|
|
269
|
+
.__dict__
|
|
270
|
+
)
|
|
271
|
+
else:
|
|
272
|
+
# 删除该 umo 的所有规则
|
|
273
|
+
await sp.clear_async("umo", umo)
|
|
274
|
+
return Response().ok({"message": "所有规则已删除", "umo": umo}).__dict__
|
|
275
|
+
except Exception as e:
|
|
276
|
+
logger.error(f"删除会话规则失败: {e!s}")
|
|
277
|
+
return Response().error(f"删除会话规则失败: {e!s}").__dict__
|
|
278
|
+
|
|
279
|
+
async def batch_delete_session_rule(self):
|
|
280
|
+
"""批量删除多个 umo 的自定义规则
|
|
281
|
+
|
|
282
|
+
请求体:
|
|
283
|
+
{
|
|
284
|
+
"umos": ["平台:消息类型:会话ID", ...] // umo 列表
|
|
285
|
+
}
|
|
286
|
+
"""
|
|
287
|
+
try:
|
|
288
|
+
data = await request.get_json()
|
|
289
|
+
umos = data.get("umos", [])
|
|
290
|
+
|
|
291
|
+
if not umos:
|
|
292
|
+
return Response().error("缺少必要参数: umos").__dict__
|
|
293
|
+
|
|
294
|
+
if not isinstance(umos, list):
|
|
295
|
+
return Response().error("参数 umos 必须是数组").__dict__
|
|
296
|
+
|
|
297
|
+
# 批量删除
|
|
298
|
+
deleted_count = 0
|
|
299
|
+
failed_umos = []
|
|
300
|
+
for umo in umos:
|
|
301
|
+
try:
|
|
302
|
+
await sp.clear_async("umo", umo)
|
|
303
|
+
deleted_count += 1
|
|
304
|
+
except Exception as e:
|
|
305
|
+
logger.error(f"删除 umo {umo} 的规则失败: {e!s}")
|
|
306
|
+
failed_umos.append(umo)
|
|
307
|
+
|
|
308
|
+
if failed_umos:
|
|
309
|
+
return (
|
|
310
|
+
Response()
|
|
311
|
+
.ok(
|
|
312
|
+
{
|
|
313
|
+
"message": f"已删除 {deleted_count} 条规则,{len(failed_umos)} 条删除失败",
|
|
314
|
+
"deleted_count": deleted_count,
|
|
315
|
+
"failed_umos": failed_umos,
|
|
316
|
+
}
|
|
317
|
+
)
|
|
318
|
+
.__dict__
|
|
319
|
+
)
|
|
320
|
+
else:
|
|
321
|
+
return (
|
|
322
|
+
Response()
|
|
323
|
+
.ok(
|
|
324
|
+
{
|
|
325
|
+
"message": f"已删除 {deleted_count} 条规则",
|
|
326
|
+
"deleted_count": deleted_count,
|
|
327
|
+
}
|
|
328
|
+
)
|
|
329
|
+
.__dict__
|
|
330
|
+
)
|
|
331
|
+
except Exception as e:
|
|
332
|
+
logger.error(f"批量删除会话规则失败: {e!s}")
|
|
333
|
+
return Response().error(f"批量删除会话规则失败: {e!s}").__dict__
|
|
334
|
+
|
|
335
|
+
async def list_umos(self):
|
|
336
|
+
"""列出所有有对话记录的 umo,从 Conversations 表中找
|
|
337
|
+
|
|
338
|
+
仅返回 umo 字符串列表,用于用户在创建规则时选择 umo
|
|
339
|
+
"""
|
|
340
|
+
try:
|
|
341
|
+
# 从 Conversation 表获取所有 distinct user_id (即 umo)
|
|
342
|
+
async with self.db_helper.get_db() as session:
|
|
343
|
+
session: AsyncSession
|
|
344
|
+
result = await session.execute(
|
|
345
|
+
select(ConversationV2.user_id)
|
|
346
|
+
.distinct()
|
|
347
|
+
.order_by(ConversationV2.user_id)
|
|
348
|
+
)
|
|
349
|
+
umos = [row[0] for row in result.fetchall()]
|
|
350
|
+
|
|
351
|
+
return Response().ok({"umos": umos}).__dict__
|
|
352
|
+
except Exception as e:
|
|
353
|
+
logger.error(f"获取 UMO 列表失败: {e!s}")
|
|
354
|
+
return Response().error(f"获取 UMO 列表失败: {e!s}").__dict__
|
astrbot/dashboard/routes/stat.py
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import time
|
|
1
3
|
import traceback
|
|
4
|
+
|
|
5
|
+
import aiohttp
|
|
2
6
|
import psutil
|
|
3
|
-
import time
|
|
4
|
-
import threading
|
|
5
|
-
from .route import Route, Response, RouteContext
|
|
6
|
-
from astrbot.core import logger
|
|
7
7
|
from quart import request
|
|
8
|
+
|
|
9
|
+
from astrbot.core import DEMO_MODE, logger
|
|
10
|
+
from astrbot.core.config import VERSION
|
|
8
11
|
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
|
|
9
12
|
from astrbot.core.db import BaseDatabase
|
|
10
|
-
from astrbot.core.
|
|
11
|
-
from astrbot.core import
|
|
13
|
+
from astrbot.core.db.migration.helper import check_migration_needed_v4
|
|
14
|
+
from astrbot.core.utils.io import get_dashboard_version
|
|
15
|
+
|
|
16
|
+
from .route import Response, Route, RouteContext
|
|
12
17
|
|
|
13
18
|
|
|
14
19
|
class StatRoute(Route):
|
|
@@ -24,6 +29,7 @@ class StatRoute(Route):
|
|
|
24
29
|
"/stat/version": ("GET", self.get_version),
|
|
25
30
|
"/stat/start-time": ("GET", self.get_start_time),
|
|
26
31
|
"/stat/restart-core": ("POST", self.restart_core),
|
|
32
|
+
"/stat/test-ghproxy-connection": ("POST", self.test_ghproxy_connection),
|
|
27
33
|
}
|
|
28
34
|
self.db_helper = db_helper
|
|
29
35
|
self.register_routes()
|
|
@@ -40,13 +46,36 @@ class StatRoute(Route):
|
|
|
40
46
|
await self.core_lifecycle.restart()
|
|
41
47
|
return Response().ok().__dict__
|
|
42
48
|
|
|
43
|
-
def
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
def _get_running_time_components(self, total_seconds: int):
|
|
50
|
+
"""将总秒数转换为时分秒组件"""
|
|
51
|
+
minutes, seconds = divmod(total_seconds, 60)
|
|
52
|
+
hours, minutes = divmod(minutes, 60)
|
|
53
|
+
return {"hours": hours, "minutes": minutes, "seconds": seconds}
|
|
54
|
+
|
|
55
|
+
def is_default_cred(self):
|
|
56
|
+
username = self.config["dashboard"]["username"]
|
|
57
|
+
password = self.config["dashboard"]["password"]
|
|
58
|
+
return (
|
|
59
|
+
username == "astrbot"
|
|
60
|
+
and password == "77b90590a8945a7d36c963981a307dc9"
|
|
61
|
+
and not DEMO_MODE
|
|
62
|
+
)
|
|
47
63
|
|
|
48
64
|
async def get_version(self):
|
|
49
|
-
|
|
65
|
+
need_migration = await check_migration_needed_v4(self.core_lifecycle.db)
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
Response()
|
|
69
|
+
.ok(
|
|
70
|
+
{
|
|
71
|
+
"version": VERSION,
|
|
72
|
+
"dashboard_version": await get_dashboard_version(),
|
|
73
|
+
"change_pwd_hint": self.is_default_cred(),
|
|
74
|
+
"need_migration": need_migration,
|
|
75
|
+
},
|
|
76
|
+
)
|
|
77
|
+
.__dict__
|
|
78
|
+
)
|
|
50
79
|
|
|
51
80
|
async def get_start_time(self):
|
|
52
81
|
return Response().ok({"start_time": self.core_lifecycle.start_time}).__dict__
|
|
@@ -61,7 +90,7 @@ class StatRoute(Route):
|
|
|
61
90
|
message_time_based_stats = []
|
|
62
91
|
|
|
63
92
|
idx = 0
|
|
64
|
-
for bucket_end in range(start_time, now,
|
|
93
|
+
for bucket_end in range(start_time, now, 3600):
|
|
65
94
|
cnt = 0
|
|
66
95
|
while (
|
|
67
96
|
idx < len(stat.platform)
|
|
@@ -87,21 +116,24 @@ class StatRoute(Route):
|
|
|
87
116
|
}
|
|
88
117
|
plugin_info.append(info)
|
|
89
118
|
|
|
119
|
+
# 计算运行时长组件
|
|
120
|
+
running_time = self._get_running_time_components(
|
|
121
|
+
int(time.time()) - self.core_lifecycle.start_time,
|
|
122
|
+
)
|
|
123
|
+
|
|
90
124
|
stat_dict.update(
|
|
91
125
|
{
|
|
92
126
|
"platform": self.db_helper.get_grouped_base_stats(
|
|
93
|
-
offset_sec
|
|
127
|
+
offset_sec,
|
|
94
128
|
).platform,
|
|
95
129
|
"message_count": self.db_helper.get_total_message_count() or 0,
|
|
96
130
|
"platform_count": len(
|
|
97
|
-
self.core_lifecycle.platform_manager.get_insts()
|
|
131
|
+
self.core_lifecycle.platform_manager.get_insts(),
|
|
98
132
|
),
|
|
99
133
|
"plugin_count": len(plugins),
|
|
100
134
|
"plugins": plugin_info,
|
|
101
135
|
"message_time_series": message_time_based_stats,
|
|
102
|
-
"running":
|
|
103
|
-
int(time.time()) - self.core_lifecycle.start_time
|
|
104
|
-
),
|
|
136
|
+
"running": running_time, # 现在返回时间组件而不是格式化的字符串
|
|
105
137
|
"memory": {
|
|
106
138
|
"process": psutil.Process().memory_info().rss >> 20,
|
|
107
139
|
"system": psutil.virtual_memory().total >> 20,
|
|
@@ -109,10 +141,45 @@ class StatRoute(Route):
|
|
|
109
141
|
"cpu_percent": round(cpu_percent, 1),
|
|
110
142
|
"thread_count": thread_count,
|
|
111
143
|
"start_time": self.core_lifecycle.start_time,
|
|
112
|
-
}
|
|
144
|
+
},
|
|
113
145
|
)
|
|
114
146
|
|
|
115
147
|
return Response().ok(stat_dict).__dict__
|
|
116
148
|
except Exception as e:
|
|
117
149
|
logger.error(traceback.format_exc())
|
|
118
150
|
return Response().error(e.__str__()).__dict__
|
|
151
|
+
|
|
152
|
+
async def test_ghproxy_connection(self):
|
|
153
|
+
"""测试 GitHub 代理连接是否可用。"""
|
|
154
|
+
try:
|
|
155
|
+
data = await request.get_json()
|
|
156
|
+
proxy_url: str = data.get("proxy_url")
|
|
157
|
+
|
|
158
|
+
if not proxy_url:
|
|
159
|
+
return Response().error("proxy_url is required").__dict__
|
|
160
|
+
|
|
161
|
+
proxy_url = proxy_url.rstrip("/")
|
|
162
|
+
|
|
163
|
+
test_url = f"{proxy_url}/https://github.com/AstrBotDevs/AstrBot/raw/refs/heads/master/.python-version"
|
|
164
|
+
start_time = time.time()
|
|
165
|
+
|
|
166
|
+
async with (
|
|
167
|
+
aiohttp.ClientSession() as session,
|
|
168
|
+
session.get(
|
|
169
|
+
test_url,
|
|
170
|
+
timeout=aiohttp.ClientTimeout(total=10),
|
|
171
|
+
) as response,
|
|
172
|
+
):
|
|
173
|
+
if response.status == 200:
|
|
174
|
+
end_time = time.time()
|
|
175
|
+
_ = await response.text()
|
|
176
|
+
ret = {
|
|
177
|
+
"latency": round((end_time - start_time) * 1000, 2),
|
|
178
|
+
}
|
|
179
|
+
return Response().ok(data=ret).__dict__
|
|
180
|
+
return (
|
|
181
|
+
Response().error(f"Failed. Status code: {response.status}").__dict__
|
|
182
|
+
)
|
|
183
|
+
except Exception as e:
|
|
184
|
+
logger.error(traceback.format_exc())
|
|
185
|
+
return Response().error(f"Error: {e!s}").__dict__
|
|
@@ -12,7 +12,10 @@ class StaticFileRoute(Route):
|
|
|
12
12
|
"/logs",
|
|
13
13
|
"/extension",
|
|
14
14
|
"/dashboard/default",
|
|
15
|
-
"/
|
|
15
|
+
"/alkaid",
|
|
16
|
+
"/alkaid/knowledge-base",
|
|
17
|
+
"/alkaid/long-term-memory",
|
|
18
|
+
"/alkaid/other",
|
|
16
19
|
"/console",
|
|
17
20
|
"/chat",
|
|
18
21
|
"/settings",
|
|
@@ -28,7 +31,7 @@ class StaticFileRoute(Route):
|
|
|
28
31
|
|
|
29
32
|
@self.app.errorhandler(404)
|
|
30
33
|
async def page_not_found(e):
|
|
31
|
-
return "404 Not found。如果你初次使用打开面板发现 404, 请参考文档: https://astrbot.app/faq.html
|
|
34
|
+
return "404 Not found。如果你初次使用打开面板发现 404, 请参考文档: https://astrbot.app/faq.html。如果你正在测试回调地址可达性,显示这段文字说明测试成功了。"
|
|
32
35
|
|
|
33
36
|
async def index(self):
|
|
34
37
|
return await self.app.send_static_file("index.html")
|