webscout 8.3__py3-none-any.whl → 8.3.2__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 (120) hide show
  1. webscout/AIauto.py +4 -4
  2. webscout/AIbase.py +61 -1
  3. webscout/AIutel.py +46 -53
  4. webscout/Bing_search.py +418 -0
  5. webscout/Extra/YTToolkit/ytapi/patterns.py +45 -45
  6. webscout/Extra/YTToolkit/ytapi/stream.py +1 -1
  7. webscout/Extra/YTToolkit/ytapi/video.py +10 -10
  8. webscout/Extra/autocoder/autocoder_utiles.py +1 -1
  9. webscout/Extra/gguf.py +706 -177
  10. webscout/Litlogger/formats.py +9 -0
  11. webscout/Litlogger/handlers.py +18 -0
  12. webscout/Litlogger/logger.py +43 -1
  13. webscout/Provider/AISEARCH/genspark_search.py +7 -7
  14. webscout/Provider/AISEARCH/scira_search.py +3 -2
  15. webscout/Provider/GeminiProxy.py +140 -0
  16. webscout/Provider/LambdaChat.py +7 -1
  17. webscout/Provider/MCPCore.py +78 -75
  18. webscout/Provider/OPENAI/BLACKBOXAI.py +1046 -1017
  19. webscout/Provider/OPENAI/GeminiProxy.py +328 -0
  20. webscout/Provider/OPENAI/Qwen3.py +303 -303
  21. webscout/Provider/OPENAI/README.md +5 -0
  22. webscout/Provider/OPENAI/README_AUTOPROXY.md +238 -0
  23. webscout/Provider/OPENAI/TogetherAI.py +355 -0
  24. webscout/Provider/OPENAI/__init__.py +16 -1
  25. webscout/Provider/OPENAI/autoproxy.py +332 -0
  26. webscout/Provider/OPENAI/base.py +101 -14
  27. webscout/Provider/OPENAI/chatgpt.py +15 -2
  28. webscout/Provider/OPENAI/chatgptclone.py +14 -3
  29. webscout/Provider/OPENAI/deepinfra.py +339 -328
  30. webscout/Provider/OPENAI/e2b.py +295 -74
  31. webscout/Provider/OPENAI/mcpcore.py +109 -70
  32. webscout/Provider/OPENAI/opkfc.py +18 -6
  33. webscout/Provider/OPENAI/scirachat.py +59 -50
  34. webscout/Provider/OPENAI/toolbaz.py +2 -10
  35. webscout/Provider/OPENAI/writecream.py +166 -166
  36. webscout/Provider/OPENAI/x0gpt.py +367 -367
  37. webscout/Provider/OPENAI/xenai.py +514 -0
  38. webscout/Provider/OPENAI/yep.py +389 -383
  39. webscout/Provider/STT/__init__.py +3 -0
  40. webscout/Provider/STT/base.py +281 -0
  41. webscout/Provider/STT/elevenlabs.py +265 -0
  42. webscout/Provider/TTI/__init__.py +4 -1
  43. webscout/Provider/TTI/aiarta.py +399 -365
  44. webscout/Provider/TTI/base.py +74 -2
  45. webscout/Provider/TTI/bing.py +231 -0
  46. webscout/Provider/TTI/fastflux.py +63 -30
  47. webscout/Provider/TTI/gpt1image.py +149 -0
  48. webscout/Provider/TTI/imagen.py +196 -0
  49. webscout/Provider/TTI/magicstudio.py +60 -29
  50. webscout/Provider/TTI/piclumen.py +43 -32
  51. webscout/Provider/TTI/pixelmuse.py +232 -225
  52. webscout/Provider/TTI/pollinations.py +43 -32
  53. webscout/Provider/TTI/together.py +287 -0
  54. webscout/Provider/TTI/utils.py +2 -1
  55. webscout/Provider/TTS/README.md +1 -0
  56. webscout/Provider/TTS/__init__.py +2 -1
  57. webscout/Provider/TTS/freetts.py +140 -0
  58. webscout/Provider/TTS/speechma.py +45 -39
  59. webscout/Provider/TogetherAI.py +366 -0
  60. webscout/Provider/UNFINISHED/ChutesAI.py +314 -0
  61. webscout/Provider/UNFINISHED/fetch_together_models.py +95 -0
  62. webscout/Provider/XenAI.py +324 -0
  63. webscout/Provider/__init__.py +8 -0
  64. webscout/Provider/deepseek_assistant.py +378 -0
  65. webscout/Provider/scira_chat.py +3 -2
  66. webscout/Provider/toolbaz.py +0 -1
  67. webscout/auth/__init__.py +44 -0
  68. webscout/auth/api_key_manager.py +189 -0
  69. webscout/auth/auth_system.py +100 -0
  70. webscout/auth/config.py +76 -0
  71. webscout/auth/database.py +400 -0
  72. webscout/auth/exceptions.py +67 -0
  73. webscout/auth/middleware.py +248 -0
  74. webscout/auth/models.py +130 -0
  75. webscout/auth/providers.py +257 -0
  76. webscout/auth/rate_limiter.py +254 -0
  77. webscout/auth/request_models.py +127 -0
  78. webscout/auth/request_processing.py +226 -0
  79. webscout/auth/routes.py +526 -0
  80. webscout/auth/schemas.py +103 -0
  81. webscout/auth/server.py +312 -0
  82. webscout/auth/static/favicon.svg +11 -0
  83. webscout/auth/swagger_ui.py +203 -0
  84. webscout/auth/templates/components/authentication.html +237 -0
  85. webscout/auth/templates/components/base.html +103 -0
  86. webscout/auth/templates/components/endpoints.html +750 -0
  87. webscout/auth/templates/components/examples.html +491 -0
  88. webscout/auth/templates/components/footer.html +75 -0
  89. webscout/auth/templates/components/header.html +27 -0
  90. webscout/auth/templates/components/models.html +286 -0
  91. webscout/auth/templates/components/navigation.html +70 -0
  92. webscout/auth/templates/static/api.js +455 -0
  93. webscout/auth/templates/static/icons.js +168 -0
  94. webscout/auth/templates/static/main.js +784 -0
  95. webscout/auth/templates/static/particles.js +201 -0
  96. webscout/auth/templates/static/styles.css +3353 -0
  97. webscout/auth/templates/static/ui.js +374 -0
  98. webscout/auth/templates/swagger_ui.html +170 -0
  99. webscout/client.py +49 -3
  100. webscout/litagent/Readme.md +12 -3
  101. webscout/litagent/agent.py +99 -62
  102. webscout/scout/core/scout.py +104 -26
  103. webscout/scout/element.py +139 -18
  104. webscout/swiftcli/core/cli.py +14 -3
  105. webscout/swiftcli/decorators/output.py +59 -9
  106. webscout/update_checker.py +31 -49
  107. webscout/version.py +1 -1
  108. webscout/webscout_search.py +4 -12
  109. webscout/webscout_search_async.py +3 -10
  110. webscout/yep_search.py +2 -11
  111. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/METADATA +41 -11
  112. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/RECORD +116 -68
  113. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/entry_points.txt +1 -1
  114. webscout/Provider/HF_space/__init__.py +0 -0
  115. webscout/Provider/HF_space/qwen_qwen2.py +0 -206
  116. webscout/Provider/OPENAI/api.py +0 -1035
  117. webscout/Provider/TTI/artbit.py +0 -0
  118. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/WHEEL +0 -0
  119. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/licenses/LICENSE.md +0 -0
  120. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/top_level.txt +0 -0
