webscout 8.3.1__py3-none-any.whl → 8.3.3__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 +180 -78
- webscout/Bing_search.py +417 -0
- webscout/Extra/gguf.py +706 -177
- webscout/Provider/AISEARCH/__init__.py +1 -0
- webscout/Provider/AISEARCH/genspark_search.py +7 -7
- webscout/Provider/AISEARCH/stellar_search.py +132 -0
- webscout/Provider/ExaChat.py +84 -58
- webscout/Provider/GeminiProxy.py +140 -0
- webscout/Provider/HeckAI.py +85 -80
- webscout/Provider/Jadve.py +56 -50
- webscout/Provider/MCPCore.py +78 -75
- webscout/Provider/MiniMax.py +207 -0
- webscout/Provider/Nemotron.py +41 -13
- webscout/Provider/Netwrck.py +34 -51
- webscout/Provider/OPENAI/BLACKBOXAI.py +0 -4
- webscout/Provider/OPENAI/GeminiProxy.py +328 -0
- webscout/Provider/OPENAI/MiniMax.py +298 -0
- webscout/Provider/OPENAI/README.md +32 -29
- webscout/Provider/OPENAI/README_AUTOPROXY.md +238 -0
- webscout/Provider/OPENAI/TogetherAI.py +4 -17
- webscout/Provider/OPENAI/__init__.py +17 -1
- webscout/Provider/OPENAI/autoproxy.py +1067 -39
- webscout/Provider/OPENAI/base.py +17 -76
- webscout/Provider/OPENAI/deepinfra.py +42 -108
- webscout/Provider/OPENAI/e2b.py +0 -1
- webscout/Provider/OPENAI/flowith.py +179 -166
- webscout/Provider/OPENAI/friendli.py +233 -0
- webscout/Provider/OPENAI/mcpcore.py +109 -70
- webscout/Provider/OPENAI/monochat.py +329 -0
- webscout/Provider/OPENAI/pydantic_imports.py +1 -172
- webscout/Provider/OPENAI/scirachat.py +59 -51
- webscout/Provider/OPENAI/toolbaz.py +3 -9
- webscout/Provider/OPENAI/typegpt.py +1 -1
- webscout/Provider/OPENAI/utils.py +19 -42
- webscout/Provider/OPENAI/x0gpt.py +14 -2
- webscout/Provider/OPENAI/xenai.py +514 -0
- webscout/Provider/OPENAI/yep.py +8 -2
- webscout/Provider/OpenGPT.py +54 -32
- webscout/Provider/PI.py +58 -84
- webscout/Provider/StandardInput.py +32 -13
- webscout/Provider/TTI/README.md +9 -9
- webscout/Provider/TTI/__init__.py +3 -1
- webscout/Provider/TTI/aiarta.py +92 -78
- webscout/Provider/TTI/bing.py +231 -0
- webscout/Provider/TTI/infip.py +212 -0
- webscout/Provider/TTI/monochat.py +220 -0
- webscout/Provider/TTS/speechma.py +45 -39
- webscout/Provider/TeachAnything.py +11 -3
- webscout/Provider/TextPollinationsAI.py +78 -70
- webscout/Provider/TogetherAI.py +350 -0
- webscout/Provider/Venice.py +37 -46
- webscout/Provider/VercelAI.py +27 -24
- webscout/Provider/WiseCat.py +35 -35
- webscout/Provider/WrDoChat.py +22 -26
- webscout/Provider/WritingMate.py +26 -22
- webscout/Provider/XenAI.py +324 -0
- webscout/Provider/__init__.py +10 -5
- webscout/Provider/deepseek_assistant.py +378 -0
- webscout/Provider/granite.py +48 -57
- webscout/Provider/koala.py +51 -39
- webscout/Provider/learnfastai.py +49 -64
- webscout/Provider/llmchat.py +79 -93
- webscout/Provider/llmchatco.py +63 -78
- webscout/Provider/multichat.py +51 -40
- webscout/Provider/oivscode.py +1 -1
- webscout/Provider/scira_chat.py +159 -96
- webscout/Provider/scnet.py +13 -13
- webscout/Provider/searchchat.py +13 -13
- webscout/Provider/sonus.py +12 -11
- webscout/Provider/toolbaz.py +25 -8
- webscout/Provider/turboseek.py +41 -42
- webscout/Provider/typefully.py +27 -12
- webscout/Provider/typegpt.py +41 -46
- webscout/Provider/uncovr.py +55 -90
- webscout/Provider/x0gpt.py +33 -17
- webscout/Provider/yep.py +79 -96
- webscout/auth/__init__.py +55 -0
- webscout/auth/api_key_manager.py +189 -0
- webscout/auth/auth_system.py +100 -0
- webscout/auth/config.py +76 -0
- webscout/auth/database.py +400 -0
- webscout/auth/exceptions.py +67 -0
- webscout/auth/middleware.py +248 -0
- webscout/auth/models.py +130 -0
- webscout/auth/providers.py +279 -0
- webscout/auth/rate_limiter.py +254 -0
- webscout/auth/request_models.py +127 -0
- webscout/auth/request_processing.py +226 -0
- webscout/auth/routes.py +550 -0
- webscout/auth/schemas.py +103 -0
- webscout/auth/server.py +367 -0
- webscout/client.py +121 -70
- webscout/litagent/Readme.md +68 -55
- webscout/litagent/agent.py +99 -9
- webscout/scout/core/scout.py +104 -26
- webscout/scout/element.py +139 -18
- webscout/swiftcli/core/cli.py +14 -3
- webscout/swiftcli/decorators/output.py +59 -9
- webscout/update_checker.py +31 -49
- webscout/version.py +1 -1
- webscout/webscout_search.py +4 -12
- webscout/webscout_search_async.py +3 -10
- webscout/yep_search.py +2 -11
- {webscout-8.3.1.dist-info → webscout-8.3.3.dist-info}/METADATA +141 -99
- {webscout-8.3.1.dist-info → webscout-8.3.3.dist-info}/RECORD +109 -83
- {webscout-8.3.1.dist-info → webscout-8.3.3.dist-info}/entry_points.txt +1 -1
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/OPENAI/api.py +0 -1320
- webscout/Provider/TTI/fastflux.py +0 -233
- webscout/Provider/Writecream.py +0 -246
- {webscout-8.3.1.dist-info → webscout-8.3.3.dist-info}/WHEEL +0 -0
- {webscout-8.3.1.dist-info → webscout-8.3.3.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.1.dist-info → webscout-8.3.3.dist-info}/top_level.txt +0 -0
webscout/Provider/koala.py
CHANGED
|
@@ -3,7 +3,7 @@ import re
|
|
|
3
3
|
from typing import Optional, Union, Any, Dict, Generator
|
|
4
4
|
from uuid import uuid4
|
|
5
5
|
|
|
6
|
-
from webscout.AIutel import Optimizers, Conversation, AwesomePrompts
|
|
6
|
+
from webscout.AIutel import Optimizers, Conversation, AwesomePrompts, sanitize_stream
|
|
7
7
|
from webscout.AIbase import Provider
|
|
8
8
|
from webscout import exceptions
|
|
9
9
|
|
|
@@ -79,7 +79,7 @@ class KOALA(Provider):
|
|
|
79
79
|
raw: bool = False,
|
|
80
80
|
optimizer: str = None,
|
|
81
81
|
conversationally: bool = False,
|
|
82
|
-
) -> Dict[str, Any]:
|
|
82
|
+
) -> Union[Dict[str, Any], Generator[Any, None, None]]:
|
|
83
83
|
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
|
|
84
84
|
if optimizer:
|
|
85
85
|
if hasattr(Optimizers, optimizer):
|
|
@@ -94,68 +94,80 @@ class KOALA(Provider):
|
|
|
94
94
|
"outputHistory": [],
|
|
95
95
|
"model": self.model
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
response = self.session.post(
|
|
98
|
+
self.api_endpoint, json=payload, headers=self.headers, stream=True, timeout=self.timeout
|
|
99
|
+
)
|
|
100
|
+
if not response.ok:
|
|
101
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
102
|
+
f"Failed to generate response - ({response.status_code}, {response.reason})"
|
|
100
103
|
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
# Use sanitize_stream with content_extractor and intro_value like YEPCHAT/X0GPT
|
|
105
|
+
processed_stream = sanitize_stream(
|
|
106
|
+
data=response.iter_lines(decode_unicode=True),
|
|
107
|
+
intro_value="data:",
|
|
108
|
+
to_json=False,
|
|
109
|
+
content_extractor=self._koala_extractor,
|
|
110
|
+
raw=raw
|
|
111
|
+
)
|
|
112
|
+
if stream:
|
|
105
113
|
streaming_response = ""
|
|
106
|
-
for
|
|
107
|
-
if
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if
|
|
113
|
-
streaming_response +=
|
|
114
|
-
yield dict(text=
|
|
115
|
-
# Only update chat history if response is not empty
|
|
114
|
+
for content_chunk in processed_stream:
|
|
115
|
+
if raw:
|
|
116
|
+
if content_chunk and isinstance(content_chunk, str) and content_chunk.strip():
|
|
117
|
+
streaming_response += content_chunk
|
|
118
|
+
yield content_chunk
|
|
119
|
+
else:
|
|
120
|
+
if content_chunk and isinstance(content_chunk, str) and content_chunk.strip():
|
|
121
|
+
streaming_response += content_chunk
|
|
122
|
+
yield dict(text=content_chunk)
|
|
116
123
|
if streaming_response.strip():
|
|
117
124
|
self.last_response = dict(text=streaming_response)
|
|
118
125
|
self.conversation.update_chat_history(
|
|
119
126
|
prompt, self.get_message(self.last_response)
|
|
120
127
|
)
|
|
121
|
-
|
|
122
|
-
# Use streaming logic to collect the full response
|
|
128
|
+
else:
|
|
123
129
|
full_text = ""
|
|
124
|
-
for
|
|
125
|
-
if
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
for content_chunk in processed_stream:
|
|
131
|
+
if raw:
|
|
132
|
+
if content_chunk and isinstance(content_chunk, str):
|
|
133
|
+
full_text += content_chunk
|
|
134
|
+
else:
|
|
135
|
+
if content_chunk and isinstance(content_chunk, str):
|
|
136
|
+
full_text += content_chunk
|
|
130
137
|
if full_text.strip():
|
|
131
138
|
self.last_response = dict(text=full_text)
|
|
132
139
|
self.conversation.update_chat_history(
|
|
133
140
|
prompt, self.get_message(self.last_response)
|
|
134
141
|
)
|
|
135
142
|
return self.last_response
|
|
136
|
-
return for_stream() if stream else for_non_stream()
|
|
137
143
|
|
|
138
144
|
def chat(
|
|
139
145
|
self,
|
|
140
146
|
prompt: str,
|
|
141
147
|
stream: bool = False,
|
|
148
|
+
raw: bool = False,
|
|
142
149
|
optimizer: str = None,
|
|
143
150
|
conversationally: bool = False,
|
|
144
151
|
) -> Union[str, Generator[str, None, None]]:
|
|
145
152
|
def for_stream():
|
|
146
153
|
for response in self.ask(
|
|
147
|
-
prompt, True, optimizer=optimizer, conversationally=conversationally
|
|
154
|
+
prompt, True, raw=raw, optimizer=optimizer, conversationally=conversationally
|
|
148
155
|
):
|
|
149
|
-
|
|
156
|
+
if raw:
|
|
157
|
+
yield response
|
|
158
|
+
else:
|
|
159
|
+
yield self.get_message(response)
|
|
150
160
|
def for_non_stream():
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
)
|
|
161
|
+
result = self.ask(
|
|
162
|
+
prompt,
|
|
163
|
+
False,
|
|
164
|
+
raw=raw,
|
|
165
|
+
optimizer=optimizer,
|
|
166
|
+
conversationally=conversationally,
|
|
158
167
|
)
|
|
168
|
+
if raw:
|
|
169
|
+
return result.get("text", "") if isinstance(result, dict) else str(result)
|
|
170
|
+
return self.get_message(result)
|
|
159
171
|
return for_stream() if stream else for_non_stream()
|
|
160
172
|
|
|
161
173
|
def get_message(self, response: dict) -> str:
|
|
@@ -165,6 +177,6 @@ class KOALA(Provider):
|
|
|
165
177
|
if __name__ == "__main__":
|
|
166
178
|
from rich import print
|
|
167
179
|
ai = KOALA(timeout=60)
|
|
168
|
-
response = ai.chat("
|
|
180
|
+
response = ai.chat("tell me about humans", stream=True, raw=False)
|
|
169
181
|
for chunk in response:
|
|
170
|
-
print(chunk
|
|
182
|
+
print(chunk)
|
webscout/Provider/learnfastai.py
CHANGED
|
@@ -153,7 +153,7 @@ class LearnFast(Provider):
|
|
|
153
153
|
optimizer: str = None,
|
|
154
154
|
conversationally: bool = False,
|
|
155
155
|
image_path: Optional[str] = None,
|
|
156
|
-
) -> Union[dict, Generator[dict, None, None]]:
|
|
156
|
+
) -> Union[dict, Generator[dict, None, None], str]:
|
|
157
157
|
"""Chat with LearnFast
|
|
158
158
|
|
|
159
159
|
Args:
|
|
@@ -166,7 +166,7 @@ class LearnFast(Provider):
|
|
|
166
166
|
Defaults to None.
|
|
167
167
|
|
|
168
168
|
Returns:
|
|
169
|
-
Union[dict, Generator[dict, None, None]]: Response generated
|
|
169
|
+
Union[dict, Generator[dict, None, None], str]: Response generated
|
|
170
170
|
"""
|
|
171
171
|
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
|
|
172
172
|
if optimizer:
|
|
@@ -202,68 +202,59 @@ class LearnFast(Provider):
|
|
|
202
202
|
data = json.dumps(payload)
|
|
203
203
|
|
|
204
204
|
def for_stream():
|
|
205
|
-
full_response = ""
|
|
205
|
+
full_response = ""
|
|
206
206
|
try:
|
|
207
|
-
# Use curl_cffi session post with impersonate
|
|
208
207
|
response = self.session.post(
|
|
209
208
|
self.api_endpoint,
|
|
210
209
|
headers=current_headers, # Use headers with uniqueid
|
|
211
210
|
data=data,
|
|
212
211
|
stream=True,
|
|
213
212
|
timeout=self.timeout,
|
|
214
|
-
|
|
215
|
-
impersonate="chrome110" # Use a common impersonation profile
|
|
216
|
-
)
|
|
217
|
-
response.raise_for_status() # Check for HTTP errors
|
|
218
|
-
|
|
219
|
-
# Use sanitize_stream
|
|
220
|
-
processed_stream = sanitize_stream(
|
|
221
|
-
data=response.iter_content(chunk_size=None), # Pass byte iterator
|
|
222
|
-
intro_value=None, # No prefix
|
|
223
|
-
to_json=True, # Stream sends JSON lines
|
|
224
|
-
skip_markers=["[DONE]"],
|
|
225
|
-
content_extractor=self._learnfast_extractor, # Use the specific extractor
|
|
226
|
-
yield_raw_on_error=False # Skip non-JSON lines or lines where extractor fails
|
|
213
|
+
impersonate="chrome110"
|
|
227
214
|
)
|
|
215
|
+
response.raise_for_status()
|
|
228
216
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
217
|
+
# Iterate over each line in the response
|
|
218
|
+
for line in response.iter_lines():
|
|
219
|
+
if not line:
|
|
220
|
+
continue
|
|
221
|
+
try:
|
|
222
|
+
chunk = json.loads(line)
|
|
223
|
+
except Exception:
|
|
224
|
+
continue
|
|
225
|
+
# Only yield message_type == "text"
|
|
226
|
+
data_field = chunk.get("data", {})
|
|
227
|
+
if (
|
|
228
|
+
chunk.get("code") == 200 and
|
|
229
|
+
data_field.get("code") == 200 and
|
|
230
|
+
data_field.get("message_type") == "text" and
|
|
231
|
+
data_field.get("message")
|
|
232
|
+
):
|
|
233
|
+
message = data_field["message"]
|
|
234
|
+
full_response += message
|
|
235
|
+
if raw:
|
|
236
|
+
yield message
|
|
237
|
+
else:
|
|
238
|
+
yield {"text": message}
|
|
237
239
|
self.last_response = {"text": full_response}
|
|
238
240
|
self.conversation.update_chat_history(prompt, full_response)
|
|
239
|
-
|
|
240
|
-
except CurlError as e: # Catch CurlError
|
|
241
|
+
except CurlError as e:
|
|
241
242
|
raise exceptions.FailedToGenerateResponseError(f"An error occurred (CurlError): {e}") from e
|
|
242
|
-
except Exception as e:
|
|
243
|
+
except Exception as e:
|
|
243
244
|
err_text = getattr(e, 'response', None) and getattr(e.response, 'text', '')
|
|
244
245
|
raise exceptions.FailedToGenerateResponseError(f"An error occurred ({type(e).__name__}): {e} - {err_text}") from e
|
|
245
|
-
|
|
246
246
|
def for_non_stream():
|
|
247
|
-
# Aggregate the stream using the updated for_stream logic
|
|
248
247
|
full_response_text = ""
|
|
249
248
|
try:
|
|
250
|
-
# Ensure raw=False so for_stream yields dicts
|
|
251
249
|
for chunk_data in for_stream():
|
|
252
|
-
if isinstance(chunk_data,
|
|
250
|
+
if raw and isinstance(chunk_data, str):
|
|
251
|
+
full_response_text += chunk_data
|
|
252
|
+
elif isinstance(chunk_data, dict) and "text" in chunk_data:
|
|
253
253
|
full_response_text += chunk_data["text"]
|
|
254
|
-
# Handle raw string case if raw=True was passed
|
|
255
|
-
elif raw and isinstance(chunk_data, str):
|
|
256
|
-
full_response_text += chunk_data
|
|
257
254
|
except Exception as e:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
raise exceptions.FailedToGenerateResponseError(f"Failed to get non-stream response: {str(e)}") from e
|
|
261
|
-
|
|
262
|
-
# last_response and history are updated within for_stream
|
|
263
|
-
# Return the final aggregated response dict or raw string
|
|
255
|
+
if not full_response_text:
|
|
256
|
+
raise exceptions.FailedToGenerateResponseError(f"Failed to get non-stream response: {str(e)}") from e
|
|
264
257
|
return full_response_text if raw else self.last_response
|
|
265
|
-
|
|
266
|
-
|
|
267
258
|
return for_stream() if stream else for_non_stream()
|
|
268
259
|
|
|
269
260
|
def chat(
|
|
@@ -273,36 +264,30 @@ class LearnFast(Provider):
|
|
|
273
264
|
optimizer: str = None,
|
|
274
265
|
conversationally: bool = False,
|
|
275
266
|
image_path: Optional[str] = None,
|
|
267
|
+
raw: bool = False
|
|
276
268
|
) -> Union[str, Generator[str, None, None]]:
|
|
277
|
-
"""Generate response `str`
|
|
278
|
-
Args:
|
|
279
|
-
prompt (str): Prompt to be send.
|
|
280
|
-
stream (bool, optional): Flag for streaming response. Defaults to False.
|
|
281
|
-
optimizer (str, optional): Prompt optimizer name - `[code, shell_command]`. Defaults to None.
|
|
282
|
-
conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
|
|
283
|
-
image_path (Optional[str], optional): Path to the image to be uploaded.
|
|
284
|
-
Defaults to None.
|
|
285
|
-
Returns:
|
|
286
|
-
Union[str, Generator[str, None, None]]: Response generated
|
|
287
|
-
"""
|
|
269
|
+
"""Generate response `str` or stream, with raw support"""
|
|
288
270
|
try:
|
|
289
|
-
# ask() yields dicts or strings when streaming
|
|
290
271
|
response_gen = self.ask(
|
|
291
|
-
prompt, stream=stream, raw=
|
|
272
|
+
prompt, stream=stream, raw=raw,
|
|
292
273
|
optimizer=optimizer, conversationally=conversationally,
|
|
293
274
|
image_path=image_path
|
|
294
275
|
)
|
|
295
276
|
if stream:
|
|
296
277
|
def stream_wrapper():
|
|
297
|
-
for
|
|
298
|
-
|
|
278
|
+
for chunk in response_gen:
|
|
279
|
+
if raw:
|
|
280
|
+
yield chunk
|
|
281
|
+
else:
|
|
282
|
+
yield self.get_message(chunk)
|
|
299
283
|
return stream_wrapper()
|
|
300
284
|
else:
|
|
301
|
-
|
|
302
|
-
|
|
285
|
+
if raw:
|
|
286
|
+
return response_gen if isinstance(response_gen, str) else self.get_message(response_gen)
|
|
287
|
+
else:
|
|
288
|
+
return self.get_message(response_gen)
|
|
303
289
|
except Exception as e:
|
|
304
|
-
|
|
305
|
-
return f"Error: {str(e)}"
|
|
290
|
+
return f"Error: {str(e)}"
|
|
306
291
|
|
|
307
292
|
def get_message(self, response: dict) -> str:
|
|
308
293
|
"""Retrieves message only from response
|
|
@@ -320,6 +305,6 @@ if __name__ == "__main__":
|
|
|
320
305
|
# Ensure curl_cffi is installed
|
|
321
306
|
from rich import print
|
|
322
307
|
ai = LearnFast()
|
|
323
|
-
response = ai.chat(input(">>> "), stream=True)
|
|
308
|
+
response = ai.chat(input(">>> "), stream=True, raw=False)
|
|
324
309
|
for chunk in response:
|
|
325
|
-
print(chunk, end=
|
|
310
|
+
print(chunk, end='', flush=True)
|
webscout/Provider/llmchat.py
CHANGED
|
@@ -3,7 +3,7 @@ from curl_cffi import CurlError
|
|
|
3
3
|
import json
|
|
4
4
|
from typing import Union, Any, Dict, Optional, Generator, List
|
|
5
5
|
|
|
6
|
-
from webscout.AIutel import Optimizers
|
|
6
|
+
from webscout.AIutel import Optimizers, sanitize_stream
|
|
7
7
|
from webscout.AIutel import Conversation
|
|
8
8
|
from webscout.AIutel import AwesomePrompts
|
|
9
9
|
from webscout.AIbase import Provider
|
|
@@ -95,8 +95,8 @@ class LLMChat(Provider):
|
|
|
95
95
|
raw: bool = False,
|
|
96
96
|
optimizer: str = None,
|
|
97
97
|
conversationally: bool = False,
|
|
98
|
-
) -> Union[Dict[str, Any], Generator[Any, None, None]]:
|
|
99
|
-
"""Chat with LLMChat with logging capabilities"""
|
|
98
|
+
) -> Union[Dict[str, Any], Generator[Any, None, None], str]:
|
|
99
|
+
"""Chat with LLMChat with logging capabilities and raw output support using sanitize_stream."""
|
|
100
100
|
|
|
101
101
|
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
|
|
102
102
|
if optimizer:
|
|
@@ -116,79 +116,59 @@ class LLMChat(Provider):
|
|
|
116
116
|
{"role": "user", "content": conversation_prompt}
|
|
117
117
|
],
|
|
118
118
|
"max_tokens": self.max_tokens_to_sample,
|
|
119
|
-
"stream": True
|
|
119
|
+
"stream": True
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
def for_stream():
|
|
123
|
-
full_response = ""
|
|
123
|
+
full_response = ""
|
|
124
124
|
try:
|
|
125
|
-
# Use curl_cffi session post with impersonate
|
|
126
125
|
response = self.session.post(
|
|
127
126
|
url,
|
|
128
127
|
json=payload,
|
|
129
128
|
stream=True,
|
|
130
129
|
timeout=self.timeout,
|
|
131
|
-
impersonate="chrome110"
|
|
130
|
+
impersonate="chrome110"
|
|
132
131
|
)
|
|
133
|
-
response.raise_for_status()
|
|
134
|
-
|
|
135
|
-
#
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
except json.JSONDecodeError:
|
|
153
|
-
continue # Ignore invalid JSON data
|
|
154
|
-
except UnicodeDecodeError:
|
|
155
|
-
continue # Ignore decoding errors
|
|
156
|
-
|
|
157
|
-
# Update history after stream finishes
|
|
132
|
+
response.raise_for_status()
|
|
133
|
+
|
|
134
|
+
# Use sanitize_stream to process SSE lines
|
|
135
|
+
processed_stream = sanitize_stream(
|
|
136
|
+
data=response.iter_lines(),
|
|
137
|
+
intro_value="data: ",
|
|
138
|
+
to_json=True,
|
|
139
|
+
skip_markers=["[DONE]"],
|
|
140
|
+
content_extractor=lambda chunk: chunk.get('response') if isinstance(chunk, dict) else None,
|
|
141
|
+
yield_raw_on_error=False,
|
|
142
|
+
raw=raw
|
|
143
|
+
)
|
|
144
|
+
for content_chunk in processed_stream:
|
|
145
|
+
if content_chunk and isinstance(content_chunk, str):
|
|
146
|
+
full_response += content_chunk
|
|
147
|
+
if raw:
|
|
148
|
+
yield content_chunk
|
|
149
|
+
else:
|
|
150
|
+
yield dict(text=content_chunk)
|
|
158
151
|
self.last_response = dict(text=full_response)
|
|
159
152
|
self.conversation.update_chat_history(
|
|
160
153
|
prompt, full_response
|
|
161
154
|
)
|
|
162
|
-
|
|
163
|
-
except CurlError as e: # Catch CurlError
|
|
155
|
+
except CurlError as e:
|
|
164
156
|
raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}") from e
|
|
165
|
-
except Exception as e:
|
|
157
|
+
except Exception as e:
|
|
166
158
|
err_text = getattr(e, 'response', None) and getattr(e.response, 'text', '')
|
|
167
159
|
raise exceptions.FailedToGenerateResponseError(f"Request failed ({type(e).__name__}): {e} - {err_text}") from e
|
|
168
|
-
|
|
169
160
|
def for_non_stream():
|
|
170
|
-
|
|
171
|
-
full_response_text = ""
|
|
161
|
+
full_response = ""
|
|
172
162
|
try:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
elif raw and isinstance(chunk_data, str):
|
|
179
|
-
full_response_text += chunk_data
|
|
163
|
+
for content_chunk in for_stream():
|
|
164
|
+
if raw and isinstance(content_chunk, str):
|
|
165
|
+
full_response += content_chunk
|
|
166
|
+
elif isinstance(content_chunk, dict) and "text" in content_chunk:
|
|
167
|
+
full_response += content_chunk["text"]
|
|
180
168
|
except Exception as e:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
# last_response and history are updated within for_stream
|
|
186
|
-
# Return the final aggregated response dict or raw string
|
|
187
|
-
return full_response_text if raw else self.last_response
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
# Since the API endpoint suggests streaming, always call the stream generator.
|
|
191
|
-
# The non-stream wrapper will handle aggregation if stream=False.
|
|
169
|
+
if not full_response:
|
|
170
|
+
raise exceptions.FailedToGenerateResponseError(f"Failed to get non-stream response: {str(e)}") from e
|
|
171
|
+
return full_response if raw else self.last_response
|
|
192
172
|
return for_stream() if stream else for_non_stream()
|
|
193
173
|
|
|
194
174
|
def chat(
|
|
@@ -197,29 +177,31 @@ class LLMChat(Provider):
|
|
|
197
177
|
stream: bool = False,
|
|
198
178
|
optimizer: str = None,
|
|
199
179
|
conversationally: bool = False,
|
|
180
|
+
raw: bool = False
|
|
200
181
|
) -> Union[str, Generator[str, None, None]]:
|
|
201
|
-
"""Generate response with logging capabilities"""
|
|
202
|
-
|
|
182
|
+
"""Generate response with logging capabilities and raw output support"""
|
|
203
183
|
def for_stream_chat():
|
|
204
|
-
# ask() yields dicts or strings when streaming
|
|
205
184
|
gen = self.ask(
|
|
206
|
-
prompt, stream=True, raw=
|
|
185
|
+
prompt, stream=True, raw=raw,
|
|
207
186
|
optimizer=optimizer, conversationally=conversationally
|
|
208
187
|
)
|
|
209
|
-
for
|
|
210
|
-
|
|
211
|
-
|
|
188
|
+
for response in gen:
|
|
189
|
+
if raw:
|
|
190
|
+
yield response
|
|
191
|
+
else:
|
|
192
|
+
yield self.get_message(response)
|
|
212
193
|
def for_non_stream_chat():
|
|
213
|
-
# ask() returns dict or str when not streaming
|
|
214
194
|
response_data = self.ask(
|
|
215
195
|
prompt,
|
|
216
196
|
stream=False,
|
|
217
|
-
raw=
|
|
197
|
+
raw=raw,
|
|
218
198
|
optimizer=optimizer,
|
|
219
|
-
conversationally=conversationally
|
|
199
|
+
conversationally=conversationally
|
|
220
200
|
)
|
|
221
|
-
|
|
222
|
-
|
|
201
|
+
if raw:
|
|
202
|
+
return response_data if isinstance(response_data, str) else self.get_message(response_data)
|
|
203
|
+
else:
|
|
204
|
+
return self.get_message(response_data)
|
|
223
205
|
return for_stream_chat() if stream else for_non_stream_chat()
|
|
224
206
|
|
|
225
207
|
def get_message(self, response: Dict[str, Any]) -> str:
|
|
@@ -228,31 +210,35 @@ class LLMChat(Provider):
|
|
|
228
210
|
return response["text"]
|
|
229
211
|
|
|
230
212
|
if __name__ == "__main__":
|
|
231
|
-
# Ensure curl_cffi is installed
|
|
232
|
-
print("-" * 80)
|
|
233
|
-
print(f"{'Model':<50} {'Status':<10} {'Response'}")
|
|
234
|
-
print("-" * 80)
|
|
213
|
+
# # Ensure curl_cffi is installed
|
|
214
|
+
# print("-" * 80)
|
|
215
|
+
# print(f"{'Model':<50} {'Status':<10} {'Response'}")
|
|
216
|
+
# print("-" * 80)
|
|
235
217
|
|
|
236
|
-
# Test all available models
|
|
237
|
-
working = 0
|
|
238
|
-
total = len(LLMChat.AVAILABLE_MODELS)
|
|
218
|
+
# # Test all available models
|
|
219
|
+
# working = 0
|
|
220
|
+
# total = len(LLMChat.AVAILABLE_MODELS)
|
|
239
221
|
|
|
240
|
-
for model in LLMChat.AVAILABLE_MODELS:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
222
|
+
# for model in LLMChat.AVAILABLE_MODELS:
|
|
223
|
+
# try:
|
|
224
|
+
# test_ai = LLMChat(model=model, timeout=60)
|
|
225
|
+
# response = test_ai.chat("Say 'Hello' in one word", stream=True)
|
|
226
|
+
# response_text = ""
|
|
227
|
+
# for chunk in response:
|
|
228
|
+
# response_text += chunk
|
|
229
|
+
# print(f"\r{model:<50} {'Testing...':<10}", end="", flush=True)
|
|
248
230
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
231
|
+
# if response_text and len(response_text.strip()) > 0:
|
|
232
|
+
# status = "✓"
|
|
233
|
+
# # Truncate response if too long
|
|
234
|
+
# display_text = response_text.strip()[:50] + "..." if len(response_text.strip()) > 50 else response_text.strip()
|
|
235
|
+
# else:
|
|
236
|
+
# status = "✗"
|
|
237
|
+
# display_text = "Empty or invalid response"
|
|
238
|
+
# print(f"\r{model:<50} {status:<10} {display_text}")
|
|
239
|
+
# except Exception as e:
|
|
240
|
+
# print(f"\r{model:<50} {'✗':<10} {str(e)}")
|
|
241
|
+
ai = LLMChat(model="@cf/meta/llama-3.1-70b-instruct")
|
|
242
|
+
response = ai.chat("Say 'Hello' in one word", stream=True, raw=False)
|
|
243
|
+
for chunk in response:
|
|
244
|
+
print(chunk, end="", flush=True)
|