webscout 8.2.3__py3-none-any.whl → 8.2.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 (87) hide show
  1. inferno/lol.py +589 -0
  2. webscout/AIutel.py +226 -14
  3. webscout/Bard.py +579 -206
  4. webscout/DWEBS.py +78 -35
  5. webscout/Extra/tempmail/base.py +1 -1
  6. webscout/Provider/AISEARCH/hika_search.py +4 -0
  7. webscout/Provider/AllenAI.py +163 -126
  8. webscout/Provider/ChatGPTClone.py +96 -84
  9. webscout/Provider/Deepinfra.py +95 -67
  10. webscout/Provider/ElectronHub.py +55 -0
  11. webscout/Provider/GPTWeb.py +96 -46
  12. webscout/Provider/Groq.py +194 -91
  13. webscout/Provider/HeckAI.py +89 -47
  14. webscout/Provider/HuggingFaceChat.py +113 -106
  15. webscout/Provider/Hunyuan.py +94 -83
  16. webscout/Provider/Jadve.py +107 -75
  17. webscout/Provider/LambdaChat.py +106 -64
  18. webscout/Provider/Llama3.py +94 -39
  19. webscout/Provider/MCPCore.py +318 -0
  20. webscout/Provider/Marcus.py +85 -36
  21. webscout/Provider/Netwrck.py +76 -43
  22. webscout/Provider/OPENAI/__init__.py +4 -1
  23. webscout/Provider/OPENAI/ai4chat.py +286 -0
  24. webscout/Provider/OPENAI/chatgptclone.py +35 -14
  25. webscout/Provider/OPENAI/deepinfra.py +37 -0
  26. webscout/Provider/OPENAI/groq.py +354 -0
  27. webscout/Provider/OPENAI/heckai.py +6 -2
  28. webscout/Provider/OPENAI/mcpcore.py +376 -0
  29. webscout/Provider/OPENAI/multichat.py +368 -0
  30. webscout/Provider/OPENAI/netwrck.py +3 -1
  31. webscout/Provider/OpenGPT.py +48 -38
  32. webscout/Provider/PI.py +168 -92
  33. webscout/Provider/PizzaGPT.py +66 -36
  34. webscout/Provider/TeachAnything.py +85 -51
  35. webscout/Provider/TextPollinationsAI.py +109 -51
  36. webscout/Provider/TwoAI.py +109 -60
  37. webscout/Provider/Venice.py +93 -56
  38. webscout/Provider/VercelAI.py +2 -2
  39. webscout/Provider/WiseCat.py +65 -28
  40. webscout/Provider/Writecream.py +37 -11
  41. webscout/Provider/WritingMate.py +135 -63
  42. webscout/Provider/__init__.py +3 -21
  43. webscout/Provider/ai4chat.py +6 -7
  44. webscout/Provider/copilot.py +0 -3
  45. webscout/Provider/elmo.py +101 -58
  46. webscout/Provider/granite.py +91 -46
  47. webscout/Provider/hermes.py +87 -47
  48. webscout/Provider/koala.py +1 -1
  49. webscout/Provider/learnfastai.py +104 -50
  50. webscout/Provider/llama3mitril.py +86 -51
  51. webscout/Provider/llmchat.py +88 -46
  52. webscout/Provider/llmchatco.py +74 -49
  53. webscout/Provider/meta.py +41 -37
  54. webscout/Provider/multichat.py +54 -25
  55. webscout/Provider/scnet.py +93 -43
  56. webscout/Provider/searchchat.py +82 -75
  57. webscout/Provider/sonus.py +103 -51
  58. webscout/Provider/toolbaz.py +132 -77
  59. webscout/Provider/turboseek.py +92 -41
  60. webscout/Provider/tutorai.py +82 -64
  61. webscout/Provider/typefully.py +75 -33
  62. webscout/Provider/typegpt.py +96 -35
  63. webscout/Provider/uncovr.py +112 -62
  64. webscout/Provider/x0gpt.py +69 -26
  65. webscout/Provider/yep.py +79 -66
  66. webscout/conversation.py +35 -21
  67. webscout/exceptions.py +20 -0
  68. webscout/prompt_manager.py +56 -42
  69. webscout/version.py +1 -1
  70. webscout/webscout_search.py +65 -47
  71. webscout/webscout_search_async.py +81 -126
  72. webscout/yep_search.py +93 -43
  73. {webscout-8.2.3.dist-info → webscout-8.2.4.dist-info}/METADATA +22 -10
  74. {webscout-8.2.3.dist-info → webscout-8.2.4.dist-info}/RECORD +78 -81
  75. {webscout-8.2.3.dist-info → webscout-8.2.4.dist-info}/WHEEL +1 -1
  76. webscout/Provider/C4ai.py +0 -432
  77. webscout/Provider/ChatGPTES.py +0 -237
  78. webscout/Provider/DeepSeek.py +0 -196
  79. webscout/Provider/Llama.py +0 -200
  80. webscout/Provider/Phind.py +0 -535
  81. webscout/Provider/WebSim.py +0 -228
  82. webscout/Provider/labyrinth.py +0 -340
  83. webscout/Provider/lepton.py +0 -194
  84. webscout/Provider/llamatutor.py +0 -192
  85. {webscout-8.2.3.dist-info → webscout-8.2.4.dist-info}/entry_points.txt +0 -0
  86. {webscout-8.2.3.dist-info → webscout-8.2.4.dist-info/licenses}/LICENSE.md +0 -0
  87. {webscout-8.2.3.dist-info → webscout-8.2.4.dist-info}/top_level.txt +0 -0
