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
@@ -0,0 +1,197 @@
1
+ import os
2
+ import uuid
3
+
4
+ import aiohttp
5
+ from xinference_client.client.restful.async_restful_client import (
6
+ AsyncClient as Client,
7
+ )
8
+
9
+ from astrbot.core import logger
10
+ from astrbot.core.utils.astrbot_path import get_astrbot_data_path
11
+ from astrbot.core.utils.tencent_record_helper import tencent_silk_to_wav
12
+
13
+ from ..entities import ProviderType
14
+ from ..provider import STTProvider
15
+ from ..register import register_provider_adapter
16
+
17
+
18
+ @register_provider_adapter(
19
+ "xinference_stt",
20
+ "Xinference STT",
21
+ provider_type=ProviderType.SPEECH_TO_TEXT,
22
+ )
23
+ class ProviderXinferenceSTT(STTProvider):
24
+ def __init__(self, provider_config: dict, provider_settings: dict) -> None:
25
+ super().__init__(provider_config, provider_settings)
26
+ self.provider_config = provider_config
27
+ self.provider_settings = provider_settings
28
+ self.base_url = provider_config.get("api_base", "http://127.0.0.1:9997")
29
+ self.base_url = self.base_url.rstrip("/")
30
+ self.timeout = provider_config.get("timeout", 180)
31
+ self.model_name = provider_config.get("model", "whisper-large-v3")
32
+ self.api_key = provider_config.get("api_key")
33
+ self.launch_model_if_not_running = provider_config.get(
34
+ "launch_model_if_not_running",
35
+ False,
36
+ )
37
+ self.client = None
38
+ self.model_uid = None
39
+
40
+ async def initialize(self):
41
+ if self.api_key:
42
+ logger.info("Xinference STT: Using API key for authentication.")
43
+ self.client = Client(self.base_url, api_key=self.api_key)
44
+ else:
45
+ logger.info("Xinference STT: No API key provided.")
46
+ self.client = Client(self.base_url)
47
+
48
+ try:
49
+ running_models = await self.client.list_models()
50
+ for uid, model_spec in running_models.items():
51
+ if model_spec.get("model_name") == self.model_name:
52
+ logger.info(
53
+ f"Model '{self.model_name}' is already running with UID: {uid}",
54
+ )
55
+ self.model_uid = uid
56
+ break
57
+
58
+ if self.model_uid is None:
59
+ if self.launch_model_if_not_running:
60
+ logger.info(f"Launching {self.model_name} model...")
61
+ self.model_uid = await self.client.launch_model(
62
+ model_name=self.model_name,
63
+ model_type="audio",
64
+ )
65
+ logger.info("Model launched.")
66
+ else:
67
+ logger.warning(
68
+ f"Model '{self.model_name}' is not running and auto-launch is disabled. Provider will not be available.",
69
+ )
70
+ return
71
+
72
+ except Exception as e:
73
+ logger.error(f"Failed to initialize Xinference model: {e}")
74
+ logger.debug(
75
+ f"Xinference initialization failed with exception: {e}",
76
+ exc_info=True,
77
+ )
78
+
79
+ async def get_text(self, audio_url: str) -> str:
80
+ if not self.model_uid or self.client is None or self.client.session is None:
81
+ logger.error("Xinference STT model is not initialized.")
82
+ return ""
83
+
84
+ audio_bytes = None
85
+ temp_files = []
86
+ is_tencent = False
87
+
88
+ try:
89
+ # 1. Get audio bytes
90
+ if audio_url.startswith("http"):
91
+ if "multimedia.nt.qq.com.cn" in audio_url:
92
+ is_tencent = True
93
+ async with aiohttp.ClientSession() as session:
94
+ async with session.get(audio_url, timeout=self.timeout) as resp:
95
+ if resp.status == 200:
96
+ audio_bytes = await resp.read()
97
+ else:
98
+ logger.error(
99
+ f"Failed to download audio from {audio_url}, status: {resp.status}",
100
+ )
101
+ return ""
102
+ elif os.path.exists(audio_url):
103
+ with open(audio_url, "rb") as f:
104
+ audio_bytes = f.read()
105
+ else:
106
+ logger.error(f"File not found: {audio_url}")
107
+ return ""
108
+
109
+ if not audio_bytes:
110
+ logger.error("Audio bytes are empty.")
111
+ return ""
112
+
113
+ # 2. Check for conversion
114
+ needs_conversion = False
115
+ if (
116
+ audio_url.endswith((".amr", ".silk"))
117
+ or is_tencent
118
+ or b"SILK" in audio_bytes[:8]
119
+ ):
120
+ needs_conversion = True
121
+
122
+ # 3. Perform conversion if needed
123
+ if needs_conversion:
124
+ logger.info("Audio requires conversion, using temporary files...")
125
+ temp_dir = os.path.join(get_astrbot_data_path(), "temp")
126
+ os.makedirs(temp_dir, exist_ok=True)
127
+
128
+ input_path = os.path.join(temp_dir, str(uuid.uuid4()))
129
+ output_path = os.path.join(temp_dir, str(uuid.uuid4()) + ".wav")
130
+ temp_files.extend([input_path, output_path])
131
+
132
+ with open(input_path, "wb") as f:
133
+ f.write(audio_bytes)
134
+
135
+ logger.info("Converting silk/amr file to wav ...")
136
+ await tencent_silk_to_wav(input_path, output_path)
137
+
138
+ with open(output_path, "rb") as f:
139
+ audio_bytes = f.read()
140
+
141
+ # 4. Transcribe
142
+ # 官方asyncCLient的客户端似乎实现有点问题,这里直接用aiohttp实现openai标准兼容请求,提交issue等待官方修复后再改回来
143
+ url = f"{self.base_url}/v1/audio/transcriptions"
144
+ headers = {
145
+ "accept": "application/json",
146
+ }
147
+ if self.client and self.client._headers:
148
+ headers.update(self.client._headers)
149
+
150
+ data = aiohttp.FormData()
151
+ data.add_field("model", self.model_uid)
152
+ data.add_field(
153
+ "file",
154
+ audio_bytes,
155
+ filename="audio.wav",
156
+ content_type="audio/wav",
157
+ )
158
+
159
+ async with self.client.session.post(
160
+ url,
161
+ data=data,
162
+ headers=headers,
163
+ timeout=self.timeout,
164
+ ) as resp:
165
+ if resp.status == 200:
166
+ result = await resp.json()
167
+ text = result.get("text", "")
168
+ logger.debug(f"Xinference STT result: {text}")
169
+ return text
170
+ error_text = await resp.text()
171
+ logger.error(
172
+ f"Xinference STT transcription failed with status {resp.status}: {error_text}",
173
+ )
174
+ return ""
175
+
176
+ except Exception as e:
177
+ logger.error(f"Xinference STT failed: {e}")
178
+ logger.debug(f"Xinference STT failed with exception: {e}", exc_info=True)
179
+ return ""
180
+ finally:
181
+ # 5. Cleanup
182
+ for temp_file in temp_files:
183
+ try:
184
+ if os.path.exists(temp_file):
185
+ os.remove(temp_file)
186
+ logger.debug(f"Removed temporary file: {temp_file}")
187
+ except Exception as e:
188
+ logger.error(f"Failed to remove temporary file {temp_file}: {e}")
189
+
190
+ async def terminate(self) -> None:
191
+ """关闭客户端会话"""
192
+ if self.client:
193
+ logger.info("Closing Xinference STT client...")
194
+ try:
195
+ await self.client.close()
196
+ except Exception as e:
197
+ logger.error(f"Failed to close Xinference client: {e}", exc_info=True)
@@ -1,83 +1,16 @@
1
- from astrbot.core.db import BaseDatabase
2
- from astrbot import logger
3
- from astrbot.core.provider.func_tool_manager import FuncCall
4
- from typing import List
1
+ # This file was originally created to adapt to glm-4v-flash, which only supports one image in the context.
2
+ # It is no longer specifically adapted to Zhipu's models. To ensure compatibility, this
3
+
4
+
5
5
  from ..register import register_provider_adapter
