返回 VideoClaw
llm_client.py
根目录 / video-claw / video-claw / backend / models / llm_client.py
1 import os
2 import sys
3
4 models_dir = os.path.dirname(os.path.abspath(__file__))
5 backend_dir = os.path.dirname(models_dir)
6 if backend_dir not in sys.path:
7 sys.path.insert(0, backend_dir)
8
9 import logging
10
11 try:
12 from models.llm_gpt import GPT
13 from models.llm_gemini import Gemini
14 from models.llm_deepseek import DeepSeek
15 from models.llm_dashscope import QwenLLM
16 from models.vlm_dashscope import QwenVLClient
17 except ImportError:
18 from llm_gpt import GPT
19 from llm_gemini import Gemini
20 from llm_deepseek import DeepSeek
21 from llm_dashscope import QwenLLM
22 from vlm_dashscope import QwenVLClient
23
24 from config import Config
25
26 logger = logging.getLogger(__name__)
27
28 class LLM:
29 def __init__(self, gemini_base_url="", gemini_api_key="", gpt_base_url="", gpt_api_key="", deepseek_base_url="", deepseek_api_key="", dashscope_api_key=""):
30 self._gemini_base_url = gemini_base_url or Config.GOOGLE_GEMINI_BASE_URL
31 self._gemini_api_key = gemini_api_key or Config.GEMINI_API_KEY
32 self._gpt_base_url = gpt_base_url or Config.OPENAI_BASE_URL
33 self._gpt_api_key = gpt_api_key or Config.OPENAI_API_KEY
34 self._deepseek_base_url = deepseek_base_url or Config.DEEPSEEK_BASE_URL
35 self._deepseek_api_key = deepseek_api_key or Config.DEEPSEEK_API_KEY
36 self._dashscope_api_key = dashscope_api_key or Config.DASHSCOPE_API_KEY
37
38 self._gemini_client = None
39 self._gpt_client = None
40 self._deepseek_client = None
41 self._dashscope_client = None
42 self._dashscope_vl_client = None
43
44 @property
45 def gemini_client(self):
46 if self._gemini_client is None:
47 self._gemini_client = Gemini(
48 base_url=self._gemini_base_url,
49 api_key=self._gemini_api_key,
50 )
51 return self._gemini_client
52
53 @property
54 def gpt_client(self):
55 if self._gpt_client is None:
56 self._gpt_client = GPT(
57 base_url=self._gpt_base_url,
58 api_key=self._gpt_api_key,
59 proxy=Config.provider_proxy("openai"),
60 )
61 return self._gpt_client
62
63 @property
64 def deepseek_client(self):
65 if self._deepseek_client is None:
66 self._deepseek_client = DeepSeek(
67 base_url=self._deepseek_base_url,
68 api_key=self._deepseek_api_key,
69 )
70 return self._deepseek_client
71
72 @property
73 def dashscope_client(self):
74 if self._dashscope_client is None:
75 self._dashscope_client = QwenLLM(api_key=self._dashscope_api_key)
76 return self._dashscope_client
77
78 @property
79 def dashscope_vl_client(self):
80 if self._dashscope_vl_client is None:
81 self._dashscope_vl_client = QwenVLClient(api_key=self._dashscope_api_key)
82 return self._dashscope_vl_client
83
84 def full_to_half(self, text):
85 if not isinstance(text, str):
86 return text
87
88 translation_table = {0x3000: 0x0020}
89 for i in range(65281, 65375):
90 translation_table[i] = i - 65248
91
92 return text.translate(translation_table)
93
94 def query(self, prompt, image_urls=[], model="qwen3.6-max-preview", safe_content=True, task_id=None, web_search=False):
95 """
96 Query the LLM with a prompt and optional image URLs.
97 Selects the backend (GPT or Gemini) based on the model name.
98
99 :param web_search: Enable web search for supported providers
100 """
101 if safe_content:
102 prompt = self.full_to_half(prompt)
103
104 if not model:
105 model = "qwen3.6-max-preview"
106
107 if Config.PRINT_MODEL_INPUT:
108 lines = [
109 "---- LLM QUERY REQUEST ----",
110 f"Model: {model}",
111 ]
112 if task_id:
113 lines.append(f"Task ID: {task_id}")
114 if image_urls:
115 lines.append(f"Images: {len(image_urls)}")
116 lines.extend(f" - {u}" for u in image_urls)
117 lines.extend([
118 f"Web Search: {web_search}",
119 f"Prompt: {prompt[:200]}{'...' if len(prompt) > 200 else ''}",
120 "-" * 30,
121 ])
122 logger.info("\n%s", "\n".join(lines))
123
124 result = ""
125 model_lower = model.lower()
126 if model_lower.startswith("gemini"):
127 result = self.gemini_client.query(prompt, image_urls=image_urls, model=model)
128 elif "gpt" in model_lower:
129 # OpenAI series models
130 result = self.gpt_client.query(prompt, image_urls=image_urls, model=model, web_search=web_search)
131 elif "kimi" in model_lower or "qwen3.6-plus" in model_lower or "qwen3.6-flash" in model_lower or "vl" in model_lower:
132 # DashScope VLM models (using MultiModalConversation API)
133 result = self.dashscope_vl_client.chat(text=prompt, images=image_urls, model=model, stream=False)
134 elif "deepseek-v3.2" in model_lower:
135 # DeepSeek v3.2 (通过 DashScope Generation API)
136 result = self.dashscope_client.query(prompt, image_urls=image_urls, model=model, web_search=web_search)
137 elif model_lower.startswith("deepseek") and "v3.2" not in model_lower:
138 # Original DeepSeek provider
139 result = self.deepseek_client.query(prompt, image_urls=image_urls, model=model, web_search=web_search)
140 else:
141 # Default to Qwen models / deepseek-v3.2 via DashScope Generation API
142 result = self.dashscope_client.query(prompt, image_urls=image_urls, model=model, web_search=web_search)
143
144 if safe_content:
145 result = self.full_to_half(result)
146
147 # Remove empty lines
148 return '\n'.join([line for line in result.split('\n') if line.strip() != ''])
149
149 lines PYTHON