webscout 8.3.2__py3-none-any.whl → 8.3.4__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.

Files changed (117) hide show
  1. webscout/AIutel.py +367 -41
  2. webscout/Bard.py +2 -22
  3. webscout/Bing_search.py +1 -2
  4. webscout/Provider/AISEARCH/__init__.py +1 -0
  5. webscout/Provider/AISEARCH/scira_search.py +24 -11
  6. webscout/Provider/AISEARCH/stellar_search.py +132 -0
  7. webscout/Provider/Deepinfra.py +75 -57
  8. webscout/Provider/ExaChat.py +93 -63
  9. webscout/Provider/Flowith.py +1 -1
  10. webscout/Provider/FreeGemini.py +2 -2
  11. webscout/Provider/Gemini.py +3 -10
  12. webscout/Provider/GeminiProxy.py +31 -5
  13. webscout/Provider/HeckAI.py +85 -80
  14. webscout/Provider/Jadve.py +56 -50
  15. webscout/Provider/LambdaChat.py +39 -31
  16. webscout/Provider/MiniMax.py +207 -0
  17. webscout/Provider/Nemotron.py +41 -13
  18. webscout/Provider/Netwrck.py +39 -59
  19. webscout/Provider/OLLAMA.py +8 -9
  20. webscout/Provider/OPENAI/BLACKBOXAI.py +0 -1
  21. webscout/Provider/OPENAI/MiniMax.py +298 -0
  22. webscout/Provider/OPENAI/README.md +31 -30
  23. webscout/Provider/OPENAI/TogetherAI.py +4 -17
  24. webscout/Provider/OPENAI/__init__.py +4 -2
  25. webscout/Provider/OPENAI/autoproxy.py +753 -18
  26. webscout/Provider/OPENAI/base.py +7 -76
  27. webscout/Provider/OPENAI/copilot.py +73 -26
  28. webscout/Provider/OPENAI/deepinfra.py +96 -132
  29. webscout/Provider/OPENAI/exachat.py +9 -5
  30. webscout/Provider/OPENAI/flowith.py +179 -166
  31. webscout/Provider/OPENAI/friendli.py +233 -0
  32. webscout/Provider/OPENAI/monochat.py +329 -0
  33. webscout/Provider/OPENAI/netwrck.py +4 -7
  34. webscout/Provider/OPENAI/pydantic_imports.py +1 -172
  35. webscout/Provider/OPENAI/qodo.py +630 -0
  36. webscout/Provider/OPENAI/scirachat.py +82 -49
  37. webscout/Provider/OPENAI/textpollinations.py +13 -12
  38. webscout/Provider/OPENAI/toolbaz.py +1 -0
  39. webscout/Provider/OPENAI/typegpt.py +4 -4
  40. webscout/Provider/OPENAI/utils.py +19 -42
  41. webscout/Provider/OPENAI/x0gpt.py +14 -2
  42. webscout/Provider/OpenGPT.py +54 -32
  43. webscout/Provider/PI.py +58 -84
  44. webscout/Provider/Qodo.py +454 -0
  45. webscout/Provider/StandardInput.py +32 -13
  46. webscout/Provider/TTI/README.md +9 -9
  47. webscout/Provider/TTI/__init__.py +2 -1
  48. webscout/Provider/TTI/aiarta.py +92 -78
  49. webscout/Provider/TTI/infip.py +212 -0
  50. webscout/Provider/TTI/monochat.py +220 -0
  51. webscout/Provider/TeachAnything.py +11 -3
  52. webscout/Provider/TextPollinationsAI.py +91 -82
  53. webscout/Provider/TogetherAI.py +32 -48
  54. webscout/Provider/Venice.py +37 -46
  55. webscout/Provider/VercelAI.py +27 -24
  56. webscout/Provider/WiseCat.py +35 -35
  57. webscout/Provider/WrDoChat.py +22 -26
  58. webscout/Provider/WritingMate.py +26 -22
  59. webscout/Provider/__init__.py +6 -6
  60. webscout/Provider/copilot.py +58 -61
  61. webscout/Provider/freeaichat.py +64 -55
  62. webscout/Provider/granite.py +48 -57
  63. webscout/Provider/koala.py +51 -39
  64. webscout/Provider/learnfastai.py +49 -64
  65. webscout/Provider/llmchat.py +79 -93
  66. webscout/Provider/llmchatco.py +63 -78
  67. webscout/Provider/monochat.py +275 -0
  68. webscout/Provider/multichat.py +51 -40
  69. webscout/Provider/oivscode.py +1 -1
  70. webscout/Provider/scira_chat.py +257 -104
  71. webscout/Provider/scnet.py +13 -13
  72. webscout/Provider/searchchat.py +13 -13
  73. webscout/Provider/sonus.py +12 -11
  74. webscout/Provider/toolbaz.py +25 -8
  75. webscout/Provider/turboseek.py +41 -42
  76. webscout/Provider/typefully.py +27 -12
  77. webscout/Provider/typegpt.py +43 -48
  78. webscout/Provider/uncovr.py +55 -90
  79. webscout/Provider/x0gpt.py +325 -299
  80. webscout/Provider/yep.py +79 -96
  81. webscout/__init__.py +7 -2
  82. webscout/auth/__init__.py +12 -1
  83. webscout/auth/providers.py +27 -5
  84. webscout/auth/routes.py +146 -105
  85. webscout/auth/server.py +367 -312
  86. webscout/client.py +121 -116
  87. webscout/litagent/Readme.md +68 -55
  88. webscout/litagent/agent.py +99 -9
  89. webscout/version.py +1 -1
  90. {webscout-8.3.2.dist-info → webscout-8.3.4.dist-info}/METADATA +102 -91
  91. {webscout-8.3.2.dist-info → webscout-8.3.4.dist-info}/RECORD +95 -107
  92. webscout/Provider/AI21.py +0 -177
  93. webscout/Provider/HuggingFaceChat.py +0 -469
  94. webscout/Provider/OPENAI/freeaichat.py +0 -363
  95. webscout/Provider/TTI/fastflux.py +0 -233
  96. webscout/Provider/Writecream.py +0 -246
  97. webscout/auth/static/favicon.svg +0 -11
  98. webscout/auth/swagger_ui.py +0 -203
  99. webscout/auth/templates/components/authentication.html +0 -237
  100. webscout/auth/templates/components/base.html +0 -103
  101. webscout/auth/templates/components/endpoints.html +0 -750
  102. webscout/auth/templates/components/examples.html +0 -491
  103. webscout/auth/templates/components/footer.html +0 -75
  104. webscout/auth/templates/components/header.html +0 -27
  105. webscout/auth/templates/components/models.html +0 -286
  106. webscout/auth/templates/components/navigation.html +0 -70
  107. webscout/auth/templates/static/api.js +0 -455
  108. webscout/auth/templates/static/icons.js +0 -168
  109. webscout/auth/templates/static/main.js +0 -784
  110. webscout/auth/templates/static/particles.js +0 -201
  111. webscout/auth/templates/static/styles.css +0 -3353
  112. webscout/auth/templates/static/ui.js +0 -374
  113. webscout/auth/templates/swagger_ui.html +0 -170
  114. {webscout-8.3.2.dist-info → webscout-8.3.4.dist-info}/WHEEL +0 -0
  115. {webscout-8.3.2.dist-info → webscout-8.3.4.dist-info}/entry_points.txt +0 -0
  116. {webscout-8.3.2.dist-info → webscout-8.3.4.dist-info}/licenses/LICENSE.md +0 -0
  117. {webscout-8.3.2.dist-info → webscout-8.3.4.dist-info}/top_level.txt +0 -0
