返回提交历史
Modified
g4f/Provider/Qwen.py
+137
-41
Modified
g4f/Provider/needs_auth/LMArena.py
+406
-357
XFEstudio/gpt4free
Qwen add media
bfc7707c
代码差异
2 个文件
+543
-398
@@ -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