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.
Files changed (288) hide show
  1. astrbot/api/__init__.py +16 -4
  2. astrbot/api/all.py +2 -1
  3. astrbot/api/event/__init__.py +5 -6
  4. astrbot/api/event/filter/__init__.py +37 -34
  5. astrbot/api/platform/__init__.py +7 -8
  6. astrbot/api/provider/__init__.py +8 -7
  7. astrbot/api/star/__init__.py +3 -4
  8. astrbot/api/util/__init__.py +2 -2
  9. astrbot/cli/__init__.py +1 -0
  10. astrbot/cli/__main__.py +18 -197
  11. astrbot/cli/commands/__init__.py +6 -0
  12. astrbot/cli/commands/cmd_conf.py +209 -0
  13. astrbot/cli/commands/cmd_init.py +56 -0
  14. astrbot/cli/commands/cmd_plug.py +245 -0
  15. astrbot/cli/commands/cmd_run.py +62 -0
  16. astrbot/cli/utils/__init__.py +18 -0
  17. astrbot/cli/utils/basic.py +76 -0
  18. astrbot/cli/utils/plugin.py +246 -0
  19. astrbot/cli/utils/version_comparator.py +90 -0
  20. astrbot/core/__init__.py +17 -19
  21. astrbot/core/agent/agent.py +14 -0
  22. astrbot/core/agent/handoff.py +38 -0
  23. astrbot/core/agent/hooks.py +30 -0
  24. astrbot/core/agent/mcp_client.py +385 -0
  25. astrbot/core/agent/message.py +175 -0
  26. astrbot/core/agent/response.py +14 -0
  27. astrbot/core/agent/run_context.py +22 -0
  28. astrbot/core/agent/runners/__init__.py +3 -0
  29. astrbot/core/agent/runners/base.py +65 -0
  30. astrbot/core/agent/runners/coze/coze_agent_runner.py +367 -0
  31. astrbot/core/agent/runners/coze/coze_api_client.py +324 -0
  32. astrbot/core/agent/runners/dashscope/dashscope_agent_runner.py +403 -0
  33. astrbot/core/agent/runners/dify/dify_agent_runner.py +336 -0
  34. astrbot/core/agent/runners/dify/dify_api_client.py +195 -0
  35. astrbot/core/agent/runners/tool_loop_agent_runner.py +400 -0
  36. astrbot/core/agent/tool.py +285 -0
  37. astrbot/core/agent/tool_executor.py +17 -0
  38. astrbot/core/astr_agent_context.py +19 -0
  39. astrbot/core/astr_agent_hooks.py +36 -0
  40. astrbot/core/astr_agent_run_util.py +80 -0
  41. astrbot/core/astr_agent_tool_exec.py +246 -0
  42. astrbot/core/astrbot_config_mgr.py +275 -0
  43. astrbot/core/config/__init__.py +2 -2
  44. astrbot/core/config/astrbot_config.py +60 -20
  45. astrbot/core/config/default.py +1972 -453
  46. astrbot/core/config/i18n_utils.py +110 -0
  47. astrbot/core/conversation_mgr.py +285 -75
  48. astrbot/core/core_lifecycle.py +167 -62
  49. astrbot/core/db/__init__.py +305 -102
  50. astrbot/core/db/migration/helper.py +69 -0
  51. astrbot/core/db/migration/migra_3_to_4.py +357 -0
  52. astrbot/core/db/migration/migra_45_to_46.py +44 -0
  53. astrbot/core/db/migration/migra_webchat_session.py +131 -0
  54. astrbot/core/db/migration/shared_preferences_v3.py +48 -0
  55. astrbot/core/db/migration/sqlite_v3.py +497 -0
  56. astrbot/core/db/po.py +259 -55
  57. astrbot/core/db/sqlite.py +773 -528
  58. astrbot/core/db/vec_db/base.py +73 -0
  59. astrbot/core/db/vec_db/faiss_impl/__init__.py +3 -0
  60. astrbot/core/db/vec_db/faiss_impl/document_storage.py +392 -0
  61. astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +93 -0
  62. astrbot/core/db/vec_db/faiss_impl/sqlite_init.sql +17 -0
  63. astrbot/core/db/vec_db/faiss_impl/vec_db.py +204 -0
  64. astrbot/core/event_bus.py +26 -22
  65. astrbot/core/exceptions.py +9 -0
  66. astrbot/core/file_token_service.py +98 -0
  67. astrbot/core/initial_loader.py +19 -10
  68. astrbot/core/knowledge_base/chunking/__init__.py +9 -0
  69. astrbot/core/knowledge_base/chunking/base.py +25 -0
  70. astrbot/core/knowledge_base/chunking/fixed_size.py +59 -0
  71. astrbot/core/knowledge_base/chunking/recursive.py +161 -0
  72. astrbot/core/knowledge_base/kb_db_sqlite.py +301 -0
  73. astrbot/core/knowledge_base/kb_helper.py +642 -0
  74. astrbot/core/knowledge_base/kb_mgr.py +330 -0
  75. astrbot/core/knowledge_base/models.py +120 -0
  76. astrbot/core/knowledge_base/parsers/__init__.py +13 -0
  77. astrbot/core/knowledge_base/parsers/base.py +51 -0
  78. astrbot/core/knowledge_base/parsers/markitdown_parser.py +26 -0
  79. astrbot/core/knowledge_base/parsers/pdf_parser.py +101 -0
  80. astrbot/core/knowledge_base/parsers/text_parser.py +42 -0
  81. astrbot/core/knowledge_base/parsers/url_parser.py +103 -0
  82. astrbot/core/knowledge_base/parsers/util.py +13 -0
  83. astrbot/core/knowledge_base/prompts.py +65 -0
  84. astrbot/core/knowledge_base/retrieval/__init__.py +14 -0
  85. astrbot/core/knowledge_base/retrieval/hit_stopwords.txt +767 -0
  86. astrbot/core/knowledge_base/retrieval/manager.py +276 -0
  87. astrbot/core/knowledge_base/retrieval/rank_fusion.py +142 -0
  88. astrbot/core/knowledge_base/retrieval/sparse_retriever.py +136 -0
  89. astrbot/core/log.py +21 -15
  90. astrbot/core/message/components.py +413 -287
  91. astrbot/core/message/message_event_result.py +35 -24
  92. astrbot/core/persona_mgr.py +192 -0
  93. astrbot/core/pipeline/__init__.py +14 -14
  94. astrbot/core/pipeline/content_safety_check/stage.py +13 -9
  95. astrbot/core/pipeline/content_safety_check/strategies/__init__.py +1 -2
  96. astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +13 -14
  97. astrbot/core/pipeline/content_safety_check/strategies/keywords.py +2 -1
  98. astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
  99. astrbot/core/pipeline/context.py +7 -1
  100. astrbot/core/pipeline/context_utils.py +107 -0
  101. astrbot/core/pipeline/preprocess_stage/stage.py +63 -36
  102. astrbot/core/pipeline/process_stage/method/agent_request.py +48 -0
  103. astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +464 -0
  104. astrbot/core/pipeline/process_stage/method/agent_sub_stages/third_party.py +202 -0
  105. astrbot/core/pipeline/process_stage/method/star_request.py +26 -32
  106. astrbot/core/pipeline/process_stage/stage.py +21 -15
  107. astrbot/core/pipeline/process_stage/utils.py +125 -0
  108. astrbot/core/pipeline/rate_limit_check/stage.py +34 -36
  109. astrbot/core/pipeline/respond/stage.py +142 -101
  110. astrbot/core/pipeline/result_decorate/stage.py +124 -57
  111. astrbot/core/pipeline/scheduler.py +21 -16
  112. astrbot/core/pipeline/session_status_check/stage.py +37 -0
  113. astrbot/core/pipeline/stage.py +11 -76
  114. astrbot/core/pipeline/waking_check/stage.py +69 -33
  115. astrbot/core/pipeline/whitelist_check/stage.py +10 -7
  116. astrbot/core/platform/__init__.py +6 -6
  117. astrbot/core/platform/astr_message_event.py +107 -129
  118. astrbot/core/platform/astrbot_message.py +32 -12
  119. astrbot/core/platform/manager.py +62 -18
  120. astrbot/core/platform/message_session.py +30 -0
  121. astrbot/core/platform/platform.py +16 -24
  122. astrbot/core/platform/platform_metadata.py +9 -4
  123. astrbot/core/platform/register.py +12 -7
  124. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +136 -60
  125. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +126 -46
  126. astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +63 -31
  127. astrbot/core/platform/sources/dingtalk/dingtalk_event.py +30 -26
  128. astrbot/core/platform/sources/discord/client.py +129 -0
  129. astrbot/core/platform/sources/discord/components.py +139 -0
  130. astrbot/core/platform/sources/discord/discord_platform_adapter.py +473 -0
  131. astrbot/core/platform/sources/discord/discord_platform_event.py +313 -0
  132. astrbot/core/platform/sources/lark/lark_adapter.py +27 -18
  133. astrbot/core/platform/sources/lark/lark_event.py +39 -13
  134. astrbot/core/platform/sources/misskey/misskey_adapter.py +770 -0
  135. astrbot/core/platform/sources/misskey/misskey_api.py +964 -0
  136. astrbot/core/platform/sources/misskey/misskey_event.py +163 -0
  137. astrbot/core/platform/sources/misskey/misskey_utils.py +550 -0
  138. astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +149 -33
  139. astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +41 -26
  140. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +36 -17
  141. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_event.py +3 -1
  142. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +14 -8
  143. astrbot/core/platform/sources/satori/satori_adapter.py +792 -0
  144. astrbot/core/platform/sources/satori/satori_event.py +432 -0
  145. astrbot/core/platform/sources/slack/client.py +164 -0
  146. astrbot/core/platform/sources/slack/slack_adapter.py +416 -0
  147. astrbot/core/platform/sources/slack/slack_event.py +253 -0
  148. astrbot/core/platform/sources/telegram/tg_adapter.py +100 -43
  149. astrbot/core/platform/sources/telegram/tg_event.py +136 -36
  150. astrbot/core/platform/sources/webchat/webchat_adapter.py +72 -22
  151. astrbot/core/platform/sources/webchat/webchat_event.py +46 -22
  152. astrbot/core/platform/sources/webchat/webchat_queue_mgr.py +35 -0
  153. astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +926 -0
  154. astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +178 -0
  155. astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +159 -0
  156. astrbot/core/platform/sources/wecom/wecom_adapter.py +169 -27
  157. astrbot/core/platform/sources/wecom/wecom_event.py +162 -77
  158. astrbot/core/platform/sources/wecom/wecom_kf.py +279 -0
  159. astrbot/core/platform/sources/wecom/wecom_kf_message.py +196 -0
  160. astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +297 -0
  161. astrbot/core/platform/sources/wecom_ai_bot/__init__.py +15 -0
  162. astrbot/core/platform/sources/wecom_ai_bot/ierror.py +19 -0
  163. astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +472 -0
  164. astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +417 -0
  165. astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +152 -0
  166. astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +153 -0
  167. astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +168 -0
  168. astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +209 -0
  169. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +306 -0
  170. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +186 -0
  171. astrbot/core/platform_message_history_mgr.py +49 -0
  172. astrbot/core/provider/__init__.py +2 -3
  173. astrbot/core/provider/entites.py +8 -8
  174. astrbot/core/provider/entities.py +154 -98
  175. astrbot/core/provider/func_tool_manager.py +446 -458
  176. astrbot/core/provider/manager.py +345 -207
  177. astrbot/core/provider/provider.py +188 -73
  178. astrbot/core/provider/register.py +9 -7
  179. astrbot/core/provider/sources/anthropic_source.py +295 -115
  180. astrbot/core/provider/sources/azure_tts_source.py +224 -0
  181. astrbot/core/provider/sources/bailian_rerank_source.py +236 -0
  182. astrbot/core/provider/sources/dashscope_tts.py +138 -14
  183. astrbot/core/provider/sources/edge_tts_source.py +24 -19
  184. astrbot/core/provider/sources/fishaudio_tts_api_source.py +58 -13
  185. astrbot/core/provider/sources/gemini_embedding_source.py +61 -0
  186. astrbot/core/provider/sources/gemini_source.py +310 -132
  187. astrbot/core/provider/sources/gemini_tts_source.py +81 -0
  188. astrbot/core/provider/sources/groq_source.py +15 -0
  189. astrbot/core/provider/sources/gsv_selfhosted_source.py +151 -0
  190. astrbot/core/provider/sources/gsvi_tts_source.py +14 -7
  191. astrbot/core/provider/sources/minimax_tts_api_source.py +159 -0
  192. astrbot/core/provider/sources/openai_embedding_source.py +40 -0
  193. astrbot/core/provider/sources/openai_source.py +241 -145
  194. astrbot/core/provider/sources/openai_tts_api_source.py +18 -7
  195. astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
  196. astrbot/core/provider/sources/vllm_rerank_source.py +71 -0
  197. astrbot/core/provider/sources/volcengine_tts.py +115 -0
  198. astrbot/core/provider/sources/whisper_api_source.py +18 -13
  199. astrbot/core/provider/sources/whisper_selfhosted_source.py +19 -12
  200. astrbot/core/provider/sources/xinference_rerank_source.py +116 -0
  201. astrbot/core/provider/sources/xinference_stt_provider.py +197 -0
  202. astrbot/core/provider/sources/zhipu_source.py +6 -73
  203. astrbot/core/star/__init__.py +43 -11
  204. astrbot/core/star/config.py +17 -18
  205. astrbot/core/star/context.py +362 -138
  206. astrbot/core/star/filter/__init__.py +4 -3
  207. astrbot/core/star/filter/command.py +111 -35
  208. astrbot/core/star/filter/command_group.py +46 -34
  209. astrbot/core/star/filter/custom_filter.py +6 -5
  210. astrbot/core/star/filter/event_message_type.py +4 -2
  211. astrbot/core/star/filter/permission.py +4 -2
  212. astrbot/core/star/filter/platform_adapter_type.py +45 -12
  213. astrbot/core/star/filter/regex.py +4 -2
  214. astrbot/core/star/register/__init__.py +19 -15
  215. astrbot/core/star/register/star.py +41 -13
  216. astrbot/core/star/register/star_handler.py +236 -86
  217. astrbot/core/star/session_llm_manager.py +280 -0
  218. astrbot/core/star/session_plugin_manager.py +170 -0
  219. astrbot/core/star/star.py +36 -43
  220. astrbot/core/star/star_handler.py +47 -85
  221. astrbot/core/star/star_manager.py +442 -260
  222. astrbot/core/star/star_tools.py +167 -45
  223. astrbot/core/star/updator.py +17 -20
  224. astrbot/core/umop_config_router.py +106 -0
  225. astrbot/core/updator.py +38 -13
  226. astrbot/core/utils/astrbot_path.py +39 -0
  227. astrbot/core/utils/command_parser.py +1 -1
  228. astrbot/core/utils/io.py +119 -60
  229. astrbot/core/utils/log_pipe.py +1 -1
  230. astrbot/core/utils/metrics.py +11 -10
  231. astrbot/core/utils/migra_helper.py +73 -0
  232. astrbot/core/utils/path_util.py +63 -62
  233. astrbot/core/utils/pip_installer.py +37 -15
  234. astrbot/core/utils/session_lock.py +29 -0
  235. astrbot/core/utils/session_waiter.py +19 -20
  236. astrbot/core/utils/shared_preferences.py +174 -34
  237. astrbot/core/utils/t2i/__init__.py +4 -1
  238. astrbot/core/utils/t2i/local_strategy.py +386 -238
  239. astrbot/core/utils/t2i/network_strategy.py +109 -49
  240. astrbot/core/utils/t2i/renderer.py +29 -14
  241. astrbot/core/utils/t2i/template/astrbot_powershell.html +184 -0
  242. astrbot/core/utils/t2i/template_manager.py +111 -0
  243. astrbot/core/utils/tencent_record_helper.py +115 -1
  244. astrbot/core/utils/version_comparator.py +10 -13
  245. astrbot/core/zip_updator.py +112 -65
  246. astrbot/dashboard/routes/__init__.py +20 -13
  247. astrbot/dashboard/routes/auth.py +20 -9
  248. astrbot/dashboard/routes/chat.py +297 -141
  249. astrbot/dashboard/routes/config.py +652 -55
  250. astrbot/dashboard/routes/conversation.py +107 -37
  251. astrbot/dashboard/routes/file.py +26 -0
  252. astrbot/dashboard/routes/knowledge_base.py +1244 -0
  253. astrbot/dashboard/routes/log.py +27 -2
  254. astrbot/dashboard/routes/persona.py +202 -0
  255. astrbot/dashboard/routes/plugin.py +197 -139
  256. astrbot/dashboard/routes/route.py +27 -7
  257. astrbot/dashboard/routes/session_management.py +354 -0
  258. astrbot/dashboard/routes/stat.py +85 -18
  259. astrbot/dashboard/routes/static_file.py +5 -2
  260. astrbot/dashboard/routes/t2i.py +233 -0
  261. astrbot/dashboard/routes/tools.py +184 -120
  262. astrbot/dashboard/routes/update.py +59 -36
  263. astrbot/dashboard/server.py +96 -36
  264. astrbot/dashboard/utils.py +165 -0
  265. astrbot-4.7.0.dist-info/METADATA +294 -0
  266. astrbot-4.7.0.dist-info/RECORD +274 -0
  267. {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/WHEEL +1 -1
  268. astrbot/core/db/plugin/sqlite_impl.py +0 -112
  269. astrbot/core/db/sqlite_init.sql +0 -50
  270. astrbot/core/pipeline/platform_compatibility/stage.py +0 -56
  271. astrbot/core/pipeline/process_stage/method/llm_request.py +0 -606
  272. astrbot/core/platform/sources/gewechat/client.py +0 -806
  273. astrbot/core/platform/sources/gewechat/downloader.py +0 -55
  274. astrbot/core/platform/sources/gewechat/gewechat_event.py +0 -255
  275. astrbot/core/platform/sources/gewechat/gewechat_platform_adapter.py +0 -103
  276. astrbot/core/platform/sources/gewechat/xml_data_parser.py +0 -110
  277. astrbot/core/provider/sources/dashscope_source.py +0 -203
  278. astrbot/core/provider/sources/dify_source.py +0 -281
  279. astrbot/core/provider/sources/llmtuner_source.py +0 -132
  280. astrbot/core/rag/embedding/openai_source.py +0 -20
  281. astrbot/core/rag/knowledge_db_mgr.py +0 -94
  282. astrbot/core/rag/store/__init__.py +0 -9
  283. astrbot/core/rag/store/chroma_db.py +0 -42
  284. astrbot/core/utils/dify_api_client.py +0 -152
  285. astrbot-3.5.6.dist-info/METADATA +0 -249
  286. astrbot-3.5.6.dist-info/RECORD +0 -158
  287. {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/entry_points.txt +0 -0
  288. {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,53 +1,97 @@
1
- import time
2
1
  import asyncio
3
- import uuid
4
2
  import os
5
- from typing import Awaitable, Any
3
+ import time
4
+ import uuid
5
+ from collections.abc import Awaitable, Callable
6
+ from typing import Any
7
+
8
+ from astrbot import logger
9
+ from astrbot.core.message.components import Image, Plain, Record
10
+ from astrbot.core.message.message_event_result import MessageChain
6
11
  from astrbot.core.platform import (
7
- Platform,
8
12
  AstrBotMessage,
9
13
  MessageMember,
10
14
  MessageType,
15
+ Platform,
11
16
  PlatformMetadata,
12
17
  )
13
- from astrbot.core.message.message_event_result import MessageChain
14
- from astrbot.core.message.components import Plain, Image, Record # noqa: F403
15
- from astrbot import logger
16
- from astrbot.core import web_chat_queue
17
- from .webchat_event import WebChatMessageEvent
18
18
  from astrbot.core.platform.astr_message_event import MessageSesion
19
+ from astrbot.core.utils.astrbot_path import get_astrbot_data_path
20
+
19
21
  from ...register import register_platform_adapter
22
+ from .webchat_event import WebChatMessageEvent
23
+ from .webchat_queue_mgr import WebChatQueueMgr, webchat_queue_mgr
20
24
 
21
25
 
22
26
  class QueueListener:
23
- def __init__(self, queue: asyncio.Queue, callback: callable) -> None:
24
- self.queue = queue
27
+ def __init__(self, webchat_queue_mgr: WebChatQueueMgr, callback: Callable) -> None:
28
+ self.webchat_queue_mgr = webchat_queue_mgr
25
29
  self.callback = callback
30
+ self.running_tasks = set()
31
+
32
+ async def listen_to_queue(self, conversation_id: str):
33
+ """Listen to a specific conversation queue"""
34
+ queue = self.webchat_queue_mgr.get_or_create_queue(conversation_id)
35
+ while True:
36
+ try:
37
+ data = await queue.get()
38
+ await self.callback(data)
39
+ except Exception as e:
40
+ logger.error(
41
+ f"Error processing message from conversation {conversation_id}: {e}",
42
+ )
43
+ break
26
44
 
27
45
  async def run(self):
46
+ """Monitor for new conversation queues and start listeners"""
47
+ monitored_conversations = set()
48
+
28
49
  while True:
29
- data = await self.queue.get()
30
- await self.callback(data)
50
+ # Check for new conversations
51
+ current_conversations = set(self.webchat_queue_mgr.queues.keys())
52
+ new_conversations = current_conversations - monitored_conversations
53
+
54
+ # Start listeners for new conversations
55
+ for conversation_id in new_conversations:
56
+ task = asyncio.create_task(self.listen_to_queue(conversation_id))
57
+ self.running_tasks.add(task)
58
+ task.add_done_callback(self.running_tasks.discard)
59
+ monitored_conversations.add(conversation_id)
60
+ logger.debug(f"Started listener for conversation: {conversation_id}")
61
+
62
+ # Clean up monitored conversations that no longer exist
63
+ removed_conversations = monitored_conversations - current_conversations
64
+ monitored_conversations -= removed_conversations
65
+
66
+ await asyncio.sleep(1) # Check for new conversations every second
31
67
 
32
68
 
33
69
  @register_platform_adapter("webchat", "webchat")
34
70
  class WebChatAdapter(Platform):
35
71
  def __init__(
36
- self, platform_config: dict, platform_settings: dict, event_queue: asyncio.Queue
72
+ self,
73
+ platform_config: dict,
74
+ platform_settings: dict,
75
+ event_queue: asyncio.Queue,
37
76
  ) -> None:
38
77
  super().__init__(event_queue)
39
78
 
40
79
  self.config = platform_config
41
80
  self.settings = platform_settings
42
81
  self.unique_session = platform_settings["unique_session"]
43
- self.imgs_dir = "data/webchat/imgs"
82
+ self.imgs_dir = os.path.join(get_astrbot_data_path(), "webchat", "imgs")
83
+ os.makedirs(self.imgs_dir, exist_ok=True)
44
84
 
45
85
  self.metadata = PlatformMetadata(
46
- name="webchat", description="webchat", id=self.config.get("id")
86
+ name="webchat",
87
+ description="webchat",
88
+ id="webchat",
47
89
  )
48
90
 
49
91
  async def send_by_session(
50
- self, session: MessageSesion, message_chain: MessageChain
92
+ self,
93
+ session: MessageSesion,
94
+ message_chain: MessageChain,
51
95
  ):
52
96
  await WebChatMessageEvent._send(message_chain, session.session_id)
53
97
  await super().send_by_session(session, message_chain)
@@ -57,7 +101,6 @@ class WebChatAdapter(Platform):
57
101
 
58
102
  abm = AstrBotMessage()
59
103
  abm.self_id = "webchat"
60
- abm.tag = "webchat"
61
104
  abm.sender = MessageMember(username, username)
62
105
 
63
106
  abm.type = MessageType.FRIEND_MESSAGE
@@ -73,13 +116,13 @@ class WebChatAdapter(Platform):
73
116
  if isinstance(payload["image_url"], list):
74
117
  for img in payload["image_url"]:
75
118
  abm.message.append(
76
- Image.fromFileSystem(os.path.join(self.imgs_dir, img))
119
+ Image.fromFileSystem(os.path.join(self.imgs_dir, img)),
77
120
  )
78
121
  else:
79
122
  abm.message.append(
80
123
  Image.fromFileSystem(
81
- os.path.join(self.imgs_dir, payload["image_url"])
82
- )
124
+ os.path.join(self.imgs_dir, payload["image_url"]),
125
+ ),
83
126
  )
84
127
  if payload["audio_url"]:
85
128
  if isinstance(payload["audio_url"], list):
@@ -103,7 +146,7 @@ class WebChatAdapter(Platform):
103
146
  abm = await self.convert_message(data)
104
147
  await self.handle_msg(abm)
105
148
 
106
- bot = QueueListener(web_chat_queue, callback)
149
+ bot = QueueListener(webchat_queue_mgr, callback)
107
150
  return bot.run()
108
151
 
109
152
  def meta(self) -> PlatformMetadata:
@@ -117,6 +160,13 @@ class WebChatAdapter(Platform):
117
160
  session_id=message.session_id,
118
161
  )
119
162
 
163
+ _, _, payload = message.raw_message # type: ignore
164
+ message_event.set_extra("selected_provider", payload.get("selected_provider"))
165
+ message_event.set_extra("selected_model", payload.get("selected_model"))
166
+ message_event.set_extra(
167
+ "enable_streaming", payload.get("enable_streaming", True)
168
+ )
169
+
120
170
  self.commit_event(message_event)
121
171
 
122
172
  async def terminate(self):
@@ -1,13 +1,16 @@
1
+ import base64
1
2
  import os
2
3
  import uuid
3
- import base64
4
+
4
5
  from astrbot.api import logger
5
6
  from astrbot.api.event import AstrMessageEvent, MessageChain
6
- from astrbot.api.message_components import Plain, Image, Record
7
+ from astrbot.api.message_components import Image, Plain, Record
8
+ from astrbot.core.utils.astrbot_path import get_astrbot_data_path
7
9
  from astrbot.core.utils.io import download_image_by_url
8
- from astrbot.core import web_chat_back_queue
9
10
 
10
- imgs_dir = "data/webchat/imgs"
11
+ from .webchat_queue_mgr import webchat_queue_mgr
12
+
13
+ imgs_dir = os.path.join(get_astrbot_data_path(), "webchat", "imgs")
11
14
 
12
15
 
13
16
  class WebChatMessageEvent(AstrMessageEvent):
@@ -17,13 +20,18 @@ class WebChatMessageEvent(AstrMessageEvent):
17
20
 
18
21
  @staticmethod
19
22
  async def _send(message: MessageChain, session_id: str, streaming: bool = False):
23
+ cid = session_id.split("!")[-1]
24
+ web_chat_back_queue = webchat_queue_mgr.get_or_create_back_queue(cid)
20
25
  if not message:
21
26
  await web_chat_back_queue.put(
22
- {"type": "end", "data": "", "streaming": False}
27
+ {
28
+ "type": "end",
29
+ "data": "",
30
+ "streaming": False,
31
+ }, # end means this request is finished
23
32
  )
24
33
  return ""
25
34
 
26
- cid = session_id.split("!")[-1]
27
35
  data = ""
28
36
  for comp in message.chain:
29
37
  if isinstance(comp, Plain):
@@ -34,7 +42,8 @@ class WebChatMessageEvent(AstrMessageEvent):
34
42
  "cid": cid,
35
43
  "data": data,
36
44
  "streaming": streaming,
37
- }
45
+ "chain_type": message.type,
46
+ },
38
47
  )
