返回提交历史
Modified
g4f/Provider/Blackbox.py
+221
-99
XFEstudio/gpt4free
feat(g4f/Provider/Blackbox.py): enhance async response handling and cleanup
4e630d29
代码差异
1 个文件
+221
-99
@@ -1,19 +1,27 @@
1
1
from __future__ import annotations
2
2
3
import re
3
import asyncio
4
import aiohttp
4
5
import random
5
6
import string
6
7
import json
7
from aiohttp import ClientSession
8
import uuid
9
import re
10
from typing import Optional, AsyncGenerator, Union
11
12
from aiohttp import ClientSession, ClientResponseError
8
13
9
from ..typing import AsyncResult, Messages, ImageType
10
from ..image import ImageResponse, to_data_uri
14
from ..typing import AsyncResult, Messages
11
15
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
16
from ..image import ImageResponse
17
12
18
13
19
class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
20
label = "Blackbox AI"
14
21
url = "https://www.blackbox.ai"
15
22
api_endpoint = "https://www.blackbox.ai/api/chat"
16
23
working = True
24
supports_gpt_4 = True
17
25
supports_stream = True
18
26
supports_system_message = True
19
27
supports_message_history = True
@@ -23,6 +31,7 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
23
31
models = [
24
32
default_model,
25
33
'blackboxai-pro',
34
*image_models,
26
35
"llama-3.1-8b",
27
36
'llama-3.1-70b',
28
37
'llama-3.1-405b',
@@ -43,7 +52,6 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
43
52
'ReactAgent',
44
53
'XcodeAgent',
45
54
'AngularJSAgent',
46
*image_models,
47
55
]
48
56
49
57
agentMode = {
@@ -71,13 +79,13 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
71
79
'XcodeAgent': {'mode': True, 'id': "Xcode Agent"},
72
80
'AngularJSAgent': {'mode': True, 'id': "AngularJS Agent"},
73
81
}
74
82
75
83
userSelectedModel = {
76
84
"gpt-4o": "gpt-4o",
77
85
"gemini-pro": "gemini-pro",
78
86
'claude-sonnet-3.5': "claude-sonnet-3.5",
79
87
}
80
88
81
89
model_prefixes = {
82
90
'gpt-4o': '@GPT-4o',
83
91
'gemini-pro': '@Gemini-PRO',
@@ -98,14 +106,14 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
98
106
'blackboxai-pro': '@BLACKBOXAI-PRO',
99
107
'ImageGeneration': '@Image Generation',
100
108
}
101
109
102
110
model_referers = {
103
"blackboxai": f"{url}/?model=blackboxai",
104
"gpt-4o": f"{url}/?model=gpt-4o",
105
"gemini-pro": f"{url}/?model=gemini-pro",
106
"claude-sonnet-3.5": f"{url}/?model=claude-sonnet-3.5"
111
"blackboxai": "/?model=blackboxai",
112
"gpt-4o": "/?model=gpt-4o",
113
"gemini-pro": "/?model=gemini-pro",
114
"claude-sonnet-3.5": "/?model=claude-sonnet-3.5"
107
115
}
108
116
109
117
model_aliases = {
110
118
"gemini-flash": "gemini-1.5-flash",
111
119
"claude-3.5-sonnet": "claude-sonnet-3.5",
@@ -116,69 +124,131 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
116
124
def get_model(cls, model: str) -> str:
117
125
if model in cls.models:
118
126
return model
119
elif model in cls.userSelectedModel:
120
return model
121
127
elif model in cls.model_aliases:
122
128
return cls.model_aliases[model]
123
129
else:
124
130
return cls.default_model
125
131
132
@staticmethod
133
def generate_random_string(length: int = 7) -> str:
134
characters = string.ascii_letters + string.digits
135
return ''.join(random.choices(characters, k=length))
136
137
@staticmethod
138
def generate_next_action() -> str:
139
return uuid.uuid4().hex
140
141
@staticmethod
142
def generate_next_router_state_tree() -> str:
143
router_state = [
144
"",
145
{
146
"children": [
147
"(chat)",
148
{
149
"children": [
150
"__PAGE__",
151
{}
152
]
153
}
154
]
155
},
156
None,
157
None,
158
True
159
]
160
return json.dumps(router_state)
161
162
@staticmethod
163
def clean_response(text: str) -> str:
164
pattern = r'^\$\@\$v=undefined-rv1\$\@\$'
165
cleaned_text = re.sub(pattern, '', text)
166
return cleaned_text
167
126
168
@classmethod
127
169
async def create_async_generator(
128
170
cls,
129
171
model: str,
130
172
messages: Messages,
131
proxy: str = None,
132
image: ImageType = None,
133
image_name: str = None,
134
webSearchMode: bool = False,
173
proxy: Optional[str] = None,
174
web_search_mode: bool = False,
135
175
**kwargs
136
) -> AsyncResult:
176
) -> AsyncGenerator[Union[str, ImageResponse], None]:
177
"""
178
Creates an asynchronous generator for streaming responses from Blackbox AI.
179
180
Parameters:
181
model (str): Model to use for generating responses.
182
messages (Messages): Message history.
183
proxy (Optional[str]): Proxy URL, if needed.
184
web_search_mode (bool): Enables or disables web search mode.
185
**kwargs: Additional keyword arguments.
186
187
Yields:
188
Union[str, ImageResponse]: Segments of the generated response or ImageResponse objects.
189
"""
137
190
model = cls.get_model(model)
138
139
headers = {
140
"accept": "*/*",
141
"accept-language": "en-US,en;q=0.9",
142
"cache-control": "no-cache",
143
"content-type": "application/json",
144
"origin": cls.url,
145
"pragma": "no-cache",
146
"priority": "u=1, i",
147
"referer": cls.model_referers.get(model, cls.url),
148
"sec-ch-ua": '"Chromium";v="129", "Not=A?Brand";v="8"',
149
"sec-ch-ua-mobile": "?0",
150
"sec-ch-ua-platform": '"Linux"',
151
"sec-fetch-dest": "empty",
152
"sec-fetch-mode": "cors",
153
"sec-fetch-site": "same-origin",
154
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
155
}
156
191
157
if model in cls.model_prefixes:
158
prefix = cls.model_prefixes[model]
159
if not messages[0]['content'].startswith(prefix):
160
messages[0]['content'] = f"{prefix} {messages[0]['content']}"
192
chat_id = cls.generate_random_string()
193
next_action = cls.generate_next_action()
194
next_router_state_tree = cls.generate_next_router_state_tree()
195
196
agent_mode = cls.agentMode.get(model, {})
197
trending_agent_mode = cls.trendingAgentMode.get(model, {})
198
199
prefix = cls.model_prefixes.get(model, "")
161
200
162
random_id = ''.join(random.choices(string.ascii_letters + string.digits, k=7))
163
messages[-1]['id'] = random_id
164
messages[-1]['role'] = 'user'
165
166
if image is not None:
167
messages[-1]['data'] = {
168
'fileText': '',
169
'imageBase64': to_data_uri(image),
170
'title': image_name
171
}
172
messages[-1]['content'] = 'FILE:BB\n$#$\n\n$#$\n' + messages[-1]['content']
201
formatted_prompt = ""
202
for message in messages:
203
role = message.get('role', '').capitalize()
204
content = message.get('content', '')
205
if role and content:
206
formatted_prompt += f"{role}: {content}\n"
173
207
174
data = {
175
"messages": messages,
176
"id": random_id,
208
if prefix:
209
formatted_prompt = f"{prefix} {formatted_prompt}".strip()
210
211
referer_path = cls.model_referers.get(model, f"/?model={model}")
212
referer_url = f"{cls.url}{referer_path}"
213
214
common_headers = {
215
'accept': '*/*',
216
'accept-language': 'en-US,en;q=0.9',
217
'cache-control': 'no-cache',
218
'origin': cls.url,
219
'pragma': 'no-cache',
220
'priority': 'u=1, i',
221
'sec-ch-ua': '"Chromium";v="129", "Not=A?Brand";v="8"',
222
'sec-ch-ua-mobile': '?0',
223
'sec-ch-ua-platform': '"Linux"',
224
'sec-fetch-dest': 'empty',
225
'sec-fetch-mode': 'cors',
226
'sec-fetch-site': 'same-origin',
227
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) '
228
'AppleWebKit/537.36 (KHTML, like Gecko) '
229
'Chrome/129.0.0.0 Safari/537.36'
230
}
231
232
headers_api_chat = {
233
'Content-Type': 'application/json',
234
'Referer': referer_url
235
}
236
headers_api_chat_combined = {**common_headers, **headers_api_chat}
237
238
payload_api_chat = {
239
"messages": [
240
{
241
"id": chat_id,
242
"content": formatted_prompt,
243
"role": "user"
244
}
245
],
246
"id": chat_id,
177
247
"previewToken": None,
178
248
"userId": None,
179
249
"codeModelMode": True,
180
"agentMode": {},
181
"trendingAgentMode": {},
250
"agentMode": agent_mode,
251
"trendingAgentMode": trending_agent_mode,
182
252
"isMicMode": False,
183
253
"userSystemPrompt": None,
184
254
"maxTokens": 1024,
@@ -191,47 +261,99 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
191
261
"clickedForceWebSearch": False,
192
262
"visitFromDelta": False,
193
263
"mobileClient": False,
194
"userSelectedModel": None,
195
"webSearchMode": webSearchMode,
264
"webSearchMode": web_search_mode,
265
"userSelectedModel": cls.userSelectedModel.get(model, model)
196
266
}
197
267
198
if model in cls.agentMode:
199
data["agentMode"] = cls.agentMode[model]
200
elif model in cls.trendingAgentMode:
201
data["trendingAgentMode"] = cls.trendingAgentMode[model]
202
elif model in cls.userSelectedModel:
203
data["userSelectedModel"] = cls.userSelectedModel[model]
204
205
async with ClientSession(headers=headers) as session:
206
async with session.post(cls.api_endpoint, json=data, proxy=proxy) as response:
207
response.raise_for_status()
208
if model == 'ImageGeneration':
209
response_text = await response.text()
210
url_match = re.search(r'https://storage\.googleapis\.com/[^\s\)]+', response_text)
211
if url_match:
212
image_url = url_match.group(0)
213
yield ImageResponse(image_url, alt=messages[-1]['content'])
214
else:
215
raise Exception("Image URL not found in the response")
216
else:
217
full_response = ""
218
search_results_json = ""
219
async for chunk, _ in response.content.iter_chunks():
220
if chunk:
221
decoded_chunk = chunk.decode()
222
decoded_chunk = re.sub(r'\$@\$v=[^$]+\$@\$', '', decoded_chunk)
223
if decoded_chunk.strip():
224
if '$~~~$' in decoded_chunk:
225
search_results_json += decoded_chunk
226
else:
227
full_response += decoded_chunk
228
yield decoded_chunk
229
230
if data["webSearchMode"] and search_results_json:
231
match = re.search(r'\$~~~\$(.*?)\$~~~\$', search_results_json, re.DOTALL)
268
headers_chat = {
269
'Accept': 'text/x-component',
270
'Content-Type': 'text/plain;charset=UTF-8',
271
'Referer': f'{cls.url}/chat/{chat_id}?model={model}',
272
'next-action': next_action,
273
'next-router-state-tree': next_router_state_tree,
274
'next-url': '/'
275
}
276
headers_chat_combined = {**common_headers, **headers_chat}
277
278
data_chat = '[]'
279
280
async with ClientSession(headers=common_headers) as session:
281
try:
282
async with session.post(
283
cls.api_endpoint,
284
headers=headers_api_chat_combined,
285
json=payload_api_chat,
286
proxy=proxy
287
) as response_api_chat:
288
response_api_chat.raise_for_status()
289
text = await response_api_chat.text()
290
cleaned_response = cls.clean_response(text)
291
292
if model in cls.image_models:
293
match = re.search(r'!\[.*?\]\((https?://[^\)]+)\)', cleaned_response)
232
294
if match:
233
search_results = json.loads(match.group(1))
234
formatted_results = "\n\n**Sources:**\n"
235
for i, result in enumerate(search_results[:5], 1):
236
formatted_results += f"{i}. [{result['title']}]({result['link']})\n"
237
yield formatted_results
295
image_url = match.group(1)
296
image_response = ImageResponse(images=image_url, alt="Generated Image")
297
yield image_response
298
else:
299
yield cleaned_response
300
else:
301
if web_search_mode:
302
match = re.search(r'\$~~~\$(.*?)\$~~~\$', cleaned_response, re.DOTALL)
303
if match:
304
source_part = match.group(1).strip()
305
answer_part = cleaned_response[match.end():].strip()
306
try:
307
sources = json.loads(source_part)
308
source_formatted = "**Source:**\n"
309
for item in sources:
310
title = item.get('title', 'No Title')
311
link = item.get('link', '#')
312
position = item.get('position', '')
313
source_formatted += f"{position}. [{title}]({link})\n"
314
final_response = f"{answer_part}\n\n{source_formatted}"
315
except json.JSONDecodeError:
316
final_response = f"{answer_part}\n\nSource information is unavailable."
317
else:
318
final_response = cleaned_response
319
else:
320
if '$~~~$' in cleaned_response:
321
final_response = cleaned_response.split('$~~~$')[0].strip()
322
else:
323
final_response = cleaned_response
324
325
yield final_response
326
except ClientResponseError as e:
327
error_text = f"Error {e.status}: {e.message}"
328
try:
329
error_response = await e.response.text()
330
cleaned_error = cls.clean_response(error_response)
331
error_text += f" - {cleaned_error}"
332
except Exception:
333
pass
334
yield error_text
335
except Exception as e:
336
yield f"Unexpected error during /api/chat request: {str(e)}"
337
338
chat_url = f'{cls.url}/chat/{chat_id}?model={model}'
339
340
try:
341
async with session.post(
342
chat_url,
343
headers=headers_chat_combined,
344
data=data_chat,
345
proxy=proxy
346
) as response_chat:
347
response_chat.raise_for_status()
348
pass
349
except ClientResponseError as e:
350
error_text = f"Error {e.status}: {e.message}"
351
try:
352
error_response = await e.response.text()
353
cleaned_error = cls.clean_response(error_response)
354
error_text += f" - {cleaned_error}"
355
except Exception:
356
pass
357
yield error_text
358
except Exception as e:
359
yield f"Unexpected error during /chat/{chat_id} request: {str(e)}"