XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

Qwen add media

bfc7707c
Ammar <ammar.alkotb@gmail.com>
提交于

代码差异

2 个文件 +543 -398
Modified g4f/Provider/Qwen.py +137 -41
@@ -5,16 +5,49 @@ import json
5 5 import re
6 6 import uuid
7 7 from time import time
8 from typing import Literal, Optional
8 9
9 10 import aiohttp
10 11 from ..errors import RateLimitError
11 from ..typing import AsyncResult, Messages
12 from ..providers.response import JsonConversation, Reasoning, Usage
12 from ..typing import AsyncResult, Messages, MediaListType
13 from ..providers.response import JsonConversation, Reasoning, Usage, ImageResponse, FinishReason
13 14 from ..requests import sse_stream
14 15 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
15 16 from .helper import get_last_user_message
16 17 from .. import debug
17 18
19 try:
20 import curl_cffi
21
22 has_curl_cffi = True
23 except ImportError:
24 has_curl_cffi = False
25
26 text_models = [
27 'qwen3-max-preview', 'qwen-plus-2025-09-11', 'qwen3-235b-a22b', 'qwen3-coder-plus', 'qwen3-30b-a3b',
28 'qwen3-coder-30b-a3b-instruct', 'qwen-max-latest', 'qwen-plus-2025-01-25', 'qwq-32b', 'qwen-turbo-2025-02-11',
29 'qwen2.5-omni-7b', 'qvq-72b-preview-0310', 'qwen2.5-vl-32b-instruct', 'qwen2.5-14b-instruct-1m',
30 'qwen2.5-coder-32b-instruct', 'qwen2.5-72b-instruct']
31
32 image_models = [
33 'qwen3-max-preview', 'qwen-plus-2025-09-11', 'qwen3-235b-a22b', 'qwen3-coder-plus', 'qwen3-30b-a3b',
34 'qwen3-coder-30b-a3b-instruct', 'qwen-max-latest', 'qwen-plus-2025-01-25', 'qwen-turbo-2025-02-11',
35 'qwen2.5-omni-7b', 'qwen2.5-vl-32b-instruct', 'qwen2.5-14b-instruct-1m', 'qwen2.5-coder-32b-instruct',
36 'qwen2.5-72b-instruct']
37
38 vision_models = [
39 'qwen3-max-preview', 'qwen-plus-2025-09-11', 'qwen3-235b-a22b', 'qwen3-coder-plus', 'qwen3-30b-a3b',
40 'qwen3-coder-30b-a3b-instruct', 'qwen-max-latest', 'qwen-plus-2025-01-25', 'qwen-turbo-2025-02-11',
41 'qwen2.5-omni-7b', 'qvq-72b-preview-0310', 'qwen2.5-vl-32b-instruct', 'qwen2.5-14b-instruct-1m',
42 'qwen2.5-coder-32b-instruct', 'qwen2.5-72b-instruct']
43
44 models = [
45 'qwen3-max-preview', 'qwen-plus-2025-09-11', 'qwen3-235b-a22b', 'qwen3-coder-plus', 'qwen3-30b-a3b',
46 'qwen3-coder-30b-a3b-instruct', 'qwen-max-latest', 'qwen-plus-2025-01-25', 'qwq-32b', 'qwen-turbo-2025-02-11',
47 'qwen2.5-omni-7b', 'qvq-72b-preview-0310', 'qwen2.5-vl-32b-instruct', 'qwen2.5-14b-instruct-1m',
48 'qwen2.5-coder-32b-instruct', 'qwen2.5-72b-instruct']
49
50
18 51 class Qwen(AsyncGeneratorProvider, ProviderModelMixin):
19 52 """
20 53 Provider for Qwen's chat service (chat.qwen.ai), with configurable
@@ -26,42 +59,69 @@ class Qwen(AsyncGeneratorProvider, ProviderModelMixin):
26 59 supports_stream = True
27 60 supports_message_history = False
28 61
62 _models_loaded = True
29 63 # Complete list of models, extracted from the API
30 models = [
31 "qwen3-max-preview",
32 "qwen3-235b-a22b",
33 "qwen3-coder-plus",
34 "qwen3-30b-a3b",
35 "qwen3-coder-30b-a3b-instruct",
36 "qwen-max-latest",
37 "qwen-plus-2025-01-25",
38 "qwq-32b",
39 "qwen-turbo-2025-02-11",
40 "qwen2.5-omni-7b",
41 "qvq-72b-preview-0310",
42 "qwen2.5-vl-32b-instruct",
43 "qwen2.5-14b-instruct-1m",
44 "qwen2.5-coder-32b-instruct",
45 "qwen2.5-72b-instruct",
46 ]
64 image_models = image_models
65 text_models = text_models
66 vision_models = vision_models
67 models = models
47 68 default_model = "qwen3-235b-a22b"
48 69
49 70 _midtoken: str = None
50 71 _midtoken_uses: int = 0
51 72
73 @classmethod
74 def get_models(cls) -> list[str]:
75 if not cls._models_loaded and has_curl_cffi:
76 response = curl_cffi.get(f"{cls.url}/api/models")
77 if response.ok:
78 models = response.json().get("data", [])
79 cls.text_models = [model["id"] for model in models if "t2t" in model["info"]["meta"]["chat_type"]]
80
81 cls.image_models = [
82 model["id"] for model in models if
83 "image_edit" in model["info"]["meta"]["chat_type"] or "t2i" in model["info"]["meta"]["chat_type"]
84 ]
85
86 cls.vision_models = [model["id"] for model in models if model["info"]["meta"]["capabilities"]["vision"]]
87
88 cls.models = [model["id"] for model in models]
89 cls.default_model = cls.models[0]
90 cls._models_loaded = True
91
92 else:
93 debug.log(f"Failed to load models from {cls.url}: {response.status_code} {response.reason}")
94 return cls.models
95
52 96 @classmethod
53 97 async def create_async_generator(
54 cls,
55 model: str,
56 messages: Messages,
57 conversation: JsonConversation = None,
58 proxy: str = None,
59 timeout: int = 120,
60 stream: bool = True,
61 enable_thinking: bool = True,
62 **kwargs
98 cls,
99 model: str,
100 messages: Messages,
101 media: MediaListType = None,
102 conversation: JsonConversation = None,
103 proxy: str = None,
104 timeout: int = 120,
105 stream: bool = True,
106 enable_thinking: bool = True,
107 chat_type: Literal[
108 "t2t", "search", "artifacts", "web_dev", "deep_research", "t2i", "image_edit", "t2v"
109 ] = "t2t",
110 image_size: Optional[Literal["1:1", "4:3", "3:4", "16:9", "9:16"]] = None,
111 **kwargs
63 112 ) -> AsyncResult:
64
113 """
114 chat_type:
115 DeepResearch = "deep_research"
116 Artifacts = "artifacts"
117 WebSearch = "search"
118 ImageGeneration = "t2i"
119 ImageEdit = "image_edit"
120 VideoGeneration = "t2v"
121 Txt2Txt = "t2t"
122 WebDev = "web_dev"
123 """
124
65 125 model_name = cls.get_model(model)
66 126
67 127 headers = {
@@ -94,7 +154,8 @@ class Qwen(AsyncGeneratorProvider, ProviderModelMixin):
94 154 raise RuntimeError("Failed to extract bx-umidtoken.")
95 155 cls._midtoken = match.group(1)
96 156 cls._midtoken_uses = 1
97 debug.log(f"[Qwen] INFO: New midtoken obtained. Use count: {cls._midtoken_uses}. Midtoken: {cls._midtoken}")
157 debug.log(
158 f"[Qwen] INFO: New midtoken obtained. Use count: {cls._midtoken_uses}. Midtoken: {cls._midtoken}")
98 159 else:
99 160 cls._midtoken_uses += 1
100 161 debug.log(f"[Qwen] INFO: Reusing midtoken. Use count: {cls._midtoken_uses}")
@@ -109,11 +170,11 @@ class Qwen(AsyncGeneratorProvider, ProviderModelMixin):
109 170 "title": "New Chat",
110 171 "models": [model_name],
111 172 "chat_mode": "normal",
112 "chat_type": "t2t",
173 "chat_type": chat_type,
113 174 "timestamp": int(time() * 1000)
114 175 }
115 176 async with session.post(
116 f'{cls.url}/api/v2/chats/new', json=chat_payload, headers=req_headers, proxy=proxy
177 f'{cls.url}/api/v2/chats/new', json=chat_payload, headers=req_headers, proxy=proxy
117 178 ) as resp:
118 179 resp.raise_for_status()
119 180 data = await resp.json()
@@ -124,7 +185,31 @@ class Qwen(AsyncGeneratorProvider, ProviderModelMixin):
124 185 cookies={key: value for key, value in resp.cookies.items()},
125 186 parent_id=None
126 187 )
127
188 files = []
189 if media:
190 for index, (_file, file_name) in enumerate(media):
191 file_class: Literal["default", "vision", "video", "audio", "document"] = "vision"
192 _type: Literal["file", "image", "video", "audio"] = "image"
193 file_type = "image/jpeg"
194 showType: Literal["file", "image", "video", "audio"] = "image"
195
196 if isinstance(_file, str) and _file.startswith('http'):
197 if chat_type == "image_edit":
198 file_class = "vision"
199 _type = "image"
200 file_type = "image"
201 showType = "image"
202
203 files.append(
204 {
205 "type": _type,
206 "name": file_name,
207 "file_type": file_type,
208 "showType": showType,
209 "file_class": file_class, # "document"
210 "url": _file
211 }
212 )
128 213 msg_payload = {
129 214 "stream": stream,
130 215 "incremental_output": stream,
@@ -140,9 +225,9 @@ class Qwen(AsyncGeneratorProvider, ProviderModelMixin):
140 225 "role": "user",
141 226 "content": prompt,
142 227 "user_action": "chat",
143 "files": [],
228 "files": files,
144 229 "models": [model_name],
145 "chat_type": "t2t",
230 "chat_type": chat_type,
146 231 "feature_config": {
147 232 "thinking_enabled": enable_thinking,
148 233 "output_schema": "phase",
@@ -150,18 +235,20 @@ class Qwen(AsyncGeneratorProvider, ProviderModelMixin):
150 235 },
151 236 "extra": {
152 237 "meta": {
153 "subChatType": "t2t"
238 "subChatType": chat_type
154 239 }
155 240 },
156 "sub_chat_type": "t2t",
241 "sub_chat_type": chat_type,
157 242 "parent_id": None
158 243 }
159 244 ]
160 245 }
246 if image_size:
247 msg_payload["size"] = image_size
161 248
162 249 async with session.post(
163 f'{cls.url}/api/v2/chat/completions?chat_id={conversation.chat_id}', json=msg_payload,
164 headers=req_headers, proxy=proxy, timeout=timeout, cookies=conversation.cookies
250 f'{cls.url}/api/v2/chat/completions?chat_id={conversation.chat_id}', json=msg_payload,
251 headers=req_headers, proxy=proxy, timeout=timeout, cookies=conversation.cookies
165 252 ) as resp:
166 253 first_line = await resp.content.readline()
167 254 line_str = first_line.decode().strip()
@@ -182,10 +269,18 @@ class Qwen(AsyncGeneratorProvider, ProviderModelMixin):
182 269 delta = choices[0].get("delta", {})
183 270 phase = delta.get("phase")
184 271 content = delta.get("content")
272 status = delta.get("status")
273 extra = delta.get("extra", {})
185 274 if phase == "think" and not thinking_started:
186 275 thinking_started = True
187 276 elif phase == "answer" and thinking_started:
188 277 thinking_started = False
278 elif phase == "image_gen" and status == "typing":
279 yield ImageResponse([content], "", extra)
280 continue
281 elif phase == "image_gen" and status == "finished":
282 yield FinishReason(status)
283
189 284 if content:
190 285 yield Reasoning(content) if thinking_started else content
191 286 except (json.JSONDecodeError, KeyError, IndexError):
@@ -198,13 +293,14 @@ class Qwen(AsyncGeneratorProvider, ProviderModelMixin):
198 293 is_rate_limit = (isinstance(e, aiohttp.ClientResponseError) and e.status == 429) or \
199 294 ("RateLimited" in str(e))
200 295 if is_rate_limit:
201 debug.log(f"[Qwen] WARNING: Rate limit detected (attempt {attempt + 1}/5). Invalidating current midtoken.")
296 debug.log(
297 f"[Qwen] WARNING: Rate limit detected (attempt {attempt + 1}/5). Invalidating current midtoken.")
202 298 cls._midtoken = None
203 299 cls._midtoken_uses = 0
300 conversation = None
204 301 await asyncio.sleep(2)
205 302 continue
206 303 else:
207 304 raise e
208 305
209 306 raise RateLimitError("The Qwen provider reached the request limit after 5 attempts.")
210