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

@@ -0,0 +1,206 @@
1
+ from dataclasses import dataclass
2
+ from enum import Enum, auto
3
+ import requests
4
+ import json
5
+ import re
6
+ import uuid
7
+ from typing import List, Dict, Generator, Optional, Any, TypedDict, Literal, Union, Final
8
+
9
+ # Type definitions
10
+ class Role(Enum):
11
+ SYSTEM = "system"
12
+ USER = "user"
13
+ ASSISTANT = "assistant"
14
+
15
+ class Message(TypedDict):
16
+ role: str
17
+ content: str
18
+
19
+ class APIResponse(TypedDict):
20
+ event_id: str
21
+ fn_index: int
22
+ data: List[Any]
23
+
24
+ class StreamData(TypedDict):
25
+ msg: str
26
+ output: Dict[str, Any]
27
+
28
+ @dataclass
29
+ class APIConfig:
30
+ url: Final[str] = "https://qwen-qwen2-72b-instruct.hf.space"
31
+ api_endpoint: Final[str] = "https://qwen-qwen2-72b-instruct.hf.space/queue/join?"
32
+
33
+ @dataclass
34
+ class RequestHeaders:
35
+ join: Dict[str, str]
36
+ data: Dict[str, str]
37
+
38
+ @classmethod
39
+ def create_default(cls, base_url: str) -> 'RequestHeaders':
40
+ common_headers = {
41
+ 'accept-language': 'en-US,en;q=0.9',
42
+ 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
43
+ }
44
+
45
+ return cls(
46
+ join={
47
+ **common_headers,
48
+ 'accept': '*/*',
49
+ 'content-type': 'application/json',
50
+ 'origin': base_url,
51
+ 'referer': f'{base_url}/',
52
+ },
53
+ data={
54
+ **common_headers,
55
+ 'accept': 'text/event-stream',
56
+ 'referer': f'{base_url}/',
57
+ }
58
+ )
59
+
60
+ class QwenAPI:
61
+ def __init__(self, config: APIConfig = APIConfig()):
62
+ self.config = config
63
+ self.headers = RequestHeaders.create_default(config.url)
64
+
65
+ @staticmethod
66
+ def generate_session_hash() -> str:
67
+ """Generate a unique session hash."""
68
+ return str(uuid.uuid4()).replace('-', '')[:12]
69
+
70
+ @staticmethod
71
+ def format_prompt(messages: List[Message]) -> str:
72
+ """
73
+ Formats a list of messages into a single prompt string.
74
+
75
+ Args:
76
+ messages: A list of message dictionaries with "role" and "content" keys.
77
+
78
+ Returns:
79
+ str: The formatted prompt.
80
+ """
81
+ return "\n".join(f"{message['role']}: {message['content']}" for message in messages)
82
+
83
+ def create_sync_generator(
84
+ self,
85
+ model: str,
86
+ messages: List[Message],
87
+ proxy: Optional[str] = None,
88
+ **kwargs: Any
89
+ ) -> Generator[str, None, None]:
90
+ """
91
+ Synchronously streams responses from the Qwen_Qwen2_72B_Instruct API.
92
+
93
+ Args:
94
+ model: The model to use for the request.
95
+ messages: A list of message dictionaries with "role" and "content" keys.
96
+ proxy: Optional proxy URL for the request.
97
+ **kwargs: Additional keyword arguments.
98
+
99
+ Yields:
100
+ str: Text chunks from the API response.
101
+
102
+ Raises:
103
+ requests.exceptions.RequestException: If the API request fails.
104
+ json.JSONDecodeError: If the response cannot be parsed as JSON.
105
+ """
106
+ session_hash: str = self.generate_session_hash()
107
+
108
+ # Prepare the prompt
109
+ system_messages: List[str] = [
110
+ message["content"]
111
+ for message in messages
112
+ if message["role"] == Role.SYSTEM.value
113
+ ]
114
+ system_prompt: str = "\n".join(system_messages)
115
+
116
+ user_messages: List[Message] = [
117
+ message
118
+ for message in messages
119
+ if message["role"] != Role.SYSTEM.value
120
+ ]
121
+ prompt: str = self.format_prompt(user_messages)
122
+
123
+ payload_join: Dict[str, Any] = {
124
+ "data": [prompt, [], system_prompt],
125
+ "event_data": None,
126
+ "fn_index": 0,
127
+ "trigger_id": 11,
128
+ "session_hash": session_hash
129
+ }
130
+
131
+ with requests.Session() as session:
132
+ # Send join request
133
+ response = session.post(
134
+ self.config.api_endpoint,
135
+ headers=self.headers.join,
136
+ json=payload_join
137
+ )
138
+ response.raise_for_status()
139
+ event_data: APIResponse = response.json()
140
+
141
+ # Prepare data stream request
142
+ url_data: str = f'{self.config.url}/queue/data'
143
+ params_data: Dict[str, str] = {'session_hash': session_hash}
144
+
145
+ # Send data stream request
146
+ full_response: str = ""
147
+ final_full_response: str = ""
148
+
149
+ with session.get(
150
+ url_data,
151
+ headers=self.headers.data,
152
+ params=params_data,
153
+ stream=True
154
+ ) as response:
155
+ response.raise_for_status()
156
+
157
+ for line in response.iter_lines():
158
+ if line:
159
+ decoded_line: str = line.decode('utf-8')
160
+ if decoded_line.startswith('data: '):
161
+ try:
162
+ json_data: StreamData = json.loads(decoded_line[6:])
163
+
164
+ if json_data.get('msg') == 'process_generating':
165
+ if 'output' in json_data and 'data' in json_data['output']:
166
+ output_data: List[Any] = json_data['output']['data']
167
+ if len(output_data) > 1 and len(output_data[1]) > 0:
168
+ for item in output_data[1]:
169
+ if isinstance(item, list) and len(item) > 1:
170
+ fragment: str = str(item[1])
171
+ if not re.match(r'^\[.*\]$', fragment) and not full_response.endswith(fragment):
172
+ full_response += fragment
173
+ yield fragment
174
+
175
+ if json_data.get('msg') == 'process_completed':
176
+ if 'output' in json_data and 'data' in json_data['output']:
177
+ output_data = json_data['output']['data']
178
+ if len(output_data) > 1 and len(output_data[1]) > 0:
179
+ final_full_response = output_data[1][0][1]
180
+
181
+ if final_full_response.startswith(full_response):
182
+ final_full_response = final_full_response[len(full_response):]
183
+
184
+ if final_full_response:
185
+ yield final_full_response
186
+ break
187
+
188
+ except json.JSONDecodeError as e:
189
+ print(f"Could not parse JSON: {decoded_line}")
190
+ raise e
191
+
192
+
193
+ def main() -> None:
194
+ messages: List[Message] = [
195
+ {"role": Role.SYSTEM.value, "content": "You are a helpful assistant."},
196
+ {"role": Role.USER.value, "content": "LOL"}
197
+ ]
198
+
199
+ api = QwenAPI()
200
+ for text in api.create_sync_generator("qwen-qwen2-72b-instruct", messages):
201
+ print(text, end="", flush=True)
202
+ print("\n---\n")
203
+
204
+
205
+ if __name__ == "__main__":
206
+ main()
@@ -0,0 +1,201 @@
1
+ import requests
2
+ import json
3
+ from typing import Any, Dict, Generator
4
+
5
+ from webscout.AIutel import Optimizers
6
+ from webscout.AIutel import Conversation
7
+ from webscout.AIutel import AwesomePrompts
8
+ from webscout.AIbase import Provider
9
+ from webscout import exceptions
10
+
11
+
12
+ class TextPollinationsAI(Provider):
13
+ """
14
+ A class to interact with the Pollinations AI API.
15
+ """
16
+
17
+ AVAILABLE_MODELS = [
18
+ "openai",
19
+ "openai-large",
20
+ "qwen",
21
+ "qwen-coder",
22
+ "llama",
23
+ "mistral",
24
+ "unity",
25
+ "midijourney",
26
+ "rtist",
27
+ "searchgpt",
28
+ "evil",
29
+ "deepseek",
30
+ "claude-hybridspace",
31
+ "deepseek-r1",
32
+ "llamalight"
33
+ ]
34
+
35
+ def __init__(
36
+ self,
37
+ is_conversation: bool = True,
38
+ max_tokens: int = 600,
39
+ timeout: int = 30,
40
+ intro: str = None,
41
+ filepath: str = None,
42
+ update_file: bool = True,
43
+ proxies: dict = {},
44
+ history_offset: int = 10250,
45
+ act: str = None,
46
+ model: str = "openai",
47
+ system_prompt: str = "You are a helpful AI assistant.",
48
+ ):
49
+ """Initializes the TextPollinationsAI API client."""
50
+ if model not in self.AVAILABLE_MODELS:
51
+ raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
52
+
53
+ self.session = requests.Session()
54
+ self.is_conversation = is_conversation
55
+ self.max_tokens_to_sample = max_tokens
56
+ self.api_endpoint = "https://text.pollinations.ai/openai"
57
+ self.stream_chunk_size = 64
58
+ self.timeout = timeout
59
+ self.last_response = {}
60
+ self.model = model
61
+ self.system_prompt = system_prompt
62
+ self.headers = {
63
+ 'Accept': '*/*',
64
+ 'Accept-Language': 'en-US,en;q=0.9',
65
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
66
+ 'Content-Type': 'application/json',
67
+ }
68
+ self.session.headers.update(self.headers)
69
+ self.session.proxies = proxies
70
+
71
+ self.__available_optimizers = (
72
+ method
73
+ for method in dir(Optimizers)
74
+ if callable(getattr(Optimizers, method)) and not method.startswith("__")
75
+ )
76
+ Conversation.intro = (
77
+ AwesomePrompts().get_act(
78
+ act, raise_not_found=True, default=None, case_insensitive=True
79
+ )
80
+ if act
81
+ else intro or Conversation.intro
82
+ )
83
+ self.conversation = Conversation(
84
+ is_conversation, self.max_tokens_to_sample, filepath, update_file
85
+ )
86
+ self.conversation.history_offset = history_offset
87
+
88
+ def ask(
89
+ self,
90
+ prompt: str,
91
+ stream: bool = False,
92
+ raw: bool = False,
93
+ optimizer: str = None,
94
+ conversationally: bool = False,
95
+ ) -> Dict[str, Any] | Generator[Dict[str, Any], None, None]:
96
+ """Chat with AI
97
+ Args:
98
+ prompt (str): Prompt to be sent.
99
+ stream (bool, optional): Flag for streaming response. Defaults to False.
100
+ raw (bool, optional): Stream back raw response as received. Defaults to False.
101
+ optimizer (str, optional): Prompt optimizer name - `[code, shell_command]`. Defaults to None.
102
+ conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
103
+ Returns:
104
+ Union[Dict, Generator[Dict, None, None]]: Response generated
105
+ """
106
+ conversation_prompt = self.conversation.gen_complete_prompt(prompt)
107
+ if optimizer:
108
+ if optimizer in self.__available_optimizers:
109
+ conversation_prompt = getattr(Optimizers, optimizer)(
110
+ conversation_prompt if conversationally else prompt
111
+ )
112
+ else:
113
+ raise Exception(
114
+ f"Optimizer is not one of {self.__available_optimizers}"
115
+ )
116
+
117
+ payload = {
118
+ "messages": [
119
+ {"role": "system", "content": self.system_prompt},
120
+ {"role": "user", "content": conversation_prompt}
121
+ ],
122
+ "model": self.model,
123
+ "stream": stream,
124
+ }
125
+
126
+ def for_stream():
127
+ response = self.session.post(
128
+ self.api_endpoint, headers=self.headers, json=payload, stream=True, timeout=self.timeout
129
+ )
130
+ if not response.ok:
131
+ raise exceptions.FailedToGenerateResponseError(
132
+ f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
133
+ )
134
+ full_response = ""
135
+ for line in response.iter_lines():
136
+ if line:
137
+ line = line.decode('utf-8').strip()
138
+ # Break if the stream signals completion
139
+ if line == "data: [DONE]":
140
+ break
141
+ if line.startswith('data: '):
142
+ try:
143
+ json_data = json.loads(line[6:])
144
+ if 'choices' in json_data and len(json_data['choices']) > 0:
145
+ choice = json_data['choices'][0]
146
+ # Handle delta responses from streaming output
147
+ if 'delta' in choice and 'content' in choice['delta']:
148
+ content = choice['delta']['content']
149
+ else:
150
+ content = ""
151
+ full_response += content
152
+ yield content if raw else dict(text=content)
153
+ except json.JSONDecodeError as e:
154
+ print(f"Error parsing line: {line} - {e}")
155
+ self.last_response.update(dict(text=full_response))
156
+ self.conversation.update_chat_history(
157
+ prompt, self.get_message(self.last_response)
158
+ )
159
+
160
+ def for_non_stream():
161
+ for _ in for_stream():
162
+ pass
163
+ return self.last_response
164
+
165
+ return for_stream() if stream else for_non_stream()
166
+
167
+ def chat(
168
+ self,
169
+ prompt: str,
170
+ stream: bool = False,
171
+ optimizer: str = None,
172
+ conversationally: bool = False,
173
+ ) -> str | Generator[str, None, None]:
174
+ """Generate response as a string"""
175
+ def for_stream():
176
+ for response in self.ask(
177
+ prompt, True, optimizer=optimizer, conversationally=conversationally
178
+ ):
179
+ yield self.get_message(response)
180
+ def for_non_stream():
181
+ return self.get_message(
182
+ self.ask(
183
+ prompt,
184
+ False,
185
+ optimizer=optimizer,
186
+ conversationally=conversationally,
187
+ )
188
+ )
189
+ return for_stream() if stream else for_non_stream()
190
+
191
+ def get_message(self, response: dict) -> str:
192
+ """Retrieves message only from response"""
193
+ assert isinstance(response, dict), "Response should be of dict data-type only"
194
+ return response["text"]
195
+
196
+ if __name__ == "__main__":
197
+ from rich import print
198
+ ai = TextPollinationsAI(model="openai")
199
+ response = ai.chat(input(">>> "), stream=True)
200
+ for chunk in response:
201
+ print(chunk, end="", flush=True)
@@ -16,31 +16,33 @@ class YouChat(Provider):
16
16
  This class provides methods for interacting with the You.com chat API in a consistent provider structure.