6
- from astrbot.core.provider.entities import LLMResponse
7
6
  from .openai_source import ProviderOpenAIOfficial
8
7
 
9
8
 
10
- @register_provider_adapter("zhipu_chat_completion", "智浦 Chat Completion 提供商适配器")
9
+ @register_provider_adapter("zhipu_chat_completion", "智谱 Chat Completion 提供商适配器")
11
10
  class ProviderZhipu(ProviderOpenAIOfficial):
12
11
  def __init__(
13
12
  self,
14
13
  provider_config: dict,
15
14
  provider_settings: dict,
16
- db_helper: BaseDatabase,
17
- persistant_history=True,
18
- default_persona=None,
19
15
  ) -> None:
20
- super().__init__(
21
- provider_config,
22
- provider_settings,
23
- db_helper,
24
- persistant_history,
25
- default_persona,
26
- )
27
-
28
- async def text_chat(
29
- self,
30
- prompt: str,
31
- session_id: str = None,
32
- image_urls: List[str] = None,
33
- func_tool: FuncCall = None,
34
- contexts=[],
35
- system_prompt=None,
36
- **kwargs,
37
- ) -> LLMResponse:
38
- new_record = await self.assemble_context(prompt, image_urls)
39
- context_query = []
40
-
41
- context_query = [*contexts, new_record]
42
-
43
- model_cfgs: dict = self.provider_config.get("model_config", {})
44
- model = self.get_model()
45
- # glm-4v-flash 只支持一张图片
46
- if model.lower() == "glm-4v-flash" and image_urls and len(context_query) > 1:
47
- logger.debug("glm-4v-flash 只支持一张图片,将只保留最后一张图片")
48
- logger.debug(context_query)
49
- new_context_query_ = []
50
- for i in range(0, len(context_query) - 1, 2):
51
- if isinstance(context_query[i].get("content", ""), list):
52
- continue
53
- new_context_query_.append(context_query[i])
54
- new_context_query_.append(context_query[i + 1])
55
- new_context_query_.append(context_query[-1]) # 保留最后一条记录
56
- context_query = new_context_query_
57
- logger.debug(context_query)
58
-
59
- if system_prompt:
60
- context_query.insert(0, {"role": "system", "content": system_prompt})
61
-
62
- payloads = {"messages": context_query, **model_cfgs}
63
- try:
64
- llm_response = await self._query(payloads, func_tool)
65
- return llm_response
66
- except Exception as e:
67
- if "maximum context length" in str(e):
68
- retry_cnt = 10
69
- while retry_cnt > 0:
70
- logger.warning(
71
- f"请求失败:{e}。上下文长度超过限制。尝试弹出最早的记录然后重试。"
72
- )
73
- try:
74
- self.pop_record(session_id)
75
- llm_response = await self._query(payloads, func_tool)
76
- break
77
- except Exception as e:
78
- if "maximum context length" in str(e):
79
- retry_cnt -= 1
80
- else:
81
- raise e
82
- else:
83
- raise e
16
+ super().__init__(provider_config, provider_settings)
@@ -1,32 +1,64 @@
1
- from .star import StarMetadata
2
- from .star_manager import PluginManager
3
- from .context import Context
4
- from astrbot.core.provider import Provider
5
- from astrbot.core.utils.command_parser import CommandParserMixin
6
1
  from astrbot.core import html_renderer
