webscout 8.2.7__py3-none-any.whl → 8.2.9__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 (281) hide show
  1. webscout/AIauto.py +33 -15
  2. webscout/AIbase.py +96 -37
  3. webscout/AIutel.py +703 -250
  4. webscout/Bard.py +441 -323
  5. webscout/Extra/Act.md +309 -0
  6. webscout/Extra/GitToolkit/__init__.py +10 -0
  7. webscout/Extra/GitToolkit/gitapi/README.md +110 -0
  8. webscout/Extra/GitToolkit/gitapi/__init__.py +12 -0
  9. webscout/Extra/GitToolkit/gitapi/repository.py +195 -0
  10. webscout/Extra/GitToolkit/gitapi/user.py +96 -0
  11. webscout/Extra/GitToolkit/gitapi/utils.py +62 -0
  12. webscout/Extra/YTToolkit/README.md +375 -0
  13. webscout/Extra/YTToolkit/YTdownloader.py +957 -0
  14. webscout/Extra/YTToolkit/__init__.py +3 -0
  15. webscout/Extra/YTToolkit/transcriber.py +476 -0
  16. webscout/Extra/YTToolkit/ytapi/README.md +44 -0
  17. webscout/Extra/YTToolkit/ytapi/__init__.py +6 -0
  18. webscout/Extra/YTToolkit/ytapi/channel.py +307 -0
  19. webscout/Extra/YTToolkit/ytapi/errors.py +13 -0
  20. webscout/Extra/YTToolkit/ytapi/extras.py +118 -0
  21. webscout/Extra/YTToolkit/ytapi/https.py +88 -0
  22. webscout/Extra/YTToolkit/ytapi/patterns.py +61 -0
  23. webscout/Extra/YTToolkit/ytapi/playlist.py +59 -0
  24. webscout/Extra/YTToolkit/ytapi/pool.py +8 -0
  25. webscout/Extra/YTToolkit/ytapi/query.py +40 -0
  26. webscout/Extra/YTToolkit/ytapi/stream.py +63 -0
  27. webscout/Extra/YTToolkit/ytapi/utils.py +62 -0
  28. webscout/Extra/YTToolkit/ytapi/video.py +232 -0
  29. webscout/Extra/__init__.py +7 -0
  30. webscout/Extra/autocoder/__init__.py +9 -0
  31. webscout/Extra/autocoder/autocoder.py +1105 -0
  32. webscout/Extra/autocoder/autocoder_utiles.py +332 -0
  33. webscout/Extra/gguf.md +430 -0
  34. webscout/Extra/gguf.py +684 -0
  35. webscout/Extra/tempmail/README.md +488 -0
  36. webscout/Extra/tempmail/__init__.py +28 -0
  37. webscout/Extra/tempmail/async_utils.py +141 -0
  38. webscout/Extra/tempmail/base.py +161 -0
  39. webscout/Extra/tempmail/cli.py +187 -0
  40. webscout/Extra/tempmail/emailnator.py +84 -0
  41. webscout/Extra/tempmail/mail_tm.py +361 -0
  42. webscout/Extra/tempmail/temp_mail_io.py +292 -0
  43. webscout/Extra/weather.md +281 -0
  44. webscout/Extra/weather.py +194 -0
  45. webscout/Extra/weather_ascii.py +76 -0
  46. webscout/Litlogger/README.md +10 -0
  47. webscout/Litlogger/__init__.py +15 -0
  48. webscout/Litlogger/formats.py +4 -0
  49. webscout/Litlogger/handlers.py +103 -0
  50. webscout/Litlogger/levels.py +13 -0
  51. webscout/Litlogger/logger.py +92 -0
  52. webscout/Provider/AI21.py +177 -0
  53. webscout/Provider/AISEARCH/DeepFind.py +254 -0
  54. webscout/Provider/AISEARCH/Perplexity.py +333 -0
  55. webscout/Provider/AISEARCH/README.md +279 -0
  56. webscout/Provider/AISEARCH/__init__.py +9 -0
  57. webscout/Provider/AISEARCH/felo_search.py +202 -0
  58. webscout/Provider/AISEARCH/genspark_search.py +324 -0
  59. webscout/Provider/AISEARCH/hika_search.py +186 -0
  60. webscout/Provider/AISEARCH/iask_search.py +410 -0
  61. webscout/Provider/AISEARCH/monica_search.py +220 -0
  62. webscout/Provider/AISEARCH/scira_search.py +298 -0
  63. webscout/Provider/AISEARCH/webpilotai_search.py +255 -0
  64. webscout/Provider/Aitopia.py +316 -0
  65. webscout/Provider/AllenAI.py +440 -0
  66. webscout/Provider/Andi.py +228 -0
  67. webscout/Provider/Blackboxai.py +791 -0
  68. webscout/Provider/ChatGPTClone.py +237 -0
  69. webscout/Provider/ChatGPTGratis.py +194 -0
  70. webscout/Provider/ChatSandbox.py +342 -0
  71. webscout/Provider/Cloudflare.py +324 -0
  72. webscout/Provider/Cohere.py +208 -0
  73. webscout/Provider/Deepinfra.py +340 -0
  74. webscout/Provider/ExaAI.py +261 -0
  75. webscout/Provider/ExaChat.py +358 -0
  76. webscout/Provider/Flowith.py +217 -0
  77. webscout/Provider/FreeGemini.py +250 -0
  78. webscout/Provider/Gemini.py +169 -0
  79. webscout/Provider/GithubChat.py +369 -0
  80. webscout/Provider/GizAI.py +295 -0
  81. webscout/Provider/Glider.py +225 -0
  82. webscout/Provider/Groq.py +801 -0
  83. webscout/Provider/HF_space/__init__.py +0 -0
  84. webscout/Provider/HF_space/qwen_qwen2.py +206 -0
  85. webscout/Provider/HeckAI.py +375 -0
  86. webscout/Provider/HuggingFaceChat.py +469 -0
  87. webscout/Provider/Hunyuan.py +283 -0
  88. webscout/Provider/Jadve.py +291 -0
  89. webscout/Provider/Koboldai.py +384 -0
  90. webscout/Provider/LambdaChat.py +411 -0
  91. webscout/Provider/Llama3.py +259 -0
  92. webscout/Provider/MCPCore.py +315 -0
  93. webscout/Provider/Marcus.py +198 -0
  94. webscout/Provider/Nemotron.py +218 -0
  95. webscout/Provider/Netwrck.py +270 -0
  96. webscout/Provider/OLLAMA.py +396 -0
  97. webscout/Provider/OPENAI/BLACKBOXAI.py +766 -0
  98. webscout/Provider/OPENAI/Cloudflare.py +378 -0
  99. webscout/Provider/OPENAI/FreeGemini.py +283 -0
  100. webscout/Provider/OPENAI/NEMOTRON.py +232 -0
  101. webscout/Provider/OPENAI/Qwen3.py +283 -0
  102. webscout/Provider/OPENAI/README.md +952 -0
  103. webscout/Provider/OPENAI/TwoAI.py +357 -0
  104. webscout/Provider/OPENAI/__init__.py +40 -0
  105. webscout/Provider/OPENAI/ai4chat.py +293 -0
  106. webscout/Provider/OPENAI/api.py +969 -0
  107. webscout/Provider/OPENAI/base.py +249 -0
  108. webscout/Provider/OPENAI/c4ai.py +373 -0
  109. webscout/Provider/OPENAI/chatgpt.py +556 -0
  110. webscout/Provider/OPENAI/chatgptclone.py +494 -0
  111. webscout/Provider/OPENAI/chatsandbox.py +173 -0
  112. webscout/Provider/OPENAI/copilot.py +242 -0
  113. webscout/Provider/OPENAI/deepinfra.py +322 -0
  114. webscout/Provider/OPENAI/e2b.py +1414 -0
  115. webscout/Provider/OPENAI/exaai.py +417 -0
  116. webscout/Provider/OPENAI/exachat.py +444 -0
  117. webscout/Provider/OPENAI/flowith.py +162 -0
  118. webscout/Provider/OPENAI/freeaichat.py +359 -0
  119. webscout/Provider/OPENAI/glider.py +326 -0
  120. webscout/Provider/OPENAI/groq.py +364 -0
  121. webscout/Provider/OPENAI/heckai.py +308 -0
  122. webscout/Provider/OPENAI/llmchatco.py +335 -0
  123. webscout/Provider/OPENAI/mcpcore.py +389 -0
  124. webscout/Provider/OPENAI/multichat.py +376 -0
  125. webscout/Provider/OPENAI/netwrck.py +357 -0
  126. webscout/Provider/OPENAI/oivscode.py +287 -0
  127. webscout/Provider/OPENAI/opkfc.py +496 -0
  128. webscout/Provider/OPENAI/pydantic_imports.py +172 -0
  129. webscout/Provider/OPENAI/scirachat.py +477 -0
  130. webscout/Provider/OPENAI/sonus.py +304 -0
  131. webscout/Provider/OPENAI/standardinput.py +433 -0
  132. webscout/Provider/OPENAI/textpollinations.py +339 -0
  133. webscout/Provider/OPENAI/toolbaz.py +413 -0
  134. webscout/Provider/OPENAI/typefully.py +355 -0
  135. webscout/Provider/OPENAI/typegpt.py +364 -0
  136. webscout/Provider/OPENAI/uncovrAI.py +463 -0
  137. webscout/Provider/OPENAI/utils.py +318 -0
  138. webscout/Provider/OPENAI/venice.py +431 -0
  139. webscout/Provider/OPENAI/wisecat.py +387 -0
  140. webscout/Provider/OPENAI/writecream.py +163 -0
  141. webscout/Provider/OPENAI/x0gpt.py +365 -0
  142. webscout/Provider/OPENAI/yep.py +382 -0
  143. webscout/Provider/OpenGPT.py +209 -0
  144. webscout/Provider/Openai.py +496 -0
  145. webscout/Provider/PI.py +429 -0
  146. webscout/Provider/Perplexitylabs.py +415 -0
  147. webscout/Provider/QwenLM.py +254 -0
  148. webscout/Provider/Reka.py +214 -0
  149. webscout/Provider/StandardInput.py +290 -0
  150. webscout/Provider/TTI/README.md +82 -0
  151. webscout/Provider/TTI/__init__.py +7 -0
  152. webscout/Provider/TTI/aiarta.py +365 -0
  153. webscout/Provider/TTI/artbit.py +0 -0
  154. webscout/Provider/TTI/base.py +64 -0
  155. webscout/Provider/TTI/fastflux.py +200 -0
  156. webscout/Provider/TTI/magicstudio.py +201 -0
  157. webscout/Provider/TTI/piclumen.py +203 -0
  158. webscout/Provider/TTI/pixelmuse.py +225 -0
  159. webscout/Provider/TTI/pollinations.py +221 -0
  160. webscout/Provider/TTI/utils.py +11 -0
  161. webscout/Provider/TTS/README.md +192 -0
  162. webscout/Provider/TTS/__init__.py +10 -0
  163. webscout/Provider/TTS/base.py +159 -0
  164. webscout/Provider/TTS/deepgram.py +156 -0
  165. webscout/Provider/TTS/elevenlabs.py +111 -0
  166. webscout/Provider/TTS/gesserit.py +128 -0
  167. webscout/Provider/TTS/murfai.py +113 -0
  168. webscout/Provider/TTS/openai_fm.py +129 -0
  169. webscout/Provider/TTS/parler.py +111 -0
  170. webscout/Provider/TTS/speechma.py +580 -0
  171. webscout/Provider/TTS/sthir.py +94 -0
  172. webscout/Provider/TTS/streamElements.py +333 -0
  173. webscout/Provider/TTS/utils.py +280 -0
  174. webscout/Provider/TeachAnything.py +229 -0
  175. webscout/Provider/TextPollinationsAI.py +308 -0
  176. webscout/Provider/TwoAI.py +475 -0
  177. webscout/Provider/TypliAI.py +305 -0
  178. webscout/Provider/UNFINISHED/ChatHub.py +209 -0
  179. webscout/Provider/UNFINISHED/Youchat.py +330 -0
  180. webscout/Provider/UNFINISHED/liner_api_request.py +263 -0
  181. webscout/Provider/UNFINISHED/puterjs.py +635 -0
  182. webscout/Provider/UNFINISHED/test_lmarena.py +119 -0
  183. webscout/Provider/Venice.py +258 -0
  184. webscout/Provider/VercelAI.py +253 -0
  185. webscout/Provider/WiseCat.py +233 -0
  186. webscout/Provider/WrDoChat.py +370 -0
  187. webscout/Provider/Writecream.py +246 -0
  188. webscout/Provider/WritingMate.py +269 -0
  189. webscout/Provider/__init__.py +174 -0
  190. webscout/Provider/ai4chat.py +174 -0
  191. webscout/Provider/akashgpt.py +335 -0
  192. webscout/Provider/asksteve.py +220 -0
  193. webscout/Provider/cerebras.py +290 -0
  194. webscout/Provider/chatglm.py +215 -0
  195. webscout/Provider/cleeai.py +213 -0
  196. webscout/Provider/copilot.py +425 -0
  197. webscout/Provider/elmo.py +283 -0
  198. webscout/Provider/freeaichat.py +285 -0
  199. webscout/Provider/geminiapi.py +208 -0
  200. webscout/Provider/granite.py +235 -0
  201. webscout/Provider/hermes.py +266 -0
  202. webscout/Provider/julius.py +223 -0
  203. webscout/Provider/koala.py +170 -0
  204. webscout/Provider/learnfastai.py +325 -0
  205. webscout/Provider/llama3mitril.py +215 -0
  206. webscout/Provider/llmchat.py +258 -0
  207. webscout/Provider/llmchatco.py +306 -0
  208. webscout/Provider/lmarena.py +198 -0
  209. webscout/Provider/meta.py +801 -0
  210. webscout/Provider/multichat.py +364 -0
  211. webscout/Provider/oivscode.py +309 -0
  212. webscout/Provider/samurai.py +224 -0
  213. webscout/Provider/scira_chat.py +299 -0
  214. webscout/Provider/scnet.py +243 -0
  215. webscout/Provider/searchchat.py +292 -0
  216. webscout/Provider/sonus.py +258 -0
  217. webscout/Provider/talkai.py +194 -0
  218. webscout/Provider/toolbaz.py +353 -0
  219. webscout/Provider/turboseek.py +266 -0
  220. webscout/Provider/typefully.py +202 -0
  221. webscout/Provider/typegpt.py +289 -0
  222. webscout/Provider/uncovr.py +368 -0
  223. webscout/Provider/x0gpt.py +299 -0
  224. webscout/Provider/yep.py +389 -0
  225. webscout/__init__.py +4 -2
  226. webscout/cli.py +3 -28
  227. webscout/client.py +70 -0
  228. webscout/conversation.py +35 -35
  229. webscout/litagent/Readme.md +276 -0
  230. webscout/litagent/__init__.py +29 -0
  231. webscout/litagent/agent.py +455 -0
  232. webscout/litagent/constants.py +60 -0
  233. webscout/litprinter/__init__.py +59 -0
  234. webscout/optimizers.py +419 -419
  235. webscout/scout/README.md +404 -0
  236. webscout/scout/__init__.py +8 -0
  237. webscout/scout/core/__init__.py +7 -0
  238. webscout/scout/core/crawler.py +210 -0
  239. webscout/scout/core/scout.py +607 -0
  240. webscout/scout/core/search_result.py +96 -0
  241. webscout/scout/core/text_analyzer.py +63 -0
  242. webscout/scout/core/text_utils.py +277 -0
  243. webscout/scout/core/web_analyzer.py +52 -0
  244. webscout/scout/element.py +478 -0
  245. webscout/scout/parsers/__init__.py +69 -0
  246. webscout/scout/parsers/html5lib_parser.py +172 -0
  247. webscout/scout/parsers/html_parser.py +236 -0
  248. webscout/scout/parsers/lxml_parser.py +178 -0
  249. webscout/scout/utils.py +37 -0
  250. webscout/swiftcli/Readme.md +323 -0
  251. webscout/swiftcli/__init__.py +95 -0
  252. webscout/swiftcli/core/__init__.py +7 -0
  253. webscout/swiftcli/core/cli.py +297 -0
  254. webscout/swiftcli/core/context.py +104 -0
  255. webscout/swiftcli/core/group.py +241 -0
  256. webscout/swiftcli/decorators/__init__.py +28 -0
  257. webscout/swiftcli/decorators/command.py +221 -0
  258. webscout/swiftcli/decorators/options.py +220 -0
  259. webscout/swiftcli/decorators/output.py +252 -0
  260. webscout/swiftcli/exceptions.py +21 -0
  261. webscout/swiftcli/plugins/__init__.py +9 -0
  262. webscout/swiftcli/plugins/base.py +135 -0
  263. webscout/swiftcli/plugins/manager.py +269 -0
  264. webscout/swiftcli/utils/__init__.py +59 -0
  265. webscout/swiftcli/utils/formatting.py +252 -0
  266. webscout/swiftcli/utils/parsing.py +267 -0
  267. webscout/version.py +1 -1
  268. webscout/webscout_search.py +2 -182
  269. webscout/webscout_search_async.py +1 -179
  270. webscout/zeroart/README.md +89 -0
  271. webscout/zeroart/__init__.py +135 -0
  272. webscout/zeroart/base.py +66 -0
  273. webscout/zeroart/effects.py +101 -0
  274. webscout/zeroart/fonts.py +1239 -0
  275. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/METADATA +262 -83
  276. webscout-8.2.9.dist-info/RECORD +289 -0
  277. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/WHEEL +1 -1
  278. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/entry_points.txt +1 -0
  279. webscout-8.2.7.dist-info/RECORD +0 -26
  280. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/licenses/LICENSE.md +0 -0
  281. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,258 @@
