webscout 8.2.8__py3-none-any.whl → 8.3__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 (197) hide show
  1. webscout/AIauto.py +34 -16
  2. webscout/AIbase.py +96 -37
  3. webscout/AIutel.py +491 -87
  4. webscout/Bard.py +441 -323
  5. webscout/Extra/GitToolkit/__init__.py +10 -10
  6. webscout/Extra/YTToolkit/ytapi/video.py +232 -232
  7. webscout/Litlogger/README.md +10 -0
  8. webscout/Litlogger/__init__.py +7 -59
  9. webscout/Litlogger/formats.py +4 -0
  10. webscout/Litlogger/handlers.py +103 -0
  11. webscout/Litlogger/levels.py +13 -0
  12. webscout/Litlogger/logger.py +92 -0
  13. webscout/Provider/AISEARCH/Perplexity.py +332 -358
  14. webscout/Provider/AISEARCH/felo_search.py +9 -35
  15. webscout/Provider/AISEARCH/genspark_search.py +30 -56
  16. webscout/Provider/AISEARCH/hika_search.py +4 -16
  17. webscout/Provider/AISEARCH/iask_search.py +410 -436
  18. webscout/Provider/AISEARCH/monica_search.py +4 -30
  19. webscout/Provider/AISEARCH/scira_search.py +6 -32
  20. webscout/Provider/AISEARCH/webpilotai_search.py +38 -64
  21. webscout/Provider/Blackboxai.py +155 -35
  22. webscout/Provider/ChatSandbox.py +2 -1
  23. webscout/Provider/Deepinfra.py +339 -339
  24. webscout/Provider/ExaChat.py +358 -358
  25. webscout/Provider/Gemini.py +169 -169
  26. webscout/Provider/GithubChat.py +1 -2
  27. webscout/Provider/Glider.py +3 -3
  28. webscout/Provider/HeckAI.py +172 -82
  29. webscout/Provider/LambdaChat.py +1 -0
  30. webscout/Provider/MCPCore.py +7 -3
  31. webscout/Provider/OPENAI/BLACKBOXAI.py +421 -139
  32. webscout/Provider/OPENAI/Cloudflare.py +38 -21
  33. webscout/Provider/OPENAI/FalconH1.py +457 -0
  34. webscout/Provider/OPENAI/FreeGemini.py +35 -18
  35. webscout/Provider/OPENAI/NEMOTRON.py +34 -34
  36. webscout/Provider/OPENAI/PI.py +427 -0
  37. webscout/Provider/OPENAI/Qwen3.py +304 -0
  38. webscout/Provider/OPENAI/README.md +952 -1253
  39. webscout/Provider/OPENAI/TwoAI.py +374 -0
  40. webscout/Provider/OPENAI/__init__.py +7 -1
  41. webscout/Provider/OPENAI/ai4chat.py +73 -63
  42. webscout/Provider/OPENAI/api.py +869 -644
  43. webscout/Provider/OPENAI/base.py +2 -0
  44. webscout/Provider/OPENAI/c4ai.py +34 -13
  45. webscout/Provider/OPENAI/chatgpt.py +575 -556
  46. webscout/Provider/OPENAI/chatgptclone.py +512 -487
  47. webscout/Provider/OPENAI/chatsandbox.py +11 -6
  48. webscout/Provider/OPENAI/copilot.py +258 -0
  49. webscout/Provider/OPENAI/deepinfra.py +327 -318
  50. webscout/Provider/OPENAI/e2b.py +140 -104
  51. webscout/Provider/OPENAI/exaai.py +420 -411
  52. webscout/Provider/OPENAI/exachat.py +448 -443
  53. webscout/Provider/OPENAI/flowith.py +7 -3
  54. webscout/Provider/OPENAI/freeaichat.py +12 -8
  55. webscout/Provider/OPENAI/glider.py +15 -8
  56. webscout/Provider/OPENAI/groq.py +5 -2
  57. webscout/Provider/OPENAI/heckai.py +311 -307
  58. webscout/Provider/OPENAI/llmchatco.py +9 -7
  59. webscout/Provider/OPENAI/mcpcore.py +18 -9
  60. webscout/Provider/OPENAI/multichat.py +7 -5
  61. webscout/Provider/OPENAI/netwrck.py +16 -11
  62. webscout/Provider/OPENAI/oivscode.py +290 -0
  63. webscout/Provider/OPENAI/opkfc.py +507 -496
  64. webscout/Provider/OPENAI/pydantic_imports.py +172 -0
  65. webscout/Provider/OPENAI/scirachat.py +29 -17
  66. webscout/Provider/OPENAI/sonus.py +308 -303
  67. webscout/Provider/OPENAI/standardinput.py +442 -433
  68. webscout/Provider/OPENAI/textpollinations.py +18 -11
  69. webscout/Provider/OPENAI/toolbaz.py +419 -413
  70. webscout/Provider/OPENAI/typefully.py +17 -10
  71. webscout/Provider/OPENAI/typegpt.py +21 -11
  72. webscout/Provider/OPENAI/uncovrAI.py +477 -462
  73. webscout/Provider/OPENAI/utils.py +90 -79
  74. webscout/Provider/OPENAI/venice.py +435 -425
  75. webscout/Provider/OPENAI/wisecat.py +387 -381
  76. webscout/Provider/OPENAI/writecream.py +166 -163
  77. webscout/Provider/OPENAI/x0gpt.py +26 -37
  78. webscout/Provider/OPENAI/yep.py +384 -356
  79. webscout/Provider/PI.py +2 -1
  80. webscout/Provider/TTI/README.md +55 -101
  81. webscout/Provider/TTI/__init__.py +4 -9
  82. webscout/Provider/TTI/aiarta.py +365 -0
  83. webscout/Provider/TTI/artbit.py +0 -0
  84. webscout/Provider/TTI/base.py +64 -0
  85. webscout/Provider/TTI/fastflux.py +200 -0
  86. webscout/Provider/TTI/magicstudio.py +201 -0
  87. webscout/Provider/TTI/piclumen.py +203 -0
  88. webscout/Provider/TTI/pixelmuse.py +225 -0
  89. webscout/Provider/TTI/pollinations.py +221 -0
  90. webscout/Provider/TTI/utils.py +11 -0
  91. webscout/Provider/TTS/__init__.py +2 -1
  92. webscout/Provider/TTS/base.py +159 -159
  93. webscout/Provider/TTS/openai_fm.py +129 -0
  94. webscout/Provider/TextPollinationsAI.py +308 -308
  95. webscout/Provider/TwoAI.py +239 -44
  96. webscout/Provider/UNFINISHED/Youchat.py +330 -330
  97. webscout/Provider/UNFINISHED/puterjs.py +635 -0
  98. webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
  99. webscout/Provider/Writecream.py +246 -246
  100. webscout/Provider/__init__.py +2 -2
  101. webscout/Provider/ai4chat.py +33 -8
  102. webscout/Provider/granite.py +41 -6
  103. webscout/Provider/koala.py +169 -169
  104. webscout/Provider/oivscode.py +309 -0
  105. webscout/Provider/samurai.py +3 -2
  106. webscout/Provider/scnet.py +1 -0
  107. webscout/Provider/typegpt.py +3 -3
  108. webscout/Provider/uncovr.py +368 -368
  109. webscout/client.py +70 -0
  110. webscout/litprinter/__init__.py +58 -58
  111. webscout/optimizers.py +419 -419
  112. webscout/scout/README.md +3 -1
  113. webscout/scout/core/crawler.py +134 -64
  114. webscout/scout/core/scout.py +148 -109
  115. webscout/scout/element.py +106 -88
  116. webscout/swiftcli/Readme.md +323 -323
  117. webscout/swiftcli/plugins/manager.py +9 -2
  118. webscout/version.py +1 -1
  119. webscout/zeroart/__init__.py +134 -134
  120. webscout/zeroart/effects.py +100 -100
  121. webscout/zeroart/fonts.py +1238 -1238
  122. {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/METADATA +160 -35
  123. webscout-8.3.dist-info/RECORD +290 -0
  124. {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/WHEEL +1 -1
  125. {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/entry_points.txt +1 -0
  126. webscout/Litlogger/Readme.md +0 -175
  127. webscout/Litlogger/core/__init__.py +0 -6
  128. webscout/Litlogger/core/level.py +0 -23
  129. webscout/Litlogger/core/logger.py +0 -165
  130. webscout/Litlogger/handlers/__init__.py +0 -12
  131. webscout/Litlogger/handlers/console.py +0 -33
  132. webscout/Litlogger/handlers/file.py +0 -143
  133. webscout/Litlogger/handlers/network.py +0 -173
  134. webscout/Litlogger/styles/__init__.py +0 -7
  135. webscout/Litlogger/styles/colors.py +0 -249
  136. webscout/Litlogger/styles/formats.py +0 -458
  137. webscout/Litlogger/styles/text.py +0 -87
  138. webscout/Litlogger/utils/__init__.py +0 -6
  139. webscout/Litlogger/utils/detectors.py +0 -153
  140. webscout/Litlogger/utils/formatters.py +0 -200
  141. webscout/Provider/ChatGPTGratis.py +0 -194
  142. webscout/Provider/TTI/AiForce/README.md +0 -159
  143. webscout/Provider/TTI/AiForce/__init__.py +0 -22
  144. webscout/Provider/TTI/AiForce/async_aiforce.py +0 -224
  145. webscout/Provider/TTI/AiForce/sync_aiforce.py +0 -245
  146. webscout/Provider/TTI/FreeAIPlayground/README.md +0 -99
  147. webscout/Provider/TTI/FreeAIPlayground/__init__.py +0 -9
  148. webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +0 -181
  149. webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +0 -180
  150. webscout/Provider/TTI/ImgSys/README.md +0 -174
  151. webscout/Provider/TTI/ImgSys/__init__.py +0 -23
  152. webscout/Provider/TTI/ImgSys/async_imgsys.py +0 -202
  153. webscout/Provider/TTI/ImgSys/sync_imgsys.py +0 -195
  154. webscout/Provider/TTI/MagicStudio/README.md +0 -101
  155. webscout/Provider/TTI/MagicStudio/__init__.py +0 -2
  156. webscout/Provider/TTI/MagicStudio/async_magicstudio.py +0 -111
  157. webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +0 -109
  158. webscout/Provider/TTI/Nexra/README.md +0 -155
  159. webscout/Provider/TTI/Nexra/__init__.py +0 -22
  160. webscout/Provider/TTI/Nexra/async_nexra.py +0 -286
  161. webscout/Provider/TTI/Nexra/sync_nexra.py +0 -258
  162. webscout/Provider/TTI/PollinationsAI/README.md +0 -146
  163. webscout/Provider/TTI/PollinationsAI/__init__.py +0 -23
  164. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +0 -311
  165. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +0 -265
  166. webscout/Provider/TTI/aiarta/README.md +0 -134
  167. webscout/Provider/TTI/aiarta/__init__.py +0 -2
  168. webscout/Provider/TTI/aiarta/async_aiarta.py +0 -482
  169. webscout/Provider/TTI/aiarta/sync_aiarta.py +0 -440
  170. webscout/Provider/TTI/artbit/README.md +0 -100
  171. webscout/Provider/TTI/artbit/__init__.py +0 -22
  172. webscout/Provider/TTI/artbit/async_artbit.py +0 -155
  173. webscout/Provider/TTI/artbit/sync_artbit.py +0 -148
  174. webscout/Provider/TTI/fastflux/README.md +0 -129
  175. webscout/Provider/TTI/fastflux/__init__.py +0 -22
  176. webscout/Provider/TTI/fastflux/async_fastflux.py +0 -261
  177. webscout/Provider/TTI/fastflux/sync_fastflux.py +0 -252
  178. webscout/Provider/TTI/huggingface/README.md +0 -114
  179. webscout/Provider/TTI/huggingface/__init__.py +0 -22
  180. webscout/Provider/TTI/huggingface/async_huggingface.py +0 -199
  181. webscout/Provider/TTI/huggingface/sync_huggingface.py +0 -195
  182. webscout/Provider/TTI/piclumen/README.md +0 -161
  183. webscout/Provider/TTI/piclumen/__init__.py +0 -23
  184. webscout/Provider/TTI/piclumen/async_piclumen.py +0 -268
  185. webscout/Provider/TTI/piclumen/sync_piclumen.py +0 -233
  186. webscout/Provider/TTI/pixelmuse/README.md +0 -79
  187. webscout/Provider/TTI/pixelmuse/__init__.py +0 -4
  188. webscout/Provider/TTI/pixelmuse/async_pixelmuse.py +0 -249
  189. webscout/Provider/TTI/pixelmuse/sync_pixelmuse.py +0 -182
  190. webscout/Provider/TTI/talkai/README.md +0 -139
  191. webscout/Provider/TTI/talkai/__init__.py +0 -4
  192. webscout/Provider/TTI/talkai/async_talkai.py +0 -229
  193. webscout/Provider/TTI/talkai/sync_talkai.py +0 -207
  194. webscout/Provider/UNFINISHED/oivscode.py +0 -351
  195. webscout-8.2.8.dist-info/RECORD +0 -334
  196. {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/licenses/LICENSE.md +0 -0
  197. {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,11 @@
1
1
  from curl_cffi.requests import Session
2
2
  from curl_cffi import CurlError
3
3
  import json
4
+ import base64
5
+ import time
4
6
  from typing import Any, Dict, Optional, Generator, Union
5
7
  import re # Import re for parsing SSE
8
+ import urllib.parse
6
9
 
7
10
  from webscout.AIutel import Optimizers
8
11
  from webscout.AIutel import Conversation
@@ -10,22 +13,153 @@ from webscout.AIutel import AwesomePrompts, sanitize_stream # Import sanitize_st
10
13
  from webscout.AIbase import Provider
11
14
  from webscout import exceptions
12
15
  from webscout.litagent import LitAgent
16
+ from webscout.Extra.tempmail import get_random_email
13
17
 
14
18
 
15
19
  class TwoAI(Provider):
16
20
  """
17
21
  A class to interact with the Two AI API (v2) with LitAgent user-agent.
22
+ SUTRA is a family of large multi-lingual language models (LMLMs) developed by TWO AI.
23
+ SUTRA's dual-transformer extends the power of both MoE and Dense AI language model architectures,
24
+ delivering cost-efficient multilingual capabilities for over 50+ languages.
25
+
26
+ API keys can be generated using the generate_api_key() method, which uses a temporary email
27
+ to register for the Two AI service and extract the API key from the confirmation email.
18
28
  """
19
29
 
20
30
  AVAILABLE_MODELS = [
21
- "sutra-v2",
22
- "sutra-r0"
23
-
31
+ "sutra-v2", # Multilingual AI model for instruction execution and conversational intelligence
32
+ "sutra-r0", # Advanced reasoning model for complex problem-solving and deep contextual understanding
24
33
  ]
25
34
 
35
+ @staticmethod
36
+ def generate_api_key() -> str:
37
+ """
38
+ Generate a new Two AI API key using a temporary email.
39
+
40
+ This method:
41
+ 1. Creates a temporary email using webscout's tempmail module
42
+ 2. Registers for Two AI using the Loops.so newsletter form
43
+ 3. Waits for and extracts the API key from the confirmation email
44
+
45
+ Returns:
46
+ str: The generated API key
47
+
48
+ Raises:
49
+ Exception: If the API key cannot be generated
50
+ """
51
+ # Get a temporary email
52
+ email, provider = get_random_email("tempmailio")
53
+
54
+ # Register for Two AI using the Loops.so newsletter form
55
+ loops_url = "https://app.loops.so/api/newsletter-form/cm7i4o92h057auy1o74cxbhxo"
56
+
57
+ # Create a session with appropriate headers
58
+ session = Session()
59
+ session.headers.update({
60
+ 'User-Agent': LitAgent().random(),
61
+ 'Content-Type': 'application/x-www-form-urlencoded',
62
+ 'Origin': 'https://www.two.ai',
63
+ 'Referer': 'https://app.loops.so/',
64
+ })
65
+
66
+ # Prepare form data
67
+ form_data = {
68
+ 'email': email,
69
+ 'userGroup': 'Via Framer',
70
+ 'mailingLists': 'cm8ay9cic00x70kjv0bd34k66'
71
+ }
72
+
73
+ # Send the registration request
74
+ encoded_data = urllib.parse.urlencode(form_data)
75
+ response = session.post(loops_url, data=encoded_data, impersonate="chrome120")
76
+
77
+ if response.status_code != 200:
78
+ raise Exception(f"Failed to register for Two AI: {response.status_code} - {response.text}")
79
+
80
+ # Wait for the confirmation email and extract the API key
81
+ max_attempts = 5
82
+ attempt = 0
83
+ api_key = None
84
+ wait_time = 2
85
+
86
+ while attempt < max_attempts and not api_key:
87
+ messages = provider.get_messages()
88
+
89
+ for message in messages:
90
+ # Check if this is likely the confirmation email based on subject and sender
91
+ subject = message.get('subject', '')
92
+ sender = ''
93
+
94
+ # Try to get the sender from different possible fields
95
+ if 'from' in message:
96
+ if isinstance(message['from'], dict):
97
+ sender = message['from'].get('address', '')
98
+ else:
99
+ sender = str(message['from'])
100
+ elif 'sender' in message:
101
+ if isinstance(message['sender'], dict):
102
+ sender = message['sender'].get('address', '')
103
+ else:
104
+ sender = str(message['sender'])
105
+
106
+ # Look for keywords in the subject that indicate this is the confirmation email
107
+ subject_match = any(keyword in subject.lower() for keyword in
108
+ ['welcome', 'confirm', 'verify', 'api', 'key', 'sutra', 'two.ai', 'loops'])
109
+
110
+ # Look for keywords in the sender that indicate this is from Two AI or Loops
111
+ sender_match = any(keyword in sender.lower() for keyword in
112
+ ['two.ai', 'sutra', 'loops.so', 'loops', 'no-reply', 'noreply'])
113
+
114
+ is_confirmation = subject_match or sender_match
115
+
116
+ if is_confirmation:
117
+ pass
118
+ # Try to get the message content from various possible fields
119
+ content = None
120
+
121
+ # Check for body field (seen in the debug output)
122
+ if 'body' in message:
123
+ content = message['body']
124
+ # Check for content.text field
125
+ elif 'content' in message and 'text' in message['content']:
126
+ content = message['content']['text']
127
+ # Check for html field
128
+ elif 'html' in message:
129
+ content = message['html']
130
+ # Check for text field
131
+ elif 'text' in message:
132
+ content = message['text']
133
+
134
+ if not content:
135
+ continue
136
+
137
+ # Look for the API key pattern in the email content
138
+ # First, try to find the API key directly
139
+ api_key_match = re.search(r'sutra_[A-Za-z0-9]{60,70}', content)
140
+
141
+ # If not found, try looking for the key with the label
142
+ if not api_key_match:
143
+ key_section_match = re.search(r'🔑 SUTRA API Key\s*([^\s]+)', content)
144
+ if key_section_match:
145
+ api_key_match = re.search(r'(sutra_[A-Za-z0-9]+)', key_section_match.group(1))
146
+
147
+ # If still not found, try a more general pattern
148
+ if not api_key_match:
149
+ api_key_match = re.search(r'sutra_\S+', content)
150
+
151
+ if api_key_match:
152
+ api_key = api_key_match.group(0)
153
+ break
154
+ if not api_key:
155
+ attempt += 1
156
+ time.sleep(wait_time)
157
+ if not api_key:
158
+ raise Exception("Failed to get API key from confirmation email")
159
+ return api_key
160
+
26
161
  def __init__(
27
162
  self,
28
- api_key: str = None,
29
163
  is_conversation: bool = True,
30
164
  max_tokens: int = 1024,
31
165
  timeout: int = 30,
@@ -35,19 +169,39 @@ class TwoAI(Provider):
35
169
  proxies: dict = {},
36
170
  history_offset: int = 10250,
37
171
  act: str = None,
38
- model: str = "sutra-v2", # Update default model
172
+ model: str = "sutra-v2", # Default model
39
173
  temperature: float = 0.6,
40
174
  system_message: str = "You are a helpful assistant."
41
175
  ):
42
- """Initializes the TwoAI API client."""
176
+ """
177
+ Initializes the TwoAI API client.
178
+
179
+ Args:
180
+ is_conversation: Whether to maintain conversation history.
181
+ max_tokens: Maximum number of tokens to generate.
182
+ timeout: Request timeout in seconds.
183
+ intro: Introduction text for the conversation.
184
+ filepath: Path to save conversation history.
185
+ update_file: Whether to update the conversation history file.
186
+ proxies: Proxy configuration for requests.
187
+ history_offset: Maximum history length in characters.
188
+ act: Persona for the conversation.
189
+ model: Model to use. Must be one of AVAILABLE_MODELS.
190
+ temperature: Temperature for generation (0.0 to 1.0).
191
+ system_message: System message to use for the conversation.
192
+ """
43
193
  if model not in self.AVAILABLE_MODELS:
44
194
  raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
45
- self.url = "https://api.two.app/v2/chat/completions" # Update API endpoint
195
+
196
+ # Always auto-generate API key
197
+ api_key = self.generate_api_key()
198
+
199
+ self.url = "https://api.two.ai/v2/chat/completions" # API endpoint
46
200
  self.headers = {
47
201
  'User-Agent': LitAgent().random(),
48
- 'Accept': 'application/json', # Keep application/json for request, response is text/event-stream
202
+ 'Accept': 'text/event-stream', # For streaming responses
49
203
  'Content-Type': 'application/json',
50
- 'X-Session-Token': api_key,
204
+ 'Authorization': f'Bearer {api_key}', # Using Bearer token authentication
51
205
  'Origin': 'https://chat.two.ai',
52
206
  'Referer': 'https://api.two.app/'
53
207
  }
@@ -64,6 +218,7 @@ class TwoAI(Provider):
64
218
  self.model = model
65
219
  self.temperature = temperature
66
220
  self.system_message = system_message
221
+ self.api_key = api_key
67
222
 
68
223
  self.__available_optimizers = (
69
224
  method
@@ -96,6 +251,19 @@ class TwoAI(Provider):
96
251
  content = delta.get("content")
97
252
  return content if isinstance(content, str) else None
98
253
 
254
+ def encode_image(self, image_path: str) -> str:
255
+ """
256
+ Encode an image file to base64 string.
257
+
258
+ Args:
259
+ image_path: Path to the image file
260
+
261
+ Returns:
262
+ Base64 encoded string of the image
263
+ """
264
+ with open(image_path, "rb") as image_file:
265
+ return base64.b64encode(image_file.read()).decode('utf-8')
266
+
99
267
  def ask(
100
268
  self,
101
269
  prompt: str,
@@ -104,6 +272,7 @@ class TwoAI(Provider):
104
272
  optimizer: str = None,
105
273
  conversationally: bool = False,
106
274
  online_search: bool = True,
275
+ image_path: str = None,
107
276
  ) -> Union[Dict[str, Any], Generator]:
108
277
  conversation_prompt = self.conversation.gen_complete_prompt(prompt)
109
278
  if optimizer:
@@ -112,14 +281,36 @@ class TwoAI(Provider):
112
281
  else:
113
282
  raise Exception(f"Optimizer is not one of {self.__available_optimizers}")
114
283
 
284
+ # Prepare messages with image if provided
285
+ if image_path:
286
+ # Create a message with image content
287
+ image_content = {
288
+ "type": "image_url",
289
+ "image_url": {
290
+ "url": f"data:image/jpeg;base64,{self.encode_image(image_path)}"
291
+ }
292
+ }
293
+ user_message = {
294
+ "role": "user",
295
+ "content": [
296
+ {"type": "text", "text": conversation_prompt},
297
+ image_content
298
+ ]
299
+ }
300
+ else:
301
+ # Text-only message
302
+ user_message = {"role": "user", "content": conversation_prompt}
303
+
304
+ # Prepare the payload
115
305
  payload = {
116
306
  "messages": [
117
307
  *([{"role": "system", "content": self.system_message}] if self.system_message else []),
118
- {"role": "user", "content": conversation_prompt},
308
+ user_message
119
309
  ],
120
310
  "model": self.model,
121
311
  "temperature": self.temperature,
122
312
  "max_tokens": self.max_tokens_to_sample,
313
+ "stream": stream,
123
314
  "extra_body": {
124
315
  "online_search": online_search,
125
316
  }
@@ -208,6 +399,7 @@ class TwoAI(Provider):
208
399
  optimizer: str = None,
209
400
  conversationally: bool = False,
210
401
  online_search: bool = True,
402
+ image_path: str = None,
211
403
  ) -> str:
212
404
  effective_stream = stream if stream is not None else True
213
405
 
@@ -220,6 +412,7 @@ class TwoAI(Provider):
220
412
  optimizer=optimizer,
221
413
  conversationally=conversationally,
222
414
  online_search=online_search,
415
+ image_path=image_path,
223
416
  )
224
417
  for response_dict in gen:
225
418
  yield self.get_message(response_dict) # get_message expects dict
@@ -233,6 +426,7 @@ class TwoAI(Provider):
233
426
  optimizer=optimizer,
234
427
  conversationally=conversationally,
235
428
  online_search=online_search,
429
+ image_path=image_path,
236
430
  )
237
431
  return self.get_message(response_dict) # get_message expects dict
238
432
 
@@ -244,37 +438,38 @@ class TwoAI(Provider):
244
438
 
245
439
 
246
440
  if __name__ == "__main__":
247
- from rich import print
248
- import os
249
-
250
- api_key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJzanl2OHJtZGxDZDFnQ2hQdGxzZHdxUlVteXkyIiwic291cmNlIjoiRmlyZWJhc2UiLCJpYXQiOjE3NDYxMDY0NjksImV4cCI6MTc0NjEwNzM2OX0.o3fprDgsUJwvwCsWr0HfqmVpSBUthHsxqnopfWhtiYc"
251
-
252
- try:
253
- ai = TwoAI(
254
- api_key=api_key,
255
- timeout=60,
256
- model="sutra-r0",
257
- system_message="You are an intelligent AI assistant. Be concise and helpful."
258
- )
259
-
260
- response_stream = ai.chat("write me a poem about AI", stream=True, online_search=True)
261
- full_stream_response = ""
262
- for chunk in response_stream:
263
- print(chunk, end="", flush=True)
264
- full_stream_response += chunk
265
- print("\n[bold green]Stream Test Complete.[/bold green]\n")
266
-
267
- # Optional: Test non-stream
268
- # print("[bold blue]Testing Non-Stream:[/bold blue]")
269
- # non_stream_response = ai.chat("What is the capital of France?", stream=False, online_search=False)
270
- # print(non_stream_response)
271
- # print("[bold green]Non-Stream Test Complete.[/bold green]\n")
272
-
273
-
274
- except exceptions.FailedToGenerateResponseError as e:
275
- print(f"\n[bold red]API Error:[/bold red] {e}")
276
- except ValueError as e:
277
- print(f"\n[bold red]Configuration Error:[/bold red] {e}")
278
- except Exception as e:
279
- print(f"\n[bold red]An unexpected error occurred:[/bold red] {e}")
280
-
441
+ print("-" * 80)
442
+ print(f"{'Model':<50} {'Status':<10} {'Response'}")
443
+ print("-" * 80)
444
+
445
+ for model in TwoAI.AVAILABLE_MODELS:
446
+ try:
447
+ test_ai = TwoAI(model=model, timeout=60)
448
+ # Test stream first
449
+ response_stream = test_ai.chat("Say 'Hello' in one word", stream=True)
450
+ response_text = ""
451
+ print(f"\r{model:<50} {'Streaming...':<10}", end="", flush=True)
452
+ for chunk in response_stream:
453
+ response_text += chunk
454
+ # Optional: print chunks as they arrive for visual feedback
455
+ # print(chunk, end="", flush=True)
456
+
457
+ if response_text and len(response_text.strip()) > 0:
458
+ status = "✓"
459
+ # Clean and truncate response
460
+ clean_text = response_text.strip() # Already decoded in get_message
461
+ display_text = clean_text[:50] + "..." if len(clean_text) > 50 else clean_text
462
+ else:
463
+ status = " (Stream)"
464
+ display_text = "Empty or invalid stream response"
465
+ print(f"\r{model:<50} {status:<10} {display_text}")
466
+
467
+ # Optional: Add non-stream test if needed, but stream test covers basic functionality
468
+ # print(f"\r{model:<50} {'Non-Stream...':<10}", end="", flush=True)
469
+ # response_non_stream = test_ai.chat("Say 'Hi' again", stream=False)
470
+ # if not response_non_stream or len(response_non_stream.strip()) == 0:
471
+ # print(f"\r{model:<50} {'✗ (Non-Stream)':<10} Empty non-stream response")
472
+
473
+
474
+ except Exception as e:
475
+ print(f"\r{model:<50} {'✗':<10} {str(e)}")