@@ -1,20 +1,25 @@
1
- import requests
2
- from requests.exceptions import RequestException
1
+ from curl_cffi.requests import Session # Import Session
2
+ from curl_cffi import CurlError # Import CurlError
3
3
  from typing import Union, Any, Dict
4
- from webscout.AIutel import Conversation, Optimizers
5
- from webscout.litagent import LitAgent
4
+ from webscout.AIbase import Provider # Import Provider base class
5
+ from webscout import exceptions # Import custom exceptions
6
+ from webscout.conversation import Conversation
7
+ from webscout.optimizers import Optimizers
6
8
  from webscout.prompt_manager import AwesomePrompts
9
+ from webscout.litagent import LitAgent
7
10
 
8
- class TeachAnything:
11
+ # Inherit from Provider
12
+ class TeachAnything(Provider):
9
13
  """
10
14
  A class to interact with the Teach-Anything API.
11
15
  """
12
-
16
+ # Add AVAILABLE_MODELS if applicable, otherwise remove model param
17
+ # AVAILABLE_MODELS = ["default"] # Example
13
18
 
14
19
  def __init__(
15
20
  self,
16
21
  is_conversation: bool = True,
17
- max_tokens: int = 600,
22
+ max_tokens: int = 600, # Note: max_tokens is not used by this API
18
23
  timeout: int = 30,
19
24
  intro: str = None,
20
25
  filepath: str = None,
@@ -22,6 +27,7 @@ class TeachAnything:
22
27
  proxies: dict = {},
23
28
  history_offset: int = 10250,
24
29
  act: str = None,
30
+ # model: str = "default" # Remove if not used
25
31
  ) -> None:
26
32
  """
27
33
  Initializes the Teach-Anything API with given parameters.
@@ -40,7 +46,8 @@ class TeachAnything:
40
46
  """
41
47
 
42
48
 
43
- self.session = requests.Session()
49
+ # Initialize curl_cffi Session
50
+ self.session = Session()
44
51
  self.is_conversation = is_conversation
45
52
  self.max_tokens_to_sample = max_tokens
46
53
  self.api_endpoint = "https://www.teach-anything.com/api/generate"
@@ -48,8 +55,6 @@ class TeachAnything:
48
55
  self.last_response = {}
49
56
  self.headers = {
50
57
  "authority": "www.teach-anything.com",
51
- "path": "/api/generate",
52
- "scheme": "https",
53
58
  "accept": "*/*",
54
59
  "accept-encoding": "gzip, deflate, br, zstd",
55
60
  "accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
@@ -57,13 +62,16 @@ class TeachAnything:
57
62
  "origin": "https://www.teach-anything.com",
58
63
  "referer": "https://www.teach-anything.com/",
59
64
  "user-agent": LitAgent().random(),
65
+ # Add sec-ch-ua headers if needed for impersonation consistency
60
66
  }
