File size: 16,681 Bytes
f1b4581
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
import json
import requests
import os
from typing import Generator
from openai import OpenAI
from .base import BaseModel

class DeepSeekModel(BaseModel):
    def __init__(self, api_key: str, temperature: float = 0.7, system_prompt: str = None, language: str = None, model_name: str = "deepseek-reasoner", api_base_url: str = None):
        super().__init__(api_key, temperature, system_prompt, language)
        self.model_name = model_name
        self.api_base_url = api_base_url  # 存储API基础URL

    def get_default_system_prompt(self) -> str:
        return """You are an expert at analyzing questions and providing detailed solutions. When presented with an image of a question:
1. First read and understand the question carefully
2. Break down the key components of the question
3. Provide a clear, step-by-step solution
4. If relevant, explain any concepts or theories involved
5. If there are multiple approaches, explain the most efficient one first"""

    def get_model_identifier(self) -> str:
        """根据模型名称返回正确的API标识符"""
        # 通过模型名称来确定实际的API调用标识符
        if self.model_name == "deepseek-chat":
            return "deepseek-chat"
        # 如果是deepseek-reasoner或包含reasoner的模型名称,返回推理模型标识符
        if "reasoner" in self.model_name.lower():
            return "deepseek-reasoner"
        # 对于deepseek-chat也返回对应的模型名称
        if "chat" in self.model_name.lower() or self.model_name == "deepseek-chat":
            return "deepseek-chat"
        
        # 根据配置中的模型ID来确定实际的模型类型
        if self.model_name == "deepseek-reasoner":
            return "deepseek-reasoner"
        elif self.model_name == "deepseek-chat":
            return "deepseek-chat"
            
        # 默认使用deepseek-chat作为API标识符
        print(f"未知的DeepSeek模型名称: {self.model_name},使用deepseek-chat作为默认值")
        return "deepseek-chat"

    def analyze_text(self, text: str, proxies: dict = None) -> Generator[dict, None, None]:
        """Stream DeepSeek's response for text analysis"""
        try:
            # Initial status
            yield {"status": "started", "content": ""}

            # 保存原始环境变量
            original_env = {
                'http_proxy': os.environ.get('http_proxy'),
                'https_proxy': os.environ.get('https_proxy')
            }

            try:
                # 如果提供了代理设置,通过环境变量设置
                if proxies:
                    if 'http' in proxies:
                        os.environ['http_proxy'] = proxies['http']
                    if 'https' in proxies:
                        os.environ['https_proxy'] = proxies['https']

                # 初始化DeepSeek客户端,不再使用session对象
                client = OpenAI(
                    api_key=self.api_key,
                    base_url="https://api.deepseek.com"
                )

                # 使用系统提供的系统提示词,不再自动添加语言指令
                system_prompt = self.system_prompt

                # 构建请求参数
                params = {
                    "model": self.get_model_identifier(),
                    "messages": [
                        {
                            'role': 'system',
                            'content': system_prompt
                        }, 
                        {
                            'role': 'user',
                            'content': text
                        }
                    ],
                    "stream": True
                }
                
                # 只有非推理模型才设置temperature参数
                if not self.get_model_identifier().endswith('reasoner') and self.temperature is not None:
                    params["temperature"] = self.temperature
                    
                print(f"调用DeepSeek API: {self.get_model_identifier()}, 是否设置温度: {not self.get_model_identifier().endswith('reasoner')}, 温度值: {self.temperature if not self.get_model_identifier().endswith('reasoner') else 'N/A'}")

                response = client.chat.completions.create(**params)
                
                # 使用两个缓冲区,分别用于常规内容和思考内容
                response_buffer = ""
                thinking_buffer = ""
                
                for chunk in response:
                    # 打印chunk以调试
                    try:
                        print(f"DeepSeek API返回chunk: {chunk}")
                    except:
                        print("无法打印chunk")
                        
                    try:
                        # 同时处理两种不同的内容,确保正确区分思考内容和最终内容
                        delta = chunk.choices[0].delta
                        
                        # 处理推理模型的思考内容
                        if hasattr(delta, 'reasoning_content') and delta.reasoning_content:
                            content = delta.reasoning_content
                            thinking_buffer += content
                            
                            # 发送思考内容更新
                            if len(content) >= 20 or content.endswith(('.', '!', '?', '。', '!', '?', '\n')):
                                yield {
                                    "status": "thinking",
                                    "content": thinking_buffer
                                }
                        
                        # 处理最终结果内容 - 即使在推理模型中也会有content字段
                        if hasattr(delta, 'content') and delta.content:
                            content = delta.content
                            response_buffer += content
                            print(f"累积响应内容: '{content}', 当前buffer: '{response_buffer}'")
                            
                            # 发送结果内容更新
                            if len(content) >= 10 or content.endswith(('.', '!', '?', '。', '!', '?', '\n')):
                                yield {
                                    "status": "streaming",
                                    "content": response_buffer
                                }
                        
                        # 处理消息结束
                        if hasattr(chunk.choices[0], 'finish_reason') and chunk.choices[0].finish_reason:
                            print(f"生成结束,原因: {chunk.choices[0].finish_reason}")
                            # 注意:不要在这里把思考内容作为正文,因为这可能导致重复内容
                    except Exception as e:
                        print(f"解析响应chunk时出错: {str(e)}")
                        continue

                # 确保发送最终的缓冲内容
                if thinking_buffer:
                    yield {
                        "status": "thinking_complete",
                        "content": thinking_buffer
                    }
                
                # 发送最终响应内容
                if response_buffer:
                    yield {
                        "status": "completed",
                        "content": response_buffer
                    }
                
                # 如果没有正常的响应内容,但有思考内容,则将思考内容作为最终结果
                elif thinking_buffer:
                    yield {
                        "status": "completed",
                        "content": thinking_buffer
                    }
                else:
                    # 如果两者都没有,返回一个空结果
                    yield {
                        "status": "completed",
                        "content": "没有获取到内容"
                    }

            except Exception as e:
                error_msg = str(e)
                print(f"DeepSeek API调用出错: {error_msg}")
                
                # 提供具体的错误信息
                if "invalid_api_key" in error_msg.lower():
                    error_msg = "DeepSeek API密钥无效,请检查您的API密钥"
                elif "rate_limit" in error_msg.lower():
                    error_msg = "DeepSeek API请求频率超限,请稍后再试"
                elif "quota_exceeded" in error_msg.lower():
                    error_msg = "DeepSeek API配额已用完,请续费或等待下个计费周期"
                
                yield {
                    "status": "error",
                    "error": f"DeepSeek API错误: {error_msg}"
                }
            finally:
                # 恢复原始环境变量
                for key, value in original_env.items():
                    if value is None:
                        if key in os.environ:
                            del os.environ[key]
                    else:
                        os.environ[key] = value

        except Exception as e:
            error_msg = str(e)
            print(f"调用DeepSeek模型时发生错误: {error_msg}")
            
            if "invalid_api_key" in error_msg.lower():
                error_msg = "API密钥无效,请检查设置"
            elif "rate_limit" in error_msg.lower():
                error_msg = "API请求频率超限,请稍后再试"
            
            yield {
                "status": "error",
                "error": f"DeepSeek API错误: {error_msg}"
            }

    def analyze_image(self, image_data: str, proxies: dict = None) -> Generator[dict, None, None]:
        """Stream DeepSeek's response for image analysis"""
        try:
            # 检查我们是否有支持图像的模型
            if self.model_name == "deepseek-chat" or self.model_name == "deepseek-reasoner":
                yield {
                    "status": "error",
                    "error": "当前DeepSeek模型不支持图像分析,请使用Anthropic或OpenAI的多模态模型"
                }
                return
                
            # Initial status
            yield {"status": "started", "content": ""}

            # 保存原始环境变量
            original_env = {
                'http_proxy': os.environ.get('http_proxy'),
                'https_proxy': os.environ.get('https_proxy')
            }

            try:
                # 如果提供了代理设置,通过环境变量设置
                if proxies:
                    if 'http' in proxies:
                        os.environ['http_proxy'] = proxies['http']
                    if 'https' in proxies:
                        os.environ['https_proxy'] = proxies['https']

                # 初始化DeepSeek客户端,不再使用session对象
                client = OpenAI(
                    api_key=self.api_key,
                    base_url="https://api.deepseek.com"
                )

                # 使用系统提供的系统提示词,不再自动添加语言指令
                system_prompt = self.system_prompt

                # 构建请求参数
                params = {
                    "model": self.get_model_identifier(),
                    "messages": [
                        {
                            'role': 'system',
                            'content': system_prompt
                        }, 
                        {
                            'role': 'user',
                            'content': f"Here's an image of a question to analyze: data:image/png;base64,{image_data}"
                        }
                    ],
                    "stream": True
                }
                
                # 只有非推理模型才设置temperature参数
                if not self.get_model_identifier().endswith('reasoner') and self.temperature is not None:
                    params["temperature"] = self.temperature

                response = client.chat.completions.create(**params)
                
                # 使用两个缓冲区,分别用于常规内容和思考内容
                response_buffer = ""
                thinking_buffer = ""
                
                for chunk in response:
                    # 打印chunk以调试
                    try:
                        print(f"DeepSeek图像API返回chunk: {chunk}")
                    except:
                        print("无法打印chunk")
                
                    try:
                        # 同时处理两种不同的内容,确保正确区分思考内容和最终内容
                        delta = chunk.choices[0].delta
                        
                        # 处理推理模型的思考内容
                        if hasattr(delta, 'reasoning_content') and delta.reasoning_content:
                            content = delta.reasoning_content
                            thinking_buffer += content
                            
                            # 发送思考内容更新
                            if len(content) >= 20 or content.endswith(('.', '!', '?', '。', '!', '?', '\n')):
                                yield {
                                    "status": "thinking",
                                    "content": thinking_buffer
                                }
                        
                        # 处理最终结果内容 - 即使在推理模型中也会有content字段
                        if hasattr(delta, 'content') and delta.content:
                            content = delta.content
                            response_buffer += content
                            print(f"累积图像响应内容: '{content}', 当前buffer: '{response_buffer}'")
                            
                            # 发送结果内容更新
                            if len(content) >= 10 or content.endswith(('.', '!', '?', '。', '!', '?', '\n')):
                                yield {
                                    "status": "streaming",
                                    "content": response_buffer
                                }
                        
                        # 处理消息结束
                        if hasattr(chunk.choices[0], 'finish_reason') and chunk.choices[0].finish_reason:
                            print(f"图像生成结束,原因: {chunk.choices[0].finish_reason}")
                    except Exception as e:
                        print(f"解析图像响应chunk时出错: {str(e)}")
                        continue

                # 确保发送最终的缓冲内容
                if thinking_buffer:
                    yield {
                        "status": "thinking_complete",
                        "content": thinking_buffer
                    }
                
                # 发送最终响应内容
                if response_buffer:
                    yield {
                        "status": "completed",
                        "content": response_buffer
                    }
                
            except Exception as e:
                error_msg = str(e)
                print(f"DeepSeek API调用出错: {error_msg}")
                
                # 提供具体的错误信息
                if "invalid_api_key" in error_msg.lower():
                    error_msg = "DeepSeek API密钥无效,请检查您的API密钥"
                elif "rate_limit" in error_msg.lower():
                    error_msg = "DeepSeek API请求频率超限,请稍后再试"
                
                yield {
                    "status": "error",
                    "error": f"DeepSeek API错误: {error_msg}"
                }
            finally:
                # 恢复原始环境变量
                for key, value in original_env.items():
                    if value is None:
                        if key in os.environ:
                            del os.environ[key]
                    else:
                        os.environ[key] = value

        except Exception as e:
            error_msg = str(e)
            if "invalid_api_key" in error_msg.lower():
                error_msg = "API密钥无效,请检查设置"
            elif "rate_limit" in error_msg.lower():
                error_msg = "API请求频率超限,请稍后再试"
            
            yield {
                "status": "error",
                "error": f"DeepSeek API错误: {error_msg}"
            }