diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index d680cf6c8..68c3146b5 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -529,6 +529,15 @@ CONFIG_METADATA_2 = { "dify_query_input_key": "astrbot_text_query", "timeout": 60, }, + "dashscope": { + "id": "dashscope", + "type": "dashscope", + "enable": True, + "dashscope_app_type": "agent", + "dashscope_api_key": "", + "dashscope_app_id": "", + "timeout": 60, + }, "whisper(API)": { "id": "whisper", "type": "openai_whisper_api", @@ -565,6 +574,13 @@ CONFIG_METADATA_2 = { }, }, "items": { + "dashscope_app_type": { + "description": "应用类型", + "type": "string", + "hint": "阿里云百炼应用的应用类型。", + "options": ["agent", "agent-arrange", "dialog-workflow", "task-workflow"], + "obvious_hint": True, + }, "timeout": { "description": "超时时间", "type": "int", diff --git a/astrbot/core/provider/manager.py b/astrbot/core/provider/manager.py index db8569b83..70a4af389 100644 --- a/astrbot/core/provider/manager.py +++ b/astrbot/core/provider/manager.py @@ -124,6 +124,8 @@ class ProviderManager(): from .sources.llmtuner_source import LLMTunerModelLoader as LLMTunerModelLoader case "dify": from .sources.dify_source import ProviderDify as ProviderDify + case "dashscope": + from .sources.dashscope_source import ProviderDashscope as ProviderDashscope case "googlegenai_chat_completion": from .sources.gemini_source import ProviderGoogleGenAI as ProviderGoogleGenAI case "openai_whisper_api": diff --git a/astrbot/core/provider/sources/dashscope_source.py b/astrbot/core/provider/sources/dashscope_source.py new file mode 100644 index 000000000..56ab79934 --- /dev/null +++ b/astrbot/core/provider/sources/dashscope_source.py @@ -0,0 +1,111 @@ +import asyncio +from typing import List +from .. import Provider, Personality +from ..entites import LLMResponse +from ..func_tool_manager import FuncCall +from astrbot.core.db import BaseDatabase +from ..register import register_provider_adapter +from .openai_source import ProviderOpenAIOfficial +from astrbot.core import logger, sp +from dashscope import Application + +@register_provider_adapter("dashscope", "Dashscope APP 适配器。") +class ProviderDashscope(ProviderOpenAIOfficial): + def __init__( + self, + provider_config: dict, + provider_settings: dict, + db_helper: BaseDatabase, + persistant_history=False, + default_persona: Personality=None + ) -> None: + Provider.__init__( + self, provider_config, provider_settings, persistant_history, db_helper, default_persona + ) + self.api_key = provider_config.get("dashscope_api_key", "") + if not self.api_key: + raise Exception("阿里云百炼 API Key 不能为空。") + self.app_id = provider_config.get("dashscope_app_id", "") + if not self.app_id: + raise Exception("阿里云百炼 APP ID 不能为空。") + self.dashscope_app_type = provider_config.get("dashscope_app_type", "") + if not self.dashscope_app_type: + raise Exception("阿里云百炼 APP 类型不能为空。") + self.model_name = "dashscope" + + self.timeout = provider_config.get("timeout", 120) + if isinstance(self.timeout, str): + self.timeout = int(self.timeout) + + async def text_chat( + self, + prompt: str, + session_id: str = None, + image_urls: List[str] = [], + func_tool: FuncCall = None, + contexts: List = None, + system_prompt: str = None, + **kwargs, + ) -> LLMResponse: + if self.dashscope_app_type in ["agent", "dialog-workflow"]: + # 支持多轮对话的 + new_record = {"role": "user", "content": prompt} + if image_urls: + logger.warning("阿里云百炼暂不支持图片输入,将自动忽略图片内容。") + contexts_no_img = await self._remove_image_from_context(contexts) + context_query = [*contexts_no_img, new_record] + if system_prompt: + context_query.insert(0, {"role": "system", "content": system_prompt}) + for part in context_query: + if '_no_save' in part: + del part['_no_save'] + # 调用阿里云百炼 API + response = await asyncio.get_event_loop().run_in_executor( + None, + Application.call, + self.app_id, + None, + None, + None, + self.api_key, + context_query + ) + else: + # 不支持多轮对话的 + # 调用阿里云百炼 API + response = await asyncio.get_event_loop().run_in_executor( + None, + Application.call, + self.app_id, + prompt, + None, + None, + self.api_key + ) + + logger.debug(f"dashscope resp: {response}") + + if response.status_code != 200: + logger.error(f"阿里云百炼请求失败: request_id={response.request_id}, code={response.status_code}, message={response.message}, 请参考文档:https://help.aliyun.com/zh/model-studio/developer-reference/error-code") + return LLMResponse(role="err", completion_text=f"阿里云百炼请求失败: message={response.message} code={response.status_code}") + + output_text = response.output.get("text", "") + return LLMResponse(role="assistant", completion_text=output_text) + + async def forget(self, session_id): + return True + + async def get_current_key(self): + return self.api_key + + async def set_key(self, key): + raise Exception("阿里云百炼 适配器不支持设置 API Key。") + + async def get_models(self): + return [self.get_model()] + + async def get_human_readable_context(self, session_id, page, page_size): + raise Exception("暂不支持获得 阿里云百炼 的历史消息记录。") + + async def terminate(self): + await self.api_client.close() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e524df84f..39abe351d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,4 +21,6 @@ silk-python lark-oapi ormsgpack -cryptography \ No newline at end of file +cryptography + +dashscope \ No newline at end of file