webscout/Provider/yep.py CHANGED
@@ -1,10 +1,8 @@
1
1
  import uuid
2
- import json
3
2
  from curl_cffi import CurlError
4
3
  from curl_cffi.requests import Session
5
4
 
6
5
  from typing import Any, Dict, Optional, Generator, Union, List, TypeVar
7
-
8
6
  from webscout.AIutel import Optimizers
9
7
  from webscout.AIutel import AwesomePrompts, sanitize_stream # Import sanitize_stream
10
8
  from webscout.AIbase import Provider
@@ -192,81 +190,63 @@ class YEPCHAT(Provider):
192
190
 
193
191
  def for_stream():
194
192
  try:
195
- # buffer = b"" # No longer needed here
196
- # Use curl_cffi session post, pass cookies explicitly
197
193
  response = self.session.post(self.chat_endpoint, headers=self.headers, cookies=self.cookies, json=data, stream=True, timeout=self.timeout, impersonate=self.fingerprint.get("browser_type", "chrome110"))
198
-
199
194
  if not response.ok:
200
- # If we get a non-200 response, try refreshing our identity once
201
195
  if response.status_code in [403, 429]:
202
196
  self.refresh_identity()
203
- # Retry with new identity
204
- # Use curl_cffi session post, pass cookies explicitly
205
197
  retry_response = self.session.post(self.chat_endpoint, headers=self.headers, cookies=self.cookies, json=data, stream=True, timeout=self.timeout, impersonate=self.fingerprint.get("browser_type", "chrome110"))