39
48
  elif isinstance(comp, Image):
40
49
  # save image to local
@@ -63,7 +72,7 @@ class WebChatMessageEvent(AstrMessageEvent):
63
72
  "cid": cid,
64
73
  "data": data,
65
74
  "streaming": streaming,
66
- }
75
+ },
67
76
  )
68
77
  elif isinstance(comp, Record):
69
78
  # save record to local
@@ -87,7 +96,7 @@ class WebChatMessageEvent(AstrMessageEvent):
87
96
  "cid": cid,
88
97
  "data": data,
89
98
  "streaming": streaming,
90
- }
99
+ },
91
100
  )
92
101
  else:
93
102
  logger.debug(f"webchat 忽略: {comp.type}")
@@ -96,29 +105,44 @@ class WebChatMessageEvent(AstrMessageEvent):
96
105
 
97
106
  async def send(self, message: MessageChain):
98
107
  await WebChatMessageEvent._send(message, session_id=self.session_id)
99
- await web_chat_back_queue.put(
100
- {
101
- "type": "end",
102
- "data": "",
103
- "streaming": False,
104
- "cid": self.session_id.split("!")[-1],
105
- }
106
- )
107
108
  await super().send(message)
108
109
 
109
110
  async def send_streaming(self, generator, use_fallback: bool = False):