2
+ from astrbot.core.provider import Provider
7
3
  from astrbot.core.star.star_tools import StarTools
4
+ from astrbot.core.utils.command_parser import CommandParserMixin
5
+
6
+ from .context import Context
7
+ from .star import StarMetadata, star_map, star_registry
8
+ from .star_manager import PluginManager
8
9
 
9
10
 
10
11
  class Star(CommandParserMixin):
11
12
  """所有插件(Star)的父类,所有插件都应该继承于这个类"""
12
13
 
13
- def __init__(self, context: Context):
14
+ def __init__(self, context: Context, config: dict | None = None):
14
15
  StarTools.initialize(context)
15
16
  self.context = context
16
17
 
18
+ def __init_subclass__(cls, **kwargs):
19
+ super().__init_subclass__(**kwargs)
20
+ if not star_map.get(cls.__module__):
21
+ metadata = StarMetadata(
22
+ star_cls_type=cls,
23
+ module_path=cls.__module__,
24
+ )
25
+ star_map[cls.__module__] = metadata
26
+ star_registry.append(metadata)
27
+ else:
28
+ star_map[cls.__module__].star_cls_type = cls
29
+ star_map[cls.__module__].module_path = cls.__module__
30
+
17
31
  async def text_to_image(self, text: str, return_url=True) -> str:
18
32
  """将文本转换为图片"""
19
- return await html_renderer.render_t2i(text, return_url=return_url)
33
+ return await html_renderer.render_t2i(
34
+ text,
35
+ return_url=return_url,
36
+ template_name=self.context._config.get("t2i_active_template"),
37
+ )
20
38
 
21
- async def html_render(self, tmpl: str, data: dict, return_url=True) -> str:
39
+ async def html_render(
40
+ self,
41
+ tmpl: str,
42
+ data: dict,
43
+ return_url=True,
44
+ options: dict | None = None,
45
+ ) -> str:
22
46
  """渲染 HTML"""
23
47
  return await html_renderer.render_custom_template(
24
- tmpl, data, return_url=return_url
48
+ tmpl,
49
+ data,
50
+ return_url=return_url,
51
+ options=options,
25
52
  )
26
53
 
54
+ async def initialize(self):
55
+ """当插件被激活时会调用这个方法"""
56
+
27
57
  async def terminate(self):