61
67
  self.__available_optimizers = (
62
68
  method
63
69
  for method in dir(Optimizers)
64
70
  if callable(getattr(Optimizers, method)) and not method.startswith("__")
65
71
  )
72
+ # Update curl_cffi session headers and proxies
66
73
  self.session.headers.update(self.headers)
74
+ self.session.proxies = proxies # Assign proxies directly
67
75
  Conversation.intro = (
68
76
  AwesomePrompts().get_act(
69
77
  act, raise_not_found=True, default=None, case_insensitive=True
@@ -75,12 +83,12 @@ class TeachAnything:
75
83
  is_conversation, self.max_tokens_to_sample, filepath, update_file
76
84
  )
77
85
  self.conversation.history_offset = history_offset
78
- self.session.proxies = proxies
86
+
79
87
  def ask(
80
88
  self,
81
89
  prompt: str,
82
- stream: bool = False,
83
- raw: bool = False,
90
+ stream: bool = False, # Keep stream param for interface, but API doesn't stream
91
+ raw: bool = False, # Keep raw param for interface
84
92
  optimizer: str = None,
85
93
  conversationally: bool = False,
86
94
  ) -> dict:
@@ -110,31 +118,40 @@ class TeachAnything:
110
118
  payload = {
111
119
  "prompt": conversation_prompt
112
120
  }
113
- def for_stream():
114
- response = self.session.post(self.api_endpoint, headers=self.headers, json=payload, timeout=self.timeout)
115
- if not response.ok:
116
- raise RequestException(
117
- f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
118
- )
119
121
 
120
- resp = response.text
121
- self.last_response.update(dict(text=resp))
122
+ # API does not stream, so implement non-stream logic directly
123
+ try:
124
+ # Use curl_cffi session post with impersonate
125
+ response = self.session.post(
126
+ self.api_endpoint,
127
+ # headers are set on the session
128
+ json=payload,
129
+ timeout=self.timeout,
130
+ impersonate="chrome110" # Use a common impersonation profile
131
+ )
132
+ response.raise_for_status() # Check for HTTP errors
133
+
134
+ # Use response.text which is already decoded
135
+ resp_text = response.text
136
+ # The response is plain text, wrap it in the expected dict format
137
+ self.last_response = {"text": resp_text}
122
138
  self.conversation.update_chat_history(
123
- prompt, self.get_message(self.last_response)
139
+ prompt, resp_text
124
140
  )
125
- return self.last_response
126
-
127
- def for_non_stream():
128
- for _ in for_stream():
129
- pass
130
- return self.last_response
141
+ # Return dict or raw string based on raw flag
142
+ return resp_text if raw else self.last_response
143
+
144
+ except CurlError as e: # Catch CurlError
145
+ raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}") from e
146
+ except Exception as e: # Catch other potential exceptions (like HTTPError)
147
+ err_text = getattr(e, 'response', None) and getattr(e.response, 'text', '')
148
+ raise exceptions.FailedToGenerateResponseError(f"An unexpected error occurred ({type(e).__name__}): {e} - {err_text}") from e
131
149
 
132
- return for_stream() if stream else for_non_stream()
133
150
 
134
151
  def chat(
135
152
  self,
136
153
  prompt: str,
137
- stream: bool = False,
154
+ stream: bool = False, # Keep stream param for interface consistency
138
155
  optimizer: str = None,
139
156
  conversationally: bool = False,
140
157
  ) -> str:
@@ -148,23 +165,22 @@ class TeachAnything:
148
165
  str: Response generated
