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,220 @@
1
+ import requests
2
+ import json
3
+ import re
4
+ import uuid
5
+ from typing import Dict, Optional, Generator, Union, Any
6
+
7
+ from webscout.AIbase import AISearch, SearchResponse
8
+ from webscout import exceptions
9
+ from webscout.litagent import LitAgent
10
+
11
+
12
+ class Monica(AISearch):
13
+ """A class to interact with the Monica AI search API.
14
+
15
+ Monica provides a powerful search interface that returns AI-generated responses
16
+ based on web content. It supports both streaming and non-streaming responses.
17
+
18
+ Basic Usage:
19
+ >>> from webscout import Monica
20
+ >>> ai = Monica()
21
+ >>> # Non-streaming example
22
+ >>> response = ai.search("What is Python?")
23
+ >>> print(response)
24
+ Python is a high-level programming language...
25
+
26
+ >>> # Streaming example
27
+ >>> for chunk in ai.search("Tell me about AI", stream=True):
28
+ ... print(chunk, end="", flush=True)
29
+ Artificial Intelligence is...
30
+
31
+ >>> # Raw response format
32
+ >>> for chunk in ai.search("Hello", stream=True, raw=True):
33
+ ... print(chunk)
34
+ {'text': 'Hello'}
35
+ {'text': ' there!'}
36
+
37
+ Args:
38
+ timeout (int, optional): Request timeout in seconds. Defaults to 60.
39
+ proxies (dict, optional): Proxy configuration for requests. Defaults to None.
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ timeout: int = 60,
45
+ proxies: Optional[dict] = None,
46
+ ):
47
+ """Initialize the Monica API client.
48
+
49
+ Args:
50
+ timeout (int, optional): Request timeout in seconds. Defaults to 60.
51
+ proxies (dict, optional): Proxy configuration for requests. Defaults to None.
52
+ """
53
+ self.session = requests.Session()
54
+ self.api_endpoint = "https://monica.so/api/search_v1/search"
55
+ self.stream_chunk_size = 64
56
+ self.timeout = timeout
57
+ self.last_response = {}
58
+ self.client_id = str(uuid.uuid4())
59
+ self.session_id = ""
60
+
61
+ self.headers = {
62
+ "accept": "*/*",
63
+ "accept-encoding": "gzip, deflate, br, zstd",
64
+ "accept-language": "en-US,en;q=0.9",
65
+ "content-type": "application/json",
66
+ "dnt": "1",
67
+ "origin": "https://monica.so",
68
+ "referer": "https://monica.so/answers",
69
+ "sec-ch-ua": '"Microsoft Edge";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
70
+ "sec-ch-ua-mobile": "?0",
71
+ "sec-ch-ua-platform": '"Windows"',
72
+ "sec-fetch-dest": "empty",
73
+ "sec-fetch-mode": "cors",
74
+ "sec-fetch-site": "same-origin",
75
+ "sec-gpc": "1",
76
+ "user-agent": LitAgent().random(),
77
+ "x-client-id": self.client_id,
78
+ "x-client-locale": "en",
79
+ "x-client-type": "web",
80
+ "x-client-version": "5.4.3",
81
+ "x-from-channel": "NA",
82
+ "x-product-name": "Monica-Search",
83
+ "x-time-zone": "Asia/Calcutta;-330"
84
+ }
85
+
86
+ self.cookies = {
87
+ "monica_home_theme": "auto",
88
+ }
89
+
90
+ self.session.headers.update(self.headers)
91
+ self.proxies = proxies
92
+
93
+ def search(
94
+ self,
95
+ prompt: str,
96
+ stream: bool = False,
97
+ raw: bool = False,
98
+ ) -> Union[SearchResponse, Generator[Union[Dict[str, str], SearchResponse], None, None]]:
99
+ """Search using the Monica API and get AI-generated responses.
100
+
101
+ This method sends a search query to Monica and returns the AI-generated response.
102
+ It supports both streaming and non-streaming modes, as well as raw response format.
103
+
104
+ Args:
105
+ prompt (str): The search query or prompt to send to the API.
106
+ stream (bool, optional): If True, yields response chunks as they arrive.
107
+ If False, returns complete response. Defaults to False.
108
+ raw (bool, optional): If True, returns raw response dictionaries with 'text' key.
109
+ If False, returns Response objects that convert to text automatically.
110
+ Defaults to False.
111
+
112
+ Returns:
113
+ Union[Response, Generator[Union[Dict[str, str], Response], None, None]]:
114
+ - If stream=False: Returns complete response as Response object
115
+ - If stream=True: Yields response chunks as either Dict or Response objects
116
+
117
+ Raises:
118
+ APIConnectionError: If the API request fails
119
+ """
120
+ task_id = str(uuid.uuid4())
121
+ payload = {
122
+ "pro": False,
123
+ "query": prompt,
124
+ "round": 1,
125
+ "session_id": self.session_id,
126
+ "language": "auto",
127
+ "task_id": task_id
128
+ }
129
+
130
+ def for_stream():
131
+ try:
132
+ with self.session.post(
133
+ self.api_endpoint,
134
+ json=payload,
135
+ stream=True,
136
+ cookies=self.cookies,
137
+ timeout=self.timeout,
138
+ proxies=self.proxies
139
+ ) as response:
140
+ if not response.ok:
141
+ raise exceptions.APIConnectionError(
142
+ f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
143
+ )
144
+
145
+ # Process the Server-Sent Events (SSE) stream
146
+ for line in response.iter_lines(decode_unicode=True):
147
+ if line and line.startswith("data: "):
148
+ try:
149
+ data = json.loads(line[6:]) # Remove 'data: ' prefix
150
+
151
+ # Save session_id for future requests if present
152
+ if "session_id" in data and data["session_id"]:
153
+ self.session_id = data["session_id"]
154
+
155
+ # Only process chunks with text content
156
+ if "text" in data and data["text"]:
157
+ text_chunk = data["text"]
158
+
159
+ if raw:
160
+ yield {"text": text_chunk}
161
+ else:
162
+ yield SearchResponse(text_chunk)
163
+
164
+ # Check if stream is finished
165
+ if "finished" in data and data["finished"]:
166
+ break
167
+
168
+ except json.JSONDecodeError:
169
+ continue
170
+
171
+ except requests.exceptions.RequestException as e:
172
+ raise exceptions.APIConnectionError(f"Request failed: {e}")
173
+
174
+ def for_non_stream():
175
+ full_response = ""
176
+ search_results = []
177
+
178
+ for chunk in for_stream():
179
+ if raw:
180
+ yield chunk
181
+ else:
182
+ full_response += str(chunk)
183
+
184
+ if not raw:
185
+ # Process the full response to clean up formatting
186
+ formatted_response = self.format_response(full_response)
187
+ self.last_response = SearchResponse(formatted_response)
188
+ return self.last_response
189
+
190
+ return for_stream() if stream else for_non_stream()
191
+
192
+ @staticmethod
193
+ def format_response(text: str) -> str:
194
+ """Format the response text for better readability.
195
+
196
+ Args:
197
+ text (str): The raw response text
198
+
199
+ Returns:
200
+ str: Formatted text
201
+ """
202
+ # Clean up markdown formatting
203
+ cleaned_text = text.replace('**', '')
204
+
205
+ # Remove any empty lines
206
+ cleaned_text = re.sub(r'\n\s*\n', '\n\n', cleaned_text)
207
+
208
+ # Remove any trailing whitespace
209
+ cleaned_text = cleaned_text.strip()
210
+
211
+ return cleaned_text
212
+
213
+
214
+ if __name__ == "__main__":
215
+ from rich import print
216
+
217
+ ai = Monica()
218
+ response = ai.search(input(">>> "), stream=True, raw=False)
219
+ for chunk in response:
220
+ print(chunk, end="", flush=True)
@@ -0,0 +1,298 @@
1
+ import requests
2
+ import json
3
+ import re
4
+ import uuid
5
+ import time
6
+ from typing import Dict, Optional, Generator, Union, Any
7
+
8
+ from webscout.AIbase import AISearch, SearchResponse
9
+ from webscout import exceptions
10
+ from webscout.litagent import LitAgent
11
+
12
+
13
+ class Scira(AISearch):
14
+ """A class to interact with the SCIRA AI search API.
15
+
16
+ SCIRA provides a powerful search interface that returns AI-generated responses
17
+ based on web content. It supports both streaming and non-streaming responses.
18
+
19
+ Basic Usage:
20
+ >>> from webscout import Scira
21
+ >>> ai = Scira()
22
+ >>> # Non-streaming example
23
+ >>> response = ai.search("What is Python?")
24
+ >>> print(response)
25
+ Python is a high-level programming language...
26
+
27
+ >>> # Streaming example
28
+ >>> for chunk in ai.search("Tell me about AI", stream=True):
29
+ ... print(chunk, end="", flush=True)
30
+ Artificial Intelligence is...
31
+
32
+ >>> # Raw response format
33
+ >>> for chunk in ai.search("Hello", stream=True, raw=True):
34
+ ... print(chunk)
35
+ {'text': 'Hello'}
36
+ {'text': ' there!'}
37
+
38
+ Args:
39
+ timeout (int, optional): Request timeout in seconds. Defaults to 60.
40
+ proxies (dict, optional): Proxy configuration for requests. Defaults to None.
41
+ model (str, optional): Model to use for the search. Defaults to "scira-default".
42
+ group (str, optional): Group parameter. Defaults to "web".
43
+ """
44
+
45
+ AVAILABLE_MODELS = {
46
+ "scira-default": "Grok3-mini", # thinking model
47
+ "scira-grok-3": "Grok3",
48
+ "scira-anthropic": "Sonnet 3.7 thinking",
49
+ "scira-vision" : "Grok2-Vision", # vision model
50
+ "scira-4o": "GPT4o",
51
+ "scira-qwq": "QWQ-32B",
52
+ "scira-o4-mini": "o4-mini",
53
+ "scira-google": "gemini 2.5 flash",
54
+ "scira-google-pro": "gemini 2.5 pro",
55
+ "scira-llama-4": "llama 4 Maverick",
56
+ }
57
+ def __init__(
58
+ self,
59
+ timeout: int = 60,
60
+ proxies: Optional[dict] = None,
61
+ model: str = "scira-default",
62
+ deepsearch: bool = False
63
+ ):
64
+ """Initialize the SCIRA API client.
65
+
66
+ Args:
67
+ timeout (int, optional): Request timeout in seconds. Defaults to 60.
68
+ proxies (dict, optional): Proxy configuration for requests. Defaults to None.
69
+ model (str, optional): Model to use for the search. Defaults to "scira-default" (Grok3).
70
+ deepsearch (bool, optional): Whether to use deep search mode. If True, uses "extreme" group for more comprehensive results. If False, uses "web" group for faster results. Defaults to False.
71
+
72
+ Example:
73
+ >>> ai = Scira(timeout=120) # Longer timeout
74
+ >>> ai = Scira(proxies={'http': 'http://proxy.com:8080'}) # With proxy
75
+ >>> ai = Scira(model="scira-claude") # Use Claude model
76
+ >>> ai = Scira(deepsearch=True) # Use deep search mode
77
+ """
78
+ # Validate model
79
+ if model not in self.AVAILABLE_MODELS:
80
+ raise ValueError(
81
+ f"Invalid model: {model}. Choose from: {list(self.AVAILABLE_MODELS.keys())}"
82
+ )
83
+
84
+ self.session = requests.Session()
85
+ self.api_endpoint = "https://scira.ai/api/search"
86
+ self.timeout = timeout
87
+ self.proxies = proxies
88
+ self.model = model
89
+
90
+ # Set group based on deepsearch parameter
91
+ self.group = "extreme" if deepsearch else "web"
92
+ self.last_response = {}
93
+
94
+ # Set headers
95
+ self.headers = {
96
+ "Content-Type": "application/json",
97
+ "Accept": "*/*",
98
+ "User-Agent": LitAgent().random()
99
+ }
100
+
101
+ self.session.headers.update(self.headers)
102
+
103
+ def search(
104
+ self,
105
+ prompt: str,
106
+ stream: bool = False,
107
+ raw: bool = False,
108
+ ) -> Union[SearchResponse, Generator[Union[Dict[str, str], SearchResponse], None, None]]:
109
+ """Search using the SCIRA API and get AI-generated responses.
110
+
111
+ This method sends a search query to SCIRA and returns the AI-generated response.
112
+ It supports both streaming and non-streaming modes, as well as raw response format.
113
+
114
+ Args:
115
+ prompt (str): The search query or prompt to send to the API.
116
+ stream (bool, optional): If True, yields response chunks as they arrive.
117
+ If False, returns complete response. Defaults to False.
118
+ raw (bool, optional): If True, returns raw response dictionaries with 'text' key.
119
+ If False, returns Response objects that convert to text automatically.
120
+ Defaults to False.
121
+
122
+ Returns:
123
+ Union[Response, Generator[Union[Dict[str, str], Response], None, None]]:
124
+ - If stream=False: Returns complete response
125
+ - If stream=True: Yields response chunks as they arrive
126
+
127
+ Raises:
128
+ exceptions.APIConnectionError: If there's an issue connecting to the API
129
+ exceptions.APIResponseError: If the API returns an error response
130
+
131
+ Example:
132
+ >>> ai = Scira()
133
+ >>> # Non-streaming example
134
+ >>> response = ai.search("What is Python?")
135
+ >>> print(response)
136
+ Python is a high-level programming language...
137
+
138
+ >>> # Streaming example
139
+ >>> for chunk in ai.search("Tell me about AI", stream=True):
140
+ ... print(chunk, end="", flush=True)
141
+ Artificial Intelligence is...
142
+ """
143
+ # Create a unique message ID
144
+ message_id = str(uuid.uuid4()).replace("-", "")[:16]
145
+ self.user_id = str(uuid.uuid4()).replace("-", "")[:16]
146
+ # Prepare the payload
147
+ payload = {
148
+ "id": message_id,
149
+ "messages": [
150
+ {
151
+ "role": "user",
152
+ "content": prompt,
153
+ "parts": [
154
+ {
155
+ "type": "text",
156
+ "text": prompt
157
+ }
158
+ ]
159
+ }
160
+ ],
161
+ "model": self.model,
162
+ "group": self.group,
163
+ "user_id": self.user_id,
164
+ "timezone": "Asia/Calcutta"
165
+ }
166
+
167
+ try:
168
+ # Send the request
169
+ response = self.session.post(
170
+ self.api_endpoint,
171
+ headers=self.headers,
172
+ data=json.dumps(payload),
173
+ stream=True,
174
+ timeout=self.timeout,
175
+ proxies=self.proxies
176
+ )
177
+
178
+ # Check for successful response
179
+ if response.status_code != 200:
180
+ raise exceptions.APIResponseError(
181
+ f"API returned error status: {response.status_code}"
182
+ )
183
+
184
+ # Store the last response
185
+ self.last_response = {"status_code": response.status_code}
186
+
187
+ # Handle streaming response
188
+ if stream:
189
+ return self._handle_streaming_response(response, raw)
190
+
191
+ # Handle non-streaming response
192
+ return self._handle_non_streaming_response(response, raw)
193
+
194
+ except requests.RequestException as e:
195
+ raise exceptions.APIConnectionError(f"Error connecting to API: {str(e)}")
196
+
197
+ def _handle_streaming_response(
198
+ self,
199
+ response: requests.Response,
200
+ raw: bool
201
+ ) -> Generator[Union[Dict[str, str], SearchResponse], None, None]:
202
+ """Handle streaming response from the API.
203
+
204
+ Args:
205
+ response (requests.Response): The streaming response from the API
206
+ raw (bool): Whether to return raw response dictionaries
207
+
208
+ Yields:
209
+ Union[Dict[str, str], Response]: Response chunks as they arrive
210
+ """
211
+ for line in response.iter_lines():
212
+ if line:
213
+ try:
214
+ # Decode the line
215
+ decoded_line = line.decode("utf-8")
216
+
217
+ # Check if this is a line starting with "0:" (content)
218
+ if re.match(r'^0:', decoded_line):
219
+ # Extract the content after "0:"
220
+ content = re.sub(r'^0:', '', decoded_line)
221
+ # Remove surrounding quotes if present
222
+ content = re.sub(r'^"(.*)"$', r'\1', content)
223
+ # Replace escaped newlines with actual newlines
224
+ content = content.replace('\\n', '\n')
225
+
226
+ if raw:
227
+ yield {"text": content}
228
+ else:
229
+ yield SearchResponse(content)
230
+ except Exception:
231
+ # Skip lines that can't be processed
232
+ pass
233
+
234
+ def _handle_non_streaming_response(
235
+ self,
236
+ response: requests.Response,
237
+ raw: bool
238
+ ) -> Union[Dict[str, str], SearchResponse]:
239
+ """Handle non-streaming response from the API.
240
+
241
+ Args:
242
+ response (requests.Response): The response from the API
243
+ raw (bool): Whether to return raw response dictionary
244
+
245
+ Returns:
246
+ Union[Dict[str, str], Response]: Complete response
247
+ """
248
+ full_text = ""
249
+
250
+ for line in response.iter_lines():
251
+ if line:
252
+ try:
253
+ # Decode the line
254
+ decoded_line = line.decode("utf-8")
255
+
256
+ # Check if this is a line starting with "0:" (content)
257
+ if re.match(r'^0:', decoded_line):
258
+ # Extract the content after "0:"
259
+ content = re.sub(r'^0:', '', decoded_line)
260
+ # Remove surrounding quotes if present
261
+ content = re.sub(r'^"(.*)"$', r'\1', content)
262
+ # Replace escaped newlines with actual newlines
263
+ content = content.replace('\\n', '\n')
264
+ full_text += content
265
+ except Exception:
266
+ # Skip lines that can't be processed
267
+ pass
268
+
269
+ if raw:
270
+ return {"text": full_text}
271
+ else:
272
+ return SearchResponse(full_text)
273
+
274
+ @staticmethod
275
+ def clean_content(text: str) -> str:
276
+ """Clean the response content by removing unnecessary formatting.
277
+
278
+ Args:
279
+ text (str): The text to clean
280
+
281
+ Returns:
282
+ str: The cleaned text
283
+ """
284
+ # Remove any extra whitespace
285
+ cleaned_text = re.sub(r'\s+', ' ', text)
286
+ # Remove any trailing whitespace
287
+ cleaned_text = cleaned_text.strip()
288
+
289
+ return cleaned_text
290
+
291
+
292
+ if __name__ == "__main__":
293
+ from rich import print
294
+ ai = Scira(deepsearch=False)
295
+ user_query = input(">>> ")
296
+ response = ai.search(user_query, stream=True, raw=False)
297
+ for chunk in response:
298
+ print(chunk, end="", flush=True)