返回 Pixelle-Video
vlm_client.py
1 import os
2 from typing import List, Optional
3 from .config import Config
4
5 try:
6 from .vlm_dashscope import QwenVLClient
7 except ImportError:
8 from vlm_dashscope import QwenVLClient
9
10 class VLM:
11 def __init__(self,
12 dashscope_api_key: Optional[str] = None,
13 dashscope_base_url: Optional[str] = None):
14 """
15 Unified VLM (Vision Language Model) Client
16 Routes asset-analysis VLM requests to DashScope (Qwen/Qwen-Omni).
17 """
18 dashscope_key = dashscope_api_key or Config.DASHSCOPE_API_KEY
19
20 self.dashscope_client = (
21 QwenVLClient(
22 api_key=dashscope_key,
23 base_url=dashscope_base_url or Config.DASHSCOPE_BASE_URL
24 )
25 if dashscope_key else None
26 )
27
28 def query(self,
29 prompt: str,
30 image_paths: Optional[List[str]] = None,
31 model: Optional[str] = None,
32 session_id: Optional[str] = None,
33 video_paths: Optional[List[str]] = None) -> str:
34 selected_model = (model or "").strip()
35 if not selected_model:
36 raise RuntimeError("DashScope VLM model must be explicitly selected.")
37
38 if Config.PRINT_MODEL_INPUT:
39 print("---- VLM REQUEST ----")
40 print(f"Prompt: {prompt}")
41 if image_paths:
42 print(f"Images: {len(image_paths)}")
43 for p in image_paths:
44 if p.startswith("data:"):
45 print(f" - [Base64图片]")
46 else:
47 print(f" - {p}")
48 if video_paths:
49 print(f"Videos: {len(video_paths)}")
50 for p in video_paths:
51 print(f" - {p}")
52 print(f"Model: {selected_model}")
53 if session_id:
54 print(f"Session ID: {session_id}")
55 print("-" * 30)
56
57 if self.dashscope_client is None:
58 raise RuntimeError("DashScope VLM API key is not configured.")
59
60 image_urls = [self._to_dashscope_file_url(path, allow_data_url=True) for path in image_paths or []]
61 video_urls = [self._to_dashscope_file_url(path, allow_data_url=False) for path in video_paths or []]
62 return self.dashscope_client.chat(
63 text=prompt,
64 images=image_urls,
65 videos=video_urls,
66 model=selected_model,
67 stream=False,
68 )
69
70 def _to_dashscope_file_url(self, path: str, allow_data_url: bool) -> str:
71 if path.startswith("data:"):
72 if not allow_data_url:
73 raise ValueError("DashScope video input does not support data URLs in this adapter.")
74
75 import base64 as b64
76 import tempfile
77
78 try:
79 header, b64_data = path.split(",", 1)
80 mime_type = header.split(";")[0].replace("data:", "")
81 image_data = b64.b64decode(b64_data)
82 suffix = f".{mime_type.split('/')[-1]}" if "/" in mime_type else ".png"
83 with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp:
84 tmp.write(image_data)
85 temp_path = tmp.name
86 return f"file://{os.path.abspath(temp_path)}"
87 except Exception as e:
88 print(f"Error processing base64 image: {e}")
89 raise ValueError(f"无法解析 base64 图片: {e}")
90
91 if path.startswith("http") or path.startswith("file://"):
92 return path
93
94 return f"file://{os.path.abspath(path)}"
95
95 lines PYTHON