110
111
  final_data = ""
112
+ reasoning_content = ""
113
+ cid = self.session_id.split("!")[-1]
114
+ web_chat_back_queue = webchat_queue_mgr.get_or_create_back_queue(cid)
111
115
  async for chain in generator:
112
- final_data += await WebChatMessageEvent._send(
113
- chain, session_id=self.session_id, streaming=True
116
+ if chain.type == "break" and final_data:
117
+ # 分割符
118
+ await web_chat_back_queue.put(
119
+ {
120
+ "type": "break", # break means a segment end
121
+ "data": final_data,
122
+ "streaming": True,
123
+ "cid": cid,
124
+ },
125
+ )
126
+ final_data = ""
127
+ continue
128
+
129
+ r = await WebChatMessageEvent._send(
130
+ chain,
131
+ session_id=self.session_id,
132
+ streaming=True,
114
133
  )
134
+ if chain.type == "reasoning":
135
+ reasoning_content += chain.get_plain_text()
136
+ else:
137
+ final_data += r
115
138
 
116
139
  await web_chat_back_queue.put(
117
140
  {
118
- "type": "end",
141
+ "type": "complete", # complete means we return the final result
119
142
  "data": final_data,
143
+ "reasoning": reasoning_content,
120
144
  "streaming": True,
121
- "cid": self.session_id.split("!")[-1],
122
- }
145
+ "cid": cid,
146
+ },
123
147
  )