webscout/scout/element.py CHANGED
@@ -151,7 +151,7 @@ class Tag:
151
151
  """
152
152
  return hash((self.name, frozenset(self.attrs.items()), str(self)))
153
153
 
154
- def find(self, name=None, attrs={}, recursive=True, text=None, **kwargs) -> Optional['Tag']:
154
+ def find(self, name=None, attrs={}, recursive=True, text=None, limit=None, class_=None, **kwargs) -> Optional['Tag']:
155
155
  """
156
156
  Find the first matching child element.
157
157
  Enhanced with more flexible matching.
@@ -165,10 +165,26 @@ class Tag:
165
165
  Returns:
166
166
  Tag or None: First matching element
167
167
  """
168
+ # Merge class_ with attrs['class'] if both are present
169
+ attrs = dict(attrs) if attrs else {}
170
+ if class_ is not None:
171
+ if 'class' in attrs:
172
+ # Merge both
173
+ if isinstance(attrs['class'], list):
174
+ class_list = attrs['class']
175
+ else:
176
+ class_list = [cls.strip() for cls in re.split(r'[ ,]+', str(attrs['class'])) if cls.strip()]
177
+ if isinstance(class_, list):
178
+ class_list += class_
179
+ else:
180
+ class_list += [cls.strip() for cls in re.split(r'[ ,]+', str(class_)) if cls.strip()]
181
+ attrs['class'] = class_list
182
+ else:
183
+ attrs['class'] = class_
168
184
  results = self.find_all(name, attrs, recursive, text, limit=1, **kwargs)
169
185
  return results[0] if results else None
170
186
 
171
- def find_all(self, name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs) -> List['Tag']:
187
+ def find_all(self, name=None, attrs={}, recursive=True, text=None, limit=None, class_=None, **kwargs) -> List['Tag']:
172
188
  """
