webscout 8.2.7__py3-none-any.whl → 8.2.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of webscout might be problematic. Click here for more details.

Files changed (323) hide show
  1. webscout/AIauto.py +1 -1
  2. webscout/AIutel.py +298 -249
  3. webscout/Extra/Act.md +309 -0
  4. webscout/Extra/GitToolkit/__init__.py +10 -0
  5. webscout/Extra/GitToolkit/gitapi/README.md +110 -0
  6. webscout/Extra/GitToolkit/gitapi/__init__.py +12 -0
  7. webscout/Extra/GitToolkit/gitapi/repository.py +195 -0
  8. webscout/Extra/GitToolkit/gitapi/user.py +96 -0
  9. webscout/Extra/GitToolkit/gitapi/utils.py +62 -0
  10. webscout/Extra/YTToolkit/README.md +375 -0
  11. webscout/Extra/YTToolkit/YTdownloader.py +957 -0
  12. webscout/Extra/YTToolkit/__init__.py +3 -0
  13. webscout/Extra/YTToolkit/transcriber.py +476 -0
  14. webscout/Extra/YTToolkit/ytapi/README.md +44 -0
  15. webscout/Extra/YTToolkit/ytapi/__init__.py +6 -0
  16. webscout/Extra/YTToolkit/ytapi/channel.py +307 -0
  17. webscout/Extra/YTToolkit/ytapi/errors.py +13 -0
  18. webscout/Extra/YTToolkit/ytapi/extras.py +118 -0
  19. webscout/Extra/YTToolkit/ytapi/https.py +88 -0
  20. webscout/Extra/YTToolkit/ytapi/patterns.py +61 -0
  21. webscout/Extra/YTToolkit/ytapi/playlist.py +59 -0
  22. webscout/Extra/YTToolkit/ytapi/pool.py +8 -0
  23. webscout/Extra/YTToolkit/ytapi/query.py +40 -0
  24. webscout/Extra/YTToolkit/ytapi/stream.py +63 -0
  25. webscout/Extra/YTToolkit/ytapi/utils.py +62 -0
  26. webscout/Extra/YTToolkit/ytapi/video.py +232 -0
  27. webscout/Extra/__init__.py +7 -0
  28. webscout/Extra/autocoder/__init__.py +9 -0
  29. webscout/Extra/autocoder/autocoder.py +1105 -0
  30. webscout/Extra/autocoder/autocoder_utiles.py +332 -0
  31. webscout/Extra/gguf.md +430 -0
  32. webscout/Extra/gguf.py +684 -0
  33. webscout/Extra/tempmail/README.md +488 -0
  34. webscout/Extra/tempmail/__init__.py +28 -0
  35. webscout/Extra/tempmail/async_utils.py +141 -0
  36. webscout/Extra/tempmail/base.py +161 -0
  37. webscout/Extra/tempmail/cli.py +187 -0
  38. webscout/Extra/tempmail/emailnator.py +84 -0
  39. webscout/Extra/tempmail/mail_tm.py +361 -0
  40. webscout/Extra/tempmail/temp_mail_io.py +292 -0
  41. webscout/Extra/weather.md +281 -0
  42. webscout/Extra/weather.py +194 -0
  43. webscout/Extra/weather_ascii.py +76 -0
  44. webscout/Litlogger/Readme.md +175 -0
  45. webscout/Litlogger/__init__.py +67 -0
  46. webscout/Litlogger/core/__init__.py +6 -0
  47. webscout/Litlogger/core/level.py +23 -0
  48. webscout/Litlogger/core/logger.py +165 -0
  49. webscout/Litlogger/handlers/__init__.py +12 -0
  50. webscout/Litlogger/handlers/console.py +33 -0
  51. webscout/Litlogger/handlers/file.py +143 -0
  52. webscout/Litlogger/handlers/network.py +173 -0
  53. webscout/Litlogger/styles/__init__.py +7 -0
  54. webscout/Litlogger/styles/colors.py +249 -0
  55. webscout/Litlogger/styles/formats.py +458 -0
  56. webscout/Litlogger/styles/text.py +87 -0
  57. webscout/Litlogger/utils/__init__.py +6 -0
  58. webscout/Litlogger/utils/detectors.py +153 -0
  59. webscout/Litlogger/utils/formatters.py +200 -0
  60. webscout/Provider/AI21.py +177 -0
  61. webscout/Provider/AISEARCH/DeepFind.py +254 -0
  62. webscout/Provider/AISEARCH/Perplexity.py +359 -0
  63. webscout/Provider/AISEARCH/README.md +279 -0
  64. webscout/Provider/AISEARCH/__init__.py +9 -0
  65. webscout/Provider/AISEARCH/felo_search.py +228 -0
  66. webscout/Provider/AISEARCH/genspark_search.py +350 -0
  67. webscout/Provider/AISEARCH/hika_search.py +198 -0
  68. webscout/Provider/AISEARCH/iask_search.py +436 -0
  69. webscout/Provider/AISEARCH/monica_search.py +246 -0
  70. webscout/Provider/AISEARCH/scira_search.py +324 -0
  71. webscout/Provider/AISEARCH/webpilotai_search.py +281 -0
  72. webscout/Provider/Aitopia.py +316 -0
  73. webscout/Provider/AllenAI.py +440 -0
  74. webscout/Provider/Andi.py +228 -0
  75. webscout/Provider/Blackboxai.py +673 -0
  76. webscout/Provider/ChatGPTClone.py +237 -0
  77. webscout/Provider/ChatGPTGratis.py +194 -0
  78. webscout/Provider/ChatSandbox.py +342 -0
  79. webscout/Provider/Cloudflare.py +324 -0
  80. webscout/Provider/Cohere.py +208 -0
  81. webscout/Provider/Deepinfra.py +340 -0
  82. webscout/Provider/ExaAI.py +261 -0
  83. webscout/Provider/ExaChat.py +358 -0
  84. webscout/Provider/Flowith.py +217 -0
  85. webscout/Provider/FreeGemini.py +250 -0
  86. webscout/Provider/Gemini.py +169 -0
  87. webscout/Provider/GithubChat.py +370 -0
  88. webscout/Provider/GizAI.py +295 -0
  89. webscout/Provider/Glider.py +225 -0
  90. webscout/Provider/Groq.py +801 -0
  91. webscout/Provider/HF_space/__init__.py +0 -0
  92. webscout/Provider/HF_space/qwen_qwen2.py +206 -0
  93. webscout/Provider/HeckAI.py +285 -0
  94. webscout/Provider/HuggingFaceChat.py +469 -0
  95. webscout/Provider/Hunyuan.py +283 -0
  96. webscout/Provider/Jadve.py +291 -0
  97. webscout/Provider/Koboldai.py +384 -0
  98. webscout/Provider/LambdaChat.py +411 -0
  99. webscout/Provider/Llama3.py +259 -0
  100. webscout/Provider/MCPCore.py +315 -0
  101. webscout/Provider/Marcus.py +198 -0
  102. webscout/Provider/Nemotron.py +218 -0
  103. webscout/Provider/Netwrck.py +270 -0
  104. webscout/Provider/OLLAMA.py +396 -0
  105. webscout/Provider/OPENAI/BLACKBOXAI.py +735 -0
  106. webscout/Provider/OPENAI/Cloudflare.py +378 -0
  107. webscout/Provider/OPENAI/FreeGemini.py +282 -0
  108. webscout/Provider/OPENAI/NEMOTRON.py +244 -0
  109. webscout/Provider/OPENAI/README.md +1253 -0
  110. webscout/Provider/OPENAI/__init__.py +36 -0
  111. webscout/Provider/OPENAI/ai4chat.py +293 -0
  112. webscout/Provider/OPENAI/api.py +810 -0
  113. webscout/Provider/OPENAI/base.py +249 -0
  114. webscout/Provider/OPENAI/c4ai.py +373 -0
  115. webscout/Provider/OPENAI/chatgpt.py +556 -0
  116. webscout/Provider/OPENAI/chatgptclone.py +488 -0
  117. webscout/Provider/OPENAI/chatsandbox.py +172 -0
  118. webscout/Provider/OPENAI/deepinfra.py +319 -0
  119. webscout/Provider/OPENAI/e2b.py +1356 -0
  120. webscout/Provider/OPENAI/exaai.py +411 -0
  121. webscout/Provider/OPENAI/exachat.py +443 -0
  122. webscout/Provider/OPENAI/flowith.py +162 -0
  123. webscout/Provider/OPENAI/freeaichat.py +359 -0
  124. webscout/Provider/OPENAI/glider.py +323 -0
  125. webscout/Provider/OPENAI/groq.py +361 -0
  126. webscout/Provider/OPENAI/heckai.py +307 -0
  127. webscout/Provider/OPENAI/llmchatco.py +335 -0
  128. webscout/Provider/OPENAI/mcpcore.py +383 -0
  129. webscout/Provider/OPENAI/multichat.py +376 -0
  130. webscout/Provider/OPENAI/netwrck.py +356 -0
  131. webscout/Provider/OPENAI/opkfc.py +496 -0
  132. webscout/Provider/OPENAI/scirachat.py +471 -0
  133. webscout/Provider/OPENAI/sonus.py +303 -0
  134. webscout/Provider/OPENAI/standardinput.py +433 -0
  135. webscout/Provider/OPENAI/textpollinations.py +339 -0
  136. webscout/Provider/OPENAI/toolbaz.py +413 -0
  137. webscout/Provider/OPENAI/typefully.py +355 -0
  138. webscout/Provider/OPENAI/typegpt.py +358 -0
  139. webscout/Provider/OPENAI/uncovrAI.py +462 -0
  140. webscout/Provider/OPENAI/utils.py +307 -0
  141. webscout/Provider/OPENAI/venice.py +425 -0
  142. webscout/Provider/OPENAI/wisecat.py +381 -0
  143. webscout/Provider/OPENAI/writecream.py +163 -0
  144. webscout/Provider/OPENAI/x0gpt.py +378 -0
  145. webscout/Provider/OPENAI/yep.py +356 -0
  146. webscout/Provider/OpenGPT.py +209 -0
  147. webscout/Provider/Openai.py +496 -0
  148. webscout/Provider/PI.py +429 -0
  149. webscout/Provider/Perplexitylabs.py +415 -0
  150. webscout/Provider/QwenLM.py +254 -0
  151. webscout/Provider/Reka.py +214 -0
  152. webscout/Provider/StandardInput.py +290 -0
  153. webscout/Provider/TTI/AiForce/README.md +159 -0
  154. webscout/Provider/TTI/AiForce/__init__.py +22 -0
  155. webscout/Provider/TTI/AiForce/async_aiforce.py +224 -0
  156. webscout/Provider/TTI/AiForce/sync_aiforce.py +245 -0
  157. webscout/Provider/TTI/FreeAIPlayground/README.md +99 -0
  158. webscout/Provider/TTI/FreeAIPlayground/__init__.py +9 -0
  159. webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +181 -0
  160. webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +180 -0
  161. webscout/Provider/TTI/ImgSys/README.md +174 -0
  162. webscout/Provider/TTI/ImgSys/__init__.py +23 -0
  163. webscout/Provider/TTI/ImgSys/async_imgsys.py +202 -0
  164. webscout/Provider/TTI/ImgSys/sync_imgsys.py +195 -0
  165. webscout/Provider/TTI/MagicStudio/README.md +101 -0
  166. webscout/Provider/TTI/MagicStudio/__init__.py +2 -0
  167. webscout/Provider/TTI/MagicStudio/async_magicstudio.py +111 -0
  168. webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +109 -0
  169. webscout/Provider/TTI/Nexra/README.md +155 -0
  170. webscout/Provider/TTI/Nexra/__init__.py +22 -0
  171. webscout/Provider/TTI/Nexra/async_nexra.py +286 -0
  172. webscout/Provider/TTI/Nexra/sync_nexra.py +258 -0
  173. webscout/Provider/TTI/PollinationsAI/README.md +146 -0
  174. webscout/Provider/TTI/PollinationsAI/__init__.py +23 -0
  175. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +311 -0
  176. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +265 -0
  177. webscout/Provider/TTI/README.md +128 -0
  178. webscout/Provider/TTI/__init__.py +12 -0
  179. webscout/Provider/TTI/aiarta/README.md +134 -0
  180. webscout/Provider/TTI/aiarta/__init__.py +2 -0
  181. webscout/Provider/TTI/aiarta/async_aiarta.py +482 -0
  182. webscout/Provider/TTI/aiarta/sync_aiarta.py +440 -0
  183. webscout/Provider/TTI/artbit/README.md +100 -0
  184. webscout/Provider/TTI/artbit/__init__.py +22 -0
  185. webscout/Provider/TTI/artbit/async_artbit.py +155 -0
  186. webscout/Provider/TTI/artbit/sync_artbit.py +148 -0
  187. webscout/Provider/TTI/fastflux/README.md +129 -0
  188. webscout/Provider/TTI/fastflux/__init__.py +22 -0
  189. webscout/Provider/TTI/fastflux/async_fastflux.py +261 -0
  190. webscout/Provider/TTI/fastflux/sync_fastflux.py +252 -0
  191. webscout/Provider/TTI/huggingface/README.md +114 -0
  192. webscout/Provider/TTI/huggingface/__init__.py +22 -0
  193. webscout/Provider/TTI/huggingface/async_huggingface.py +199 -0
  194. webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -0
  195. webscout/Provider/TTI/piclumen/README.md +161 -0
  196. webscout/Provider/TTI/piclumen/__init__.py +23 -0
  197. webscout/Provider/TTI/piclumen/async_piclumen.py +268 -0
  198. webscout/Provider/TTI/piclumen/sync_piclumen.py +233 -0
  199. webscout/Provider/TTI/pixelmuse/README.md +79 -0
  200. webscout/Provider/TTI/pixelmuse/__init__.py +4 -0
  201. webscout/Provider/TTI/pixelmuse/async_pixelmuse.py +249 -0
  202. webscout/Provider/TTI/pixelmuse/sync_pixelmuse.py +182 -0
  203. webscout/Provider/TTI/talkai/README.md +139 -0
  204. webscout/Provider/TTI/talkai/__init__.py +4 -0
  205. webscout/Provider/TTI/talkai/async_talkai.py +229 -0
  206. webscout/Provider/TTI/talkai/sync_talkai.py +207 -0
  207. webscout/Provider/TTS/README.md +192 -0
  208. webscout/Provider/TTS/__init__.py +9 -0
  209. webscout/Provider/TTS/base.py +159 -0
  210. webscout/Provider/TTS/deepgram.py +156 -0
  211. webscout/Provider/TTS/elevenlabs.py +111 -0
  212. webscout/Provider/TTS/gesserit.py +128 -0
  213. webscout/Provider/TTS/murfai.py +113 -0
  214. webscout/Provider/TTS/parler.py +111 -0
  215. webscout/Provider/TTS/speechma.py +580 -0
  216. webscout/Provider/TTS/sthir.py +94 -0
  217. webscout/Provider/TTS/streamElements.py +333 -0
  218. webscout/Provider/TTS/utils.py +280 -0
  219. webscout/Provider/TeachAnything.py +229 -0
  220. webscout/Provider/TextPollinationsAI.py +308 -0
  221. webscout/Provider/TwoAI.py +280 -0
  222. webscout/Provider/TypliAI.py +305 -0
  223. webscout/Provider/UNFINISHED/ChatHub.py +209 -0
  224. webscout/Provider/UNFINISHED/Youchat.py +330 -0
  225. webscout/Provider/UNFINISHED/liner_api_request.py +263 -0
  226. webscout/Provider/UNFINISHED/oivscode.py +351 -0
  227. webscout/Provider/UNFINISHED/test_lmarena.py +119 -0
  228. webscout/Provider/Venice.py +258 -0
  229. webscout/Provider/VercelAI.py +253 -0
  230. webscout/Provider/WiseCat.py +233 -0
  231. webscout/Provider/WrDoChat.py +370 -0
  232. webscout/Provider/Writecream.py +246 -0
  233. webscout/Provider/WritingMate.py +269 -0
  234. webscout/Provider/__init__.py +172 -0
  235. webscout/Provider/ai4chat.py +149 -0
  236. webscout/Provider/akashgpt.py +335 -0
  237. webscout/Provider/asksteve.py +220 -0
  238. webscout/Provider/cerebras.py +290 -0
  239. webscout/Provider/chatglm.py +215 -0
  240. webscout/Provider/cleeai.py +213 -0
  241. webscout/Provider/copilot.py +425 -0
  242. webscout/Provider/elmo.py +283 -0
  243. webscout/Provider/freeaichat.py +285 -0
  244. webscout/Provider/geminiapi.py +208 -0
  245. webscout/Provider/granite.py +235 -0
  246. webscout/Provider/hermes.py +266 -0
  247. webscout/Provider/julius.py +223 -0
  248. webscout/Provider/koala.py +170 -0
  249. webscout/Provider/learnfastai.py +325 -0
  250. webscout/Provider/llama3mitril.py +215 -0
  251. webscout/Provider/llmchat.py +258 -0
  252. webscout/Provider/llmchatco.py +306 -0
  253. webscout/Provider/lmarena.py +198 -0
  254. webscout/Provider/meta.py +801 -0
  255. webscout/Provider/multichat.py +364 -0
  256. webscout/Provider/samurai.py +223 -0
  257. webscout/Provider/scira_chat.py +299 -0
  258. webscout/Provider/scnet.py +243 -0
  259. webscout/Provider/searchchat.py +292 -0
  260. webscout/Provider/sonus.py +258 -0
  261. webscout/Provider/talkai.py +194 -0
  262. webscout/Provider/toolbaz.py +353 -0
  263. webscout/Provider/turboseek.py +266 -0
  264. webscout/Provider/typefully.py +202 -0
  265. webscout/Provider/typegpt.py +289 -0
  266. webscout/Provider/uncovr.py +368 -0
  267. webscout/Provider/x0gpt.py +299 -0
  268. webscout/Provider/yep.py +389 -0
  269. webscout/__init__.py +4 -2
  270. webscout/cli.py +3 -28
  271. webscout/conversation.py +35 -35
  272. webscout/litagent/Readme.md +276 -0
  273. webscout/litagent/__init__.py +29 -0
  274. webscout/litagent/agent.py +455 -0
  275. webscout/litagent/constants.py +60 -0
  276. webscout/litprinter/__init__.py +59 -0
  277. webscout/scout/README.md +402 -0
  278. webscout/scout/__init__.py +8 -0
  279. webscout/scout/core/__init__.py +7 -0
  280. webscout/scout/core/crawler.py +140 -0
  281. webscout/scout/core/scout.py +568 -0
  282. webscout/scout/core/search_result.py +96 -0
  283. webscout/scout/core/text_analyzer.py +63 -0
  284. webscout/scout/core/text_utils.py +277 -0
  285. webscout/scout/core/web_analyzer.py +52 -0
  286. webscout/scout/element.py +460 -0
  287. webscout/scout/parsers/__init__.py +69 -0
  288. webscout/scout/parsers/html5lib_parser.py +172 -0
  289. webscout/scout/parsers/html_parser.py +236 -0
  290. webscout/scout/parsers/lxml_parser.py +178 -0
  291. webscout/scout/utils.py +37 -0
  292. webscout/swiftcli/Readme.md +323 -0
  293. webscout/swiftcli/__init__.py +95 -0
  294. webscout/swiftcli/core/__init__.py +7 -0
  295. webscout/swiftcli/core/cli.py +297 -0
  296. webscout/swiftcli/core/context.py +104 -0
  297. webscout/swiftcli/core/group.py +241 -0
  298. webscout/swiftcli/decorators/__init__.py +28 -0
  299. webscout/swiftcli/decorators/command.py +221 -0
  300. webscout/swiftcli/decorators/options.py +220 -0
  301. webscout/swiftcli/decorators/output.py +252 -0
  302. webscout/swiftcli/exceptions.py +21 -0
  303. webscout/swiftcli/plugins/__init__.py +9 -0
  304. webscout/swiftcli/plugins/base.py +135 -0
  305. webscout/swiftcli/plugins/manager.py +262 -0
  306. webscout/swiftcli/utils/__init__.py +59 -0
  307. webscout/swiftcli/utils/formatting.py +252 -0
  308. webscout/swiftcli/utils/parsing.py +267 -0
  309. webscout/version.py +1 -1
  310. webscout/webscout_search.py +2 -182
  311. webscout/webscout_search_async.py +1 -179
  312. webscout/zeroart/README.md +89 -0
  313. webscout/zeroart/__init__.py +135 -0
  314. webscout/zeroart/base.py +66 -0
  315. webscout/zeroart/effects.py +101 -0
  316. webscout/zeroart/fonts.py +1239 -0
  317. {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/METADATA +115 -60
  318. webscout-8.2.8.dist-info/RECORD +334 -0
  319. {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/WHEEL +1 -1
  320. webscout-8.2.7.dist-info/RECORD +0 -26
  321. {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/entry_points.txt +0 -0
  322. {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/licenses/LICENSE.md +0 -0
  323. {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/top_level.txt +0 -0
File without changes
@@ -0,0 +1,206 @@
1
+ from dataclasses import dataclass
2
+ from enum import Enum
3
+ import requests
4
+ import json
5
+ import re
6
+ import uuid
7
+ from typing import Union, List, Dict, Generator, Optional, Any, TypedDict, Final
8
+
9
+ # Type definitions
10
+ class Role(Enum):
11
+ SYSTEM = "system"
12
+ USER = "user"
13
+ ASSISTANT = "assistant"
14
+
15
+ class Message(TypedDict):
16
+ role: str
17
+ content: str
18
+
19
+ class APIResponse(TypedDict):
20
+ event_id: str
21
+ fn_index: int
22
+ data: List[Any]
23
+
24
+ class StreamData(TypedDict):
25
+ msg: str
26
+ output: Dict[str, Any]
27
+
28
+ @dataclass
29
+ class APIConfig:
30
+ url: Final[str] = "https://qwen-qwen2-72b-instruct.hf.space"
31
+ api_endpoint: Final[str] = "https://qwen-qwen2-72b-instruct.hf.space/queue/join?"
32
+
33
+ @dataclass
34
+ class RequestHeaders:
35
+ join: Dict[str, str]
36
+ data: Dict[str, str]
37
+
38
+ @classmethod
39
+ def create_default(cls, base_url: str) -> 'RequestHeaders':
40
+ common_headers = {
41
+ 'accept-language': 'en-US,en;q=0.9',
42
+ 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
43
+ }
44
+
45
+ return cls(
46
+ join={
47
+ **common_headers,
48
+ 'accept': '*/*',
49
+ 'content-type': 'application/json',
50
+ 'origin': base_url,
51
+ 'referer': f'{base_url}/',
52
+ },
53
+ data={
54
+ **common_headers,
55
+ 'accept': 'text/event-stream',
56
+ 'referer': f'{base_url}/',
57
+ }
58
+ )
59
+
60
+ class QwenAPI:
61
+ def __init__(self, config: APIConfig = APIConfig()):
62
+ self.config = config
63
+ self.headers = RequestHeaders.create_default(config.url)
64
+
65
+ @staticmethod
66
+ def generate_session_hash() -> str:
67
+ """Generate a unique session hash."""
68
+ return str(uuid.uuid4()).replace('-', '')[:12]
69
+
70
+ @staticmethod
71
+ def format_prompt(messages: List[Message]) -> str:
72
+ """
73
+ Formats a list of messages into a single prompt string.
74
+
75
+ Args:
76
+ messages: A list of message dictionaries with "role" and "content" keys.
77
+
78
+ Returns:
79
+ str: The formatted prompt.
80
+ """
81
+ return "\n".join(f"{message['role']}: {message['content']}" for message in messages)
82
+
83
+ def create_sync_generator(
84
+ self,
85
+ model: str,
86
+ messages: List[Message],
87
+ proxy: Optional[str] = None,
88
+ **kwargs: Any
89
+ ) -> Generator[str, None, None]:
90
+ """
91
+ Synchronously streams responses from the Qwen_Qwen2_72B_Instruct API.
92
+
93
+ Args:
94
+ model: The model to use for the request.
95
+ messages: A list of message dictionaries with "role" and "content" keys.
96
+ proxy: Optional proxy URL for the request.
97
+ **kwargs: Additional keyword arguments.
98
+
99
+ Yields:
100
+ str: Text chunks from the API response.
101
+
102
+ Raises:
103
+ requests.exceptions.RequestException: If the API request fails.
104
+ json.JSONDecodeError: If the response cannot be parsed as JSON.
105
+ """
106
+ session_hash: str = self.generate_session_hash()
107
+
108
+ # Prepare the prompt
109
+ system_messages: List[str] = [
110
+ message["content"]
111
+ for message in messages
112
+ if message["role"] == Role.SYSTEM.value
113
+ ]
114
+ system_prompt: str = "\n".join(system_messages)
115
+
116
+ user_messages: List[Message] = [
117
+ message
118
+ for message in messages
119
+ if message["role"] != Role.SYSTEM.value
120
+ ]
121
+ prompt: str = self.format_prompt(user_messages)
122
+
123
+ payload_join: Dict[str, Any] = {
124
+ "data": [prompt, [], system_prompt],
125
+ "event_data": None,
126
+ "fn_index": 0,
127
+ "trigger_id": 11,
128
+ "session_hash": session_hash
129
+ }
130
+
131
+ with requests.Session() as session:
132
+ # Send join request
133
+ response = session.post(
134
+ self.config.api_endpoint,
135
+ headers=self.headers.join,
136
+ json=payload_join
137
+ )
138
+ response.raise_for_status()
139
+ event_data: APIResponse = response.json()
140
+
141
+ # Prepare data stream request
142
+ url_data: str = f'{self.config.url}/queue/data'
143
+ params_data: Dict[str, str] = {'session_hash': session_hash}
144
+
145
+ # Send data stream request
146
+ full_response: str = ""
147
+ final_full_response: str = ""
148
+
149
+ with session.get(
150
+ url_data,
151
+ headers=self.headers.data,
152
+ params=params_data,
153
+ stream=True
154
+ ) as response:
155
+ response.raise_for_status()
156
+
157
+ for line in response.iter_lines():
158
+ if line:
159
+ decoded_line: str = line.decode('utf-8')
160
+ if decoded_line.startswith('data: '):
161
+ try:
162
+ json_data: StreamData = json.loads(decoded_line[6:])
163
+
164
+ if json_data.get('msg') == 'process_generating':
165
+ if 'output' in json_data and 'data' in json_data['output']:
166
+ output_data: List[Any] = json_data['output']['data']
167
+ if len(output_data) > 1 and len(output_data[1]) > 0:
168
+ for item in output_data[1]:
169
+ if isinstance(item, list) and len(item) > 1:
170
+ fragment: str = str(item[1])
171
+ if not re.match(r'^\[.*\]$', fragment) and not full_response.endswith(fragment):
172
+ full_response += fragment
173
+ yield fragment
174
+
175
+ if json_data.get('msg') == 'process_completed':
176
+ if 'output' in json_data and 'data' in json_data['output']:
177
+ output_data = json_data['output']['data']
178
+ if len(output_data) > 1 and len(output_data[1]) > 0:
179
+ final_full_response = output_data[1][0][1]
180
+
181
+ if final_full_response.startswith(full_response):
182
+ final_full_response = final_full_response[len(full_response):]
183
+
184
+ if final_full_response:
185
+ yield final_full_response
186
+ break
187
+
188
+ except json.JSONDecodeError as e:
189
+ print(f"Could not parse JSON: {decoded_line}")
190
+ raise e
191
+
192
+
193
+ def main() -> None:
194
+ messages: List[Message] = [
195
+ {"role": Role.SYSTEM.value, "content": "You are a helpful assistant."},
196
+ {"role": Role.USER.value, "content": "LOL"}
197
+ ]
198
+
199
+ api = QwenAPI()
200
+ for text in api.create_sync_generator("qwen-qwen2-72b-instruct", messages):
201
+ print(text, end="", flush=True)
202
+ print("\n---\n")
203
+
204
+
205
+ if __name__ == "__main__":
206
+ main()
@@ -0,0 +1,285 @@
1
+ from curl_cffi.requests import Session
2
+ from curl_cffi import CurlError
3
+ import json
4
+ import uuid
5
+ from typing import Any, Dict, Optional, Generator, Union
6
+
7
+ from webscout.AIutel import Optimizers
8
+ from webscout.AIutel import Conversation
9
+ from webscout.AIutel import AwesomePrompts, sanitize_stream
10
+ from webscout.AIbase import Provider, AsyncProvider
11
+ from webscout import exceptions
12
+ from webscout.litagent import LitAgent
13
+
14
+ class HeckAI(Provider):
15
+ """
16
+ A class to interact with the HeckAI API with LitAgent user-agent.
17
+ """
18
+
19
+ AVAILABLE_MODELS = [
20
+ "google/gemini-2.0-flash-001",
21
+ "deepseek/deepseek-chat",
22
+ "deepseek/deepseek-r1",
23
+ "openai/gpt-4o-mini",
24
+ "openai/gpt-4.1-mini",
25
+ "x-ai/grok-3-mini-beta",
26
+ "meta-llama/llama-4-scout"
27
+ ]
28
+
29
+ def __init__(
30
+ self,
31
+ is_conversation: bool = True,
32
+ max_tokens: int = 2049, # Note: max_tokens is not used by this API
33
+ timeout: int = 30,
34
+ intro: str = None,
35
+ filepath: str = None,
36
+ update_file: bool = True,
37
+ proxies: dict = {},
38
+ history_offset: int = 10250,
39
+ act: str = None,
40
+ model: str = "google/gemini-2.0-flash-001",
41
+ language: str = "English"
42
+ ):
43
+ """Initializes the HeckAI API client."""
44
+ if model not in self.AVAILABLE_MODELS:
45
+ raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
46
+
47
+ self.url = "https://api.heckai.weight-wave.com/api/ha/v1/chat"
48
+ self.session_id = str(uuid.uuid4())
49
+ self.language = language
50
+
51
+ # Use LitAgent (keep if needed for other headers or logic)
52
+ self.headers = {
53
+ 'Content-Type': 'application/json',
54
+ 'Origin': 'https://heck.ai', # Keep Origin
55
+ 'Referer': 'https://heck.ai/', # Keep Referer
56
+ }
57
+
58
+ # Initialize curl_cffi Session
59
+ self.session = Session()
60
+ # Update curl_cffi session headers and proxies
61
+ self.session.headers.update(self.headers)
62
+ self.session.proxies = proxies # Assign proxies directly
63
+
64
+ self.is_conversation = is_conversation
65
+ self.max_tokens_to_sample = max_tokens
66
+ self.timeout = timeout
67
+ self.last_response = {}
68
+ self.model = model
69
+ self.previous_question = None
70
+ self.previous_answer = None
71
+
72
+ self.__available_optimizers = (
73
+ method
74
+ for method in dir(Optimizers)
75
+ if callable(getattr(Optimizers, method)) and not method.startswith("__")
76
+ )
77
+ Conversation.intro = (
78
+ AwesomePrompts().get_act(
79
+ act, raise_not_found=True, default=None, case_insensitive=True
80
+ )
81
+ if act
82
+ else intro or Conversation.intro
83
+ )
84
+
85
+ self.conversation = Conversation(
86
+ is_conversation, self.max_tokens_to_sample, filepath, update_file
87
+ )
88
+ self.conversation.history_offset = history_offset
89
+
90
+ def ask(
91
+ self,
92
+ prompt: str,
93
+ stream: bool = False, # API supports streaming
94
+ raw: bool = False,
95
+ optimizer: str = None,
96
+ conversationally: bool = False,
97
+ ) -> Union[Dict[str, Any], Generator]:
98
+ conversation_prompt = self.conversation.gen_complete_prompt(prompt)
99
+ if optimizer:
100
+ if optimizer in self.__available_optimizers:
101
+ conversation_prompt = getattr(Optimizers, optimizer)(conversation_prompt if conversationally else prompt)
102
+ else:
103
+ raise Exception(f"Optimizer is not one of {self.__available_optimizers}")
104
+
105
+ # Payload construction
106
+ payload = {
107
+ "model": self.model,
108
+ "question": conversation_prompt,
109
+ "language": self.language,
110
+ "sessionId": self.session_id,
111
+ "previousQuestion": self.previous_question,
112
+ "previousAnswer": self.previous_answer,
113
+ "imgUrls": [],
114
+ "superSmartMode": False # Added based on API request data
115
+ }
116
+
117
+ # Store this message as previous for next request
118
+ self.previous_question = conversation_prompt
119
+
120
+ def for_stream():
121
+ streaming_text = "" # Initialize outside try block
122
+ try:
123
+ # Use curl_cffi session post with impersonate
124
+ response = self.session.post(
125
+ self.url,
126
+ # headers are set on the session
127
+ data=json.dumps(payload),
128
+ stream=True,
129
+ timeout=self.timeout,
130
+ impersonate="chrome110" # Use a common impersonation profile
131
+ )
132
+ response.raise_for_status() # Check for HTTP errors
133
+
134
+ # Use sanitize_stream to process the stream
135
+ processed_stream = sanitize_stream(
136
+ data=response.iter_content(chunk_size=1024), # Pass byte iterator
137
+ intro_value="data: ", # Prefix to remove (note the space)
138
+ to_json=False, # Content is text
139
+ start_marker="data: [ANSWER_START]",
140
+ end_marker="data: [ANSWER_DONE]",
141
+ skip_markers=["data: [RELATE_Q_START]", "data: [RELATE_Q_DONE]", "data: [REASON_START]", "data: [REASON_DONE]"],
142
+ yield_raw_on_error=True,
143
+ strip_chars=" \n\r\t" # Strip whitespace characters from chunks
144
+ )
145
+
146
+ for content_chunk in processed_stream:
147
+ # content_chunk is the text between ANSWER_START and ANSWER_DONE
148
+ if content_chunk and isinstance(content_chunk, str):
149
+ streaming_text += content_chunk
150
+ yield dict(text=content_chunk) if not raw else content_chunk
151
+
152
+ # Only update history if we received a valid response
153
+ if streaming_text:
154
+ # Update history and previous answer after stream finishes
155
+ self.previous_answer = streaming_text
156
+ # Convert to simple text before updating conversation
157
+ try:
158
+ # Ensure content is valid before updating conversation
159
+ if streaming_text and isinstance(streaming_text, str):
160
+ # Sanitize the content to ensure it's valid
161
+ sanitized_text = streaming_text.strip()
162
+ if sanitized_text: # Only update if we have non-empty content
163
+ self.conversation.update_chat_history(prompt, sanitized_text)
164
+ except Exception as e:
165
+ # If conversation update fails, log but don't crash
166
+ print(f"Warning: Failed to update conversation history: {str(e)}")
167
+
168
+ except CurlError as e: # Catch CurlError
169
+ raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {str(e)}") from e
170
+ except Exception as e: # Catch other potential exceptions (like HTTPError)
171
+ err_text = getattr(e, 'response', None) and getattr(e.response, 'text', '')
172
+ raise exceptions.FailedToGenerateResponseError(f"Request failed ({type(e).__name__}): {str(e)} - {err_text}") from e
173
+
174
+
175
+ def for_non_stream():
176
+ # Aggregate the stream using the updated for_stream logic
177
+ full_text = ""
178
+ try:
179
+ # Ensure raw=False so for_stream yields dicts
180
+ for chunk_data in for_stream():
181
+ if isinstance(chunk_data, dict) and "text" in chunk_data:
182
+ full_text += chunk_data["text"]
183
+ # Handle raw string case if raw=True was passed
184
+ elif raw and isinstance(chunk_data, str):
185
+ full_text += chunk_data
186
+ except Exception as e:
187
+ # If aggregation fails but some text was received, use it. Otherwise, re-raise.
188
+ if not full_text:
189
+ raise exceptions.FailedToGenerateResponseError(f"Failed to get non-stream response: {str(e)}") from e
190
+
191
+ # Return the final aggregated response dict or raw string
192
+ self.last_response = {"text": full_text} # Update last_response here
193
+ return full_text if raw else self.last_response
194
+
195
+
196
+ return for_stream() if stream else for_non_stream()
197
+
198
+ @staticmethod
199
+ def fix_encoding(text):
200
+ if isinstance(text, dict) and "text" in text:
201
+ try:
202
+ text["text"] = text["text"].encode("latin1").decode("utf-8")
203
+ return text
204
+ except (UnicodeError, AttributeError) as e:
205
+ return text
206
+ elif isinstance(text, str):
207
+ try:
208
+ return text.encode("latin1").decode("utf-8")
209
+ except (UnicodeError, AttributeError) as e:
210
+ return text
211
+ return text
212
+
213
+ def chat(
214
+ self,
215
+ prompt: str,
216
+ stream: bool = False,
217
+ optimizer: str = None,
218
+ conversationally: bool = False,
219
+ ) -> Union[str, Generator[str, None, None]]: # Corrected return type hint
220
+ def for_stream_chat():
221
+ # ask() yields dicts or strings when streaming
222
+ gen = self.ask(
223
+ prompt, stream=True, raw=False, # Ensure ask yields dicts
224
+ optimizer=optimizer, conversationally=conversationally
225
+ )
226
+ for response_dict in gen:
227
+ yield self.get_message(response_dict) # get_message expects dict
228
+
229
+ def for_non_stream_chat():
230
+ # ask() returns dict or str when not streaming
231
+ response_data = self.ask(
232
+ prompt, stream=False, raw=False, # Ensure ask returns dict
233
+ optimizer=optimizer, conversationally=conversationally
234
+ )
235
+ return self.get_message(response_data) # get_message expects dict
236
+
237
+ return for_stream_chat() if stream else for_non_stream_chat()
238
+
239
+ def get_message(self, response: dict) -> str:
240
+ # Validate response format
241
+ if not isinstance(response, dict):
242
+ raise TypeError(f"Expected dict response, got {type(response).__name__}")
243
+
244
+ # Handle missing text key gracefully
245
+ if "text" not in response:
246
+ return ""
247
+
248
+ # Ensure text is a string
249
+ text = response["text"]
250
+ if not isinstance(text, str):
251
+ return str(text)
252
+
253
+ return text
254
+
255
+ if __name__ == "__main__":
256
+ # Ensure curl_cffi is installed
257
+ print("-" * 80)
258
+ print(f"{'Model':<50} {'Status':<10} {'Response'}")
259
+ print("-" * 80)
260
+
261
+ for model in HeckAI.AVAILABLE_MODELS:
262
+ try:
263
+ test_ai = HeckAI(model=model, timeout=60)
264
+ # Use non-streaming mode first to avoid potential streaming issues
265
+ try:
266
+ response_text = test_ai.chat("Say 'Hello' in one word", stream=False)
267
+ print(f"\r{model:<50} {'✓':<10} {response_text.strip()[:50]}")
268
+ except Exception as e1:
269
+ # Fall back to streaming if non-streaming fails
270
+ print(f"\r{model:<50} {'Testing stream...':<10}", end="", flush=True)
271
+ response = test_ai.chat("Say 'Hello' in one word", stream=True)
272
+ response_text = ""
273
+ for chunk in response:
274
+ if chunk and isinstance(chunk, str):
275
+ response_text += chunk
276
+
277
+ if response_text and len(response_text.strip()) > 0:
278
+ status = "✓"
279
+ # Truncate response if too long
280
+ display_text = response_text.strip()[:50] + "..." if len(response_text.strip()) > 50 else response_text.strip()
281
+ print(f"\r{model:<50} {status:<10} {display_text}")
282
+ else:
283
+ raise ValueError("Empty or invalid response")
284
+ except Exception as e:
285
+ print(f"\r{model:<50} {'✗':<10} {str(e)}")