webscout 5.1__py3-none-any.whl → 5.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/AIauto.py +83 -277
- webscout/AIbase.py +106 -4
- webscout/AIutel.py +41 -10
- webscout/Agents/Onlinesearcher.py +91 -104
- webscout/Agents/__init__.py +2 -1
- webscout/Agents/ai.py +186 -0
- webscout/Agents/functioncall.py +57 -27
- webscout/Bing_search.py +73 -43
- webscout/DWEBS.py +99 -77
- webscout/Local/_version.py +1 -1
- webscout/Provider/AI21.py +177 -0
- webscout/Provider/Chatify.py +174 -0
- webscout/Provider/Cloudflare.py +0 -4
- webscout/Provider/EDITEE.py +215 -0
- webscout/Provider/{Berlin4h.py → NetFly.py} +81 -82
- webscout/Provider/RUBIKSAI.py +11 -5
- webscout/Provider/TTI/PollinationsAI.py +138 -0
- webscout/Provider/TTI/__init__.py +2 -0
- webscout/Provider/TTI/deepinfra.py +148 -0
- webscout/Provider/TTS/__init__.py +2 -0
- webscout/Provider/TTS/streamElements.py +292 -0
- webscout/Provider/TTS/voicepod.py +118 -0
- webscout/Provider/{liaobots.py → TeachAnything.py} +31 -122
- webscout/Provider/__init__.py +14 -4
- webscout/Provider/ai4chat.py +14 -8
- webscout/Provider/cerebras.py +199 -0
- webscout/Provider/felo_search.py +28 -68
- webscout/Provider/x0gpt.py +181 -0
- webscout/__init__.py +4 -2
- webscout/exceptions.py +2 -1
- webscout/transcriber.py +195 -140
- webscout/version.py +1 -1
- {webscout-5.1.dist-info → webscout-5.3.dist-info}/METADATA +41 -82
- {webscout-5.1.dist-info → webscout-5.3.dist-info}/RECORD +38 -28
- webscout/async_providers.py +0 -21
- webscout/voice.py +0 -34
- {webscout-5.1.dist-info → webscout-5.3.dist-info}/LICENSE.md +0 -0
- {webscout-5.1.dist-info → webscout-5.3.dist-info}/WHEEL +0 -0
- {webscout-5.1.dist-info → webscout-5.3.dist-info}/entry_points.txt +0 -0
- {webscout-5.1.dist-info → webscout-5.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import json
|
|
3
|
+
import pygame
|
|
4
|
+
import time
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Generator
|
|
7
|
+
from playsound import playsound
|
|
8
|
+
from webscout import exceptions
|
|
9
|
+
from webscout.AIbase import TTSProvider
|
|
10
|
+
|
|
11
|
+
class Voicepods(TTSProvider):
|
|
12
|
+
"""
|
|
13
|
+
A class to interact with the Voicepods text-to-speech API.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, timeout: int = 20, proxies: dict = None):
|
|
17
|
+
"""
|
|
18
|
+
Initializes the Voicepods API client.
|
|
19
|
+
"""
|
|
20
|
+
self.api_endpoint = "https://voicepods-stream.vercel.app/api/resemble"
|
|
21
|
+
self.headers = {
|
|
22
|
+
'Accept': '*/*',
|
|
23
|
+
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
|
24
|
+
'Accept-Language': 'en-US,en;q=0.9,en-IN;q=0.8',
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
'DNT': '1',
|
|
27
|
+
'Origin': 'https://voicepods-stream.vercel.app',
|
|
28
|
+
'Referer': 'https://voicepods-stream.vercel.app/',
|
|
29
|
+
'Sec-CH-UA': '"Chromium";v="128", "Not;A=Brand";v="24", "Microsoft Edge";v="128"',
|
|
30
|
+
'Sec-CH-UA-Mobile': '?0',
|
|
31
|
+
'Sec-CH-UA-Platform': '"Windows"',
|
|
32
|
+
'Sec-Fetch-Dest': 'empty',
|
|
33
|
+
'Sec-Fetch-Mode': 'cors',
|
|
34
|
+
'Sec-Fetch-Site': 'same-origin',
|
|
35
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0',
|
|
36
|
+
}
|
|
37
|
+
self.session = requests.Session()
|
|
38
|
+
self.session.headers.update(self.headers)
|
|
39
|
+
if proxies:
|
|
40
|
+
self.session.proxies.update(proxies)
|
|
41
|
+
self.timeout = timeout
|
|
42
|
+
self.audio_cache_dir = Path("./audio_cache")
|
|
43
|
+
|
|
44
|
+
def tts(self, text: str) -> str:
|
|
45
|
+
"""
|
|
46
|
+
Converts text to speech using the Voicepods API.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
text (str): The text to be converted to speech.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
str: The filename of the saved audio file.
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
exceptions.FailedToGenerateResponseError: If there is an error generating or saving the audio.
|
|
56
|
+
"""
|
|
57
|
+
payload = json.dumps({"query": text})
|
|
58
|
+
filename = self.audio_cache_dir / f"{int(time.time())}.wav" # Using timestamp for filename
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
response = self.session.post(self.api_endpoint, data=payload, timeout=self.timeout)
|
|
62
|
+
response.raise_for_status()
|
|
63
|
+
|
|
64
|
+
content_type = response.headers.get('Content-Type', '')
|
|
65
|
+
if 'audio' not in content_type.lower():
|
|
66
|
+
raise ValueError(f"Unexpected content type: {content_type}")
|
|
67
|
+
|
|
68
|
+
audio_data = response.content
|
|
69
|
+
self._save_audio(audio_data, filename)
|
|
70
|
+
return filename.as_posix() # Return the filename as a string
|
|
71
|
+
|
|
72
|
+
except requests.exceptions.RequestException as e:
|
|
73
|
+
raise exceptions.FailedToGenerateResponseError(f"Error generating audio: {e}")
|
|
74
|
+
|
|
75
|
+
def _save_audio(self, audio_data: bytes, filename: Path):
|
|
76
|
+
"""Saves the audio data to a WAV file in the audio cache directory."""
|
|
77
|
+
try:
|
|
78
|
+
# Create the audio_cache directory if it doesn't exist
|
|
79
|
+
self.audio_cache_dir.mkdir(parents=True, exist_ok=True)
|
|
80
|
+
|
|
81
|
+
riff_start = audio_data.find(b'RIFF')
|
|
82
|
+
if riff_start == -1:
|
|
83
|
+
raise ValueError("RIFF header not found in audio data")
|
|
84
|
+
|
|
85
|
+
trimmed_audio_data = audio_data[riff_start:]
|
|
86
|
+
|
|
87
|
+
with open(filename, "wb") as f:
|
|
88
|
+
f.write(trimmed_audio_data)
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
raise exceptions.FailedToGenerateResponseError(f"Error saving audio: {e}")
|
|
92
|
+
|
|
93
|
+
def play_audio(self, filename: str):
|
|
94
|
+
"""
|
|
95
|
+
Plays an audio file using playsound.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
filename (str): The path to the audio file.
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
RuntimeError: If there is an error playing the audio.
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
playsound(filename)
|
|
105
|
+
except Exception as e:
|
|
106
|
+
raise RuntimeError(f"Error playing audio: {e}")
|
|
107
|
+
|
|
108
|
+
# Example usage
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
|
|
111
|
+
voicepods = Voicepods()
|
|
112
|
+
text = "Hello, this is a test of the Voicepods text-to-speech system."
|
|
113
|
+
|
|
114
|
+
print("Generating audio...")
|
|
115
|
+
audio_file = voicepods.tts(text)
|
|
116
|
+
|
|
117
|
+
print("Playing audio...")
|
|
118
|
+
voicepods.play_audio(audio_file)
|
|
@@ -1,40 +1,19 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import re
|
|
3
|
-
import uuid
|
|
4
|
-
import gzip
|
|
5
|
-
import zlib
|
|
6
|
-
from typing import Any, Dict, Generator, Union
|
|
7
|
-
|
|
8
1
|
import requests
|
|
2
|
+
from requests.exceptions import RequestException
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
import logging
|
|
5
|
+
import random
|
|
9
6
|
|
|
10
|
-
from webscout.AIutel import Optimizers
|
|
11
|
-
from webscout.AIutel import Conversation
|
|
12
|
-
from webscout.AIutel import AwesomePrompts
|
|
13
|
-
from webscout.AIbase import Provider
|
|
14
|
-
from webscout import exceptions
|
|
7
|
+
from webscout.AIutel import Conversation, Optimizers
|
|
15
8
|
|
|
16
|
-
class
|
|
9
|
+
class TeachAnything:
|
|
17
10
|
"""
|
|
18
|
-
A class to interact with the
|
|
11
|
+
A class to interact with the Teach-Anything API.
|
|
19
12
|
"""
|
|
20
13
|
|
|
21
|
-
# List of available models
|
|
22
|
-
AVAILABLE_MODELS = [
|
|
23
|
-
"gpt-4o-mini",
|
|
24
|
-
"gpt-4o-free",
|
|
25
|
-
"gpt-4o-mini-free",
|
|
26
|
-
"gpt-4-turbo-2024-04-09",
|
|
27
|
-
"gpt-4o",
|
|
28
|
-
"gpt-4-0613",
|
|
29
|
-
"claude-3-5-sonnet-20240620",
|
|
30
|
-
"gemini-1.5-pro-latest",
|
|
31
|
-
"gemini-1.5-flash-latest"
|
|
32
|
-
]
|
|
33
14
|
|
|
34
15
|
def __init__(
|
|
35
16
|
self,
|
|
36
|
-
auth_code: str = "G3USRn7M5zsXn",
|
|
37
|
-
cookie: str = "gkp2=pevIjZCYj8wMcrWPEAq6",
|
|
38
17
|
is_conversation: bool = True,
|
|
39
18
|
max_tokens: int = 600,
|
|
40
19
|
timeout: int = 30,
|
|
@@ -44,15 +23,11 @@ class LiaoBots(Provider):
|
|
|
44
23
|
proxies: dict = {},
|
|
45
24
|
history_offset: int = 10250,
|
|
46
25
|
act: str = None,
|
|
47
|
-
model: str = "claude-3-5-sonnet-20240620",
|
|
48
|
-
system_prompt: str = "You are a helpful assistant."
|
|
49
26
|
) -> None:
|
|
50
27
|
"""
|
|
51
|
-
Initializes the
|
|
28
|
+
Initializes the Teach-Anything API with given parameters.
|
|
52
29
|
|
|
53
30
|
Args:
|
|
54
|
-
auth_code (str): The auth code for authentication.
|
|
55
|
-
cookie (str): The cookie for authentication.
|
|
56
31
|
is_conversation (bool, optional): Flag for chatting conversationally. Defaults to True.
|
|
57
32
|
max_tokens (int, optional): Maximum number of tokens to be generated upon completion. Defaults to 600.
|
|
58
33
|
timeout (int, optional): Http request timeout. Defaults to 30.
|
|
@@ -62,57 +37,29 @@ class LiaoBots(Provider):
|
|
|
62
37
|
proxies (dict, optional): Http request proxies. Defaults to {}.
|
|
63
38
|
history_offset (int, optional): Limit conversation history to this number of last texts. Defaults to 10250.
|
|
64
39
|
act (str|int, optional): Awesome prompt key or index. (Used as intro). Defaults to None.
|
|
65
|
-
model (str, optional): AI model to use for text generation. Defaults to "
|
|
66
|
-
system_prompt (str, optional): System prompt for LiaoBots. Defaults to "You are a helpful assistant.".
|
|
40
|
+
model (str, optional): AI model to use for text generation. Defaults to "gpt4".
|
|
67
41
|
"""
|
|
68
42
|
|
|
69
|
-
# Check if the chosen model is available
|
|
70
|
-
if model not in self.AVAILABLE_MODELS:
|
|
71
|
-
raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
|
|
72
43
|
|
|
73
|
-
self.auth_code = auth_code
|
|
74
|
-
self.cookie = cookie
|
|
75
|
-
self.api_endpoint = "https://liaobots.work/api/chat"
|
|
76
|
-
self.model = model
|
|
77
|
-
self.system_prompt = system_prompt
|
|
78
44
|
self.session = requests.Session()
|
|
79
45
|
self.is_conversation = is_conversation
|
|
80
46
|
self.max_tokens_to_sample = max_tokens
|
|
81
|
-
self.
|
|
47
|
+
self.api_endpoint = "https://www.teach-anything.com/api/generate"
|
|
82
48
|
self.timeout = timeout
|
|
83
49
|
self.last_response = {}
|
|
84
50
|
self.headers = {
|
|
51
|
+
"authority": "www.teach-anything.com",
|
|
52
|
+
"path": "/api/generate",
|
|
53
|
+
"scheme": "https",
|
|
85
54
|
"accept": "*/*",
|
|
86
55
|
"accept-encoding": "gzip, deflate, br, zstd",
|
|
87
56
|
"accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
|
|
88
57
|
"content-type": "application/json",
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"origin": "https://liaobots.work",
|
|
92
|
-
"priority": "u=1, i",
|
|
93
|
-
"referer": "https://liaobots.work/en",
|
|
94
|
-
"sec-ch-ua": '"Not)A;Brand";v="99", "Microsoft Edge";v="127", "Chromium";v="127"',
|
|
95
|
-
"sec-ch-ua-mobile": "?0",
|
|
96
|
-
"sec-ch-ua-platform": '"Windows"',
|
|
97
|
-
"sec-fetch-dest": "empty",
|
|
98
|
-
"sec-fetch-mode": "cors",
|
|
99
|
-
"sec-fetch-site": "same-origin",
|
|
58
|
+
"origin": "https://www.teach-anything.com",
|
|
59
|
+
"referer": "https://www.teach-anything.com/",
|
|
100
60
|
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0",
|
|
101
|
-
"x-Auth-Code": self.auth_code,
|
|
102
61
|
}
|
|
103
|
-
self.__available_optimizers = (
|
|
104
|
-
method
|
|
105
|
-
for method in dir(Optimizers)
|
|
106
|
-
if callable(getattr(Optimizers, method)) and not method.startswith("__")
|
|
107
|
-
)
|
|
108
62
|
self.session.headers.update(self.headers)
|
|
109
|
-
Conversation.intro = (
|
|
110
|
-
AwesomePrompts().get_act(
|
|
111
|
-
act, raise_not_found=True, default=None, case_insensitive=True
|
|
112
|
-
)
|
|
113
|
-
if act
|
|
114
|
-
else intro or Conversation.intro
|
|
115
|
-
)
|
|
116
63
|
self.conversation = Conversation(
|
|
117
64
|
is_conversation, self.max_tokens_to_sample, filepath, update_file
|
|
118
65
|
)
|
|
@@ -126,12 +73,11 @@ class LiaoBots(Provider):
|
|
|
126
73
|
raw: bool = False,
|
|
127
74
|
optimizer: str = None,
|
|
128
75
|
conversationally: bool = False,
|
|
129
|
-
) ->
|
|
130
|
-
"""
|
|
131
|
-
Sends a prompt to the LiaoBots API and returns the response.
|
|
76
|
+
) -> dict:
|
|
77
|
+
"""Chat with AI
|
|
132
78
|
|
|
133
79
|
Args:
|
|
134
|
-
prompt:
|
|
80
|
+
prompt (str): Prompt to be send.
|
|
135
81
|
stream (bool, optional): Whether to stream the response. Defaults to False.
|
|
136
82
|
raw (bool, optional): Whether to return the raw response. Defaults to False.
|
|
137
83
|
optimizer (str, optional): The name of the optimizer to use. Defaults to None.
|
|
@@ -151,61 +97,23 @@ class LiaoBots(Provider):
|
|
|
151
97
|
f"Optimizer is not one of {self.__available_optimizers}"
|
|
152
98
|
)
|
|
153
99
|
|
|
154
|
-
payload
|
|
155
|
-
"
|
|
156
|
-
"model": {
|
|
157
|
-
"id": self.model
|
|
158
|
-
},
|
|
159
|
-
"messages": [
|
|
160
|
-
{
|
|
161
|
-
"role": "user",
|
|
162
|
-
"content": conversation_prompt
|
|
163
|
-
}
|
|
164
|
-
],
|
|
165
|
-
"key": "",
|
|
166
|
-
"prompt": self.system_prompt
|
|
100
|
+
payload = {
|
|
101
|
+
"prompt": conversation_prompt
|
|
167
102
|
}
|
|
168
|
-
|
|
169
103
|
def for_stream():
|
|
170
|
-
response = self.session.post(
|
|
171
|
-
self.api_endpoint, json=payload, headers=self.headers, stream=True, timeout=self.timeout
|
|
172
|
-
)
|
|
173
|
-
|
|
104
|
+
response = self.session.post(self.api_endpoint, headers=self.headers, json=payload, timeout=self.timeout)
|
|
174
105
|
if not response.ok:
|
|
175
|
-
raise
|
|
176
|
-
f"Failed to generate response - ({response.status_code}, {response.reason})"
|
|
106
|
+
raise RequestException(
|
|
107
|
+
f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
|
|
177
108
|
)
|
|
178
109
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
# Stream the response
|
|
182
|
-
for chunk in response.iter_content():
|
|
183
|
-
if chunk:
|
|
184
|
-
try:
|
|
185
|
-
# Decompress the chunk if necessary
|
|
186
|
-
if content_encoding == 'gzip':
|
|
187
|
-
chunk = gzip.decompress(chunk)
|
|
188
|
-
elif content_encoding == 'deflate':
|
|
189
|
-
chunk = zlib.decompress(chunk)
|
|
190
|
-
|
|
191
|
-
# Decode the chunk
|
|
192
|
-
decoded_chunk = chunk.decode('utf-8')
|
|
193
|
-
streaming_response += decoded_chunk
|
|
194
|
-
except UnicodeDecodeError:
|
|
195
|
-
# Handle non-textual data
|
|
196
|
-
pass
|
|
197
|
-
else:
|
|
198
|
-
pass
|
|
199
|
-
self.last_response.update(dict(text=streaming_response))
|
|
110
|
+
resp = response.text
|
|
111
|
+
self.last_response.update(dict(text=resp))
|
|
200
112
|
self.conversation.update_chat_history(
|
|
201
113
|
prompt, self.get_message(self.last_response)
|
|
202
114
|
)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
yield from [] # Yield nothing when streaming, focus on side effects
|
|
206
|
-
else:
|
|
207
|
-
return [] # Return empty list for non-streaming case
|
|
208
|
-
|
|
115
|
+
return self.last_response
|
|
116
|
+
|
|
209
117
|
def for_non_stream():
|
|
210
118
|
for _ in for_stream():
|
|
211
119
|
pass
|
|
@@ -260,9 +168,10 @@ class LiaoBots(Provider):
|
|
|
260
168
|
assert isinstance(response, dict), "Response should be of dict data-type only"
|
|
261
169
|
return response["text"]
|
|
262
170
|
|
|
171
|
+
|
|
263
172
|
if __name__ == '__main__':
|
|
264
173
|
from rich import print
|
|
265
|
-
|
|
266
|
-
response =
|
|
174
|
+
ai = TeachAnything()
|
|
175
|
+
response = ai.chat(input(">>> "))
|
|
267
176
|
for chunk in response:
|
|
268
|
-
print(chunk, end="", flush=True)
|
|
177
|
+
print(chunk, end="", flush=True)
|
webscout/Provider/__init__.py
CHANGED
|
@@ -20,7 +20,6 @@ from .Phind import Phindv2
|
|
|
20
20
|
from .Phind import AsyncPhindv2
|
|
21
21
|
from .ai4chat import *
|
|
22
22
|
from .Gemini import GEMINI
|
|
23
|
-
from .Berlin4h import Berlin4h
|
|
24
23
|
from .Poe import POE
|
|
25
24
|
from .BasedGPT import BasedGPT
|
|
26
25
|
from .Deepseek import DeepSeek
|
|
@@ -34,7 +33,6 @@ from .DARKAI import *
|
|
|
34
33
|
from .koala import *
|
|
35
34
|
from .RUBIKSAI import *
|
|
36
35
|
from .meta import *
|
|
37
|
-
from .liaobots import *
|
|
38
36
|
from .DiscordRocks import *
|
|
39
37
|
from .felo_search import *
|
|
40
38
|
from .xdash import *
|
|
@@ -43,6 +41,13 @@ from .Youchat import *
|
|
|
43
41
|
from .yep import *
|
|
44
42
|
from .Cloudflare import *
|
|
45
43
|
from .turboseek import *
|
|
44
|
+
from .NetFly import *
|
|
45
|
+
from .EDITEE import *
|
|
46
|
+
from .TeachAnything import *
|
|
47
|
+
from .AI21 import *
|
|
48
|
+
from .Chatify import *
|
|
49
|
+
from .x0gpt import *
|
|
50
|
+
from .cerebras import *
|
|
46
51
|
__all__ = [
|
|
47
52
|
'ThinkAnyAI',
|
|
48
53
|
'Farfalle',
|
|
@@ -62,7 +67,6 @@ __all__ = [
|
|
|
62
67
|
'AsyncPhindSearch',
|
|
63
68
|
'Felo',
|
|
64
69
|
'GEMINI',
|
|
65
|
-
'Berlin4h',
|
|
66
70
|
'POE',
|
|
67
71
|
'BasedGPT',
|
|
68
72
|
'DeepSeek',
|
|
@@ -80,7 +84,6 @@ __all__ = [
|
|
|
80
84
|
'KOALA',
|
|
81
85
|
'RUBIKSAI',
|
|
82
86
|
'Meta',
|
|
83
|
-
'LiaoBots',
|
|
84
87
|
'DiscordRocks',
|
|
85
88
|
'PiAI',
|
|
86
89
|
'XDASH',
|
|
@@ -89,4 +92,11 @@ __all__ = [
|
|
|
89
92
|
'YEPCHAT',
|
|
90
93
|
'Cloudflare',
|
|
91
94
|
'TurboSeek',
|
|
95
|
+
'NetFly',
|
|
96
|
+
'Editee',
|
|
97
|
+
'TeachAnything',
|
|
98
|
+
'AI21',
|
|
99
|
+
'Chatify',
|
|
100
|
+
'X0GPT',
|
|
101
|
+
'Cerebras'
|
|
92
102
|
]
|
webscout/Provider/ai4chat.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import requests
|
|
2
2
|
import json
|
|
3
3
|
import html
|
|
4
|
-
|
|
4
|
+
import re
|
|
5
5
|
from typing import Any, Dict
|
|
6
6
|
|
|
7
7
|
from webscout.AIutel import Optimizers
|
|
@@ -94,7 +94,7 @@ class AI4Chat(Provider):
|
|
|
94
94
|
def ask(
|
|
95
95
|
self,
|
|
96
96
|
prompt: str,
|
|
97
|
-
stream: bool = False,
|
|
97
|
+
stream: bool = False,
|
|
98
98
|
raw: bool = False,
|
|
99
99
|
optimizer: str = None,
|
|
100
100
|
conversationally: bool = False,
|
|
@@ -137,9 +137,17 @@ class AI4Chat(Provider):
|
|
|
137
137
|
response_data = response.json()
|
|
138
138
|
message_content = response_data.get('message', 'No message found')
|
|
139
139
|
|
|
140
|
-
# Decode HTML entities
|
|
140
|
+
# Decode HTML entities
|
|
141
141
|
decoded_message = html.unescape(message_content)
|
|
142
|
-
|
|
142
|
+
|
|
143
|
+
# Remove HTML tags while preserving newlines and list structure
|
|
144
|
+
cleaned_text = re.sub(r'<p>(.*?)</p>', r'\1\n\n', decoded_message)
|
|
145
|
+
cleaned_text = re.sub(r'<ol>|</ol>', '', cleaned_text)
|
|
146
|
+
cleaned_text = re.sub(r'<li><p>(.*?)</p></li>', r'• \1\n', cleaned_text)
|
|
147
|
+
cleaned_text = re.sub(r'</?[^>]+>', '', cleaned_text)
|
|
148
|
+
|
|
149
|
+
# Remove extra newlines
|
|
150
|
+
cleaned_text = re.sub(r'\n{3,}', '\n\n', cleaned_text.strip())
|
|
143
151
|
|
|
144
152
|
self.last_response.update(dict(text=cleaned_text))
|
|
145
153
|
self.conversation.update_chat_history(prompt, cleaned_text)
|
|
@@ -183,11 +191,9 @@ class AI4Chat(Provider):
|
|
|
183
191
|
"""
|
|
184
192
|
assert isinstance(response, dict), "Response should be of dict data-type only"
|
|
185
193
|
return response["text"]
|
|
194
|
+
|
|
186
195
|
if __name__ == "__main__":
|
|
187
196
|
from rich import print
|
|
188
|
-
|
|
189
197
|
ai = AI4Chat()
|
|
190
|
-
# Stream the response
|
|
191
198
|
response = ai.chat(input(">>> "))
|
|
192
|
-
|
|
193
|
-
print(chunk, end="", flush=True)
|
|
199
|
+
print(response)
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import requests
|
|
3
|
+
from webscout.AIutel import Optimizers, Conversation, AwesomePrompts
|
|
4
|
+
from webscout.AIbase import Provider
|
|
5
|
+
from webscout import exceptions
|
|
6
|
+
from typing import Dict, Any
|
|
7
|
+
|
|
8
|
+
class Cerebras(Provider):
|
|
9
|
+
"""
|
|
10
|
+
A class to interact with the Cerebras AI API.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
AVAILABLE_MODELS = ["llama3.1-8b", "llama3.1-70b"]
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
api_key: str,
|
|
18
|
+
is_conversation: bool = True,
|
|
19
|
+
max_tokens: int = 4096,
|
|
20
|
+
timeout: int = 30,
|
|
21
|
+
intro: str = None,
|
|
22
|
+
filepath: str = None,
|
|
23
|
+
update_file: bool = True,
|
|
24
|
+
proxies: dict = {},
|
|
25
|
+
history_offset: int = 10250,
|
|
26
|
+
act: str = None,
|
|
27
|
+
model: str = "llama3.1-8b",
|
|
28
|
+
system_prompt: str = "Please try to provide useful, helpful and actionable answers.",
|
|
29
|
+
):
|
|
30
|
+
"""
|
|
31
|
+
Initializes the Cerebras AI API with given parameters.
|
|
32
|
+
"""
|
|
33
|
+
if model not in self.AVAILABLE_MODELS:
|
|
34
|
+
raise ValueError(f"Invalid model: {model}. Available models are: {', '.join(self.AVAILABLE_MODELS)}")
|
|
35
|
+
|
|
36
|
+
self.session = requests.Session()
|
|
37
|
+
self.is_conversation = is_conversation
|
|
38
|
+
self.max_tokens_to_sample = max_tokens
|
|
39
|
+
self.api_endpoint = "https://api.cerebras.ai/v1/chat/completions"
|
|
40
|
+
self.timeout = timeout
|
|
41
|
+
self.last_response = {}
|
|
42
|
+
self.model = model
|
|
43
|
+
self.system_prompt = system_prompt
|
|
44
|
+
self.headers = {
|
|
45
|
+
"accept": "application/json",
|
|
46
|
+
"accept-encoding": "gzip, deflate, br, zstd",
|
|
47
|
+
"accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
|
|
48
|
+
"authorization": f"Bearer {api_key}",
|
|
49
|
+
"content-type": "application/json",
|
|
50
|
+
"dnt": "1",
|
|
51
|
+
"origin": "https://inference.cerebras.ai",
|
|
52
|
+
"referer": "https://inference.cerebras.ai/",
|
|
53
|
+
"sec-ch-ua": '"Chromium";v="128", "Not;A=Brand";v="24", "Microsoft Edge";v="128"',
|
|
54
|
+
"sec-ch-ua-mobile": "?0",
|
|
55
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
56
|
+
"sec-fetch-dest": "empty",
|
|
57
|
+
"sec-fetch-mode": "cors",
|
|
58
|
+
"sec-fetch-site": "same-site",
|
|
59
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
self.__available_optimizers = (
|
|
63
|
+
method
|
|
64
|
+
for method in dir(Optimizers)
|
|
65
|
+
if callable(getattr(Optimizers, method)) and not method.startswith("__")
|
|
66
|
+
)
|
|
67
|
+
self.session.headers.update(self.headers)
|
|
68
|
+
Conversation.intro = (
|
|
69
|
+
AwesomePrompts().get_act(
|
|
70
|
+
act, raise_not_found=True, default=None, case_insensitive=True
|
|
71
|
+
)
|
|
72
|
+
if act
|
|
73
|
+
else intro or Conversation.intro
|
|
74
|
+
)
|
|
75
|
+
self.conversation = Conversation(
|
|
76
|
+
is_conversation, self.max_tokens_to_sample, filepath, update_file
|
|
77
|
+
)
|
|
78
|
+
self.conversation.history_offset = history_offset
|
|
79
|
+
self.session.proxies = proxies
|
|
80
|
+
|
|
81
|
+
def ask(
|
|
82
|
+
self,
|
|
83
|
+
prompt: str,
|
|
84
|
+
stream: bool = False,
|
|
85
|
+
raw: bool = False,
|
|
86
|
+
optimizer: str = None,
|
|
87
|
+
conversationally: bool = False,
|
|
88
|
+
) -> Dict[str, Any]:
|
|
89
|
+
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
|
|
90
|
+
if optimizer:
|
|
91
|
+
if optimizer in self.__available_optimizers:
|
|
92
|
+
conversation_prompt = getattr(Optimizers, optimizer)(
|
|
93
|
+
conversation_prompt if conversationally else prompt
|
|
94
|
+
)
|
|
95
|
+
else:
|
|
96
|
+
raise Exception(
|
|
97
|
+
f"Optimizer is not one of {self.__available_optimizers}"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
payload = {
|
|
101
|
+
"messages": [
|
|
102
|
+
{"role": "system", "content": self.system_prompt},
|
|
103
|
+
{"role": "user", "content": conversation_prompt},
|
|
104
|
+
],
|
|
105
|
+
"model": self.model,
|
|
106
|
+
"stream": True,
|
|
107
|
+
"temperature": 0.2,
|
|
108
|
+
"top_p": 1,
|
|
109
|
+
"max_tokens": self.max_tokens_to_sample
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
def for_stream():
|
|
113
|
+
response = self.session.post(
|
|
114
|
+
self.api_endpoint, json=payload, stream=True, timeout=self.timeout
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if not response.ok:
|
|
118
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
119
|
+
f"Failed to generate response - ({response.status_code}, {response.reason})"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
full_response = ""
|
|
123
|
+
for line in response.iter_lines():
|
|
124
|
+
if line:
|
|
125
|
+
line_data = line.decode('utf-8').strip()
|
|
126
|
+
if line_data.startswith("data: "):
|
|
127
|
+
json_str = line_data[6:]
|
|
128
|
+
if json_str != "[DONE]":
|
|
129
|
+
chunk = json.loads(json_str)
|
|
130
|
+
if 'choices' in chunk and 'delta' in chunk['choices'][0]:
|
|
131
|
+
content = chunk['choices'][0]['delta'].get('content', '')
|
|
132
|
+
full_response += content
|
|
133
|
+
yield content if raw else dict(text=content)
|
|
134
|
+
else:
|
|
135
|
+
break
|
|
136
|
+
|
|
137
|
+
self.last_response.update(dict(text=full_response))
|
|
138
|
+
self.conversation.update_chat_history(
|
|
139
|
+
prompt, self.get_message(self.last_response)
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def for_non_stream():
|
|
143
|
+
full_response = ""
|
|
144
|
+
for chunk in for_stream():
|
|
145
|
+
if isinstance(chunk, dict):
|
|
146
|
+
full_response += chunk['text']
|
|
147
|
+
else:
|
|
148
|
+
full_response += chunk
|
|
149
|
+
return dict(text=full_response)
|
|
150
|
+
|
|
151
|
+
return for_stream() if stream else for_non_stream()
|
|
152
|
+
|
|
153
|
+
def chat(
|
|
154
|
+
self,
|
|
155
|
+
prompt: str,
|
|
156
|
+
stream: bool = False,
|
|
157
|
+
optimizer: str = None,
|
|
158
|
+
conversationally: bool = False,
|
|
159
|
+
) -> str:
|
|
160
|
+
def for_stream():
|
|
161
|
+
for response in self.ask(
|
|
162
|
+
prompt, True, optimizer=optimizer, conversationally=conversationally
|
|
163
|
+
):
|
|
164
|
+
yield self.get_message(response)
|
|
165
|
+
|
|
166
|
+
def for_non_stream():
|
|
167
|
+
return self.get_message(
|
|
168
|
+
self.ask(
|
|
169
|
+
prompt,
|
|
170
|
+
False,
|
|
171
|
+
optimizer=optimizer,
|
|
172
|
+
conversationally=conversationally,
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
return for_stream() if stream else for_non_stream()
|
|
177
|
+
|
|
178
|
+
def get_message(self, response: dict) -> str:
|
|
179
|
+
"""Retrieves message only from response
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
response (dict): Response generated by `self.ask`
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
str: Message extracted
|
|
186
|
+
"""
|
|
187
|
+
assert isinstance(response, dict), "Response should be of dict data-type only"
|
|
188
|
+
return response["text"]
|
|
189
|
+
|
|
190
|
+
if __name__ == '__main__':
|
|
191
|
+
from rich import print
|
|
192
|
+
|
|
193
|
+
# You can replace this with your actual API key
|
|
194
|
+
api_key = "YOUR_API_KEY_HERE"
|
|
195
|
+
|
|
196
|
+
ai = Cerebras(api_key=api_key)
|
|
197
|
+
response = ai.chat(input(">>> "), stream=True)
|
|
198
|
+
for chunk in response:
|
|
199
|
+
print(chunk, end="", flush=True)
|