From 6aa0a04c55f260b48562c0c153cf47a23aebc96a Mon Sep 17 00:00:00 2001 From: SXP-Simon Date: Thu, 11 Sep 2025 21:00:50 +0800 Subject: [PATCH] =?UTF-8?q?[feat]=20(provider)=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=AF=B9=E8=87=AA=E5=AE=9A=E4=B9=89=20LLM=20=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81=EF=BC=88=E5=85=BC=E5=AE=B9=20OpenAI?= =?UTF-8?q?=20=E6=A0=BC=E5=BC=8F=EF=BC=89=EF=BC=8C=E9=9C=80=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=20apikey,=20base=5Furl=20=E4=B8=8E=20model=20?= =?UTF-8?q?=EF=BC=8C=E7=95=99=E7=A9=BA=E5=B0=B1=E4=BD=BF=E7=94=A8=20Astrbo?= =?UTF-8?q?t=20=E5=86=85=E7=BD=AE=E7=9A=84=20LLM=20=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _conf_schema.json | 18 +++++++++++++++ src/analysis/llm_analyzer.py | 44 ++++++++++++++++++++++++++++++++---- src/core/config.py | 11 +++++++++ 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/_conf_schema.json b/_conf_schema.json index 3584d9d..cf5aef8 100644 --- a/_conf_schema.json +++ b/_conf_schema.json @@ -99,6 +99,24 @@ "default": 2, "hint": "重试之间的基准等待时间(秒),实际等待时间为基值乘以尝试次数。" }, + "custom_api_key": { + "type": "string", + "description": "自定义 LLM 服务 API Key (选填)", + "default": "", + "hint": "若使用自建或第三方的 LLM 服务,可在此填写 API Key;留空则使用 Astrbot 统一内置提供商。" + }, + "custom_api_base_url": { + "type": "string", + "description": "自定义 LLM 服务 Base URL (选填)", + "default": "", + "hint": "自定义 LLM 服务的基础请求地址,例如 https://api.example.com/v1/chat 。留空则使用 Astrbot 统一内置提供商。" + }, + "custom_model_name": { + "type": "string", + "description": "自定义 LLM 模型名称 (选填)", + "default": "", + "hint": "自定义服务所使用的模型名称,例如 gpt-4 或自定义模型标识。留空则使用 Astrbot 统一内置提供商。" + }, "pdf_output_dir": { "type": "string", "description": "PDF输出目录", diff --git a/src/analysis/llm_analyzer.py b/src/analysis/llm_analyzer.py index f7c2689..11fdde7 100644 --- a/src/analysis/llm_analyzer.py +++ b/src/analysis/llm_analyzer.py @@ -20,17 +20,53 @@ class LLMAnalyzer: self.config_manager = config_manager async def _call_provider_with_retry(self, provider, prompt: str, max_tokens: int, temperature: float): - """调用LLM提供者,带超时、重试与退避。""" - + """调用LLM提供者,带超时、重试与退避。支持自定义服务商。""" timeout = self.config_manager.get_llm_timeout() retries = self.config_manager.get_llm_retries() backoff = self.config_manager.get_llm_backoff() + # 获取自定义服务商参数 + custom_api_key = getattr(self.config_manager, 'get_custom_api_key', lambda: None)() + custom_api_base = getattr(self.config_manager, 'get_custom_api_base_url', lambda: None)() + custom_model = getattr(self.config_manager, 'get_custom_model_name', lambda: None)() + last_exc = None for attempt in range(1, retries + 1): try: - coro = provider.text_chat(prompt=prompt, max_tokens=max_tokens, temperature=temperature) - return await asyncio.wait_for(coro, timeout=timeout) + if custom_api_key and custom_api_base and custom_model: + logger.info(f"使用自定义LLM提供商: {custom_api_base} model={custom_model}") + import aiohttp + async with aiohttp.ClientSession() as session: + headers = { + "Authorization": f"Bearer {custom_api_key}", + "Content-Type": "application/json" + } + payload = { + "model": custom_model, + "messages": [{"role": "user", "content": prompt}], + "max_tokens": max_tokens, + "temperature": temperature + } + async with session.post(custom_api_base, json=payload, headers=headers, timeout=timeout) as resp: + if resp.status != 200: + error_text = await resp.text() + logger.error(f"自定义LLM服务商请求失败: HTTP {resp.status}, 内容: {error_text}") + try: + response_json = await resp.json() + except Exception as json_err: + error_text = await resp.text() + logger.error(f"自定义LLM服务商响应JSON解析失败: {json_err}, 内容: {error_text}") + # 兼容 OpenAI 格式 + content = response_json["choices"][0]["message"]["content"] + # 构造一个兼容原有逻辑的对象 + class CustomResponse: + completion_text = content + raw_completion = response_json + return CustomResponse() + else: + logger.info(f"使用默认LLM provider: {provider}") + coro = provider.text_chat(prompt=prompt, max_tokens=max_tokens, temperature=temperature) + return await asyncio.wait_for(coro, timeout=timeout) except asyncio.TimeoutError as e: last_exc = e logger.warning(f"LLM请求超时: 第{attempt}次, timeout={timeout}s") diff --git a/src/core/config.py b/src/core/config.py index 0243ac3..7bda27e 100644 --- a/src/core/config.py +++ b/src/core/config.py @@ -83,6 +83,17 @@ class ConfigManager: """获取LLM请求重试退避基值(秒),实际退避会乘以尝试次数""" return self.config.get("llm_backoff", 2) + def get_custom_api_key(self) -> str: + """获取自定义 LLM 服务的 API Key""" + return self.config.get("custom_api_key", "") + + def get_custom_api_base_url(self) -> str: + """获取自定义 LLM 服务的 Base URL""" + return self.config.get("custom_api_base_url", "") + + def get_custom_model_name(self) -> str: + """获取自定义 LLM 服务的模型名称""" + return self.config.get("custom_model_name", "") def get_pdf_output_dir(self) -> str: """获取PDF输出目录""" return self.config.get("pdf_output_dir", "data/plugins/astrbot-qq-group-daily-analysis/reports")