28
58
  """当插件被禁用、重载插件时会调用这个方法"""
29
- pass
59
+
60
+ def __del__(self):
61
+ """[Deprecated] 当插件被禁用、重载插件时会调用这个方法"""
30
62
 
31
63
 
32
- __all__ = ["Star", "StarMetadata", "PluginManager", "Context", "Provider", "StarTools"]
64
+ __all__ = ["Context", "PluginManager", "Provider", "Star", "StarMetadata", "StarTools"]
@@ -1,22 +1,20 @@
1
- """
2
- 此功能已过时,参考 https://astrbot.app/dev/plugin.html#%E6%B3%A8%E5%86%8C%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE-beta
3
- """
1
+ """此功能已过时,参考 https://astrbot.app/dev/plugin.html#%E6%B3%A8%E5%86%8C%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE-beta"""
4
2
 
5
- from typing import Union
6
- import os
7
3
  import json
4
+ import os
8
5
 
6
+ from astrbot.core.utils.astrbot_path import get_astrbot_data_path
9
7
 
10
- def load_config(namespace: str) -> Union[dict, bool]:
11
- """
12
- 从配置文件中加载配置。
8
+
9
+ def load_config(namespace: str) -> dict | bool:
10
+ """从配置文件中加载配置。
13
11
  namespace: str, 配置的唯一识别符,也就是配置文件的名字。
14
12
  返回值: 当配置文件存在时,返回 namespace 对应配置文件的内容dict,否则返回 False。
15
13
  """
16
- path = f"data/config/{namespace}.json"
14
+ path = os.path.join(get_astrbot_data_path(), "config", f"{namespace}.json")
17
15
  if not os.path.exists(path):
18
16
  return False
19
- with open(path, "r", encoding="utf-8-sig") as f:
17
+ with open(path, encoding="utf-8-sig") as f:
20
18
  ret = {}
21
19
  data = json.load(f)
22
20
  for k in data:
@@ -25,8 +23,7 @@ def load_config(namespace: str) -> Union[dict, bool]:
25
23
 
26
24
 
27
25
  def put_config(namespace: str, name: str, key: str, value, description: str):
28
- """
29
- 将配置项写入以namespace为名字的配置文件,如果key不存在于目标配置文件中。当前 value 仅支持 str, int, float, bool, list 类型(暂不支持 dict)。
26
+ """将配置项写入以namespace为名字的配置文件,如果key不存在于目标配置文件中。当前 value 仅支持 str, int, float, bool, list 类型(暂不支持 dict)。
30
27
  namespace: str, 配置的唯一识别符,也就是配置文件的名字。
31
28
  name: str, 配置项的显示名字。
32
29
  key: str, 配置项的键。
@@ -43,11 +40,14 @@ def put_config(namespace: str, name: str, key: str, value, description: str):
43
40
  raise ValueError("key 只支持 str 类型。")
44
41
  if not isinstance(value, (str, int, float, bool, list)):
45
42
  raise ValueError("value 只支持 str, int, float, bool, list 类型。")
46
- path = f"data/config/{namespace}.json"
43
+
44
+ config_dir = os.path.join(get_astrbot_data_path(), "config")
45
+ path = os.path.join(config_dir, f"{namespace}.json")
46
+
47
47
  if not os.path.exists(path):
48
48
  with open(path, "w", encoding="utf-8-sig") as f:
49
49
  f.write("{}")
50
- with open(path, "r", encoding="utf-8-sig") as f:
50
+ with open(path, encoding="utf-8-sig") as f:
51
51
  d = json.load(f)
52
52
  assert isinstance(d, dict)
53
53
  if key not in d:
@@ -65,16 +65,15 @@ def put_config(namespace: str, name: str, key: str, value, description: str):
65
65
 
66
66
 
67
67
  def update_config(namespace: str, key: str, value):
68
- """
69
- 更新配置文件中的配置项。
68
+ """更新配置文件中的配置项。
70
69
  namespace: str, 配置的唯一识别符,也就是配置文件的名字。
71
70
  key: str, 配置项的键。
72
71
  value: str, int, float, bool, list, 配置项的值。
73
72
  """
74
- path = f"data/config/{namespace}.json"
73
+ path = os.path.join(get_astrbot_data_path(), "config", f"{namespace}.json")
75
74
  if not os.path.exists(path):
76
75
  raise FileNotFoundError(f"配置文件 {namespace}.json 不存在。")
77
- with open(path, "r", encoding="utf-8-sig") as f:
76
+ with open(path, encoding="utf-8-sig") as f:
78
77
  d = json.load(f)
79
78
  assert isinstance(d, dict)
80
79
  if key not in d: