webscout 5.2__py3-none-any.whl → 5.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 (58) hide show
  1. webscout/AIauto.py +8 -12
  2. webscout/AIutel.py +10 -10
  3. webscout/Agents/Onlinesearcher.py +5 -5
  4. webscout/Agents/functioncall.py +123 -97
  5. webscout/DWEBS.py +99 -77
  6. webscout/Local/_version.py +2 -2
  7. webscout/Provider/Andi.py +1 -21
  8. webscout/Provider/BasedGPT.py +1 -21
  9. webscout/Provider/Blackboxai.py +1 -21
  10. webscout/Provider/Chatify.py +175 -0
  11. webscout/Provider/Cloudflare.py +1 -22
  12. webscout/Provider/Cohere.py +2 -23
  13. webscout/Provider/DARKAI.py +0 -1
  14. webscout/Provider/Deepinfra.py +2 -16
  15. webscout/Provider/EDITEE.py +3 -26
  16. webscout/Provider/Gemini.py +1 -24
  17. webscout/Provider/Groq.py +0 -2
  18. webscout/Provider/Koboldai.py +0 -21
  19. webscout/Provider/Llama.py +4 -21
  20. webscout/Provider/NetFly.py +21 -61
  21. webscout/Provider/OLLAMA.py +0 -17
  22. webscout/Provider/Openai.py +2 -22
  23. webscout/Provider/Perplexity.py +1 -2
  24. webscout/Provider/Phind.py +3 -508
  25. webscout/Provider/RUBIKSAI.py +11 -5
  26. webscout/Provider/Reka.py +4 -21
  27. webscout/Provider/TTS/streamElements.py +1 -22
  28. webscout/Provider/TTS/voicepod.py +11 -8
  29. webscout/Provider/ThinkAnyAI.py +17 -78
  30. webscout/Provider/Youchat.py +3 -20
  31. webscout/Provider/__init__.py +17 -8
  32. webscout/Provider/ai4chat.py +14 -8
  33. webscout/Provider/cerebras.py +199 -0
  34. webscout/Provider/{Berlin4h.py → cleeai.py} +68 -73
  35. webscout/Provider/{liaobots.py → elmo.py} +75 -106
  36. webscout/Provider/felo_search.py +29 -87
  37. webscout/Provider/geminiapi.py +198 -0
  38. webscout/Provider/genspark.py +222 -0
  39. webscout/Provider/julius.py +3 -20
  40. webscout/Provider/koala.py +1 -1
  41. webscout/Provider/lepton.py +194 -0
  42. webscout/Provider/turboseek.py +4 -21
  43. webscout/Provider/x0gpt.py +182 -0
  44. webscout/Provider/xdash.py +2 -22
  45. webscout/Provider/yep.py +391 -149
  46. webscout/YTdownloader.py +2 -3
  47. webscout/__init__.py +2 -2
  48. webscout/exceptions.py +2 -1
  49. webscout/transcriber.py +195 -140
  50. webscout/version.py +1 -1
  51. {webscout-5.2.dist-info → webscout-5.4.dist-info}/METADATA +47 -134
  52. webscout-5.4.dist-info/RECORD +98 -0
  53. webscout/voice.py +0 -34
  54. webscout-5.2.dist-info/RECORD +0 -93
  55. {webscout-5.2.dist-info → webscout-5.4.dist-info}/LICENSE.md +0 -0
  56. {webscout-5.2.dist-info → webscout-5.4.dist-info}/WHEEL +0 -0
  57. {webscout-5.2.dist-info → webscout-5.4.dist-info}/entry_points.txt +0 -0
  58. {webscout-5.2.dist-info → webscout-5.4.dist-info}/top_level.txt +0 -0
webscout/Provider/yep.py CHANGED
@@ -1,42 +1,58 @@
1
1
  import time
2
2
  import uuid
3
- from selenium import webdriver
4
- from selenium.webdriver.chrome.options import Options
5
- from selenium.webdriver.common.by import By
6
- from selenium.webdriver.support import expected_conditions as EC
7
- from selenium.webdriver.support.ui import WebDriverWait
8
- import click
9
- import requests
10
- from requests import get
11
- from uuid import uuid4
12
- from re import findall
13
- from requests.exceptions import RequestException
14
- from curl_cffi.requests import get, RequestsError
15
- import g4f
16
- from random import randint
17
- from PIL import Image
18
- import io
19
- import re
3
+ import cloudscraper
20
4
  import json
21
- import yaml
5
+
6
+ from typing import Any, Dict, Optional, Callable, Union
7
+ from dataclasses import dataclass, asdict
8
+ from datetime import date
9
+
22
10
  from webscout.AIutel import Optimizers
23
11
  from webscout.AIutel import Conversation
24
- from webscout.AIutel import AwesomePrompts, sanitize_stream
25
- from webscout.AIbase import Provider, AsyncProvider
26
- from webscout import exceptions
27
- from typing import Any, AsyncGenerator, Dict
28
- import logging
29
- import httpx
30
- import cloudscraper
12
+ from webscout.AIutel import AwesomePrompts
13
+ from webscout.AIbase import Provider
14
+ from webscout import WEBS, exceptions
15
+
16
+
17
+ @dataclass
18
+ class ToolCall:
19
+ tool_name: str
20
+ tool_input: Dict[str, Any]
21
+
31
22
 
32
23
  class YEPCHAT(Provider):
33
- """
34
- This class provides methods for interacting with the Yep.com chat API in a consistent provider structure.
35
- """
24
+ AVAILABLE_MODELS = ["Mixtral-8x7B-Instruct-v0.1"]
25
+ tool_call_start = "```tool_code"
26
+ tool_call_end = "```"
27
+
28
+ class ToolRegistry:
29
+ def __init__(self):
30
+ self.tools: Dict[str, Dict[str, Union[Callable, str, Dict]]] = {}
31
+
32
+ def register_tool(
33
+ self,
34
+ name: str,
35
+ function: Callable,
36
+ description: str = "",
37
+ parameters: Optional[Dict[str, Any]] = None,
38
+ ):
39
+ self.tools[name] = {
40
+ "function": function,
41
+ "description": description,
42
+ "parameters": parameters,
43
+ }
44
+
45
+ def get_tool(self, name: str) -> Optional[Callable]:
46
+ tool = self.tools.get(name)
47
+ return tool["function"] if tool else None
48
+
49
+ def get_tool_description(self, name: str) -> str:
50
+ tool = self.tools.get(name)
51
+ return tool["description"] if tool else ""
36
52
 
37
- AVAILABLE_MODELS = [
38
- "Mixtral-8x7B-Instruct-v0.1" # Add other available models here
39
- ]
53
+ def get_tool_parameters(self, name: str) -> Optional[Dict[str, Any]]:
54
+ tool = self.tools.get(name)
55
+ return tool["parameters"] if tool else None
40
56
 
41
57
  def __init__(
42
58
  self,
@@ -52,28 +68,16 @@ class YEPCHAT(Provider):
52
68
  model: str = "Mixtral-8x7B-Instruct-v0.1",
53
69
  temperature: float = 0.6,
54
70
  top_p: float = 0.7,
71
+ Tools: bool = False,
72
+ retries: int = 3,
73
+ retry_delay: int = 5,
55
74
  ):
56
- """Instantiates YEPCHAT
57
-
58
- Args:
59
- is_conversation (bool, optional): Flag for chatting conversationally. Defaults to True.
60
- max_tokens (int, optional): Maximum number of tokens to be generated upon completion. Defaults to 1280.
61
- timeout (int, optional): Http request timeout. Defaults to 30.
62
- intro (str, optional): Conversation introductory prompt. Defaults to None.
63
- filepath (str, optional): Path to file containing conversation history. Defaults to None.
64
- update_file (bool, optional): Add new prompts and responses to the file. Defaults to True.
65
- proxies (dict, optional): Http request proxies. Defaults to {}.
66
- history_offset (int, optional): Limit conversation history to this number of last texts. Defaults to 10250.
67
- act (str|int, optional): Awesome prompt key or index. (Used as intro). Defaults to None.
68
- model (str, optional): Model to use for generating text. Defaults to "Mixtral-8x7B-Instruct-v0.1".
69
- temperature (float, optional): Temperature parameter for the model. Defaults to 0.6.
70
- top_p (float, optional): Top_p parameter for the model. Defaults to 0.7.
71
- """
72
-
73
75
  if model not in self.AVAILABLE_MODELS:
74
- raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
76
+ raise ValueError(
77
+ f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}"
78
+ )
75
79
 
76
- self.session = cloudscraper.create_scraper() # Create a Cloudscraper session
80
+ self.session = cloudscraper.create_scraper()
77
81
  self.is_conversation = is_conversation
78
82
  self.max_tokens_to_sample = max_tokens
79
83
  self.chat_endpoint = "https://api.yep.com/v1/chat/completions"
@@ -83,32 +87,30 @@ class YEPCHAT(Provider):
83
87
  self.model = model
84
88
  self.temperature = temperature
85
89
  self.top_p = top_p
90
+ self.use_tools = Tools
86
91
  self.headers = {
87
- 'Accept': '*/*',
88
- 'Accept-Encoding': 'gzip, deflate, br, zstd',
89
- 'Accept-Language': 'en-US,en;q=0.9,en-IN;q=0.8',
90
- 'Content-Type': 'application/json; charset=utf-8',
91
- 'DNT': '1',
92
- 'Origin': 'https://yep.com',
93
- 'Referer': 'https://yep.com/',
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
- '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'
98
- }
99
- self.cookies = {
100
- '__Host-session': uuid4().hex,
92
+ "Accept": "*/*",
93
+ "Accept-Encoding": "gzip, deflate, br, zstd",
94
+ "Accept-Language": "en-US,en;q=0.9,en-IN;q=0.8",
95
+ "Content-Type": "application/json; charset=utf-8",
96
+ "DNT": "1",
97
+ "Origin": "https://yep.com",
98
+ "Referer": "https://yep.com/",
99
+ "Sec-CH-UA": '"Not)A;Brand";v="99", "Microsoft Edge";v="127", "Chromium";v="127"',
100
+ "Sec-CH-UA-Mobile": "?0",
101
+ "Sec-CH-UA-Platform": '"Windows"',
102
+ "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
103
  }
104
+ self.cookies = {"__Host-session": uuid.uuid4().hex}
102
105
 
103
106
  self.__available_optimizers = (
104
107
  method
105
108
  for method in dir(Optimizers)
106
- if callable(getattr(Optimizers, method)) and not method.startswith("__")
109
+ if callable(getattr(Optimizers, method))
110
+ and not method.startswith("__")
107
111
  )
108
112
  Conversation.intro = (
109
- AwesomePrompts().get_act(
110
- act, raise_not_found=True, default=None, case_insensitive=True
111
- )
113
+ AwesomePrompts().get_act(act, raise_not_found=True, default=None, case_insensitive=True)
112
114
  if act
113
115
  else intro or Conversation.intro
114
116
  )
@@ -118,6 +120,11 @@ class YEPCHAT(Provider):
118
120
  self.conversation.history_offset = history_offset
119
121
  self.session.proxies = proxies
120
122
 
123
+ self.tool_registry = self.ToolRegistry()
124
+ self.knowledge_cutoff = "December 2023"
125
+ self.retries = retries
126
+ self.retry_delay = retry_delay
127
+
121
128
  def ask(
122
129
  self,
123
130
  prompt: str,
@@ -126,18 +133,7 @@ class YEPCHAT(Provider):
126
133
  optimizer: str = None,
127
134
  conversationally: bool = False,
128
135
  ) -> dict:
129
- """Chat with AI
130
-
131
- Args:
132
- prompt (str): Prompt to be send.
133
- stream (bool, optional): Whether to stream the response. Defaults to False.
134
- raw (bool, optional): Whether to return the raw response. Defaults to False.
135
- optimizer (str, optional): The name of the optimizer to use. Defaults to None.
136
- conversationally (bool, optional): Whether to chat conversationally. Defaults to False.
137
-
138
- Returns:
139
- The response from the API.
140
- """
136
+ initial_response = None
141
137
  conversation_prompt = self.conversation.gen_complete_prompt(prompt)
142
138
  if optimizer:
143
139
  if optimizer in self.__available_optimizers:
@@ -149,60 +145,145 @@ class YEPCHAT(Provider):
149
145
  f"Optimizer is not one of {self.__available_optimizers}"
150
146
  )
151
147
 
152
- data = {
153
- "stream": stream,
154
- "max_tokens": self.max_tokens_to_sample,
155
- "top_p": self.top_p,
156
- "temperature": self.temperature,
157
- "messages": [
158
- {"content": conversation_prompt, "role": "user"}
159
- ],
160
- "model": self.model
161
- }
162
-
163
- def for_stream():
164
- response = self.session.post(
165
- self.chat_endpoint, headers=self.headers, cookies=self.cookies, json=data, stream=True, timeout=self.timeout
166
- )
167
- if not response.ok:
168
- raise exceptions.FailedToGenerateResponseError(
169
- f"Failed to generate response - ({response.status_code}, {response.reason})"
170
- )
171
- streaming_response = ""
172
- for line in response.iter_lines(decode_unicode=True):
173
- if line:
174
- if line.startswith("data: "):
175
- json_data = line[6:]
176
- if json_data == "[DONE]":
177
- break
178
- try:
179
- data = json.loads(json_data)
180
- content = data["choices"][0]["delta"].get("content", "")
181
- streaming_response += content
182
- yield content if raw else dict(text=streaming_response)
183
- except json.decoder.JSONDecodeError:
184
- continue
185
- self.last_response.update(dict(text=streaming_response))
186
- self.conversation.update_chat_history(
187
- prompt, self.get_message(self.last_response)
148
+ tool_call_data = None
149
+ tool_output = None
150
+ if self.use_tools:
151
+ initial_response = self._get_initial_response(
152
+ conversation_prompt, self.retries, self.retry_delay
188
153
  )
154
+ # logging.info(f"Initial AI response: {initial_response}")
189
155
 
190
- def for_non_stream():
191
- response = self.session.post(
192
- self.chat_endpoint, headers=self.headers, cookies=self.cookies, json=data, timeout=self.timeout
193
- )
194
- if not response.ok:
195
- raise exceptions.FailedToGenerateResponseError(
196
- f"Failed to generate response - ({response.status_code}, {response.reason})"
156
+ tool_call_data = self._parse_function_call(initial_response)
157
+ if tool_call_data and "error" not in tool_call_data:
158
+ tool_call = ToolCall(**tool_call_data)
159
+ tool_output = self.execute_tool(tool_call)
160
+ # logging.info(f"Tool output: {tool_output}")
161
+
162
+ final_response = self._get_final_response(
163
+ prompt,
164
+ initial_response,
165
+ tool_call_data,
166
+ tool_output,
167
+ self.retries,
168
+ self.retry_delay,
169
+ )
170
+ # logging.info(f"Final AI response: {final_response}")
171
+
172
+ self.last_response.update(dict(text=final_response))
173
+ self.conversation.update_chat_history(
174
+ prompt, self.get_message(self.last_response)
175
+ )
176
+ return self.last_response
177
+
178
+ def _get_initial_response(self, prompt: str, retries: int, retry_delay: int) -> str:
179
+ for attempt in range(retries + 1):
180
+ try:
181
+ prompt = self._generate_system_message(prompt)
182
+ data = {
183
+ "stream": False,
184
+ "max_tokens": self.max_tokens_to_sample,
185
+ "top_p": self.top_p,
186
+ "temperature": self.temperature,
187
+ "messages": [{"content": prompt, "role": "user"}],
188
+ "model": self.model,
189
+ }
190
+
191
+ response = self.session.post(
192
+ self.chat_endpoint,
193
+ headers=self.headers,
194
+ cookies=self.cookies,
195
+ json=data,
196
+ timeout=self.timeout,
197
197
  )
198
- resp = response.json()
199
- self.last_response.update(dict(text=resp['choices'][0]['message']['content']))
200
- self.conversation.update_chat_history(
201
- prompt, self.get_message(self.last_response)
202
- )
203
- return self.last_response
198
+ if not response.ok:
199
+ raise exceptions.FailedToGenerateResponseError(
200
+ f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
201
+ )
202
+ return response.json()["choices"][0]["message"]["content"]
203
+ except exceptions.FailedToGenerateResponseError as e:
204
+ if attempt < retries:
205
+ # logging.warning(f"API request failed: {e}. Retrying in {retry_delay} seconds...")
206
+ time.sleep(retry_delay)
207
+ else:
208
+ raise e
209
+ except Exception as e:
210
+ if attempt < retries:
211
+ # logging.warning(
212
+ # f"An unexpected error occurred: {e}. Retrying in {retry_delay} seconds..."
213
+ # )
214
+ time.sleep(retry_delay)
215
+ else:
216
+ raise e
204
217
 
205
- return for_stream() if stream else for_non_stream()
218
+ def _get_final_response(
219
+ self,
220
+ prompt: str,
221
+ initial_response: str,
222
+ tool_call_data: Optional[Dict],
223
+ tool_output: Optional[str],
224
+ retries: int,
225
+ retry_delay: int,
226
+ ) -> str:
227
+ for attempt in range(retries + 1):
228
+ try:
229
+ data = {
230
+ "stream": False,
231
+ "max_tokens": self.max_tokens_to_sample,
232
+ "top_p": self.top_p,
233
+ "temperature": self.temperature,
234
+ "messages": [{"content": prompt, "role": "user"}],
235
+ "model": self.model,
236
+ }
237
+
238
+ if tool_output:
239
+ tool_call = ToolCall(**tool_call_data)
240
+ tool_description = self.tool_registry.get_tool_description(tool_call.tool_name)
241
+ final_prompt = (
242
+ f"I asked you to answer this question: '{prompt}'\n\n"
243
+ f"To assist in answering, you used the '{tool_call.tool_name}' tool, which {tool_description}\n"
244
+ f"The tool was called with these parameters: {json.dumps(tool_call.tool_input)}\n"
245
+ f"The tool provided this output:\n\n{tool_output}\n\n"
246
+ "Based on the original question and the tool's output, please provide a comprehensive and accurate answer. "
247
+ "Make sure to:\n"
248
+ "1. Directly address the user's question\n"
249
+ "2. Incorporate relevant information from the tool's output\n"
250
+ "3. Provide context or explanations where necessary\n"
251
+ "4. If the tool output doesn't fully answer the question, supplement with your general knowledge\n"
252
+ "5. If the tool output is an error message, acknowledge it and try to provide a helpful response based on your general knowledge\n\n"
253
+ "Your response:"
254
+ )
255
+ data["messages"][0]["content"] = final_prompt
256
+ else:
257
+ data["messages"][0]["content"] = prompt
258
+
259
+ response = self.session.post(
260
+ self.chat_endpoint,
261
+ headers=self.headers,
262
+ cookies=self.cookies,
263
+ json=data,
264
+ timeout=self.timeout,
265
+ )
266
+ if not response.ok:
267
+ raise exceptions.FailedToGenerateResponseError(
268
+ f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
269
+ )
270
+ return response.json()["choices"][0]["message"]["content"]
271
+ except exceptions.FailedToGenerateResponseError as e:
272
+ if attempt < retries:
273
+ # logging.warning(
274
+ # f"API request failed: {e}. Retrying in {retry_delay} seconds..."
275
+ # )
276
+ time.sleep(retry_delay)
277
+ else:
278
+ raise e
279
+ except Exception as e:
280
+ if attempt < retries:
281
+ # logging.warning(
282
+ # f"An unexpected error occurred: {e}. Retrying in {retry_delay} seconds..."
283
+ # )
284
+ time.sleep(retry_delay)
285
+ else:
286
+ raise e
206
287
 
207
288
  def chat(
208
289
  self,
@@ -211,16 +292,6 @@ class YEPCHAT(Provider):
211
292
  optimizer: str = None,
212
293
  conversationally: bool = False,
213
294
  ) -> str:
214
- """Generate response `str`
215
- Args:
216
- prompt (str): Prompt to be send.
217
- stream (bool, optional): Flag for streaming response. Defaults to False.
218
- optimizer (str, optional): Prompt optimizer name - `[code, shell_command]`. Defaults to None.
219
- conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
220
- Returns:
221
- str: Response generated
222
- """
223
-
224
295
  def for_stream():
225
296
  for response in self.ask(
226
297
  prompt, True, optimizer=optimizer, conversationally=conversationally
@@ -240,19 +311,190 @@ class YEPCHAT(Provider):
240
311
  return for_stream() if stream else for_non_stream()
241
312
 
242
313
  def get_message(self, response: dict) -> str:
243
- """Retrieves message only from response
314
+ assert isinstance(response, dict)
315
+ return response["text"]
316
+
317
+ def _generate_system_message(self, user_message: str) -> str:
318
+ tools_description = ""
319
+ for name, tool_data in self.tool_registry.tools.items():
320
+ description = tool_data["description"]
321
+ parameters = tool_data.get("parameters")
322
+ if parameters:
323
+ params_str = ", ".join(
324
+ f"{param_name}: {param_desc['description']}"
325
+ for param_name, param_desc in parameters["properties"].items()
326
+ )
327
+ tools_description += f"- **{name}({params_str}):** {description}\n"
328
+ else:
329
+ tools_description += f"- **{name}()**: {description}\n"
330
+
331
+ current_date = date.today().strftime("%B %d, %Y")
332
+ return (
333
+ f"Today's date is {current_date}. Your knowledge cutoff is {self.knowledge_cutoff}.\n"
334
+ "You are a helpful AI assistant designed to assist users with their questions. "
335
+ "You have access to a set of tools that can help you provide more accurate and informative answers. "
336
+ f"Here is a list of the available tools and their functions:\n{tools_description}\n\n"
337
+ "**Instructions:**\n"
338
+ "1. Carefully analyze the user's request to understand their intent.\n"
339
+ "2. Determine if any of the provided tools can be used to fulfill the request.\n"
340
+ "3. If a tool can be used, choose the MOST APPROPRIATE tool. Don't choose a tool if it's not relevant to the request.\n"
341
+ "4. If you decide to use a tool, provide your response ONLY in the following JSON format:\n"
342
+ " ```json\n"
343
+ " {{\n"
344
+ ' "tool_name": "name_of_the_tool",\n'
345
+ ' "tool_input": {{\n'
346
+ ' "parameter1": "value1",\n'
347
+ ' "parameter2": "value2"\n'
348
+ ' }}\n'
349
+ " }}\n"
350
+ " ```\n"
351
+ " - Replace 'name_of_the_tool' with the actual name of the tool.\n"
352
+ " - Replace 'parameter1', 'parameter2', etc., with the actual parameters of the tool, along with their corresponding values.\n"
353
+ " - Do NOT include any explanations or additional text within the JSON response.\n"
354
+ "5. If you determine that no tool is needed to answer the user's question, respond with the following JSON:\n"
355
+ " ```json\n"
356
+ " {{\n"
357
+ ' "tool_name": "general_ai",\n'
358
+ ' "tool_input": "None"\n'
359
+ " }}\n"
360
+ " ```\n"
361
+ f"User Request: {user_message}\n"
362
+ "Your Response (JSON only):"
363
+ )
364
+
365
+ def _parse_function_call(self, response: str) -> dict:
366
+ try:
367
+ parsed_response = json.loads(response)
368
+ if isinstance(parsed_response, dict) and "tool_name" in parsed_response:
369
+ # Ensure tool_input is a dictionary
370
+ if "tool_input" not in parsed_response or not isinstance(parsed_response["tool_input"], dict):
371
+ parsed_response["tool_input"] = {"query": parsed_response.get("tool_input", "")}
372
+ return parsed_response
373
+ except json.JSONDecodeError:
374
+ pass
375
+
376
+ # If JSON parsing fails or doesn't contain expected structure, try to extract JSON from the response
377
+ start_idx = response.find("{")
378
+ end_idx = response.rfind("}") + 1
379
+ if start_idx != -1 and end_idx != -1:
380
+ try:
381
+ parsed_response = json.loads(response[start_idx:end_idx])
382
+ if "tool_name" in parsed_response:
383
+ # Ensure tool_input is a dictionary
384
+ if "tool_input" not in parsed_response or not isinstance(parsed_response["tool_input"], dict):
385
+ parsed_response["tool_input"] = {"query": parsed_response.get("tool_input", "")}
386
+ return parsed_response
387
+ except json.JSONDecodeError:
388
+ pass
389
+ # logging.error("Error parsing tool call: Invalid JSON structure.")
390
+
391
+ # logging.error("Error parsing tool call: No valid JSON structure found.")
392
+ return {"error": "No valid JSON structure found."}
393
+
394
+ def _should_call_tool(self, response_text: str) -> bool:
395
+ return any(
396
+ tool_name.lower() in response_text.lower()
397
+ for tool_name in self.tool_registry.tools
398
+ )
399
+
400
+ def execute_tool(self, tool_call: ToolCall) -> str:
401
+ tool_name = tool_call.tool_name
402
+ tool_input = tool_call.tool_input
403
+
404
+ if tool_name == "general_ai":
405
+ return tool_input
406
+
407
+ tool_function = self.tool_registry.get_tool(tool_name)
408
+ if tool_function:
409
+ try:
410
+ parameters = self.tool_registry.get_tool_parameters(tool_name)
411
+ if parameters:
412
+ # If the tool expects parameters, pass them as keyword arguments
413
+ tool_output = tool_function(**tool_input)
414
+ else:
415
+ # If the tool doesn't expect parameters, call it without arguments
416
+ tool_output = tool_function()
417
+ return tool_output
418
+ except Exception as e:
419
+ # logging.error(f"Error executing tool {tool_name}: {e}")
420
+ return f"Error executing tool {tool_name}: {e}"
421
+ else:
422
+ return f"Tool '{tool_name}' not found."
423
+
424
+
425
+ if __name__ == "__main__":
426
+ from rich import print
427
+
428
+ def get_current_time():
429
+ """Returns the current time in HH:MM:SS format."""
430
+ return time.strftime("%H:%M:%S")
431
+
432
+ def get_weather(location: str) -> str:
433
+ """
434
+ Gets the current weather for a given location.
244
435
 
245
436
  Args:
246
- response (dict): Response generated by `self.ask`
437
+ location (str): The location for which to retrieve the weather,
438
+ such as a city and state, or a zip code.
439
+ Examples: "London, UK", "90210".
247
440
 
248
441
  Returns:
249
- str: Message extracted
442
+ str: A string describing the current weather in the specified location.
443
+ Note: This is a placeholder and should be replaced with an actual weather API call.
250
444
  """
251
- assert isinstance(response, dict), "Response should be of dict data-type only"
252
- return response["text"]
253
- if __name__ == '__main__':
254
- from rich import print
255
- ai = YEPCHAT()
256
- response = ai.chat("tell me about india")
445
+ return f"The weather in {location} is sunny."
446
+
447
+ def web_search(query: str) -> str:
448
+ """
449
+ Performs a web search and returns the top 3 results.
450
+
451
+ Args:
452
+ query (str): The search query to use.
453
+
454
+ Returns:
455
+ str: A formatted string containing the title, body, and URL of
456
+ the top 3 search results.
457
+ If no results are found, returns "No results found for your query."
458
+ """
459
+ webs = WEBS()
460
+ results = webs.text(query, max_results=3)
461
+ if results:
462
+ formatted_results = "\n\n".join(
463
+ f"**{i+1}. {result['title']}**\n{result['body']}\n[URL: {result['href']}]"
464
+ for i, result in enumerate(results)
465
+ )
466
+ return formatted_results
467
+ else:
468
+ return "No results found for your query."
469
+
470
+ ai = YEPCHAT(Tools=True)
471
+
472
+ ai.tool_registry.register_tool("get_current_time", get_current_time, "Gets the current time.")
473
+ ai.tool_registry.register_tool(
474
+ "get_weather",
475
+ get_weather,
476
+ "Gets the weather for a given location.",
477
+ parameters={
478
+ "type": "object",
479
+ "properties": {
480
+ "location": {"type": "string", "description": "The city and state, or zip code"}
481
+ },
482
+ "required": ["location"],
483
+ },
484
+ )
485
+ ai.tool_registry.register_tool(
486
+ "web_search",
487
+ web_search,
488
+ "Searches the web for information.",
489
+ parameters={
490
+ "type": "object",
491
+ "properties": {
492
+ "query": {"type": "string", "description": "The search query."}
493
+ },
494
+ "required": ["query"],
495
+ },
496
+ )
497
+
498
+ response = ai.chat(input(">>> "))
257
499
  for chunk in response:
258
500
  print(chunk, end="", flush=True)
webscout/YTdownloader.py CHANGED
@@ -11,7 +11,7 @@ from threading import Thread
11
11
  from sys import stdout
12
12
  from click import launch as launch_media, confirm as confirm_from_user
13
13
  import warnings
14
-
14
+ from webscout.version import __prog__, __version__
15
15
  from os import getcwd, remove
16
16
  import appdirs
17
17
  """
@@ -26,8 +26,7 @@ import appdirs
26
26
  - max-video quality
27
27
  - path to file containing links
28
28
  """
29
- __version__ = "4.0"
30
- __prog__ = "webscout"
29
+
31
30
  session = requests.session()
32
31
 
33
32
  headers = {
webscout/__init__.py CHANGED
@@ -2,8 +2,8 @@ from .webscout_search import WEBS
2
2
  from .webscout_search_async import AsyncWEBS
3
3
  from .version import __version__
4
4
  from .DWEBS import *
5
- from .transcriber import transcriber
6
- from .voice import play_audio
5
+ from .transcriber import *
6
+ from .tempid import *
7
7
  from .websx_search import WEBSX
8
8
  from .LLM import VLM, LLM
9
9
  from .YTdownloader import *
webscout/exceptions.py CHANGED
@@ -21,4 +21,5 @@ class FacebookInvalidCredentialsException(Exception):
21
21
 
22
22
 
23
23
  class FacebookRegionBlocked(Exception):
24
- pass
24
+ pass
25
+