17
17
  """
18
18
 
19
+ # Updated available models based on provided "aiModels" list
19
20
  AVAILABLE_MODELS = [
21
+ "openai_o3_mini_high",
22
+ "openai_o3_mini_medium",
20
23
  "openai_o1",
24
+ "openai_o1_preview",
21
25
  "openai_o1_mini",
22
26
  "gpt_4o_mini",
23
27
  "gpt_4o",
24
28
  "gpt_4_turbo",
25
29
  "gpt_4",
30
+ "grok_2",
26
31
  "claude_3_5_sonnet",
27
32
  "claude_3_opus",
28
33
  "claude_3_sonnet",
29
34
  "claude_3_5_haiku",
30
- "claude_3_haiku",
35
+ "deepseek_r1",
36
+ "deepseek_v3",
31
37
  "llama3_3_70b",
32
38
  "llama3_2_90b",
33
- "llama3_2_11b",
34
39
  "llama3_1_405b",
35
- "llama3_1_70b",
36
- "llama3",
37
40
  "mistral_large_2",
38
41
  "gemini_1_5_flash",
39
42
  "gemini_1_5_pro",
40
43
  "databricks_dbrx_instruct",
41
44
  "qwen2p5_72b",
42
45
  "qwen2p5_coder_32b",
43
- "command_r",
44
46
  "command_r_plus",
45
47
  "solar_1_mini",
46
48
  "dolphin_2_5"
@@ -156,22 +158,25 @@ class YouChat(Provider):
156
158
  )
157
159
 
158
160
  payload = {
159
- "q": conversation_prompt,
160
- "page": 2,
161
- "count": 20,
162
- "safeSearch": "Moderate",
163
- "mkt": "en-IN",
164
- "domain": "youchat",
165
- "use_personalization_extraction": "false",
166
- "queryTraceId": str(uuid4()),
167
- "chatId": str(uuid4()),
168
- "conversationTurnId": str(uuid4()),
169
- "pastChatLength": 0,
170
- "isSmallMediumDevice": "true",
171
- "selectedChatMode": self.model, # Use the selected model
172
- "traceId": str(uuid4()),
173
- "chat": "[]"
174
- }
161
+ "q": conversation_prompt,
162
+ "page": 1,
163
+ "count": 10,
164
+ "safeSearch": "Moderate",
165
+ "mkt": "en-IN",
166
+ "enable_workflow_generation_ux": "true",
167
+ "domain": "youchat",
168
+ "use_personalization_extraction": "false",
169
+ "enable_agent_clarification_questions": "true",
170
+ "queryTraceId": str(uuid4()),
171
+ "chatId": str(uuid4()),
172
+ "conversationTurnId": str(uuid4()),
173
+ "pastChatLength": 0,
174
+ "isSmallMediumDevice": "true",
175
+ "selectedChatMode": self.model,
176
+ "use_nested_youchat_updates": "true",
177
+ "traceId": str(uuid4()),
178
+ "chat": "[]"
179
+ }
175
180
 
176
181
  def for_stream():
177
182
  response = self.session.get(
@@ -255,9 +260,10 @@ class YouChat(Provider):
255
260
  """