206
198
  if not retry_response.ok:
207
199
  raise exceptions.FailedToGenerateResponseError(
208
200
  f"Failed to generate response after identity refresh - ({retry_response.status_code}, {retry_response.reason}) - {retry_response.text}"
209
201
  )
210
- response = retry_response # Use the successful retry response
202
+ response = retry_response
211
203
  else:
212
204
  raise exceptions.FailedToGenerateResponseError(
213
205
  f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
214
206
  )
215
-
216
- # --- Start of stream processing block (should be outside the 'if not response.ok' block) ---
217
207
  streaming_text = ""
218
-
219
- # Use sanitize_stream to process the lines
220
208
  processed_stream = sanitize_stream(
221
- data=response.iter_content(chunk_size=None), # Pass the byte iterator directly
209
+ data=response.iter_content(chunk_size=None),
222
210
  intro_value="data:",
223
- to_json=True, # Yep sends JSON after 'data:'
224
- skip_markers=["[DONE]"], # Skip the final marker
225
- yield_raw_on_error=False, # Only process valid JSON data
226
- # --- Add the content extractor ---
227
- content_extractor=lambda chunk: chunk.get('choices', [{}])[0].get('delta', {}).get('content') if isinstance(chunk, dict) else None
211
+ to_json=True,
212
+ skip_markers=["[DONE]"],
213
+ yield_raw_on_error=False,
214
+ content_extractor=lambda chunk: chunk.get('choices', [{}])[0].get('delta', {}).get('content') if isinstance(chunk, dict) else None,
215
+ raw=raw
228
216
  )
229
- # The loop now yields the final extracted string content directly
230
217
  for content_chunk in processed_stream:
231
- # --- TEMPORARY DEBUG PRINT ---
232
- # print(f"\nDEBUG: Received extracted content: {content_chunk!r}\n", flush=True) # Keep or remove debug print as needed
233
- if content_chunk and isinstance(content_chunk, str): # Ensure it's a non-empty string
234
- streaming_text += content_chunk
235
- # Yield dict or raw string chunk based on 'raw' flag
236
- yield dict(text=content_chunk) if not raw else content_chunk
237
- # --- End of stream processing block ---
238
-
239
- # Check if the response contains a tool call (This should happen *after* processing the stream)
240
- response_data = self.conversation.handle_tool_response(streaming_text)
241
-
242
- if response_data["is_tool_call"]:
243
- # Handle tool call results
244
- if response_data["success"]:
245
- for tool_call in response_data.get("tool_calls", []):
246
- tool_name = tool_call.get("name", "unknown_tool")
247
- result = response_data["result"]
248
- self.conversation.update_chat_history_with_tool(prompt, tool_name, result)
218
+ # Always yield as string, even in raw mode
219
+ if isinstance(content_chunk, bytes):
220
+ content_chunk = content_chunk.decode('utf-8', errors='ignore')
221
+ if raw:
222
+ yield content_chunk
249
223
  else:
250
- # If tool call failed, update history with error
251
- self.conversation.update_chat_history(prompt,
252
- f"Error executing tool call: {response_data['result']}")
253
- else:
254
- # Normal response handling
255
- self.conversation.update_chat_history(prompt, streaming_text)
256
-
257
- except CurlError as e: # Catch CurlError
258
- raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}")
224
+ if content_chunk and isinstance(content_chunk, str):
225
+ streaming_text += content_chunk
226
+ yield dict(text=content_chunk)
227
+ if not raw:
228
+ response_data = self.conversation.handle_tool_response(streaming_text)
229
+ if response_data["is_tool_call"]:
230
+ if response_data["success"]:
231
+ for tool_call in response_data.get("tool_calls", []):
232
+ tool_name = tool_call.get("name", "unknown_tool")
233
+ result = response_data["result"]
234
+ self.conversation.update_chat_history_with_tool(prompt, tool_name, result)
235
+ else:
236
+ self.conversation.update_chat_history(prompt, f"Error executing tool call: {response_data['result']}")
237
+ else:
238
+ self.conversation.update_chat_history(prompt, streaming_text)
239
+ except CurlError as e:
240
+ raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}")
259
241
  except Exception as e:
260
242
  raise exceptions.FailedToGenerateResponseError(f"Request failed: {e}")
261
243
 
262
244
  def for_non_stream():
263
245
  try:
264
- # Use curl_cffi session post, pass cookies explicitly
265
246
  response = self.session.post(self.chat_endpoint, headers=self.headers, cookies=self.cookies, json=data, timeout=self.timeout, impersonate=self.fingerprint.get("browser_type", "chrome110"))
266
247
  if not response.ok:
267
248
  if response.status_code in [403, 429]:
268
249
  self.refresh_identity()
269
- # Use curl_cffi session post, pass cookies explicitly
270
250
  response = self.session.post(self.chat_endpoint, headers=self.headers, cookies=self.cookies, json=data, timeout=self.timeout, impersonate=self.fingerprint.get("browser_type", "chrome110"))
271
251
  if not response.ok:
272
252
  raise exceptions.FailedToGenerateResponseError(
@@ -276,40 +256,28 @@ class YEPCHAT(Provider):
276
256
  raise exceptions.FailedToGenerateResponseError(
277
257
  f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
278
258
  )
279
-
280
- # ... existing non-stream response handling code ...
259
+ if raw:
260
+ return response.text
281
261
  response_data = response.json()
282
262
  if 'choices' in response_data and len(response_data['choices']) > 0:
283
263
  content = response_data['choices'][0].get('message', {}).get('content', '')
284
-
285
- # Check if the response contains a tool call
286
264
  tool_response = self.conversation.handle_tool_response(content)
287
-
288
265
  if tool_response["is_tool_call"]:
289
- # Process tool call
290
266
  if tool_response["success"]:
291
- # Get the first tool call for simplicity
292
267
  if "tool_calls" in tool_response and len(tool_response["tool_calls"]) > 0:
293
268
  tool_call = tool_response["tool_calls"][0]
294
269
  tool_name = tool_call.get("name", "unknown_tool")
295
270
  tool_result = tool_response["result"]
296
-
297
- # Update chat history with tool call
298
271
  self.conversation.update_chat_history_with_tool(prompt, tool_name, tool_result)
299
-
300
- # Return tool result
301
272
  return {"text": tool_result, "is_tool_call": True, "tool_name": tool_name}
302
-
303
- # If tool call processing failed
304
273
  return {"text": tool_response["result"], "is_tool_call": True, "error": True}
305
274
  else:
306
- # Normal response handling
307
275
  self.conversation.update_chat_history(prompt, content)
308
276
  return {"text": content}
309
277
  else:
310
278
  raise exceptions.FailedToGenerateResponseError("No response content found")
311
- except CurlError as e: # Catch CurlError
312
- raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}")
279
+ except CurlError as e:
280
+ raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}")
313
281
  except Exception as e:
314
282
  raise exceptions.FailedToGenerateResponseError(f"Request failed: {e}")
315
283
 
@@ -321,6 +289,7 @@ class YEPCHAT(Provider):
321
289
  stream: bool = False,
322
290
  optimizer: str = None,
323
291
  conversationally: bool = False,