173
189
  Find all matching child elements.
174
190
  Enhanced with more flexible matching and BeautifulSoup-like features.
@@ -197,12 +213,17 @@ class Tag:
197
213
 
198
214
  # Check attributes with more flexible matching
199
215
  for k, v in attrs.items():
200
- # Handle special attribute matching
201
216
  if k == 'class':
202
217
  tag_classes = tag.get('class', [])
203
- if isinstance(v, str) and v not in tag_classes:
204
- return False
205
- elif isinstance(v, list) and not all(cls in tag_classes for cls in v):
218
+ # Support multiple classes separated by space or comma
219
+ if isinstance(v, str):
220
+ v_classes = [cls.strip() for cls in re.split(r'[ ,]+', v) if cls.strip()]
221
+ if not all(cls in tag_classes for cls in v_classes):
222
+ return False
223
+ elif isinstance(v, list):
224
+ if not all(cls in tag_classes for cls in v):
225
+ return False
226
+ else:
206
227
  return False
207
228
  elif k == 'id':
208
229
  if tag.get('id') != v:
@@ -412,31 +433,131 @@ class Tag:
412
433
  """Remove all contents of the tag."""
413
434
  self.contents.clear()
414
435
 
436
+ @property
437
+ def string(self):
438
+ """
439
+ Get the string content of the tag.
440
+ Returns the combined text of the tag's contents.
441
+ """
442
+ return self.get_text()
443
+
444
+ @string.setter
445
+ def string(self, value):
446
+ """
447
+ Set the string content of the tag.
448
+ Clears existing contents and sets new string value.
449
+
450
+ Args:
451
+ value (str): New string content
452
+ """
453
+ self.clear()
454
+ if value is not None:
455
+ self.append(value)
456
+
415
457
  def append(self, new_child: Union['Tag', NavigableString, str]) -> None:
416
- """Append a new child to this tag."""
458
+ """Append a new child to this tag with error handling."""
417
459
  if isinstance(new_child, str):
418
460
  new_child = NavigableString(new_child)
419
- new_child.parent = self
461
+ if hasattr(new_child, 'parent'):
462
+ new_child.parent = self
420
463
  self.contents.append(new_child)
421
464
 
422
465
  def insert(self, index: int, new_child: Union['Tag', NavigableString, str]) -> None:
423
- """Insert a new child at the given index."""
466
+ """Insert a new child at the given index with error handling."""
424
467
  if isinstance(new_child, str):
425
468
  new_child = NavigableString(new_child)
426
- new_child.parent = self
469
+ if hasattr(new_child, 'parent'):
470
+ new_child.parent = self
427
471
  self.contents.insert(index, new_child)
428
472
 
429
473
  def replace_with(self, new_tag: 'Tag') -> None:
430
- """
431
- Replace this tag with another tag.
474
+ """Replace this tag with another tag with error handling."""
475
+ if self.parent:
476
+ try:
477
+ index = self.parent.contents.index(self)
478
+ self.parent.contents[index] = new_tag
479
+ new_tag.parent = self.parent
480
+ except ValueError:
481
+ pass
482
+
483
+ def wrap(self, wrapper_tag: 'Tag') -> 'Tag':
484
+ """Wrap this tag in another tag."""
485
+ if self.parent:
486
+ idx = self.parent.contents.index(self)
487
+ self.parent.contents[idx] = wrapper_tag
488
+ wrapper_tag.parent = self.parent
489
+ else:
490
+ wrapper_tag.parent = None
491
+ wrapper_tag.contents.append(self)
492
+ self.parent = wrapper_tag
493
+ return wrapper_tag
494
+
495
+ def unwrap(self) -> None:
496
+ """Remove this tag but keep its contents in the parent."""
497
+ if self.parent:
498
+ idx = self.parent.contents.index(self)
499
+ for child in reversed(self.contents):
500
+ child.parent = self.parent
501
+ self.parent.contents.insert(idx, child)
502
+ self.parent.contents.remove(self)
503
+ self.parent = None
504
+ self.contents = []
432
505
 
433
- Args:
434
- new_tag (Tag): Tag to replace the current tag
435
- """
506
+ def insert_before(self, new_element: 'Tag') -> None:
507
+ """Insert a tag or string immediately before this tag."""
508
+ if self.parent:
509
+ idx = self.parent.contents.index(self)
510
+ new_element.parent = self.parent
511
+ self.parent.contents.insert(idx, new_element)
512
+
513
+ def insert_after(self, new_element: 'Tag') -> None:
514
+ """Insert a tag or string immediately after this tag."""
436
515
  if self.parent:
437
- index = self.parent.contents.index(self)
438
- self.parent.contents[index] = new_tag
439
- new_tag.parent = self.parent
516
+ idx = self.parent.contents.index(self)
517
+ new_element.parent = self.parent
518
+ self.parent.contents.insert(idx + 1, new_element)
519
+
520
+ @property
521
+ def descendants(self):
522
+ """Yield all descendants in document order."""
523
+ for child in self.contents:
524
+ yield child
525
+ if isinstance(child, Tag):
526
+ yield from child.descendants
527
+
528
+ @property
529
+ def parents(self):
530
+ """Yield all parents up the tree."""
531
+ current = self.parent
532
+ while current:
533
+ yield current
534
+ current = current.parent
535
+
536
+ @property
537
+ def next_element(self):
538
+ """Return the next element in document order."""
539
+ if self.contents:
540
+ return self.contents[0]
541
+ current = self
542
+ while current.parent:
543
+ idx = current.parent.contents.index(current)
544
+ if idx + 1 < len(current.parent.contents):
545
+ return current.parent.contents[idx + 1]
546
+ current = current.parent
547
+ return None
548
+
549
+ @property
550
+ def previous_element(self):
551
+ """Return the previous element in document order."""
552
+ if not self.parent:
553
+ return None
554
+ idx = self.parent.contents.index(self)
555
+ if idx > 0:
556
+ prev = self.parent.contents[idx - 1]
557
+ while isinstance(prev, Tag) and prev.contents:
558
+ prev = prev.contents[-1]
559
+ return prev
560
+ return self.parent
440
561
 
441
562
  def decode_contents(self, eventual_encoding='utf-8') -> str:
