webscout 8.2.6__py3-none-any.whl → 8.2.8__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 (150) hide show
  1. webscout/AIauto.py +1 -1
  2. webscout/AIutel.py +298 -239
  3. webscout/Extra/Act.md +309 -0
  4. webscout/Extra/GitToolkit/gitapi/README.md +110 -0
  5. webscout/Extra/YTToolkit/README.md +375 -0
  6. webscout/Extra/YTToolkit/ytapi/README.md +44 -0
  7. webscout/Extra/YTToolkit/ytapi/extras.py +92 -19
  8. webscout/Extra/autocoder/autocoder.py +309 -114
  9. webscout/Extra/autocoder/autocoder_utiles.py +15 -15
  10. webscout/Extra/gguf.md +430 -0
  11. webscout/Extra/tempmail/README.md +488 -0
  12. webscout/Extra/weather.md +281 -0
  13. webscout/Litlogger/Readme.md +175 -0
  14. webscout/Provider/AISEARCH/DeepFind.py +41 -37
  15. webscout/Provider/AISEARCH/README.md +279 -0
  16. webscout/Provider/AISEARCH/__init__.py +0 -1
  17. webscout/Provider/AISEARCH/genspark_search.py +228 -86
  18. webscout/Provider/AISEARCH/hika_search.py +11 -11
  19. webscout/Provider/AISEARCH/scira_search.py +324 -322
  20. webscout/Provider/AllenAI.py +7 -14
  21. webscout/Provider/Blackboxai.py +518 -74
  22. webscout/Provider/Cloudflare.py +0 -1
  23. webscout/Provider/Deepinfra.py +23 -21
  24. webscout/Provider/Flowith.py +217 -0
  25. webscout/Provider/FreeGemini.py +250 -0
  26. webscout/Provider/GizAI.py +15 -5
  27. webscout/Provider/Glider.py +11 -8
  28. webscout/Provider/HeckAI.py +80 -52
  29. webscout/Provider/Koboldai.py +7 -4
  30. webscout/Provider/LambdaChat.py +2 -2
  31. webscout/Provider/Marcus.py +10 -18
  32. webscout/Provider/OPENAI/BLACKBOXAI.py +735 -0
  33. webscout/Provider/OPENAI/Cloudflare.py +378 -0
  34. webscout/Provider/OPENAI/FreeGemini.py +282 -0
  35. webscout/Provider/OPENAI/NEMOTRON.py +244 -0
  36. webscout/Provider/OPENAI/README.md +1253 -0
  37. webscout/Provider/OPENAI/__init__.py +8 -0
  38. webscout/Provider/OPENAI/ai4chat.py +293 -286
  39. webscout/Provider/OPENAI/api.py +810 -0
  40. webscout/Provider/OPENAI/base.py +217 -14
  41. webscout/Provider/OPENAI/c4ai.py +373 -367
  42. webscout/Provider/OPENAI/chatgpt.py +7 -0
  43. webscout/Provider/OPENAI/chatgptclone.py +7 -0
  44. webscout/Provider/OPENAI/chatsandbox.py +172 -0
  45. webscout/Provider/OPENAI/deepinfra.py +30 -20
  46. webscout/Provider/OPENAI/e2b.py +6 -0
  47. webscout/Provider/OPENAI/exaai.py +7 -0
  48. webscout/Provider/OPENAI/exachat.py +6 -0
  49. webscout/Provider/OPENAI/flowith.py +162 -0
  50. webscout/Provider/OPENAI/freeaichat.py +359 -352
  51. webscout/Provider/OPENAI/glider.py +323 -316
  52. webscout/Provider/OPENAI/groq.py +361 -354
  53. webscout/Provider/OPENAI/heckai.py +30 -64
  54. webscout/Provider/OPENAI/llmchatco.py +8 -0
  55. webscout/Provider/OPENAI/mcpcore.py +7 -0
  56. webscout/Provider/OPENAI/multichat.py +8 -0
  57. webscout/Provider/OPENAI/netwrck.py +356 -350
  58. webscout/Provider/OPENAI/opkfc.py +8 -0
  59. webscout/Provider/OPENAI/scirachat.py +471 -462
  60. webscout/Provider/OPENAI/sonus.py +9 -0
  61. webscout/Provider/OPENAI/standardinput.py +9 -1
  62. webscout/Provider/OPENAI/textpollinations.py +339 -329
  63. webscout/Provider/OPENAI/toolbaz.py +7 -0
  64. webscout/Provider/OPENAI/typefully.py +355 -0
  65. webscout/Provider/OPENAI/typegpt.py +358 -346
  66. webscout/Provider/OPENAI/uncovrAI.py +7 -0
  67. webscout/Provider/OPENAI/utils.py +103 -7
  68. webscout/Provider/OPENAI/venice.py +12 -0
  69. webscout/Provider/OPENAI/wisecat.py +19 -19
  70. webscout/Provider/OPENAI/writecream.py +7 -0
  71. webscout/Provider/OPENAI/x0gpt.py +7 -0
  72. webscout/Provider/OPENAI/yep.py +50 -21
  73. webscout/Provider/OpenGPT.py +1 -1
  74. webscout/Provider/TTI/AiForce/README.md +159 -0
  75. webscout/Provider/TTI/FreeAIPlayground/README.md +99 -0
  76. webscout/Provider/TTI/ImgSys/README.md +174 -0
  77. webscout/Provider/TTI/MagicStudio/README.md +101 -0
  78. webscout/Provider/TTI/Nexra/README.md +155 -0
  79. webscout/Provider/TTI/PollinationsAI/README.md +146 -0
  80. webscout/Provider/TTI/README.md +128 -0
  81. webscout/Provider/TTI/aiarta/README.md +134 -0
  82. webscout/Provider/TTI/artbit/README.md +100 -0
  83. webscout/Provider/TTI/fastflux/README.md +129 -0
  84. webscout/Provider/TTI/huggingface/README.md +114 -0
  85. webscout/Provider/TTI/piclumen/README.md +161 -0
  86. webscout/Provider/TTI/pixelmuse/README.md +79 -0
  87. webscout/Provider/TTI/talkai/README.md +139 -0
  88. webscout/Provider/TTS/README.md +192 -0
  89. webscout/Provider/TTS/__init__.py +2 -1
  90. webscout/Provider/TTS/speechma.py +500 -100
  91. webscout/Provider/TTS/sthir.py +94 -0
  92. webscout/Provider/TeachAnything.py +3 -7
  93. webscout/Provider/TextPollinationsAI.py +4 -2
  94. webscout/Provider/{aimathgpt.py → UNFINISHED/ChatHub.py} +88 -68
  95. webscout/Provider/UNFINISHED/liner_api_request.py +263 -0
  96. webscout/Provider/UNFINISHED/oivscode.py +351 -0
  97. webscout/Provider/UNFINISHED/test_lmarena.py +119 -0
  98. webscout/Provider/Writecream.py +11 -2
  99. webscout/Provider/__init__.py +8 -14
  100. webscout/Provider/ai4chat.py +4 -58
  101. webscout/Provider/asksteve.py +17 -9
  102. webscout/Provider/cerebras.py +3 -1
  103. webscout/Provider/koala.py +170 -268
  104. webscout/Provider/llmchat.py +3 -0
  105. webscout/Provider/lmarena.py +198 -0
  106. webscout/Provider/meta.py +7 -4
  107. webscout/Provider/samurai.py +223 -0
  108. webscout/Provider/scira_chat.py +4 -2
  109. webscout/Provider/typefully.py +23 -151
  110. webscout/__init__.py +4 -2
  111. webscout/cli.py +3 -28
  112. webscout/conversation.py +35 -35
  113. webscout/litagent/Readme.md +276 -0
  114. webscout/scout/README.md +402 -0
  115. webscout/swiftcli/Readme.md +323 -0
  116. webscout/version.py +1 -1
  117. webscout/webscout_search.py +2 -182
  118. webscout/webscout_search_async.py +1 -179
  119. webscout/zeroart/README.md +89 -0
  120. webscout/zeroart/__init__.py +134 -54
  121. webscout/zeroart/base.py +19 -13
  122. webscout/zeroart/effects.py +101 -99
  123. webscout/zeroart/fonts.py +1239 -816
  124. {webscout-8.2.6.dist-info → webscout-8.2.8.dist-info}/METADATA +116 -74
  125. {webscout-8.2.6.dist-info → webscout-8.2.8.dist-info}/RECORD +130 -103
  126. {webscout-8.2.6.dist-info → webscout-8.2.8.dist-info}/WHEEL +1 -1
  127. webscout-8.2.8.dist-info/entry_points.txt +3 -0
  128. webscout-8.2.8.dist-info/top_level.txt +1 -0
  129. webscout/Provider/AISEARCH/ISou.py +0 -256
  130. webscout/Provider/ElectronHub.py +0 -773
  131. webscout/Provider/Free2GPT.py +0 -241
  132. webscout/Provider/GPTWeb.py +0 -249
  133. webscout/Provider/bagoodex.py +0 -145
  134. webscout/Provider/geminiprorealtime.py +0 -160
  135. webscout/scout/core.py +0 -881
  136. webscout-8.2.6.dist-info/entry_points.txt +0 -3
  137. webscout-8.2.6.dist-info/top_level.txt +0 -2
  138. webstoken/__init__.py +0 -30
  139. webstoken/classifier.py +0 -189
  140. webstoken/keywords.py +0 -216
  141. webstoken/language.py +0 -128
  142. webstoken/ner.py +0 -164
  143. webstoken/normalizer.py +0 -35
  144. webstoken/processor.py +0 -77
  145. webstoken/sentiment.py +0 -206
  146. webstoken/stemmer.py +0 -73
  147. webstoken/tagger.py +0 -60
  148. webstoken/tokenizer.py +0 -158
  149. /webscout/Provider/{Youchat.py → UNFINISHED/Youchat.py} +0 -0
  150. {webscout-8.2.6.dist-info → webscout-8.2.8.dist-info}/licenses/LICENSE.md +0 -0
