webscout 8.2.6__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 (150) hide show
  1. webscout/AIauto.py +1 -1
  2. webscout/AIutel.py +298 -239
  3. webscout/Extra/Act.md +309 -0
  4. webscout/Extra/GitToolkit/gitapi/README.md +110 -0
  5. webscout/Extra/YTToolkit/README.md +375 -0
  6. webscout/Extra/YTToolkit/ytapi/README.md +44 -0
  7. webscout/Extra/YTToolkit/ytapi/extras.py +92 -19
  8. webscout/Extra/autocoder/autocoder.py +309 -114
  9. webscout/Extra/autocoder/autocoder_utiles.py +15 -15
  10. webscout/Extra/gguf.md +430 -0
  11. webscout/Extra/tempmail/README.md +488 -0
  12. webscout/Extra/weather.md +281 -0
  13. webscout/Litlogger/Readme.md +175 -0
  14. webscout/Provider/AISEARCH/DeepFind.py +41 -37
  15. webscout/Provider/AISEARCH/README.md +279 -0
  16. webscout/Provider/AISEARCH/__init__.py +0 -1
  17. webscout/Provider/AISEARCH/genspark_search.py +228 -86
  18. webscout/Provider/AISEARCH/hika_search.py +11 -11
  19. webscout/Provider/AISEARCH/scira_search.py +324 -322
  20. webscout/Provider/AllenAI.py +7 -14
  21. webscout/Provider/Blackboxai.py +518 -74
  22. webscout/Provider/Cloudflare.py +0 -1
  23. webscout/Provider/Deepinfra.py +23 -21
  24. webscout/Provider/Flowith.py +217 -0
  25. webscout/Provider/FreeGemini.py +250 -0
  26. webscout/Provider/GizAI.py +15 -5
  27. webscout/Provider/Glider.py +11 -8
  28. webscout/Provider/HeckAI.py +80 -52
  29. webscout/Provider/Koboldai.py +7 -4
  30. webscout/Provider/LambdaChat.py +2 -2
  31. webscout/Provider/Marcus.py +10 -18
  32. webscout/Provider/OPENAI/BLACKBOXAI.py +735 -0
  33. webscout/Provider/OPENAI/Cloudflare.py +378 -0
  34. webscout/Provider/OPENAI/FreeGemini.py +282 -0
  35. webscout/Provider/OPENAI/NEMOTRON.py +244 -0
  36. webscout/Provider/OPENAI/README.md +1253 -0
  37. webscout/Provider/OPENAI/__init__.py +8 -0
  38. webscout/Provider/OPENAI/ai4chat.py +293 -286
  39. webscout/Provider/OPENAI/api.py +810 -0
  40. webscout/Provider/OPENAI/base.py +217 -14
  41. webscout/Provider/OPENAI/c4ai.py +373 -367
  42. webscout/Provider/OPENAI/chatgpt.py +7 -0
  43. webscout/Provider/OPENAI/chatgptclone.py +7 -0
  44. webscout/Provider/OPENAI/chatsandbox.py +172 -0
  45. webscout/Provider/OPENAI/deepinfra.py +30 -20
  46. webscout/Provider/OPENAI/e2b.py +6 -0
  47. webscout/Provider/OPENAI/exaai.py +7 -0
  48. webscout/Provider/OPENAI/exachat.py +6 -0
  49. webscout/Provider/OPENAI/flowith.py +162 -0
  50. webscout/Provider/OPENAI/freeaichat.py +359 -352
  51. webscout/Provider/OPENAI/glider.py +323 -316
  52. webscout/Provider/OPENAI/groq.py +361 -354
  53. webscout/Provider/OPENAI/heckai.py +30 -64
  54. webscout/Provider/OPENAI/llmchatco.py +8 -0
  55. webscout/Provider/OPENAI/mcpcore.py +7 -0
  56. webscout/Provider/OPENAI/multichat.py +8 -0
  57. webscout/Provider/OPENAI/netwrck.py +356 -350
  58. webscout/Provider/OPENAI/opkfc.py +8 -0
  59. webscout/Provider/OPENAI/scirachat.py +471 -462
  60. webscout/Provider/OPENAI/sonus.py +9 -0
  61. webscout/Provider/OPENAI/standardinput.py +9 -1
  62. webscout/Provider/OPENAI/textpollinations.py +339 -329
  63. webscout/Provider/OPENAI/toolbaz.py +7 -0
  64. webscout/Provider/OPENAI/typefully.py +355 -0
  65. webscout/Provider/OPENAI/typegpt.py +358 -346
  66. webscout/Provider/OPENAI/uncovrAI.py +7 -0
  67. webscout/Provider/OPENAI/utils.py +103 -7
  68. webscout/Provider/OPENAI/venice.py +12 -0
  69. webscout/Provider/OPENAI/wisecat.py +19 -19
  70. webscout/Provider/OPENAI/writecream.py +7 -0
  71. webscout/Provider/OPENAI/x0gpt.py +7 -0
  72. webscout/Provider/OPENAI/yep.py +50 -21
  73. webscout/Provider/OpenGPT.py +1 -1
  74. webscout/Provider/TTI/AiForce/README.md +159 -0
  75. webscout/Provider/TTI/FreeAIPlayground/README.md +99 -0
  76. webscout/Provider/TTI/ImgSys/README.md +174 -0
  77. webscout/Provider/TTI/MagicStudio/README.md +101 -0
  78. webscout/Provider/TTI/Nexra/README.md +155 -0
  79. webscout/Provider/TTI/PollinationsAI/README.md +146 -0
  80. webscout/Provider/TTI/README.md +128 -0
  81. webscout/Provider/TTI/aiarta/README.md +134 -0
  82. webscout/Provider/TTI/artbit/README.md +100 -0
  83. webscout/Provider/TTI/fastflux/README.md +129 -0
  84. webscout/Provider/TTI/huggingface/README.md +114 -0
  85. webscout/Provider/TTI/piclumen/README.md +161 -0
  86. webscout/Provider/TTI/pixelmuse/README.md +79 -0
  87. webscout/Provider/TTI/talkai/README.md +139 -0
  88. webscout/Provider/TTS/README.md +192 -0
  89. webscout/Provider/TTS/__init__.py +2 -1
  90. webscout/Provider/TTS/speechma.py +500 -100
  91. webscout/Provider/TTS/sthir.py +94 -0
  92. webscout/Provider/TeachAnything.py +3 -7
  93. webscout/Provider/TextPollinationsAI.py +4 -2
  94. webscout/Provider/{aimathgpt.py → UNFINISHED/ChatHub.py} +88 -68
  95. webscout/Provider/UNFINISHED/liner_api_request.py +263 -0
  96. webscout/Provider/UNFINISHED/oivscode.py +351 -0
  97. webscout/Provider/UNFINISHED/test_lmarena.py +119 -0
  98. webscout/Provider/Writecream.py +11 -2
  99. webscout/Provider/__init__.py +8 -14
  100. webscout/Provider/ai4chat.py +4 -58
  101. webscout/Provider/asksteve.py +17 -9
  102. webscout/Provider/cerebras.py +3 -1
  103. webscout/Provider/koala.py +170 -268
  104. webscout/Provider/llmchat.py +3 -0
  105. webscout/Provider/lmarena.py +198 -0
  106. webscout/Provider/meta.py +7 -4
  107. webscout/Provider/samurai.py +223 -0
  108. webscout/Provider/scira_chat.py +4 -2
  109. webscout/Provider/typefully.py +23 -151
  110. webscout/__init__.py +4 -2
  111. webscout/cli.py +3 -28
  112. webscout/conversation.py +35 -35
  113. webscout/litagent/Readme.md +276 -0
  114. webscout/scout/README.md +402 -0
  115. webscout/swiftcli/Readme.md +323 -0
  116. webscout/version.py +1 -1
  117. webscout/webscout_search.py +2 -182
  118. webscout/webscout_search_async.py +1 -179
  119. webscout/zeroart/README.md +89 -0
  120. webscout/zeroart/__init__.py +134 -54
  121. webscout/zeroart/base.py +19 -13
  122. webscout/zeroart/effects.py +101 -99
  123. webscout/zeroart/fonts.py +1239 -816
  124. {webscout-8.2.6.dist-info → webscout-8.2.8.dist-info}/METADATA +116 -74
  125. {webscout-8.2.6.dist-info → webscout-8.2.8.dist-info}/RECORD +130 -103
  126. {webscout-8.2.6.dist-info → webscout-8.2.8.dist-info}/WHEEL +1 -1
  127. webscout-8.2.8.dist-info/entry_points.txt +3 -0
  128. webscout-8.2.8.dist-info/top_level.txt +1 -0
  129. webscout/Provider/AISEARCH/ISou.py +0 -256
  130. webscout/Provider/ElectronHub.py +0 -773
  131. webscout/Provider/Free2GPT.py +0 -241
  132. webscout/Provider/GPTWeb.py +0 -249
  133. webscout/Provider/bagoodex.py +0 -145
  134. webscout/Provider/geminiprorealtime.py +0 -160
  135. webscout/scout/core.py +0 -881
  136. webscout-8.2.6.dist-info/entry_points.txt +0 -3
  137. webscout-8.2.6.dist-info/top_level.txt +0 -2
  138. webstoken/__init__.py +0 -30
  139. webstoken/classifier.py +0 -189
  140. webstoken/keywords.py +0 -216
  141. webstoken/language.py +0 -128
  142. webstoken/ner.py +0 -164
  143. webstoken/normalizer.py +0 -35
  144. webstoken/processor.py +0 -77
  145. webstoken/sentiment.py +0 -206
  146. webstoken/stemmer.py +0 -73
  147. webstoken/tagger.py +0 -60
  148. webstoken/tokenizer.py +0 -158
  149. /webscout/Provider/{Youchat.py → UNFINISHED/Youchat.py} +0 -0
  150. {webscout-8.2.6.dist-info → webscout-8.2.8.dist-info}/licenses/LICENSE.md +0 -0
