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
@@ -0,0 +1,471 @@
1
+ import time
2
+ import uuid
3
+ import requests
4
+ import json
5
+ import re
6
+ from typing import List, Dict, Optional, Union, Generator, Any
7
+
8
+ # Import base classes and utility structures
9
+ from .base import OpenAICompatibleProvider, BaseChat, BaseCompletions
10
+ from .utils import (
11
+ ChatCompletionChunk, ChatCompletion, Choice, ChoiceDelta,
12
+ ChatCompletionMessage, CompletionUsage, get_system_prompt
13
+ )
14
+
15
+ # Attempt to import LitAgent, fallback if not available
16
+ try:
17
+ from webscout.litagent import LitAgent
18
+ except ImportError:
19
+ # Define a dummy LitAgent if webscout is not installed or accessible
20
+ class LitAgent:
21
+ def generate_fingerprint(self, browser: str = "chrome") -> Dict[str, Any]:
22
+ # Return minimal default headers if LitAgent is unavailable
23
+ print("Warning: LitAgent not found. Using default minimal headers.")
24
+ return {
25
+ "accept": "*/*",
26
+ "accept_language": "en-US,en;q=0.9",
27
+ "platform": "Windows",
28
+ "sec_ch_ua": '"Not/A)Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"',
29
+ "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
30
+ "browser_type": browser,
31
+ }
32
+
33
+ # --- SciraChat Client ---
34
+
35
+ class Completions(BaseCompletions):
36
+ def __init__(self, client: 'SciraChat'):
37
+ self._client = client
38
+
39
+ def create(
40
+ self,
41
+ *,
42
+ model: str,
43
+ messages: List[Dict[str, str]],
44
+ max_tokens: Optional[int] = None,
45
+ stream: bool = False,
46
+ temperature: Optional[float] = None,
47
+ top_p: Optional[float] = None,
48
+ **kwargs: Any
49
+ ) -> Union[ChatCompletion, Generator[ChatCompletionChunk, None, None]]:
50
+ """
51
+ Creates a model response for the given chat conversation.
52
+ Mimics openai.chat.completions.create
53
+ """
54
+
55
+ # Prepare the payload for SciraChat API
56
+ payload = {
57
+ "id": self._client.chat_id,
58
+ "messages": messages,
59
+ "model": self._client.convert_model_name(model),
60
+ "group": "chat", # Always use chat mode (no web search)
61
+ "user_id": self._client.user_id,
62
+ "timezone": "Asia/Calcutta"
63
+ }
64
+
65
+ # Add optional parameters if provided
66
+ if max_tokens is not None and max_tokens > 0:
67
+ payload["max_tokens"] = max_tokens
68
+
69
+ # Add any additional parameters
70
+ for key, value in kwargs.items():
71
+ if key not in payload:
72
+ payload[key] = value
73
+
74
+ request_id = f"chatcmpl-{uuid.uuid4()}"
75
+ created_time = int(time.time())
76
+
77
+ if stream:
78
+ return self._create_stream(request_id, created_time, model, payload)
79
+ else:
80
+ return self._create_non_stream(request_id, created_time, model, payload)
81
+
82
+ def _create_stream(
83
+ self, request_id: str, created_time: int, model: str, payload: Dict[str, Any]
84
+ ) -> Generator[ChatCompletionChunk, None, None]:
85
+ try:
86
+ response = self._client.session.post(
87
+ self._client.api_endpoint,
88
+ json=payload,
89
+ stream=True,
90
+ timeout=self._client.timeout
91
+ )
92
+
93
+ # Handle non-200 responses
94
+ if not response.ok:
95
+ # Try to refresh identity if we get a 403 or 429
96
+ if response.status_code in [403, 429]:
97
+ print(f"Received status code {response.status_code}, refreshing identity...")
98
+ self._client.refresh_identity()
99
+ response = self._client.session.post(
100
+ self._client.api_endpoint,
101
+ json=payload,
102
+ stream=True,
103
+ timeout=self._client.timeout
104
+ )
105
+ if not response.ok:
106
+ raise IOError(
107
+ f"Failed to generate response after identity refresh - ({response.status_code}, {response.reason}) - {response.text}"
108
+ )
109
+ print("Identity refreshed successfully.")
110
+ else:
111
+ raise IOError(
112
+ f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
113
+ )
114
+
115
+ # Track token usage across chunks
116
+ prompt_tokens = 0
117
+ completion_tokens = 0
118
+ total_tokens = 0
119
+
120
+ # Estimate prompt tokens based on message length
121
+ prompt_tokens = len(payload.get("messages", [{}])[0].get("content", "").split())
122
+
123
+ for line in response.iter_lines():
124
+ if not line:
125
+ continue
126
+
127
+ try:
128
+ line_str = line.decode('utf-8')
129
+
130
+ # Format: 0:"content" (quoted format)
131
+ match = re.search(r'0:"(.*?)"', line_str)
132
+ if match:
133
+ content = match.group(1)
134
+
135
+ # Format the content (replace escaped newlines)
136
+ content = self._client.format_text(content)
137
+
138
+ # Update token counts
139
+ completion_tokens += 1
140
+ total_tokens = prompt_tokens + completion_tokens
141
+
142
+ # Create the delta object
143
+ delta = ChoiceDelta(
144
+ content=content,
145
+ role="assistant",
146
+ tool_calls=None
147
+ )
148
+
149
+ # Create the choice object
150
+ choice = Choice(
151
+ index=0,
152
+ delta=delta,
153
+ finish_reason=None,
154
+ logprobs=None
155
+ )
156
+
157
+ # Create the chunk object
158
+ chunk = ChatCompletionChunk(
159
+ id=request_id,
160
+ choices=[choice],
161
+ created=created_time,
162
+ model=model,
163
+ system_fingerprint=None
164
+ )
165
+
166
+ # Convert to dict for proper formatting
167
+ chunk_dict = chunk.to_dict()
168
+
169
+ # Add usage information to match OpenAI format
170
+ usage_dict = {
171
+ "prompt_tokens": prompt_tokens,
172
+ "completion_tokens": completion_tokens,
173
+ "total_tokens": total_tokens,
174
+ "estimated_cost": None
175
+ }
176
+
177
+ chunk_dict["usage"] = usage_dict
178
+
179
+ # Return the chunk object for internal processing
180
+ yield chunk
181
+ except Exception as e:
182
+ print(f"Error processing chunk: {e}")
183
+ continue
184
+
185
+ # Final chunk with finish_reason="stop"
186
+ delta = ChoiceDelta(
187
+ content=None,
188
+ role=None,
189
+ tool_calls=None
190
+ )
191
+
192
+ choice = Choice(
193
+ index=0,
194
+ delta=delta,
195
+ finish_reason="stop",
196
+ logprobs=None
197
+ )
198
+
199
+ chunk = ChatCompletionChunk(
200
+ id=request_id,
201
+ choices=[choice],
202
+ created=created_time,
203
+ model=model,
204
+ system_fingerprint=None
205
+ )
206
+
207
+ chunk_dict = chunk.to_dict()
208
+ chunk_dict["usage"] = {
209
+ "prompt_tokens": prompt_tokens,
210
+ "completion_tokens": completion_tokens,
211
+ "total_tokens": total_tokens,
212
+ "estimated_cost": None
213
+ }
214
+
215
+ yield chunk
216
+
217
+ except Exception as e:
218
+ print(f"Error during SciraChat stream request: {e}")
219
+ raise IOError(f"SciraChat request failed: {e}") from e
220
+
221
+ def _create_non_stream(
222
+ self, request_id: str, created_time: int, model: str, payload: Dict[str, Any]
223
+ ) -> ChatCompletion:
224
+ try:
225
+ response = self._client.session.post(
226
+ self._client.api_endpoint,
227
+ json=payload,
228
+ timeout=self._client.timeout
229
+ )
230
+
231
+ # Handle non-200 responses
232
+ if not response.ok:
233
+ # Try to refresh identity if we get a 403 or 429
234
+ if response.status_code in [403, 429]:
235
+ print(f"Received status code {response.status_code}, refreshing identity...")
236
+ self._client.refresh_identity()
237
+ response = self._client.session.post(
238
+ self._client.api_endpoint,
239
+ json=payload,
240
+ timeout=self._client.timeout
241
+ )
242
+ if not response.ok:
243
+ raise IOError(
244
+ f"Failed to generate response after identity refresh - ({response.status_code}, {response.reason}) - {response.text}"
245
+ )
246
+ print("Identity refreshed successfully.")
247
+ else:
248
+ raise IOError(
249
+ f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
250
+ )
251
+
252
+ # Collect the full response
253
+ full_response = ""
254
+ for line in response.iter_lines():
255
+ if line:
256
+ try:
257
+ line_str = line.decode('utf-8')
258
+
259
+ # Format: 0:"content" (quoted format)
260
+ match = re.search(r'0:"(.*?)"', line_str)
261
+ if match:
262
+ content = match.group(1)
263
+ full_response += content
264
+ except:
265
+ continue
266
+
267
+ # Format the text (replace escaped newlines)
268
+ full_response = self._client.format_text(full_response)
269
+
270
+ # Estimate token counts
271
+ prompt_tokens = len(payload.get("messages", [{}])[0].get("content", "").split())
272
+ completion_tokens = len(full_response.split())
273
+ total_tokens = prompt_tokens + completion_tokens
274
+
275
+ # Create the message object
276
+ message = ChatCompletionMessage(
277
+ role="assistant",
278
+ content=full_response
279
+ )
280
+
281
+ # Create the choice object
282
+ choice = Choice(
283
+ index=0,
284
+ message=message,
285
+ finish_reason="stop"
286
+ )
287
+
288
+ # Create the usage object
289
+ usage = CompletionUsage(
290
+ prompt_tokens=prompt_tokens,
291
+ completion_tokens=completion_tokens,
292
+ total_tokens=total_tokens
293
+ )
294
+
295
+ # Create the completion object
296
+ completion = ChatCompletion(
297
+ id=request_id,
298
+ choices=[choice],
299
+ created=created_time,
300
+ model=model,
301
+ usage=usage,
302
+ )
303
+
304
+ return completion
305
+
306
+ except Exception as e:
307
+ print(f"Error during SciraChat non-stream request: {e}")
308
+ raise IOError(f"SciraChat request failed: {e}") from e
309
+
310
+ class Chat(BaseChat):
311
+ def __init__(self, client: 'SciraChat'):
312
+ self.completions = Completions(client)
313
+
314
+ class SciraChat(OpenAICompatibleProvider):
315
+ """
316
+ OpenAI-compatible client for Scira Chat API.
317
+
318
+ Usage:
319
+ client = SciraChat()
320
+ response = client.chat.completions.create(
321
+ model="scira-default",
322
+ messages=[{"role": "user", "content": "Hello!"}]
323
+ )
324
+ """
325
+
326
+ AVAILABLE_MODELS = {
327
+ "scira-default": "Grok3-mini", # thinking model
328
+ "scira-grok-3": "Grok3",
329
+ "scira-anthropic": "Sonnet 3.7 thinking",
330
+ "scira-vision" : "Grok2-Vision", # vision model
331
+ "scira-4o": "GPT4o",
332
+ "scira-qwq": "QWQ-32B",
333
+ "scira-o4-mini": "o4-mini",
334
+ "scira-google": "gemini 2.5 flash",
335
+ "scira-google-pro": "gemini 2.5 pro",
336
+ "scira-llama-4": "llama 4 Maverick",
337
+ }
338
+
339
+ def __init__(
340
+ self,
341
+ timeout: Optional[int] = None,
342
+ browser: str = "chrome"
343
+ ):
344
+ """
345
+ Initialize the SciraChat client.
346
+
347
+ Args:
348
+ timeout: Request timeout in seconds (None for no timeout)
349
+ browser: Browser to emulate in user agent
350
+ """
351
+ self.timeout = timeout or 30 # Default to 30 seconds if None
352
+ self.api_endpoint = "https://scira.ai/api/search"
353
+ self.session = requests.Session()
354
+
355
+ # Initialize LitAgent for user agent generation
356
+ self.agent = LitAgent()
357
+ self.fingerprint = self.agent.generate_fingerprint(browser)
358
+
359
+ # Use the fingerprint for headers
360
+ self.headers = {
361
+ "Accept": self.fingerprint["accept"],
362
+ "Accept-Encoding": "gzip, deflate, br, zstd",
363
+ "Accept-Language": self.fingerprint["accept_language"],
364
+ "Content-Type": "application/json",
365
+ "Origin": "https://scira.ai",
366
+ "Referer": "https://scira.ai/",
367
+ "Sec-CH-UA": self.fingerprint["sec_ch_ua"] or '"Not)A;Brand";v="99", "Microsoft Edge";v="127", "Chromium";v="127"',
368
+ "Sec-CH-UA-Mobile": "?0",
369
+ "Sec-CH-UA-Platform": f'"{self.fingerprint["platform"]}"',
370
+ "User-Agent": self.fingerprint["user_agent"],
371
+ "Sec-Fetch-Dest": "empty",
372
+ "Sec-Fetch-Mode": "cors",
373
+ "Sec-Fetch-Site": "same-origin"
374
+ }
375
+
376
+ self.session.headers.update(self.headers)
377
+
378
+ # Generate unique IDs for chat session and user
379
+ self.chat_id = str(uuid.uuid4())
380
+ self.user_id = f"user_{str(uuid.uuid4())[:8].upper()}"
381
+
382
+ # Initialize the chat interface
383
+ self.chat = Chat(self)
384
+
385
+ def refresh_identity(self, browser: str = None):
386
+ """
387
+ Refreshes the browser identity fingerprint.
388
+
389
+ Args:
390
+ browser: Specific browser to use for the new fingerprint
391
+ """
392
+ browser = browser or self.fingerprint.get("browser_type", "chrome")
393
+ self.fingerprint = self.agent.generate_fingerprint(browser)
394
+
395
+ # Update headers with new fingerprint
396
+ self.headers.update({
397
+ "Accept": self.fingerprint["accept"],
398
+ "Accept-Language": self.fingerprint["accept_language"],
399
+ "Sec-CH-UA": self.fingerprint["sec_ch_ua"] or self.headers["Sec-CH-UA"],
400
+ "Sec-CH-UA-Platform": f'"{self.fingerprint["platform"]}"',
401
+ "User-Agent": self.fingerprint["user_agent"],
402
+ })
403
+
404
+ # Update session headers
405
+ for header, value in self.headers.items():
406
+ self.session.headers[header] = value
407
+
408
+ return self.fingerprint
409
+
410
+ def format_text(self, text: str) -> str:
411
+ """
412
+ Format text by replacing escaped newlines with actual newlines.
413
+
414
+ Args:
415
+ text: Text to format
416
+
417
+ Returns:
418
+ Formatted text
419
+ """
420
+ # Use a more comprehensive approach to handle all escape sequences
421
+ try:
422
+ # First handle double backslashes to avoid issues
423
+ text = text.replace('\\\\', '\\')
424
+
425
+ # Handle common escape sequences
426
+ text = text.replace('\\n', '\n')
427
+ text = text.replace('\\r', '\r')
428
+ text = text.replace('\\t', '\t')
429
+ text = text.replace('\\"', '"')
430
+ text = text.replace("\\'", "'")
431
+
432
+ # Handle any remaining escape sequences using JSON decoding
433
+ # This is a fallback in case there are other escape sequences
434
+ try:
435
+ # Add quotes to make it a valid JSON string
436
+ json_str = f'"{text}"'
437
+ # Use json module to decode all escape sequences
438
+ decoded = json.loads(json_str)
439
+ return decoded
440
+ except json.JSONDecodeError:
441
+ # If JSON decoding fails, return the text with the replacements we've already done
442
+ return text
443
+ except Exception as e:
444
+ # If any error occurs, return the original text
445
+ print(f"Warning: Error formatting text: {e}")
446
+ return text
447
+
448
+ def convert_model_name(self, model: str) -> str:
449
+ """
450
+ Convert model names to ones supported by SciraChat.
451
+
452
+ Args:
453
+ model: Model name to convert
454
+
455
+ Returns:
456
+ SciraChat model name
457
+ """
458
+ # If the model is already a valid SciraChat model, return it
459
+ if model in self.AVAILABLE_MODELS:
460
+ return model
461
+
462
+ # Default to scira-default if model not found
463
+ print(f"Warning: Unknown model '{model}'. Using 'scira-default' instead.")
464
+ return "scira-default"
465
+
466
+ @property
467
+ def models(self):
468
+ class _ModelList:
469
+ def list(inner_self):
470
+ return type(self).AVAILABLE_MODELS
471
+ return _ModelList()