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.
Files changed (413) hide show
  1. webscout/AIauto.py +524 -251
  2. webscout/AIbase.py +247 -319
  3. webscout/AIutel.py +68 -703
  4. webscout/Bard.py +1072 -1026
  5. webscout/Extra/GitToolkit/__init__.py +10 -10
  6. webscout/Extra/GitToolkit/gitapi/__init__.py +20 -12
  7. webscout/Extra/GitToolkit/gitapi/gist.py +142 -0
  8. webscout/Extra/GitToolkit/gitapi/organization.py +91 -0
  9. webscout/Extra/GitToolkit/gitapi/repository.py +308 -195
  10. webscout/Extra/GitToolkit/gitapi/search.py +162 -0
  11. webscout/Extra/GitToolkit/gitapi/trending.py +236 -0
  12. webscout/Extra/GitToolkit/gitapi/user.py +128 -96
  13. webscout/Extra/GitToolkit/gitapi/utils.py +82 -62
  14. webscout/Extra/YTToolkit/README.md +443 -375
  15. webscout/Extra/YTToolkit/YTdownloader.py +953 -957
  16. webscout/Extra/YTToolkit/__init__.py +3 -3
  17. webscout/Extra/YTToolkit/transcriber.py +595 -476
  18. webscout/Extra/YTToolkit/ytapi/README.md +230 -44
  19. webscout/Extra/YTToolkit/ytapi/__init__.py +22 -6
  20. webscout/Extra/YTToolkit/ytapi/captions.py +190 -0
  21. webscout/Extra/YTToolkit/ytapi/channel.py +302 -307
  22. webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
  23. webscout/Extra/YTToolkit/ytapi/extras.py +178 -118
  24. webscout/Extra/YTToolkit/ytapi/hashtag.py +120 -0
  25. webscout/Extra/YTToolkit/ytapi/https.py +89 -88
  26. webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
  27. webscout/Extra/YTToolkit/ytapi/playlist.py +59 -59
  28. webscout/Extra/YTToolkit/ytapi/pool.py +8 -8
  29. webscout/Extra/YTToolkit/ytapi/query.py +143 -40
  30. webscout/Extra/YTToolkit/ytapi/shorts.py +122 -0
  31. webscout/Extra/YTToolkit/ytapi/stream.py +68 -63
  32. webscout/Extra/YTToolkit/ytapi/suggestions.py +97 -0
  33. webscout/Extra/YTToolkit/ytapi/utils.py +66 -62
  34. webscout/Extra/YTToolkit/ytapi/video.py +403 -232
  35. webscout/Extra/__init__.py +2 -3
  36. webscout/Extra/gguf.py +1298 -684
  37. webscout/Extra/tempmail/README.md +487 -487
  38. webscout/Extra/tempmail/__init__.py +28 -28
  39. webscout/Extra/tempmail/async_utils.py +143 -141
  40. webscout/Extra/tempmail/base.py +172 -161
  41. webscout/Extra/tempmail/cli.py +191 -187
  42. webscout/Extra/tempmail/emailnator.py +88 -84
  43. webscout/Extra/tempmail/mail_tm.py +378 -361
  44. webscout/Extra/tempmail/temp_mail_io.py +304 -292
  45. webscout/Extra/weather.py +196 -194
  46. webscout/Extra/weather_ascii.py +17 -15
  47. webscout/Provider/AISEARCH/PERPLEXED_search.py +175 -0
  48. webscout/Provider/AISEARCH/Perplexity.py +292 -333
  49. webscout/Provider/AISEARCH/README.md +106 -279
  50. webscout/Provider/AISEARCH/__init__.py +16 -9
  51. webscout/Provider/AISEARCH/brave_search.py +298 -0
  52. webscout/Provider/AISEARCH/iask_search.py +357 -410
  53. webscout/Provider/AISEARCH/monica_search.py +200 -220
  54. webscout/Provider/AISEARCH/webpilotai_search.py +242 -255
  55. webscout/Provider/Algion.py +413 -0
  56. webscout/Provider/Andi.py +74 -69
  57. webscout/Provider/Apriel.py +313 -0
  58. webscout/Provider/Ayle.py +323 -0
  59. webscout/Provider/ChatSandbox.py +329 -342
  60. webscout/Provider/ClaudeOnline.py +365 -0
  61. webscout/Provider/Cohere.py +232 -208
  62. webscout/Provider/DeepAI.py +367 -0
  63. webscout/Provider/Deepinfra.py +467 -340
  64. webscout/Provider/EssentialAI.py +217 -0
  65. webscout/Provider/ExaAI.py +274 -261
  66. webscout/Provider/Gemini.py +175 -169
  67. webscout/Provider/GithubChat.py +385 -369
  68. webscout/Provider/Gradient.py +286 -0
  69. webscout/Provider/Groq.py +556 -801
  70. webscout/Provider/HadadXYZ.py +323 -0
  71. webscout/Provider/HeckAI.py +392 -375
  72. webscout/Provider/HuggingFace.py +387 -0
  73. webscout/Provider/IBM.py +340 -0
  74. webscout/Provider/Jadve.py +317 -291
  75. webscout/Provider/K2Think.py +306 -0
  76. webscout/Provider/Koboldai.py +221 -384
  77. webscout/Provider/Netwrck.py +273 -270
  78. webscout/Provider/Nvidia.py +310 -0
  79. webscout/Provider/OPENAI/DeepAI.py +489 -0
  80. webscout/Provider/OPENAI/K2Think.py +423 -0
  81. webscout/Provider/OPENAI/PI.py +463 -0
  82. webscout/Provider/OPENAI/README.md +890 -952
  83. webscout/Provider/OPENAI/TogetherAI.py +405 -0
  84. webscout/Provider/OPENAI/TwoAI.py +255 -357
  85. webscout/Provider/OPENAI/__init__.py +148 -40
  86. webscout/Provider/OPENAI/ai4chat.py +348 -293
  87. webscout/Provider/OPENAI/akashgpt.py +436 -0
  88. webscout/Provider/OPENAI/algion.py +303 -0
  89. webscout/Provider/OPENAI/{exachat.py → ayle.py} +365 -444
  90. webscout/Provider/OPENAI/base.py +253 -249
  91. webscout/Provider/OPENAI/cerebras.py +296 -0
  92. webscout/Provider/OPENAI/chatgpt.py +870 -556
  93. webscout/Provider/OPENAI/chatsandbox.py +233 -173
  94. webscout/Provider/OPENAI/deepinfra.py +403 -322
  95. webscout/Provider/OPENAI/e2b.py +2370 -1414
  96. webscout/Provider/OPENAI/elmo.py +278 -0
  97. webscout/Provider/OPENAI/exaai.py +452 -417
  98. webscout/Provider/OPENAI/freeassist.py +446 -0
  99. webscout/Provider/OPENAI/gradient.py +448 -0
  100. webscout/Provider/OPENAI/groq.py +380 -364
  101. webscout/Provider/OPENAI/hadadxyz.py +292 -0
  102. webscout/Provider/OPENAI/heckai.py +333 -308
  103. webscout/Provider/OPENAI/huggingface.py +321 -0
  104. webscout/Provider/OPENAI/ibm.py +425 -0
  105. webscout/Provider/OPENAI/llmchat.py +253 -0
  106. webscout/Provider/OPENAI/llmchatco.py +378 -335
  107. webscout/Provider/OPENAI/meta.py +541 -0
  108. webscout/Provider/OPENAI/netwrck.py +374 -357
  109. webscout/Provider/OPENAI/nvidia.py +317 -0
  110. webscout/Provider/OPENAI/oivscode.py +348 -287
  111. webscout/Provider/OPENAI/openrouter.py +328 -0
  112. webscout/Provider/OPENAI/pydantic_imports.py +1 -172
  113. webscout/Provider/OPENAI/sambanova.py +397 -0
  114. webscout/Provider/OPENAI/sonus.py +305 -304
  115. webscout/Provider/OPENAI/textpollinations.py +370 -339
  116. webscout/Provider/OPENAI/toolbaz.py +375 -413
  117. webscout/Provider/OPENAI/typefully.py +419 -355
  118. webscout/Provider/OPENAI/typliai.py +279 -0
  119. webscout/Provider/OPENAI/utils.py +314 -318
  120. webscout/Provider/OPENAI/wisecat.py +359 -387
  121. webscout/Provider/OPENAI/writecream.py +185 -163
  122. webscout/Provider/OPENAI/x0gpt.py +462 -365
  123. webscout/Provider/OPENAI/zenmux.py +380 -0
  124. webscout/Provider/OpenRouter.py +386 -0
  125. webscout/Provider/Openai.py +337 -496
  126. webscout/Provider/PI.py +443 -429
  127. webscout/Provider/QwenLM.py +346 -254
  128. webscout/Provider/STT/__init__.py +28 -0
  129. webscout/Provider/STT/base.py +303 -0
  130. webscout/Provider/STT/elevenlabs.py +264 -0
  131. webscout/Provider/Sambanova.py +317 -0
  132. webscout/Provider/TTI/README.md +69 -82
  133. webscout/Provider/TTI/__init__.py +37 -7
  134. webscout/Provider/TTI/base.py +147 -64
  135. webscout/Provider/TTI/claudeonline.py +393 -0
  136. webscout/Provider/TTI/magicstudio.py +292 -201
  137. webscout/Provider/TTI/miragic.py +180 -0
  138. webscout/Provider/TTI/pollinations.py +331 -221
  139. webscout/Provider/TTI/together.py +334 -0
  140. webscout/Provider/TTI/utils.py +14 -11
  141. webscout/Provider/TTS/README.md +186 -192
  142. webscout/Provider/TTS/__init__.py +43 -10
  143. webscout/Provider/TTS/base.py +523 -159
  144. webscout/Provider/TTS/deepgram.py +286 -156
  145. webscout/Provider/TTS/elevenlabs.py +189 -111
  146. webscout/Provider/TTS/freetts.py +218 -0
  147. webscout/Provider/TTS/murfai.py +288 -113
  148. webscout/Provider/TTS/openai_fm.py +364 -129
  149. webscout/Provider/TTS/parler.py +203 -111
  150. webscout/Provider/TTS/qwen.py +334 -0
  151. webscout/Provider/TTS/sherpa.py +286 -0
  152. webscout/Provider/TTS/speechma.py +693 -580
  153. webscout/Provider/TTS/streamElements.py +275 -333
  154. webscout/Provider/TTS/utils.py +280 -280
  155. webscout/Provider/TextPollinationsAI.py +331 -308
  156. webscout/Provider/TogetherAI.py +450 -0
  157. webscout/Provider/TwoAI.py +309 -475
  158. webscout/Provider/TypliAI.py +311 -305
  159. webscout/Provider/UNFINISHED/ChatHub.py +219 -209
  160. webscout/Provider/{OPENAI/glider.py → UNFINISHED/ChutesAI.py} +331 -326
  161. webscout/Provider/{GizAI.py → UNFINISHED/GizAI.py} +300 -295
  162. webscout/Provider/{Marcus.py → UNFINISHED/Marcus.py} +218 -198
  163. webscout/Provider/UNFINISHED/Qodo.py +481 -0
  164. webscout/Provider/{MCPCore.py → UNFINISHED/XenAI.py} +330 -315
  165. webscout/Provider/UNFINISHED/Youchat.py +347 -330
  166. webscout/Provider/UNFINISHED/aihumanizer.py +41 -0
  167. webscout/Provider/UNFINISHED/grammerchecker.py +37 -0
  168. webscout/Provider/UNFINISHED/liner.py +342 -0
  169. webscout/Provider/UNFINISHED/liner_api_request.py +246 -263
  170. webscout/Provider/{samurai.py → UNFINISHED/samurai.py} +231 -224
  171. webscout/Provider/WiseCat.py +256 -233
  172. webscout/Provider/WrDoChat.py +390 -370
  173. webscout/Provider/__init__.py +115 -174
  174. webscout/Provider/ai4chat.py +181 -174
  175. webscout/Provider/akashgpt.py +330 -335
  176. webscout/Provider/cerebras.py +397 -290
  177. webscout/Provider/cleeai.py +236 -213
  178. webscout/Provider/elmo.py +291 -283
  179. webscout/Provider/geminiapi.py +343 -208
  180. webscout/Provider/julius.py +245 -223
  181. webscout/Provider/learnfastai.py +333 -325
  182. webscout/Provider/llama3mitril.py +230 -215
  183. webscout/Provider/llmchat.py +308 -258
  184. webscout/Provider/llmchatco.py +321 -306
  185. webscout/Provider/meta.py +996 -801
  186. webscout/Provider/oivscode.py +332 -309
  187. webscout/Provider/searchchat.py +316 -292
  188. webscout/Provider/sonus.py +264 -258
  189. webscout/Provider/toolbaz.py +359 -353
  190. webscout/Provider/turboseek.py +332 -266
  191. webscout/Provider/typefully.py +262 -202
  192. webscout/Provider/x0gpt.py +332 -299
  193. webscout/__init__.py +31 -39
  194. webscout/__main__.py +5 -5
  195. webscout/cli.py +585 -524
  196. webscout/client.py +1497 -70
  197. webscout/conversation.py +140 -436
  198. webscout/exceptions.py +383 -362
  199. webscout/litagent/__init__.py +29 -29
  200. webscout/litagent/agent.py +492 -455
  201. webscout/litagent/constants.py +60 -60
  202. webscout/models.py +505 -181
  203. webscout/optimizers.py +74 -420
  204. webscout/prompt_manager.py +376 -288
  205. webscout/sanitize.py +1514 -0
  206. webscout/scout/README.md +452 -404
  207. webscout/scout/__init__.py +8 -8
  208. webscout/scout/core/__init__.py +7 -7
  209. webscout/scout/core/crawler.py +330 -210
  210. webscout/scout/core/scout.py +800 -607
  211. webscout/scout/core/search_result.py +51 -96
  212. webscout/scout/core/text_analyzer.py +64 -63
  213. webscout/scout/core/text_utils.py +412 -277
  214. webscout/scout/core/web_analyzer.py +54 -52
  215. webscout/scout/element.py +872 -478
  216. webscout/scout/parsers/__init__.py +70 -69
  217. webscout/scout/parsers/html5lib_parser.py +182 -172
  218. webscout/scout/parsers/html_parser.py +238 -236
  219. webscout/scout/parsers/lxml_parser.py +203 -178
  220. webscout/scout/utils.py +38 -37
  221. webscout/search/__init__.py +47 -0
  222. webscout/search/base.py +201 -0
  223. webscout/search/bing_main.py +45 -0
  224. webscout/search/brave_main.py +92 -0
  225. webscout/search/duckduckgo_main.py +57 -0
  226. webscout/search/engines/__init__.py +127 -0
  227. webscout/search/engines/bing/__init__.py +15 -0
  228. webscout/search/engines/bing/base.py +35 -0
  229. webscout/search/engines/bing/images.py +114 -0
  230. webscout/search/engines/bing/news.py +96 -0
  231. webscout/search/engines/bing/suggestions.py +36 -0
  232. webscout/search/engines/bing/text.py +109 -0
  233. webscout/search/engines/brave/__init__.py +19 -0
  234. webscout/search/engines/brave/base.py +47 -0
  235. webscout/search/engines/brave/images.py +213 -0
  236. webscout/search/engines/brave/news.py +353 -0
  237. webscout/search/engines/brave/suggestions.py +318 -0
  238. webscout/search/engines/brave/text.py +167 -0
  239. webscout/search/engines/brave/videos.py +364 -0
  240. webscout/search/engines/duckduckgo/__init__.py +25 -0
  241. webscout/search/engines/duckduckgo/answers.py +80 -0
  242. webscout/search/engines/duckduckgo/base.py +189 -0
  243. webscout/search/engines/duckduckgo/images.py +100 -0
  244. webscout/search/engines/duckduckgo/maps.py +183 -0
  245. webscout/search/engines/duckduckgo/news.py +70 -0
  246. webscout/search/engines/duckduckgo/suggestions.py +22 -0
  247. webscout/search/engines/duckduckgo/text.py +221 -0
  248. webscout/search/engines/duckduckgo/translate.py +48 -0
  249. webscout/search/engines/duckduckgo/videos.py +80 -0
  250. webscout/search/engines/duckduckgo/weather.py +84 -0
  251. webscout/search/engines/mojeek.py +61 -0
  252. webscout/search/engines/wikipedia.py +77 -0
  253. webscout/search/engines/yahoo/__init__.py +41 -0
  254. webscout/search/engines/yahoo/answers.py +19 -0
  255. webscout/search/engines/yahoo/base.py +34 -0
  256. webscout/search/engines/yahoo/images.py +323 -0
  257. webscout/search/engines/yahoo/maps.py +19 -0
  258. webscout/search/engines/yahoo/news.py +258 -0
  259. webscout/search/engines/yahoo/suggestions.py +140 -0
  260. webscout/search/engines/yahoo/text.py +273 -0
  261. webscout/search/engines/yahoo/translate.py +19 -0
  262. webscout/search/engines/yahoo/videos.py +302 -0
  263. webscout/search/engines/yahoo/weather.py +220 -0
  264. webscout/search/engines/yandex.py +67 -0
  265. webscout/search/engines/yep/__init__.py +13 -0
  266. webscout/search/engines/yep/base.py +34 -0
  267. webscout/search/engines/yep/images.py +101 -0
  268. webscout/search/engines/yep/suggestions.py +38 -0
  269. webscout/search/engines/yep/text.py +99 -0
  270. webscout/search/http_client.py +172 -0
  271. webscout/search/results.py +141 -0
  272. webscout/search/yahoo_main.py +57 -0
  273. webscout/search/yep_main.py +48 -0
  274. webscout/server/__init__.py +48 -0
  275. webscout/server/config.py +78 -0
  276. webscout/server/exceptions.py +69 -0
  277. webscout/server/providers.py +286 -0
  278. webscout/server/request_models.py +131 -0
  279. webscout/server/request_processing.py +404 -0
  280. webscout/server/routes.py +642 -0
  281. webscout/server/server.py +351 -0
  282. webscout/server/ui_templates.py +1171 -0
  283. webscout/swiftcli/__init__.py +79 -95
  284. webscout/swiftcli/core/__init__.py +7 -7
  285. webscout/swiftcli/core/cli.py +574 -297
  286. webscout/swiftcli/core/context.py +98 -104
  287. webscout/swiftcli/core/group.py +268 -241
  288. webscout/swiftcli/decorators/__init__.py +28 -28
  289. webscout/swiftcli/decorators/command.py +243 -221
  290. webscout/swiftcli/decorators/options.py +247 -220
  291. webscout/swiftcli/decorators/output.py +392 -252
  292. webscout/swiftcli/exceptions.py +21 -21
  293. webscout/swiftcli/plugins/__init__.py +9 -9
  294. webscout/swiftcli/plugins/base.py +134 -135
  295. webscout/swiftcli/plugins/manager.py +269 -269
  296. webscout/swiftcli/utils/__init__.py +58 -59
  297. webscout/swiftcli/utils/formatting.py +251 -252
  298. webscout/swiftcli/utils/parsing.py +368 -267
  299. webscout/update_checker.py +280 -136
  300. webscout/utils.py +28 -14
  301. webscout/version.py +2 -1
  302. webscout/version.py.bak +3 -0
  303. webscout/zeroart/__init__.py +218 -135
  304. webscout/zeroart/base.py +70 -66
  305. webscout/zeroart/effects.py +155 -101
  306. webscout/zeroart/fonts.py +1799 -1239
  307. webscout-2026.1.19.dist-info/METADATA +638 -0
  308. webscout-2026.1.19.dist-info/RECORD +312 -0
  309. {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/WHEEL +1 -1
  310. {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/entry_points.txt +1 -1
  311. webscout/DWEBS.py +0 -520
  312. webscout/Extra/Act.md +0 -309
  313. webscout/Extra/GitToolkit/gitapi/README.md +0 -110
  314. webscout/Extra/autocoder/__init__.py +0 -9
  315. webscout/Extra/autocoder/autocoder.py +0 -1105
  316. webscout/Extra/autocoder/autocoder_utiles.py +0 -332
  317. webscout/Extra/gguf.md +0 -430
  318. webscout/Extra/weather.md +0 -281
  319. webscout/Litlogger/README.md +0 -10
  320. webscout/Litlogger/__init__.py +0 -15
  321. webscout/Litlogger/formats.py +0 -4
  322. webscout/Litlogger/handlers.py +0 -103
  323. webscout/Litlogger/levels.py +0 -13
  324. webscout/Litlogger/logger.py +0 -92
  325. webscout/Provider/AI21.py +0 -177
  326. webscout/Provider/AISEARCH/DeepFind.py +0 -254
  327. webscout/Provider/AISEARCH/felo_search.py +0 -202
  328. webscout/Provider/AISEARCH/genspark_search.py +0 -324
  329. webscout/Provider/AISEARCH/hika_search.py +0 -186
  330. webscout/Provider/AISEARCH/scira_search.py +0 -298
  331. webscout/Provider/Aitopia.py +0 -316
  332. webscout/Provider/AllenAI.py +0 -440
  333. webscout/Provider/Blackboxai.py +0 -791
  334. webscout/Provider/ChatGPTClone.py +0 -237
  335. webscout/Provider/ChatGPTGratis.py +0 -194
  336. webscout/Provider/Cloudflare.py +0 -324
  337. webscout/Provider/ExaChat.py +0 -358
  338. webscout/Provider/Flowith.py +0 -217
  339. webscout/Provider/FreeGemini.py +0 -250
  340. webscout/Provider/Glider.py +0 -225
  341. webscout/Provider/HF_space/__init__.py +0 -0
  342. webscout/Provider/HF_space/qwen_qwen2.py +0 -206
  343. webscout/Provider/HuggingFaceChat.py +0 -469
  344. webscout/Provider/Hunyuan.py +0 -283
  345. webscout/Provider/LambdaChat.py +0 -411
  346. webscout/Provider/Llama3.py +0 -259
  347. webscout/Provider/Nemotron.py +0 -218
  348. webscout/Provider/OLLAMA.py +0 -396
  349. webscout/Provider/OPENAI/BLACKBOXAI.py +0 -766
  350. webscout/Provider/OPENAI/Cloudflare.py +0 -378
  351. webscout/Provider/OPENAI/FreeGemini.py +0 -283
  352. webscout/Provider/OPENAI/NEMOTRON.py +0 -232
  353. webscout/Provider/OPENAI/Qwen3.py +0 -283
  354. webscout/Provider/OPENAI/api.py +0 -969
  355. webscout/Provider/OPENAI/c4ai.py +0 -373
  356. webscout/Provider/OPENAI/chatgptclone.py +0 -494
  357. webscout/Provider/OPENAI/copilot.py +0 -242
  358. webscout/Provider/OPENAI/flowith.py +0 -162
  359. webscout/Provider/OPENAI/freeaichat.py +0 -359
  360. webscout/Provider/OPENAI/mcpcore.py +0 -389
  361. webscout/Provider/OPENAI/multichat.py +0 -376
  362. webscout/Provider/OPENAI/opkfc.py +0 -496
  363. webscout/Provider/OPENAI/scirachat.py +0 -477
  364. webscout/Provider/OPENAI/standardinput.py +0 -433
  365. webscout/Provider/OPENAI/typegpt.py +0 -364
  366. webscout/Provider/OPENAI/uncovrAI.py +0 -463
  367. webscout/Provider/OPENAI/venice.py +0 -431
  368. webscout/Provider/OPENAI/yep.py +0 -382
  369. webscout/Provider/OpenGPT.py +0 -209
  370. webscout/Provider/Perplexitylabs.py +0 -415
  371. webscout/Provider/Reka.py +0 -214
  372. webscout/Provider/StandardInput.py +0 -290
  373. webscout/Provider/TTI/aiarta.py +0 -365
  374. webscout/Provider/TTI/artbit.py +0 -0
  375. webscout/Provider/TTI/fastflux.py +0 -200
  376. webscout/Provider/TTI/piclumen.py +0 -203
  377. webscout/Provider/TTI/pixelmuse.py +0 -225
  378. webscout/Provider/TTS/gesserit.py +0 -128
  379. webscout/Provider/TTS/sthir.py +0 -94
  380. webscout/Provider/TeachAnything.py +0 -229
  381. webscout/Provider/UNFINISHED/puterjs.py +0 -635
  382. webscout/Provider/UNFINISHED/test_lmarena.py +0 -119
  383. webscout/Provider/Venice.py +0 -258
  384. webscout/Provider/VercelAI.py +0 -253
  385. webscout/Provider/Writecream.py +0 -246
  386. webscout/Provider/WritingMate.py +0 -269
  387. webscout/Provider/asksteve.py +0 -220
  388. webscout/Provider/chatglm.py +0 -215
  389. webscout/Provider/copilot.py +0 -425
  390. webscout/Provider/freeaichat.py +0 -285
  391. webscout/Provider/granite.py +0 -235
  392. webscout/Provider/hermes.py +0 -266
  393. webscout/Provider/koala.py +0 -170
  394. webscout/Provider/lmarena.py +0 -198
  395. webscout/Provider/multichat.py +0 -364
  396. webscout/Provider/scira_chat.py +0 -299
  397. webscout/Provider/scnet.py +0 -243
  398. webscout/Provider/talkai.py +0 -194
  399. webscout/Provider/typegpt.py +0 -289
  400. webscout/Provider/uncovr.py +0 -368
  401. webscout/Provider/yep.py +0 -389
  402. webscout/litagent/Readme.md +0 -276
  403. webscout/litprinter/__init__.py +0 -59
  404. webscout/swiftcli/Readme.md +0 -323
  405. webscout/tempid.py +0 -128
  406. webscout/webscout_search.py +0 -1184
  407. webscout/webscout_search_async.py +0 -654
  408. webscout/yep_search.py +0 -347
  409. webscout/zeroart/README.md +0 -89
  410. webscout-8.2.9.dist-info/METADATA +0 -1033
  411. webscout-8.2.9.dist-info/RECORD +0 -289
  412. {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/licenses/LICENSE.md +0 -0
  413. {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/top_level.txt +0 -0
@@ -1,290 +1,397 @@
1
-
2
- import re
3
- import curl_cffi
4
- from curl_cffi.requests import Session
5
- import json
6
- import os
7
- from typing import Any, Dict, Optional, Generator, List, Union
8
- from webscout.AIutel import Optimizers, Conversation, AwesomePrompts, sanitize_stream # Import sanitize_stream
9
- from webscout.AIbase import Provider
10
- from webscout import exceptions
11
- from webscout.litagent import LitAgent as UserAgent
12
-
13
- class Cerebras(Provider):
14
- """
15
- A class to interact with the Cerebras API using a cookie for authentication.
16
- """
17
-
18
- AVAILABLE_MODELS = [
19
- "llama3.1-8b",
20
- "llama-3.3-70b",
21
- "deepseek-r1-distill-llama-70b",
22
- "llama-4-scout-17b-16e-instruct",
23
- "qwen-3-32b",
24
-
25
-
26
- ]
27
-
28
- def __init__(
29
- self,
30
- is_conversation: bool = True,
31
- max_tokens: int = 2049,
32
- timeout: int = 30,
33
- intro: str = None,
34
- filepath: str = None,
35
- update_file: bool = True,
36
- proxies: dict = {},
37
- history_offset: int = 10250,
38
- act: str = None,
39
- cookie_path: str = "cookie.json",
40
- model: str = "llama3.1-8b",
41
- system_prompt: str = "You are a helpful assistant.",
42
- ):
43
- # Validate model choice
44
- if model not in self.AVAILABLE_MODELS:
45
- raise ValueError(
46
- f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}"
47
- )
48
-
49
- # Initialize basic settings first
50
- self.timeout = timeout
51
- self.model = model
52
- self.system_prompt = system_prompt
53
- self.is_conversation = is_conversation
54
- self.max_tokens_to_sample = max_tokens
55
- self.last_response = {}
56
-
57
- self.session = Session() # Initialize curl_cffi session
58
-
59
- # Get API key first
60
- try:
61
- self.api_key = self.get_demo_api_key(cookie_path)
62
- except Exception as e:
63
- raise exceptions.APIConnectionError(f"Failed to initialize Cerebras client: {e}")
64
-
65
- # Initialize optimizers
66
- self.__available_optimizers = (
67
- method
68
- for method in dir(Optimizers)
69
- if callable(getattr(Optimizers, method)) and not method.startswith("__")
70
- )
71
-
72
- # Initialize conversation settings
73
- Conversation.intro = (
74
- AwesomePrompts().get_act(
75
- act, raise_not_found=True, default=None, case_insensitive=True
76
- )
77
- if act
78
- else None
79
- )
80
- self.conversation = Conversation(
81
- is_conversation, self.max_tokens_to_sample, filepath, update_file
82
- )
83
- self.conversation.history_offset = history_offset
84
-
85
- # Apply proxies to the session
86
- self.session.proxies = proxies
87
-
88
- # Rest of the class implementation remains the same...
89
- @staticmethod
90
- def extract_query(text: str) -> str:
91
- """Extracts the first code block from the given text."""
92
- pattern = r"```(.*?)```"
93
- matches = re.findall(pattern, text, re.DOTALL)
94
- return matches[0].strip() if matches else text.strip()
95
-
96
- @staticmethod
97
- def refiner(text: str) -> str:
98
- """Refines the input text by removing surrounding quotes."""
99
- return text.strip('"')
100
-
101
- @staticmethod
102
- def _cerebras_extractor(chunk: Union[str, Dict[str, Any]]) -> Optional[str]:
103
- """Extracts content from Cerebras stream JSON objects."""
104
- if isinstance(chunk, dict):
105
- return chunk.get("choices", [{}])[0].get("delta", {}).get("content")
106
- return None
107
-
108
- def get_demo_api_key(self, cookie_path: str) -> str: # Keep this using requests or switch to curl_cffi
109
- """Retrieves the demo API key using the provided cookie."""
110
- try:
111
- with open(cookie_path, "r") as file:
112
- cookies = {item["name"]: item["value"] for item in json.load(file)}
113
- except FileNotFoundError:
114
- raise FileNotFoundError(f"Cookie file not found at path: {cookie_path}")
115
- except json.JSONDecodeError:
116
- raise json.JSONDecodeError("Invalid JSON format in the cookie file.", "", 0)
117
-
118
- headers = {
119
- "Accept": "*/*",
120
- "Accept-Language": "en-US,en;q=0.9",
121
- "Content-Type": "application/json",
122
- "Origin": "https://inference.cerebras.ai",
123
- "Referer": "https://inference.cerebras.ai/",
124
- "user-agent": UserAgent().random(),
125
- }
126
-
127
- json_data = {
128
- "operationName": "GetMyDemoApiKey",
129
- "variables": {},
130
- "query": "query GetMyDemoApiKey {\n GetMyDemoApiKey\n}",
131
- }
132
-
133
- try:
134
- # Use the initialized curl_cffi session
135
- response = self.session.post(
136
- "https://inference.cerebras.ai/api/graphql",
137
- cookies=cookies,
138
- headers=headers,
139
- json=json_data,
140
- timeout=self.timeout,
141
- impersonate="chrome120" # Add impersonate
142
- )
143
- response.raise_for_status()
144
- api_key = response.json().get("data", {}).get("GetMyDemoApiKey")
145
- return api_key
146
- except curl_cffi.CurlError as e:
147
- raise exceptions.APIConnectionError(f"Failed to retrieve API key: {e}")
148
- except KeyError:
149
- raise exceptions.InvalidResponseError("API key not found in response.")
150
-
151
- def _make_request(self, messages: List[Dict], stream: bool = False) -> Union[Dict, Generator]:
152
- """Make a request to the Cerebras API."""
153
- headers = {
154
- "Authorization": f"Bearer {self.api_key}",
155
- "Content-Type": "application/json",
156
- "User-Agent": UserAgent().random(),
157
- }
158
-
159
- payload = {
160
- "model": self.model,
161
- "messages": messages,
162
- "stream": stream
163
- }
164
-
165
- try:
166
- # Use the initialized curl_cffi session
167
- response = self.session.post(
168
- "https://api.cerebras.ai/v1/chat/completions",
169
- headers=headers,
170
- json=payload,
171
- stream=stream,
172
- timeout=self.timeout,
173
- impersonate="chrome120" # Add impersonate
174
- )
175
- response.raise_for_status()
176
-
177
- if stream:
178
- def generate_stream():
179
- # Use sanitize_stream
180
- processed_stream = sanitize_stream(
181
- data=response.iter_content(chunk_size=None), # Pass byte iterator
182
- intro_value="data:",
183
- to_json=True, # Stream sends JSON
184
- content_extractor=self._cerebras_extractor, # Use the specific extractor
185
- yield_raw_on_error=False # Skip non-JSON lines or lines where extractor fails
186
- )
187
- for content_chunk in processed_stream:
188
- if content_chunk and isinstance(content_chunk, str):
189
- yield content_chunk # Yield the extracted text chunk
190
-
191
- return generate_stream()
192
- else:
193
- response_json = response.json()
194
- # Extract content for non-streaming response
195
- content = response_json.get("choices", [{}])[0].get("message", {}).get("content")
196
- return content if content else "" # Return empty string if not found
197
-
198
- except curl_cffi.CurlError as e:
199
- raise exceptions.APIConnectionError(f"Request failed (CurlError): {e}") from e
200
- except Exception as e: # Catch other potential errors
201
- raise exceptions.APIConnectionError(f"Request failed: {e}")
202
-
203
- def ask(
204
- self,
205
- prompt: str,
206
- stream: bool = False,
207
- raw: bool = False, # Add raw parameter for consistency
208
- optimizer: str = None,
209
- conversationally: bool = False,
210
- ) -> Union[Dict, Generator]:
211
- """Send a prompt to the model and get a response."""
212
- conversation_prompt = self.conversation.gen_complete_prompt(prompt)
213
- if optimizer:
214
- if optimizer in self.__available_optimizers:
215
- conversation_prompt = getattr(Optimizers, optimizer)(
216
- conversation_prompt if conversationally else prompt
217
- )
218
- else:
219
- raise Exception(f"Optimizer is not one of {self.__available_optimizers}")
220
-
221
- messages = [
222
- {"role": "system", "content": self.system_prompt},
223
- {"role": "user", "content": conversation_prompt}
224
- ]
225
-
226
- try:
227
- response = self._make_request(messages, stream)
228
-
229
- if stream:
230
- # Wrap the generator to yield dicts or raw strings
231
- def stream_wrapper():
232
- full_text = ""
233
- for chunk in response:
234
- full_text += chunk
235
- yield chunk if raw else {"text": chunk}
236
- # Update history after stream finishes
237
- self.last_response = {"text": full_text}
238
- self.conversation.update_chat_history(prompt, full_text)
239
- return stream_wrapper()
240
- else:
241
- # Non-streaming response is already the full text string
242
- self.last_response = {"text": response}
243
- self.conversation.update_chat_history(prompt, response)
244
- return self.last_response if not raw else response # Return dict or raw string
245
-
246
- except Exception as e:
247
- raise exceptions.FailedToGenerateResponseError(f"Error during request: {e}")
248
-
249
- def chat(
250
- self,
251
- prompt: str,
252
- stream: bool = False,
253
- optimizer: str = None,
254
- conversationally: bool = False,
255
- ) -> Union[str, Generator]:
256
- """Chat with the model."""
257
- # Ask returns a generator for stream=True, dict/str for stream=False
258
- response_gen_or_dict = self.ask(prompt, stream, raw=False, optimizer=optimizer, conversationally=conversationally)
259
-
260
- if stream:
261
- # Wrap the generator from ask() to get message text
262
- def stream_wrapper():
263
- for chunk_dict in response_gen_or_dict:
264
- yield self.get_message(chunk_dict)
265
- return stream_wrapper()
266
- else:
267
- # Non-streaming response is already a dict
268
- return self.get_message(response_gen_or_dict)
269
-
270
- def get_message(self, response: str) -> str:
271
- """Retrieves message from response."""
272
- # Updated to handle dict input from ask()
273
- assert isinstance(response, dict), "Response should be of dict data-type only for get_message"
274
- return response.get("text", "")
275
-
276
-
277
- if __name__ == "__main__":
278
- from rich import print
279
-
280
- # Example usage
281
- cerebras = Cerebras(
282
- cookie_path=r'cookies.json',
283
- model='llama3.1-8b',
284
- system_prompt="You are a helpful AI assistant."
285
- )
286
-
287
- # Test with streaming
288
- response = cerebras.chat("Hello!", stream=True)
289
- for chunk in response:
290
- print(chunk, end="", flush=True)
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)