@@ -156,8 +156,8 @@ class AskSteve(Provider):
156
156
  self.conversation.update_chat_history(
157
157
  prompt, self.get_message(self.last_response)
158
158
  )
159
- # Return dict or raw string based on raw flag
160
- return text if raw else self.last_response
159
+ # Always return a dict for consistency
160
+ return {"text": text} if raw else self.last_response
161
161
 
162
162
  except CurlError as e:
163
163
  raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}") from e
@@ -181,7 +181,6 @@ class AskSteve(Provider):
181
181
  str: Response generated
182
182
  """
183
183
 
184
- # Since ask() doesn't truly stream, we just call it once.
185
184
  response_data = self.ask(
186
185
  prompt,
187
186
  stream=False, # Always False for this API
@@ -189,19 +188,28 @@ class AskSteve(Provider):
189
188
  optimizer=optimizer,
190
189
  conversationally=conversationally,
191
190
  )
192
- return self.get_message(response_data)
193
-
194
- def get_message(self, response: dict) -> str:
191
+ if stream:
192
+ def stream_wrapper():
193
+ yield self.get_message(response_data)
194
+ return stream_wrapper()
195
+ else:
196
+ return self.get_message(response_data)
197
+
198
+ def get_message(self, response) -> str:
195
199
  """Retrieves message only from response
