webscout 8.3.4__py3-none-any.whl → 8.3.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of webscout might be problematic. Click here for more details.
- webscout/AIutel.py +52 -1016
- webscout/Provider/AISEARCH/__init__.py +11 -10
- webscout/Provider/AISEARCH/felo_search.py +7 -3
- webscout/Provider/AISEARCH/scira_search.py +2 -0
- webscout/Provider/AISEARCH/stellar_search.py +53 -8
- webscout/Provider/Deepinfra.py +7 -1
- webscout/Provider/OPENAI/TogetherAI.py +57 -48
- webscout/Provider/OPENAI/TwoAI.py +94 -1
- webscout/Provider/OPENAI/__init__.py +0 -2
- webscout/Provider/OPENAI/deepinfra.py +6 -0
- webscout/Provider/OPENAI/scirachat.py +4 -0
- webscout/Provider/OPENAI/textpollinations.py +11 -7
- webscout/Provider/OPENAI/venice.py +1 -0
- webscout/Provider/Perplexitylabs.py +163 -147
- webscout/Provider/Qodo.py +30 -6
- webscout/Provider/TTI/__init__.py +1 -0
- webscout/Provider/TTI/together.py +7 -6
- webscout/Provider/TTI/venice.py +368 -0
- webscout/Provider/TextPollinationsAI.py +11 -7
- webscout/Provider/TogetherAI.py +57 -44
- webscout/Provider/TwoAI.py +96 -2
- webscout/Provider/TypliAI.py +33 -27
- webscout/Provider/UNFINISHED/PERPLEXED_search.py +254 -0
- webscout/Provider/UNFINISHED/fetch_together_models.py +6 -11
- webscout/Provider/Venice.py +1 -0
- webscout/Provider/WiseCat.py +18 -20
- webscout/Provider/__init__.py +0 -6
- webscout/Provider/scira_chat.py +4 -0
- webscout/Provider/toolbaz.py +5 -10
- webscout/Provider/typefully.py +1 -11
- webscout/__init__.py +3 -15
- webscout/auth/__init__.py +19 -4
- webscout/auth/api_key_manager.py +189 -189
- webscout/auth/auth_system.py +25 -40
- webscout/auth/config.py +105 -6
- webscout/auth/database.py +377 -22
- webscout/auth/models.py +185 -130
- webscout/auth/request_processing.py +175 -11
- webscout/auth/routes.py +99 -2
- webscout/auth/server.py +9 -2
- webscout/auth/simple_logger.py +236 -0
- webscout/sanitize.py +1074 -0
- webscout/version.py +1 -1
- {webscout-8.3.4.dist-info → webscout-8.3.5.dist-info}/METADATA +9 -149
- {webscout-8.3.4.dist-info → webscout-8.3.5.dist-info}/RECORD +49 -51
- webscout/Provider/OPENAI/README_AUTOPROXY.md +0 -238
- webscout/Provider/OPENAI/typegpt.py +0 -368
- webscout/Provider/OPENAI/uncovrAI.py +0 -477
- webscout/Provider/WritingMate.py +0 -273
- webscout/Provider/typegpt.py +0 -284
- webscout/Provider/uncovr.py +0 -333
- {webscout-8.3.4.dist-info → webscout-8.3.5.dist-info}/WHEEL +0 -0
- {webscout-8.3.4.dist-info → webscout-8.3.5.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.4.dist-info → webscout-8.3.5.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.4.dist-info → webscout-8.3.5.dist-info}/top_level.txt +0 -0
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import ssl
|
|
2
1
|
import json
|
|
3
2
|
import time
|
|
4
|
-
import socket
|
|
5
3
|
import random
|
|
6
|
-
from threading import
|
|
4
|
+
from threading import Event
|
|
7
5
|
from curl_cffi import requests
|
|
8
|
-
from
|
|
9
|
-
from typing import Dict, Any, Union, Generator, List, Optional
|
|
6
|
+
from typing import Dict, Any, Union, Generator
|
|
10
7
|
|
|
11
8
|
from webscout.AIutel import Optimizers
|
|
12
9
|
from webscout.AIutel import Conversation
|
|
13
|
-
from webscout.AIutel import AwesomePrompts
|
|
10
|
+
from webscout.AIutel import AwesomePrompts
|
|
14
11
|
from webscout.AIbase import Provider
|
|
15
12
|
from webscout import exceptions
|
|
16
|
-
|
|
13
|
+
|
|
14
|
+
API_URL = "https://www.perplexity.ai/socket.io/"
|
|
17
15
|
|
|
18
16
|
class PerplexityLabs(Provider):
|
|
19
17
|
"""
|
|
@@ -70,26 +68,16 @@ class PerplexityLabs(Provider):
|
|
|
70
68
|
self.connected = Event()
|
|
71
69
|
self.last_answer = None
|
|
72
70
|
|
|
73
|
-
# Initialize session with headers
|
|
74
|
-
self.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
'dnt': '1',
|
|
80
|
-
'priority': 'u=0, i',
|
|
81
|
-
'sec-ch-ua': '"Not;A=Brand";v="24", "Chromium";v="128"',
|
|
82
|
-
'sec-ch-ua-mobile': '?0',
|
|
83
|
-
'sec-ch-ua-platform': '"Windows"',
|
|
84
|
-
'sec-fetch-dest': 'document',
|
|
85
|
-
'sec-fetch-mode': 'navigate',
|
|
86
|
-
'sec-fetch-site': 'same-origin',
|
|
87
|
-
'sec-fetch-user': '?1',
|
|
88
|
-
'upgrade-insecure-requests': '1',
|
|
89
|
-
})
|
|
71
|
+
# Initialize session with headers matching the working example
|
|
72
|
+
self.headers = {
|
|
73
|
+
"Origin": "https://labs.perplexity.ai",
|
|
74
|
+
"Referer": "https://labs.perplexity.ai/",
|
|
75
|
+
}
|
|
76
|
+
self.session = requests.Session(impersonate="chrome")
|
|
90
77
|
|
|
91
78
|
# Apply proxies if provided
|
|
92
|
-
|
|
79
|
+
if proxies:
|
|
80
|
+
self.session.proxies.update(proxies)
|
|
93
81
|
|
|
94
82
|
# Set up conversation handling
|
|
95
83
|
self.is_conversation = is_conversation
|
|
@@ -117,14 +105,14 @@ class PerplexityLabs(Provider):
|
|
|
117
105
|
self._initialize_connection()
|
|
118
106
|
|
|
119
107
|
def _initialize_connection(self) -> None:
|
|
120
|
-
"""Initialize the connection to Perplexity
|
|
108
|
+
"""Initialize the connection to Perplexity using polling approach"""
|
|
121
109
|
for attempt in range(1, self.max_retries + 1):
|
|
122
110
|
try:
|
|
123
111
|
# Get a session ID via polling
|
|
124
112
|
self.timestamp = format(random.getrandbits(32), '08x')
|
|
125
|
-
poll_url = f'
|
|
113
|
+
poll_url = f'{API_URL}?EIO=4&transport=polling&t={self.timestamp}'
|
|
126
114
|
|
|
127
|
-
response = self.session.get(poll_url)
|
|
115
|
+
response = self.session.get(poll_url, headers=self.headers)
|
|
128
116
|
if response.status_code != 200:
|
|
129
117
|
if attempt == self.max_retries:
|
|
130
118
|
raise ConnectionError(f"Failed to get session ID: HTTP {response.status_code}")
|
|
@@ -132,57 +120,35 @@ class PerplexityLabs(Provider):
|
|
|
132
120
|
|
|
133
121
|
# Extract the session ID
|
|
134
122
|
try:
|
|
135
|
-
|
|
123
|
+
text = response.text
|
|
124
|
+
if not text.startswith("0"):
|
|
125
|
+
raise ConnectionError("Invalid response format")
|
|
126
|
+
self.sid = json.loads(text[1:])['sid']
|
|
136
127
|
except (json.JSONDecodeError, KeyError) as e:
|
|
137
128
|
if attempt == self.max_retries:
|
|
138
129
|
raise ConnectionError(f"Failed to parse session ID: {e}")
|
|
139
130
|
continue
|
|
140
131
|
|
|
141
132
|
# Authenticate the session
|
|
142
|
-
auth_url = f'
|
|
143
|
-
|
|
133
|
+
self.auth_url = f'{API_URL}?EIO=4&transport=polling&t={self.timestamp}&sid={self.sid}'
|
|
134
|
+
post_data = '40{"jwt":"anonymous-ask-user"}'
|
|
135
|
+
auth_response = self.session.post(self.auth_url, data=post_data, headers=self.headers)
|
|
144
136
|
|
|
145
137
|
if auth_response.status_code != 200 or auth_response.text != 'OK':
|
|
146
138
|
if attempt == self.max_retries:
|
|
147
139
|
raise ConnectionError("Authentication failed")
|
|
148
140
|
continue
|
|
149
141
|
|
|
150
|
-
#
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
try:
|
|
154
|
-
self.sock = context.wrap_socket(
|
|
155
|
-
socket.create_connection(('www.perplexity.ai', 443), timeout=self.connection_timeout),
|
|
156
|
-
server_hostname='www.perplexity.ai'
|
|
157
|
-
)
|
|
158
|
-
except (socket.timeout, socket.error, ssl.SSLError) as e:
|
|
142
|
+
# Get additional response to complete handshake
|
|
143
|
+
get_response = self.session.get(self.auth_url, headers=self.headers)
|
|
144
|
+
if get_response.status_code != 200:
|
|
159
145
|
if attempt == self.max_retries:
|
|
160
|
-
raise ConnectionError(
|
|
146
|
+
raise ConnectionError("Failed to complete authentication handshake")
|
|
161
147
|
continue
|
|
162
148
|
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
self.connected.clear()
|
|
168
|
-
self.ws = WebSocketApp(
|
|
169
|
-
url=ws_url,
|
|
170
|
-
header={'User-Agent': self.session.headers['User-Agent']},
|
|
171
|
-
cookie=cookies,
|
|
172
|
-
on_open=self._on_open,
|
|
173
|
-
on_message=self._on_message,
|
|
174
|
-
on_error=self._on_error,
|
|
175
|
-
on_close=self._on_close,
|
|
176
|
-
socket=self.sock
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
# Start WebSocket in a thread
|
|
180
|
-
self.ws_thread = Thread(target=self.ws.run_forever, daemon=True)
|
|
181
|
-
self.ws_thread.start()
|
|
182
|
-
|
|
183
|
-
# Wait for connection to be established
|
|
184
|
-
if self.connected.wait(timeout=self.connection_timeout):
|
|
185
|
-
return
|
|
149
|
+
# Connection successful - using polling instead of WebSocket
|
|
150
|
+
self.connected.set()
|
|
151
|
+
return
|
|
186
152
|
|
|
187
153
|
except Exception as e:
|
|
188
154
|
if attempt == self.max_retries:
|
|
@@ -195,37 +161,107 @@ class PerplexityLabs(Provider):
|
|
|
195
161
|
|
|
196
162
|
raise exceptions.FailedToGenerateResponseError("Failed to connect to Perplexity after multiple attempts")
|
|
197
163
|
|
|
198
|
-
def
|
|
199
|
-
"""
|
|
200
|
-
|
|
201
|
-
|
|
164
|
+
def _send_query_polling(self, message_data):
|
|
165
|
+
"""Send query using polling approach"""
|
|
166
|
+
payload = '42' + json.dumps(["perplexity_labs", message_data])
|
|
167
|
+
response = self.session.post(self.auth_url, data=payload, headers=self.headers, timeout=10)
|
|
168
|
+
return response.status_code == 200
|
|
202
169
|
|
|
203
|
-
def
|
|
204
|
-
"""
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
170
|
+
def _poll_for_response(self, timeout_seconds):
|
|
171
|
+
"""Poll for response using the polling approach"""
|
|
172
|
+
start_time = time.time()
|
|
173
|
+
last_message = 0
|
|
174
|
+
full_output = ""
|
|
175
|
+
|
|
176
|
+
while True:
|
|
177
|
+
if time.time() - start_time > timeout_seconds:
|
|
178
|
+
if last_message == 0:
|
|
179
|
+
raise exceptions.FailedToGenerateResponseError("Response timed out")
|
|
180
|
+
else:
|
|
181
|
+
# Return partial response if we got some content
|
|
182
|
+
yield {"text": "", "final": True, "full_output": full_output}
|
|
183
|
+
return
|
|
217
184
|
|
|
218
|
-
elif message.startswith('42'):
|
|
219
185
|
try:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
186
|
+
poll_response = self.session.get(self.auth_url, headers=self.headers, timeout=3)
|
|
187
|
+
|
|
188
|
+
if poll_response.status_code == 400:
|
|
189
|
+
# Session expired, try to return what we have
|
|
190
|
+
if full_output:
|
|
191
|
+
yield {"text": "", "final": True, "full_output": full_output}
|
|
192
|
+
return
|
|
193
|
+
else:
|
|
194
|
+
raise exceptions.FailedToGenerateResponseError("Session expired")
|
|
195
|
+
|
|
196
|
+
if poll_response.status_code != 200:
|
|
197
|
+
time.sleep(0.5)
|
|
198
|
+
continue
|
|
199
|
+
|
|
200
|
+
response_text = poll_response.text
|
|
201
|
+
|
|
202
|
+
# Handle heartbeat
|
|
203
|
+
if response_text == '2':
|
|
204
|
+
try:
|
|
205
|
+
self.session.post(self.auth_url, data='3', headers=self.headers, timeout=3)
|
|
206
|
+
except:
|
|
207
|
+
pass
|
|
208
|
+
continue
|
|
209
|
+
|
|
210
|
+
# Handle data messages containing output
|
|
211
|
+
if '42[' in response_text and 'output' in response_text:
|
|
212
|
+
try:
|
|
213
|
+
# Find the JSON part more reliably
|
|
214
|
+
start = response_text.find('42[')
|
|
215
|
+
if start != -1:
|
|
216
|
+
# Find the end of this JSON message
|
|
217
|
+
bracket_count = 0
|
|
218
|
+
json_start = start + 2
|
|
219
|
+
json_end = json_start
|
|
220
|
+
|
|
221
|
+
for j, char in enumerate(response_text[json_start:]):
|
|
222
|
+
if char == '[':
|
|
223
|
+
bracket_count += 1
|
|
224
|
+
elif char == ']':
|
|
225
|
+
bracket_count -= 1
|
|
226
|
+
if bracket_count == 0:
|
|
227
|
+
json_end = json_start + j + 1
|
|
228
|
+
break
|
|
229
|
+
|
|
230
|
+
json_str = response_text[json_start:json_end]
|
|
231
|
+
parsed_data = json.loads(json_str)
|
|
232
|
+
|
|
233
|
+
if len(parsed_data) > 1 and isinstance(parsed_data[1], dict):
|
|
234
|
+
data = parsed_data[1]
|
|
235
|
+
|
|
236
|
+
# Handle error responses
|
|
237
|
+
if data.get("status") == "failed":
|
|
238
|
+
error_message = data.get("text", "Unknown API error")
|
|
239
|
+
raise exceptions.FailedToGenerateResponseError(f"API Error: {error_message}")
|
|
240
|
+
|
|
241
|
+
# Handle normal responses
|
|
242
|
+
if "output" in data:
|
|
243
|
+
current_output = data["output"]
|
|
244
|
+
if len(current_output) > last_message:
|
|
245
|
+
delta = current_output[last_message:]
|
|
246
|
+
last_message = len(current_output)
|
|
247
|
+
full_output = current_output
|
|
248
|
+
yield {"text": delta, "final": data.get("final", False), "full_output": full_output}
|
|
249
|
+
|
|
250
|
+
if data.get("final", False):
|
|
251
|
+
return
|
|
252
|
+
|
|
253
|
+
except (json.JSONDecodeError, IndexError, KeyError) as e:
|
|
254
|
+
# Continue on parsing errors
|
|
255
|
+
pass
|
|
256
|
+
|
|
257
|
+
except Exception as e:
|
|
258
|
+
# Handle timeout and other errors more gracefully
|
|
259
|
+
if "timeout" in str(e).lower():
|
|
260
|
+
continue
|
|
261
|
+
time.sleep(0.5)
|
|
262
|
+
continue
|
|
263
|
+
|
|
264
|
+
time.sleep(0.5)
|
|
229
265
|
|
|
230
266
|
def ask(
|
|
231
267
|
self,
|
|
@@ -270,67 +306,47 @@ class PerplexityLabs(Provider):
|
|
|
270
306
|
else:
|
|
271
307
|
raise Exception(f"Optimizer is not one of {self.__available_optimizers}")
|
|
272
308
|
|
|
273
|
-
|
|
309
|
+
# Send the query using polling approach
|
|
310
|
+
message_data = {
|
|
311
|
+
"version": "2.18",
|
|
312
|
+
"source": "default",
|
|
313
|
+
"model": use_model,
|
|
314
|
+
"messages": [{"role": "user", "content": conversation_prompt}],
|
|
315
|
+
}
|
|
274
316
|
|
|
275
|
-
# Send
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
{
|
|
279
|
-
'messages': [{'role': 'user', 'content': conversation_prompt}],
|
|
280
|
-
'model': use_model,
|
|
281
|
-
'source': 'default',
|
|
282
|
-
'version': '2.18',
|
|
283
|
-
}
|
|
284
|
-
])
|
|
285
|
-
self.ws.send('42' + payload)
|
|
317
|
+
# Send query
|
|
318
|
+
if not self._send_query_polling(message_data):
|
|
319
|
+
raise exceptions.FailedToGenerateResponseError("Failed to send query")
|
|
286
320
|
|
|
287
321
|
def for_stream():
|
|
288
|
-
"""Handle streaming responses"""
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
# Check for timeout
|
|
295
|
-
if time.time() - start_time > self.timeout:
|
|
296
|
-
raise exceptions.FailedToGenerateResponseError("Response stream timed out")
|
|
297
|
-
|
|
298
|
-
# If we have a new response different from what we've seen
|
|
299
|
-
if self.last_answer != last_seen:
|
|
300
|
-
last_seen = self.last_answer
|
|
301
|
-
if last_seen is not None:
|
|
302
|
-
if 'output' in last_seen:
|
|
303
|
-
current_output = last_seen['output']
|
|
304
|
-
# For delta output in streaming
|
|
305
|
-
delta = current_output[len(streaming_text):]
|
|
306
|
-
streaming_text = current_output
|
|
307
|
-
resp = dict(text=delta)
|
|
308
|
-
yield resp if raw else resp
|
|
322
|
+
"""Handle streaming responses using polling"""
|
|
323
|
+
full_text = ""
|
|
324
|
+
for response_chunk in self._poll_for_response(self.timeout):
|
|
325
|
+
if response_chunk["text"]:
|
|
326
|
+
full_text += response_chunk["text"]
|
|
327
|
+
yield dict(text=response_chunk["text"]) if raw else dict(text=response_chunk["text"])
|
|
309
328
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
answer = self.last_answer
|
|
313
|
-
self.conversation.update_chat_history(prompt, streaming_text)
|
|
329
|
+
if response_chunk["final"]:
|
|
330
|
+
self.conversation.update_chat_history(prompt, full_text)
|
|
314
331
|
return
|
|
315
|
-
|
|
316
|
-
time.sleep(0.01)
|
|
317
332
|
|
|
318
333
|
def for_non_stream():
|
|
319
|
-
"""Handle non-streaming responses"""
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if self.last_answer and self.last_answer.get('final', False):
|
|
325
|
-
answer = self.last_answer
|
|
326
|
-
self.conversation.update_chat_history(prompt, answer['output'])
|
|
327
|
-
return answer if raw else dict(text=answer['output'])
|
|
334
|
+
"""Handle non-streaming responses using polling"""
|
|
335
|
+
full_text = ""
|
|
336
|
+
for response_chunk in self._poll_for_response(self.timeout):
|
|
337
|
+
if response_chunk["text"]:
|
|
338
|
+
full_text += response_chunk["text"]
|
|
328
339
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
340
|
+
if response_chunk["final"]:
|
|
341
|
+
self.conversation.update_chat_history(prompt, full_text)
|
|
342
|
+
return dict(text=full_text) if raw else dict(text=full_text)
|
|
343
|
+
|
|
344
|
+
# If we get here, no final response was received
|
|
345
|
+
if full_text:
|
|
346
|
+
self.conversation.update_chat_history(prompt, full_text)
|
|
347
|
+
return dict(text=full_text) if raw else dict(text=full_text)
|
|
348
|
+
else:
|
|
349
|
+
raise exceptions.FailedToGenerateResponseError("No response received")
|
|
334
350
|
|
|
335
351
|
return for_stream() if stream else for_non_stream()
|
|
336
352
|
|
|
@@ -396,7 +412,7 @@ if __name__ == "__main__":
|
|
|
396
412
|
|
|
397
413
|
for model in PerplexityLabs.AVAILABLE_MODELS:
|
|
398
414
|
try:
|
|
399
|
-
test_ai = PerplexityLabs(model=model, timeout=
|
|
415
|
+
test_ai = PerplexityLabs(model=model, timeout=30, connection_timeout=5.0)
|
|
400
416
|
response = test_ai.chat("Say 'Hello' in one word", stream=True)
|
|
401
417
|
response_text = ""
|
|
402
418
|
for chunk in response:
|
|
@@ -412,4 +428,4 @@ if __name__ == "__main__":
|
|
|
412
428
|
display_text = "Empty or invalid response"
|
|
413
429
|
print(f"\r{model:<50} {status:<10} {display_text}")
|
|
414
430
|
except Exception as e:
|
|
415
|
-
print(f"\r{model:<50} {'✗':<10} {str(e)}")
|
|
431
|
+
print(f"\r{model:<50} {'✗':<10} {str(e)[:80]}")
|
webscout/Provider/Qodo.py
CHANGED
|
@@ -23,6 +23,7 @@ class QodoAI(Provider):
|
|
|
23
23
|
"o4-mini",
|
|
24
24
|
"claude-4-sonnet",
|
|
25
25
|
"gemini-2.5-pro",
|
|
26
|
+
"grok-4"
|
|
26
27
|
|
|
27
28
|
]
|
|
28
29
|
|
|
@@ -225,15 +226,27 @@ class QodoAI(Provider):
|
|
|
225
226
|
payload = self._build_payload(conversation_prompt)
|
|
226
227
|
payload["stream"] = stream
|
|
227
228
|
|
|
229
|
+
|
|
228
230
|
def for_stream():
|
|
229
231
|
try:
|
|
230
232
|
response = self.session.post(
|
|
231
|
-
self.url,
|
|
232
|
-
json=payload,
|
|
233
|
-
stream=True,
|
|
233
|
+
self.url,
|
|
234
|
+
json=payload,
|
|
235
|
+
stream=True,
|
|
234
236
|
timeout=self.timeout,
|
|
235
237
|
impersonate=self.fingerprint.get("browser_type", "chrome110")
|
|
236
238
|
)
|
|
239
|
+
# Check for internal server error with session ID in the response
|
|
240
|
+
if response.status_code == 500 and response.text and "Internal server error, session ID:" in response.text:
|
|
241
|
+
# Switch to continue-task endpoint and retry
|
|
242
|
+
self.url = "https://api.cli.qodo.ai/v2/agentic/continue-task"
|
|
243
|
+
response = self.session.post(
|
|
244
|
+
self.url,
|
|
245
|
+
json=payload,
|
|
246
|
+
stream=True,
|
|
247
|
+
timeout=self.timeout,
|
|
248
|
+
impersonate=self.fingerprint.get("browser_type", "chrome110")
|
|
249
|
+
)
|
|
237
250
|
if response.status_code == 401:
|
|
238
251
|
raise exceptions.FailedToGenerateResponseError(
|
|
239
252
|
"Invalid API key. You need to provide your own API key.\n"
|
|
@@ -270,11 +283,20 @@ class QodoAI(Provider):
|
|
|
270
283
|
try:
|
|
271
284
|
payload["stream"] = False
|
|
272
285
|
response = self.session.post(
|
|
273
|
-
self.url,
|
|
274
|
-
json=payload,
|
|
286
|
+
self.url,
|
|
287
|
+
json=payload,
|
|
275
288
|
timeout=self.timeout,
|
|
276
289
|
impersonate=self.fingerprint.get("browser_type", "chrome110")
|
|
277
290
|
)
|
|
291
|
+
# Check for internal server error with session ID in the response
|
|
292
|
+
if response.status_code == 500 and response.text and "Internal server error, session ID:" in response.text:
|
|
293
|
+
self.url = "https://api.cli.qodo.ai/v2/agentic/continue-task"
|
|
294
|
+
response = self.session.post(
|
|
295
|
+
self.url,
|
|
296
|
+
json=payload,
|
|
297
|
+
timeout=self.timeout,
|
|
298
|
+
impersonate=self.fingerprint.get("browser_type", "chrome110")
|
|
299
|
+
)
|
|
278
300
|
if response.status_code == 401:
|
|
279
301
|
raise exceptions.FailedToGenerateResponseError(
|
|
280
302
|
"Invalid API key. You need to provide your own API key.\n"
|
|
@@ -386,7 +408,9 @@ class QodoAI(Provider):
|
|
|
386
408
|
)
|
|
387
409
|
|
|
388
410
|
# Fallback to generated session ID if API call fails
|
|
389
|
-
|
|
411
|
+
from datetime import datetime
|
|
412
|
+
today = datetime.now().strftime("%Y%m%d")
|
|
413
|
+
return f"{today}-{str(uuid.uuid4())}"
|
|
390
414
|
|
|
391
415
|
except exceptions.FailedToGenerateResponseError:
|
|
392
416
|
# Re-raise our custom exceptions
|
|
@@ -208,17 +208,18 @@ class TogetherImage(TTICompatibleProvider):
|
|
|
208
208
|
|
|
209
209
|
# Image models from Together.xyz API (filtered for image type only)
|
|
210
210
|
AVAILABLE_MODELS = [
|
|
211
|
-
"black-forest-labs/FLUX.1-schnell-Free",
|
|
212
|
-
"black-forest-labs/FLUX.1.1-pro",
|
|
213
211
|
"black-forest-labs/FLUX.1-pro",
|
|
212
|
+
"black-forest-labs/FLUX.1.1-pro",
|
|
214
213
|
"black-forest-labs/FLUX.1-redux",
|
|
215
|
-
"black-forest-labs/FLUX.1-depth",
|
|
216
|
-
"black-forest-labs/FLUX.1-canny",
|
|
217
|
-
"black-forest-labs/FLUX.1-kontext-max",
|
|
218
214
|
"black-forest-labs/FLUX.1-dev-lora",
|
|
219
215
|
"black-forest-labs/FLUX.1-schnell",
|
|
216
|
+
"black-forest-labs/FLUX.1-depth",
|
|
217
|
+
"black-forest-labs/FLUX.1-kontext-dev",
|
|
220
218
|
"black-forest-labs/FLUX.1-dev",
|
|
221
|
-
"black-forest-labs/FLUX.1-
|
|
219
|
+
"black-forest-labs/FLUX.1-canny",
|
|
220
|
+
"black-forest-labs/FLUX.1-kontext-max",
|
|
221
|
+
"black-forest-labs/FLUX.1-schnell-Free",
|
|
222
|
+
"black-forest-labs/FLUX.1-kontext-pro"
|
|
222
223
|
]
|
|
223
224
|
|
|
224
225
|
def __init__(self):
|