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,355 @@
1
+ import time
2
+ import uuid
3
+ import json
4
+ import re
5
+ from typing import List, Dict, Optional, Union, Generator, Any
6
+
7
+ # Import base classes and utility structures
8
+ from .base import OpenAICompatibleProvider, BaseChat, BaseCompletions
9
+ from .utils import (
10
+ ChatCompletionChunk, ChatCompletion, Choice, ChoiceDelta,
11
+ ChatCompletionMessage, CompletionUsage,
12
+ format_prompt, get_system_prompt, count_tokens # Import format_prompt, get_system_prompt and count_tokens
13
+ )
14
+
15
+ # Import LitAgent for browser fingerprinting
16
+ from webscout.litagent import LitAgent
17
+
18
+ # Import curl_cffi for better request handling
19
+ from curl_cffi.requests import Session
20
+ from curl_cffi import CurlError
21
+
22
+ # ANSI escape codes for formatting
23
+ BOLD = "\033[1m"
24
+ RED = "\033[91m"
25
+ RESET = "\033[0m"
26
+
27
+ class Completions(BaseCompletions):
28
+ def __init__(self, client: 'TypefullyAI'):
29
+ self._client = client
30
+
31
+ def create(
32
+ self,
33
+ *,
34
+ model: str,
35
+ messages: List[Dict[str, str]],
36
+ max_tokens: Optional[int] = None,
37
+ stream: bool = False,
38
+ temperature: Optional[float] = None,
39
+ **kwargs: Any
40
+ ) -> Union[ChatCompletion, Generator[ChatCompletionChunk, None, None]]:
41
+ """
42
+ Creates a model response for the given chat conversation.
43
+ Mimics openai.chat.completions.create
44
+ """
45
+ # Extract system message using get_system_prompt utility
46
+ system_prompt = get_system_prompt(messages) or self._client.system_prompt
47
+
48
+ # Format the conversation using format_prompt utility
49
+ # Use add_special_tokens=True to format as "User: ... Assistant: ..."
50
+ # Use do_continue=True to ensure it ends with "Assistant: " for model to continue
51
+ conversation_prompt = format_prompt(
52
+ messages,
53
+ add_special_tokens=True,
54
+ do_continue=True,
55
+ include_system=False # System prompt is sent separately
56
+ )
57
+
58
+ # Prepare the payload for Typefully API
59
+ payload = {
60
+ "prompt": conversation_prompt,
61
+ "systemPrompt": system_prompt,
62
+ "modelIdentifier": self._client.convert_model_name(model),
63
+ "outputLength": max_tokens if max_tokens is not None else self._client.output_length
64
+ }
65
+
66
+ request_id = f"chatcmpl-{uuid.uuid4()}"
67
+ created_time = int(time.time())
68
+
69
+ if stream:
70
+ return self._create_streaming(request_id, created_time, model, payload)
71
+ else:
72
+ return self._create_non_streaming(request_id, created_time, model, payload)
73
+
74
+ def _create_streaming(
75
+ self,
76
+ request_id: str,
77
+ created_time: int,
78
+ model: str,
79
+ payload: Dict[str, Any]
80
+ ) -> Generator[ChatCompletionChunk, None, None]:
81
+ """Implementation for streaming chat completions."""
82
+ try:
83
+ # Make the streaming request
84
+ response = self._client.session.post(
85
+ self._client.api_endpoint,
86
+ headers=self._client.headers,
87
+ json=payload,
88
+ stream=True,
89
+ timeout=self._client.timeout,
90
+ impersonate="chrome120"
91
+ )
92
+
93
+ if not response.ok:
94
+ raise IOError(f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}")
95
+
96
+ streaming_text = ""
97
+
98
+ for chunk in response.iter_content(chunk_size=None):
99
+ if not chunk:
100
+ continue
101
+
102
+ chunk_str = chunk.decode('utf-8', errors='replace')
103
+ content = self._client._typefully_extractor(chunk_str)
104
+
105
+ if content:
106
+ streaming_text += content
107
+
108
+ # Create the delta object
109
+ delta = ChoiceDelta(
110
+ content=content,
111
+ role="assistant"
112
+ )
113
+
114
+ # Create the choice object
115
+ choice = Choice(
116
+ index=0,
117
+ delta=delta,
118
+ finish_reason=None
119
+ )
120
+
121
+ # Create the chunk object
122
+ chunk = ChatCompletionChunk(
123
+ id=request_id,
124
+ choices=[choice],
125
+ created=created_time,
126
+ model=model
127
+ )
128
+
129
+ yield chunk
130
+
131
+ # Final chunk with finish_reason="stop"
132
+ delta = ChoiceDelta(
133
+ content=None,
134
+ role=None
135
+ )
136
+
137
+ choice = Choice(
138
+ index=0,
139
+ delta=delta,
140
+ finish_reason="stop"
141
+ )
142
+
143
+ chunk = ChatCompletionChunk(
144
+ id=request_id,
145
+ choices=[choice],
146
+ created=created_time,
147
+ model=model
148
+ )
149
+
150
+ yield chunk
151
+
152
+ except CurlError as e:
153
+ print(f"{RED}Error during Typefully streaming request (CurlError): {e}{RESET}")
154
+ raise IOError(f"Typefully streaming request failed (CurlError): {e}") from e
155
+ except Exception as e:
156
+ print(f"{RED}Error during Typefully streaming request: {e}{RESET}")
157
+ raise IOError(f"Typefully streaming request failed: {e}") from e
158
+
159
+ def _create_non_streaming(
160
+ self,
161
+ request_id: str,
162
+ created_time: int,
163
+ model: str,
164
+ payload: Dict[str, Any]
165
+ ) -> ChatCompletion:
166
+ """Implementation for non-streaming chat completions."""
167
+ try:
168
+ # Make the non-streaming request
169
+ response = self._client.session.post(
170
+ self._client.api_endpoint,
171
+ headers=self._client.headers,
172
+ json=payload,
173
+ stream=True,
174
+ timeout=self._client.timeout,
175
+ impersonate="chrome120"
176
+ )
177
+
178
+ if not response.ok:
179
+ raise IOError(f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}")
180
+
181
+ # Collect the full response
182
+ full_text = ""
183
+ for chunk in response.iter_content(chunk_size=None):
184
+ if not chunk:
185
+ continue
186
+
187
+ chunk_str = chunk.decode('utf-8', errors='replace')
188
+ content = self._client._typefully_extractor(chunk_str)
189
+
190
+ if content:
191
+ full_text += content
192
+
193
+ # Format the text (replace escaped newlines)
194
+ full_text = full_text.replace('\\n', '\n').replace('\\n\\n', '\n\n')
195
+
196
+ # Estimate token counts
197
+ prompt_tokens = count_tokens(payload.get("prompt", "")) + count_tokens(payload.get("systemPrompt", ""))
198
+ completion_tokens = count_tokens(full_text)
199
+ total_tokens = prompt_tokens + completion_tokens
200
+
201
+ # Create the message object
202
+ message = ChatCompletionMessage(
203
+ role="assistant",
204
+ content=full_text
205
+ )
206
+
207
+ # Create the choice object
208
+ choice = Choice(
209
+ index=0,
210
+ message=message,
211
+ finish_reason="stop"
212
+ )
213
+
214
+ # Create the usage object
215
+ usage = CompletionUsage(
216
+ prompt_tokens=prompt_tokens,
217
+ completion_tokens=completion_tokens,
218
+ total_tokens=total_tokens
219
+ )
220
+
221
+ # Create the completion object
222
+ completion = ChatCompletion(
223
+ id=request_id,
224
+ choices=[choice],
225
+ created=created_time,
226
+ model=model,
227
+ usage=usage,
228
+ )
229
+
230
+ return completion
231
+
232
+ except CurlError as e:
233
+ print(f"{RED}Error during Typefully non-streaming request (CurlError): {e}{RESET}")
234
+ raise IOError(f"Typefully request failed (CurlError): {e}") from e
235
+ except Exception as e:
236
+ print(f"{RED}Error during Typefully non-streaming request: {e}{RESET}")
237
+ raise IOError(f"Typefully request failed: {e}") from e
238
+
239
+ class Chat(BaseChat):
240
+ def __init__(self, client: 'TypefullyAI'):
241
+ self.completions = Completions(client)
242
+
243
+ class TypefullyAI(OpenAICompatibleProvider):
244
+ """
245
+ OpenAI-compatible client for Typefully AI API.
246
+
247
+ Usage:
248
+ client = TypefullyAI()
249
+ response = client.chat.completions.create(
250
+ model="openai:gpt-4o-mini",
251
+ messages=[{"role": "user", "content": "Hello!"}]
252
+ )
253
+ print(response.choices[0].message.content)
254
+ """
255
+
256
+ AVAILABLE_MODELS = [
257
+ "openai:gpt-4o-mini",
258
+ "openai:gpt-4o",
259
+ "anthropic:claude-3-5-haiku-20241022",
260
+ "groq:llama-3.3-70b-versatile"
261
+ ]
262
+
263
+ def __init__(
264
+ self,
265
+ timeout: int = 30,
266
+
267
+ ):
268
+ """
269
+ Initialize the TypefullyAI client.
270
+
271
+ Args:
272
+ timeout: Request timeout in seconds
273
+ proxies: Optional proxy configuration
274
+ system_prompt: Default system prompt
275
+ output_length: Maximum length of the generated output
276
+ """
277
+ self.timeout = timeout
278
+ self.api_endpoint = "https://typefully.com/tools/ai/api/completion"
279
+
280
+ # Initialize curl_cffi Session
281
+ self.session = Session()
282
+
283
+ # Initialize LitAgent for user agent generation
284
+ agent = LitAgent()
285
+ self.user_agent = agent.random()
286
+
287
+ # Set headers
288
+ self.headers = {
289
+ "authority": "typefully.com",
290
+ "accept": "*/*",
291
+ "accept-encoding": "gzip, deflate, br, zstd",
292
+ "accept-language": "en-US,en;q=0.9",
293
+ "content-type": "application/json",
294
+ "dnt": "1",
295
+ "origin": "https://typefully.com",
296
+ "referer": "https://typefully.com/tools/ai/chat-gpt-alternative",
297
+ "sec-ch-ua": '"Microsoft Edge";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
298
+ "sec-ch-ua-mobile": "?0",
299
+ "sec-ch-ua-platform": '"Windows"',
300
+ "user-agent": self.user_agent
301
+ }
302
+
303
+ # Update curl_cffi session headers and proxies
304
+ self.session.headers.update(self.headers)
305
+
306
+ # Initialize chat interface
307
+ self.chat = Chat(self)
308
+
309
+ @staticmethod
310
+ def _typefully_extractor(chunk: str) -> Optional[str]:
311
+ """Extracts content from the Typefully stream format '0:"..."'."""
312
+ if isinstance(chunk, str):
313
+ match = re.search(r'0:"(.*?)"(?=,|$)', chunk)
314
+ if match:
315
+ # Decode potential unicode escapes like \u00e9 and handle escaped quotes/backslashes
316
+ content = match.group(1).encode().decode('unicode_escape')
317
+ return content.replace('\\\\', '\\').replace('\\"', '"')
318
+ return None
319
+
320
+ def convert_model_name(self, model: str) -> str:
321
+ """
322
+ Convert model names to ones supported by Typefully.
323
+
324
+ Args:
325
+ model: Model name to convert
326
+
327
+ Returns:
328
+ Typefully model name
329
+ """
330
+ # If the model is already a valid Typefully model, return it
331
+ if model in self.AVAILABLE_MODELS:
332
+ return model
333
+
334
+ # Map common OpenAI model names to Typefully models
335
+ model_mapping = {
336
+ "gpt-4o-mini": "openai:gpt-4o-mini",
337
+ "gpt-4o": "openai:gpt-4o",
338
+ "claude-3-5-haiku": "anthropic:claude-3-5-haiku-20241022",
339
+ "llama-3.3-70b": "groq:llama-3.3-70b-versatile"
340
+ }
341
+
342
+ if model in model_mapping:
343
+ return model_mapping[model]
344
+
345
+ # Default to the most capable model
346
+ print(f"{RED}Warning: Unknown model '{model}'. Using 'openai:gpt-4o-mini' instead.{RESET}")
347
+ return "openai:gpt-4o-mini"
348
+
349
+ @property
350
+ def models(self):
351
+ class _ModelList:
352
+ def list(inner_self):
353
+ return type(self).AVAILABLE_MODELS
354
+ return _ModelList()
355
+