442
563
  """
@@ -223,9 +223,20 @@ class CLI:
223
223
  # Handle options
224
224
  if hasattr(func, '_options'):
225
225
  for opt in func._options:
226
- name = opt['param_decls'][0].lstrip('-').replace('-', '_')
227
- if name in parsed_args:
228
- value = parsed_args[name]
226
+ # Use the longest parameter name (usually the --long-form) for the parameter name
227
+ param_names = [p.lstrip('-').replace('-', '_') for p in opt['param_decls']]
228
+ name = max(param_names, key=len) # Use the longest name
229
+
230
+ # Check all possible parameter names in parsed args
231
+ value = None
232
+ found = False
233
+ for param_name in param_names:
234
+ if param_name in parsed_args:
235
+ value = parsed_args[param_name]
236
+ found = True
237
+ break
238
+
239
+ if found:
229
240
  if 'type' in opt:
230
241
  value = convert_type(value, opt['type'], name)
231
242
  if 'choices' in opt and opt['choices']:
@@ -5,16 +5,62 @@ from typing import Any, Callable, List, Optional, Union
5
5
 
6
6
  from rich.console import Console
7
7
  from rich.table import Table
8
- from rich.progress import (
9
- Progress,
10
- SpinnerColumn,
11
- TextColumn,
12
- BarColumn,
13
- TaskProgressColumn,
14
- TimeRemainingColumn
15
- )
16
8
  from rich.panel import Panel
17
9
 
10
+ # Handle different versions of rich
11
+ try:
12
+ from rich.progress import (
13
+ Progress,
14
+ SpinnerColumn,
15
+ TextColumn,
16
+ BarColumn,
17
+ TaskProgressColumn,
18
+ TimeRemainingColumn
19
+ )
20
+ except ImportError:
21
+ # Fallback for older versions of rich
22
+ try:
23
+ from rich.progress import (
24
+ Progress,
25
+ SpinnerColumn,
26
+ TextColumn,
27
+ BarColumn,
28
+ TimeRemainingColumn
29
+ )
30
+ # Create a simple TaskProgressColumn replacement for older versions
31
+ class TaskProgressColumn:
32
+ def __init__(self):
33
+ pass
34
+
35
+ def __call__(self, task):
36
+ return f"{task.percentage:.1f}%"
37
+
38
+ except ImportError:
39
+ # If rich is too old, create minimal fallbacks
40
+ class Progress:
41
+ def __init__(self, *args, **kwargs):
42
+ pass
43
+ def __enter__(self):
44
+ return self
45
+ def __exit__(self, *args):
46
+ pass
47
+ def add_task(self, description, total=None):
48
+ return 0
49
+ def update(self, task_id, **kwargs):
50
+ pass
51
+
52
+ class SpinnerColumn:
53
+ pass
54
+ class TextColumn:
55
+ def __init__(self, text):
56
+ pass
57
+ class BarColumn:
58
+ pass
59
+ class TaskProgressColumn:
60
+ pass
61
+ class TimeRemainingColumn:
62
+ pass
63
+
18
64
  console = Console()
19
65
 
20
66
  def table_output(
@@ -111,7 +157,11 @@ def progress(
111
157
  if show_bar:
112
158
  columns.append(BarColumn())
113
159
  if show_percentage:
114
- columns.append(TaskProgressColumn())
160
+ try:
161
+ columns.append(TaskProgressColumn())
162
+ except:
163
+ # Fallback for older rich versions
164
+ columns.append(TextColumn("[progress.percentage]{task.percentage:>3.0f}%"))
115
165
  if show_time:
116
166
  columns.append(TimeRemainingColumn())
117
167
 
@@ -1,21 +1,11 @@
1
- from pkg_resources import get_distribution, DistributionNotFound as PackageNotFoundError
2
- def get_package_version(package_name: str) -> str:
3
- return get_distribution(package_name).version
4
- """
5
- Webscout Update Checker
6
- >>> from webscout import check_for_updates
7
- >>> result = check_for_updates()
8
- >>> print(result)
9
- 'New Webscout version available: 2.0.0 - Update with: pip install --upgrade webscout'
10
- """
11
-
1
+ import importlib.metadata
12
2
  from typing import Optional, Dict, Any, Literal
13
-
14
3
  import requests
15
4
  from packaging import version
16
5
 
17
6
  # Constants
18
7
  PYPI_URL = "https://pypi.org/pypi/webscout/json"
8
+ YOUTUBE_URL = "https://youtube.com/@OEvortex"
19
9
 
20
10
  # Create a session for HTTP requests
21
11
  session = requests.Session()
@@ -23,6 +13,13 @@ session = requests.Session()
23
13
  # Version comparison result type
24
14
  VersionCompareResult = Literal[-1, 0, 1]
25
15
 
16
+ def get_package_version(package_name: str) -> Optional[str]:
17
+ """Get the installed version of a package by name."""
18
+ try:
19
+ return importlib.metadata.version(package_name)
20
+ except importlib.metadata.PackageNotFoundError:
21
+ return None
22
+
26
23
  def get_installed_version() -> Optional[str]:
27
24
  """Get the currently installed version of webscout.
28
25
 
@@ -34,10 +31,7 @@ def get_installed_version() -> Optional[str]:
34
31
  >>> print(version)
35
32
  '1.2.3'
36
33
  """
37
- try:
38
- return get_package_version('webscout')
39
- except PackageNotFoundError:
40
- return None
34
+ return get_package_version('webscout')
41
35
 
42
36
  def get_pypi_version() -> Optional[str]:
43
37
  """Get the latest version available on PyPI.
