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,304 @@
1
+ import time
2
+ import uuid
3
+ import requests
4
+ import json
5
+ from typing import List, Dict, Optional, Union, Generator, Any
6
+
7
+ from webscout.litagent import LitAgent
8
+ from .base import BaseChat, BaseCompletions, OpenAICompatibleProvider
9
+ from .utils import (
10
+ ChatCompletion,
11
+ ChatCompletionChunk,
12
+ Choice,
13
+ ChatCompletionMessage,
14
+ ChoiceDelta,
15
+ CompletionUsage,
16
+ format_prompt,
17
+ count_tokens
18
+ )
19
+
20
+ # ANSI escape codes for formatting
21
+ BOLD = "\033[1m"
22
+ RED = "\033[91m"
23
+ RESET = "\033[0m"
24
+
25
+ class Completions(BaseCompletions):
26
+ def __init__(self, client: 'SonusAI'):
27
+ self._client = client
28
+
29
+ def create(
30
+ self,
31
+ *,
32
+ model: str,
33
+ messages: List[Dict[str, str]],
34
+ max_tokens: Optional[int] = None, # Not used by SonusAI but kept for compatibility
35
+ stream: bool = False,
36
+ temperature: Optional[float] = None, # Not used by SonusAI but kept for compatibility
37
+ top_p: Optional[float] = None, # Not used by SonusAI but kept for compatibility
38
+ **kwargs: Any # Not used by SonusAI but kept for compatibility
39
+ ) -> Union[ChatCompletion, Generator[ChatCompletionChunk, None, None]]:
40
+ """
41
+ Creates a model response for the given chat conversation.
42
+ Mimics openai.chat.completions.create
43
+ """
44
+ # Format the messages using the format_prompt utility
45
+ # This creates a conversation in the format: "User: message\nAssistant: response\nUser: message\nAssistant:"
46
+ # SonusAI works better with a properly formatted conversation
47
+ question = format_prompt(messages, add_special_tokens=True, do_continue=True)
48
+
49
+ # Extract reasoning parameter if provided
50
+ reasoning = kwargs.get('reasoning', False)
51
+
52
+ # Prepare the multipart form data for SonusAI API
53
+ files = {
54
+ 'message': (None, question),
55
+ 'history': (None),
56
+ 'reasoning': (None, str(reasoning).lower()),
57
+ 'model': (None, self._client.convert_model_name(model))
58
+ }
59
+
60
+ request_id = f"chatcmpl-{uuid.uuid4()}"
61
+ created_time = int(time.time())
62
+
63
+ if stream:
64
+ return self._create_stream(request_id, created_time, model, files)
65
+ else:
66
+ return self._create_non_stream(request_id, created_time, model, files)
67
+
68
+ def _create_stream(
69
+ self, request_id: str, created_time: int, model: str, files: Dict[str, Any]
70
+ ) -> Generator[ChatCompletionChunk, None, None]:
71
+ try:
72
+ response = requests.post(
73
+ self._client.url,
74
+ files=files,
75
+ headers=self._client.headers,
76
+ stream=True,
77
+ timeout=self._client.timeout
78
+ )
79
+ response.raise_for_status()
80
+
81
+ # Track token usage across chunks
82
+ completion_tokens = 0
83
+ streaming_text = ""
84
+
85
+ for line in response.iter_lines():
86
+ if not line:
87
+ continue
88
+
89
+ try:
90
+ # Decode the line and remove 'data: ' prefix if present
91
+ line_text = line.decode('utf-8')
92
+ if line_text.startswith('data: '):
93
+ line_text = line_text[6:]
94
+
95
+ data = json.loads(line_text)
96
+ if "content" in data:
97
+ content = data["content"]
98
+ streaming_text += content
99
+ completion_tokens += count_tokens(content)
100
+
101
+ # Create a delta object for this chunk
102
+ delta = ChoiceDelta(content=content)
103
+ choice = Choice(index=0, delta=delta, finish_reason=None)
104
+
105
+ chunk = ChatCompletionChunk(
106
+ id=request_id,
107
+ choices=[choice],
108
+ created=created_time,
109
+ model=model,
110
+ )
111
+
112
+ yield chunk
113
+ except (json.JSONDecodeError, UnicodeDecodeError):
114
+ continue
115
+
116
+ # Final chunk with finish_reason
117
+ delta = ChoiceDelta(content=None)
118
+ choice = Choice(index=0, delta=delta, finish_reason="stop")
119
+
120
+ chunk = ChatCompletionChunk(
121
+ id=request_id,
122
+ choices=[choice],
123
+ created=created_time,
124
+ model=model,
125
+ )
126
+
127
+ yield chunk
128
+
129
+ except requests.exceptions.RequestException as e:
130
+ print(f"{RED}Error during SonusAI stream request: {e}{RESET}")
131
+ raise IOError(f"SonusAI request failed: {e}") from e
132
+
133
+ def _create_non_stream(
134
+ self, request_id: str, created_time: int, model: str, files: Dict[str, Any]
135
+ ) -> ChatCompletion:
136
+ try:
137
+ response = requests.post(
138
+ self._client.url,
139
+ files=files,
140
+ headers=self._client.headers,
141
+ timeout=self._client.timeout
142
+ )
143
+ response.raise_for_status()
144
+
145
+ full_response = ""
146
+ for line in response.iter_lines():
147
+ if line:
148
+ try:
149
+ line_text = line.decode('utf-8')
150
+ if line_text.startswith('data: '):
151
+ line_text = line_text[6:]
152
+ data = json.loads(line_text)
153
+ if "content" in data:
154
+ full_response += data["content"]
155
+ except (json.JSONDecodeError, UnicodeDecodeError):
156
+ continue
157
+
158
+ # Create usage statistics using count_tokens
159
+ prompt_tokens = count_tokens(files.get('message', ['',''])[1])
160
+ completion_tokens = count_tokens(full_response)
161
+ total_tokens = prompt_tokens + completion_tokens
162
+
163
+ usage = CompletionUsage(
164
+ prompt_tokens=prompt_tokens,
165
+ completion_tokens=completion_tokens,
166
+ total_tokens=total_tokens
167
+ )
168
+
169
+ # Create the message object
170
+ message = ChatCompletionMessage(
171
+ role="assistant",
172
+ content=full_response
173
+ )
174
+
175
+ # Create the choice object
176
+ choice = Choice(
177
+ index=0,
178
+ message=message,
179
+ finish_reason="stop"
180
+ )
181
+
182
+ # Create the completion object
183
+ completion = ChatCompletion(
184
+ id=request_id,
185
+ choices=[choice],
186
+ created=created_time,
187
+ model=model,
188
+ usage=usage,
189
+ )
190
+
191
+ return completion
192
+
193
+ except Exception as e:
194
+ print(f"{RED}Error during SonusAI non-stream request: {e}{RESET}")
195
+ raise IOError(f"SonusAI request failed: {e}") from e
196
+
197
+ class Chat(BaseChat):
198
+ def __init__(self, client: 'SonusAI'):
199
+ self.completions = Completions(client)
200
+
201
+ class SonusAI(OpenAICompatibleProvider):
202
+ """
203
+ OpenAI-compatible client for Sonus AI API.
204
+
205
+ Usage:
206
+ client = SonusAI()
207
+ response = client.chat.completions.create(
208
+ model="pro",
209
+ messages=[{"role": "user", "content": "Hello!"}]
210
+ )
211
+ print(response.choices[0].message.content)
212
+ """
213
+
214
+ AVAILABLE_MODELS = [
215
+ "pro",
216
+ "air",
217
+ "mini"
218
+ ]
219
+
220
+ def __init__(
221
+ self,
222
+ timeout: int = 30
223
+ ):
224
+ """
225
+ Initialize the SonusAI client.
226
+
227
+ Args:
228
+ timeout: Request timeout in seconds.
229
+ """
230
+ self.timeout = timeout
231
+ self.url = "https://chat.sonus.ai/chat.php"
232
+
233
+ # Headers for the request
234
+ agent = LitAgent()
235
+ self.headers = {
236
+ 'Accept': '*/*',
237
+ 'Accept-Language': 'en-US,en;q=0.9',
238
+ 'Origin': 'https://chat.sonus.ai',
239
+ 'Referer': 'https://chat.sonus.ai/',
240
+ 'User-Agent': agent.random()
241
+ }
242
+
243
+ self.session = requests.Session()
244
+ self.session.headers.update(self.headers)
245
+
246
+ # Initialize the chat interface
247
+ self.chat = Chat(self)
248
+
249
+ def convert_model_name(self, model: str) -> str:
250
+ """
251
+ Ensure the model name is in the correct format.
252
+ """
253
+ if model in self.AVAILABLE_MODELS:
254
+ return model
255
+
256
+ # Try to find a matching model
257
+ for available_model in self.AVAILABLE_MODELS:
258
+ if model.lower() in available_model.lower():
259
+ return available_model
260
+
261
+ # Default to pro if no match
262
+ print(f"{BOLD}Warning: Model '{model}' not found, using default model 'pro'{RESET}")
263
+ return "pro"
264
+
265
+ @property
266
+ def models(self):
267
+ class _ModelList:
268
+ def list(inner_self):
269
+ return type(self).AVAILABLE_MODELS
270
+ return _ModelList()
271
+
272
+
273
+
274
+
275
+ # Simple test if run directly
276
+ if __name__ == "__main__":
277
+ print("-" * 80)
278
+ print(f"{'Model':<50} {'Status':<10} {'Response'}")
279
+ print("-" * 80)
280
+
281
+ for model in SonusAI.AVAILABLE_MODELS:
282
+ try:
283
+ client = SonusAI(timeout=60)
284
+ # Test with a simple conversation to demonstrate format_prompt usage
285
+ response = client.chat.completions.create(
286
+ model=model,
287
+ messages=[
288
+ {"role": "system", "content": "You are a helpful assistant."},
289
+ {"role": "user", "content": "Say 'Hello' in one word"},
290
+ ],
291
+ stream=False
292
+ )
293
+
294
+ if response and response.choices and response.choices[0].message.content:
295
+ status = "✓"
296
+ # Truncate response if too long
297
+ display_text = response.choices[0].message.content.strip()
298
+ display_text = display_text[:50] + "..." if len(display_text) > 50 else display_text
299
+ else:
300
+ status = "✗"
301
+ display_text = "Empty or invalid response"
302
+ print(f"{model:<50} {status:<10} {display_text}")
303
+ except Exception as e:
304
+ print(f"{model:<50} {'✗':<10} {str(e)}")