1
+ from curl_cffi.requests import Session
2
+ from curl_cffi import CurlError
3
+ import json
4
+ from typing import Any, Dict, Optional, Generator, Union
5
+ from webscout.AIutel import Optimizers
6
+ from webscout.AIutel import Conversation
7
+ from webscout.AIutel import AwesomePrompts, sanitize_stream # Import sanitize_stream
8
+ from webscout.AIbase import Provider
9
+ from webscout import exceptions
10
+ from webscout.litagent import LitAgent
11
+ class SonusAI(Provider):
12
+ """
13
+ A class to interact with the Sonus AI chat API.
14
+ """
15
+
16
+ AVAILABLE_MODELS = [
17
+ "pro",
18
+ "air",
19
+ "mini"
20
+ ]
21
+
22
+ def __init__(
23
+ self,
24
+ is_conversation: bool = True,
25
+ max_tokens: int = 2049, # Note: max_tokens is not directly used by this API
26
+ timeout: int = 30,
27
+ intro: str = None,
28
+ filepath: str = None,
29
+ update_file: bool = True,
30
+ proxies: dict = {},
31
+ history_offset: int = 10250,
32
+ act: str = None,
33
+ model: str = "pro"
34
+ ):
35
+ """Initializes the Sonus AI API client."""
36
+ if model not in self.AVAILABLE_MODELS:
37
+ raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
38
+
39
+ self.url = "https://chat.sonus.ai/chat.php"
40
+
41
+ # Headers for the request
42
+ self.headers = {
43
+ 'Accept': '*/*',
44
+ 'Accept-Language': 'en-US,en;q=0.9',
45
+ 'Origin': 'https://chat.sonus.ai',
46
+ 'Referer': 'https://chat.sonus.ai/',
47
+ 'User-Agent': LitAgent().random()
48
+ # Add sec-ch-ua headers if needed for impersonation consistency
49
+ }
50
+
51
+ # Initialize curl_cffi Session
52
+ self.session = Session()
53
+ # Update curl_cffi session headers and proxies
54
+ self.session.headers.update(self.headers)
55
+ self.session.proxies = proxies # Assign proxies directly
56
+
57
+ self.is_conversation = is_conversation
58
+ self.max_tokens_to_sample = max_tokens
59
+ self.timeout = timeout
60
+ self.last_response = {}
61
+ self.model = model
62
+
63
+ self.__available_optimizers = (
64
+ method
65
+ for method in dir(Optimizers)
66
+ if callable(getattr(Optimizers, method)) and not method.startswith("__")
67
+ )
68
+ Conversation.intro = (
69
+ AwesomePrompts().get_act(
70
+ act, raise_not_found=True, default=None, case_insensitive=True
71
+ )
72
+ if act
73
+ else intro or Conversation.intro
74
+ )
75
+
76
+ self.conversation = Conversation(
77
+ is_conversation, self.max_tokens_to_sample, filepath, update_file
78
+ )
79
+ self.conversation.history_offset = history_offset
80
+
81
+ @staticmethod
82
+ def _sonus_extractor(chunk: Union[str, Dict[str, Any]]) -> Optional[str]:
83
+ """Extracts content from Sonus stream JSON objects."""
84
+ if isinstance(chunk, dict) and "content" in chunk:
85
+ return chunk.get("content")
86
+ return None
87
+
88
+ def ask(
89
+ self,
90
+ prompt: str,
91
+ stream: bool = False,
92
+ raw: bool = False,
93
+ optimizer: str = None,
94
+ conversationally: bool = False,
95
+ reasoning: bool = False,
96
+ ) -> Union[Dict[str, Any], Generator]:
97
+ conversation_prompt = self.conversation.gen_complete_prompt(prompt)
98
+ if optimizer:
99
+ if optimizer in self.__available_optimizers:
100
+ conversation_prompt = getattr(Optimizers, optimizer)(
101
+ conversation_prompt if conversationally else prompt
102
+ )
103
+ else:
104
+ raise Exception(f"Optimizer is not one of {self.__available_optimizers}")
105
+
106
+ # Prepare the multipart form data (curl_cffi handles tuples for files/data)
107
+ # No need for explicit (None, ...) for simple fields when using `data=`
108
+ form_data = {
109
+ 'message': conversation_prompt,
110
+ 'history': "", # Explicitly empty string if needed, or omit if None is acceptable
111
+ 'reasoning': str(reasoning).lower(),
112
+ 'model': self.model
113
+ }
114
+ # Note: curl_cffi's `files` parameter is for actual file uploads.
115
+ # For simple key-value pairs like this, `data` is usually sufficient for multipart/form-data.
116
+ # If the server strictly requires `files`, keep the original structure but it might not work as expected with curl_cffi without actual file objects.
117
+
118
+ def for_stream():
119
+ try:
120
+ # Use curl_cffi session post with impersonate
121
+ # Use `data` instead of `files` for simple key-value multipart
122
+ response = self.session.post(
123
+ self.url,
124
+ # headers are set on the session
125
+ data=form_data, # Use data for multipart form fields
126
+ stream=True,
127
+ timeout=self.timeout,
128
+ impersonate="chrome110" # Use a common impersonation profile
129
+ )
130
+ if response.status_code != 200:
131
+ raise exceptions.FailedToGenerateResponseError(
132
+ f"Request failed with status code {response.status_code} - {response.text}"
133
+ )
134
+
135
+ streaming_text = ""
136
+ # Use sanitize_stream
137
+ processed_stream = sanitize_stream(
138
+ data=response.iter_content(chunk_size=None), # Pass byte iterator
139
+ intro_value="data:",
140
+ to_json=True, # Stream sends JSON
141
+ content_extractor=self._sonus_extractor, # Use the specific extractor
142
+ yield_raw_on_error=False # Skip non-JSON lines or lines where extractor fails
143
+ )
144
+
145
+ for content_chunk in processed_stream:
146
+ # content_chunk is the string extracted by _sonus_extractor
147
+ if content_chunk and isinstance(content_chunk, str):
148
+ streaming_text += content_chunk
149
+ yield dict(text=content_chunk) if not raw else content_chunk
150
+
151
+ # Update history and last response after stream finishes
152
+ self.last_response = {"text": streaming_text}
153
+ self.conversation.update_chat_history(prompt, streaming_text)
154
+
155
+ except CurlError as e: # Catch CurlError
156
+ raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {str(e)}") from e
157
+ except Exception as e: # Catch other potential exceptions
158
+ raise exceptions.FailedToGenerateResponseError(f"An unexpected error occurred ({type(e).__name__}): {e}") from e
159
+
160
+ def for_non_stream():
161
+ try:
162
+ # Use curl_cffi session post with impersonate
163
+ response = self.session.post(
164
+ self.url,
165
+ # headers are set on the session
166
+ data=form_data, # Use data for multipart form fields
167
+ timeout=self.timeout,
168
+ impersonate="chrome110" # Use a common impersonation profile
169
+ )
170
+ if response.status_code != 200:
171
+ raise exceptions.FailedToGenerateResponseError(
172
+ f"Request failed with status code {response.status_code} - {response.text}"
173
+ )
174
+
175
+ response_text_raw = response.text # Get raw text
176
+
177
+ # Use sanitize_stream to process the non-streaming text
178
+ processed_stream = sanitize_stream(
179
+ data=response_text_raw.splitlines(), # Split into lines
180
+ intro_value="data:",
181
+ to_json=True,
182
+ content_extractor=self._sonus_extractor,
183
+ yield_raw_on_error=False
184
+ )
185
+
186
+ # Aggregate the results
187
+ full_response = ""
188
+ for content in processed_stream:
189
+ if content and isinstance(content, str):
190
+ full_response += content
191
+
192
+ self.last_response = {"text": full_response}
193
+ self.conversation.update_chat_history(prompt, full_response)
194
+ # Return dict or raw string
195
+ return full_response if raw else {"text": full_response}
196
+
197
+ except CurlError as e: # Catch CurlError
198
+ raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {str(e)}") from e
199
+ except Exception as e: # Catch other potential exceptions
200
+ raise exceptions.FailedToGenerateResponseError(f"An unexpected error occurred ({type(e).__name__}): {e}") from e
201
+
202
+ return for_stream() if stream else for_non_stream()
203
+
204
+ def chat(
205
+ self,
206
+ prompt: str,
207
+ stream: bool = False,
208
+ optimizer: str = None,
209
+ conversationally: bool = False,
210
+ reasoning: bool = False,
211
+ ) -> Union[str, Generator[str, None, None]]:
212
+ def for_stream_chat():
213
+ # ask() yields dicts when raw=False
214
+ for response_dict in self.ask(
215
+ prompt, stream=True, raw=False, # Ensure ask yields dicts
216
+ optimizer=optimizer, conversationally=conversationally, reasoning=reasoning
217
+ ):
218
+ yield self.get_message(response_dict)
219
+
220
+ def for_non_stream_chat():
221
+ # ask() returns dict or str when raw=False/True
222
+ response_data = self.ask(
223
+ prompt, stream=False, raw=False, # Ensure ask returns dict
224
+ optimizer=optimizer, conversationally=conversationally, reasoning=reasoning
225
+ )
226
+ return self.get_message(response_data) # get_message expects dict
227
+
228
+ return for_stream_chat() if stream else for_non_stream_chat()
229
+
230
+ def get_message(self, response: dict) -> str:
231
+ assert isinstance(response, dict), "Response should be of dict data-type only"
232
+ return response["text"]
233
+
234
+ if __name__ == "__main__":
235
+ # Ensure curl_cffi is installed
236
+ print("-" * 80)
237
+ print(f"{'Model':<50} {'Status':<10} {'Response'}")
238
+ print("-" * 80)
239
+
240
+ for model in SonusAI.AVAILABLE_MODELS:
241
+ try:
242
+ test_ai = SonusAI(model=model, timeout=60)
243
+ response = test_ai.chat("Say 'Hello' in one word", stream=True)
244
+ response_text = ""
245
+ for chunk in response:
246
+ response_text += chunk
247
+
248
+ if response_text and len(response_text.strip()) > 0:
249
+ status = "✓"
250
+ # Clean and truncate response
251
+ clean_text = response_text.strip().encode('utf-8', errors='ignore').decode('utf-8')
252
+ display_text = clean_text[:50] + "..." if len(clean_text) > 50 else clean_text
253
+ else:
254
+ status = "✗"
255
+ display_text = "Empty or invalid response"
256
+ print(f"\r{model:<50} {status:<10} {display_text}")
257
+ except Exception as e:
258
+ print(f"\r{model:<50} {'✗':<10} {str(e)}")
@@ -0,0 +1,194 @@
1
+ import uuid
2
+ import cloudscraper
3
+ import json
4
+ from typing import Union, Any, Dict, Optional, Generator
5
+
6
+ from webscout.AIutel import Optimizers
7
+ from webscout.AIutel import Conversation
8
+ from webscout.AIutel import AwesomePrompts
9
+ from webscout.AIbase import Provider
10
+ from webscout import exceptions
11
+ from webscout.litagent import LitAgent
12
+
13
+ class Talkai(Provider):
14
+ """
15
+ A class to interact with the Talkai.info API.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ is_conversation: bool = True,
21
+ max_tokens: int = 2048,
22
+ timeout: int = 30,
23
+ intro: str = None,
24
+ filepath: str = None,
25
+ update_file: bool = True,
26
+ proxies: dict = {},
27
+ history_offset: int = 10250,
28
+ act: str = None,
29
+ model: str = "gpt-4o-mini", # Default model
30
+ ):
31
+ """
32
+ Initializes the Talkai.info API with given parameters.
33
+ """
34
+ self.session = cloudscraper.create_scraper()
35
+ self.is_conversation = is_conversation
36
+ self.max_tokens_to_sample = max_tokens
37
+ self.api_endpoint = "https://talkai.info/chat/send/"
38
+ self.timeout = timeout
39
+ self.last_response = {}
40
+ self.model = model
41
+ self.headers = {
42
+ 'Accept': 'application/json, text/event-stream',
43
+ 'Accept-Language': 'en-US,en;q=0.9,en-IN;q=0.8',
44
+ 'Content-Type': 'application/json',
45
+ 'Origin': 'https://talkai.info',
46
+ 'Referer': 'https://talkai.info/chat/',
47
+ 'User-Agent': LitAgent().random(),
48
+ 'Cookie': '_csrf-front=e19e203a958c74e439261f6860535403324c9ab2ede76449e6407e54e1f366afa%3A2%3A%7Bi%3A0%3Bs%3A11%3A%22_csrf-front%22%3Bi%3A1%3Bs%3A32%3A%22QbnGY7XS5q9i3JnDvi6KRzrOk0D6XFnk%22%3B%7D; _ga=GA1.1.1383924142.1734246140; _ym_uid=1723397035198647017; _ym_d=1734246141; _ym_isad=1; _ym_visorc=b; talkai-front=ngbj23of1t0ujg2raoa3l57vqe; _ga_FB7V9WMN30=GS1.1.1734246139.1.1734246143.0.0.0'
49
+ }
50
+ self.__available_optimizers = (
51
+ method
52
+ for method in dir(Optimizers)
53
+ if callable(getattr(Optimizers, method)) and not method.startswith("__")
54
+ )
55
+ Conversation.intro = (
56
+ AwesomePrompts().get_act(
57
+ act, raise_not_found=True, default=None, case_insensitive=True
58
+ )
59
+ if act
60
+ else intro or Conversation.intro
61
+ )
62
+ self.conversation = Conversation(
63
+ is_conversation, self.max_tokens_to_sample, filepath, update_file
64
+ )
65
+ self.conversation.history_offset = history_offset
66
+ self.session.proxies = proxies
67
+
68
+ def ask(
69
+ self,
70
+ prompt: str,
71
+ stream: bool = False,
72
+ raw: bool = False,
73
+ optimizer: str = None,
74
+ conversationally: bool = False,
75
+ ) -> Dict[str, Any]:
76
+ """Chat with Talkai
77
+
78
+ Args:
79
+ prompt (str): Prompt to be sent.
80
+ stream (bool, optional): Flag for streaming response. Defaults to False.
81
+ raw (bool, optional): Stream back raw response as received. Defaults to False.
82
+ optimizer (str, optional): Prompt optimizer name - `[code, shell_command]`. Defaults to None.
83
+ conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
84
+ Returns:
85
+ dict: Response dictionary.
86
+ """
87
+ conversation_prompt = self.conversation.gen_complete_prompt(prompt)
88
+ if optimizer:
89
+ if optimizer in self.__available_optimizers:
90
+ conversation_prompt = getattr(Optimizers, optimizer)(conversation_prompt if conversationally else prompt)
91
+ else:
92
+ raise exceptions.FailedToGenerateResponseError(
93
+ f"Optimizer is not one of {self.__available_optimizers}"
94
+ )
95
+
96
+ payload = {
97
+ "type": "chat",
98
+ "messagesHistory": [
99
+ {
100
+ "id": str(uuid.uuid4()),
101
+ "from": "you",
102
+ "content": conversation_prompt
103
+ }
104
+ ],
105
+ "settings": {
106
+ "model": self.model
107
+ }
108
+ }
109
+
110
+ def for_stream():
111
+ try:
112
+ with self.session.post(self.api_endpoint, headers=self.headers, json=payload, stream=True, timeout=self.timeout) as response:
113
+ response.raise_for_status()
114
+
115
+ full_response = ""
116
+ for line in response.iter_lines():
117
+ if line:
118
+ decoded_line = line.decode('utf-8')
119
+ if 'event: trylimit' in decoded_line:
120
+ break # Stop if trylimit event is encountered
121
+ if decoded_line.startswith('data:'):
122
+ data = decoded_line[6:] # Remove 'data: ' prefix
123
+ full_response += data
124
+ yield data if raw else dict(text=data)
125
+
126
+ self.last_response.update(dict(text=full_response))
127
+ self.conversation.update_chat_history(
128
+ prompt, self.get_message(self.last_response)
129
+ )
130
+
131
+ except cloudscraper.exceptions as e:
132
+ raise exceptions.FailedToGenerateResponseError(f"Request failed: {e}")
133
+
134
+ def for_non_stream():
135
+ full_response = ""
136
+ for line in for_stream():
137
+ full_response += line['text'] if not raw else line
138
+ return dict(text=full_response)
139
+
140
+ return for_stream() if stream else for_non_stream()
141
+
142
+
143
+ def chat(
144
+ self,
145
+ prompt: str,
146
+ stream: bool = False,
147
+ optimizer: str = None,
148
+ conversationally: bool = False,
149
+ ) -> Union[str, Generator[str, None, None]]:
150
+ """Generate response `str`
151
+ Args:
152
+ prompt (str): Prompt to be send.
153
+ stream (bool, optional): Flag for streaming response. Defaults to False.
154
+ optimizer (str, optional): Prompt optimizer name - `[code, shell_command]`. Defaults to None.
155
+ conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
156
+ Returns:
157
+ str: Response generated
158
+ """
159
+
160
+ def for_stream():
161
+ for response in self.ask(
162
+ prompt, True, optimizer=optimizer, conversationally=conversationally
163
+ ):
164
+ yield self.get_message(response)
165
+
166
+ def for_non_stream():
167
+ return self.get_message(
168
+ self.ask(
169
+ prompt,
170
+ False,
171
+ optimizer=optimizer,
172
+ conversationally=conversationally,
173
+ )
174
+ )
175
+
176
+ return for_stream() if stream else for_non_stream()
177
+
178
+ def get_message(self, response: Dict[str, Any]) -> str:
179
+ """Retrieves message only from response.
180
+
181
+ Args:
182
+ response (dict): Response generated by `self.ask`
183
+
184
+ Returns:
185
+ str: Message extracted.
186
+ """
187
+ assert isinstance(response, dict), "Response should be of dict data-type only"
188
+ return response["text"].replace('\\n', '\n').replace('\\n\\n', '\n\n')
189
+
190
+ if __name__ == "__main__":
191
+ t = Talkai()
192
+ resp = t.chat("write me about AI", stream=True)
193
+ for chunk in resp:
194
+ print(chunk, end="", flush=True)