@@ -54,19 +48,15 @@ def get_pypi_version() -> Optional[str]:
54
48
  response = session.get(PYPI_URL, timeout=10)
55
49
  response.raise_for_status()
56
50
  data: Dict[str, Any] = response.json()
57
- return data['info']['version']
51
+ return data.get('info', {}).get('version')
58
52
  except (requests.RequestException, KeyError, ValueError):
59
53
  return None
60
54
 
61
55
  def version_compare(v1: str, v2: str) -> VersionCompareResult:
62
56
  """Compare two version strings.
63
57
 
64
- Args:
65
- v1: First version string
66
- v2: Second version string to compare with
67
-
68
58
  Returns:
69
- VersionCompareResult: -1 if v1 < v2, 1 if v1 > v2, 0 if equal
59
+ -1 if v1 < v2, 1 if v1 > v2, 0 if equal or invalid
70
60
 
71
61
  Examples:
72
62
  >>> version_compare('1.0.0', '2.0.0')
@@ -80,48 +70,40 @@ def version_compare(v1: str, v2: str) -> VersionCompareResult:
80
70
  if version1 > version2:
81
71
  return 1
82
72
  return 0
83
- except version.InvalidVersion:
73
+ except Exception:
84
74
  return 0
85
75
 
86
- def get_update_message(installed: str, latest: str) -> Optional[str]:
87
- """Generate appropriate update message based on version comparison.
88
-
89
- Args:
90
- installed: Currently installed version
91
- latest: Latest available version
92
-
93
- Returns:
94
- Optional[str]: Update message if needed, None if already on latest version
95
-
96
- Examples:
97
- >>> get_update_message('1.0.0', '2.0.0')
98
- 'New Webscout version available: 2.0.0 - Update with: pip install --upgrade webscout'
99
- """
76
+ def get_update_message(installed: str, latest: str) -> str:
77
+ """Generate appropriate update message based on version comparison."""
100
78
  comparison_result = version_compare(installed, latest)
79
+ youtube_msg = f'\033[1;32mSubscribe to my YouTube Channel: {YOUTUBE_URL}\033[0m'
101
80
  if comparison_result < 0:
102
- return f"New Webscout version available: {latest} - Update with: pip install --upgrade webscout"
81
+ # Bold red for update available
82
+ return (
83
+ f"\033[1;31mNew Webscout version available: {latest} "
84
+ f"- Update with: pip install --upgrade webscout\033[0m\n{youtube_msg}"
85
+ )
103
86
  elif comparison_result > 0:
104
- return f"You're running a development version ({installed}) ahead of latest release ({latest})"
105
- return None # Already on the latest version
87
+ # Bold yellow for dev version
88
+ return (
89
+ f"\033[1;33mYou're running a development version ({installed}) "
90
+ f"ahead of latest release ({latest})\033[0m\n{youtube_msg}"
91
+ )
92
+ # Bold green for up-to-date
106
93
 
107
94
  def check_for_updates() -> Optional[str]:
108
95
  """Check if a newer version of Webscout is available.
109
96
 
110
97
  Returns:
111
98
  Optional[str]: Update message if newer version exists, None otherwise