149
166
  """
150
167
 
151
- def for_stream():
152
- for response in self.ask(
153
- prompt, True, optimizer=optimizer, conversationally=conversationally
154
- ):
155
- yield self.get_message(response)
156
-
157
- def for_non_stream():
158
- return self.get_message(
159
- self.ask(
160
- prompt,
161
- False,
162
- optimizer=optimizer,
163
- conversationally=conversationally,
164
- )
165
- )
166
-
167
- return for_stream() if stream else for_non_stream()
168
+ # Since ask() now handles both stream=True/False by returning the full response dict/str:
169
+ response_data = self.ask(
170
+ prompt,
171
+ stream=False, # Call ask in non-stream mode internally
172
+ raw=False, # Ensure ask returns dict
173
+ optimizer=optimizer,
174
+ conversationally=conversationally
175
+ )
176
+ # If stream=True was requested, simulate streaming by yielding the full message at once
177
+ if stream:
178
+ def stream_wrapper():
179
+ yield self.get_message(response_data)
180
+ return stream_wrapper()
181
+ else:
182
+ # If stream=False, return the full message directly
183
+ return self.get_message(response_data)
168
184
 
169
185
  def get_message(self, response: dict) -> str:
170
186
  """Retrieves message only from response
@@ -180,8 +196,26 @@ class TeachAnything:
180
196
 
181
197
 
182
198
  if __name__ == '__main__':
199
+ # Ensure curl_cffi is installed
183
200
  from rich import print
184
- ai = TeachAnything()
185
- response = ai.chat("hi")
186
- for chunk in response:
187
- print(chunk, end="", flush=True)
201
+ try: # Add try-except block for testing
202
+ ai = TeachAnything(timeout=60)
203
+ print("[bold blue]Testing Chat (Non-Stream Simulation):[/bold blue]")
204
+ # Test non-stream first as API doesn't truly stream
205
+ response_non_stream = ai.chat("hi", stream=False)
206
+ print(response_non_stream)
207
+ print("[bold green]Non-Stream Test Complete.[/bold green]\n")
208
+
209
+ # Test stream interface (will yield the full response at once)
210
+ print("[bold blue]Testing Chat (Stream Simulation):[/bold blue]")
211
+ response_stream = ai.chat("hello again", stream=True)
212
+ full_stream_response = ""
213
+ for chunk in response_stream:
214
+ print(chunk, end="", flush=True)
215
+ full_stream_response += chunk
216
+ print("\n[bold green]Stream Test Complete.[/bold green]")
217
+
218
+ except exceptions.FailedToGenerateResponseError as e:
219
+ print(f"\n[bold red]API Error:[/bold red] {e}")
220
+ except Exception as e:
221
+ print(f"\n[bold red]An unexpected error occurred:[/bold red] {e}")
@@ -1,4 +1,5 @@
1
- import requests
1
+ from curl_cffi.requests import Session
2
+ from curl_cffi import CurlError
2
3
  import json
3
4
  from typing import Union, Any, Dict, Generator, Optional, List
4
5
  from webscout.AIutel import Optimizers, Conversation, AwesomePrompts
@@ -39,7 +40,7 @@ class TextPollinationsAI(Provider):
39
40
  def __init__(
40
41
  self,
41
42
  is_conversation: bool = True,
42
- max_tokens: int = 8096,
43
+ max_tokens: int = 8096, # Note: max_tokens is not directly used by this API endpoint
43
44
  timeout: int = 30,
44
45
  intro: str = None,
45
46
  filepath: str = None,
@@ -54,7 +55,8 @@ class TextPollinationsAI(Provider):
54
55
  if model not in self.AVAILABLE_MODELS:
55
56
  raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
56
57
 
57
- self.session = requests.Session()
58
+ # Initialize curl_cffi Session
59
+ self.session = Session()
58
60
  self.is_conversation = is_conversation
59
61
  self.max_tokens_to_sample = max_tokens
60
62
  self.api_endpoint = "https://text.pollinations.ai/openai"
@@ -69,10 +71,12 @@ class TextPollinationsAI(Provider):
69
71
  'Accept-Language': 'en-US,en;q=0.9',
70
72
  'User-Agent': Lit().random(),
71
73
  'Content-Type': 'application/json',
74
+ # Add sec-ch-ua headers if needed for impersonation consistency
72
75
  }
73
76
 
