webscout 8.3.7__py3-none-any.whl → 2025.10.11__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 +250 -250
- webscout/AIbase.py +379 -379
- webscout/AIutel.py +60 -60
- webscout/Bard.py +1012 -1012
- webscout/Bing_search.py +417 -417
- webscout/DWEBS.py +529 -529
- webscout/Extra/Act.md +309 -309
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/README.md +110 -110
- webscout/Extra/GitToolkit/gitapi/__init__.py +11 -11
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -195
- webscout/Extra/GitToolkit/gitapi/user.py +96 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +61 -61
- webscout/Extra/YTToolkit/README.md +375 -375
- webscout/Extra/YTToolkit/YTdownloader.py +956 -956
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +475 -475
- webscout/Extra/YTToolkit/ytapi/README.md +44 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -6
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -118
- webscout/Extra/YTToolkit/ytapi/https.py +88 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/query.py +39 -39
- webscout/Extra/YTToolkit/ytapi/stream.py +62 -62
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +232 -232
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder.py +1105 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +332 -332
- webscout/Extra/gguf.md +429 -429
- webscout/Extra/gguf.py +1213 -1213
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +27 -27
- webscout/Extra/tempmail/async_utils.py +140 -140
- webscout/Extra/tempmail/base.py +160 -160
- webscout/Extra/tempmail/cli.py +186 -186
- webscout/Extra/tempmail/emailnator.py +84 -84
- webscout/Extra/tempmail/mail_tm.py +360 -360
- webscout/Extra/tempmail/temp_mail_io.py +291 -291
- webscout/Extra/weather.md +281 -281
- webscout/Extra/weather.py +193 -193
- webscout/Litlogger/README.md +10 -10
- webscout/Litlogger/__init__.py +15 -15
- webscout/Litlogger/formats.py +13 -13
- webscout/Litlogger/handlers.py +121 -121
- webscout/Litlogger/levels.py +13 -13
- webscout/Litlogger/logger.py +134 -134
- webscout/Provider/AISEARCH/Perplexity.py +332 -332
- webscout/Provider/AISEARCH/README.md +279 -279
- webscout/Provider/AISEARCH/__init__.py +16 -1
- webscout/Provider/AISEARCH/felo_search.py +206 -206
- webscout/Provider/AISEARCH/genspark_search.py +323 -323
- webscout/Provider/AISEARCH/hika_search.py +185 -185
- webscout/Provider/AISEARCH/iask_search.py +410 -410
- webscout/Provider/AISEARCH/monica_search.py +219 -219
- webscout/Provider/AISEARCH/scira_search.py +316 -316
- webscout/Provider/AISEARCH/stellar_search.py +177 -177
- webscout/Provider/AISEARCH/webpilotai_search.py +255 -255
- webscout/Provider/Aitopia.py +314 -314
- webscout/Provider/Apriel.py +306 -0
- webscout/Provider/ChatGPTClone.py +236 -236
- webscout/Provider/ChatSandbox.py +343 -343
- webscout/Provider/Cloudflare.py +324 -324
- webscout/Provider/Cohere.py +208 -208
- webscout/Provider/Deepinfra.py +370 -366
- webscout/Provider/ExaAI.py +260 -260
- webscout/Provider/ExaChat.py +308 -308
- webscout/Provider/Flowith.py +221 -221
- webscout/Provider/GMI.py +293 -0
- webscout/Provider/Gemini.py +164 -164
- webscout/Provider/GeminiProxy.py +167 -167
- webscout/Provider/GithubChat.py +371 -372
- webscout/Provider/Groq.py +800 -800
- webscout/Provider/HeckAI.py +383 -383
- webscout/Provider/Jadve.py +282 -282
- webscout/Provider/K2Think.py +307 -307
- webscout/Provider/Koboldai.py +205 -205
- webscout/Provider/LambdaChat.py +423 -423
- webscout/Provider/Nemotron.py +244 -244
- webscout/Provider/Netwrck.py +248 -248
- webscout/Provider/OLLAMA.py +395 -395
- webscout/Provider/OPENAI/Cloudflare.py +393 -393
- webscout/Provider/OPENAI/FalconH1.py +451 -451
- webscout/Provider/OPENAI/FreeGemini.py +296 -296
- webscout/Provider/OPENAI/K2Think.py +431 -431
- webscout/Provider/OPENAI/NEMOTRON.py +240 -240
- webscout/Provider/OPENAI/PI.py +427 -427
- webscout/Provider/OPENAI/README.md +959 -959
- webscout/Provider/OPENAI/TogetherAI.py +345 -345
- webscout/Provider/OPENAI/TwoAI.py +465 -465
- webscout/Provider/OPENAI/__init__.py +33 -18
- webscout/Provider/OPENAI/base.py +248 -248
- webscout/Provider/OPENAI/chatglm.py +528 -0
- webscout/Provider/OPENAI/chatgpt.py +592 -592
- webscout/Provider/OPENAI/chatgptclone.py +521 -521
- webscout/Provider/OPENAI/chatsandbox.py +202 -202
- webscout/Provider/OPENAI/deepinfra.py +318 -314
- webscout/Provider/OPENAI/e2b.py +1665 -1665
- webscout/Provider/OPENAI/exaai.py +420 -420
- webscout/Provider/OPENAI/exachat.py +452 -452
- webscout/Provider/OPENAI/friendli.py +232 -232
- webscout/Provider/OPENAI/{refact.py → gmi.py} +324 -274
- webscout/Provider/OPENAI/groq.py +364 -364
- webscout/Provider/OPENAI/heckai.py +314 -314
- webscout/Provider/OPENAI/llmchatco.py +337 -337
- webscout/Provider/OPENAI/netwrck.py +355 -355
- webscout/Provider/OPENAI/oivscode.py +290 -290
- webscout/Provider/OPENAI/opkfc.py +518 -518
- webscout/Provider/OPENAI/pydantic_imports.py +1 -1
- webscout/Provider/OPENAI/scirachat.py +535 -535
- webscout/Provider/OPENAI/sonus.py +308 -308
- webscout/Provider/OPENAI/standardinput.py +442 -442
- webscout/Provider/OPENAI/textpollinations.py +340 -340
- webscout/Provider/OPENAI/toolbaz.py +419 -416
- webscout/Provider/OPENAI/typefully.py +362 -362
- webscout/Provider/OPENAI/utils.py +295 -295
- webscout/Provider/OPENAI/venice.py +436 -436
- webscout/Provider/OPENAI/wisecat.py +387 -387
- webscout/Provider/OPENAI/writecream.py +166 -166
- webscout/Provider/OPENAI/x0gpt.py +378 -378
- webscout/Provider/OPENAI/yep.py +389 -389
- webscout/Provider/OpenGPT.py +230 -230
- webscout/Provider/Openai.py +243 -243
- webscout/Provider/PI.py +405 -405
- webscout/Provider/Perplexitylabs.py +430 -430
- webscout/Provider/QwenLM.py +272 -272
- webscout/Provider/STT/__init__.py +16 -1
- webscout/Provider/Sambanova.py +257 -257
- webscout/Provider/StandardInput.py +309 -309
- webscout/Provider/TTI/README.md +82 -82
- webscout/Provider/TTI/__init__.py +33 -18
- webscout/Provider/TTI/aiarta.py +413 -413
- webscout/Provider/TTI/base.py +136 -136
- webscout/Provider/TTI/bing.py +243 -243
- webscout/Provider/TTI/gpt1image.py +149 -149
- webscout/Provider/TTI/imagen.py +196 -196
- webscout/Provider/TTI/infip.py +211 -211
- webscout/Provider/TTI/magicstudio.py +232 -232
- webscout/Provider/TTI/monochat.py +219 -219
- webscout/Provider/TTI/piclumen.py +214 -214
- webscout/Provider/TTI/pixelmuse.py +232 -232
- webscout/Provider/TTI/pollinations.py +232 -232
- webscout/Provider/TTI/together.py +288 -288
- webscout/Provider/TTI/utils.py +12 -12
- webscout/Provider/TTI/venice.py +367 -367
- webscout/Provider/TTS/README.md +192 -192
- webscout/Provider/TTS/__init__.py +33 -18
- webscout/Provider/TTS/parler.py +110 -110
- webscout/Provider/TTS/streamElements.py +333 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TeachAnything.py +237 -237
- webscout/Provider/TextPollinationsAI.py +310 -310
- webscout/Provider/TogetherAI.py +356 -356
- webscout/Provider/TwoAI.py +312 -312
- webscout/Provider/TypliAI.py +311 -311
- webscout/Provider/UNFINISHED/ChatHub.py +208 -208
- webscout/Provider/UNFINISHED/ChutesAI.py +313 -313
- webscout/Provider/UNFINISHED/GizAI.py +294 -294
- webscout/Provider/UNFINISHED/Marcus.py +198 -198
- webscout/Provider/UNFINISHED/Qodo.py +477 -477
- webscout/Provider/UNFINISHED/VercelAIGateway.py +338 -338
- webscout/Provider/UNFINISHED/XenAI.py +324 -324
- webscout/Provider/UNFINISHED/Youchat.py +330 -330
- webscout/Provider/UNFINISHED/liner.py +334 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +262 -262
- webscout/Provider/UNFINISHED/puterjs.py +634 -634
- webscout/Provider/UNFINISHED/samurai.py +223 -223
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
- webscout/Provider/Venice.py +250 -250
- webscout/Provider/VercelAI.py +256 -256
- webscout/Provider/WiseCat.py +231 -231
- webscout/Provider/WrDoChat.py +366 -366
- webscout/Provider/__init__.py +33 -18
- webscout/Provider/ai4chat.py +174 -174
- webscout/Provider/akashgpt.py +331 -331
- webscout/Provider/cerebras.py +446 -446
- webscout/Provider/chatglm.py +394 -301
- webscout/Provider/cleeai.py +211 -211
- webscout/Provider/elmo.py +282 -282
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +261 -261
- webscout/Provider/hermes.py +263 -263
- webscout/Provider/julius.py +223 -223
- webscout/Provider/learnfastai.py +309 -309
- webscout/Provider/llama3mitril.py +214 -214
- webscout/Provider/llmchat.py +243 -243
- webscout/Provider/llmchatco.py +290 -290
- webscout/Provider/meta.py +801 -801
- webscout/Provider/oivscode.py +309 -309
- webscout/Provider/scira_chat.py +383 -383
- webscout/Provider/searchchat.py +292 -292
- webscout/Provider/sonus.py +258 -258
- webscout/Provider/toolbaz.py +370 -367
- webscout/Provider/turboseek.py +273 -273
- webscout/Provider/typefully.py +207 -207
- webscout/Provider/yep.py +372 -372
- webscout/__init__.py +30 -31
- webscout/__main__.py +5 -5
- webscout/auth/api_key_manager.py +189 -189
- webscout/auth/config.py +175 -175
- webscout/auth/models.py +185 -185
- webscout/auth/routes.py +664 -664
- webscout/auth/simple_logger.py +236 -236
- webscout/cli.py +523 -523
- webscout/conversation.py +438 -438
- webscout/exceptions.py +361 -361
- webscout/litagent/Readme.md +298 -298
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +581 -581
- webscout/litagent/constants.py +59 -59
- webscout/litprinter/__init__.py +58 -58
- webscout/models.py +181 -181
- webscout/optimizers.py +419 -419
- webscout/prompt_manager.py +288 -288
- webscout/sanitize.py +1078 -1078
- webscout/scout/README.md +401 -401
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +6 -6
- webscout/scout/core/crawler.py +297 -297
- webscout/scout/core/scout.py +706 -706
- webscout/scout/core/search_result.py +95 -95
- webscout/scout/core/text_analyzer.py +62 -62
- webscout/scout/core/text_utils.py +277 -277
- webscout/scout/core/web_analyzer.py +51 -51
- webscout/scout/element.py +599 -599
- webscout/scout/parsers/__init__.py +69 -69
- webscout/scout/parsers/html5lib_parser.py +172 -172
- webscout/scout/parsers/html_parser.py +236 -236
- webscout/scout/parsers/lxml_parser.py +178 -178
- webscout/scout/utils.py +37 -37
- webscout/swiftcli/Readme.md +323 -323
- webscout/swiftcli/__init__.py +95 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +308 -308
- webscout/swiftcli/core/context.py +104 -104
- webscout/swiftcli/core/group.py +241 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +221 -221
- webscout/swiftcli/decorators/options.py +220 -220
- webscout/swiftcli/decorators/output.py +302 -302
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +135 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +59 -59
- webscout/swiftcli/utils/formatting.py +252 -252
- webscout/swiftcli/utils/parsing.py +267 -267
- webscout/update_checker.py +117 -117
- webscout/version.py +1 -1
- webscout/webscout_search.py +1183 -1183
- webscout/webscout_search_async.py +649 -649
- webscout/yep_search.py +346 -346
- webscout/zeroart/README.md +89 -89
- webscout/zeroart/__init__.py +134 -134
- webscout/zeroart/base.py +66 -66
- webscout/zeroart/effects.py +100 -100
- webscout/zeroart/fonts.py +1238 -1238
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/METADATA +937 -937
- webscout-2025.10.11.dist-info/RECORD +300 -0
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/OPENAI/Qwen3.py +0 -303
- webscout/Provider/OPENAI/qodo.py +0 -630
- webscout/Provider/OPENAI/xenai.py +0 -514
- webscout/tempid.py +0 -134
- webscout-8.3.7.dist-info/RECORD +0 -301
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/WHEEL +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/top_level.txt +0 -0
|
@@ -1,177 +1,177 @@
|
|
|
1
|
-
import requests
|
|
2
|
-
import re
|
|
3
|
-
from typing import Dict, Optional, Generator, Union, Any
|
|
4
|
-
from webscout.AIbase import AISearch, SearchResponse
|
|
5
|
-
from webscout import exceptions
|
|
6
|
-
from webscout.litagent import LitAgent
|
|
7
|
-
from webscout.AIutel import sanitize_stream
|
|
8
|
-
|
|
9
|
-
class Stellar(AISearch):
|
|
10
|
-
"""AI Search provider for stellar.chatastra.ai"""
|
|
11
|
-
def __init__(self, timeout: int = 30, proxies: Optional[dict] = None):
|
|
12
|
-
self.api_endpoint = "https://stellar.chatastra.ai/search/x1GUVzl"
|
|
13
|
-
self.timeout = timeout
|
|
14
|
-
self.proxies = proxies
|
|
15
|
-
self.session = requests.Session()
|
|
16
|
-
self.headers = {
|
|
17
|
-
"accept": "text/x-component",
|
|
18
|
-
"accept-encoding": "gzip, deflate, br, zstd",
|
|
19
|
-
"accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
|
|
20
|
-
"content-type": "multipart/form-data; boundary=----WebKitFormBoundaryQsWD5Qs3QqDkNBPH",
|
|
21
|
-
"dnt": "1",
|
|
22
|
-
"next-action": "efc2643ed9bafe182a010b58ebea17f068ad3985",
|
|
23
|
-
"next-router-state-tree": "%5B%22%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2C%22%2F%22%2C%22refresh%22%5D%7D%2Cnull%2Cnull%2Ctrue%5D",
|
|
24
|
-
"origin": "https://stellar.chatastra.ai",
|
|
25
|
-
"priority": "u=1, i",
|
|
26
|
-
"referer": "https://stellar.chatastra.ai/search/x1GUVzl",
|
|
27
|
-
"sec-ch-ua": '"Microsoft Edge";v="137", "Chromium";v="137", "Not/A)Brand";v="24"',
|
|
28
|
-
"sec-ch-ua-mobile": "?0",
|
|
29
|
-
"sec-ch-ua-platform": '"Windows"',
|
|
30
|
-
"sec-fetch-dest": "empty",
|
|
31
|
-
"sec-fetch-mode": "cors",
|
|
32
|
-
"sec-fetch-site": "same-origin",
|
|
33
|
-
"sec-gpc": "1",
|
|
34
|
-
"user-agent": LitAgent().random(),
|
|
35
|
-
"cookie": "__client_uat=0; __client_uat_K90aduOv=0",
|
|
36
|
-
}
|
|
37
|
-
self.session.headers.update(self.headers)
|
|
38
|
-
if proxies:
|
|
39
|
-
self.session.proxies = proxies
|
|
40
|
-
|
|
41
|
-
def _make_payload(self, prompt: str) -> bytes: # This is a static payload for the demo; in production, generate dynamically as needed
|
|
42
|
-
boundary = "----WebKitFormBoundaryQsWD5Qs3QqDkNBPH"
|
|
43
|
-
parts = [
|
|
44
|
-
f"--{boundary}\r\nContent-Disposition: form-data; name=\"1\"\r\n\r\n{{\"id\":\"71bb616ba5b7cbcac2308fe0c249a9f2d51825b7\",\"bound\":null}}\r\n",
|
|
45
|
-
f"--{boundary}\r\nContent-Disposition: form-data; name=\"2\"\r\n\r\n{{\"id\":\"8bcca1d0cb933b14fefde88dacb2865be3d1d525\",\"bound\":null}}\r\n",
|
|
46
|
-
f"--{boundary}\r\nContent-Disposition: form-data; name=\"3_input\"\r\n\r\n{prompt}\r\n",
|
|
47
|
-
f"--{boundary}\r\nContent-Disposition: form-data; name=\"3_id\"\r\n\r\nx1GUVzl\r\n",
|
|
48
|
-
f"--{boundary}\r\nContent-Disposition: form-data; name=\"3_userId\"\r\n\r\nnull\r\n",
|
|
49
|
-
f"--{boundary}\r\nContent-Disposition: form-data; name=\"0\"\r\n\r\n[{{\"action\":\"$F1\",\"options\":{{\"onSetAIState\":\"$F2\"}}}},{{\"messages\":[],\"chatId\":\"\"}},\"$K3\"]\r\n",
|
|
50
|
-
f"--{boundary}--\r\n"
|
|
51
|
-
]
|
|
52
|
-
return "".join(parts).encode("utf-8")
|
|
53
|
-
|
|
54
|
-
@staticmethod
|
|
55
|
-
def _stellar_extractor(chunk: Union[str, bytes, Dict[str, Any]]) -> Optional[str]:
|
|
56
|
-
"""
|
|
57
|
-
Extracts content from the Stellar stream format with focused pattern matching.
|
|
58
|
-
|
|
59
|
-
Prioritizes the primary diff pattern to avoid duplication and focuses on
|
|
60
|
-
incremental content building from stellar.chatastra.ai streaming response.
|
|
61
|
-
"""
|
|
62
|
-
if isinstance(chunk, bytes):
|
|
63
|
-
try:
|
|
64
|
-
chunk = chunk.decode('utf-8', errors='replace')
|
|
65
|
-
except Exception:
|
|
66
|
-
return None
|
|
67
|
-
if not isinstance(chunk, str):
|
|
68
|
-
return None
|
|
69
|
-
|
|
70
|
-
# Primary pattern: Hex key diff format (most reliable for streaming)
|
|
71
|
-
# Matches: 16:{"diff":[0,"AI"],"next":"$@18"}
|
|
72
|
-
primary_pattern = r'[0-9a-f]+:\{"diff":\[0,"([^"]*?)"\]'
|
|
73
|
-
primary_matches = re.findall(primary_pattern, chunk)
|
|
74
|
-
|
|
75
|
-
if primary_matches:
|
|
76
|
-
# Join the matches and clean up
|
|
77
|
-
extracted_text = ''.join(primary_matches)
|
|
78
|
-
|
|
79
|
-
# Handle escape sequences properly
|
|
80
|
-
extracted_text = extracted_text.replace('\\n', '\n')
|
|
81
|
-
extracted_text = extracted_text.replace('\\r', '\r')
|
|
82
|
-
extracted_text = extracted_text.replace('\\"', '"')
|
|
83
|
-
extracted_text = extracted_text.replace('\\t', '\t')
|
|
84
|
-
extracted_text = extracted_text.replace('\\/', '/')
|
|
85
|
-
extracted_text = extracted_text.replace('\\\\', '\\')
|
|
86
|
-
|
|
87
|
-
# Clean up markdown formatting
|
|
88
|
-
extracted_text = extracted_text.replace('\\*', '*')
|
|
89
|
-
extracted_text = extracted_text.replace('\\#', '#')
|
|
90
|
-
extracted_text = extracted_text.replace('\\[', '[')
|
|
91
|
-
extracted_text = extracted_text.replace('\\]', ']')
|
|
92
|
-
extracted_text = extracted_text.replace('\\(', '(')
|
|
93
|
-
extracted_text = extracted_text.replace('\\)', ')')
|
|
94
|
-
|
|
95
|
-
return extracted_text if extracted_text.strip() else None
|
|
96
|
-
|
|
97
|
-
# # Fallback: Look for Ta24 content blocks (complete responses)
|
|
98
|
-
# if ':Ta24,' in chunk:
|
|
99
|
-
# ta24_pattern = r':Ta24,([^}]*?)(?:\d+:|$)'
|
|
100
|
-
# ta24_matches = re.findall(ta24_pattern, chunk)
|
|
101
|
-
# if ta24_matches:
|
|
102
|
-
# extracted_text = ''.join(ta24_matches)
|
|
103
|
-
# # Basic cleanup
|
|
104
|
-
# extracted_text = extracted_text.replace('\\n', '\n')
|
|
105
|
-
# extracted_text = extracted_text.replace('\\"', '"')
|
|
106
|
-
# return extracted_text.strip() if extracted_text.strip() else None
|
|
107
|
-
|
|
108
|
-
# # Secondary fallback: Direct diff patterns without hex prefix
|
|
109
|
-
# fallback_pattern = r'\{"diff":\[0,"([^"]*?)"\]'
|
|
110
|
-
# fallback_matches = re.findall(fallback_pattern, chunk)
|
|
111
|
-
# if fallback_matches:
|
|
112
|
-
# extracted_text = ''.join(fallback_matches)
|
|
113
|
-
# extracted_text = extracted_text.replace('\\n', '\n')
|
|
114
|
-
# extracted_text = extracted_text.replace('\\"', '"')
|
|
115
|
-
# return extracted_text if extracted_text.strip() else None
|
|
116
|
-
|
|
117
|
-
return None
|
|
118
|
-
|
|
119
|
-
def search(self, prompt: str, stream: bool = False, raw: bool = False) -> Union[SearchResponse, Generator[Union[Dict[str, str], SearchResponse, str], None, None]]:
|
|
120
|
-
payload = self._make_payload(prompt)
|
|
121
|
-
try:
|
|
122
|
-
response = self.session.post(
|
|
123
|
-
self.api_endpoint,
|
|
124
|
-
data=payload,
|
|
125
|
-
timeout=self.timeout,
|
|
126
|
-
proxies=self.proxies,
|
|
127
|
-
stream=stream,
|
|
128
|
-
)
|
|
129
|
-
if not response.ok:
|
|
130
|
-
raise exceptions.APIConnectionError(f"Failed to get response: {response.status_code} {response.text}")
|
|
131
|
-
|
|
132
|
-
def _yield_stream():
|
|
133
|
-
# Use sanitize_stream for real-time extraction from the response iterator
|
|
134
|
-
processed_stream = sanitize_stream(
|
|
135
|
-
data=response.iter_lines(decode_unicode=True),
|
|
136
|
-
intro_value=None,
|
|
137
|
-
to_json=False,
|
|
138
|
-
content_extractor=self._stellar_extractor
|
|
139
|
-
)
|
|
140
|
-
full_response = ""
|
|
141
|
-
for content in processed_stream:
|
|
142
|
-
if content and isinstance(content, str):
|
|
143
|
-
full_response += content
|
|
144
|
-
if raw:
|
|
145
|
-
yield {"text": content}
|
|
146
|
-
else:
|
|
147
|
-
yield content
|
|
148
|
-
# Do NOT yield SearchResponse(full_response) in streaming mode to avoid duplicate output
|
|
149
|
-
|
|
150
|
-
if stream:
|
|
151
|
-
return _yield_stream()
|
|
152
|
-
else:
|
|
153
|
-
# Use sanitize_stream for the full response text
|
|
154
|
-
processed_stream = sanitize_stream(
|
|
155
|
-
data=response.text.splitlines(),
|
|
156
|
-
intro_value=None,
|
|
157
|
-
to_json=False,
|
|
158
|
-
content_extractor=self._stellar_extractor
|
|
159
|
-
)
|
|
160
|
-
full_response = ""
|
|
161
|
-
for content in processed_stream:
|
|
162
|
-
if content and isinstance(content, str):
|
|
163
|
-
full_response += content
|
|
164
|
-
if raw:
|
|
165
|
-
return {"text": full_response}
|
|
166
|
-
else:
|
|
167
|
-
return SearchResponse(full_response)
|
|
168
|
-
except requests.RequestException as e:
|
|
169
|
-
raise exceptions.APIConnectionError(f"Request failed: {e}")
|
|
170
|
-
|
|
171
|
-
if __name__ == "__main__":
|
|
172
|
-
from rich import print
|
|
173
|
-
ai = Stellar()
|
|
174
|
-
user_query = input(">>> ")
|
|
175
|
-
response = ai.search(user_query, stream=True, raw=False)
|
|
176
|
-
for chunk in response:
|
|
177
|
-
print(chunk, end="", flush=True)
|
|
1
|
+
import requests
|
|
2
|
+
import re
|
|
3
|
+
from typing import Dict, Optional, Generator, Union, Any
|
|
4
|
+
from webscout.AIbase import AISearch, SearchResponse
|
|
5
|
+
from webscout import exceptions
|
|
6
|
+
from webscout.litagent import LitAgent
|
|
7
|
+
from webscout.AIutel import sanitize_stream
|
|
8
|
+
|
|
9
|
+
class Stellar(AISearch):
|
|
10
|
+
"""AI Search provider for stellar.chatastra.ai"""
|
|
11
|
+
def __init__(self, timeout: int = 30, proxies: Optional[dict] = None):
|
|
12
|
+
self.api_endpoint = "https://stellar.chatastra.ai/search/x1GUVzl"
|
|
13
|
+
self.timeout = timeout
|
|
14
|
+
self.proxies = proxies
|
|
15
|
+
self.session = requests.Session()
|
|
16
|
+
self.headers = {
|
|
17
|
+
"accept": "text/x-component",
|
|
18
|
+
"accept-encoding": "gzip, deflate, br, zstd",
|
|
19
|
+
"accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
|
|
20
|
+
"content-type": "multipart/form-data; boundary=----WebKitFormBoundaryQsWD5Qs3QqDkNBPH",
|
|
21
|
+
"dnt": "1",
|
|
22
|
+
"next-action": "efc2643ed9bafe182a010b58ebea17f068ad3985",
|
|
23
|
+
"next-router-state-tree": "%5B%22%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2C%22%2F%22%2C%22refresh%22%5D%7D%2Cnull%2Cnull%2Ctrue%5D",
|
|
24
|
+
"origin": "https://stellar.chatastra.ai",
|
|
25
|
+
"priority": "u=1, i",
|
|
26
|
+
"referer": "https://stellar.chatastra.ai/search/x1GUVzl",
|
|
27
|
+
"sec-ch-ua": '"Microsoft Edge";v="137", "Chromium";v="137", "Not/A)Brand";v="24"',
|
|
28
|
+
"sec-ch-ua-mobile": "?0",
|
|
29
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
30
|
+
"sec-fetch-dest": "empty",
|
|
31
|
+
"sec-fetch-mode": "cors",
|
|
32
|
+
"sec-fetch-site": "same-origin",
|
|
33
|
+
"sec-gpc": "1",
|
|
34
|
+
"user-agent": LitAgent().random(),
|
|
35
|
+
"cookie": "__client_uat=0; __client_uat_K90aduOv=0",
|
|
36
|
+
}
|
|
37
|
+
self.session.headers.update(self.headers)
|
|
38
|
+
if proxies:
|
|
39
|
+
self.session.proxies = proxies
|
|
40
|
+
|
|
41
|
+
def _make_payload(self, prompt: str) -> bytes: # This is a static payload for the demo; in production, generate dynamically as needed
|
|
42
|
+
boundary = "----WebKitFormBoundaryQsWD5Qs3QqDkNBPH"
|
|
43
|
+
parts = [
|
|
44
|
+
f"--{boundary}\r\nContent-Disposition: form-data; name=\"1\"\r\n\r\n{{\"id\":\"71bb616ba5b7cbcac2308fe0c249a9f2d51825b7\",\"bound\":null}}\r\n",
|
|
45
|
+
f"--{boundary}\r\nContent-Disposition: form-data; name=\"2\"\r\n\r\n{{\"id\":\"8bcca1d0cb933b14fefde88dacb2865be3d1d525\",\"bound\":null}}\r\n",
|
|
46
|
+
f"--{boundary}\r\nContent-Disposition: form-data; name=\"3_input\"\r\n\r\n{prompt}\r\n",
|
|
47
|
+
f"--{boundary}\r\nContent-Disposition: form-data; name=\"3_id\"\r\n\r\nx1GUVzl\r\n",
|
|
48
|
+
f"--{boundary}\r\nContent-Disposition: form-data; name=\"3_userId\"\r\n\r\nnull\r\n",
|
|
49
|
+
f"--{boundary}\r\nContent-Disposition: form-data; name=\"0\"\r\n\r\n[{{\"action\":\"$F1\",\"options\":{{\"onSetAIState\":\"$F2\"}}}},{{\"messages\":[],\"chatId\":\"\"}},\"$K3\"]\r\n",
|
|
50
|
+
f"--{boundary}--\r\n"
|
|
51
|
+
]
|
|
52
|
+
return "".join(parts).encode("utf-8")
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def _stellar_extractor(chunk: Union[str, bytes, Dict[str, Any]]) -> Optional[str]:
|
|
56
|
+
"""
|
|
57
|
+
Extracts content from the Stellar stream format with focused pattern matching.
|
|
58
|
+
|
|
59
|
+
Prioritizes the primary diff pattern to avoid duplication and focuses on
|
|
60
|
+
incremental content building from stellar.chatastra.ai streaming response.
|
|
61
|
+
"""
|
|
62
|
+
if isinstance(chunk, bytes):
|
|
63
|
+
try:
|
|
64
|
+
chunk = chunk.decode('utf-8', errors='replace')
|
|
65
|
+
except Exception:
|
|
66
|
+
return None
|
|
67
|
+
if not isinstance(chunk, str):
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
# Primary pattern: Hex key diff format (most reliable for streaming)
|
|
71
|
+
# Matches: 16:{"diff":[0,"AI"],"next":"$@18"}
|
|
72
|
+
primary_pattern = r'[0-9a-f]+:\{"diff":\[0,"([^"]*?)"\]'
|
|
73
|
+
primary_matches = re.findall(primary_pattern, chunk)
|
|
74
|
+
|
|
75
|
+
if primary_matches:
|
|
76
|
+
# Join the matches and clean up
|
|
77
|
+
extracted_text = ''.join(primary_matches)
|
|
78
|
+
|
|
79
|
+
# Handle escape sequences properly
|
|
80
|
+
extracted_text = extracted_text.replace('\\n', '\n')
|
|
81
|
+
extracted_text = extracted_text.replace('\\r', '\r')
|
|
82
|
+
extracted_text = extracted_text.replace('\\"', '"')
|
|
83
|
+
extracted_text = extracted_text.replace('\\t', '\t')
|
|
84
|
+
extracted_text = extracted_text.replace('\\/', '/')
|
|
85
|
+
extracted_text = extracted_text.replace('\\\\', '\\')
|
|
86
|
+
|
|
87
|
+
# Clean up markdown formatting
|
|
88
|
+
extracted_text = extracted_text.replace('\\*', '*')
|
|
89
|
+
extracted_text = extracted_text.replace('\\#', '#')
|
|
90
|
+
extracted_text = extracted_text.replace('\\[', '[')
|
|
91
|
+
extracted_text = extracted_text.replace('\\]', ']')
|
|
92
|
+
extracted_text = extracted_text.replace('\\(', '(')
|
|
93
|
+
extracted_text = extracted_text.replace('\\)', ')')
|
|
94
|
+
|
|
95
|
+
return extracted_text if extracted_text.strip() else None
|
|
96
|
+
|
|
97
|
+
# # Fallback: Look for Ta24 content blocks (complete responses)
|
|
98
|
+
# if ':Ta24,' in chunk:
|
|
99
|
+
# ta24_pattern = r':Ta24,([^}]*?)(?:\d+:|$)'
|
|
100
|
+
# ta24_matches = re.findall(ta24_pattern, chunk)
|
|
101
|
+
# if ta24_matches:
|
|
102
|
+
# extracted_text = ''.join(ta24_matches)
|
|
103
|
+
# # Basic cleanup
|
|
104
|
+
# extracted_text = extracted_text.replace('\\n', '\n')
|
|
105
|
+
# extracted_text = extracted_text.replace('\\"', '"')
|
|
106
|
+
# return extracted_text.strip() if extracted_text.strip() else None
|
|
107
|
+
|
|
108
|
+
# # Secondary fallback: Direct diff patterns without hex prefix
|
|
109
|
+
# fallback_pattern = r'\{"diff":\[0,"([^"]*?)"\]'
|
|
110
|
+
# fallback_matches = re.findall(fallback_pattern, chunk)
|
|
111
|
+
# if fallback_matches:
|
|
112
|
+
# extracted_text = ''.join(fallback_matches)
|
|
113
|
+
# extracted_text = extracted_text.replace('\\n', '\n')
|
|
114
|
+
# extracted_text = extracted_text.replace('\\"', '"')
|
|
115
|
+
# return extracted_text if extracted_text.strip() else None
|
|
116
|
+
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
def search(self, prompt: str, stream: bool = False, raw: bool = False) -> Union[SearchResponse, Generator[Union[Dict[str, str], SearchResponse, str], None, None]]:
|
|
120
|
+
payload = self._make_payload(prompt)
|
|
121
|
+
try:
|
|
122
|
+
response = self.session.post(
|
|
123
|
+
self.api_endpoint,
|
|
124
|
+
data=payload,
|
|
125
|
+
timeout=self.timeout,
|
|
126
|
+
proxies=self.proxies,
|
|
127
|
+
stream=stream,
|
|
128
|
+
)
|
|
129
|
+
if not response.ok:
|
|
130
|
+
raise exceptions.APIConnectionError(f"Failed to get response: {response.status_code} {response.text}")
|
|
131
|
+
|
|
132
|
+
def _yield_stream():
|
|
133
|
+
# Use sanitize_stream for real-time extraction from the response iterator
|
|
134
|
+
processed_stream = sanitize_stream(
|
|
135
|
+
data=response.iter_lines(decode_unicode=True),
|
|
136
|
+
intro_value=None,
|
|
137
|
+
to_json=False,
|
|
138
|
+
content_extractor=self._stellar_extractor
|
|
139
|
+
)
|
|
140
|
+
full_response = ""
|
|
141
|
+
for content in processed_stream:
|
|
142
|
+
if content and isinstance(content, str):
|
|
143
|
+
full_response += content
|
|
144
|
+
if raw:
|
|
145
|
+
yield {"text": content}
|
|
146
|
+
else:
|
|
147
|
+
yield content
|
|
148
|
+
# Do NOT yield SearchResponse(full_response) in streaming mode to avoid duplicate output
|
|
149
|
+
|
|
150
|
+
if stream:
|
|
151
|
+
return _yield_stream()
|
|
152
|
+
else:
|
|
153
|
+
# Use sanitize_stream for the full response text
|
|
154
|
+
processed_stream = sanitize_stream(
|
|
155
|
+
data=response.text.splitlines(),
|
|
156
|
+
intro_value=None,
|
|
157
|
+
to_json=False,
|
|
158
|
+
content_extractor=self._stellar_extractor
|
|
159
|
+
)
|
|
160
|
+
full_response = ""
|
|
161
|
+
for content in processed_stream:
|
|
162
|
+
if content and isinstance(content, str):
|
|
163
|
+
full_response += content
|
|
164
|
+
if raw:
|
|
165
|
+
return {"text": full_response}
|
|
166
|
+
else:
|
|
167
|
+
return SearchResponse(full_response)
|
|
168
|
+
except requests.RequestException as e:
|
|
169
|
+
raise exceptions.APIConnectionError(f"Request failed: {e}")
|
|
170
|
+
|
|
171
|
+
if __name__ == "__main__":
|
|
172
|
+
from rich import print
|
|
173
|
+
ai = Stellar()
|
|
174
|
+
user_query = input(">>> ")
|
|
175
|
+
response = ai.search(user_query, stream=True, raw=False)
|
|
176
|
+
for chunk in response:
|
|
177
|
+
print(chunk, end="", flush=True)
|