112
-
113
- Examples:
114
- >>> result = check_for_updates()
115
- >>> print(result)
116
- 'New Webscout version available: 2.0.0 - Update with: pip install --upgrade webscout'
117
99
  """
118
100
  installed_version = get_installed_version()
119
101
  if not installed_version:
120
- return None
102
+ return "\033[1;31mWebscout is not installed.\033[0m"
121
103
 
122
104
  latest_version = get_pypi_version()
123
105
  if not latest_version:
124
- return None
106
+ return "\033[1;31mCould not retrieve latest version from PyPI.\033[0m"
125
107
 
126
108
  return get_update_message(installed_version, latest_version)
127
109
 
@@ -131,6 +113,6 @@ if __name__ == "__main__":
131
113
  if update_message:
132
114
  print(update_message)
133
115
  except KeyboardInterrupt:
134
- print("Update check canceled")
116
+ print("\nUpdate check canceled by user.")
135
117
  except Exception as e:
136
- print(f"Update check failed: {str(e)}")
118
+ print(f"\033[1;31mUpdate check failed: {str(e)}\033[0m")
webscout/version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "8.3"
1
+ __version__ = "8.3.2"
2
2
  __prog__ = "webscout"
@@ -16,7 +16,7 @@ from types import TracebackType
16
16
  from typing import Any, cast
17
17
  import os
18
18
  from typing import Literal, Iterator
19
-
19
+ from webscout.litagent import LitAgent
20
20
  import curl_cffi.requests # type: ignore
21
21
 
22
22
  try:
@@ -83,17 +83,9 @@ class WEBS:
83
83
  self.proxy = proxies.get("http") or proxies.get("https") if isinstance(proxies, dict) else proxies
84
84
 
85
85
  default_headers = {
86
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
87
- "Accept-Language": "en-US,en;q=0.5",
88
- "Accept-Encoding": "gzip, deflate, br",
89
- "DNT": "1",
90
- "Connection": "keep-alive",
91
- "Upgrade-Insecure-Requests": "1",
92
- "Sec-Fetch-Dest": "document",
93
- "Sec-Fetch-Mode": "navigate",
94
- "Sec-Fetch-Site": "none",
95
- "Sec-Fetch-User": "?1",
96
- "Referer": "https://duckduckgo.com/",
86
+ **LitAgent().generate_fingerprint(),
87
+ "Origin": "https://duckduckgo.com",
88
+ "Referer": "https://yep.com/",
97
89
  }
98
90
 
99
91
  self.headers = headers if headers else {}
@@ -16,6 +16,8 @@ from lxml.etree import _Element
16
16
  from lxml.html import HTMLParser as LHTMLParser
17
17
  from lxml.html import document_fromstring
18
18
 
19
+ from webscout.litagent.agent import LitAgent
20
+
19
21
  from .exceptions import ConversationLimitException, RatelimitE, TimeoutE, WebscoutE
20
22
  from .utils import (
21
23
  _expand_proxy_tb_alias,
@@ -74,16 +76,7 @@ class AsyncWEBS:
74
76
  self.proxy = proxies.get("http") or proxies.get("https") if isinstance(proxies, dict) else proxies
75
77
 
76
78
  default_headers = {
77
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
78
- "Accept-Language": "en-US,en;q=0.5",
79
- "Accept-Encoding": "gzip, deflate, br",
80
- "DNT": "1",
81
- "Connection": "keep-alive",
82
- "Upgrade-Insecure-Requests": "1",
83
- "Sec-Fetch-Dest": "document",
84
- "Sec-Fetch-Mode": "navigate",
85
- "Sec-Fetch-Site": "none",
86
- "Sec-Fetch-User": "?1",
79
+ **LitAgent().generate_fingerprint(),
87
80
  "Referer": "https://duckduckgo.com/",
88
81
  }
89
82
 
webscout/yep_search.py CHANGED
@@ -35,20 +35,11 @@ class YepSearch:
35
35
  timeout=timeout # Set timeout directly in session
36
36
  )
37
37
  self.session.headers.update({
38
- "Accept": "*/*",
39
- "Accept-Language": "en-US,en;q=0.9,en-IN;q=0.8",
40
- "DNT": "1",
38
+ **LitAgent().generate_fingerprint(),
41
39
  "Origin": "https://yep.com",
42
40
  "Referer": "https://yep.com/",
43
- # Sec-Ch-Ua headers are often handled by impersonate, but keeping them might be safer
44
- "Sec-Ch-Ua": '"Not(A:Brand";v="99", "Microsoft Edge";v="133", "Chromium";v="133"',
45
- "Sec-Ch-Ua-Mobile": "?0",
46
- "Sec-Ch-Ua-Platform": '"Windows"',
47
- "Sec-Fetch-Dest": "empty",
48
- "Sec-Fetch-Mode": "cors",
49
- "Sec-Fetch-Site": "same-site",
50
- "User-Agent": LitAgent().random() # Keep custom User-Agent or rely on impersonate
51
41
  })
42
+
52
43
  # Proxies and verify are handled by the Session constructor now
53
44
 
54
45
  def _remove_html_tags(self, text: str) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webscout
3
- Version: 8.3
3
+ Version: 8.3.2
4
4
  Summary: Search for anything using Google, DuckDuckGo, phind.com, Contains AI models, can transcribe yt videos, temporary email and phone number generation, has TTS support, webai (terminal gpt and open interpreter) and offline LLMs and more
5
5
  Author-email: OEvortex <helpingai5@gmail.com>
6
6
  License: HelpingAI
@@ -71,6 +71,8 @@ Requires-Dist: uvicorn[standard]; extra == "api"
71
71
  Requires-Dist: pydantic; extra == "api"
72
72
  Requires-Dist: python-multipart; extra == "api"
73
73
  Requires-Dist: tiktoken; extra == "api"
74
+ Requires-Dist: motor; extra == "api"
75
+ Requires-Dist: jinja2; extra == "api"
74
76
  Dynamic: license-file
75
77
 
76
78
  <div align="center">
@@ -227,14 +229,14 @@ uv add "webscout[api]"
227
229
  uv run webscout --help
228
230
 
229
231
  # Run with API dependencies
230
- uv run --extra api webscout-server
232
+ uv run webscout --extra api webscout-server
231
233
 
232
234
  # Install as a UV tool for global access
233
235
  uv tool install webscout
234
236
 
235
237
  # Use UV tool commands
236
238
  webscout --help
237
- webscout-server --help
239
+ webscout-server
238
240
  ```
239
241
 
240
242
  ### 🔧 Development Installation
@@ -308,7 +310,7 @@ uv run --extra api webscout-server
308
310
  ```bash
309
311
  # Traditional Python module execution
310
312
  python -m webscout --help
311
- python -m webscout.client # Start API server
313
+ python -m webscout-server
312
314
  ```
313
315
 
314
316
  <details open>
@@ -403,13 +405,19 @@ webscout-server --port 8080
403
405
  # Start with API key authentication
404
406
  webscout-server --api-key "your-secret-key"
405
407
 
408
+ # Start in no-auth mode using command line flag (no API key required)
409
+ webscout-server --no-auth
410
+
411
+ # Start in no-auth mode using environment variable
412
+ $env:WEBSCOUT_NO_AUTH='true'; webscout-server
413
+
406
414
  # Specify a default provider
407
415
  webscout-server --default-provider "Claude"
408
416
 
409
417
  # Run in debug mode
410
418
  webscout-server --debug
411
419
 
412
- # Get help for all options
420
+ # Get help for all options (includes authentication options)
413
421
  webscout-server --help
414
422
  ```
415
423
 
@@ -420,17 +428,36 @@ webscout-server --help
420
428
  uv run --extra api webscout-server
421
429
 
422
430
  # Using Python module
423
- python -m webscout.client
431
+ python -m webscout.auth.server
432
+ ```
433
+
434
+ #### Environment Variables
435
+
436
+ Webscout server supports configuration through environment variables:
424
437
 
425
- # Legacy method (still supported)
426
- python -m webscout.Provider.OPENAI.api
438
+ ```bash
439
+ # Start server in no-auth mode (no API key required)
440
+ $env:WEBSCOUT_NO_AUTH='true'; webscout-server
441
+
442
+ # Disable rate limiting
443
+ $env:WEBSCOUT_NO_RATE_LIMIT='true'; webscout-server
444
+
445
+ # Start with custom port using environment variable
446
+ $env:WEBSCOUT_PORT='7860'; webscout-server
427
447
  ```
428
448
 
449
+ For a complete list of supported environment variables and Docker deployment options, see [DOCKER.md](DOCKER.md).
450
+
451
+
429
452
  #### From Python Code
430
453
 
454
+ > **Recommended:**
455
+ > Use `start_server` from `webscout.client` for the simplest programmatic startup.
456
+ > For advanced control (custom host, debug, etc.), use `run_api`.
457
+
431
458
  ```python
432
- # Method 1: Using the helper function
433
- from webscout.client import start_server # <--- Now recommended
459
+ # Method 1: Using the helper function (recommended)
460
+ from webscout.client import start_server
434
461
 
435
462
  # Start with default settings
436
463
  start_server()
@@ -438,7 +465,10 @@ start_server()
438
465
  # Start with custom settings
439
466
  start_server(port=8080, api_key="your-secret-key", default_provider="Claude")
440
467
 
441
- # Method 2: Using the run_api function for more control
468
+ # Start in no-auth mode (no API key required)
469
+ start_server(no_auth=True)
470
+
471
+ # Method 2: Advanced usage with run_api
442
472
  from webscout.client import run_api
443
473
 
444
474
  run_api(