77
+ # Update curl_cffi session headers and proxies
74
78
  self.session.headers.update(self.headers)
75
- self.session.proxies = proxies
79
+ self.session.proxies = proxies # Assign proxies directly
76
80
 
77
81
  self.__available_optimizers = (
78
82
  method for method in dir(Optimizers)
@@ -128,52 +132,96 @@ class TextPollinationsAI(Provider):
128
132
  payload["tool_choice"] = tool_choice
129
133
 
130
134
  def for_stream():
131
- response = self.session.post(
132
- self.api_endpoint,
133
- headers=self.headers,
134
- json=payload,
135
- stream=True,
136
- timeout=self.timeout
137
- )
138
-
139
- if not response.ok:
140
- raise exceptions.FailedToGenerateResponseError(
141
- f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
135
+ try: # Add try block for CurlError
136
+ # Use curl_cffi session post with impersonate
137
+ response = self.session.post(
138
+ self.api_endpoint,
139
+ # headers are set on the session
140
+ json=payload,
141
+ stream=True,
142
+ timeout=self.timeout,
143
+ impersonate="chrome120" # Add impersonate
142
144
  )
143
145
 
144
- full_response = ""
145
- for line in response.iter_lines():
146
- if line:
147
- line = line.decode('utf-8').strip()
148
- if line == "data: [DONE]":
149
- break
150
- if line.startswith('data: '):
151
- try:
152
- json_data = json.loads(line[6:])
153
- if 'choices' in json_data and len(json_data['choices']) > 0:
154
- choice = json_data['choices'][0]
155
- if 'delta' in choice:
156
- if 'content' in choice['delta']:
157
- content = choice['delta']['content']
158
- full_response += content
159
- yield content if raw else dict(text=content)
160
- elif 'tool_calls' in choice['delta']:
161
- # Handle tool calls in streaming response
162
- tool_calls = choice['delta']['tool_calls']
163
- yield tool_calls if raw else dict(tool_calls=tool_calls)
164
- except json.JSONDecodeError:
165
- continue
166
-
167
- self.last_response.update(dict(text=full_response))
168
- self.conversation.update_chat_history(
169
- prompt, self.get_message(self.last_response)
170
- )
146
+ if not response.ok:
147
+ raise exceptions.FailedToGenerateResponseError(
148
+ f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
149
+ )
150
+
151
+ full_response = ""
152
+ # Iterate over bytes and decode manually
153
+ for line_bytes in response.iter_lines():
154
+ if line_bytes:
155
+ line = line_bytes.decode('utf-8').strip()
156
+ if line == "data: [DONE]":
157
+ break
158
+ if line.startswith('data: '):
159
+ try:
160
+ json_data = json.loads(line[6:])
161
+ if 'choices' in json_data and len(json_data['choices']) > 0:
162
+ choice = json_data['choices'][0]
163
+ if 'delta' in choice:
164
+ if 'content' in choice['delta'] and choice['delta']['content'] is not None:
165
+ content = choice['delta']['content']
166
+ full_response += content
167
+ # Yield dict or raw string
168
+ yield content if raw else dict(text=content)
169
+ elif 'tool_calls' in choice['delta']:
170
+ # Handle tool calls in streaming response
171
+ tool_calls = choice['delta']['tool_calls']
172
+ # Yield dict or raw list
173
+ yield tool_calls if raw else dict(tool_calls=tool_calls)
174
+ except json.JSONDecodeError:
175
+ continue
176
+ except UnicodeDecodeError:
177
+ continue
178
+
179
+ # Update history and last response after stream finishes
180
+ # Note: last_response might only contain text, not tool calls if they occurred
181
+ self.last_response.update(dict(text=full_response))
182
+ if full_response: # Only update history if text was received
183
+ self.conversation.update_chat_history(
184
+ prompt, full_response # Use the fully aggregated text
185
+ )
186
+ except CurlError as e: # Catch CurlError
187
+ raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}") from e
188
+ except Exception as e: # Catch other potential exceptions
189
+ raise exceptions.FailedToGenerateResponseError(f"An unexpected error occurred ({type(e).__name__}): {e}") from e
190
+
171
191
 
172
192
  def for_non_stream():
173
- for _ in for_stream():
174
- pass
193
+ # Aggregate the stream using the updated for_stream logic
194
+ final_content = ""
195
+ tool_calls_aggregated = None # To store potential tool calls
196
+ for chunk_data in for_stream():
197
+ if isinstance(chunk_data, dict):
198
+ if "text" in chunk_data:
199
+ final_content += chunk_data["text"]
200
+ elif "tool_calls" in chunk_data:
201
+ # Aggregate tool calls (simple aggregation, might need refinement)
202
+ if tool_calls_aggregated is None:
203
+ tool_calls_aggregated = []
204
+ tool_calls_aggregated.extend(chunk_data["tool_calls"])
205
+ elif isinstance(chunk_data, str): # Handle raw stream case
206
+ final_content += chunk_data
207
+ # Handle raw tool calls list if raw=True
208
+ elif isinstance(chunk_data, list) and raw:
209
+ if tool_calls_aggregated is None:
210
+ tool_calls_aggregated = []
211
+ tool_calls_aggregated.extend(chunk_data)
212
+
213
+
214
+ # last_response and history are updated within for_stream (for text)
215
+ # Return a dict containing text and/or tool_calls
216
+ result = {}
217
+ if final_content:
218
+ result["text"] = final_content
219
+ if tool_calls_aggregated:
220
+ result["tool_calls"] = tool_calls_aggregated
221
+ self.last_response = result # Update last_response with aggregated result
175
222
  return self.last_response
176
223
 
224
+
177
225
  return for_stream() if stream else for_non_stream()
178
226
 
179
227
  def chat(
@@ -217,6 +265,7 @@ class TextPollinationsAI(Provider):
217
265
  return json.dumps(response["tool_calls"])
218
266
 
219
267
  if __name__ == "__main__":
268
+ # Ensure curl_cffi is installed
220
269
  print("-" * 80)
221
270
  print(f"{'Model':<50} {'Status':<10} {'Response'}")
222
271
  print("-" * 80)
@@ -228,19 +277,28 @@ if __name__ == "__main__":
228
277
  for model in TextPollinationsAI.AVAILABLE_MODELS:
229
278
  try:
230
279
  test_ai = TextPollinationsAI(model=model, timeout=60)
231
- response = test_ai.chat("Say 'Hello' in one word", stream=True)
280
+ # Test stream first
281
+ response_stream = test_ai.chat("Say 'Hello' in one word", stream=True)
232
282
  response_text = ""
233
- for chunk in response:
283
+ print(f"\r{model:<50} {'Streaming...':<10}", end="", flush=True)
284
+ for chunk in response_stream:
234
285
  response_text += chunk
235
- print(f"\r{model:<50} {'Testing...':<10}", end="", flush=True)
236
286
 
237
287
  if response_text and len(response_text.strip()) > 0:
238
288
  status = "✓"
239
- # Truncate response if too long
240
- display_text = response_text.strip()[:50] + "..." if len(response_text.strip()) > 50 else response_text.strip()
289
+ # Clean and truncate response
290
+ clean_text = response_text.strip()
291
+ display_text = clean_text[:50] + "..." if len(clean_text) > 50 else clean_text
241
292
  else:
242
- status = "✗"
243
- display_text = "Empty or invalid response"
293
+ status = "✗ (Stream)"
294
+ display_text = "Empty or invalid stream response"
244
295
  print(f"\r{model:<50} {status:<10} {display_text}")
296
+
297
+ # Optional: Add non-stream test if needed
298
+ # print(f"\r{model:<50} {'Non-Stream...':<10}", end="", flush=True)
299
+ # response_non_stream = test_ai.chat("Say 'Hi' again", stream=False)
300
+ # if not response_non_stream or len(response_non_stream.strip()) == 0:
301
+ # print(f"\r{model:<50} {'✗ (Non-Stream)':<10} Empty non-stream response")
302
+
245
303
  except Exception as e:
246
304
  print(f"\r{model:<50} {'✗':<10} {str(e)}")