124
148
  await super().send_streaming(generator, use_fallback)
@@ -0,0 +1,35 @@
1
+ import asyncio
2
+
3
+
4
+ class WebChatQueueMgr:
5
+ def __init__(self) -> None:
6
+ self.queues = {}
7
+ """Conversation ID to asyncio.Queue mapping"""
8
+ self.back_queues = {}
9
+ """Conversation ID to asyncio.Queue mapping for responses"""
10
+
11
+ def get_or_create_queue(self, conversation_id: str) -> asyncio.Queue:
12
+ """Get or create a queue for the given conversation ID"""
13
+ if conversation_id not in self.queues:
14
+ self.queues[conversation_id] = asyncio.Queue()
15
+ return self.queues[conversation_id]
16
+
17
+ def get_or_create_back_queue(self, conversation_id: str) -> asyncio.Queue:
18
+ """Get or create a back queue for the given conversation ID"""
19
+ if conversation_id not in self.back_queues:
20
+ self.back_queues[conversation_id] = asyncio.Queue()
21
+ return self.back_queues[conversation_id]
22
+
23
+ def remove_queues(self, conversation_id: str):
24
+ """Remove queues for the given conversation ID"""
25
+ if conversation_id in self.queues:
26
+ del self.queues[conversation_id]
27
+ if conversation_id in self.back_queues:
28
+ del self.back_queues[conversation_id]
29
+
30
+ def has_queue(self, conversation_id: str) -> bool:
31
+ """Check if a queue exists for the given conversation ID"""
32
+ return conversation_id in self.queues
33
+
34
+
35
+ webchat_queue_mgr = WebChatQueueMgr()