Files
BiliNote/backend/app/utils/openai_client.py
huangjianwu 41f17592c2 fix(backend): 部署韧性——模型自愈/就绪门禁/全局代理/启动诊断
- whisper: model.bin 截断/损坏时删目录重下重试一次,修「Unable to
  open file model.bin」死循环;mlx 同样按 config.json 判完整性
- /generate_note 加就绪门禁:本地转写引擎模型没下好直接拦截,返回
  reason=transcriber_model_not_ready,不让任务静默卡在首次下载
- 全局代理:新增 ProxyConfigManager(JSON 配置 + HTTP_PROXY env 兜底)
  + build_openai_client,统一注入代理到 LLM/Groq 客户端;yt-dlp 与
  youtube-transcript-api 也走代理
- build_openai_client 校验 api_key 非空,空 key 给「xxx 的 API Key
  未配置」而不是天书般的 Illegal header value b'Bearer '
- universal_gpt: 模型拒绝自定义 temperature(o1/o3/gpt-5 系列)时
  就地去掉参数重试,不消耗重试预算
- connect_test 改用真实 chat completion 而非 /v1/models 探测
- main.py: lifespan 拆 [startup 1/5..5/5] 分段日志 + 异常清晰定位
- /sys_health 重构为结构化返回 {backend,ffmpeg,db,whisper_model}

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 19:01:14 +08:00

46 lines
1.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""统一构造 OpenAI 兼容客户端:注入全局代理 + 校验 api_key。
为什么要这一层:
- 代理openai SDK 默认只认进程级 HTTP_PROXY 环境变量,桌面端用户在 UI 里
填的代理需要显式塞进 httpx.Client 才生效。
- api_key 校验:空 key 会让 httpx 拼出非法 header `Bearer `,抛出
`httpx.LocalProtocolError: Illegal header value b'Bearer '` 这种天书报错。
在入口挡掉给用户「xxx 的 API Key 未配置」这种能看懂的提示。
"""
from typing import Optional
from openai import OpenAI
from app.services.proxy_config_manager import ProxyConfigManager
from app.utils.logger import get_logger
logger = get_logger(__name__)
def build_openai_client(
api_key: Optional[str],
base_url: Optional[str],
*,
key_label: str = "API Key",
timeout: Optional[float] = None,
) -> OpenAI:
"""构造 OpenAI 客户端。api_key 为空直接抛清晰错误;代理已配置则注入。
key_label 用于错误提示,例如 "Groq 的 API Key" / "OpenAI 供应商的 API Key"
"""
if not api_key or not str(api_key).strip():
raise ValueError(f"{key_label} 未配置,请先在「设置」里填写后再使用")
kwargs = {"api_key": str(api_key).strip(), "base_url": base_url}
if timeout is not None:
kwargs["timeout"] = timeout
proxy_url = ProxyConfigManager().get_proxy_url()
if proxy_url:
# 延迟 import httpx仅在确实要走代理时才需要
import httpx
kwargs["http_client"] = httpx.Client(proxy=proxy_url, timeout=timeout or 600.0)
logger.info(f"OpenAI 客户端走代理: {proxy_url}")
return OpenAI(**kwargs)