返回 VideoClaw
vlm_gpt.py
1 # -*- coding: utf-8 -*-
2 """
3 OpenAI GPT 多模态大模型 API 客户端
4 支持 gpt-4o, gpt-5.4 (未来版本) 等视觉模型
5
6 代理由 provider 的 enable_proxy 决定,通过 httpx.Client 注入到 OpenAI SDK。
7 """
8
9 import os
10 import sys
11
12 models_dir = os.path.dirname(os.path.abspath(__file__))
13 backend_dir = os.path.dirname(models_dir)
14 if backend_dir not in sys.path:
15 sys.path.insert(0, backend_dir)
16
17 import time
18 import base64
19 import httpx
20 import logging
21 from openai import OpenAI
22 from typing import Dict, List, Optional
23 from config import Config
24
25 logger = logging.getLogger(__name__)
26
27
28 class GPTVLClient:
29 """
30 GPT VLM 客户端,支持多模态对话
31 """
32 def __init__(self,
33 api_key: Optional[str] = None,
34 base_url: Optional[str] = None,
35 proxy: Optional[str] = None,
36 timeout: float = 60.0):
37 """
38 GPT 多模态客户端
39 :param api_key: OpenAI API Key
40 :param base_url: 自定义 Base URL(如果传入,则不使用本地代理)
41 :param timeout: 超时时间
42 """
43 # 优先使用传入的 api_key,否则从 config.yaml 读取
44 self.api_key = api_key or Config.OPENAI_API_KEY
45 self.timeout = timeout
46
47 kwargs = {"api_key": self.api_key, "timeout": self.timeout}
48
49 self.base_url = base_url
50 if proxy is None:
51 proxy = Config.provider_proxy("openai")
52 if proxy:
53 kwargs["http_client"] = httpx.Client(
54 proxy=proxy,
55 timeout=self.timeout,
56 )
57 if self.base_url:
58 kwargs["base_url"] = self.base_url
59 self.client = OpenAI(**kwargs)
60 self.max_attempts = 5
61
62 def _encode_image(self, image_path: str) -> str:
63 """将本地图片编码为 base64"""
64 abs_path = os.path.abspath(image_path)
65 with open(abs_path, "rb") as f:
66 return base64.b64encode(f.read()).decode("utf-8")
67
68 def _get_mime_type(self, image_path: str) -> str:
69 """根据文件扩展名获取 MIME 类型"""
70 ext = os.path.splitext(image_path)[1].lower()
71 mime_types = {
72 ".jpg": "image/jpeg",
73 ".jpeg": "image/jpeg",
74 ".png": "image/png",
75 ".webp": "image/webp",
76 ".gif": "image/gif"
77 }
78 return mime_types.get(ext, "image/jpeg")
79
80 def chat(self, text: str, images: List[str], model: str = "gpt-5.4",
81 parameters: Optional[Dict] = None) -> str:
82 """
83 使用 GPT 进行多模态对话(文本+图片)
84 :param text: 文本内容
85 :param images: 图片路径列表(支持本地路径或URL)
86 :param model: 模型名(如 gpt-4o, gpt-5.4)
87 :param parameters: 其他API参数
88 :return: API响应内容
89 """
90 if not self.api_key:
91 raise ValueError("OPENAI_API_KEY 未设置")
92
93 # 构建消息格式
94 content: list = [{"type": "text", "text": text}]
95
96 # 处理图片
97 if images:
98 for img_path in images:
99 if img_path.startswith("data:"):
100 # Base64 数据 URL
101 content.append({
102 "type": "image_url",
103 "image_url": {"url": img_path}
104 })
105 elif img_path.startswith("http"):
106 # URL 图片
107 content.append({
108 "type": "image_url",
109 "image_url": {"url": img_path}
110 })
111 else:
112 # 本地图片 - 转为 base64
113 mime_type = self._get_mime_type(img_path)
114 base64_data = self._encode_image(img_path)
115 data_url = f"data:{mime_type};base64,{base64_data}"
116 content.append({
117 "type": "image_url",
118 "image_url": {"url": data_url}
119 })
120
121 messages = [{"role": "user", "content": content}]
122
123 attempts = 0
124 while attempts < self.max_attempts:
125 try:
126 response = self.client.chat.completions.create(
127 model=model,
128 messages=messages,
129 temperature=parameters.get("temperature", 0.7) if parameters else 0.7
130 )
131
132 if response.choices and len(response.choices) > 0:
133 return response.choices[0].message.content
134
135 except Exception as e:
136 logger.warning("GPT VLM request failed: %s", e)
137 attempts += 1
138 if attempts < self.max_attempts:
139 time.sleep(2 * attempts) # 指数退避
140
141 raise Exception("GPTVL: 达到最大重试次数,仍未获得有效响应。")
142
143
144 if __name__ == "__main__":
145 import sys
146 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
147 from config import Config
148
149 print("=== GPT VL 多模态可用性测试 ===")
150 if not Config.OPENAI_API_KEY:
151 print("✗ 未设置 OPENAI_API_KEY,跳过")
152 sys.exit(1)
153 client = GPTVLClient(api_key=Config.OPENAI_API_KEY, base_url=Config.OPENAI_BASE_URL, proxy=Config.provider_proxy("openai"))
154
155 # 支持的 VLM 模型列表,包含用户要求的 gpt-5.4
156 MODELS = ["gpt-5.4"]
157
158 img_path = "code/result/image/test_avail/test_input.png"
159 if not os.path.exists(img_path):
160 print(f"✗ 测试图片不存在: {img_path}")
161 else:
162 text = "请描述这张图片的内容"
163 for model in MODELS:
164 print(f"\n--- 测试模型: {model} ---")
165 print(f"输入文本: {text}")
166 print(f"输入图片: {img_path}")
167 t0 = time.time()
168 try:
169 result = client.chat(text=text, images=[img_path], model=model)
170 elapsed = time.time() - t0
171 if result:
172 print(f"✓ 返回结果 ({elapsed:.1f}s): {result[:200]}")
173 else:
174 print(f"✗ 返回空结果 ({elapsed:.1f}s)")
175 except Exception as e:
176 print(f"✗ 失败: {e}")
177
177 lines PYTHON