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/Provider/cerebras.py
CHANGED
|
@@ -1,290 +1,397 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
from
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
"
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
# Import trio before curl_cffi to prevent eventlet socket monkey-patching conflicts
|
|
4
|
+
# See: https://github.com/python-trio/trio/issues/3015
|
|
5
|
+
try:
|
|
6
|
+
import trio # noqa: F401 # type: ignore
|
|
7
|
+
except ImportError:
|
|
8
|
+
pass # trio is optional, ignore if not available
|
|
9
|
+
import json
|
|
10
|
+
from typing import Any, Dict, Generator, List, Optional, Union, cast
|
|
11
|
+
|
|
12
|
+
import curl_cffi
|
|
13
|
+
from curl_cffi.requests import Session
|
|
14
|
+
|
|
15
|
+
from webscout import exceptions
|
|
16
|
+
from webscout.AIbase import Provider, Response
|
|
17
|
+
from webscout.AIutel import ( # Import sanitize_stream
|
|
18
|
+
AwesomePrompts,
|
|
19
|
+
Conversation,
|
|
20
|
+
Optimizers,
|
|
21
|
+
sanitize_stream,
|
|
22
|
+
)
|
|
23
|
+
from webscout.litagent import LitAgent as UserAgent
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Cerebras(Provider):
|
|
27
|
+
"""
|
|
28
|
+
A class to interact with the Cerebras API using a cookie for authentication.
|
|
29
|
+
"""
|
|
30
|
+
required_auth = True
|
|
31
|
+
AVAILABLE_MODELS = [
|
|
32
|
+
"qwen-3-coder-480b",
|
|
33
|
+
"qwen-3-235b-a22b-instruct-2507",
|
|
34
|
+
"qwen-3-235b-a22b-thinking-2507",
|
|
35
|
+
"qwen-3-32b",
|
|
36
|
+
"llama-3.3-70b",
|
|
37
|
+
"llama-4-maverick-17b-128e-instruct",
|
|
38
|
+
"gpt-oss-120b",
|
|
39
|
+
"llama-4-scout-17b-16e-instruct",
|
|
40
|
+
"llama3.1-8b"
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def get_models(cls, api_key: Optional[str] = None):
|
|
45
|
+
"""Fetch available models from Cerebras API.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
api_key (str, optional): Cerebras API key. If not provided, returns default models.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
list: List of available model IDs
|
|
52
|
+
"""
|
|
53
|
+
if not api_key:
|
|
54
|
+
raise Exception("API key required to fetch models")
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
# Use a temporary curl_cffi session for this class method
|
|
58
|
+
temp_session = Session()
|
|
59
|
+
headers = {
|
|
60
|
+
"Content-Type": "application/json",
|
|
61
|
+
"Authorization": f"Bearer {api_key}",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
response = temp_session.get(
|
|
65
|
+
"https://api.cerebras.ai/v1/models",
|
|
66
|
+
headers=headers,
|
|
67
|
+
impersonate="chrome120"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if response.status_code != 200:
|
|
71
|
+
raise Exception(f"Failed to fetch models: HTTP {response.status_code}")
|
|
72
|
+
|
|
73
|
+
data = response.json()
|
|
74
|
+
if "data" in data and isinstance(data["data"], list):
|
|
75
|
+
return [model['id'] for model in data['data']]
|
|
76
|
+
raise Exception("Invalid response format from API")
|
|
77
|
+
|
|
78
|
+
except (curl_cffi.CurlError, Exception) as e:
|
|
79
|
+
raise Exception(f"Failed to fetch models: {str(e)}")
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
cookie_path: Optional[str] = None,
|
|
84
|
+
is_conversation: bool = True,
|
|
85
|
+
max_tokens: int = 40000,
|
|
86
|
+
timeout: int = 30,
|
|
87
|
+
intro: Optional[str] = None,
|
|
88
|
+
filepath: Optional[str] = None,
|
|
89
|
+
update_file: bool = True,
|
|
90
|
+
proxies: dict = {},
|
|
91
|
+
history_offset: int = 10250,
|
|
92
|
+
act: Optional[str] = None,
|
|
93
|
+
api_key: Optional[str] = None,
|
|
94
|
+
model: str = "qwen-3-coder-480b",
|
|
95
|
+
system_prompt: str = "You are a helpful assistant.",
|
|
96
|
+
temperature: float = 0.7,
|
|
97
|
+
top_p: float = 0.8,
|
|
98
|
+
):
|
|
99
|
+
# Initialize basic settings first
|
|
100
|
+
self.timeout = timeout
|
|
101
|
+
self.model = model
|
|
102
|
+
self.system_prompt = system_prompt
|
|
103
|
+
self.is_conversation = is_conversation
|
|
104
|
+
self.max_tokens_to_sample = max_tokens
|
|
105
|
+
self.temperature = temperature
|
|
106
|
+
self.top_p = top_p
|
|
107
|
+
self.last_response = {}
|
|
108
|
+
|
|
109
|
+
self.session = Session() # Initialize curl_cffi session
|
|
110
|
+
|
|
111
|
+
# Handle API key - either provided directly or retrieved from cookies
|
|
112
|
+
if api_key:
|
|
113
|
+
self.api_key = api_key.strip()
|
|
114
|
+
# Basic validation for API key format
|
|
115
|
+
if not self.api_key or len(self.api_key) < 10:
|
|
116
|
+
raise ValueError("Invalid API key format. API key must be at least 10 characters long.")
|
|
117
|
+
elif cookie_path:
|
|
118
|
+
# Get API key from cookies
|
|
119
|
+
try:
|
|
120
|
+
self.api_key = self.get_demo_api_key(cookie_path)
|
|
121
|
+
except Exception as e:
|
|
122
|
+
raise exceptions.APIConnectionError(f"Failed to initialize Cerebras client: {e}")
|
|
123
|
+
else:
|
|
124
|
+
raise ValueError("Either api_key must be provided or cookie_path must be specified")
|
|
125
|
+
|
|
126
|
+
# Validate model choice after updating models
|
|
127
|
+
if model not in self.AVAILABLE_MODELS:
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Initialize optimizers
|
|
133
|
+
self.__available_optimizers = (
|
|
134
|
+
method
|
|
135
|
+
for method in dir(Optimizers)
|
|
136
|
+
if callable(getattr(Optimizers, method)) and not method.startswith("__")
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Initialize conversation settings
|
|
140
|
+
self.conversation = Conversation(
|
|
141
|
+
is_conversation, self.max_tokens_to_sample, filepath, update_file
|
|
142
|
+
)
|
|
143
|
+
self.conversation.history_offset = history_offset
|
|
144
|
+
|
|
145
|
+
if act:
|
|
146
|
+
self.conversation.intro = AwesomePrompts().get_act(cast(Union[str, int], act), default=self.conversation.intro, case_insensitive=True
|
|
147
|
+
) or self.conversation.intro
|
|
148
|
+
elif intro:
|
|
149
|
+
self.conversation.intro = intro
|
|
150
|
+
|
|
151
|
+
# Set headers for the session
|
|
152
|
+
self.headers = {
|
|
153
|
+
"Accept": "application/json",
|
|
154
|
+
"Content-Type": "application/json",
|
|
155
|
+
"User-Agent": UserAgent().random(),
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# Apply proxies to the session
|
|
159
|
+
self.session.headers.update(self.headers)
|
|
160
|
+
if proxies:
|
|
161
|
+
self.session.proxies.update(cast(Any, proxies))
|
|
162
|
+
self.last_response = {}
|
|
163
|
+
|
|
164
|
+
# Rest of the class implementation remains the same...
|
|
165
|
+
@staticmethod
|
|
166
|
+
def extract_query(text: str) -> str:
|
|
167
|
+
"""Extracts the first code block from the given text."""
|
|
168
|
+
pattern = r"```(.*?)```"
|
|
169
|
+
matches = re.findall(pattern, text, re.DOTALL)
|
|
170
|
+
return matches[0].strip() if matches else text.strip()
|
|
171
|
+
|
|
172
|
+
@staticmethod
|
|
173
|
+
def refiner(text: str) -> str:
|
|
174
|
+
"""Refines the input text by removing surrounding quotes."""
|
|
175
|
+
return text.strip('"')
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def _cerebras_extractor(chunk: Union[str, Dict[str, Any]]) -> Optional[str]:
|
|
179
|
+
"""Extracts content from Cerebras stream JSON objects."""
|
|
180
|
+
if isinstance(chunk, dict):
|
|
181
|
+
return chunk.get("choices", [{}])[0].get("delta", {}).get("content")
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
def get_demo_api_key(self, cookie_path: Optional[str] = None) -> str: # Keep this using requests or switch to curl_cffi
|
|
185
|
+
"""Retrieves the demo API key using the provided cookie."""
|
|
186
|
+
if not cookie_path:
|
|
187
|
+
raise ValueError("cookie_path must be provided when using cookie-based authentication")
|
|
188
|
+
try:
|
|
189
|
+
with open(cookie_path, "r") as file:
|
|
190
|
+
cookies = {item["name"]: item["value"] for item in json.load(file)}
|
|
191
|
+
except FileNotFoundError:
|
|
192
|
+
raise FileNotFoundError(f"Cookie file not found at path: {cookie_path}")
|
|
193
|
+
except json.JSONDecodeError:
|
|
194
|
+
raise json.JSONDecodeError("Invalid JSON format in the cookie file.", "", 0)
|
|
195
|
+
|
|
196
|
+
headers = {
|
|
197
|
+
"Accept": "*/*",
|
|
198
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
199
|
+
"Content-Type": "application/json",
|
|
200
|
+
"Origin": "https://inference.cerebras.ai",
|
|
201
|
+
"Referer": "https://inference.cerebras.ai/",
|
|
202
|
+
"user-agent": UserAgent().random(),
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
json_data = {
|
|
206
|
+
"operationName": "GetMyDemoApiKey",
|
|
207
|
+
"variables": {},
|
|
208
|
+
"query": "query GetMyDemoApiKey {\n GetMyDemoApiKey\n}",
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
# Use the initialized curl_cffi session
|
|
213
|
+
response = self.session.post(
|
|
214
|
+
"https://inference.cerebras.ai/api/graphql",
|
|
215
|
+
cookies=cookies,
|
|
216
|
+
headers=headers,
|
|
217
|
+
json=json_data,
|
|
218
|
+
timeout=self.timeout,
|
|
219
|
+
impersonate="chrome120" # Add impersonate
|
|
220
|
+
)
|
|
221
|
+
response.raise_for_status()
|
|
222
|
+
api_key = response.json().get("data", {}).get("GetMyDemoApiKey")
|
|
223
|
+
return api_key
|
|
224
|
+
except curl_cffi.CurlError as e:
|
|
225
|
+
raise exceptions.APIConnectionError(f"Failed to retrieve API key: {e}")
|
|
226
|
+
except KeyError:
|
|
227
|
+
raise exceptions.InvalidResponseError("API key not found in response.")
|
|
228
|
+
|
|
229
|
+
def _make_request(self, messages: List[Dict], stream: bool = False) -> Union[Dict, Generator, str]:
|
|
230
|
+
"""Make a request to the Cerebras API."""
|
|
231
|
+
headers = {
|
|
232
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
233
|
+
"Content-Type": "application/json",
|
|
234
|
+
"User-Agent": UserAgent().random(),
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
payload = {
|
|
238
|
+
"model": self.model,
|
|
239
|
+
"messages": messages,
|
|
240
|
+
"stream": stream,
|
|
241
|
+
"max_tokens": self.max_tokens_to_sample,
|
|
242
|
+
"temperature": self.temperature,
|
|
243
|
+
"top_p": self.top_p
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
# Use the initialized curl_cffi session
|
|
248
|
+
response = self.session.post(
|
|
249
|
+
"https://api.cerebras.ai/v1/chat/completions",
|
|
250
|
+
headers=headers,
|
|
251
|
+
json=payload,
|
|
252
|
+
stream=stream,
|
|
253
|
+
timeout=self.timeout,
|
|
254
|
+
impersonate="chrome120" # Add impersonate
|
|
255
|
+
)
|
|
256
|
+
response.raise_for_status()
|
|
257
|
+
|
|
258
|
+
if stream:
|
|
259
|
+
def generate_stream() -> Generator[str, None, None]:
|
|
260
|
+
# Use sanitize_stream
|
|
261
|
+
processed_stream = sanitize_stream(
|
|
262
|
+
data=response.iter_content(chunk_size=None), # Pass byte iterator
|
|
263
|
+
intro_value="data:",
|
|
264
|
+
to_json=True, # Stream sends JSON
|
|
265
|
+
content_extractor=self._cerebras_extractor, # Use the specific extractor
|
|
266
|
+
yield_raw_on_error=False # Skip non-JSON lines or lines where extractor fails
|
|
267
|
+
)
|
|
268
|
+
for content_chunk in processed_stream:
|
|
269
|
+
if content_chunk and isinstance(content_chunk, str):
|
|
270
|
+
yield content_chunk # Yield the extracted text chunk
|
|
271
|
+
|
|
272
|
+
return generate_stream()
|
|
273
|
+
else:
|
|
274
|
+
response_json = response.json()
|
|
275
|
+
# Extract content for non-streaming response
|
|
276
|
+
content = response_json.get("choices", [{}])[0].get("message", {}).get("content")
|
|
277
|
+
return content if content else "" # Return empty string if not found
|
|
278
|
+
|
|
279
|
+
except curl_cffi.CurlError as e:
|
|
280
|
+
raise exceptions.APIConnectionError(f"Request failed (CurlError): {e}") from e
|
|
281
|
+
except Exception as e:
|
|
282
|
+
# Check if it's an HTTP error with status code
|
|
283
|
+
if hasattr(e, 'response') and hasattr(e.response, 'status_code'):
|
|
284
|
+
status_code = e.response.status_code
|
|
285
|
+
if status_code == 401:
|
|
286
|
+
raise exceptions.APIConnectionError(
|
|
287
|
+
"Authentication failed (401): Invalid API key. Please check your API key and try again."
|
|
288
|
+
) from e
|
|
289
|
+
elif status_code == 403:
|
|
290
|
+
raise exceptions.APIConnectionError(
|
|
291
|
+
"Access forbidden (403): Your API key may not have permission to access this resource."
|
|
292
|
+
) from e
|
|
293
|
+
elif status_code == 429:
|
|
294
|
+
raise exceptions.APIConnectionError(
|
|
295
|
+
"Rate limit exceeded (429): Too many requests. Please wait and try again."
|
|
296
|
+
) from e
|
|
297
|
+
else:
|
|
298
|
+
raise exceptions.APIConnectionError(f"HTTP {status_code} error: {e}") from e
|
|
299
|
+
else:
|
|
300
|
+
raise exceptions.APIConnectionError(f"Request failed: {e}") from e
|
|
301
|
+
|
|
302
|
+
def ask(
|
|
303
|
+
self,
|
|
304
|
+
prompt: str,
|
|
305
|
+
stream: bool = False,
|
|
306
|
+
raw: bool = False, # Add raw parameter for consistency
|
|
307
|
+
optimizer: Optional[str] = None,
|
|
308
|
+
conversationally: bool = False,
|
|
309
|
+
**kwargs: Any,
|
|
310
|
+
) -> Response:
|
|
311
|
+
"""Send a prompt to the model and get a response."""
|
|
312
|
+
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
|
|
313
|
+
if optimizer:
|
|
314
|
+
if optimizer in self.__available_optimizers:
|
|
315
|
+
conversation_prompt = getattr(Optimizers, optimizer)(
|
|
316
|
+
conversation_prompt if conversationally else prompt
|
|
317
|
+
)
|
|
318
|
+
else:
|
|
319
|
+
raise Exception(f"Optimizer is not one of {self.__available_optimizers}")
|
|
320
|
+
|
|
321
|
+
messages = [
|
|
322
|
+
{"role": "system", "content": self.system_prompt},
|
|
323
|
+
{"role": "user", "content": conversation_prompt}
|
|
324
|
+
]
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
response = self._make_request(messages, stream)
|
|
328
|
+
|
|
329
|
+
if stream:
|
|
330
|
+
# Wrap the generator to yield dicts or raw strings
|
|
331
|
+
def stream_wrapper() -> Generator[Union[str, Dict[str, str]], None, None]:
|
|
332
|
+
full_text = ""
|
|
333
|
+
for chunk in response:
|
|
334
|
+
full_text += chunk
|
|
335
|
+
yield chunk if raw else {"text": chunk}
|
|
336
|
+
# Update history after stream finishes
|
|
337
|
+
self.last_response = {"text": full_text}
|
|
338
|
+
self.conversation.update_chat_history(prompt, full_text)
|
|
339
|
+
return stream_wrapper()
|
|
340
|
+
else:
|
|
341
|
+
# Non-streaming response is already the full text string
|
|
342
|
+
self.last_response = {"text": response}
|
|
343
|
+
self.conversation.update_chat_history(prompt, response)
|
|
344
|
+
return self.last_response if not raw else json.dumps(self.last_response)
|
|
345
|
+
|
|
346
|
+
except Exception as e:
|
|
347
|
+
raise exceptions.FailedToGenerateResponseError(f"Error during request: {e}")
|
|
348
|
+
|
|
349
|
+
def chat(
|
|
350
|
+
self,
|
|
351
|
+
prompt: str,
|
|
352
|
+
stream: bool = False,
|
|
353
|
+
optimizer: Optional[str] = None,
|
|
354
|
+
conversationally: bool = False,
|
|
355
|
+
**kwargs: Any,
|
|
356
|
+
) -> Union[str, Generator[str, None, None]]:
|
|
357
|
+
"""Chat with the model."""
|
|
358
|
+
raw = kwargs.get("raw", False)
|
|
359
|
+
# Ask returns a generator for stream=True, dict/str for stream=False
|
|
360
|
+
response_gen_or_dict = self.ask(prompt, stream, raw=raw, optimizer=optimizer, conversationally=conversationally, **kwargs)
|
|
361
|
+
|
|
362
|
+
if stream:
|
|
363
|
+
# Wrap the generator from ask() to get message text
|
|
364
|
+
def stream_wrapper() -> Generator[str, None, None]:
|
|
365
|
+
for chunk in response_gen_or_dict:
|
|
366
|
+
if raw:
|
|
367
|
+
yield chunk
|
|
368
|
+
else:
|
|
369
|
+
yield self.get_message(cast(Response, chunk))
|
|
370
|
+
return stream_wrapper()
|
|
371
|
+
else:
|
|
372
|
+
# Non-streaming response
|
|
373
|
+
if raw:
|
|
374
|
+
return str(response_gen_or_dict)
|
|
375
|
+
return self.get_message(response_gen_or_dict)
|
|
376
|
+
|
|
377
|
+
def get_message(self, response: Response) -> str:
|
|
378
|
+
"""Retrieves message from response."""
|
|
379
|
+
# Updated to handle dict input from ask()
|
|
380
|
+
if not isinstance(response, dict):
|
|
381
|
+
return str(response)
|
|
382
|
+
return cast(Dict[str, Any], response).get("text", "")
|
|
383
|
+
|
|
384
|
+
if __name__ == "__main__":
|
|
385
|
+
from rich import print
|
|
386
|
+
|
|
387
|
+
# Example usage
|
|
388
|
+
cerebras = Cerebras(
|
|
389
|
+
api_key='csk-**********************', # Replace with your actual API key
|
|
390
|
+
model='qwen-3-235b-a22b-instruct-2507',
|
|
391
|
+
system_prompt="You are a helpful AI assistant."
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
# Test with streaming
|
|
395
|
+
response = cerebras.chat("Hello!", stream=True)
|
|
396
|
+
for chunk in response:
|
|
397
|
+
print(chunk, end="", flush=True)
|