From 7288348857e39f7bd211d38a10ae5162ca8f4bdb Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Sun, 30 Mar 2025 22:00:45 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=8E=88=20perf:=20OpenAI=20sources=20s?= =?UTF-8?q?upports=20api=20key=20load=20balance(random)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/provider/sources/openai_source.py | 124 +++++++++--------- 1 file changed, 63 insertions(+), 61 deletions(-) diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index 628b1849d..d452f09f4 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -2,6 +2,7 @@ import base64 import json import os import inspect +import random from openai import AsyncOpenAI, AsyncAzureOpenAI from openai.types.chat.chat_completion import ChatCompletion @@ -176,77 +177,78 @@ class ProviderOpenAIOfficial(Provider): payloads = {"messages": context_query, **model_config} llm_response = None - try: - llm_response = await self._query(payloads, func_tool) - except UnprocessableEntityError as e: - logger.warning(f"不可处理的实体错误:{e},尝试删除图片。") - # 尝试删除所有 image - new_contexts = await self._remove_image_from_context(context_query) - payloads["messages"] = new_contexts - context_query = new_contexts - llm_response = await self._query(payloads, func_tool) - except Exception as e: - if "maximum context length" in str(e): - # 重试 10 次 - retry_cnt = 20 - while retry_cnt > 0: - logger.warning( - f"上下文长度超过限制。尝试弹出最早的记录然后重试。当前记录条数: {len(context_query)}" - ) - try: - await self.pop_record(context_query) - llm_response = await self._query(payloads, func_tool) - break - except Exception as e: - if "maximum context length" in str(e): - retry_cnt -= 1 - else: - raise e - if retry_cnt == 0: - llm_response = LLMResponse( - "err", "err: 请尝试 /reset 清除会话记录。" - ) - elif "The model is not a VLM" in str(e): # siliconcloud + + retry = 10 + keys = self.api_keys.copy() + chosen_key = random.choice(keys) + + e = None + retry_cnt = 0 + for retry_cnt in range(retry): + try: + self.client.api_key = chosen_key + llm_response = await self._query(payloads, func_tool) + break + except UnprocessableEntityError as e: + logger.warning(f"不可处理的实体错误:{e},尝试删除图片。") # 尝试删除所有 image new_contexts = await self._remove_image_from_context(context_query) payloads["messages"] = new_contexts - llm_response = await self._query(payloads, func_tool) - - # openai, ollama, gemini openai, siliconcloud 的错误提示与 code 不统一,只能通过字符串匹配 - elif ( - "does not support Function Calling" in str(e) - or "does not support tools" in str(e) - or "Function call is not supported" in str(e) - or "Function calling is not enabled" in str(e) - or "Tool calling is not supported" in str(e) - or "No endpoints found that support tool use" in str(e) - or "model does not support function calling" in str(e) - or ("tool" in str(e) and "support" in str(e).lower()) - or ("function" in str(e) and "support" in str(e).lower()) - ): - logger.info( - f"{self.get_model()} 不支持函数工具调用,已自动去除,不影响使用。" - ) - if "tools" in payloads: - del payloads["tools"] - llm_response = await self._query(payloads, None) - else: - logger.error(f"发生了错误。Provider 配置如下: {self.provider_config}") - - if "tool" in str(e).lower() and "support" in str(e).lower(): + context_query = new_contexts + except Exception as e: + if "429" in str(e): + logger.warning( + f"API 调用过于频繁,尝试使用其他 Key 重试。当前 Key: {chosen_key[:12]}" + ) + keys.remove(chosen_key) + if len(keys) > 0: + chosen_key = random.choice(keys) + continue + else: + raise e + elif "maximum context length" in str(e): + logger.warning( + f"上下文长度超过限制。尝试弹出最早的记录然后重试。当前记录条数: {len(context_query)}" + ) + await self.pop_record(context_query) + elif "The model is not a VLM" in str(e): # siliconcloud + # 尝试删除所有 image + new_contexts = await self._remove_image_from_context(context_query) + payloads["messages"] = new_contexts + elif ( + "Function calling is not enabled" in str(e) + or ("tool" in str(e) and "support" in str(e).lower()) + or ("function" in str(e) and "support" in str(e).lower()) + ): + # openai, ollama, gemini openai, siliconcloud 的错误提示与 code 不统一,只能通过字符串匹配 + logger.info( + f"{self.get_model()} 不支持函数工具调用,已自动去除,不影响使用。" + ) + if "tools" in payloads: + del payloads["tools"] + func_tool = None + else: logger.error( - "疑似该模型不支持函数调用工具调用。请输入 /tool off_all" + f"发生了错误。Provider 配置如下: {self.provider_config}" ) - if "Connection error." in str(e): - proxy = os.environ.get("http_proxy", None) - if proxy: + if "tool" in str(e).lower() and "support" in str(e).lower(): logger.error( - f"可能为代理原因,请检查代理是否正常。当前代理: {proxy}" + "疑似该模型不支持函数调用工具调用。请输入 /tool off_all" ) - raise e + if "Connection error." in str(e): + proxy = os.environ.get("http_proxy", None) + if proxy: + logger.error( + f"可能为代理原因,请检查代理是否正常。当前代理: {proxy}" + ) + raise e + + if retry_cnt == retry - 1: + logger.error(f"API 调用失败,重试 {retry} 次仍然失败。") + raise e return llm_response async def _remove_image_from_context(self, contexts: List): From 76895fe86d48d0e156276b20ec5acedc7d3d8bd1 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Sun, 30 Mar 2025 22:12:34 +0800 Subject: [PATCH 2/4] chore: improve variable names --- astrbot/core/provider/sources/openai_source.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index d452f09f4..68d0bd6c0 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -178,13 +178,13 @@ class ProviderOpenAIOfficial(Provider): llm_response = None - retry = 10 - keys = self.api_keys.copy() - chosen_key = random.choice(keys) + max_retries = 10 + available_api_keys = self.api_keys.copy() + chosen_key = random.choice(available_api_keys) e = None retry_cnt = 0 - for retry_cnt in range(retry): + for retry_cnt in range(max_retries): try: self.client.api_key = chosen_key llm_response = await self._query(payloads, func_tool) @@ -200,9 +200,9 @@ class ProviderOpenAIOfficial(Provider): logger.warning( f"API 调用过于频繁,尝试使用其他 Key 重试。当前 Key: {chosen_key[:12]}" ) - keys.remove(chosen_key) - if len(keys) > 0: - chosen_key = random.choice(keys) + available_api_keys.remove(chosen_key) + if len(available_api_keys) > 0: + chosen_key = random.choice(available_api_keys) continue else: raise e @@ -246,8 +246,8 @@ class ProviderOpenAIOfficial(Provider): raise e - if retry_cnt == retry - 1: - logger.error(f"API 调用失败,重试 {retry} 次仍然失败。") + if retry_cnt == max_retries - 1: + logger.error(f"API 调用失败,重试 {max_retries} 次仍然失败。") raise e return llm_response From 627d2a4701c13144a03eba9e4ff37ae107b501c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=A1=E9=B8=A695676?= Date: Sun, 30 Mar 2025 22:33:21 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=87=8D=E8=AF=95?= =?UTF-8?q?=E9=97=B4=E9=9A=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/provider/sources/openai_source.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index 68d0bd6c0..32860826d 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -200,6 +200,9 @@ class ProviderOpenAIOfficial(Provider): logger.warning( f"API 调用过于频繁,尝试使用其他 Key 重试。当前 Key: {chosen_key[:12]}" ) + # 最后一次不等待 + if retry_cnt < max_retries - 1: + await asyncio.sleep(1) available_api_keys.remove(chosen_key) if len(available_api_keys) > 0: chosen_key = random.choice(available_api_keys) From 2eb9e5dde347b89b42dbd308bde66822b560fc58 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Sun, 30 Mar 2025 22:51:34 +0800 Subject: [PATCH 4/4] =?UTF-8?q?perf:=20=E6=B7=BB=E5=8A=A0=E9=87=8D?= =?UTF-8?q?=E8=AF=95=E7=AD=89=E5=BE=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/provider/sources/gemini_source.py | 2 ++ astrbot/core/provider/sources/openai_source.py | 1 + 2 files changed, 3 insertions(+) diff --git a/astrbot/core/provider/sources/gemini_source.py b/astrbot/core/provider/sources/gemini_source.py index e9d1b50a5..3233b3453 100644 --- a/astrbot/core/provider/sources/gemini_source.py +++ b/astrbot/core/provider/sources/gemini_source.py @@ -2,6 +2,7 @@ import base64 import aiohttp import json import random +import asyncio import astrbot.core.message.components as Comp from astrbot.core.message.message_event_result import MessageChain from astrbot.core.utils.io import download_image_by_url @@ -301,6 +302,7 @@ class ProviderGoogleGenAI(Provider): logger.info( f"检测到 Key 异常({str(e)}),正在尝试更换 API Key 重试... 当前 Key: {chosen_key[:12]}..." ) + await asyncio.sleep(1) continue else: logger.error( diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index 32860826d..766835719 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -3,6 +3,7 @@ import json import os import inspect import random +import asyncio from openai import AsyncOpenAI, AsyncAzureOpenAI from openai.types.chat.chat_completion import ChatCompletion