feat: LLM 提供商模板

This commit is contained in:
Soulter
2024-11-29 15:25:49 +08:00
parent 4d8d9ecfc2
commit bd40404f58
4 changed files with 107 additions and 41 deletions
+4 -4
View File
@@ -54,8 +54,10 @@ class ModelConfig:
max_tokens: int = 6000
temperature: float = 0.9
top_p: float = 1
frequency_penalty: float = 0
presence_penalty: float = 0
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
@dataclass
class ImageGenerationModelConfig:
@@ -122,7 +124,6 @@ class AstrBotConfig():
llm_settings: LLMSettings = field(default_factory=LLMSettings)
content_safety: ContentSafetyConfig = field(default_factory=ContentSafetyConfig)
t2i: bool = True
dump_history_interval: int = 10
admins_id: List[str] = field(default_factory=list)
https_proxy: str = ""
http_proxy: str = ""
@@ -180,7 +181,6 @@ class AstrBotConfig():
self.llm_settings=LLMSettings(**data.get("llm_settings", {}))
self.content_safety=ContentSafetyConfig(**data.get("content_safety", {}))
self.t2i=data.get("t2i", True)
self.dump_history_interval=data.get("dump_history_interval", 10)
self.admins_id=data.get("admins_id", [])
self.https_proxy=data.get("https_proxy", "")
self.http_proxy=data.get("http_proxy", "")
+86 -33
View File
@@ -1,6 +1,86 @@
'''
这里定义了一些默认配置文件,请不要修改这个文件。如需修改配置,请在 `data/cmd_config.json` 中修改或者在管理面板中可视化修改。
'''
VERSION = '3.4.0'
DB_PATH = 'data/data_v2.db'
# LLM 提供商配置模板
PROVIDER_CONFIG_TEMPLATE = {
"openai": {
"id": "default",
"name": "openai",
"enable": True,
"key": [],
"api_base": "",
"prompt_prefix": "",
"default_personality": "",
"model_config": {
"model": "gpt-4o",
"max_tokens": 6000,
"temperature": 0.9,
"top_p": 1,
},
"image_generation_model_config": {
"enable": False,
"model": "dall-e-3",
"size": "1024x1024",
"style": "vivid",
"quality": "standard",
}
},
"ollama": {
"id": "ollama_default",
"name": "ollama",
"enable": True,
"key": ["ollama"], # ollama 的 key 默认是 ollama
"api_base": "http://localhost:11434",
"prompt_prefix": "",
"default_personality": "",
"model_config": {
"model": "llama3.1-8b",
"temperature": 0.9,
"top_p": 1,
}
},
"gemini": {
"id": "gemini_default",
"name": "gemini",
"enable": True,
"key": [],
"api_base": "https://generativelanguage.googleapis.com/v1beta/openai/",
"prompt_prefix": "",
"default_personality": "",
"model_config": {
"model": "gemini-1.5-flash",
}
},
"deepseek": {
"id": "deepseek_default",
"name": "deepseek",
"enable": True,
"key": [],
"api_base": "https://api.deepseek.com/v1",
"prompt_prefix": "",
"default_personality": "",
"model_config": {
"model": "deepseek-chat",
}
},
"zhipu": {
"id": "zhipu_default",
"name": "zhipu(glm)",
"enable": True,
"key": [],
"api_base": "https://open.bigmodel.cn/api/paas/v4/",
"prompt_prefix": "",
"default_personality": "",
"model_config": {
"model": "glm-4-flash",
}
},
}
# 新版本配置文件,摈弃旧版本令人困惑的配置项 :D
DEFAULT_CONFIG_VERSION_2 = {
"config_version": 2,
@@ -37,33 +117,10 @@ DEFAULT_CONFIG_VERSION_2 = {
"count": 30,
},
"reply_prefix": "",
"forward_threshold": 200, # 转发消息的阈值
"forward_threshold": 200, # 转发消息的阈值
},
"llm": [
{
"id": "default",
"name": "openai",
"enable": True,
"key": [],
"api_base": "",
"prompt_prefix": "",
"default_personality": "",
"model_config": {
"model": "gpt-4o",
"max_tokens": 6000,
"temperature": 0.9,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0,
},
"image_generation_model_config": {
"enable": True,
"model": "dall-e-3",
"size": "1024x1024",
"style": "vivid",
"quality": "standard",
}
},
PROVIDER_CONFIG_TEMPLATE["openai"]
],
"llm_settings": {
"wake_prefix": "",
@@ -78,7 +135,6 @@ DEFAULT_CONFIG_VERSION_2 = {
},
"wake_prefix": ["/"],
"t2i": True,
"dump_history_interval": 10,
"admins_id": [],
"https_proxy": "",
"http_proxy": "",
@@ -101,7 +157,7 @@ CONFIG_METADATA_2 = {
"type": "list",
"items": {
"id": {"description": "ID", "type": "string", "hint": "提供商 ID 名,用于在多实例下方便管理和识别。自定义,ID 不能重复。"},
"name": {"description": "适配器类型", "type": "string", "hint": "当前版本下,内置支持 `qq_official`QQ 官方机器人), `aiocqhttp`(Onebot 适用) 适配器类型。", "options": ["qq_official", "aiocqhttp"]},
"name": {"description": "适配器类型", "type": "string", "hint": "当前版本下,内置支持 `qq_official`QQ 官方机器人), `aiocqhttp`(Onebot 适用) 适配器类型。", "options": ["qq_official", "aiocqhttp", "wechat"], "readonly": True},
"enable": {"description": "启用", "type": "bool", "hint": "是否启用该适配器。未启用的适配器对应的消息平台将不会接收到消息。"},
"appid": {"description": "appid", "type": "string", "hint": "必填项。QQ 官方机器人平台的 appid。如何获取请参考文档。"},
"secret": {"description": "secret", "type": "string", "hint": "必填项。QQ 官方机器人平台的 secret。如何获取请参考文档。"},
@@ -137,7 +193,7 @@ CONFIG_METADATA_2 = {
"type": "list",
"items": {
"id": {"description": "ID", "type": "string", "hint": "提供商 ID 名,用于在多实例下方便管理和识别。自定义,ID 不能重复。"},
"name": {"description": "模型提供商类型", "type": "string", "hint": "当前版本下,支持 `openai` 一个模型提供商", "options": ["openai"]},
"name": {"description": "模型提供商类型", "type": "string", "hint": "如需变更模型提供商,请点击上面的 + 新建一个。如果没有找到你想要接入的提供商,可以前往你的提供商的官网查看是否兼容 OpenAI API,如兼容,可以选择 `openai`。大多数提供商都是兼容的", "options": list(PROVIDER_CONFIG_TEMPLATE.keys()), "obvious_hint": True, "readonly": True},
"enable": {"description": "启用", "type": "bool", "hint": "是否启用该模型。未启用的模型将不会被使用。"},
"key": {"description": "API Key", "type": "list", "items": {"type": "string"}, "hint": "API Key 列表。填写好后输入回车即可添加 API Key。支持多个 API Key。"},
"api_base": {"description": "API Base URL", "type": "string", "hint": "API Base URL 请在在模型提供商处获得。支持 Ollama 开放的 API 地址。如果您确认填写正确但是使用时出现了 404 异常,可以尝试在地址末尾加上 `/v1`。"},
@@ -151,8 +207,6 @@ CONFIG_METADATA_2 = {
"max_tokens": {"description": "最大令牌数", "type": "int"},
"temperature": {"description": "温度", "type": "float"},
"top_p": {"description": "Top P值", "type": "float"},
"frequency_penalty": {"description": "频率惩罚", "type": "float"},
"presence_penalty": {"description": "存在惩罚", "type": "float"},
}
},
"image_generation_model_config": {
@@ -203,7 +257,6 @@ CONFIG_METADATA_2 = {
},
"wake_prefix": {"description": "机器人唤醒前缀", "type": "list", "items": {"type": "string"}, "hint": "在不 @ 机器人的情况下,可以通过外加消息前缀来唤醒机器人。"},
"t2i": {"description": "文本转图像", "type": "bool", "hint": "启用后,超出一定长度的文本将会通过 AstrBot API 渲染成 Markdown 图片发送。可以缓解审核和消息过长刷屏的问题,并提高 Markdown 文本的可读性。"},
"dump_history_interval": {"description": "历史记录保存间隔", "type": "int", "hint": "每隔多少分钟将 LLM 聊天的历史记录转储到数据库。"},
"admins_id": {"description": "管理员 ID", "type": "list", "items": {"type": "int"}, "hint": "管理员 ID 列表,管理员可以使用一些特权命令,如 `update`, `plugin` 等。ID 可以通过 `/myid` 指令获得。回车添加,可添加多个。"},
"https_proxy": {"description": "HTTPS 代理", "type": "string", "hint": "启用后,会以添加环境变量的方式设置代理。格式为 `http://ip:port`"},
"http_proxy": {"description": "HTTP 代理", "type": "string", "hint": "启用后,会以添加环境变量的方式设置代理。格式为 `http://ip:port`"},
@@ -221,7 +274,7 @@ CONFIG_METADATA_2 = {
"pip_install_arg": {"description": "pip 安装参数", "type": "string", "hint": "安装插件依赖时,会使用 Python 的 pip 工具。这里可以填写额外的参数,如 `--break-system-package` 等。"},
"plugin_repo_mirror": {"description": "插件仓库镜像", "type": "string", "hint": "插件仓库的镜像地址,用于加速插件的下载。", "options": ["default", "https://ghp.ci/", "https://github-mirror.us.kg/"]},
}
DEFAULT_VALUE_MAP = {
"int": 0,
"float": 0.0,
@@ -230,4 +283,4 @@ DEFAULT_VALUE_MAP = {
"text": "",
"list": [],
"object": {},
}
}
+2 -1
View File
@@ -1,7 +1,7 @@
import os, json
from .route import Route, Response
from quart import Quart, request
from core.config.default import CONFIG_METADATA_2, DEFAULT_VALUE_MAP
from core.config.default import CONFIG_METADATA_2, DEFAULT_VALUE_MAP, PROVIDER_CONFIG_TEMPLATE
from core.config.astrbot_config import AstrBotConfig
from core.plugin.config import update_config
from core.core_lifecycle import AstrBotCoreLifecycle
@@ -130,6 +130,7 @@ class ConfigRoute(Route):
return {
"metadata": CONFIG_METADATA_2,
"config": config,
"provider_config_tmpl": PROVIDER_CONFIG_TEMPLATE
}
async def _get_extension_config(self, namespace: str):
+15 -3
View File
@@ -1,5 +1,5 @@
import json, traceback
from typing import List
from typing import List, Dict
from astrbot.api import Context, AstrMessageEvent, MessageEventResult
from .openai_adapter import ProviderOpenAIOfficial
from .commands import OpenAIAdapterCommand
@@ -11,31 +11,38 @@ from openai.types.chat.chat_completion_message_tool_call import Function
from astrbot.api import command_parser
from .web_searcher import search_from_bing, fetch_website_content
from astrbot.core.utils.metrics import Metric
from astrbot.core.config.astrbot_config import LLMConfig
class Main:
def __init__(self, context: Context) -> None:
supported_provider_names = ["openai", "ollama", "gemini", "deepseek", "zhipu"]
self.context = context
self.provider_insts: List[ProviderOpenAIOfficial] = []
self.provider_llm_configs: List[LLMConfig] = []
self.provider = None
self.provider_config = None
llms_config = self.context.get_config().llm
loaded = False
for llm in llms_config:
if llm.enable:
if llm.name == "openai":
if llm.name in supported_provider_names:
if not llm.key or not llm.enable:
logger.warning("没有开启 LLM Provider 或 API Key 未填写。")
continue
self.provider_insts.append(ProviderOpenAIOfficial(llm, self.context.get_db()))
self.provider_llm_configs.append(llm)
loaded = True
logger.info(f"已启用 LLM Provider(OpenAI API): {llm.id}({llm.name})。")
logger.info(f"已启用 LLM Provider(OpenAI API 适配器): {llm.id}({llm.name})。")
if loaded:
self.command_handler = OpenAIAdapterCommand(self.context)
self.command_handler.set_provider(self.provider_insts[0])
self.context.register_listener(PLUGIN_NAME, "openai_adapter_chat", self.chat, "OpenAI Adapter LLM 调用监听器", after_commands=True)
self.provider = self.command_handler.provider
self.provider_config = self.provider_llm_configs[0]
self.context.register_commands(PLUGIN_NAME, "provider", "查看当前 LLM Provider", 10, self.provider_info)
self.context.register_commands(PLUGIN_NAME, "websearch", "启用/关闭网页搜索", 10, self.web_search)
@@ -88,6 +95,7 @@ class Main:
if idx >= len(self.provider_insts):
event.set_result(MessageEventResult().message("无效的序号。"))
self.provider = self.provider_insts[idx]
self.provider_config = self.provider_llm_configs[idx]
self.command_handler.set_provider(self.provider)
event.set_result(MessageEventResult().message(f"已经成功切换到 LLM 接入源 {self.provider.llm_config.id}"))
return
@@ -114,6 +122,10 @@ class Main:
if not event.is_wake_up():
return
# prompt 前缀
if self.provider_config.prompt_prefix:
event.message_str = self.provider_config.prompt_prefix + event.message_str
image_url = None
for comp in event.message_obj.message:
if isinstance(comp, Image):