webscout 8.2.8__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 (184) hide show
  1. webscout/AIauto.py +32 -14
  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 +153 -35
  22. webscout/Provider/Deepinfra.py +339 -339
  23. webscout/Provider/ExaChat.py +358 -358
  24. webscout/Provider/Gemini.py +169 -169
  25. webscout/Provider/GithubChat.py +1 -2
  26. webscout/Provider/Glider.py +3 -3
  27. webscout/Provider/HeckAI.py +171 -81
  28. webscout/Provider/OPENAI/BLACKBOXAI.py +766 -735
  29. webscout/Provider/OPENAI/Cloudflare.py +7 -7
  30. webscout/Provider/OPENAI/FreeGemini.py +6 -5
  31. webscout/Provider/OPENAI/NEMOTRON.py +8 -20
  32. webscout/Provider/OPENAI/Qwen3.py +283 -0
  33. webscout/Provider/OPENAI/README.md +952 -1253
  34. webscout/Provider/OPENAI/TwoAI.py +357 -0
  35. webscout/Provider/OPENAI/__init__.py +5 -1
  36. webscout/Provider/OPENAI/ai4chat.py +40 -40
  37. webscout/Provider/OPENAI/api.py +808 -649
  38. webscout/Provider/OPENAI/c4ai.py +3 -3
  39. webscout/Provider/OPENAI/chatgpt.py +555 -555
  40. webscout/Provider/OPENAI/chatgptclone.py +493 -487
  41. webscout/Provider/OPENAI/chatsandbox.py +4 -3
  42. webscout/Provider/OPENAI/copilot.py +242 -0
  43. webscout/Provider/OPENAI/deepinfra.py +5 -2
  44. webscout/Provider/OPENAI/e2b.py +63 -5
  45. webscout/Provider/OPENAI/exaai.py +416 -410
  46. webscout/Provider/OPENAI/exachat.py +444 -443
  47. webscout/Provider/OPENAI/freeaichat.py +2 -2
  48. webscout/Provider/OPENAI/glider.py +5 -2
  49. webscout/Provider/OPENAI/groq.py +5 -2
  50. webscout/Provider/OPENAI/heckai.py +308 -307
  51. webscout/Provider/OPENAI/mcpcore.py +8 -2
  52. webscout/Provider/OPENAI/multichat.py +4 -4
  53. webscout/Provider/OPENAI/netwrck.py +6 -5
  54. webscout/Provider/OPENAI/oivscode.py +287 -0
  55. webscout/Provider/OPENAI/opkfc.py +496 -496
  56. webscout/Provider/OPENAI/pydantic_imports.py +172 -0
  57. webscout/Provider/OPENAI/scirachat.py +15 -9
  58. webscout/Provider/OPENAI/sonus.py +304 -303
  59. webscout/Provider/OPENAI/standardinput.py +433 -433
  60. webscout/Provider/OPENAI/textpollinations.py +4 -4
  61. webscout/Provider/OPENAI/toolbaz.py +413 -413
  62. webscout/Provider/OPENAI/typefully.py +3 -3
  63. webscout/Provider/OPENAI/typegpt.py +11 -5
  64. webscout/Provider/OPENAI/uncovrAI.py +463 -462
  65. webscout/Provider/OPENAI/utils.py +90 -79
  66. webscout/Provider/OPENAI/venice.py +431 -425
  67. webscout/Provider/OPENAI/wisecat.py +387 -381
  68. webscout/Provider/OPENAI/writecream.py +3 -3
  69. webscout/Provider/OPENAI/x0gpt.py +365 -378
  70. webscout/Provider/OPENAI/yep.py +39 -13
  71. webscout/Provider/TTI/README.md +55 -101
  72. webscout/Provider/TTI/__init__.py +4 -9
  73. webscout/Provider/TTI/aiarta.py +365 -0
  74. webscout/Provider/TTI/artbit.py +0 -0
  75. webscout/Provider/TTI/base.py +64 -0
  76. webscout/Provider/TTI/fastflux.py +200 -0
  77. webscout/Provider/TTI/magicstudio.py +201 -0
  78. webscout/Provider/TTI/piclumen.py +203 -0
  79. webscout/Provider/TTI/pixelmuse.py +225 -0
  80. webscout/Provider/TTI/pollinations.py +221 -0
  81. webscout/Provider/TTI/utils.py +11 -0
  82. webscout/Provider/TTS/__init__.py +2 -1
  83. webscout/Provider/TTS/base.py +159 -159
  84. webscout/Provider/TTS/openai_fm.py +129 -0
  85. webscout/Provider/TextPollinationsAI.py +308 -308
  86. webscout/Provider/TwoAI.py +239 -44
  87. webscout/Provider/UNFINISHED/Youchat.py +330 -330
  88. webscout/Provider/UNFINISHED/puterjs.py +635 -0
  89. webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
  90. webscout/Provider/Writecream.py +246 -246
  91. webscout/Provider/__init__.py +2 -0
  92. webscout/Provider/ai4chat.py +33 -8
  93. webscout/Provider/koala.py +169 -169
  94. webscout/Provider/oivscode.py +309 -0
  95. webscout/Provider/samurai.py +3 -2
  96. webscout/Provider/typegpt.py +3 -3
  97. webscout/Provider/uncovr.py +368 -368
  98. webscout/client.py +70 -0
  99. webscout/litprinter/__init__.py +58 -58
  100. webscout/optimizers.py +419 -419
  101. webscout/scout/README.md +3 -1
  102. webscout/scout/core/crawler.py +134 -64
  103. webscout/scout/core/scout.py +148 -109
  104. webscout/scout/element.py +106 -88
  105. webscout/swiftcli/Readme.md +323 -323
  106. webscout/swiftcli/plugins/manager.py +9 -2
  107. webscout/version.py +1 -1
  108. webscout/zeroart/__init__.py +134 -134
  109. webscout/zeroart/effects.py +100 -100
  110. webscout/zeroart/fonts.py +1238 -1238
  111. {webscout-8.2.8.dist-info → webscout-8.2.9.dist-info}/METADATA +159 -35
  112. {webscout-8.2.8.dist-info → webscout-8.2.9.dist-info}/RECORD +116 -161
  113. {webscout-8.2.8.dist-info → webscout-8.2.9.dist-info}/WHEEL +1 -1
  114. {webscout-8.2.8.dist-info → webscout-8.2.9.dist-info}/entry_points.txt +1 -0
  115. webscout/Litlogger/Readme.md +0 -175
  116. webscout/Litlogger/core/__init__.py +0 -6
  117. webscout/Litlogger/core/level.py +0 -23
  118. webscout/Litlogger/core/logger.py +0 -165
  119. webscout/Litlogger/handlers/__init__.py +0 -12
  120. webscout/Litlogger/handlers/console.py +0 -33
  121. webscout/Litlogger/handlers/file.py +0 -143
  122. webscout/Litlogger/handlers/network.py +0 -173
  123. webscout/Litlogger/styles/__init__.py +0 -7
  124. webscout/Litlogger/styles/colors.py +0 -249
  125. webscout/Litlogger/styles/formats.py +0 -458
  126. webscout/Litlogger/styles/text.py +0 -87
  127. webscout/Litlogger/utils/__init__.py +0 -6
  128. webscout/Litlogger/utils/detectors.py +0 -153
  129. webscout/Litlogger/utils/formatters.py +0 -200
  130. webscout/Provider/TTI/AiForce/README.md +0 -159
  131. webscout/Provider/TTI/AiForce/__init__.py +0 -22
  132. webscout/Provider/TTI/AiForce/async_aiforce.py +0 -224
  133. webscout/Provider/TTI/AiForce/sync_aiforce.py +0 -245
  134. webscout/Provider/TTI/FreeAIPlayground/README.md +0 -99
  135. webscout/Provider/TTI/FreeAIPlayground/__init__.py +0 -9
  136. webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +0 -181
  137. webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +0 -180
  138. webscout/Provider/TTI/ImgSys/README.md +0 -174
  139. webscout/Provider/TTI/ImgSys/__init__.py +0 -23
  140. webscout/Provider/TTI/ImgSys/async_imgsys.py +0 -202
  141. webscout/Provider/TTI/ImgSys/sync_imgsys.py +0 -195
  142. webscout/Provider/TTI/MagicStudio/README.md +0 -101
  143. webscout/Provider/TTI/MagicStudio/__init__.py +0 -2
  144. webscout/Provider/TTI/MagicStudio/async_magicstudio.py +0 -111
  145. webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +0 -109
  146. webscout/Provider/TTI/Nexra/README.md +0 -155
  147. webscout/Provider/TTI/Nexra/__init__.py +0 -22
  148. webscout/Provider/TTI/Nexra/async_nexra.py +0 -286
  149. webscout/Provider/TTI/Nexra/sync_nexra.py +0 -258
  150. webscout/Provider/TTI/PollinationsAI/README.md +0 -146
  151. webscout/Provider/TTI/PollinationsAI/__init__.py +0 -23
  152. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +0 -311
  153. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +0 -265
  154. webscout/Provider/TTI/aiarta/README.md +0 -134
  155. webscout/Provider/TTI/aiarta/__init__.py +0 -2
  156. webscout/Provider/TTI/aiarta/async_aiarta.py +0 -482
  157. webscout/Provider/TTI/aiarta/sync_aiarta.py +0 -440
  158. webscout/Provider/TTI/artbit/README.md +0 -100
  159. webscout/Provider/TTI/artbit/__init__.py +0 -22
  160. webscout/Provider/TTI/artbit/async_artbit.py +0 -155
  161. webscout/Provider/TTI/artbit/sync_artbit.py +0 -148
  162. webscout/Provider/TTI/fastflux/README.md +0 -129
  163. webscout/Provider/TTI/fastflux/__init__.py +0 -22
  164. webscout/Provider/TTI/fastflux/async_fastflux.py +0 -261
  165. webscout/Provider/TTI/fastflux/sync_fastflux.py +0 -252
  166. webscout/Provider/TTI/huggingface/README.md +0 -114
  167. webscout/Provider/TTI/huggingface/__init__.py +0 -22
  168. webscout/Provider/TTI/huggingface/async_huggingface.py +0 -199
  169. webscout/Provider/TTI/huggingface/sync_huggingface.py +0 -195
  170. webscout/Provider/TTI/piclumen/README.md +0 -161
  171. webscout/Provider/TTI/piclumen/__init__.py +0 -23
  172. webscout/Provider/TTI/piclumen/async_piclumen.py +0 -268
  173. webscout/Provider/TTI/piclumen/sync_piclumen.py +0 -233
  174. webscout/Provider/TTI/pixelmuse/README.md +0 -79
  175. webscout/Provider/TTI/pixelmuse/__init__.py +0 -4
  176. webscout/Provider/TTI/pixelmuse/async_pixelmuse.py +0 -249
  177. webscout/Provider/TTI/pixelmuse/sync_pixelmuse.py +0 -182
  178. webscout/Provider/TTI/talkai/README.md +0 -139
  179. webscout/Provider/TTI/talkai/__init__.py +0 -4
  180. webscout/Provider/TTI/talkai/async_talkai.py +0 -229
  181. webscout/Provider/TTI/talkai/sync_talkai.py +0 -207
  182. webscout/Provider/UNFINISHED/oivscode.py +0 -351
  183. {webscout-8.2.8.dist-info → webscout-8.2.9.dist-info}/licenses/LICENSE.md +0 -0
  184. {webscout-8.2.8.dist-info → webscout-8.2.9.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)}")