@@ -0,0 +1,323 @@
1
+ <div align="center">
2
+
3
+ # ⚡ SwiftCLI
4
+
5
+ > Build Beautiful Command-Line Applications at Light Speed
6
+
7
+ [![Python 3.8+](https://img.shields.io/badge/Python-3.8+-blue.svg?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org)
8
+ [![PyPI version](https://img.shields.io/badge/View_on-PyPI-orange.svg?style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/webscout/)
9
+ [![License](https://img.shields.io/badge/License-MIT-green.svg?style=for-the-badge)](LICENSE)
10
+
11
+
12
+ </div>
13
+
14
+ ## 🌟 Key Features
15
+
16
+ - 🎨 **Rich Output**: Beautiful tables, progress bars, and styled text
17
+ - 🔄 **Command Groups**: Organize commands logically
18
+ - 🎯 **Type Safety**: Full type hints and runtime validation
19
+ - 🔌 **Plugin System**: Extend functionality easily
20
+ - 🌍 **Environment Support**: Load config from env vars and files
21
+ - 🚀 **Modern Python**: Async support, type hints, and more
22
+
23
+ ## 📦 Installation
24
+
25
+ ```bash
26
+ pip install -U webscout
27
+ ```
28
+
29
+ ## 🚀 Quick Start
30
+
31
+ ```python
32
+ from webscout.swiftcli import CLI, option, table_output
33
+
34
+ app = CLI("myapp", version="1.0.0")
35
+
36
+ @app.command()
37
+ @option("--count", "-c", type=int, default=5)
38
+ @table_output(["ID", "Status"])
39
+ def list_items(count: int):
40
+ """List system items with status"""
41
+ return [
42
+ [i, "Active" if i % 2 == 0 else "Inactive"]
43
+ for i in range(1, count + 1)
44
+ ]
45
+
46
+ if __name__ == "__main__":
47
+ app.run()
48
+ ```
49
+
50
+ Run it:
51
+ ```bash
52
+ $ python app.py list-items --count 3
53
+ ┌────┬──────────┐
54
+ │ ID │ Status │
55
+ ├────┼──────────┤
56
+ │ 1 │ Inactive │
57
+ │ 2 │ Active │
58
+ │ 3 │ Inactive │
59
+ └────┴──────────┘
60
+ ```
61
+
62
+ ## 📚 Documentation
63
+
64
+ ### Command Groups
65
+
66
+ Organize related commands:
67
+
68
+ ```python
69
+ @app.group()
70
+ def db():
71
+ """Database operations"""
72
+ pass
73
+
74
+ @db.command()
75
+ @option("--force", is_flag=True)
76
+ def migrate(force: bool):
77
+ """Run database migrations"""
78
+ print(f"Running migrations (force={force})")
79
+
80
+ # Usage: python app.py db migrate --force
81
+ ```
82
+
83
+ ### Rich Output
84
+
85
+ Beautiful progress bars and tables:
86
+
87
+ ```python
88
+ @app.command()
89
+ @progress("Processing")
90
+ async def process():
91
+ """Process items with progress"""
92
+ for i in range(5):
93
+ yield f"Step {i+1}/5"
94
+ await asyncio.sleep(0.5)
95
+
96
+ @app.command()
97
+ @table_output(["Name", "Score"])
98
+ def top_scores():
99
+ """Show top scores"""
100
+ return [
101
+ ["Alice", 100],
102
+ ["Bob", 95],
103
+ ["Charlie", 90]
104
+ ]
105
+ ```
106
+
107
+ ### Type-Safe Options
108
+
109
+ ```python
110
+ from enum import Enum
111
+ from datetime import datetime
112
+ from typing import List, Optional
113
+
114
+ class Format(Enum):
115
+ JSON = "json"
116
+ YAML = "yaml"
117
+ CSV = "csv"
118
+
119
+ @app.command()
120
+ @option("--format", type=Format, default=Format.JSON)
121
+ @option("--date", type=datetime)
122
+ @option("--tags", type=List[str])
123
+ def export(
124
+ format: Format,
125
+ date: datetime,
126
+ tags: Optional[List[str]] = None
127
+ ):
128
+ """Export data with type validation"""
129
+ print(f"Exporting as {format.value}")
130
+ print(f"Date: {date}")
131
+ if tags:
132
+ print(f"Tags: {', '.join(tags)}")
133
+ ```
134
+
135
+ ### Environment & Config
136
+
137
+ ```python
138
+ @app.command()
139
+ @envvar("API_KEY", required=True)
140
+ @config_file("~/.config/myapp.yaml")
141
+ def api_call(api_key: str, config: dict):
142
+ """Make API call using config"""
143
+ url = config.get("api_url")
144
+ print(f"Calling {url} with key {api_key[:4]}...")
145
+ ```
146
+
147
+ ### Async Support
148
+
149
+ ```python
150
+ @app.command()
151
+ async def fetch_data():
152
+ """Fetch data asynchronously"""
153
+ async with aiohttp.ClientSession() as session:
154
+ async with session.get("https://api.example.com") as resp:
155
+ data = await resp.json()
156
+ return data
157
+ ```
158
+
159
+ ### Plugin System
160
+
161
+ ```python
162
+ from webscout.swiftcli import Plugin
163
+
164
+ class MetricsPlugin(Plugin):
165
+ def __init__(self):
166
+ self.start_time = None
167
+
168
+ def before_command(self, command: str, args: list):
169
+ self.start_time = time.time()
170
+
171
+ def after_command(self, command: str, args: list, result: any):
172
+ duration = time.time() - self.start_time
173
+ print(f"Command {command} took {duration:.2f}s")
174
+
175
+ app.plugin_manager.register(MetricsPlugin())
176
+ ```
177
+
178
+ ## 🛠 Advanced Features
179
+
180
+ ### Custom Output Formatting
181
+
182
+ ```python
183
+ from rich.console import Console
184
+ from rich.panel import Panel
185
+ from rich.table import Table
186
+
187
+ console = Console()
188
+
189
+ @app.command()
190
+ def status():
191
+ """Show system status"""
192
+ table = Table(show_header=True)
193
+ table.add_column("Service")
194
+ table.add_column("Status")
195
+ table.add_column("Uptime")
196
+
197
+ table.add_row("API", "✅ Online", "24h")
198
+ table.add_row("DB", "✅ Online", "12h")
199
+ table.add_row("Cache", "⚠️ Degraded", "6h")
200
+
201
+ console.print(Panel(
202
+ table,
203
+ title="System Status",
204
+ border_style="green"
205
+ ))
206
+ ```
207
+
208
+ ### Command Pipelines
209
+
210
+ ```python
211
+ @app.group(chain=True)
212
+ def process():
213
+ """Data processing pipeline"""
214
+ pass
215
+
216
+ @process.command()
217
+ def extract():
218
+ """Extract data"""
219
+ return {"data": [1, 2, 3]}
220
+
221
+ @process.command()
222
+ def transform(data: dict):
223
+ """Transform data"""
224
+ return {"data": [x * 2 for x in data["data"]]}
225
+
226
+ @process.command()
227
+ def load(data: dict):
228
+ """Load data"""
229
+ print(f"Loading: {data}")
230
+
231
+ # Usage: python app.py process extract transform load
232
+ ```
233
+
234
+ ## 🔧 Configuration
235
+
236
+ ### Application Config
237
+
238
+ ```python
239
+ app = CLI(
240
+ name="myapp",
241
+ version="1.0.0",
242
+ description="My awesome CLI app",
243
+ config_file="~/.config/myapp.yaml",
244
+ auto_envvar_prefix="MYAPP",
245
+ plugin_folder="~/.myapp/plugins"
246
+ )
247
+ ```
248
+
249
+ ### Command Config
250
+
251
+ ```python
252
+ @app.command()
253
+ @option("--config", type=click.Path(exists=True))
254
+ @option("--verbose", "-v", count=True)
255
+ @option("--format", type=click.Choice(["json", "yaml"]))
256
+ def process(config: str, verbose: int, format: str):
257
+ """Process with configuration"""
258
+ pass
259
+ ```
260
+
261
+ ## 📋 Best Practices
262
+
263
+ 1. **Use Type Hints**
264
+ ```python
265
+ from typing import Optional, List, Dict
266
+
267
+ @app.command()
268
+ def search(
269
+ query: str,
270
+ limit: Optional[int] = 10,
271
+ tags: List[str] = None
272
+ ) -> Dict[str, any]:
273
+ """Search with proper type hints"""
274
+ pass
275
+ ```
276
+
277
+ 2. **Structured Error Handling**
278
+ ```python
279
+ from webscout.swiftcli import CLIError
280
+
281
+ @app.command()
282
+ def risky():
283
+ try:
284
+ # Risky operation
285
+ pass
286
+ except FileNotFoundError as e:
287
+ raise CLIError("Config file not found") from e
288
+ except PermissionError as e:
289
+ raise CLIError("Permission denied") from e
290
+ ```
291
+
292
+ 3. **Command Organization**
293
+ ```python
294
+ # commands/
295
+ # ├── __init__.py
296
+ # ├── db.py
297
+ # ├── auth.py
298
+ # └── utils.py
299
+
300
+ from .commands import db, auth, utils
301
+
302
+ app.add_command_group(db.commands)
303
+ app.add_command_group(auth.commands)
304
+ app.add_command_group(utils.commands)
305
+ ```
306
+
307
+ ## 🤝 Contributing
308
+
309
+ Contributions are welcome! Check out our [Contributing Guide](CONTRIBUTING.md) for details.
310
+
311
+ ## 📝 License
312
+
313
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
314
+
315
+ <div align="center">
316
+
317
+ ---
318
+
319
+ Made with ❤️ by the [Webscout](https://github.com/OEvortex/Webscout) team
320
+
321
+ [![GitHub stars](https://img.shields.io/github/stars/OEvortex/Webscout?style=social)](https://github.com/OEvortex/Webscout)
322
+
323
+ </div>
webscout/version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "8.2.6"
1
+ __version__ = "8.2.8"
2
2
  __prog__ = "webscout"
@@ -56,13 +56,7 @@ class WEBS:
56
56
  "firefox133", "firefox135",
57
57
  ) # fmt: skip
58
58
  _impersonates_os = ("android", "ios", "linux", "macos", "windows")
59
- _chat_models = {
60
- "gpt-4o-mini": "gpt-4o-mini",
61
- "llama-3.3-70b": "meta-llama/Llama-3.3-70B-Instruct-Turbo",
62
- "claude-3-haiku": "claude-3-haiku-20240307",
63
- "o3-mini": "o3-mini",
64
- "mistral-small-3": "mistralai/Mistral-Small-24B-Instruct-2501",
65
- }
59
+
66
60
 
67
61
  def __init__(
68
62
  self,
@@ -119,11 +113,6 @@ class WEBS:
119
113
  self.sleep_timestamp = 0.0
120
114
 
121
115
  self._exception_event = Event()
122
- self._chat_messages: list[dict[str, str]] = []
123
- self._chat_tokens_count = 0
124
- self._chat_vqd: str = ""
125
- self._chat_vqd_hash: str = ""
126
- self._chat_xfe: str = ""
127
116
 
128
117
  def __enter__(self) -> WEBS:
129
118
  return self
@@ -203,176 +192,7 @@ class WEBS:
203
192
  resp_content = self._get_url("GET", "https://duckduckgo.com", params={"q": keywords}).content
204
193
  return _extract_vqd(resp_content, keywords)
205
194
 
206
- def chat_yield(self, keywords: str, model: str = "gpt-4o-mini", timeout: int = 30, max_retries: int = 3) -> Iterator[str]:
207
- """Initiates a chat session with webscout AI.
208
195
 
209
- Args:
210
- keywords (str): The initial message or question to send to the AI.
211
- model (str): The model to use: "gpt-4o-mini", "llama-3.3-70b", "claude-3-haiku",
212
- "o3-mini", "mistral-small-3". Defaults to "gpt-4o-mini".
213
- timeout (int): Timeout value for the HTTP client. Defaults to 30.
214
- max_retries (int): Maximum number of retry attempts for rate limited requests. Defaults to 3.
215
-
216
- Yields:
217
- str: Chunks of the response from the AI.
218
- """
219
- # Get Cloudflare Turnstile token
220
- def get_turnstile_token():
221
- try:
222
- # Visit the DuckDuckGo chat page to get the Turnstile token
223
- resp_content = self._get_url(
224
- method="GET",
225
- url="https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1",
226
- ).content
227
-
228
- # Extract the Turnstile token if available
229
- if b'cf-turnstile-response' in resp_content:
230
- token = resp_content.split(b'cf-turnstile-response="', maxsplit=1)[1].split(b'"', maxsplit=1)[0].decode()
231
- return token
232
- return ""
233
- except Exception:
234
- return ""
235
-
236
- # x-fe-version
237
- if not self._chat_xfe:
238
- resp_content = self._get_url(
239
- method="GET",
240
- url="https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1",
241
- ).content
242
- try:
243
- xfe1 = resp_content.split(b'__DDG_BE_VERSION__="', maxsplit=1)[1].split(b'"', maxsplit=1)[0].decode()
244
- xfe2 = resp_content.split(b'__DDG_FE_CHAT_HASH__="', maxsplit=1)[1].split(b'"', maxsplit=1)[0].decode()
245
- self._chat_xfe = f"{xfe1}-{xfe2}"
246
- except Exception as ex:
247
- raise WebscoutE(
248
- f"chat_yield() Error to get _chat_xfe: {type(ex).__name__}: {ex}"
249
- ) from ex
250
- # vqd
251
- if not self._chat_vqd:
252
- resp = self._get_url(
253
- method="GET", url="https://duckduckgo.com/duckchat/v1/status", headers={"x-vqd-accept": "1"}
254
- )
255
- self._chat_vqd = resp.headers.get("x-vqd-4", "")
256
- self._chat_vqd_hash = resp.headers.get("x-vqd-hash-1", "")
257
-
258
- self._chat_messages.append({"role": "user", "content": keywords})
259
- self._chat_tokens_count += max(len(keywords) // 4, 1) # approximate number of tokens
260
- if model not in self._chat_models:
261
- warnings.warn(f"{model=} is unavailable. Using 'gpt-4o-mini'", stacklevel=1)
262
- model = "gpt-4o-mini"
263
-
264
- # Get Cloudflare Turnstile token
265
- turnstile_token = get_turnstile_token()
266
-
267
- json_data = {
268
- "model": self._chat_models[model],
269
- "messages": self._chat_messages,
270
- }
271
-
272
- # Add Turnstile token if available
273
- if turnstile_token:
274
- json_data["cf-turnstile-response"] = turnstile_token
275
-
276
- # Enhanced headers to better mimic a real browser
277
- chat_headers = {
278
- "x-fe-version": self._chat_xfe,
279
- "x-vqd-4": self._chat_vqd,
280
- "x-vqd-hash-1": "",
281
- "Accept": "text/event-stream",
282
- "Accept-Language": "en-US,en;q=0.9",
283
- "Cache-Control": "no-cache",
284
- "Content-Type": "application/json",
285
- "DNT": "1",
286
- "Origin": "https://duckduckgo.com",
287
- "Referer": "https://duckduckgo.com/",
288
- "Sec-Fetch-Dest": "empty",
289
- "Sec-Fetch-Mode": "cors",
290
- "Sec-Fetch-Site": "same-origin",
291
- "User-Agent": self.client.headers.get("User-Agent", "")
292
- }
293
-
294
- # Retry logic for rate limited requests
295
- retry_count = 0
296
- while retry_count <= max_retries:
297
- try:
298
- resp = self._get_url(
299
- method="POST",
300
- url="https://duckduckgo.com/duckchat/v1/chat",
301
- headers=chat_headers,
302
- json=json_data,
303
- timeout=timeout,
304
- )
305
-
306
- self._chat_vqd = resp.headers.get("x-vqd-4", "")
307
- self._chat_vqd_hash = resp.headers.get("x-vqd-hash-1", "")
308
- chunks = []
309
-
310
- # curl_cffi uses iter_content instead of stream
311
- for chunk in resp.iter_content(chunk_size=1024):
312
- lines = chunk.split(b"data:")
313
- for line in lines:
314
- if line := line.strip():
315
- if line == b"[DONE]":
316
- break
317
- if line == b"[DONE][LIMIT_CONVERSATION]":
318
- raise ConversationLimitException("ERR_CONVERSATION_LIMIT")
319
- try:
320
- x = json_loads(line)
321
- if isinstance(x, dict):
322
- if x.get("action") == "error":
323
- err_message = x.get("type", "")
324
- if x.get("status") == 429:
325
- raise (
326
- ConversationLimitException(err_message)
327
- if err_message == "ERR_CONVERSATION_LIMIT"
328
- else RatelimitE(err_message)
329
- )
330
- raise WebscoutE(err_message)
331
- elif message := x.get("message"):
332
- chunks.append(message)
333
- yield message
334
- except Exception as e:
335
- # Skip invalid JSON data
336
- continue
337
-
338
- # If we get here, the request was successful
339
- result = "".join(chunks)
340
- self._chat_messages.append({"role": "assistant", "content": result})
341
- self._chat_tokens_count += len(result)
342
- return
343
-
344
- except RatelimitE as ex:
345
- retry_count += 1
346
- if retry_count > max_retries:
347
- raise WebscoutE(f"chat_yield() Rate limit exceeded after {max_retries} retries: {ex}") from ex
348
-
349
- # Get a fresh Turnstile token for the retry
350
- turnstile_token = get_turnstile_token()
351
- if turnstile_token:
352
- json_data["cf-turnstile-response"] = turnstile_token
353
-
354
- # Exponential backoff
355
- sleep_time = 2 ** retry_count
356
- sleep(sleep_time)
357
-
358
- except Exception as ex:
359
- raise WebscoutE(f"chat_yield() {type(ex).__name__}: {ex}") from ex
360
-
361
- def chat(self, keywords: str, model: str = "gpt-4o-mini", timeout: int = 30, max_retries: int = 3) -> str:
362
- """Initiates a chat session with webscout AI.
363
-
364
- Args:
365
- keywords (str): The initial message or question to send to the AI.
366
- model (str): The model to use: "gpt-4o-mini", "llama-3.3-70b", "claude-3-haiku",
367
- "o3-mini", "mistral-small-3". Defaults to "gpt-4o-mini".
368
- timeout (int): Timeout value for the HTTP client. Defaults to 30.
369
- max_retries (int): Maximum number of retry attempts for rate limited requests. Defaults to 3.
370
-
371
- Returns:
372
- str: The response from the AI.
373
- """
374
- answer_generator = self.chat_yield(keywords, model, timeout, max_retries)
375
- return "".join(answer_generator)
376
196
 
377
197
  def text(
378
198
  self,
@@ -1361,4 +1181,4 @@ class WEBS:
1361
1181
  "visibility_m": hour.get("visibility"),
1362
1182
  })
1363
1183
 
1364
- return formatted_data
1184
+ return formatted_data