AstrBot 4.5.1__py3-none-any.whl → 4.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- astrbot/api/__init__.py +10 -11
- astrbot/api/event/__init__.py +5 -6
- astrbot/api/event/filter/__init__.py +37 -36
- astrbot/api/platform/__init__.py +7 -8
- astrbot/api/provider/__init__.py +7 -7
- astrbot/api/star/__init__.py +3 -4
- astrbot/api/util/__init__.py +2 -2
- astrbot/cli/__main__.py +5 -5
- astrbot/cli/commands/__init__.py +3 -3
- astrbot/cli/commands/cmd_conf.py +19 -16
- astrbot/cli/commands/cmd_init.py +3 -2
- astrbot/cli/commands/cmd_plug.py +8 -10
- astrbot/cli/commands/cmd_run.py +5 -6
- astrbot/cli/utils/__init__.py +6 -6
- astrbot/cli/utils/basic.py +14 -14
- astrbot/cli/utils/plugin.py +24 -15
- astrbot/cli/utils/version_comparator.py +10 -12
- astrbot/core/__init__.py +8 -6
- astrbot/core/agent/agent.py +3 -2
- astrbot/core/agent/handoff.py +6 -2
- astrbot/core/agent/hooks.py +9 -6
- astrbot/core/agent/mcp_client.py +50 -15
- astrbot/core/agent/message.py +168 -0
- astrbot/core/agent/response.py +2 -1
- astrbot/core/agent/run_context.py +2 -3
- astrbot/core/agent/runners/base.py +10 -13
- astrbot/core/agent/runners/tool_loop_agent_runner.py +52 -51
- astrbot/core/agent/tool.py +60 -41
- astrbot/core/agent/tool_executor.py +9 -3
- astrbot/core/astr_agent_context.py +3 -1
- astrbot/core/astrbot_config_mgr.py +29 -9
- astrbot/core/config/__init__.py +2 -2
- astrbot/core/config/astrbot_config.py +28 -26
- astrbot/core/config/default.py +4 -6
- astrbot/core/conversation_mgr.py +105 -36
- astrbot/core/core_lifecycle.py +68 -54
- astrbot/core/db/__init__.py +33 -18
- astrbot/core/db/migration/helper.py +12 -10
- astrbot/core/db/migration/migra_3_to_4.py +53 -34
- astrbot/core/db/migration/migra_45_to_46.py +1 -1
- astrbot/core/db/migration/shared_preferences_v3.py +2 -1
- astrbot/core/db/migration/sqlite_v3.py +26 -23
- astrbot/core/db/po.py +27 -18
- astrbot/core/db/sqlite.py +74 -45
- astrbot/core/db/vec_db/base.py +10 -14
- astrbot/core/db/vec_db/faiss_impl/document_storage.py +90 -77
- astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +9 -3
- astrbot/core/db/vec_db/faiss_impl/vec_db.py +36 -31
- astrbot/core/event_bus.py +8 -6
- astrbot/core/file_token_service.py +6 -5
- astrbot/core/initial_loader.py +7 -5
- astrbot/core/knowledge_base/chunking/__init__.py +1 -3
- astrbot/core/knowledge_base/chunking/base.py +1 -0
- astrbot/core/knowledge_base/chunking/fixed_size.py +2 -0
- astrbot/core/knowledge_base/chunking/recursive.py +16 -10
- astrbot/core/knowledge_base/kb_db_sqlite.py +50 -48
- astrbot/core/knowledge_base/kb_helper.py +30 -17
- astrbot/core/knowledge_base/kb_mgr.py +6 -7
- astrbot/core/knowledge_base/models.py +10 -4
- astrbot/core/knowledge_base/parsers/__init__.py +3 -5
- astrbot/core/knowledge_base/parsers/base.py +1 -0
- astrbot/core/knowledge_base/parsers/markitdown_parser.py +2 -1
- astrbot/core/knowledge_base/parsers/pdf_parser.py +2 -1
- astrbot/core/knowledge_base/parsers/text_parser.py +1 -0
- astrbot/core/knowledge_base/parsers/util.py +1 -1
- astrbot/core/knowledge_base/retrieval/__init__.py +6 -8
- astrbot/core/knowledge_base/retrieval/manager.py +17 -14
- astrbot/core/knowledge_base/retrieval/rank_fusion.py +7 -3
- astrbot/core/knowledge_base/retrieval/sparse_retriever.py +11 -5
- astrbot/core/log.py +21 -13
- astrbot/core/message/components.py +123 -217
- astrbot/core/message/message_event_result.py +24 -24
- astrbot/core/persona_mgr.py +20 -11
- astrbot/core/pipeline/__init__.py +7 -7
- astrbot/core/pipeline/content_safety_check/stage.py +13 -9
- astrbot/core/pipeline/content_safety_check/strategies/__init__.py +1 -2
- astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +12 -13
- astrbot/core/pipeline/content_safety_check/strategies/keywords.py +1 -0
- astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
- astrbot/core/pipeline/context.py +4 -1
- astrbot/core/pipeline/context_utils.py +77 -7
- astrbot/core/pipeline/preprocess_stage/stage.py +12 -9
- astrbot/core/pipeline/process_stage/method/llm_request.py +125 -72
- astrbot/core/pipeline/process_stage/method/star_request.py +19 -17
- astrbot/core/pipeline/process_stage/stage.py +13 -10
- astrbot/core/pipeline/process_stage/utils.py +6 -5
- astrbot/core/pipeline/rate_limit_check/stage.py +37 -36
- astrbot/core/pipeline/respond/stage.py +23 -20
- astrbot/core/pipeline/result_decorate/stage.py +31 -23
- astrbot/core/pipeline/scheduler.py +12 -8
- astrbot/core/pipeline/session_status_check/stage.py +12 -8
- astrbot/core/pipeline/stage.py +10 -4
- astrbot/core/pipeline/waking_check/stage.py +24 -18
- astrbot/core/pipeline/whitelist_check/stage.py +10 -7
- astrbot/core/platform/__init__.py +6 -6
- astrbot/core/platform/astr_message_event.py +76 -110
- astrbot/core/platform/astrbot_message.py +11 -13
- astrbot/core/platform/manager.py +16 -15
- astrbot/core/platform/message_session.py +5 -3
- astrbot/core/platform/platform.py +16 -24
- astrbot/core/platform/platform_metadata.py +4 -4
- astrbot/core/platform/register.py +8 -8
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +23 -15
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +51 -33
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +42 -27
- astrbot/core/platform/sources/dingtalk/dingtalk_event.py +7 -3
- astrbot/core/platform/sources/discord/client.py +9 -6
- astrbot/core/platform/sources/discord/components.py +18 -14
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +45 -30
- astrbot/core/platform/sources/discord/discord_platform_event.py +38 -30
- astrbot/core/platform/sources/lark/lark_adapter.py +23 -17
- astrbot/core/platform/sources/lark/lark_event.py +21 -14
- astrbot/core/platform/sources/misskey/misskey_adapter.py +107 -67
- astrbot/core/platform/sources/misskey/misskey_api.py +153 -129
- astrbot/core/platform/sources/misskey/misskey_event.py +20 -15
- astrbot/core/platform/sources/misskey/misskey_utils.py +74 -62
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +63 -44
- astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +41 -26
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +36 -17
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_event.py +3 -1
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +12 -7
- astrbot/core/platform/sources/satori/satori_adapter.py +56 -38
- astrbot/core/platform/sources/satori/satori_event.py +34 -25
- astrbot/core/platform/sources/slack/client.py +11 -9
- astrbot/core/platform/sources/slack/slack_adapter.py +52 -36
- astrbot/core/platform/sources/slack/slack_event.py +34 -24
- astrbot/core/platform/sources/telegram/tg_adapter.py +38 -18
- astrbot/core/platform/sources/telegram/tg_event.py +32 -18
- astrbot/core/platform/sources/webchat/webchat_adapter.py +27 -17
- astrbot/core/platform/sources/webchat/webchat_event.py +14 -10
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +115 -120
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +9 -8
- astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +15 -16
- astrbot/core/platform/sources/wecom/wecom_adapter.py +35 -18
- astrbot/core/platform/sources/wecom/wecom_event.py +55 -48
- astrbot/core/platform/sources/wecom/wecom_kf.py +34 -44
- astrbot/core/platform/sources/wecom/wecom_kf_message.py +26 -10
- astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +18 -10
- astrbot/core/platform/sources/wecom_ai_bot/__init__.py +3 -5
- astrbot/core/platform/sources/wecom_ai_bot/ierror.py +0 -1
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +61 -37
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +67 -28
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +8 -9
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +18 -9
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +14 -12
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +22 -12
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +40 -26
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +47 -45
- astrbot/core/platform_message_history_mgr.py +5 -3
- astrbot/core/provider/__init__.py +2 -3
- astrbot/core/provider/entites.py +8 -8
- astrbot/core/provider/entities.py +61 -75
- astrbot/core/provider/func_tool_manager.py +59 -55
- astrbot/core/provider/manager.py +32 -22
- astrbot/core/provider/provider.py +72 -46
- astrbot/core/provider/register.py +7 -7
- astrbot/core/provider/sources/anthropic_source.py +48 -30
- astrbot/core/provider/sources/azure_tts_source.py +17 -13
- astrbot/core/provider/sources/coze_api_client.py +27 -17
- astrbot/core/provider/sources/coze_source.py +104 -87
- astrbot/core/provider/sources/dashscope_source.py +18 -11
- astrbot/core/provider/sources/dashscope_tts.py +36 -23
- astrbot/core/provider/sources/dify_source.py +25 -20
- astrbot/core/provider/sources/edge_tts_source.py +21 -17
- astrbot/core/provider/sources/fishaudio_tts_api_source.py +22 -14
- astrbot/core/provider/sources/gemini_embedding_source.py +12 -13
- astrbot/core/provider/sources/gemini_source.py +72 -58
- astrbot/core/provider/sources/gemini_tts_source.py +8 -6
- astrbot/core/provider/sources/gsv_selfhosted_source.py +17 -14
- astrbot/core/provider/sources/gsvi_tts_source.py +11 -7
- astrbot/core/provider/sources/minimax_tts_api_source.py +50 -40
- astrbot/core/provider/sources/openai_embedding_source.py +6 -8
- astrbot/core/provider/sources/openai_source.py +77 -69
- astrbot/core/provider/sources/openai_tts_api_source.py +14 -6
- astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
- astrbot/core/provider/sources/vllm_rerank_source.py +10 -4
- astrbot/core/provider/sources/volcengine_tts.py +38 -31
- astrbot/core/provider/sources/whisper_api_source.py +14 -12
- astrbot/core/provider/sources/whisper_selfhosted_source.py +15 -11
- astrbot/core/provider/sources/xinference_rerank_source.py +16 -8
- astrbot/core/provider/sources/xinference_stt_provider.py +35 -25
- astrbot/core/star/__init__.py +16 -11
- astrbot/core/star/config.py +10 -15
- astrbot/core/star/context.py +97 -75
- astrbot/core/star/filter/__init__.py +4 -3
- astrbot/core/star/filter/command.py +30 -28
- astrbot/core/star/filter/command_group.py +27 -24
- astrbot/core/star/filter/custom_filter.py +6 -5
- astrbot/core/star/filter/event_message_type.py +4 -2
- astrbot/core/star/filter/permission.py +4 -2
- astrbot/core/star/filter/platform_adapter_type.py +4 -2
- astrbot/core/star/filter/regex.py +4 -2
- astrbot/core/star/register/__init__.py +19 -19
- astrbot/core/star/register/star.py +6 -2
- astrbot/core/star/register/star_handler.py +96 -73
- astrbot/core/star/session_llm_manager.py +48 -14
- astrbot/core/star/session_plugin_manager.py +29 -15
- astrbot/core/star/star.py +1 -2
- astrbot/core/star/star_handler.py +13 -8
- astrbot/core/star/star_manager.py +151 -59
- astrbot/core/star/star_tools.py +44 -37
- astrbot/core/star/updator.py +10 -10
- astrbot/core/umop_config_router.py +10 -4
- astrbot/core/updator.py +13 -5
- astrbot/core/utils/astrbot_path.py +3 -5
- astrbot/core/utils/dify_api_client.py +33 -15
- astrbot/core/utils/io.py +66 -42
- astrbot/core/utils/log_pipe.py +1 -1
- astrbot/core/utils/metrics.py +7 -7
- astrbot/core/utils/path_util.py +15 -16
- astrbot/core/utils/pip_installer.py +5 -5
- astrbot/core/utils/session_waiter.py +19 -20
- astrbot/core/utils/shared_preferences.py +45 -20
- astrbot/core/utils/t2i/__init__.py +4 -1
- astrbot/core/utils/t2i/network_strategy.py +35 -26
- astrbot/core/utils/t2i/renderer.py +11 -5
- astrbot/core/utils/t2i/template_manager.py +14 -15
- astrbot/core/utils/tencent_record_helper.py +19 -13
- astrbot/core/utils/version_comparator.py +10 -13
- astrbot/core/zip_updator.py +43 -40
- astrbot/dashboard/routes/__init__.py +18 -18
- astrbot/dashboard/routes/auth.py +10 -8
- astrbot/dashboard/routes/chat.py +30 -21
- astrbot/dashboard/routes/config.py +92 -75
- astrbot/dashboard/routes/conversation.py +46 -39
- astrbot/dashboard/routes/file.py +4 -2
- astrbot/dashboard/routes/knowledge_base.py +47 -40
- astrbot/dashboard/routes/log.py +9 -4
- astrbot/dashboard/routes/persona.py +19 -16
- astrbot/dashboard/routes/plugin.py +69 -55
- astrbot/dashboard/routes/route.py +3 -1
- astrbot/dashboard/routes/session_management.py +130 -116
- astrbot/dashboard/routes/stat.py +34 -34
- astrbot/dashboard/routes/t2i.py +15 -12
- astrbot/dashboard/routes/tools.py +47 -52
- astrbot/dashboard/routes/update.py +32 -28
- astrbot/dashboard/server.py +30 -26
- astrbot/dashboard/utils.py +8 -4
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/METADATA +2 -1
- astrbot-4.5.2.dist-info/RECORD +261 -0
- astrbot-4.5.1.dist-info/RECORD +0 -260
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/WHEEL +0 -0
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/entry_points.txt +0 -0
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import aiohttp
|
|
2
|
+
|
|
2
3
|
from astrbot import logger
|
|
4
|
+
|
|
5
|
+
from ..entities import ProviderType, RerankResult
|
|
3
6
|
from ..provider import RerankProvider
|
|
4
7
|
from ..register import register_provider_adapter
|
|
5
|
-
from ..entities import ProviderType, RerankResult
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
@register_provider_adapter(
|
|
@@ -30,7 +32,10 @@ class VLLMRerankProvider(RerankProvider):
|
|
|
30
32
|
)
|
|
31
33
|
|
|
32
34
|
async def rerank(
|
|
33
|
-
self,
|
|
35
|
+
self,
|
|
36
|
+
query: str,
|
|
37
|
+
documents: list[str],
|
|
38
|
+
top_n: int | None = None,
|
|
34
39
|
) -> list[RerankResult]:
|
|
35
40
|
payload = {
|
|
36
41
|
"query": query,
|
|
@@ -40,14 +45,15 @@ class VLLMRerankProvider(RerankProvider):
|
|
|
40
45
|
if top_n is not None:
|
|
41
46
|
payload["top_n"] = top_n
|
|
42
47
|
async with self.client.post(
|
|
43
|
-
f"{self.base_url}/v1/rerank",
|
|
48
|
+
f"{self.base_url}/v1/rerank",
|
|
49
|
+
json=payload,
|
|
44
50
|
) as response:
|
|
45
51
|
response_data = await response.json()
|
|
46
52
|
results = response_data.get("results", [])
|
|
47
53
|
|
|
48
54
|
if not results:
|
|
49
55
|
logger.warning(
|
|
50
|
-
f"Rerank API 返回了空的列表数据。原始响应: {response_data}"
|
|
56
|
+
f"Rerank API 返回了空的列表数据。原始响应: {response_data}",
|
|
51
57
|
)
|
|
52
58
|
|
|
53
59
|
return [
|
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
import
|
|
1
|
+
import asyncio
|
|
2
2
|
import base64
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
5
|
import traceback
|
|
6
|
-
import
|
|
6
|
+
import uuid
|
|
7
|
+
|
|
7
8
|
import aiohttp
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
from astrbot import logger
|
|
11
|
+
|
|
9
12
|
from ..entities import ProviderType
|
|
13
|
+
from ..provider import TTSProvider
|
|
10
14
|
from ..register import register_provider_adapter
|
|
11
|
-
from astrbot import logger
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
@register_provider_adapter(
|
|
15
|
-
"volcengine_tts",
|
|
18
|
+
"volcengine_tts",
|
|
19
|
+
"火山引擎 TTS",
|
|
20
|
+
provider_type=ProviderType.TEXT_TO_SPEECH,
|
|
16
21
|
)
|
|
17
22
|
class ProviderVolcengineTTS(TTSProvider):
|
|
18
23
|
def __init__(self, provider_config: dict, provider_settings: dict) -> None:
|
|
@@ -23,7 +28,8 @@ class ProviderVolcengineTTS(TTSProvider):
|
|
|
23
28
|
self.voice_type = provider_config.get("volcengine_voice_type", "")
|
|
24
29
|
self.speed_ratio = provider_config.get("volcengine_speed_ratio", 1.0)
|
|
25
30
|
self.api_base = provider_config.get(
|
|
26
|
-
"api_base",
|
|
31
|
+
"api_base",
|
|
32
|
+
"https://openspeech.bytedance.com/api/v1/tts",
|
|
27
33
|
)
|
|
28
34
|
self.timeout = provider_config.get("timeout", 20)
|
|
29
35
|
|
|
@@ -66,43 +72,44 @@ class ProviderVolcengineTTS(TTSProvider):
|
|
|
66
72
|
logger.debug(f"请求体: {json.dumps(payload, ensure_ascii=False)[:100]}...")
|
|
67
73
|
|
|
68
74
|
try:
|
|
69
|
-
async with
|
|
70
|
-
|
|
75
|
+
async with (
|
|
76
|
+
aiohttp.ClientSession() as session,
|
|
77
|
+
session.post(
|
|
71
78
|
self.api_base,
|
|
72
79
|
data=json.dumps(payload),
|
|
73
80
|
headers=headers,
|
|
74
81
|
timeout=self.timeout,
|
|
75
|
-
) as response
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
response_text = await response.text()
|
|
79
|
-
logger.debug(f"响应内容: {response_text[:200]}...")
|
|
82
|
+
) as response,
|
|
83
|
+
):
|
|
84
|
+
logger.debug(f"响应状态码: {response.status}")
|
|
80
85
|
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
response_text = await response.text()
|
|
87
|
+
logger.debug(f"响应内容: {response_text[:200]}...")
|
|
83
88
|
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
if response.status == 200:
|
|
90
|
+
resp_data = json.loads(response_text)
|
|
86
91
|
|
|
87
|
-
|
|
92
|
+
if "data" in resp_data:
|
|
93
|
+
audio_data = base64.b64decode(resp_data["data"])
|
|
88
94
|
|
|
89
|
-
|
|
95
|
+
os.makedirs("data/temp", exist_ok=True)
|
|
90
96
|
|
|
91
|
-
|
|
92
|
-
await loop.run_in_executor(
|
|
93
|
-
None, lambda: open(file_path, "wb").write(audio_data)
|
|
94
|
-
)
|
|
97
|
+
file_path = f"data/temp/volcengine_tts_{uuid.uuid4()}.mp3"
|
|
95
98
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
else:
|
|
101
|
-
raise Exception(
|
|
102
|
-
f"火山引擎 TTS API 请求失败: {response.status}, {response_text}"
|
|
99
|
+
loop = asyncio.get_running_loop()
|
|
100
|
+
await loop.run_in_executor(
|
|
101
|
+
None,
|
|
102
|
+
lambda: open(file_path, "wb").write(audio_data),
|
|
103
103
|
)
|
|
104
104
|
|
|
105
|
+
return file_path
|
|
106
|
+
error_msg = resp_data.get("message", "未知错误")
|
|
107
|
+
raise Exception(f"火山引擎 TTS API 返回错误: {error_msg}")
|
|
108
|
+
raise Exception(
|
|
109
|
+
f"火山引擎 TTS API 请求失败: {response.status}, {response_text}",
|
|
110
|
+
)
|
|
111
|
+
|
|
105
112
|
except Exception as e:
|
|
106
113
|
error_details = traceback.format_exc()
|
|
107
114
|
logger.debug(f"火山引擎 TTS 异常详情: {error_details}")
|
|
108
|
-
raise Exception(f"火山引擎 TTS 异常: {
|
|
115
|
+
raise Exception(f"火山引擎 TTS 异常: {e!s}")
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import uuid
|
|
2
1
|
import os
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
from ..register import register_provider_adapter
|
|
2
|
+
import uuid
|
|
3
|
+
|
|
4
|
+
from openai import NOT_GIVEN, AsyncOpenAI
|
|
5
|
+
|
|
8
6
|
from astrbot.core import logger
|
|
9
|
-
from astrbot.core.utils.tencent_record_helper import tencent_silk_to_wav
|
|
10
7
|
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
8
|
+
from astrbot.core.utils.io import download_file
|
|
9
|
+
from astrbot.core.utils.tencent_record_helper import tencent_silk_to_wav
|
|
10
|
+
|
|
11
|
+
from ..entities import ProviderType
|
|
12
|
+
from ..provider import STTProvider
|
|
13
|
+
from ..register import register_provider_adapter
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
@register_provider_adapter(
|
|
@@ -26,11 +29,11 @@ class ProviderOpenAIWhisperAPI(STTProvider):
|
|
|
26
29
|
|
|
27
30
|
self.client = AsyncOpenAI(
|
|
28
31
|
api_key=self.chosen_api_key,
|
|
29
|
-
base_url=provider_config.get("api_base"
|
|
32
|
+
base_url=provider_config.get("api_base"),
|
|
30
33
|
timeout=provider_config.get("timeout", NOT_GIVEN),
|
|
31
34
|
)
|
|
32
35
|
|
|
33
|
-
self.set_model(provider_config.get("model"
|
|
36
|
+
self.set_model(provider_config.get("model"))
|
|
34
37
|
|
|
35
38
|
async def _is_silk_file(self, file_path):
|
|
36
39
|
silk_header = b"SILK"
|
|
@@ -39,11 +42,10 @@ class ProviderOpenAIWhisperAPI(STTProvider):
|
|
|
39
42
|
|
|
40
43
|
if silk_header in file_header:
|
|
41
44
|
return True
|
|
42
|
-
|
|
43
|
-
return False
|
|
45
|
+
return False
|
|
44
46
|
|
|
45
47
|
async def get_text(self, audio_url: str) -> str:
|
|
46
|
-
"""
|
|
48
|
+
"""Only supports mp3, mp4, mpeg, m4a, wav, webm"""
|
|
47
49
|
is_tencent = False
|
|
48
50
|
|
|
49
51
|
if audio_url.startswith("http"):
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import uuid
|
|
2
|
-
import os
|
|
3
1
|
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
4
5
|
import whisper
|
|
5
|
-
|
|
6
|
-
from ..entities import ProviderType
|
|
7
|
-
from astrbot.core.utils.io import download_file
|
|
8
|
-
from ..register import register_provider_adapter
|
|
6
|
+
|
|
9
7
|
from astrbot.core import logger
|
|
10
|
-
from astrbot.core.utils.tencent_record_helper import tencent_silk_to_wav
|
|
11
8
|
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
9
|
+
from astrbot.core.utils.io import download_file
|
|
10
|
+
from astrbot.core.utils.tencent_record_helper import tencent_silk_to_wav
|
|
11
|
+
|
|
12
|
+
from ..entities import ProviderType
|
|
13
|
+
from ..provider import STTProvider
|
|
14
|
+
from ..register import register_provider_adapter
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
@register_provider_adapter(
|
|
@@ -23,14 +26,16 @@ class ProviderOpenAIWhisperSelfHost(STTProvider):
|
|
|
23
26
|
provider_settings: dict,
|
|
24
27
|
) -> None:
|
|
25
28
|
super().__init__(provider_config, provider_settings)
|
|
26
|
-
self.set_model(provider_config.get("model"
|
|
29
|
+
self.set_model(provider_config.get("model"))
|
|
27
30
|
self.model = None
|
|
28
31
|
|
|
29
32
|
async def initialize(self):
|
|
30
33
|
loop = asyncio.get_event_loop()
|
|
31
34
|
logger.info("下载或者加载 Whisper 模型中,这可能需要一些时间 ...")
|
|
32
35
|
self.model = await loop.run_in_executor(
|
|
33
|
-
None,
|
|
36
|
+
None,
|
|
37
|
+
whisper.load_model,
|
|
38
|
+
self.model_name,
|
|
34
39
|
)
|
|
35
40
|
logger.info("Whisper 模型加载完成。")
|
|
36
41
|
|
|
@@ -41,8 +46,7 @@ class ProviderOpenAIWhisperSelfHost(STTProvider):
|
|
|
41
46
|
|
|
42
47
|
if silk_header in file_header:
|
|
43
48
|
return True
|
|
44
|
-
|
|
45
|
-
return False
|
|
49
|
+
return False
|
|
46
50
|
|
|
47
51
|
async def get_text(self, audio_url: str) -> str:
|
|
48
52
|
loop = asyncio.get_event_loop()
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
from xinference_client.client.restful.async_restful_client import (
|
|
2
2
|
AsyncClient as Client,
|
|
3
3
|
)
|
|
4
|
+
|
|
4
5
|
from astrbot import logger
|
|
6
|
+
|
|
7
|
+
from ..entities import ProviderType, RerankResult
|
|
5
8
|
from ..provider import RerankProvider
|
|
6
9
|
from ..register import register_provider_adapter
|
|
7
|
-
from ..entities import ProviderType, RerankResult
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
@register_provider_adapter(
|
|
@@ -23,7 +25,8 @@ class XinferenceRerankProvider(RerankProvider):
|
|
|
23
25
|
self.model_name = provider_config.get("rerank_model", "BAAI/bge-reranker-base")
|
|
24
26
|
self.api_key = provider_config.get("rerank_api_key")
|
|
25
27
|
self.launch_model_if_not_running = provider_config.get(
|
|
26
|
-
"launch_model_if_not_running",
|
|
28
|
+
"launch_model_if_not_running",
|
|
29
|
+
False,
|
|
27
30
|
)
|
|
28
31
|
self.client = None
|
|
29
32
|
self.model = None
|
|
@@ -42,7 +45,7 @@ class XinferenceRerankProvider(RerankProvider):
|
|
|
42
45
|
for uid, model_spec in running_models.items():
|
|
43
46
|
if model_spec.get("model_name") == self.model_name:
|
|
44
47
|
logger.info(
|
|
45
|
-
f"Model '{self.model_name}' is already running with UID: {uid}"
|
|
48
|
+
f"Model '{self.model_name}' is already running with UID: {uid}",
|
|
46
49
|
)
|
|
47
50
|
self.model_uid = uid
|
|
48
51
|
break
|
|
@@ -51,12 +54,13 @@ class XinferenceRerankProvider(RerankProvider):
|
|
|
51
54
|
if self.launch_model_if_not_running:
|
|
52
55
|
logger.info(f"Launching {self.model_name} model...")
|
|
53
56
|
self.model_uid = await self.client.launch_model(
|
|
54
|
-
model_name=self.model_name,
|
|
57
|
+
model_name=self.model_name,
|
|
58
|
+
model_type="rerank",
|
|
55
59
|
)
|
|
56
60
|
logger.info("Model launched.")
|
|
57
61
|
else:
|
|
58
62
|
logger.warning(
|
|
59
|
-
f"Model '{self.model_name}' is not running and auto-launch is disabled. Provider will not be available."
|
|
63
|
+
f"Model '{self.model_name}' is not running and auto-launch is disabled. Provider will not be available.",
|
|
60
64
|
)
|
|
61
65
|
return
|
|
62
66
|
|
|
@@ -66,12 +70,16 @@ class XinferenceRerankProvider(RerankProvider):
|
|
|
66
70
|
except Exception as e:
|
|
67
71
|
logger.error(f"Failed to initialize Xinference model: {e}")
|
|
68
72
|
logger.debug(
|
|
69
|
-
f"Xinference initialization failed with exception: {e}",
|
|
73
|
+
f"Xinference initialization failed with exception: {e}",
|
|
74
|
+
exc_info=True,
|
|
70
75
|
)
|
|
71
76
|
self.model = None
|
|
72
77
|
|
|
73
78
|
async def rerank(
|
|
74
|
-
self,
|
|
79
|
+
self,
|
|
80
|
+
query: str,
|
|
81
|
+
documents: list[str],
|
|
82
|
+
top_n: int | None = None,
|
|
75
83
|
) -> list[RerankResult]:
|
|
76
84
|
if not self.model:
|
|
77
85
|
logger.error("Xinference rerank model is not initialized.")
|
|
@@ -83,7 +91,7 @@ class XinferenceRerankProvider(RerankProvider):
|
|
|
83
91
|
|
|
84
92
|
if not results:
|
|
85
93
|
logger.warning(
|
|
86
|
-
f"Rerank API returned an empty list. Original response: {response}"
|
|
94
|
+
f"Rerank API returned an empty list. Original response: {response}",
|
|
87
95
|
)
|
|
88
96
|
|
|
89
97
|
return [
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import uuid
|
|
2
1
|
import os
|
|
2
|
+
import uuid
|
|
3
|
+
|
|
3
4
|
import aiohttp
|
|
4
5
|
from xinference_client.client.restful.async_restful_client import (
|
|
5
6
|
AsyncClient as Client,
|
|
6
7
|
)
|
|
7
|
-
|
|
8
|
-
from ..entities import ProviderType
|
|
9
|
-
from ..register import register_provider_adapter
|
|
8
|
+
|
|
10
9
|
from astrbot.core import logger
|
|
11
|
-
from astrbot.core.utils.tencent_record_helper import tencent_silk_to_wav
|
|
12
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
|
|
13
16
|
|
|
14
17
|
|
|
15
18
|
@register_provider_adapter(
|
|
@@ -28,7 +31,8 @@ class ProviderXinferenceSTT(STTProvider):
|
|
|
28
31
|
self.model_name = provider_config.get("model", "whisper-large-v3")
|
|
29
32
|
self.api_key = provider_config.get("api_key")
|
|
30
33
|
self.launch_model_if_not_running = provider_config.get(
|
|
31
|
-
"launch_model_if_not_running",
|
|
34
|
+
"launch_model_if_not_running",
|
|
35
|
+
False,
|
|
32
36
|
)
|
|
33
37
|
self.client = None
|
|
34
38
|
self.model_uid = None
|
|
@@ -46,7 +50,7 @@ class ProviderXinferenceSTT(STTProvider):
|
|
|
46
50
|
for uid, model_spec in running_models.items():
|
|
47
51
|
if model_spec.get("model_name") == self.model_name:
|
|
48
52
|
logger.info(
|
|
49
|
-
f"Model '{self.model_name}' is already running with UID: {uid}"
|
|
53
|
+
f"Model '{self.model_name}' is already running with UID: {uid}",
|
|
50
54
|
)
|
|
51
55
|
self.model_uid = uid
|
|
52
56
|
break
|
|
@@ -55,19 +59,21 @@ class ProviderXinferenceSTT(STTProvider):
|
|
|
55
59
|
if self.launch_model_if_not_running:
|
|
56
60
|
logger.info(f"Launching {self.model_name} model...")
|
|
57
61
|
self.model_uid = await self.client.launch_model(
|
|
58
|
-
model_name=self.model_name,
|
|
62
|
+
model_name=self.model_name,
|
|
63
|
+
model_type="audio",
|
|
59
64
|
)
|
|
60
65
|
logger.info("Model launched.")
|
|
61
66
|
else:
|
|
62
67
|
logger.warning(
|
|
63
|
-
f"Model '{self.model_name}' is not running and auto-launch is disabled. Provider will not be available."
|
|
68
|
+
f"Model '{self.model_name}' is not running and auto-launch is disabled. Provider will not be available.",
|
|
64
69
|
)
|
|
65
70
|
return
|
|
66
71
|
|
|
67
72
|
except Exception as e:
|
|
68
73
|
logger.error(f"Failed to initialize Xinference model: {e}")
|
|
69
74
|
logger.debug(
|
|
70
|
-
f"Xinference initialization failed with exception: {e}",
|
|
75
|
+
f"Xinference initialization failed with exception: {e}",
|
|
76
|
+
exc_info=True,
|
|
71
77
|
)
|
|
72
78
|
|
|
73
79
|
async def get_text(self, audio_url: str) -> str:
|
|
@@ -90,16 +96,15 @@ class ProviderXinferenceSTT(STTProvider):
|
|
|
90
96
|
audio_bytes = await resp.read()
|
|
91
97
|
else:
|
|
92
98
|
logger.error(
|
|
93
|
-
f"Failed to download audio from {audio_url}, status: {resp.status}"
|
|
99
|
+
f"Failed to download audio from {audio_url}, status: {resp.status}",
|
|
94
100
|
)
|
|
95
101
|
return ""
|
|
102
|
+
elif os.path.exists(audio_url):
|
|
103
|
+
with open(audio_url, "rb") as f:
|
|
104
|
+
audio_bytes = f.read()
|
|
96
105
|
else:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
audio_bytes = f.read()
|
|
100
|
-
else:
|
|
101
|
-
logger.error(f"File not found: {audio_url}")
|
|
102
|
-
return ""
|
|
106
|
+
logger.error(f"File not found: {audio_url}")
|
|
107
|
+
return ""
|
|
103
108
|
|
|
104
109
|
if not audio_bytes:
|
|
105
110
|
logger.error("Audio bytes are empty.")
|
|
@@ -145,23 +150,28 @@ class ProviderXinferenceSTT(STTProvider):
|
|
|
145
150
|
data = aiohttp.FormData()
|
|
146
151
|
data.add_field("model", self.model_uid)
|
|
147
152
|
data.add_field(
|
|
148
|
-
"file",
|
|
153
|
+
"file",
|
|
154
|
+
audio_bytes,
|
|
155
|
+
filename="audio.wav",
|
|
156
|
+
content_type="audio/wav",
|
|
149
157
|
)
|
|
150
158
|
|
|
151
159
|
async with self.client.session.post(
|
|
152
|
-
url,
|
|
160
|
+
url,
|
|
161
|
+
data=data,
|
|
162
|
+
headers=headers,
|
|
163
|
+
timeout=self.timeout,
|
|
153
164
|
) as resp:
|
|
154
165
|
if resp.status == 200:
|
|
155
166
|
result = await resp.json()
|
|
156
167
|
text = result.get("text", "")
|
|
157
168
|
logger.debug(f"Xinference STT result: {text}")
|
|
158
169
|
return text
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return ""
|
|
170
|
+
error_text = await resp.text()
|
|
171
|
+
logger.error(
|
|
172
|
+
f"Xinference STT transcription failed with status {resp.status}: {error_text}",
|
|
173
|
+
)
|
|
174
|
+
return ""
|
|
165
175
|
|
|
166
176
|
except Exception as e:
|
|
167
177
|
logger.error(f"Xinference STT failed: {e}")
|
astrbot/core/star/__init__.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
from .star import StarMetadata, star_map, star_registry
|
|
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):
|
|
@@ -36,24 +37,28 @@ class Star(CommandParserMixin):
|
|
|
36
37
|
)
|
|
37
38
|
|
|
38
39
|
async def html_render(
|
|
39
|
-
self,
|
|
40
|
+
self,
|
|
41
|
+
tmpl: str,
|
|
42
|
+
data: dict,
|
|
43
|
+
return_url=True,
|
|
44
|
+
options: dict | None = None,
|
|
40
45
|
) -> str:
|
|
41
46
|
"""渲染 HTML"""
|
|
42
47
|
return await html_renderer.render_custom_template(
|
|
43
|
-
tmpl,
|
|
48
|
+
tmpl,
|
|
49
|
+
data,
|
|
50
|
+
return_url=return_url,
|
|
51
|
+
options=options,
|
|
44
52
|
)
|
|
45
53
|
|
|
46
54
|
async def initialize(self):
|
|
47
55
|
"""当插件被激活时会调用这个方法"""
|
|
48
|
-
pass
|
|
49
56
|
|
|
50
57
|
async def terminate(self):
|
|
51
58
|
"""当插件被禁用、重载插件时会调用这个方法"""
|
|
52
|
-
pass
|
|
53
59
|
|
|
54
60
|
def __del__(self):
|
|
55
61
|
"""[Deprecated] 当插件被禁用、重载插件时会调用这个方法"""
|
|
56
|
-
pass
|
|
57
62
|
|
|
58
63
|
|
|
59
|
-
__all__ = ["
|
|
64
|
+
__all__ = ["Context", "PluginManager", "Provider", "Star", "StarMetadata", "StarTools"]
|
astrbot/core/star/config.py
CHANGED
|
@@ -1,23 +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
|
|
5
|
+
|
|
8
6
|
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
9
7
|
|
|
10
8
|
|
|
11
|
-
def load_config(namespace: str) ->
|
|
12
|
-
"""
|
|
13
|
-
从配置文件中加载配置。
|
|
9
|
+
def load_config(namespace: str) -> dict | bool:
|
|
10
|
+
"""从配置文件中加载配置。
|
|
14
11
|
namespace: str, 配置的唯一识别符,也就是配置文件的名字。
|
|
15
12
|
返回值: 当配置文件存在时,返回 namespace 对应配置文件的内容dict,否则返回 False。
|
|
16
13
|
"""
|
|
17
14
|
path = os.path.join(get_astrbot_data_path(), "config", f"{namespace}.json")
|
|
18
15
|
if not os.path.exists(path):
|
|
19
16
|
return False
|
|
20
|
-
with open(path,
|
|
17
|
+
with open(path, encoding="utf-8-sig") as f:
|
|
21
18
|
ret = {}
|
|
22
19
|
data = json.load(f)
|
|
23
20
|
for k in data:
|
|
@@ -26,8 +23,7 @@ def load_config(namespace: str) -> Union[dict, bool]:
|
|
|
26
23
|
|
|
27
24
|
|
|
28
25
|
def put_config(namespace: str, name: str, key: str, value, description: str):
|
|
29
|
-
"""
|
|
30
|
-
将配置项写入以namespace为名字的配置文件,如果key不存在于目标配置文件中。当前 value 仅支持 str, int, float, bool, list 类型(暂不支持 dict)。
|
|
26
|
+
"""将配置项写入以namespace为名字的配置文件,如果key不存在于目标配置文件中。当前 value 仅支持 str, int, float, bool, list 类型(暂不支持 dict)。
|
|
31
27
|
namespace: str, 配置的唯一识别符,也就是配置文件的名字。
|
|
32
28
|
name: str, 配置项的显示名字。
|
|
33
29
|
key: str, 配置项的键。
|
|
@@ -51,7 +47,7 @@ def put_config(namespace: str, name: str, key: str, value, description: str):
|
|
|
51
47
|
if not os.path.exists(path):
|
|
52
48
|
with open(path, "w", encoding="utf-8-sig") as f:
|
|
53
49
|
f.write("{}")
|
|
54
|
-
with open(path,
|
|
50
|
+
with open(path, encoding="utf-8-sig") as f:
|
|
55
51
|
d = json.load(f)
|
|
56
52
|
assert isinstance(d, dict)
|
|
57
53
|
if key not in d:
|
|
@@ -69,8 +65,7 @@ def put_config(namespace: str, name: str, key: str, value, description: str):
|
|
|
69
65
|
|
|
70
66
|
|
|
71
67
|
def update_config(namespace: str, key: str, value):
|
|
72
|
-
"""
|
|
73
|
-
更新配置文件中的配置项。
|
|
68
|
+
"""更新配置文件中的配置项。
|
|
74
69
|
namespace: str, 配置的唯一识别符,也就是配置文件的名字。
|
|
75
70
|
key: str, 配置项的键。
|
|
76
71
|
value: str, int, float, bool, list, 配置项的值。
|
|
@@ -78,7 +73,7 @@ def update_config(namespace: str, key: str, value):
|
|
|
78
73
|
path = os.path.join(get_astrbot_data_path(), "config", f"{namespace}.json")
|
|
79
74
|
if not os.path.exists(path):
|
|
80
75
|
raise FileNotFoundError(f"配置文件 {namespace}.json 不存在。")
|
|
81
|
-
with open(path,
|
|
76
|
+
with open(path, encoding="utf-8-sig") as f:
|
|
82
77
|
d = json.load(f)
|
|
83
78
|
assert isinstance(d, dict)
|
|
84
79
|
if key not in d:
|