292
+ raw: bool = False, # Added raw parameter
324
293
  ) -> Union[str, Generator[str, None, None]]:
325
294
  """
326
295
  Initiates a chat with the Yep API using the provided prompt.
@@ -335,19 +304,25 @@ class YEPCHAT(Provider):
335
304
  """
336
305
  def for_stream():
337
306
  for response in self.ask(
338
- prompt, True, optimizer=optimizer, conversationally=conversationally
307
+ prompt, True, raw=raw, optimizer=optimizer, conversationally=conversationally
339
308
  ):
340
- yield self.get_message(response)
309
+ if raw:
310
+ yield response
311
+ else:
312
+ yield self.get_message(response)
341
313
 
342
314
  def for_non_stream():
343
- return self.get_message(
344
- self.ask(
345
- prompt,
346
- False,
347
- optimizer=optimizer,
348
- conversationally=conversationally,
349
- )
315
+ result = self.ask(
316
+ prompt,
317
+ False,
318
+ raw=raw,
319
+ optimizer=optimizer,
320
+ conversationally=conversationally,
350
321
  )
322
+ if raw:
323
+ return result
324
+ else:
325
+ return self.get_message(result)
351
326
 
352
327
  return for_stream() if stream else for_non_stream()
353
328
 
@@ -361,29 +336,37 @@ class YEPCHAT(Provider):
361
336
  >>> ai.get_message(response)
362
337
  Extracts and returns the message content from the response.
363
338
  """
364
- assert isinstance(response, dict)
365
- return response["text"]
339
+ if isinstance(response, dict):
340
+ return response["text"]
341
+ elif isinstance(response, (str, bytes)):
342
+ return response
343
+ else:
344
+ raise TypeError(f"Unexpected response type: {type(response)}")
366
345
 
367
346
 
368
347
  if __name__ == "__main__":
369
- print("-" * 80)
370
- print(f"{'Model':<50} {'Status':<10} {'Response'}")
371
- print("-" * 80)
372
-
373
- for model in YEPCHAT.AVAILABLE_MODELS:
374
- try:
375
- test_ai = YEPCHAT(model=model, timeout=60)
376
- response = test_ai.chat("Say 'Hello' in one word")
377
- response_text = response
348
+ # print("-" * 80)
349
+ # print(f"{'Model':<50} {'Status':<10} {'Response'}")
350
+ # print("-" * 80)
351
+
352
+ # for model in YEPCHAT.AVAILABLE_MODELS:
353
+ # try:
354
+ # test_ai = YEPCHAT(model=model, timeout=60)
355
+ # response = test_ai.chat("Say 'Hello' in one word")
356
+ # response_text = response
378
357
 
379
- if response_text and len(response_text.strip()) > 0:
380
- status = "✓"
381
- # Truncate response if too long
382
- display_text = response_text.strip()[:50] + "..." if len(response_text.strip()) > 50 else response_text.strip()
383
- else:
384
- status = "✗"
385
- display_text = "Empty or invalid response"
386
- print(f"{model:<50} {status:<10} {display_text}")
387
- except Exception as e:
388
- print(f"{model:<50} {'✗':<10} {str(e)}")
389
-
358
+ # if response_text and len(response_text.strip()) > 0:
359
+ # status = "✓"
360
+ # # Truncate response if too long
361
+ # display_text = response_text.strip()[:50] + "..." if len(response_text.strip()) > 50 else response_text.strip()
362
+ # else:
363
+ # status = "✗"
364
+ # display_text = "Empty or invalid response"
365
+ # print(f"{model:<50} {status:<10} {display_text}")
366
+ # except Exception as e:
367
+ # print(f"{model:<50} {'✗':<10} {str(e)}")
368
+ ai = YEPCHAT(model="DeepSeek-R1-Distill-Qwen-32B", timeout=60)
369
+ response = ai.chat("Say 'Hello' in one word", raw=True, stream=True)
370
+ for chunk in response:
371
+
372
+ print(chunk, end='', flush=True)
webscout/__init__.py CHANGED
@@ -17,8 +17,13 @@ from .litagent import LitAgent
17
17
  from .scout import *
18
18
  from .zeroart import *
19
19
  from .yep_search import *
20
- # # Import litprinter components for direct access from webscout
20
+
21
+ # Import litprinter components for direct access from webscout (uncomment if needed)
21
22
  # from .litprinter import lit, litprint, ic, install, uninstall
23
+
24
+ # Import sanitize_stream and lit_streamer for easy access
25
+ from .AIutel import sanitize_stream, lit_streamer, LITSTREAM
26
+
22
27
  agent = LitAgent()
23
28
 
24
29
  __repo__ = "https://github.com/OE-LUCIFER/Webscout"
@@ -30,7 +35,7 @@ try:
30
35
  if update_message:
31
36
  print(update_message)
32
37
  except Exception:
33
- pass # Silently handle any update check errorslently handle any update check errors
38
+ pass # Silently handle any update check errors
34
39
 
35
40
  import logging
36
41
  logging.getLogger("webscout").addHandler(logging.NullHandler())
webscout/auth/__init__.py CHANGED
@@ -13,7 +13,18 @@ from .schemas import (
13
13
  UserResponse,
14
14
  HealthCheckResponse
15
15
  )
16
- from .server import create_app, run_api, start_server
16
+ # Import server functions lazily to avoid module execution issues
17
+ def create_app():
18
+ from .server import create_app as _create_app
19
+ return _create_app()
20
+
21
+ def run_api(*args, **kwargs):
22
+ from .server import run_api as _run_api
23
+ return _run_api(*args, **kwargs)
24
+
25
+ def start_server(*args, **kwargs):
26
+ from .server import start_server as _start_server
27
+ return _start_server(*args, **kwargs)
17
28
  from .routes import Api
18
29
  from .config import ServerConfig, AppConfig
19
30
  from .exceptions import APIError
@@ -87,13 +87,13 @@ def initialize_tti_provider_map() -> None:
87
87
  logger.info("Initializing TTI provider map...")
88
88
 
89
89
  try:
90
- import webscout.Provider.TTI as tti_module
91
90
  from webscout.Provider.TTI.base import TTICompatibleProvider
92
-
91
+ module = sys.modules["webscout.Provider.TTI"]
92
+
93
93
  provider_count = 0
94
94
  model_count = 0
95
95
 
96
- for name, obj in inspect.getmembers(tti_module):
96
+ for name, obj in inspect.getmembers(module):
97
97
  if (
98
98
  inspect.isclass(obj)
99
99
  and issubclass(obj, TTICompatibleProvider)
@@ -242,7 +242,18 @@ def get_provider_instance(provider_class: Any):
242
242
  key = provider_class.__name__
243
243
  instance = provider_instances.get(key)
244
244
  if instance is None:
245
- instance = provider_class()
245
+ try:
246
+ instance = provider_class()
247
+ except TypeError as e:
248
+ # Handle abstract class instantiation error
249
+ if "abstract class" in str(e):
250
+ from .exceptions import APIError
251
+ raise APIError(
252
+ f"Provider misconfiguration: Cannot instantiate abstract class '{provider_class.__name__}'. Please check the provider implementation.",
253
+ HTTP_500_INTERNAL_SERVER_ERROR,
254
+ "provider_error"
255
+ )
256
+ raise
246
257
  provider_instances[key] = instance
247
258
  return instance
248
259
 
@@ -252,6 +263,17 @@ def get_tti_provider_instance(provider_class: Any):
252
263
  key = provider_class.__name__
253
264
  instance = tti_provider_instances.get(key)
254
265
  if instance is None:
255
- instance = provider_class()
266
+ try:
267
+ instance = provider_class()
268
+ except TypeError as e:
269
+ # Handle abstract class instantiation error
270
+ if "abstract class" in str(e):
271
+ from .exceptions import APIError
272
+ raise APIError(
273
+ f"Provider misconfiguration: Cannot instantiate abstract class '{provider_class.__name__}'. Please check the provider implementation.",
274
+ HTTP_500_INTERNAL_SERVER_ERROR,
275
+ "provider_error",
276
+ )
277
+ raise
256
278
  tti_provider_instances[key] = instance
257
279
  return instance