196
200
 
197
201
  Args:
198
- response (dict): Response generated by `self.ask`
202
+ response (dict or str): Response generated by `self.ask` or a string
199
203
 
200
204
  Returns:
201
205
  str: Message extracted
202
206
  """
203
- assert isinstance(response, dict), "Response should be of dict data-type only"
204
- return response.get("text", "") # Use .get for safety
207
+ if isinstance(response, dict):
208
+ return response.get("text", "") # Use .get for safety
209
+ elif isinstance(response, str):
210
+ return response
211
+ else:
212
+ raise TypeError(f"Unsupported response type: {type(response)}")
205
213
 
206
214
 
207
215
  if __name__ == "__main__":
@@ -19,7 +19,9 @@ class Cerebras(Provider):
19
19
  "llama3.1-8b",
20
20
  "llama-3.3-70b",
21
21
  "deepseek-r1-distill-llama-70b",
22
- "llama-4-scout-17b-16e-instruct"
22
+ "llama-4-scout-17b-16e-instruct",
23
+ "qwen-3-32b",
24
+
23
25
 
24
26
  ]
25
27
 
@@ -1,268 +1,170 @@
1
- import requests
2
- import json
3
- from typing import Union, Any, Dict, Optional
4
- from webscout.AIutel import Optimizers
5
- from webscout.AIutel import Conversation
6
- from webscout.AIutel import AwesomePrompts, sanitize_stream
7
- from webscout.AIbase import Provider
8
- from webscout import exceptions
9
-
10
- class KOALA(Provider):
11
- """
12
- A class to interact with the Koala.sh API.
13
- """
14
-
15
- AVAILABLE_MODELS = [
16
- "gpt-4.1-mini",
17
- "gpt-4.1",
18
- ]
19
-
20
- def __init__(
21
- self,
22
- is_conversation: bool = True,
23
- max_tokens: int = 600,
24
- timeout: int = 30,
25
- intro: str = None,
26
- filepath: str = None,
27
- update_file: bool = True,
28
- proxies: dict = {},
29
- history_offset: int = 10250,
30
- act: str = None,
31
- model: str = "gpt-4.1",
32
- web_search: bool = True,
33
-
34
- ) -> None:
35
- """
36
- Initializes the KOALASH API with given parameters.
37
-
38
- Args:
39
- is_conversation (bool, optional): Flag for chatting conversationally. Defaults to True.
40
- max_tokens (int, optional): Maximum number of tokens to be generated upon completion.
41
- Defaults to 600.
42
- timeout (int, optional): Http request timeout. Defaults to 30.
43
- intro (str, optional): Conversation introductory prompt. Defaults to None.
44
- filepath (str, optional): Path to file containing conversation history. Defaults to None.
45
- update_file (bool, optional): Add new prompts and responses to the file. Defaults to True.
46
- proxies (dict, optional): Http request proxies. Defaults to {}.
47
- history_offset (int, optional): Limit conversation history to this number of last texts.
48
- Defaults to 10250.
49
- act (str|int, optional): Awesome prompt key or index. (Used as intro). Defaults to None.
50
- model (str, optional): AI model to use. Defaults to "gpt-4o-mini".
51
- """
52
- if model not in self.AVAILABLE_MODELS:
53
- raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
54
-
55
- self.session = requests.Session()
56
- self.is_conversation = is_conversation
57
- self.max_tokens_to_sample = max_tokens
58
- self.api_endpoint = "https://koala.sh/api/gpt/"
59
- self.stream_chunk_size = 64
60
- self.timeout = timeout
61
- self.last_response = {}
62
- self.model = model
63
- self.headers = {
64
- "accept": "text/event-stream",
65
- "accept-encoding": "gzip, deflate, br, zstd",
66
- "accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
67
- "content-length": "73",
68
- "content-type": "application/json",
69
- "dnt": "1",
70
- "flag-real-time-data": "true" if web_search else "false",
71
- "origin": "https://koala.sh",
72
- "priority": "u=1, i",
73
- "referer": "https://koala.sh/chat",
74
- "sec-ch-ua": '"Not)A;Brand";v="99", "Microsoft Edge";v="127", "Chromium";v="127"',
75
- "sec-ch-ua-mobile": "?0",
76
- "sec-ch-ua-platform": '"Windows"',
77
- "sec-fetch-dest": "empty",
78
- "sec-fetch-mode": "cors",
79
- "sec-fetch-site": "same-origin",
80
- "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 Edg/127.0.0.0",
81
- }
82
-
83
- self.__available_optimizers = (
84
- method
85
- for method in dir(Optimizers)
86
- if callable(getattr(Optimizers, method)) and not method.startswith("__")
87
- )
88
- self.session.headers.update(self.headers)
89
- Conversation.intro = (
90
- AwesomePrompts().get_act(
91
- act, raise_not_found=True, default=None, case_insensitive=True
92
- )
93
- if act
94
- else intro or Conversation.intro
95
- )
96
- self.conversation = Conversation(
97
- is_conversation, self.max_tokens_to_sample, filepath, update_file
98
- )
99
- self.conversation.history_offset = history_offset
100
- self.session.proxies = proxies
101
-
102
- def ask(
103
- self,
104
- prompt: str,
105
- stream: bool = False,
106
- raw: bool = False,
107
- optimizer: str = None,
108
- conversationally: bool = False,
109
- ) -> Dict[str, Any]:
110
- """
111
- Sends a prompt to the Koala.sh API and returns the response.
112
-
113
- Args:
114
- prompt: The text prompt to generate text from.
115
- stream (bool, optional): Whether to stream the response. Defaults to False.
116
- raw (bool, optional): Whether to return the raw response. Defaults to False.
117
- optimizer (str, optional): The name of the optimizer to use. Defaults to None.
118
- conversationally (bool, optional): Whether to chat conversationally. Defaults to False.
119
-
120
- Returns:
121
- The response from the API.
122
- """
123
- conversation_prompt = self.conversation.gen_complete_prompt(prompt)
124
- if optimizer:
125
- if optimizer in self.__available_optimizers:
126
- conversation_prompt = getattr(Optimizers, optimizer)(
127
- conversation_prompt if conversationally else prompt
128
- )
129
- else:
130
- raise Exception(
131
- f"Optimizer is not one of {self.__available_optimizers}"
132
- )
133
-
134
- payload = {
135
- "input": conversation_prompt,
136
- "model": self.model
137
- }
138
-
139
- def for_stream():
140
- response = self.session.post(
141
- self.api_endpoint, json=payload, headers=self.headers, stream=True, timeout=self.timeout
142
- )
143
-
144
- if not response.ok:
145
- raise exceptions.FailedToGenerateResponseError(
146
- f"Failed to generate response - ({response.status_code}, {response.reason})"
147
- )
148
-
149
- streaming_response = ""
150
- for line in response.iter_lines(decode_unicode=True):
151
- if line:
152
- if line.startswith("data:"):
153
- data = line[len("data:"):].strip()
154
- if data:
155
- try:
156
- event = json.loads(data)
157
- streaming_response += event.get("choices", [{}])[0].get("delta", {}).get("content", "")
158
- yield event if raw else dict(text=streaming_response)
159
- except json.decoder.JSONDecodeError:
160
- continue
161
- self.last_response.update(dict(text=streaming_response))
162
- self.conversation.update_chat_history(
163
- prompt, self.get_message(self.last_response)
164
- )
165
- def for_non_stream():
166
- response = self.session.post(
167
- self.api_endpoint, json=payload, headers=self.headers, timeout=self.timeout
168
- )
169
-
170
- if not response.ok:
171
- raise exceptions.FailedToGenerateResponseError(
172
- f"Failed to generate response - ({response.status_code}, {response.reason})"
173
- )
174
-
175
- response_content = response.content.decode('utf-8')
176
- data_parts = response_content.strip().split('\n\n')
177
- formatted_response = ''.join([part.replace('data: ', '') for part in data_parts if part.startswith('data: ')])
178
-
179
- # Remove extra quotes from the formatted response
180
- formatted_response = formatted_response.replace('""', '')
181
-
182
- # Split the response into lines and format with new lines before headers
183
- lines = formatted_response.split('\n')
184
- formatted_lines = []
185
- for line in lines:
186
- if line.startswith('###'):
187
- formatted_lines.append('\n' + line)
188
- else:
189
- formatted_lines.append(line)
190
-
191
- # Join the formatted lines back into a single string
192
- final_response = '\n'.join(formatted_lines)
193
-
194
- # self.last_response.update(dict(text=streaming_response))
195
- self.conversation.update_chat_history(
196
- prompt, final_response
197
- )
198
- return dict(text=final_response)
199
-
200
- return for_stream() if stream else for_non_stream()
201
-
202
- def chat(
203
- self,
204
- prompt: str,
205
- stream: bool = False,
206
- optimizer: str = None,
207
- conversationally: bool = False,
208
- ) -> str:
209
- """Generate response `str`
210
- Args:
211
- prompt (str): Prompt to be send.
212
- stream (bool, optional): Flag for streaming response. Defaults to False.
213
- optimizer (str, optional): Prompt optimizer name - `[code, shell_command]`. Defaults to None.
214
- conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
215
- Returns:
216
- str: Response generated
217
- """
218
-
219
- def for_stream():
220
- for response in self.ask(
221
- prompt, True, optimizer=optimizer, conversationally=conversationally
222
- ):
223
- yield self.get_message(response)
224
-
225
- def for_non_stream():
226
- return self.get_message(
227
- self.ask(
228
- prompt,
229
- False,
230
- optimizer=optimizer,
231
- conversationally=conversationally,
232
- )
233
- )
234
-
235
- return for_stream() if stream else for_non_stream()
236
-
237
- def get_message(self, response: dict) -> str:
238
- """Retrieves message only from response
239
-
240
- Args:
241
- response (dict): Response generated by `self.ask`
242
-
243
- Returns:
244
- str: Message extracted
245
- """
246
- assert isinstance(response, dict), "Response should be of dict data-type only"
247
- return response["text"].replace('\\n', '\n').replace('\\n\\n', '\n\n')
248
- if __name__ == '__main__':
249
- print("-" * 80)
250
- print(f"{'Model':<50} {'Status':<10} {'Response'}")
251
- print("-" * 80)
252
-
253
- for model in KOALA.AVAILABLE_MODELS:
254
- try:
255
- test_ai = KOALA(model=model, timeout=60)
256
- response = test_ai.chat("Say 'Hello' in one word")
257
- response_text = response
258
-
259
- if response_text and len(response_text.strip()) > 0:
260
- status = "✓"
261
- # Truncate response if too long
262
- display_text = response_text.strip()[:50] + "..." if len(response_text.strip()) > 50 else response_text.strip()
263
- else:
264
- status = "✗"
265
- display_text = "Empty or invalid response"
266
- print(f"{model:<50} {status:<10} {display_text}")
267
- except Exception as e:
268
- print(f"{model:<50} {'✗':<10} {str(e)}")
1
+ import requests
2
+ import re
3
+ from typing import Optional, Union, Any, Dict, Generator
4
+ from uuid import uuid4
5
+
6
+ from webscout.AIutel import Optimizers, Conversation, AwesomePrompts
7
+ from webscout.AIbase import Provider
8
+ from webscout import exceptions
9
+
10
+ class KOALA(Provider):
11
+ """
12
+ A class to interact with the Koala.sh API, X0GPT-style, without sanitize_stream.
13
+ """
14
+ AVAILABLE_MODELS = [
15
+ "gpt-4.1-mini",
16
+ "gpt-4.1",
17
+ ]
18
+
19
+ def __init__(
20
+ self,
21
+ is_conversation: bool = True,
22
+ max_tokens: int = 600,
23
+ timeout: int = 30,
24
+ intro: str = None,
25
+ filepath: str = None,
26
+ update_file: bool = True,
27
+ proxies: dict = {},
28
+ history_offset: int = 10250,
29
+ act: str = None,
30
+ model: str = "gpt-4.1",
31
+ web_search: bool = True,
32
+ ):
33
+ if model not in self.AVAILABLE_MODELS:
34
+ raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
35
+ self.session = requests.Session()
36
+ self.is_conversation = is_conversation
37
+ self.max_tokens_to_sample = max_tokens
38
+ self.api_endpoint = "https://koala.sh/api/gpt/"
39
+ self.timeout = timeout
40
+ self.last_response = {}
41
+ self.model = model
42
+ self.headers = {
43
+ "accept": "text/event-stream",
44
+ "accept-encoding": "gzip, deflate, br, zstd",
45
+ "accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
46
+ "content-type": "application/json",
47
+ "dnt": "1",
48
+ "flag-real-time-data": "true" if web_search else "false",
49
+ "origin": "https://koala.sh",
50
+ "referer": "https://koala.sh/chat",
51
+ "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 Edg/127.0.0.0",
52
+ }
53
+ self.session.headers.update(self.headers)
54
+ Conversation.intro = (
55
+ AwesomePrompts().get_act(
56
+ act, raise_not_found=True, default=None, case_insensitive=True
57
+ )
58
+ if act
59
+ else intro or Conversation.intro
60
+ )
61
+ self.conversation = Conversation(
62
+ is_conversation, self.max_tokens_to_sample, filepath, update_file
63
+ )
64
+ self.conversation.history_offset = history_offset
65
+ self.session.proxies = proxies
66
+
67
+ @staticmethod
68
+ def _koala_extractor(line: str) -> Optional[str]:
69
+ # Koala returns lines like: data: "Hello" or data: "..."
70
+ match = re.match(r'data:\s*"(.*)"', line)
71
+ if match:
72
+ return match.group(1)
73
+ return None
74
+
75
+ def ask(
76
+ self,
77
+ prompt: str,
78
+ stream: bool = False,
79
+ raw: bool = False,
80
+ optimizer: str = None,
81
+ conversationally: bool = False,
82
+ ) -> Dict[str, Any]:
83
+ conversation_prompt = self.conversation.gen_complete_prompt(prompt)
84
+ if optimizer:
85
+ if hasattr(Optimizers, optimizer):
86
+ conversation_prompt = getattr(Optimizers, optimizer)(
87
+ conversation_prompt if conversationally else prompt
88
+ )
89
+ else:
90
+ raise Exception(f"Optimizer is not valid.")
91
+ payload = {
92
+ "input": conversation_prompt,
93
+ "inputHistory": [],
94
+ "outputHistory": [],
95
+ "model": self.model
96
+ }
97
+ def for_stream():
98
+ response = self.session.post(
99
+ self.api_endpoint, json=payload, headers=self.headers, stream=True, timeout=self.timeout
100
+ )
101
+ if not response.ok:
102
+ raise exceptions.FailedToGenerateResponseError(
103
+ f"Failed to generate response - ({response.status_code}, {response.reason})"
104
+ )
105
+ streaming_response = ""
106
+ for line in response.iter_lines(decode_unicode=True):
107
+ if not line:
108
+ continue
109
+ # Only process lines starting with data:
110
+ if line.startswith("data:"):
111
+ content = self._koala_extractor(line)
112
+ if content and content.strip():
113
+ streaming_response += content
114
+ yield dict(text=content) if not raw else content
115
+ # Only update chat history if response is not empty
116
+ if streaming_response.strip():
117
+ self.last_response = dict(text=streaming_response)
118
+ self.conversation.update_chat_history(
119
+ prompt, self.get_message(self.last_response)
120
+ )
121
+ def for_non_stream():
122
+ # Use streaming logic to collect the full response
123
+ full_text = ""
124
+ for chunk in for_stream():
125
+ if isinstance(chunk, dict):
126
+ full_text += chunk.get("text", "")
127
+ elif isinstance(chunk, str):
128
+ full_text += chunk
129
+ # Only update chat history if response is not empty
130
+ if full_text.strip():
131
+ self.last_response = dict(text=full_text)
132
+ self.conversation.update_chat_history(
133
+ prompt, self.get_message(self.last_response)
134
+ )
135
+ return self.last_response
136
+ return for_stream() if stream else for_non_stream()
137
+
138
+ def chat(
139
+ self,
140
+ prompt: str,
141
+ stream: bool = False,
142
+ optimizer: str = None,
143
+ conversationally: bool = False,
144
+ ) -> Union[str, Generator[str, None, None]]:
145
+ def for_stream():
146
+ for response in self.ask(
147
+ prompt, True, optimizer=optimizer, conversationally=conversationally
148
+ ):
149
+ yield self.get_message(response)
150
+ def for_non_stream():
151
+ return self.get_message(
152
+ self.ask(
153
+ prompt,
154
+ False,
155
+ optimizer=optimizer,
156
+ conversationally=conversationally,
157
+ )
158
+ )
159
+ return for_stream() if stream else for_non_stream()
160
+
161
+ def get_message(self, response: dict) -> str:
162
+ assert isinstance(response, dict), "Response should be of dict data-type only"
163
+ return response.get("text", "")
164
+
165
+ if __name__ == "__main__":
166
+ from rich import print
167
+ ai = KOALA(timeout=60)
168
+ response = ai.chat("Say 'Hello' in one word", stream=True)
169
+ for chunk in response:
170
+ print(chunk, end="", flush=True)
@@ -22,6 +22,9 @@ class LLMChat(Provider):
22
22
  "@cf/meta/llama-3.2-1b-instruct",
23
23
  "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
24
24
  "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
25
+ "@cf/meta/llama-4-scout-17b-16e-instruct",
26
+ "@cf/mistralai/mistral-small-3.1-24b-instruct",
27
+ "@cf/google/gemma-3-12b-it",
25
28
  ]
26
29
 
27
30
  def __init__(