webscout 8.2.9__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 -251
- webscout/AIbase.py +247 -319
- webscout/AIutel.py +68 -703
- webscout/Bard.py +1072 -1026
- webscout/Extra/GitToolkit/__init__.py +10 -10
- 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 -375
- 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 -44
- 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 -118
- 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 +403 -232
- webscout/Extra/__init__.py +2 -3
- webscout/Extra/gguf.py +1298 -684
- webscout/Extra/tempmail/README.md +487 -487
- 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 +292 -333
- webscout/Provider/AISEARCH/README.md +106 -279
- webscout/Provider/AISEARCH/__init__.py +16 -9
- webscout/Provider/AISEARCH/brave_search.py +298 -0
- webscout/Provider/AISEARCH/iask_search.py +357 -410
- webscout/Provider/AISEARCH/monica_search.py +200 -220
- webscout/Provider/AISEARCH/webpilotai_search.py +242 -255
- 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 -342
- webscout/Provider/ClaudeOnline.py +365 -0
- webscout/Provider/Cohere.py +232 -208
- webscout/Provider/DeepAI.py +367 -0
- webscout/Provider/Deepinfra.py +467 -340
- webscout/Provider/EssentialAI.py +217 -0
- webscout/Provider/ExaAI.py +274 -261
- webscout/Provider/Gemini.py +175 -169
- webscout/Provider/GithubChat.py +385 -369
- webscout/Provider/Gradient.py +286 -0
- webscout/Provider/Groq.py +556 -801
- webscout/Provider/HadadXYZ.py +323 -0
- webscout/Provider/HeckAI.py +392 -375
- webscout/Provider/HuggingFace.py +387 -0
- webscout/Provider/IBM.py +340 -0
- webscout/Provider/Jadve.py +317 -291
- webscout/Provider/K2Think.py +306 -0
- webscout/Provider/Koboldai.py +221 -384
- webscout/Provider/Netwrck.py +273 -270
- 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 -952
- webscout/Provider/OPENAI/TogetherAI.py +405 -0
- webscout/Provider/OPENAI/TwoAI.py +255 -357
- webscout/Provider/OPENAI/__init__.py +148 -40
- webscout/Provider/OPENAI/ai4chat.py +348 -293
- webscout/Provider/OPENAI/akashgpt.py +436 -0
- webscout/Provider/OPENAI/algion.py +303 -0
- webscout/Provider/OPENAI/{exachat.py → ayle.py} +365 -444
- webscout/Provider/OPENAI/base.py +253 -249
- webscout/Provider/OPENAI/cerebras.py +296 -0
- webscout/Provider/OPENAI/chatgpt.py +870 -556
- webscout/Provider/OPENAI/chatsandbox.py +233 -173
- webscout/Provider/OPENAI/deepinfra.py +403 -322
- webscout/Provider/OPENAI/e2b.py +2370 -1414
- webscout/Provider/OPENAI/elmo.py +278 -0
- webscout/Provider/OPENAI/exaai.py +452 -417
- webscout/Provider/OPENAI/freeassist.py +446 -0
- webscout/Provider/OPENAI/gradient.py +448 -0
- webscout/Provider/OPENAI/groq.py +380 -364
- webscout/Provider/OPENAI/hadadxyz.py +292 -0
- webscout/Provider/OPENAI/heckai.py +333 -308
- 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 -335
- webscout/Provider/OPENAI/meta.py +541 -0
- webscout/Provider/OPENAI/netwrck.py +374 -357
- webscout/Provider/OPENAI/nvidia.py +317 -0
- webscout/Provider/OPENAI/oivscode.py +348 -287
- webscout/Provider/OPENAI/openrouter.py +328 -0
- webscout/Provider/OPENAI/pydantic_imports.py +1 -172
- webscout/Provider/OPENAI/sambanova.py +397 -0
- webscout/Provider/OPENAI/sonus.py +305 -304
- webscout/Provider/OPENAI/textpollinations.py +370 -339
- webscout/Provider/OPENAI/toolbaz.py +375 -413
- webscout/Provider/OPENAI/typefully.py +419 -355
- webscout/Provider/OPENAI/typliai.py +279 -0
- webscout/Provider/OPENAI/utils.py +314 -318
- webscout/Provider/OPENAI/wisecat.py +359 -387
- webscout/Provider/OPENAI/writecream.py +185 -163
- webscout/Provider/OPENAI/x0gpt.py +462 -365
- webscout/Provider/OPENAI/zenmux.py +380 -0
- webscout/Provider/OpenRouter.py +386 -0
- webscout/Provider/Openai.py +337 -496
- webscout/Provider/PI.py +443 -429
- 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 -82
- webscout/Provider/TTI/__init__.py +37 -7
- webscout/Provider/TTI/base.py +147 -64
- webscout/Provider/TTI/claudeonline.py +393 -0
- webscout/Provider/TTI/magicstudio.py +292 -201
- webscout/Provider/TTI/miragic.py +180 -0
- webscout/Provider/TTI/pollinations.py +331 -221
- webscout/Provider/TTI/together.py +334 -0
- webscout/Provider/TTI/utils.py +14 -11
- webscout/Provider/TTS/README.md +186 -192
- webscout/Provider/TTS/__init__.py +43 -10
- webscout/Provider/TTS/base.py +523 -159
- 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 -129
- 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 -580
- webscout/Provider/TTS/streamElements.py +275 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TextPollinationsAI.py +331 -308
- webscout/Provider/TogetherAI.py +450 -0
- webscout/Provider/TwoAI.py +309 -475
- webscout/Provider/TypliAI.py +311 -305
- webscout/Provider/UNFINISHED/ChatHub.py +219 -209
- webscout/Provider/{OPENAI/glider.py → UNFINISHED/ChutesAI.py} +331 -326
- webscout/Provider/{GizAI.py → UNFINISHED/GizAI.py} +300 -295
- webscout/Provider/{Marcus.py → UNFINISHED/Marcus.py} +218 -198
- webscout/Provider/UNFINISHED/Qodo.py +481 -0
- webscout/Provider/{MCPCore.py → UNFINISHED/XenAI.py} +330 -315
- webscout/Provider/UNFINISHED/Youchat.py +347 -330
- 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 -263
- webscout/Provider/{samurai.py → UNFINISHED/samurai.py} +231 -224
- webscout/Provider/WiseCat.py +256 -233
- webscout/Provider/WrDoChat.py +390 -370
- webscout/Provider/__init__.py +115 -174
- webscout/Provider/ai4chat.py +181 -174
- webscout/Provider/akashgpt.py +330 -335
- webscout/Provider/cerebras.py +397 -290
- webscout/Provider/cleeai.py +236 -213
- webscout/Provider/elmo.py +291 -283
- webscout/Provider/geminiapi.py +343 -208
- webscout/Provider/julius.py +245 -223
- webscout/Provider/learnfastai.py +333 -325
- webscout/Provider/llama3mitril.py +230 -215
- webscout/Provider/llmchat.py +308 -258
- webscout/Provider/llmchatco.py +321 -306
- webscout/Provider/meta.py +996 -801
- webscout/Provider/oivscode.py +332 -309
- webscout/Provider/searchchat.py +316 -292
- webscout/Provider/sonus.py +264 -258
- webscout/Provider/toolbaz.py +359 -353
- webscout/Provider/turboseek.py +332 -266
- webscout/Provider/typefully.py +262 -202
- webscout/Provider/x0gpt.py +332 -299
- webscout/__init__.py +31 -39
- webscout/__main__.py +5 -5
- webscout/cli.py +585 -524
- webscout/client.py +1497 -70
- webscout/conversation.py +140 -436
- webscout/exceptions.py +383 -362
- 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 +74 -420
- webscout/prompt_manager.py +376 -288
- webscout/sanitize.py +1514 -0
- webscout/scout/README.md +452 -404
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +7 -7
- webscout/scout/core/crawler.py +330 -210
- webscout/scout/core/scout.py +800 -607
- 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 -478
- 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 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +574 -297
- webscout/swiftcli/core/context.py +98 -104
- webscout/swiftcli/core/group.py +268 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +243 -221
- webscout/swiftcli/decorators/options.py +247 -220
- webscout/swiftcli/decorators/output.py +392 -252
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +134 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +58 -59
- webscout/swiftcli/utils/formatting.py +251 -252
- webscout/swiftcli/utils/parsing.py +368 -267
- 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 -135
- webscout/zeroart/base.py +70 -66
- webscout/zeroart/effects.py +155 -101
- webscout/zeroart/fonts.py +1799 -1239
- webscout-2026.1.19.dist-info/METADATA +638 -0
- webscout-2026.1.19.dist-info/RECORD +312 -0
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/WHEEL +1 -1
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/entry_points.txt +1 -1
- webscout/DWEBS.py +0 -520
- webscout/Extra/Act.md +0 -309
- webscout/Extra/GitToolkit/gitapi/README.md +0 -110
- webscout/Extra/autocoder/__init__.py +0 -9
- webscout/Extra/autocoder/autocoder.py +0 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +0 -332
- webscout/Extra/gguf.md +0 -430
- webscout/Extra/weather.md +0 -281
- webscout/Litlogger/README.md +0 -10
- webscout/Litlogger/__init__.py +0 -15
- webscout/Litlogger/formats.py +0 -4
- webscout/Litlogger/handlers.py +0 -103
- webscout/Litlogger/levels.py +0 -13
- webscout/Litlogger/logger.py +0 -92
- webscout/Provider/AI21.py +0 -177
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/AISEARCH/felo_search.py +0 -202
- webscout/Provider/AISEARCH/genspark_search.py +0 -324
- webscout/Provider/AISEARCH/hika_search.py +0 -186
- webscout/Provider/AISEARCH/scira_search.py +0 -298
- webscout/Provider/Aitopia.py +0 -316
- webscout/Provider/AllenAI.py +0 -440
- webscout/Provider/Blackboxai.py +0 -791
- webscout/Provider/ChatGPTClone.py +0 -237
- webscout/Provider/ChatGPTGratis.py +0 -194
- webscout/Provider/Cloudflare.py +0 -324
- webscout/Provider/ExaChat.py +0 -358
- webscout/Provider/Flowith.py +0 -217
- webscout/Provider/FreeGemini.py +0 -250
- webscout/Provider/Glider.py +0 -225
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/HuggingFaceChat.py +0 -469
- webscout/Provider/Hunyuan.py +0 -283
- webscout/Provider/LambdaChat.py +0 -411
- webscout/Provider/Llama3.py +0 -259
- webscout/Provider/Nemotron.py +0 -218
- webscout/Provider/OLLAMA.py +0 -396
- webscout/Provider/OPENAI/BLACKBOXAI.py +0 -766
- webscout/Provider/OPENAI/Cloudflare.py +0 -378
- webscout/Provider/OPENAI/FreeGemini.py +0 -283
- webscout/Provider/OPENAI/NEMOTRON.py +0 -232
- webscout/Provider/OPENAI/Qwen3.py +0 -283
- webscout/Provider/OPENAI/api.py +0 -969
- webscout/Provider/OPENAI/c4ai.py +0 -373
- webscout/Provider/OPENAI/chatgptclone.py +0 -494
- webscout/Provider/OPENAI/copilot.py +0 -242
- webscout/Provider/OPENAI/flowith.py +0 -162
- webscout/Provider/OPENAI/freeaichat.py +0 -359
- webscout/Provider/OPENAI/mcpcore.py +0 -389
- webscout/Provider/OPENAI/multichat.py +0 -376
- webscout/Provider/OPENAI/opkfc.py +0 -496
- webscout/Provider/OPENAI/scirachat.py +0 -477
- webscout/Provider/OPENAI/standardinput.py +0 -433
- webscout/Provider/OPENAI/typegpt.py +0 -364
- webscout/Provider/OPENAI/uncovrAI.py +0 -463
- webscout/Provider/OPENAI/venice.py +0 -431
- webscout/Provider/OPENAI/yep.py +0 -382
- webscout/Provider/OpenGPT.py +0 -209
- webscout/Provider/Perplexitylabs.py +0 -415
- webscout/Provider/Reka.py +0 -214
- webscout/Provider/StandardInput.py +0 -290
- webscout/Provider/TTI/aiarta.py +0 -365
- webscout/Provider/TTI/artbit.py +0 -0
- webscout/Provider/TTI/fastflux.py +0 -200
- webscout/Provider/TTI/piclumen.py +0 -203
- webscout/Provider/TTI/pixelmuse.py +0 -225
- webscout/Provider/TTS/gesserit.py +0 -128
- webscout/Provider/TTS/sthir.py +0 -94
- webscout/Provider/TeachAnything.py +0 -229
- webscout/Provider/UNFINISHED/puterjs.py +0 -635
- webscout/Provider/UNFINISHED/test_lmarena.py +0 -119
- webscout/Provider/Venice.py +0 -258
- webscout/Provider/VercelAI.py +0 -253
- webscout/Provider/Writecream.py +0 -246
- webscout/Provider/WritingMate.py +0 -269
- webscout/Provider/asksteve.py +0 -220
- webscout/Provider/chatglm.py +0 -215
- webscout/Provider/copilot.py +0 -425
- webscout/Provider/freeaichat.py +0 -285
- webscout/Provider/granite.py +0 -235
- webscout/Provider/hermes.py +0 -266
- webscout/Provider/koala.py +0 -170
- webscout/Provider/lmarena.py +0 -198
- webscout/Provider/multichat.py +0 -364
- webscout/Provider/scira_chat.py +0 -299
- webscout/Provider/scnet.py +0 -243
- webscout/Provider/talkai.py +0 -194
- webscout/Provider/typegpt.py +0 -289
- webscout/Provider/uncovr.py +0 -368
- webscout/Provider/yep.py +0 -389
- webscout/litagent/Readme.md +0 -276
- webscout/litprinter/__init__.py +0 -59
- webscout/swiftcli/Readme.md +0 -323
- webscout/tempid.py +0 -128
- webscout/webscout_search.py +0 -1184
- webscout/webscout_search_async.py +0 -654
- webscout/yep_search.py +0 -347
- webscout/zeroart/README.md +0 -89
- webscout-8.2.9.dist-info/METADATA +0 -1033
- webscout-8.2.9.dist-info/RECORD +0 -289
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/top_level.txt +0 -0
webscout/Extra/weather.py
CHANGED
|
@@ -1,194 +1,196 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Weather information module with a clean, strongly-typed API structure.
|
|
3
|
-
|
|
4
|
-
This module provides a simple client for fetching weather data
|
|
5
|
-
from the wttr.in service with proper typing and a consistent interface.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
from
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
self.
|
|
24
|
-
self.
|
|
25
|
-
self.
|
|
26
|
-
self.
|
|
27
|
-
self.
|
|
28
|
-
self.
|
|
29
|
-
self.
|
|
30
|
-
self.
|
|
31
|
-
self.
|
|
32
|
-
self.
|
|
33
|
-
self.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
self.
|
|
47
|
-
self.
|
|
48
|
-
self.
|
|
49
|
-
self.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
self.
|
|
63
|
-
self.
|
|
64
|
-
self.
|
|
65
|
-
self.
|
|
66
|
-
self.
|
|
67
|
-
self.
|
|
68
|
-
self.
|
|
69
|
-
self.
|
|
70
|
-
self.
|
|
71
|
-
self.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
self.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
self.
|
|
93
|
-
self.
|
|
94
|
-
self.
|
|
95
|
-
self.
|
|
96
|
-
self.
|
|
97
|
-
self.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
self.
|
|
104
|
-
self.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
self.
|
|
126
|
-
self.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
1
|
+
"""
|
|
2
|
+
Weather information module with a clean, strongly-typed API structure.
|
|
3
|
+
|
|
4
|
+
This module provides a simple client for fetching weather data
|
|
5
|
+
from the wttr.in service with proper typing and a consistent interface.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
from curl_cffi.requests import Session
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CurrentCondition:
|
|
15
|
+
"""Current weather conditions with strongly typed properties."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, data: Dict[str, Any]) -> None:
|
|
18
|
+
"""Initialize with current condition data.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
data: Current condition data dictionary from wttr.in
|
|
22
|
+
"""
|
|
23
|
+
self.temp_c: Optional[str] = data.get('temp_C')
|
|
24
|
+
self.temp_f: Optional[str] = data.get('temp_F')
|
|
25
|
+
self.feels_like_c: Optional[str] = data.get('FeelsLikeC')
|
|
26
|
+
self.feels_like_f: Optional[str] = data.get('FeelsLikeF')
|
|
27
|
+
self.weather_desc: str = data.get('weatherDesc', [{}])[0].get('value', '')
|
|
28
|
+
self.weather_code: Optional[str] = data.get('weatherCode')
|
|
29
|
+
self.humidity: Optional[str] = data.get('humidity')
|
|
30
|
+
self.visibility: Optional[str] = data.get('visibility')
|
|
31
|
+
self.pressure: Optional[str] = data.get('pressure')
|
|
32
|
+
self.wind_speed_kmph: Optional[str] = data.get('windspeedKmph')
|
|
33
|
+
self.wind_direction: Optional[str] = data.get('winddir16Point')
|
|
34
|
+
self.wind_degree: Optional[str] = data.get('winddirDegree')
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Location:
|
|
38
|
+
"""Location information with strongly typed properties."""
|
|
39
|
+
|
|
40
|
+
def __init__(self, data: Dict[str, Any]) -> None:
|
|
41
|
+
"""Initialize with location data.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
data: Location data dictionary from wttr.in
|
|
45
|
+
"""
|
|
46
|
+
self.name: str = data.get('areaName', [{}])[0].get('value', '')
|
|
47
|
+
self.country: str = data.get('country', [{}])[0].get('value', '')
|
|
48
|
+
self.region: str = data.get('region', [{}])[0].get('value', '')
|
|
49
|
+
self.latitude: Optional[str] = data.get('latitude')
|
|
50
|
+
self.longitude: Optional[str] = data.get('longitude')
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class HourlyForecast:
|
|
54
|
+
"""Hourly forecast information with strongly typed properties."""
|
|
55
|
+
|
|
56
|
+
def __init__(self, data: Dict[str, Any]) -> None:
|
|
57
|
+
"""Initialize with hourly forecast data.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
data: Hourly forecast data dictionary from wttr.in
|
|
61
|
+
"""
|
|
62
|
+
self.time: Optional[str] = data.get('time')
|
|
63
|
+
self.temp_c: Optional[str] = data.get('tempC')
|
|
64
|
+
self.temp_f: Optional[str] = data.get('tempF')
|
|
65
|
+
self.weather_desc: str = data.get('weatherDesc', [{}])[0].get('value', '')
|
|
66
|
+
self.weather_code: Optional[str] = data.get('weatherCode')
|
|
67
|
+
self.wind_speed_kmph: Optional[str] = data.get('windspeedKmph')
|
|
68
|
+
self.wind_direction: Optional[str] = data.get('winddir16Point')
|
|
69
|
+
self.feels_like_c: Optional[str] = data.get('FeelsLikeC')
|
|
70
|
+
self.feels_like_f: Optional[str] = data.get('FeelsLikeF')
|
|
71
|
+
self.chance_of_rain: Optional[str] = data.get('chanceofrain')
|
|
72
|
+
self.chance_of_snow: Optional[str] = data.get('chanceofsnow')
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class DayForecast:
|
|
76
|
+
"""Daily forecast information with strongly typed properties."""
|
|
77
|
+
|
|
78
|
+
def __init__(self, data: Dict[str, Any]) -> None:
|
|
79
|
+
"""Initialize with daily forecast data.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
data: Daily forecast data dictionary from wttr.in
|
|
83
|
+
"""
|
|
84
|
+
self.date: Optional[str] = data.get('date')
|
|
85
|
+
self.date_formatted: Optional[str] = None
|
|
86
|
+
if self.date:
|
|
87
|
+
try:
|
|
88
|
+
self.date_formatted = datetime.strptime(self.date, '%Y-%m-%d').strftime('%a, %b %d')
|
|
89
|
+
except ValueError:
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
self.max_temp_c: Optional[str] = data.get('maxtempC')
|
|
93
|
+
self.max_temp_f: Optional[str] = data.get('maxtempF')
|
|
94
|
+
self.min_temp_c: Optional[str] = data.get('mintempC')
|
|
95
|
+
self.min_temp_f: Optional[str] = data.get('mintempF')
|
|
96
|
+
self.avg_temp_c: Optional[str] = data.get('avgtempC')
|
|
97
|
+
self.avg_temp_f: Optional[str] = data.get('avgtempF')
|
|
98
|
+
self.sun_hour: Optional[str] = data.get('sunHour')
|
|
99
|
+
|
|
100
|
+
# Parse astronomy data (simplified)
|
|
101
|
+
if data.get('astronomy') and len(data.get('astronomy', [])) > 0:
|
|
102
|
+
astro = data.get('astronomy', [{}])[0]
|
|
103
|
+
self.sunrise: Optional[str] = astro.get('sunrise')
|
|
104
|
+
self.sunset: Optional[str] = astro.get('sunset')
|
|
105
|
+
self.moon_phase: Optional[str] = astro.get('moon_phase')
|
|
106
|
+
else:
|
|
107
|
+
self.sunrise = self.sunset = self.moon_phase = None
|
|
108
|
+
|
|
109
|
+
# Parse hourly forecasts
|
|
110
|
+
self.hourly: List[HourlyForecast] = []
|
|
111
|
+
for hour_data in data.get('hourly', []):
|
|
112
|
+
self.hourly.append(HourlyForecast(hour_data))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class Weather:
|
|
116
|
+
"""Weather response object with strongly typed properties."""
|
|
117
|
+
|
|
118
|
+
def __init__(self, data: Optional[Dict[str, Any]] = None) -> None:
|
|
119
|
+
"""Initialize with weather data.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
data: Weather data dictionary from wttr.in
|
|
123
|
+
"""
|
|
124
|
+
if not data:
|
|
125
|
+
self.current_condition = None
|
|
126
|
+
self.location = None
|
|
127
|
+
self.forecast_days = []
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
# Parse current condition
|
|
131
|
+
self.current_condition: Optional[CurrentCondition] = None
|
|
132
|
+
if data.get('current_condition') and len(data.get('current_condition', [])) > 0:
|
|
133
|
+
self.current_condition = CurrentCondition(data.get('current_condition', [{}])[0])
|
|
134
|
+
|
|
135
|
+
# Parse location
|
|
136
|
+
self.location: Optional[Location] = None
|
|
137
|
+
if data.get('nearest_area') and len(data.get('nearest_area', [])) > 0:
|
|
138
|
+
self.location = Location(data.get('nearest_area', [{}])[0])
|
|
139
|
+
|
|
140
|
+
# Parse forecast days
|
|
141
|
+
self.forecast_days: List[DayForecast] = []
|
|
142
|
+
for day_data in data.get('weather', []):
|
|
143
|
+
self.forecast_days.append(DayForecast(day_data))
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def today(self) -> Optional[DayForecast]:
|
|
147
|
+
"""Get today's forecast."""
|
|
148
|
+
return self.forecast_days[0] if self.forecast_days else None
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def tomorrow(self) -> Optional[DayForecast]:
|
|
152
|
+
"""Get tomorrow's forecast."""
|
|
153
|
+
return self.forecast_days[1] if len(self.forecast_days) > 1 else None
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def summary(self) -> str:
|
|
157
|
+
"""Get a simple text summary of current weather."""
|
|
158
|
+
if not self.current_condition or not self.location:
|
|
159
|
+
return "Weather data not available"
|
|
160
|
+
|
|
161
|
+
return f"{self.location.name}, {self.location.country}: {self.current_condition.weather_desc}, {self.current_condition.temp_c}°C ({self.current_condition.temp_f}°F)"
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class WeatherClient:
|
|
165
|
+
"""Client for fetching weather information."""
|
|
166
|
+
|
|
167
|
+
def get_weather(self, location: str) -> Weather:
|
|
168
|
+
"""Get weather for the specified location.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
location: Location to get weather for (city name, zip code, etc.)
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Weather object containing all weather data
|
|
175
|
+
"""
|
|
176
|
+
try:
|
|
177
|
+
session = Session()
|
|
178
|
+
response = session.get(f"https://wttr.in/{location}?format=j1", timeout=10)
|
|
179
|
+
response.raise_for_status()
|
|
180
|
+
return Weather(response.json())
|
|
181
|
+
except Exception as e:
|
|
182
|
+
print(f"Error fetching weather data: {str(e)}")
|
|
183
|
+
return Weather()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def get(location: str) -> Weather:
|
|
187
|
+
"""Convenience function to get weather for a location.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
location: Location to get weather for
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Weather object containing all weather data
|
|
194
|
+
"""
|
|
195
|
+
client = WeatherClient()
|
|
196
|
+
return client.get_weather(location)
|
webscout/Extra/weather_ascii.py
CHANGED
|
@@ -5,26 +5,27 @@ This module provides a clean interface for fetching weather information
|
|
|
5
5
|
in ASCII art format using the wttr.in service.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
from curl_cffi.requests import Session
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class WeatherAscii:
|
|
13
14
|
"""Container for ASCII weather data with a simple API."""
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
def __init__(self, content: str) -> None:
|
|
16
17
|
"""Initialize with ASCII weather content.
|
|
17
|
-
|
|
18
|
+
|
|
18
19
|
Args:
|
|
19
20
|
content: ASCII weather data or error message
|
|
20
21
|
"""
|
|
21
22
|
self._content = content
|
|
22
|
-
|
|
23
|
+
|
|
23
24
|
@property
|
|
24
25
|
def content(self) -> str:
|
|
25
26
|
"""Get the ASCII content, similar to choices.message.content in OpenAI API."""
|
|
26
27
|
return self._content
|
|
27
|
-
|
|
28
|
+
|
|
28
29
|
def __str__(self) -> str:
|
|
29
30
|
"""String representation of ASCII weather."""
|
|
30
31
|
return self.content
|
|
@@ -32,24 +33,25 @@ class WeatherAscii:
|
|
|
32
33
|
|
|
33
34
|
class WeatherAsciiClient:
|
|
34
35
|
"""Client for fetching weather information in ASCII art."""
|
|
35
|
-
|
|
36
|
+
|
|
36
37
|
def get_weather(self, location: str, params: Optional[Dict[str, Any]] = None) -> WeatherAscii:
|
|
37
38
|
"""Get ASCII weather for a location.
|
|
38
|
-
|
|
39
|
+
|
|
39
40
|
Args:
|
|
40
41
|
location: The location for which to fetch weather data
|
|
41
42
|
params: Additional parameters for the request
|
|
42
|
-
|
|
43
|
+
|
|
43
44
|
Returns:
|
|
44
45
|
WeatherAscii object containing ASCII art weather data
|
|
45
46
|
"""
|
|
46
47
|
url = f"https://wttr.in/{location}"
|
|
47
48
|
headers = {'User-Agent': 'curl'}
|
|
48
|
-
|
|
49
|
+
|
|
49
50
|
try:
|
|
50
|
-
|
|
51
|
+
session = Session()
|
|
52
|
+
response = session.get(url, headers=headers, params=params, timeout=10)
|
|
51
53
|
response.raise_for_status()
|
|
52
|
-
|
|
54
|
+
|
|
53
55
|
if response.status_code == 200:
|
|
54
56
|
# Remove the footer line from wttr.in
|
|
55
57
|
ascii_weather = "\n".join(response.text.splitlines()[:-1])
|
|
@@ -57,17 +59,17 @@ class WeatherAsciiClient:
|
|
|
57
59
|
else:
|
|
58
60
|
error_msg = f"Error: Unable to fetch weather data. Status code: {response.status_code}"
|
|
59
61
|
return WeatherAscii(error_msg)
|
|
60
|
-
except
|
|
62
|
+
except Exception as e:
|
|
61
63
|
return WeatherAscii(f"Error: {str(e)}")
|
|
62
64
|
|
|
63
65
|
|
|
64
66
|
def get(location: str, params: Optional[Dict[str, Any]] = None) -> WeatherAscii:
|
|
65
67
|
"""Convenience function to get ASCII weather for a location.
|
|
66
|
-
|
|
68
|
+
|
|
67
69
|
Args:
|
|
68
70
|
location: Location to get weather for
|
|
69
71
|
params: Additional parameters for the request
|
|
70
|
-
|
|
72
|
+
|
|
71
73
|
Returns:
|
|
72
74
|
WeatherAscii object containing ASCII art weather data
|
|
73
75
|
"""
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from typing import Any, Dict, Generator, List, Optional, Union, cast
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from webscout import exceptions
|
|
6
|
+
from webscout.AIbase import AISearch, SearchResponse
|
|
7
|
+
from webscout.litagent import LitAgent
|
|
8
|
+
from webscout.sanitize import sanitize_stream
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PERPLEXED(AISearch):
|
|
12
|
+
"""
|
|
13
|
+
A class to interact with the PERPLEXED stream search API.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
timeout: int = 30,
|
|
19
|
+
proxies: Optional[dict] = None,
|
|
20
|
+
):
|
|
21
|
+
"""Initializes the PERPLEXED API client."""
|
|
22
|
+
self.session = requests.Session()
|
|
23
|
+
self.api_endpoint = "https://d21l5c617zttgr.cloudfront.net/stream_search"
|
|
24
|
+
self.stream_chunk_size = 64
|
|
25
|
+
self.timeout = timeout
|
|
26
|
+
self.last_response = {}
|
|
27
|
+
self.headers = {
|
|
28
|
+
"accept": "*/*",
|
|
29
|
+
"accept-encoding": "gzip, deflate, br, zstd",
|
|
30
|
+
"accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
|
|
31
|
+
"content-type": "application/json",
|
|
32
|
+
"dnt": "1",
|
|
33
|
+
"origin": "https://d37ozmhmvu2kcg.cloudfront.net",
|
|
34
|
+
"referer": "https://d37ozmhmvu2kcg.cloudfront.net/",
|
|
35
|
+
"sec-ch-ua": '"Not)A;Brand";v="8", "Chromium";v="138", "Microsoft Edge";v="138"',
|
|
36
|
+
"sec-ch-ua-mobile": "?0",
|
|
37
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
38
|
+
"sec-fetch-dest": "empty",
|
|
39
|
+
"sec-fetch-mode": "cors",
|
|
40
|
+
"sec-fetch-site": "cross-site",
|
|
41
|
+
"sec-gpc": "1",
|
|
42
|
+
"user-agent": LitAgent().random(),
|
|
43
|
+
}
|
|
44
|
+
self.session.headers.update(self.headers)
|
|
45
|
+
self.proxies = proxies
|
|
46
|
+
|
|
47
|
+
def search(
|
|
48
|
+
self,
|
|
49
|
+
prompt: str,
|
|
50
|
+
stream: bool = False,
|
|
51
|
+
raw: bool = False,
|
|
52
|
+
**kwargs: Any,
|
|
53
|
+
) -> Union[SearchResponse, Generator[Union[Dict[str, str], SearchResponse], None, None], List[Any], Dict[str, Any], str]:
|
|
54
|
+
"""
|
|
55
|
+
Sends a prompt to the PERPLEXED API and returns the response.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
prompt: The search query or prompt to send to the API
|
|
59
|
+
stream: Whether to stream the response
|
|
60
|
+
raw: If True, returns unprocessed response chunks without any
|
|
61
|
+
processing or sanitization. Useful for debugging or custom
|
|
62
|
+
processing pipelines. Defaults to False.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
When raw=False: SearchResponse object (non-streaming) or
|
|
66
|
+
Generator yielding SearchResponse objects (streaming)
|
|
67
|
+
When raw=True: Raw string response (non-streaming) or
|
|
68
|
+
Generator yielding raw string chunks (streaming)
|
|
69
|
+
|
|
70
|
+
Examples:
|
|
71
|
+
>>> ai = PERPLEXED()
|
|
72
|
+
>>> # Get processed response
|
|
73
|
+
>>> response = ai.search("Hello")
|
|
74
|
+
>>> print(response)
|
|
75
|
+
|
|
76
|
+
>>> # Get raw response
|
|
77
|
+
>>> raw_response = ai.search("Hello", raw=True)
|
|
78
|
+
>>> print(raw_response)
|
|
79
|
+
|
|
80
|
+
>>> # Stream raw chunks
|
|
81
|
+
>>> for chunk in ai.search("Hello", stream=True, raw=True):
|
|
82
|
+
... print(chunk, end='', flush=True)
|
|
83
|
+
"""
|
|
84
|
+
payload = {"user_prompt": prompt}
|
|
85
|
+
|
|
86
|
+
def for_stream():
|
|
87
|
+
try:
|
|
88
|
+
with self.session.post(
|
|
89
|
+
self.api_endpoint,
|
|
90
|
+
json=payload,
|
|
91
|
+
stream=True,
|
|
92
|
+
timeout=self.timeout,
|
|
93
|
+
proxies=self.proxies,
|
|
94
|
+
) as response:
|
|
95
|
+
if not response.ok:
|
|
96
|
+
raise exceptions.APIConnectionError(
|
|
97
|
+
f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
processed_chunks = sanitize_stream(
|
|
101
|
+
data=response.iter_content(chunk_size=1024),
|
|
102
|
+
intro_value="",
|
|
103
|
+
to_json=True,
|
|
104
|
+
content_extractor=lambda chunk: (chunk.get("answer") if isinstance(chunk, dict) and chunk.get("success") and chunk.get("answer") is not None else None),
|
|
105
|
+
yield_raw_on_error=False,
|
|
106
|
+
encoding='utf-8',
|
|
107
|
+
encoding_errors='replace',
|
|
108
|
+
line_delimiter="[/PERPLEXED-SEPARATOR]",
|
|
109
|
+
raw=raw,
|
|
110
|
+
output_formatter=None if raw else lambda x: SearchResponse(x) if isinstance(x, str) else x,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
for chunk in processed_chunks:
|
|
114
|
+
yield chunk
|
|
115
|
+
|
|
116
|
+
except requests.exceptions.RequestException as e:
|
|
117
|
+
raise exceptions.APIConnectionError(f"Request failed: {e}")
|
|
118
|
+
|
|
119
|
+
def for_non_stream():
|
|
120
|
+
try:
|
|
121
|
+
with self.session.post(
|
|
122
|
+
self.api_endpoint,
|
|
123
|
+
json=payload,
|
|
124
|
+
stream=False,
|
|
125
|
+
timeout=self.timeout,
|
|
126
|
+
proxies=self.proxies,
|
|
127
|
+
) as response:
|
|
128
|
+
if not response.ok:
|
|
129
|
+
raise exceptions.APIConnectionError(
|
|
130
|
+
f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if raw:
|
|
134
|
+
# Return raw response text when raw=True
|
|
135
|
+
return response.text
|
|
136
|
+
else:
|
|
137
|
+
# Process response similar to streaming when raw=False
|
|
138
|
+
processed_chunks = sanitize_stream(
|
|
139
|
+
data=response.content,
|
|
140
|
+
intro_value="",
|
|
141
|
+
to_json=True,
|
|
142
|
+
skip_markers=[],
|
|
143
|
+
strip_chars=None,
|
|
144
|
+
start_marker=None,
|
|
145
|
+
end_marker=None,
|
|
146
|
+
content_extractor=lambda chunk: (chunk.get("answer") if isinstance(chunk, dict) and chunk.get("success") and chunk.get("answer") is not None else None),
|
|
147
|
+
yield_raw_on_error=False,
|
|
148
|
+
encoding='utf-8',
|
|
149
|
+
encoding_errors='replace',
|
|
150
|
+
buffer_size=8192,
|
|
151
|
+
line_delimiter="[/PERPLEXED-SEPARATOR]",
|
|
152
|
+
error_handler=None,
|
|
153
|
+
skip_regexes=None,
|
|
154
|
+
raw=False,
|
|
155
|
+
output_formatter=None,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
full_response = ""
|
|
159
|
+
for content_chunk in processed_chunks:
|
|
160
|
+
if content_chunk is not None and isinstance(content_chunk, str):
|
|
161
|
+
full_response += content_chunk
|
|
162
|
+
|
|
163
|
+
self.last_response = SearchResponse(full_response)
|
|
164
|
+
return self.last_response
|
|
165
|
+
|
|
166
|
+
except requests.exceptions.RequestException as e:
|
|
167
|
+
raise exceptions.APIConnectionError(f"Request failed: {e}")
|
|
168
|
+
|
|
169
|
+
return for_stream() if stream else for_non_stream()
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if __name__ == "__main__":
|
|
173
|
+
ai = PERPLEXED()
|
|
174
|
+
response = ai.search("What is Python?", stream=False, raw=False)
|
|
175
|
+
print(response)
|