webscout 8.2.2__py3-none-any.whl → 2026.1.19__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.
- webscout/AIauto.py +524 -143
- webscout/AIbase.py +247 -123
- webscout/AIutel.py +68 -132
- webscout/Bard.py +1072 -535
- webscout/Extra/GitToolkit/__init__.py +2 -2
- webscout/Extra/GitToolkit/gitapi/__init__.py +20 -12
- webscout/Extra/GitToolkit/gitapi/gist.py +142 -0
- webscout/Extra/GitToolkit/gitapi/organization.py +91 -0
- webscout/Extra/GitToolkit/gitapi/repository.py +308 -195
- webscout/Extra/GitToolkit/gitapi/search.py +162 -0
- webscout/Extra/GitToolkit/gitapi/trending.py +236 -0
- webscout/Extra/GitToolkit/gitapi/user.py +128 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +82 -62
- webscout/Extra/YTToolkit/README.md +443 -0
- webscout/Extra/YTToolkit/YTdownloader.py +953 -957
- webscout/Extra/YTToolkit/__init__.py +3 -3
- webscout/Extra/YTToolkit/transcriber.py +595 -476
- webscout/Extra/YTToolkit/ytapi/README.md +230 -0
- webscout/Extra/YTToolkit/ytapi/__init__.py +22 -6
- webscout/Extra/YTToolkit/ytapi/captions.py +190 -0
- webscout/Extra/YTToolkit/ytapi/channel.py +302 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +178 -45
- webscout/Extra/YTToolkit/ytapi/hashtag.py +120 -0
- webscout/Extra/YTToolkit/ytapi/https.py +89 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +59 -59
- webscout/Extra/YTToolkit/ytapi/pool.py +8 -8
- webscout/Extra/YTToolkit/ytapi/query.py +143 -40
- webscout/Extra/YTToolkit/ytapi/shorts.py +122 -0
- webscout/Extra/YTToolkit/ytapi/stream.py +68 -63
- webscout/Extra/YTToolkit/ytapi/suggestions.py +97 -0
- webscout/Extra/YTToolkit/ytapi/utils.py +66 -62
- webscout/Extra/YTToolkit/ytapi/video.py +189 -18
- webscout/Extra/__init__.py +2 -3
- webscout/Extra/gguf.py +1298 -682
- webscout/Extra/tempmail/README.md +488 -0
- webscout/Extra/tempmail/__init__.py +28 -28
- webscout/Extra/tempmail/async_utils.py +143 -141
- webscout/Extra/tempmail/base.py +172 -161
- webscout/Extra/tempmail/cli.py +191 -187
- webscout/Extra/tempmail/emailnator.py +88 -84
- webscout/Extra/tempmail/mail_tm.py +378 -361
- webscout/Extra/tempmail/temp_mail_io.py +304 -292
- webscout/Extra/weather.py +196 -194
- webscout/Extra/weather_ascii.py +17 -15
- webscout/Provider/AISEARCH/PERPLEXED_search.py +175 -0
- webscout/Provider/AISEARCH/Perplexity.py +237 -304
- webscout/Provider/AISEARCH/README.md +106 -0
- webscout/Provider/AISEARCH/__init__.py +16 -10
- webscout/Provider/AISEARCH/brave_search.py +298 -0
- webscout/Provider/AISEARCH/iask_search.py +130 -209
- webscout/Provider/AISEARCH/monica_search.py +200 -246
- webscout/Provider/AISEARCH/webpilotai_search.py +242 -281
- webscout/Provider/Algion.py +413 -0
- webscout/Provider/Andi.py +74 -69
- webscout/Provider/Apriel.py +313 -0
- webscout/Provider/Ayle.py +323 -0
- webscout/Provider/ChatSandbox.py +329 -0
- webscout/Provider/ClaudeOnline.py +365 -0
- webscout/Provider/Cohere.py +232 -208
- webscout/Provider/DeepAI.py +367 -0
- webscout/Provider/Deepinfra.py +343 -173
- webscout/Provider/EssentialAI.py +217 -0
- webscout/Provider/ExaAI.py +274 -261
- webscout/Provider/Gemini.py +60 -54
- webscout/Provider/GithubChat.py +385 -367
- webscout/Provider/Gradient.py +286 -0
- webscout/Provider/Groq.py +556 -670
- webscout/Provider/HadadXYZ.py +323 -0
- webscout/Provider/HeckAI.py +392 -233
- webscout/Provider/HuggingFace.py +387 -0
- webscout/Provider/IBM.py +340 -0
- webscout/Provider/Jadve.py +317 -266
- webscout/Provider/K2Think.py +306 -0
- webscout/Provider/Koboldai.py +221 -381
- webscout/Provider/Netwrck.py +273 -228
- webscout/Provider/Nvidia.py +310 -0
- webscout/Provider/OPENAI/DeepAI.py +489 -0
- webscout/Provider/OPENAI/K2Think.py +423 -0
- webscout/Provider/OPENAI/PI.py +463 -0
- webscout/Provider/OPENAI/README.md +890 -0
- webscout/Provider/OPENAI/TogetherAI.py +405 -0
- webscout/Provider/OPENAI/TwoAI.py +255 -0
- webscout/Provider/OPENAI/__init__.py +148 -25
- webscout/Provider/OPENAI/ai4chat.py +348 -0
- webscout/Provider/OPENAI/akashgpt.py +436 -0
- webscout/Provider/OPENAI/algion.py +303 -0
- webscout/Provider/OPENAI/ayle.py +365 -0
- webscout/Provider/OPENAI/base.py +253 -46
- webscout/Provider/OPENAI/cerebras.py +296 -0
- webscout/Provider/OPENAI/chatgpt.py +514 -193
- webscout/Provider/OPENAI/chatsandbox.py +233 -0
- webscout/Provider/OPENAI/deepinfra.py +403 -272
- webscout/Provider/OPENAI/e2b.py +2370 -1350
- webscout/Provider/OPENAI/elmo.py +278 -0
- webscout/Provider/OPENAI/exaai.py +186 -138
- webscout/Provider/OPENAI/freeassist.py +446 -0
- webscout/Provider/OPENAI/gradient.py +448 -0
- webscout/Provider/OPENAI/groq.py +380 -0
- webscout/Provider/OPENAI/hadadxyz.py +292 -0
- webscout/Provider/OPENAI/heckai.py +100 -104
- webscout/Provider/OPENAI/huggingface.py +321 -0
- webscout/Provider/OPENAI/ibm.py +425 -0
- webscout/Provider/OPENAI/llmchat.py +253 -0
- webscout/Provider/OPENAI/llmchatco.py +378 -327
- webscout/Provider/OPENAI/meta.py +541 -0
- webscout/Provider/OPENAI/netwrck.py +110 -84
- webscout/Provider/OPENAI/nvidia.py +317 -0
- webscout/Provider/OPENAI/oivscode.py +348 -0
- webscout/Provider/OPENAI/openrouter.py +328 -0
- webscout/Provider/OPENAI/pydantic_imports.py +1 -0
- webscout/Provider/OPENAI/sambanova.py +397 -0
- webscout/Provider/OPENAI/sonus.py +126 -115
- webscout/Provider/OPENAI/textpollinations.py +218 -133
- webscout/Provider/OPENAI/toolbaz.py +136 -166
- webscout/Provider/OPENAI/typefully.py +419 -0
- webscout/Provider/OPENAI/typliai.py +279 -0
- webscout/Provider/OPENAI/utils.py +314 -211
- webscout/Provider/OPENAI/wisecat.py +103 -125
- webscout/Provider/OPENAI/writecream.py +185 -156
- webscout/Provider/OPENAI/x0gpt.py +227 -136
- webscout/Provider/OPENAI/zenmux.py +380 -0
- webscout/Provider/OpenRouter.py +386 -0
- webscout/Provider/Openai.py +337 -496
- webscout/Provider/PI.py +443 -344
- webscout/Provider/QwenLM.py +346 -254
- webscout/Provider/STT/__init__.py +28 -0
- webscout/Provider/STT/base.py +303 -0
- webscout/Provider/STT/elevenlabs.py +264 -0
- webscout/Provider/Sambanova.py +317 -0
- webscout/Provider/TTI/README.md +69 -0
- webscout/Provider/TTI/__init__.py +37 -12
- webscout/Provider/TTI/base.py +147 -0
- webscout/Provider/TTI/claudeonline.py +393 -0
- webscout/Provider/TTI/magicstudio.py +292 -0
- webscout/Provider/TTI/miragic.py +180 -0
- webscout/Provider/TTI/pollinations.py +331 -0
- webscout/Provider/TTI/together.py +334 -0
- webscout/Provider/TTI/utils.py +14 -0
- webscout/Provider/TTS/README.md +186 -0
- webscout/Provider/TTS/__init__.py +43 -7
- webscout/Provider/TTS/base.py +523 -0
- webscout/Provider/TTS/deepgram.py +286 -156
- webscout/Provider/TTS/elevenlabs.py +189 -111
- webscout/Provider/TTS/freetts.py +218 -0
- webscout/Provider/TTS/murfai.py +288 -113
- webscout/Provider/TTS/openai_fm.py +364 -0
- webscout/Provider/TTS/parler.py +203 -111
- webscout/Provider/TTS/qwen.py +334 -0
- webscout/Provider/TTS/sherpa.py +286 -0
- webscout/Provider/TTS/speechma.py +693 -180
- webscout/Provider/TTS/streamElements.py +275 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TextPollinationsAI.py +221 -121
- webscout/Provider/TogetherAI.py +450 -0
- webscout/Provider/TwoAI.py +309 -199
- webscout/Provider/TypliAI.py +311 -0
- webscout/Provider/UNFINISHED/ChatHub.py +219 -0
- webscout/Provider/{OPENAI/glider.py → UNFINISHED/ChutesAI.py} +160 -145
- webscout/Provider/UNFINISHED/GizAI.py +300 -0
- webscout/Provider/UNFINISHED/Marcus.py +218 -0
- webscout/Provider/UNFINISHED/Qodo.py +481 -0
- webscout/Provider/UNFINISHED/XenAI.py +330 -0
- webscout/Provider/{Youchat.py → UNFINISHED/Youchat.py} +64 -47
- webscout/Provider/UNFINISHED/aihumanizer.py +41 -0
- webscout/Provider/UNFINISHED/grammerchecker.py +37 -0
- webscout/Provider/UNFINISHED/liner.py +342 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +246 -0
- webscout/Provider/UNFINISHED/samurai.py +231 -0
- webscout/Provider/WiseCat.py +256 -196
- webscout/Provider/WrDoChat.py +390 -0
- webscout/Provider/__init__.py +115 -198
- webscout/Provider/ai4chat.py +181 -202
- webscout/Provider/akashgpt.py +330 -342
- webscout/Provider/cerebras.py +397 -242
- webscout/Provider/cleeai.py +236 -213
- webscout/Provider/elmo.py +291 -234
- webscout/Provider/geminiapi.py +343 -208
- webscout/Provider/julius.py +245 -223
- webscout/Provider/learnfastai.py +333 -266
- webscout/Provider/llama3mitril.py +230 -180
- webscout/Provider/llmchat.py +308 -213
- webscout/Provider/llmchatco.py +321 -311
- webscout/Provider/meta.py +996 -794
- webscout/Provider/oivscode.py +332 -0
- webscout/Provider/searchchat.py +316 -293
- webscout/Provider/sonus.py +264 -208
- webscout/Provider/toolbaz.py +359 -320
- webscout/Provider/turboseek.py +332 -219
- webscout/Provider/typefully.py +262 -280
- webscout/Provider/x0gpt.py +332 -256
- webscout/__init__.py +31 -38
- webscout/__main__.py +5 -5
- webscout/cli.py +585 -293
- webscout/client.py +1497 -0
- webscout/conversation.py +140 -565
- webscout/exceptions.py +383 -339
- webscout/litagent/__init__.py +29 -29
- webscout/litagent/agent.py +492 -455
- webscout/litagent/constants.py +60 -60
- webscout/models.py +505 -181
- webscout/optimizers.py +32 -378
- webscout/prompt_manager.py +376 -274
- webscout/sanitize.py +1514 -0
- webscout/scout/README.md +452 -0
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +7 -7
- webscout/scout/core/crawler.py +330 -140
- webscout/scout/core/scout.py +800 -568
- webscout/scout/core/search_result.py +51 -96
- webscout/scout/core/text_analyzer.py +64 -63
- webscout/scout/core/text_utils.py +412 -277
- webscout/scout/core/web_analyzer.py +54 -52
- webscout/scout/element.py +872 -460
- webscout/scout/parsers/__init__.py +70 -69
- webscout/scout/parsers/html5lib_parser.py +182 -172
- webscout/scout/parsers/html_parser.py +238 -236
- webscout/scout/parsers/lxml_parser.py +203 -178
- webscout/scout/utils.py +38 -37
- webscout/search/__init__.py +47 -0
- webscout/search/base.py +201 -0
- webscout/search/bing_main.py +45 -0
- webscout/search/brave_main.py +92 -0
- webscout/search/duckduckgo_main.py +57 -0
- webscout/search/engines/__init__.py +127 -0
- webscout/search/engines/bing/__init__.py +15 -0
- webscout/search/engines/bing/base.py +35 -0
- webscout/search/engines/bing/images.py +114 -0
- webscout/search/engines/bing/news.py +96 -0
- webscout/search/engines/bing/suggestions.py +36 -0
- webscout/search/engines/bing/text.py +109 -0
- webscout/search/engines/brave/__init__.py +19 -0
- webscout/search/engines/brave/base.py +47 -0
- webscout/search/engines/brave/images.py +213 -0
- webscout/search/engines/brave/news.py +353 -0
- webscout/search/engines/brave/suggestions.py +318 -0
- webscout/search/engines/brave/text.py +167 -0
- webscout/search/engines/brave/videos.py +364 -0
- webscout/search/engines/duckduckgo/__init__.py +25 -0
- webscout/search/engines/duckduckgo/answers.py +80 -0
- webscout/search/engines/duckduckgo/base.py +189 -0
- webscout/search/engines/duckduckgo/images.py +100 -0
- webscout/search/engines/duckduckgo/maps.py +183 -0
- webscout/search/engines/duckduckgo/news.py +70 -0
- webscout/search/engines/duckduckgo/suggestions.py +22 -0
- webscout/search/engines/duckduckgo/text.py +221 -0
- webscout/search/engines/duckduckgo/translate.py +48 -0
- webscout/search/engines/duckduckgo/videos.py +80 -0
- webscout/search/engines/duckduckgo/weather.py +84 -0
- webscout/search/engines/mojeek.py +61 -0
- webscout/search/engines/wikipedia.py +77 -0
- webscout/search/engines/yahoo/__init__.py +41 -0
- webscout/search/engines/yahoo/answers.py +19 -0
- webscout/search/engines/yahoo/base.py +34 -0
- webscout/search/engines/yahoo/images.py +323 -0
- webscout/search/engines/yahoo/maps.py +19 -0
- webscout/search/engines/yahoo/news.py +258 -0
- webscout/search/engines/yahoo/suggestions.py +140 -0
- webscout/search/engines/yahoo/text.py +273 -0
- webscout/search/engines/yahoo/translate.py +19 -0
- webscout/search/engines/yahoo/videos.py +302 -0
- webscout/search/engines/yahoo/weather.py +220 -0
- webscout/search/engines/yandex.py +67 -0
- webscout/search/engines/yep/__init__.py +13 -0
- webscout/search/engines/yep/base.py +34 -0
- webscout/search/engines/yep/images.py +101 -0
- webscout/search/engines/yep/suggestions.py +38 -0
- webscout/search/engines/yep/text.py +99 -0
- webscout/search/http_client.py +172 -0
- webscout/search/results.py +141 -0
- webscout/search/yahoo_main.py +57 -0
- webscout/search/yep_main.py +48 -0
- webscout/server/__init__.py +48 -0
- webscout/server/config.py +78 -0
- webscout/server/exceptions.py +69 -0
- webscout/server/providers.py +286 -0
- webscout/server/request_models.py +131 -0
- webscout/server/request_processing.py +404 -0
- webscout/server/routes.py +642 -0
- webscout/server/server.py +351 -0
- webscout/server/ui_templates.py +1171 -0
- webscout/swiftcli/__init__.py +79 -809
- webscout/swiftcli/core/__init__.py +7 -0
- webscout/swiftcli/core/cli.py +574 -0
- webscout/swiftcli/core/context.py +98 -0
- webscout/swiftcli/core/group.py +268 -0
- webscout/swiftcli/decorators/__init__.py +28 -0
- webscout/swiftcli/decorators/command.py +243 -0
- webscout/swiftcli/decorators/options.py +247 -0
- webscout/swiftcli/decorators/output.py +392 -0
- webscout/swiftcli/exceptions.py +21 -0
- webscout/swiftcli/plugins/__init__.py +9 -0
- webscout/swiftcli/plugins/base.py +134 -0
- webscout/swiftcli/plugins/manager.py +269 -0
- webscout/swiftcli/utils/__init__.py +58 -0
- webscout/swiftcli/utils/formatting.py +251 -0
- webscout/swiftcli/utils/parsing.py +368 -0
- webscout/update_checker.py +280 -136
- webscout/utils.py +28 -14
- webscout/version.py +2 -1
- webscout/version.py.bak +3 -0
- webscout/zeroart/__init__.py +218 -55
- webscout/zeroart/base.py +70 -60
- webscout/zeroart/effects.py +155 -99
- webscout/zeroart/fonts.py +1799 -816
- webscout-2026.1.19.dist-info/METADATA +638 -0
- webscout-2026.1.19.dist-info/RECORD +312 -0
- {webscout-8.2.2.dist-info → webscout-2026.1.19.dist-info}/WHEEL +1 -1
- webscout-2026.1.19.dist-info/entry_points.txt +4 -0
- webscout-2026.1.19.dist-info/top_level.txt +1 -0
- inferno/__init__.py +0 -6
- inferno/__main__.py +0 -9
- inferno/cli.py +0 -6
- webscout/DWEBS.py +0 -477
- webscout/Extra/autocoder/__init__.py +0 -9
- webscout/Extra/autocoder/autocoder.py +0 -849
- webscout/Extra/autocoder/autocoder_utiles.py +0 -332
- webscout/LLM.py +0 -442
- webscout/Litlogger/__init__.py +0 -67
- webscout/Litlogger/core/__init__.py +0 -6
- webscout/Litlogger/core/level.py +0 -23
- webscout/Litlogger/core/logger.py +0 -165
- webscout/Litlogger/handlers/__init__.py +0 -12
- webscout/Litlogger/handlers/console.py +0 -33
- webscout/Litlogger/handlers/file.py +0 -143
- webscout/Litlogger/handlers/network.py +0 -173
- webscout/Litlogger/styles/__init__.py +0 -7
- webscout/Litlogger/styles/colors.py +0 -249
- webscout/Litlogger/styles/formats.py +0 -458
- webscout/Litlogger/styles/text.py +0 -87
- webscout/Litlogger/utils/__init__.py +0 -6
- webscout/Litlogger/utils/detectors.py +0 -153
- webscout/Litlogger/utils/formatters.py +0 -200
- webscout/Local/__init__.py +0 -12
- webscout/Local/__main__.py +0 -9
- webscout/Local/api.py +0 -576
- webscout/Local/cli.py +0 -516
- webscout/Local/config.py +0 -75
- webscout/Local/llm.py +0 -287
- webscout/Local/model_manager.py +0 -253
- webscout/Local/server.py +0 -721
- webscout/Local/utils.py +0 -93
- webscout/Provider/AI21.py +0 -177
- webscout/Provider/AISEARCH/DeepFind.py +0 -250
- webscout/Provider/AISEARCH/ISou.py +0 -256
- webscout/Provider/AISEARCH/felo_search.py +0 -228
- webscout/Provider/AISEARCH/genspark_search.py +0 -208
- webscout/Provider/AISEARCH/hika_search.py +0 -194
- webscout/Provider/AISEARCH/scira_search.py +0 -324
- webscout/Provider/Aitopia.py +0 -292
- webscout/Provider/AllenAI.py +0 -413
- webscout/Provider/Blackboxai.py +0 -229
- webscout/Provider/C4ai.py +0 -432
- webscout/Provider/ChatGPTClone.py +0 -226
- webscout/Provider/ChatGPTES.py +0 -237
- webscout/Provider/ChatGPTGratis.py +0 -194
- webscout/Provider/Chatify.py +0 -175
- webscout/Provider/Cloudflare.py +0 -273
- webscout/Provider/DeepSeek.py +0 -196
- webscout/Provider/ElectronHub.py +0 -709
- webscout/Provider/ExaChat.py +0 -342
- webscout/Provider/Free2GPT.py +0 -241
- webscout/Provider/GPTWeb.py +0 -193
- webscout/Provider/Glider.py +0 -211
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/HuggingFaceChat.py +0 -462
- webscout/Provider/Hunyuan.py +0 -272
- webscout/Provider/LambdaChat.py +0 -392
- webscout/Provider/Llama.py +0 -200
- webscout/Provider/Llama3.py +0 -204
- webscout/Provider/Marcus.py +0 -148
- webscout/Provider/OLLAMA.py +0 -396
- webscout/Provider/OPENAI/c4ai.py +0 -367
- webscout/Provider/OPENAI/chatgptclone.py +0 -460
- webscout/Provider/OPENAI/exachat.py +0 -433
- webscout/Provider/OPENAI/freeaichat.py +0 -352
- webscout/Provider/OPENAI/opkfc.py +0 -488
- webscout/Provider/OPENAI/scirachat.py +0 -463
- webscout/Provider/OPENAI/standardinput.py +0 -425
- webscout/Provider/OPENAI/typegpt.py +0 -346
- webscout/Provider/OPENAI/uncovrAI.py +0 -455
- webscout/Provider/OPENAI/venice.py +0 -413
- webscout/Provider/OPENAI/yep.py +0 -327
- webscout/Provider/OpenGPT.py +0 -199
- webscout/Provider/Perplexitylabs.py +0 -415
- webscout/Provider/Phind.py +0 -535
- webscout/Provider/PizzaGPT.py +0 -198
- webscout/Provider/Reka.py +0 -214
- webscout/Provider/StandardInput.py +0 -278
- webscout/Provider/TTI/AiForce/__init__.py +0 -22
- webscout/Provider/TTI/AiForce/async_aiforce.py +0 -224
- webscout/Provider/TTI/AiForce/sync_aiforce.py +0 -245
- webscout/Provider/TTI/FreeAIPlayground/__init__.py +0 -9
- webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +0 -181
- webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +0 -180
- webscout/Provider/TTI/ImgSys/__init__.py +0 -23
- webscout/Provider/TTI/ImgSys/async_imgsys.py +0 -202
- webscout/Provider/TTI/ImgSys/sync_imgsys.py +0 -195
- webscout/Provider/TTI/MagicStudio/__init__.py +0 -2
- webscout/Provider/TTI/MagicStudio/async_magicstudio.py +0 -111
- webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +0 -109
- webscout/Provider/TTI/Nexra/__init__.py +0 -22
- webscout/Provider/TTI/Nexra/async_nexra.py +0 -286
- webscout/Provider/TTI/Nexra/sync_nexra.py +0 -258
- webscout/Provider/TTI/PollinationsAI/__init__.py +0 -23
- webscout/Provider/TTI/PollinationsAI/async_pollinations.py +0 -311
- webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +0 -265
- webscout/Provider/TTI/aiarta/__init__.py +0 -2
- webscout/Provider/TTI/aiarta/async_aiarta.py +0 -482
- webscout/Provider/TTI/aiarta/sync_aiarta.py +0 -440
- webscout/Provider/TTI/artbit/__init__.py +0 -22
- webscout/Provider/TTI/artbit/async_artbit.py +0 -155
- webscout/Provider/TTI/artbit/sync_artbit.py +0 -148
- webscout/Provider/TTI/fastflux/__init__.py +0 -22
- webscout/Provider/TTI/fastflux/async_fastflux.py +0 -261
- webscout/Provider/TTI/fastflux/sync_fastflux.py +0 -252
- webscout/Provider/TTI/huggingface/__init__.py +0 -22
- webscout/Provider/TTI/huggingface/async_huggingface.py +0 -199
- webscout/Provider/TTI/huggingface/sync_huggingface.py +0 -195
- webscout/Provider/TTI/piclumen/__init__.py +0 -23
- webscout/Provider/TTI/piclumen/async_piclumen.py +0 -268
- webscout/Provider/TTI/piclumen/sync_piclumen.py +0 -233
- webscout/Provider/TTI/pixelmuse/__init__.py +0 -4
- webscout/Provider/TTI/pixelmuse/async_pixelmuse.py +0 -249
- webscout/Provider/TTI/pixelmuse/sync_pixelmuse.py +0 -182
- webscout/Provider/TTI/talkai/__init__.py +0 -4
- webscout/Provider/TTI/talkai/async_talkai.py +0 -229
- webscout/Provider/TTI/talkai/sync_talkai.py +0 -207
- webscout/Provider/TTS/gesserit.py +0 -127
- webscout/Provider/TeachAnything.py +0 -187
- webscout/Provider/Venice.py +0 -219
- webscout/Provider/VercelAI.py +0 -234
- webscout/Provider/WebSim.py +0 -228
- webscout/Provider/Writecream.py +0 -211
- webscout/Provider/WritingMate.py +0 -197
- webscout/Provider/aimathgpt.py +0 -189
- webscout/Provider/askmyai.py +0 -158
- webscout/Provider/asksteve.py +0 -203
- webscout/Provider/bagoodex.py +0 -145
- webscout/Provider/chatglm.py +0 -205
- webscout/Provider/copilot.py +0 -428
- webscout/Provider/freeaichat.py +0 -271
- webscout/Provider/gaurish.py +0 -244
- webscout/Provider/geminiprorealtime.py +0 -160
- webscout/Provider/granite.py +0 -187
- webscout/Provider/hermes.py +0 -219
- webscout/Provider/koala.py +0 -268
- webscout/Provider/labyrinth.py +0 -340
- webscout/Provider/lepton.py +0 -194
- webscout/Provider/llamatutor.py +0 -192
- webscout/Provider/multichat.py +0 -325
- webscout/Provider/promptrefine.py +0 -193
- webscout/Provider/scira_chat.py +0 -277
- webscout/Provider/scnet.py +0 -187
- webscout/Provider/talkai.py +0 -194
- webscout/Provider/tutorai.py +0 -252
- webscout/Provider/typegpt.py +0 -232
- webscout/Provider/uncovr.py +0 -312
- webscout/Provider/yep.py +0 -376
- webscout/litprinter/__init__.py +0 -59
- webscout/scout/core.py +0 -881
- webscout/tempid.py +0 -128
- webscout/webscout_search.py +0 -1346
- webscout/webscout_search_async.py +0 -877
- webscout/yep_search.py +0 -297
- webscout-8.2.2.dist-info/METADATA +0 -734
- webscout-8.2.2.dist-info/RECORD +0 -309
- webscout-8.2.2.dist-info/entry_points.txt +0 -5
- webscout-8.2.2.dist-info/top_level.txt +0 -3
- webstoken/__init__.py +0 -30
- webstoken/classifier.py +0 -189
- webstoken/keywords.py +0 -216
- webstoken/language.py +0 -128
- webstoken/ner.py +0 -164
- webstoken/normalizer.py +0 -35
- webstoken/processor.py +0 -77
- webstoken/sentiment.py +0 -206
- webstoken/stemmer.py +0 -73
- webstoken/tagger.py +0 -60
- webstoken/tokenizer.py +0 -158
- {webscout-8.2.2.dist-info → webscout-2026.1.19.dist-info/licenses}/LICENSE.md +0 -0
|
@@ -1,47 +1,24 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import random
|
|
3
|
+
import time
|
|
4
|
+
from typing import Any, Dict, Generator, List, Optional, Union
|
|
3
5
|
from uuid import uuid4
|
|
4
|
-
|
|
6
|
+
|
|
5
7
|
from curl_cffi import requests
|
|
6
8
|
|
|
7
|
-
from webscout.AIbase import AISearch
|
|
8
9
|
from webscout import exceptions
|
|
10
|
+
from webscout.AIbase import AISearch, SearchResponse
|
|
9
11
|
from webscout.litagent import LitAgent
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class Response:
|
|
13
|
-
"""A wrapper class for Perplexity API responses.
|
|
14
|
-
|
|
15
|
-
This class automatically converts response objects to their text representation
|
|
16
|
-
when printed or converted to string.
|
|
17
|
-
|
|
18
|
-
Attributes:
|
|
19
|
-
text (str): The text content of the response
|
|
20
|
-
|
|
21
|
-
Example:
|
|
22
|
-
>>> response = Response("Hello, world!")
|
|
23
|
-
>>> print(response)
|
|
24
|
-
Hello, world!
|
|
25
|
-
>>> str(response)
|
|
26
|
-
'Hello, world!'
|
|
27
|
-
"""
|
|
28
|
-
def __init__(self, text: str):
|
|
29
|
-
self.text = text
|
|
30
|
-
|
|
31
|
-
def __str__(self):
|
|
32
|
-
return self.text
|
|
33
|
-
|
|
34
|
-
def __repr__(self):
|
|
35
|
-
return self.text
|
|
12
|
+
from webscout.sanitize import sanitize_stream
|
|
36
13
|
|
|
37
14
|
|
|
38
15
|
class Perplexity(AISearch):
|
|
39
16
|
"""A class to interact with the Perplexity AI search API.
|
|
40
|
-
|
|
17
|
+
|
|
41
18
|
Perplexity provides a powerful search interface that returns AI-generated responses
|
|
42
19
|
based on web content. It supports both streaming and non-streaming responses,
|
|
43
20
|
multiple search modes, and model selection.
|
|
44
|
-
|
|
21
|
+
|
|
45
22
|
Basic Usage:
|
|
46
23
|
>>> from webscout import Perplexity
|
|
47
24
|
>>> ai = Perplexity()
|
|
@@ -49,311 +26,267 @@ class Perplexity(AISearch):
|
|
|
49
26
|
>>> response = ai.search("What is Python?")
|
|
50
27
|
>>> print(response)
|
|
51
28
|
Python is a high-level programming language...
|
|
52
|
-
|
|
29
|
+
|
|
53
30
|
>>> # Streaming example
|
|
54
31
|
>>> for chunk in ai.search("Tell me about AI", stream=True):
|
|
55
32
|
... print(chunk, end="", flush=True)
|
|
56
33
|
Artificial Intelligence is...
|
|
57
|
-
|
|
58
|
-
>>> # Pro search with specific model (requires authentication via cookies)
|
|
59
|
-
>>> cookies = {"perplexity-user": "your_cookie_value"}
|
|
60
|
-
>>> ai_pro = Perplexity(cookies=cookies)
|
|
61
|
-
>>> response = ai_pro.search("Latest AI research", mode="pro", model="gpt-4o")
|
|
62
|
-
>>> print(response)
|
|
63
|
-
|
|
64
|
-
>>> # Raw response format
|
|
65
|
-
>>> for chunk in ai.search("Hello", stream=True, raw=True):
|
|
66
|
-
... print(chunk)
|
|
67
|
-
{'text': 'Hello'}
|
|
68
|
-
{'text': ' there!'}
|
|
69
|
-
|
|
70
|
-
Args:
|
|
71
|
-
cookies (dict, optional): Cookies to use for authentication. Defaults to None.
|
|
72
|
-
timeout (int, optional): Request timeout in seconds. Defaults to 60.
|
|
73
|
-
proxies (dict, optional): Proxy configuration for requests. Defaults to None.
|
|
74
34
|
"""
|
|
75
|
-
|
|
35
|
+
|
|
76
36
|
def __init__(
|
|
77
37
|
self,
|
|
78
38
|
cookies: Optional[Dict[str, str]] = None,
|
|
79
39
|
timeout: int = 60,
|
|
80
|
-
proxies: Optional[Dict[str, str]] = None
|
|
40
|
+
proxies: Optional[Dict[str, str]] = None,
|
|
81
41
|
):
|
|
82
42
|
"""
|
|
83
43
|
Initialize the Perplexity client.
|
|
84
|
-
|
|
85
|
-
Args:
|
|
86
|
-
cookies (dict, optional): Cookies to use for authentication. Defaults to None.
|
|
87
|
-
timeout (int, optional): Request timeout in seconds. Defaults to 60.
|
|
88
|
-
proxies (dict, optional): Proxy configuration for requests. Defaults to None.
|
|
89
44
|
"""
|
|
90
45
|
self.timeout = timeout
|
|
91
46
|
self.agent = LitAgent()
|
|
92
|
-
self.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
'user-agent': self.agent.random(),
|
|
113
|
-
}, cookies=cookies or {}, impersonate='chrome')
|
|
114
|
-
|
|
115
|
-
# Apply proxies if provided
|
|
47
|
+
self.headers = {
|
|
48
|
+
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
49
|
+
"accept-language": "en-US,en;q=0.9",
|
|
50
|
+
"cache-control": "max-age=0",
|
|
51
|
+
"dnt": "1",
|
|
52
|
+
"priority": "u=0, i",
|
|
53
|
+
"sec-ch-ua": '"Not;A=Brand";v="24", "Chromium";v="128"',
|
|
54
|
+
"sec-ch-ua-mobile": "?0",
|
|
55
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
56
|
+
"sec-fetch-dest": "document",
|
|
57
|
+
"sec-fetch-mode": "navigate",
|
|
58
|
+
"sec-fetch-site": "same-origin",
|
|
59
|
+
"sec-fetch-user": "?1",
|
|
60
|
+
"upgrade-insecure-requests": "1",
|
|
61
|
+
"user-agent": self.agent.random(),
|
|
62
|
+
}
|
|
63
|
+
self.session = requests.Session(
|
|
64
|
+
headers=self.headers, cookies=cookies or {}, impersonate="chrome"
|
|
65
|
+
)
|
|
66
|
+
|
|
116
67
|
if proxies:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
self.copilot = 0 if not cookies else float('inf')
|
|
137
|
-
self.file_upload = 0 if not cookies else float('inf')
|
|
138
|
-
|
|
68
|
+
from typing import cast
|
|
69
|
+
self.session.proxies.update(cast(Any, proxies))
|
|
70
|
+
|
|
71
|
+
# Initialize session info if possible
|
|
72
|
+
try:
|
|
73
|
+
self.timestamp = format(random.getrandbits(32), "08x")
|
|
74
|
+
response = self.session.get(
|
|
75
|
+
f"https://www.perplexity.ai/socket.io/?EIO=4&transport=polling&t={self.timestamp}"
|
|
76
|
+
)
|
|
77
|
+
if response.status_code == 200 and response.text.startswith("0"):
|
|
78
|
+
self.sid = json.loads(response.text[1:])["sid"]
|
|
79
|
+
self.session.post(
|
|
80
|
+
f"https://www.perplexity.ai/socket.io/?EIO=4&transport=polling&t={self.timestamp}&sid={self.sid}",
|
|
81
|
+
data='40{"jwt":"anonymous-ask-user"}',
|
|
82
|
+
)
|
|
83
|
+
self.session.get("https://www.perplexity.ai/api/auth/session")
|
|
84
|
+
except Exception:
|
|
85
|
+
pass
|
|
86
|
+
|
|
139
87
|
def _extract_answer(self, response):
|
|
140
88
|
"""
|
|
141
89
|
Extract the answer from the response.
|
|
142
|
-
|
|
143
|
-
Args:
|
|
144
|
-
response (dict): The response from Perplexity AI.
|
|
145
|
-
|
|
146
|
-
Returns:
|
|
147
|
-
str: The extracted answer text.
|
|
148
90
|
"""
|
|
149
91
|
if not response:
|
|
150
92
|
return ""
|
|
151
|
-
|
|
152
|
-
#
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
93
|
+
|
|
94
|
+
# Handle FINAL step at top level
|
|
95
|
+
if response.get("step_type") == "FINAL" and "content" in response:
|
|
96
|
+
content = response["content"]
|
|
97
|
+
if isinstance(content, dict) and "answer" in content:
|
|
98
|
+
answer_content = content["answer"]
|
|
99
|
+
if isinstance(answer_content, str):
|
|
100
|
+
try:
|
|
101
|
+
return json.loads(answer_content).get("answer", "")
|
|
102
|
+
except Exception:
|
|
103
|
+
return str(answer_content)
|
|
104
|
+
elif isinstance(answer_content, dict):
|
|
105
|
+
return answer_content.get("answer", "")
|
|
106
|
+
|
|
107
|
+
# New pattern extraction
|
|
108
|
+
if "text" in response:
|
|
109
|
+
text_val = response["text"]
|
|
110
|
+
if isinstance(text_val, list):
|
|
111
|
+
for step in text_val:
|
|
112
|
+
if step.get("type") == "answer" and "value" in step:
|
|
113
|
+
return step["value"]
|
|
114
|
+
if step.get("step_type") == "FINAL" and "content" in step:
|
|
115
|
+
content = step["content"]
|
|
116
|
+
if isinstance(content, dict) and "answer" in content:
|
|
117
|
+
answer_content = content["answer"]
|
|
118
|
+
if isinstance(answer_content, str):
|
|
119
|
+
try:
|
|
120
|
+
return json.loads(answer_content).get("answer", "")
|
|
121
|
+
except Exception:
|
|
122
|
+
return str(answer_content)
|
|
123
|
+
elif isinstance(answer_content, dict):
|
|
124
|
+
return answer_content.get("answer", "")
|
|
125
|
+
elif isinstance(text_val, str):
|
|
126
|
+
try:
|
|
127
|
+
return json.loads(text_val).get("answer", text_val)
|
|
128
|
+
except Exception:
|
|
129
|
+
return text_val
|
|
130
|
+
return ""
|
|
131
|
+
|
|
132
|
+
# New pattern extraction
|
|
133
|
+
if "text" in response:
|
|
134
|
+
text_val = response["text"]
|
|
135
|
+
if isinstance(text_val, list):
|
|
136
|
+
for step in text_val:
|
|
137
|
+
if step.get("type") == "answer" and "value" in step:
|
|
138
|
+
return step["value"]
|
|
139
|
+
if step.get("step_type") == "FINAL" and "content" in step:
|
|
140
|
+
content = step["content"]
|
|
141
|
+
if isinstance(content, dict) and "answer" in content:
|
|
142
|
+
answer_content = content["answer"]
|
|
143
|
+
if isinstance(answer_content, str):
|
|
144
|
+
try:
|
|
145
|
+
return json.loads(answer_content).get("answer", "")
|
|
146
|
+
except Exception:
|
|
147
|
+
return str(answer_content)
|
|
148
|
+
elif isinstance(answer_content, dict):
|
|
149
|
+
return answer_content.get("answer", "")
|
|
150
|
+
elif isinstance(text_val, str):
|
|
151
|
+
try:
|
|
152
|
+
return json.loads(text_val).get("answer", text_val)
|
|
153
|
+
except Exception:
|
|
154
|
+
return text_val
|
|
155
|
+
return ""
|
|
156
|
+
|
|
157
|
+
# New pattern extraction
|
|
158
|
+
if "text" in response:
|
|
159
|
+
text_val = response["text"]
|
|
160
|
+
if isinstance(text_val, list):
|
|
161
|
+
for step in text_val:
|
|
162
|
+
if step.get("type") == "answer" and "value" in step:
|
|
163
|
+
return step["value"]
|
|
164
|
+
if step.get("step_type") == "FINAL" and "content" in step:
|
|
165
|
+
content = step["content"]
|
|
166
|
+
if isinstance(content, dict) and "answer" in content:
|
|
167
|
+
try:
|
|
168
|
+
return json.loads(content["answer"]).get("answer", "")
|
|
169
|
+
except Exception:
|
|
170
|
+
return str(content["answer"])
|
|
171
|
+
elif isinstance(text_val, str):
|
|
172
|
+
try:
|
|
173
|
+
return json.loads(text_val).get("answer", text_val)
|
|
174
|
+
except Exception:
|
|
175
|
+
return text_val
|
|
176
|
+
return ""
|
|
177
|
+
|
|
170
178
|
def search(
|
|
171
179
|
self,
|
|
172
180
|
prompt: str,
|
|
173
|
-
mode: str = 'auto',
|
|
174
|
-
model: Optional[str] = None,
|
|
175
|
-
sources: Optional[list] = None,
|
|
176
181
|
stream: bool = False,
|
|
177
182
|
raw: bool = False,
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
mode (str, optional): Search mode. Options: 'auto', 'pro', 'reasoning', 'deep research'.
|
|
190
|
-
Defaults to 'auto'.
|
|
191
|
-
model (str, optional): Model to use. Available models depend on the mode. Defaults to None.
|
|
192
|
-
sources (list, optional): Sources to use. Options: 'web', 'scholar', 'social'.
|
|
193
|
-
Defaults to ['web'].
|
|
194
|
-
stream (bool, optional): If True, yields response chunks as they arrive.
|
|
195
|
-
If False, returns complete response. Defaults to False.
|
|
196
|
-
raw (bool, optional): If True, returns raw response dictionaries.
|
|
197
|
-
If False, returns Response objects that convert to text automatically.
|
|
198
|
-
Defaults to False.
|
|
199
|
-
language (str, optional): Language to use. Defaults to 'en-US'.
|
|
200
|
-
follow_up (dict, optional): Follow-up information. Defaults to None.
|
|
201
|
-
incognito (bool, optional): Whether to use incognito mode. Defaults to False.
|
|
202
|
-
|
|
203
|
-
Returns:
|
|
204
|
-
If stream=True: Generator yielding response chunks as they arrive
|
|
205
|
-
If stream=False: Complete response
|
|
206
|
-
|
|
207
|
-
Raises:
|
|
208
|
-
ValueError: If invalid mode or model is provided
|
|
209
|
-
exceptions.APIConnectionError: If connection to API fails
|
|
210
|
-
exceptions.FailedToGenerateResponseError: If response generation fails
|
|
211
|
-
"""
|
|
212
|
-
if sources is None:
|
|
213
|
-
sources = ['web']
|
|
214
|
-
|
|
215
|
-
# Validate inputs
|
|
216
|
-
if mode not in ['auto', 'pro', 'reasoning', 'deep research']:
|
|
217
|
-
raise ValueError('Search modes -> ["auto", "pro", "reasoning", "deep research"]')
|
|
218
|
-
|
|
219
|
-
if not all([source in ('web', 'scholar', 'social') for source in sources]):
|
|
220
|
-
raise ValueError('Sources -> ["web", "scholar", "social"]')
|
|
221
|
-
|
|
222
|
-
# Check if model is valid for the selected mode
|
|
223
|
-
valid_models = {
|
|
224
|
-
'auto': [None],
|
|
225
|
-
'pro': [None, 'sonar', 'gpt-4.5', 'gpt-4o', 'claude 3.7 sonnet', 'gemini 2.0 flash', 'grok-2'],
|
|
226
|
-
'reasoning': [None, 'r1', 'o3-mini', 'claude 3.7 sonnet'],
|
|
227
|
-
'deep research': [None]
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if mode in valid_models and model not in valid_models[mode] and model is not None:
|
|
231
|
-
raise ValueError(f'Invalid model for {mode} mode. Valid models: {valid_models[mode]}')
|
|
232
|
-
|
|
183
|
+
**kwargs: Any,
|
|
184
|
+
) -> Union[SearchResponse, Generator[Union[Dict[str, str], SearchResponse], None, None], List[Any], Dict[str, Any], str]:
|
|
185
|
+
"""Search using the Perplexity API and get AI-generated responses."""
|
|
186
|
+
sources = kwargs.get("sources", ["web"])
|
|
187
|
+
follow_up = kwargs.get("follow_up")
|
|
188
|
+
incognito = kwargs.get("incognito", True)
|
|
189
|
+
language = kwargs.get("language", "en-US")
|
|
190
|
+
mode = kwargs.get("mode", "auto")
|
|
191
|
+
model = kwargs.get("model")
|
|
192
|
+
max_retries = kwargs.get("max_retries", 3)
|
|
193
|
+
|
|
233
194
|
# Prepare request data
|
|
234
|
-
json_data = {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
None: 'pplx_pro',
|
|
250
|
-
'sonar': 'experimental',
|
|
251
|
-
'gpt-4.5': 'gpt45',
|
|
252
|
-
'gpt-4o': 'gpt4o',
|
|
253
|
-
'claude 3.7 sonnet': 'claude2',
|
|
254
|
-
'gemini 2.0 flash': 'gemini2flash',
|
|
255
|
-
'grok-2': 'grok'
|
|
256
|
-
},
|
|
257
|
-
'reasoning': {
|
|
258
|
-
None: 'pplx_reasoning',
|
|
259
|
-
'r1': 'r1',
|
|
260
|
-
'o3-mini': 'o3mini',
|
|
261
|
-
'claude 3.7 sonnet': 'claude37sonnetthinking'
|
|
262
|
-
},
|
|
263
|
-
'deep research': {
|
|
264
|
-
None: 'pplx_alpha'
|
|
265
|
-
}
|
|
266
|
-
}[mode][model],
|
|
267
|
-
'source': 'default',
|
|
268
|
-
'sources': sources,
|
|
269
|
-
'version': '2.18'
|
|
270
|
-
}
|
|
195
|
+
json_data: Dict[str, Any] = {
|
|
196
|
+
"query_str": prompt,
|
|
197
|
+
"params": {
|
|
198
|
+
"attachments": follow_up["attachments"] if follow_up else [],
|
|
199
|
+
"frontend_context_uuid": str(uuid4()),
|
|
200
|
+
"frontend_uuid": str(uuid4()),
|
|
201
|
+
"is_incognito": incognito,
|
|
202
|
+
"language": language,
|
|
203
|
+
"last_backend_uuid": follow_up["backend_uuid"] if follow_up else None,
|
|
204
|
+
"mode": "concise" if mode == "auto" else "copilot",
|
|
205
|
+
"model_preference": "turbo", # Default
|
|
206
|
+
"source": "default",
|
|
207
|
+
"sources": sources,
|
|
208
|
+
"version": "2.18",
|
|
209
|
+
},
|
|
271
210
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
except json.JSONDecodeError:
|
|
349
|
-
raise exceptions.FailedToGenerateResponseError("Failed to parse response JSON")
|
|
350
|
-
except Exception as e:
|
|
351
|
-
raise exceptions.FailedToGenerateResponseError(f"Error: {str(e)}")
|
|
211
|
+
|
|
212
|
+
# Handle model selection if provided
|
|
213
|
+
if model:
|
|
214
|
+
json_data["params"]["model_preference"] = model
|
|
215
|
+
|
|
216
|
+
for attempt in range(max_retries):
|
|
217
|
+
try:
|
|
218
|
+
resp = self.session.post(
|
|
219
|
+
"https://www.perplexity.ai/rest/sse/perplexity_ask",
|
|
220
|
+
json=json_data,
|
|
221
|
+
stream=True,
|
|
222
|
+
timeout=self.timeout,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
if resp.status_code == 502:
|
|
226
|
+
if attempt < max_retries - 1:
|
|
227
|
+
time.sleep(2**attempt)
|
|
228
|
+
continue
|
|
229
|
+
raise exceptions.APIConnectionError(
|
|
230
|
+
f"Perplexity API returned 502 Bad Gateway after {max_retries} attempts."
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
if resp.status_code != 200:
|
|
234
|
+
raise exceptions.APIConnectionError(
|
|
235
|
+
f"API returned status code {resp.status_code}: {resp.text}"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
def stream_response():
|
|
239
|
+
full_text = ""
|
|
240
|
+
def extract_perplexity_content(data):
|
|
241
|
+
nonlocal full_text
|
|
242
|
+
current_answer = self._extract_answer(data)
|
|
243
|
+
if current_answer and len(current_answer) > len(full_text):
|
|
244
|
+
delta = current_answer[len(full_text):]
|
|
245
|
+
full_text = current_answer
|
|
246
|
+
return delta
|
|
247
|
+
return None
|
|
248
|
+
|
|
249
|
+
processed_chunks = sanitize_stream(
|
|
250
|
+
data=resp.iter_content(chunk_size=1024),
|
|
251
|
+
to_json=True,
|
|
252
|
+
extract_regexes=[r"data:\s*({.*})"],
|
|
253
|
+
content_extractor=lambda chunk: extract_perplexity_content(chunk),
|
|
254
|
+
yield_raw_on_error=False,
|
|
255
|
+
encoding='utf-8',
|
|
256
|
+
encoding_errors='replace',
|
|
257
|
+
line_delimiter="\r\n\r\n",
|
|
258
|
+
raw=raw,
|
|
259
|
+
output_formatter=None if raw else lambda x: SearchResponse(x) if isinstance(x, str) else x,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
yield from processed_chunks
|
|
263
|
+
|
|
264
|
+
if stream:
|
|
265
|
+
return stream_response()
|
|
266
|
+
else:
|
|
267
|
+
if raw:
|
|
268
|
+
return resp.text
|
|
269
|
+
|
|
270
|
+
full_response_text = ""
|
|
271
|
+
for chunk in stream_response():
|
|
272
|
+
full_response_text += str(chunk)
|
|
273
|
+
|
|
274
|
+
return SearchResponse(full_response_text)
|
|
275
|
+
|
|
276
|
+
except requests.RequestsError as e:
|
|
277
|
+
if attempt < max_retries - 1:
|
|
278
|
+
time.sleep(2**attempt)
|
|
279
|
+
continue
|
|
280
|
+
raise exceptions.APIConnectionError(f"Connection error: {str(e)}")
|
|
281
|
+
except Exception as e:
|
|
282
|
+
raise exceptions.FailedToGenerateResponseError(f"Error: {str(e)}")
|
|
283
|
+
|
|
284
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
285
|
+
"Failed to get response after multiple attempts"
|
|
286
|
+
)
|
|
352
287
|
|
|
353
288
|
|
|
354
289
|
if __name__ == "__main__":
|
|
355
|
-
# Simple test
|
|
356
290
|
ai = Perplexity()
|
|
357
|
-
response = ai.search("What is Python?")
|
|
291
|
+
response = ai.search("What is Python?", stream=False)
|
|
358
292
|
print(response)
|
|
359
|
-
|