256
261
  assert isinstance(response, dict), "Response should be of dict data-type only"
257
262
  return response["text"]
263
+
258
264
  if __name__ == '__main__':
259
265
  from rich import print
260
266
  ai = YouChat(timeout=5000)
261
267
  response = ai.chat("hi", stream=True)
262
268
  for chunk in response:
263
- print(chunk, end="", flush=True)
269
+ print(chunk, end="", flush=True)
@@ -60,8 +60,16 @@ from .Marcus import *
60
60
  from .typegpt import *
61
61
  from .multichat import *
62
62
  from .Jadve import *
63
+ from .chatglm import *
64
+ from .hermes import *
65
+ from .TextPollinationsAI import *
66
+ from .Glider import *
67
+ from .dgaf import *
63
68
  __all__ = [
64
- 'LLAMA',
69
+ 'LLAMA',
70
+ 'DGAFAI',
71
+ 'TextPollinationsAI',
72
+ 'GliderAI',
65
73
  'Cohere',
66
74
  'REKA',
67
75
  'GROQ',
@@ -123,4 +131,6 @@ __all__ = [
123
131
  'Netwrck',
124
132
  'MultiChatAI',
125
133
  'JadveOpenAI',
134
+ 'ChatGLM',
135
+ 'NousHermes',
126
136
  ]
@@ -8,7 +8,7 @@ from webscout.AIutel import Conversation
8
8
  from webscout.AIutel import AwesomePrompts
9
9
  from webscout.AIbase import Provider
10
10
  from webscout import exceptions
11
- from fake_useragent import UserAgent
11
+ from webscout import LitAgent as UserAgent
12
12
 
13
13
  class AskMyAI(Provider):
14
14
  """
@@ -41,7 +41,7 @@ class AskMyAI(Provider):
41
41
  "Accept": "*/*",
42
42
  "Accept-Encoding": "gzip, deflate, br",
43
43
  "Accept-Language": "en-US,en;q=0.9",
44
- 'user-agent': UserAgent().random
44
+ 'user-agent': UserAgent().random()
45
45
  }
46
46
  self.__available_optimizers = (
47
47
  method