11# coding=utf-8
22
3- from typing import Dict
3+ from typing import Dict , Optional , Any , Iterator
44
5+ import requests
56from langchain_community .chat_models import ChatTongyi
6- from langchain_core .messages import HumanMessage
7+ from langchain_core .language_models import LanguageModelInput
8+ from langchain_core .messages import HumanMessage , BaseMessageChunk , AIMessage
79from django .utils .translation import gettext
10+ from langchain_core .runnables import RunnableConfig
11+
812from models_provider .base_model_provider import MaxKBBaseModel
913from models_provider .impl .base_chat_open_ai import BaseChatOpenAI
10-
14+ import json
1115
1216class QwenVLChatModel (MaxKBBaseModel , BaseChatOpenAI ):
1317
@@ -32,3 +36,113 @@ def new_instance(model_type, model_name, model_credential: Dict[str, object], **
3236 def check_auth (self , api_key ):
3337 chat = ChatTongyi (api_key = api_key , model_name = 'qwen-max' )
3438 chat .invoke ([HumanMessage ([{"type" : "text" , "text" : gettext ('Hello' )}])])
39+
40+ def get_upload_policy (self , api_key , model_name ):
41+ """获取文件上传凭证"""
42+ url = "https://dashscope.aliyuncs.com/api/v1/uploads"
43+ headers = {
44+ "Authorization" : f"Bearer { api_key } " ,
45+ "Content-Type" : "application/json"
46+ }
47+ params = {
48+ "action" : "getPolicy" ,
49+ "model" : model_name
50+ }
51+
52+ response = requests .get (url , headers = headers , params = params )
53+ if response .status_code != 200 :
54+ raise Exception (f"Failed to get upload policy: { response .text } " )
55+
56+ return response .json ()['data' ]
57+
58+ def upload_file_to_oss (self , policy_data , file_stream , file_name ):
59+ """将文件流上传到临时存储OSS"""
60+ # 构建OSS上传的目标路径
61+ key = f"{ policy_data ['upload_dir' ]} /{ file_name } "
62+
63+ # 构建上传数据
64+ files = {
65+ 'OSSAccessKeyId' : (None , policy_data ['oss_access_key_id' ]),
66+ 'Signature' : (None , policy_data ['signature' ]),
67+ 'policy' : (None , policy_data ['policy' ]),
68+ 'x-oss-object-acl' : (None , policy_data ['x_oss_object_acl' ]),
69+ 'x-oss-forbid-overwrite' : (None , policy_data ['x_oss_forbid_overwrite' ]),
70+ 'key' : (None , key ),
71+ 'success_action_status' : (None , '200' ),
72+ 'file' : (file_name , file_stream )
73+ }
74+
75+ # 执行上传请求
76+ response = requests .post (policy_data ['upload_host' ], files = files )
77+ if response .status_code != 200 :
78+ raise Exception (f"Failed to upload file: { response .text } " )
79+
80+ return f"oss://{ key } "
81+
82+ def upload_file_and_get_url (self , file_stream , file_name ):
83+ """上传文件并获取URL"""
84+ # 1. 获取上传凭证,上传凭证接口有限流,超出限流将导致请求失败
85+ policy_data = self .get_upload_policy (self .openai_api_key .get_secret_value (), self .model_name )
86+ # 2. 上传文件到OSS
87+ oss_url = self .upload_file_to_oss (policy_data , file_stream , file_name )
88+ print (oss_url )
89+
90+ return oss_url
91+
92+ def stream (
93+ self ,
94+ input : LanguageModelInput ,
95+ config : Optional [RunnableConfig ] = None ,
96+ * ,
97+ stop : Optional [list [str ]] = None ,
98+ ** kwargs : Any ,
99+ ) -> Iterator [BaseMessageChunk ]:
100+ url = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions"
101+
102+ headers = {
103+ "Authorization" : f"Bearer { self .openai_api_key .get_secret_value ()} " ,
104+ "Content-Type" : "application/json" ,
105+ "X-DashScope-OssResourceResolve" : "enable"
106+ }
107+
108+ data = {
109+ "model" : self .model_name ,
110+ "messages" : [
111+ {
112+ "role" : "user" ,
113+ "content" : input [0 ].content
114+ }
115+ ],
116+ ** self .extra_body ,
117+ "stream" : True ,
118+ }
119+ response = requests .post (url , headers = headers , json = data )
120+ if response .status_code != 200 :
121+ raise Exception (f"Failed to get response: { response .text } " )
122+ for line in response .iter_lines ():
123+ if line :
124+ try :
125+ decoded_line = line .decode ('utf-8' )
126+ # 检查是否是有效的SSE数据行
127+ if decoded_line .startswith ('data: ' ):
128+ # 提取JSON部分
129+ json_str = decoded_line [6 :] # 移除 'data: ' 前缀
130+ # 检查是否是结束标记
131+ if json_str .strip () == '[DONE]' :
132+ continue
133+
134+ # 尝试解析JSON
135+ chunk_data = json .loads (json_str )
136+
137+ if 'choices' in chunk_data and chunk_data ['choices' ]:
138+ delta = chunk_data ['choices' ][0 ].get ('delta' , {})
139+ content = delta .get ('content' , '' )
140+ if content :
141+ print (content )
142+ yield AIMessage (content = content )
143+ except json .JSONDecodeError :
144+ # 忽略无法解析的行
145+ continue
146+ except Exception as e :
147+ # 处理其他可能的异常
148+ continue
0 commit comments