webscout 8.3__py3-none-any.whl → 8.3.1__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 (62) hide show
  1. webscout/AIauto.py +4 -4
  2. webscout/AIbase.py +61 -1
  3. webscout/Extra/YTToolkit/ytapi/patterns.py +45 -45
  4. webscout/Extra/YTToolkit/ytapi/stream.py +1 -1
  5. webscout/Extra/YTToolkit/ytapi/video.py +10 -10
  6. webscout/Extra/autocoder/autocoder_utiles.py +1 -1
  7. webscout/Litlogger/formats.py +9 -0
  8. webscout/Litlogger/handlers.py +18 -0
  9. webscout/Litlogger/logger.py +43 -1
  10. webscout/Provider/AISEARCH/scira_search.py +3 -2
  11. webscout/Provider/LambdaChat.py +7 -1
  12. webscout/Provider/OPENAI/BLACKBOXAI.py +1049 -1017
  13. webscout/Provider/OPENAI/Qwen3.py +303 -303
  14. webscout/Provider/OPENAI/README.md +3 -0
  15. webscout/Provider/OPENAI/TogetherAI.py +355 -0
  16. webscout/Provider/OPENAI/__init__.py +2 -1
  17. webscout/Provider/OPENAI/api.py +298 -13
  18. webscout/Provider/OPENAI/autoproxy.py +39 -0
  19. webscout/Provider/OPENAI/base.py +89 -12
  20. webscout/Provider/OPENAI/chatgpt.py +15 -2
  21. webscout/Provider/OPENAI/chatgptclone.py +14 -3
  22. webscout/Provider/OPENAI/deepinfra.py +339 -328
  23. webscout/Provider/OPENAI/e2b.py +295 -73
  24. webscout/Provider/OPENAI/opkfc.py +18 -6
  25. webscout/Provider/OPENAI/scirachat.py +3 -2
  26. webscout/Provider/OPENAI/toolbaz.py +0 -1
  27. webscout/Provider/OPENAI/writecream.py +166 -166
  28. webscout/Provider/OPENAI/x0gpt.py +367 -367
  29. webscout/Provider/OPENAI/yep.py +383 -383
  30. webscout/Provider/STT/__init__.py +3 -0
  31. webscout/Provider/STT/base.py +281 -0
  32. webscout/Provider/STT/elevenlabs.py +265 -0
  33. webscout/Provider/TTI/__init__.py +3 -1
  34. webscout/Provider/TTI/aiarta.py +399 -365
  35. webscout/Provider/TTI/base.py +74 -2
  36. webscout/Provider/TTI/fastflux.py +63 -30
  37. webscout/Provider/TTI/gpt1image.py +149 -0
  38. webscout/Provider/TTI/imagen.py +196 -0
  39. webscout/Provider/TTI/magicstudio.py +60 -29
  40. webscout/Provider/TTI/piclumen.py +43 -32
  41. webscout/Provider/TTI/pixelmuse.py +232 -225
  42. webscout/Provider/TTI/pollinations.py +43 -32
  43. webscout/Provider/TTI/together.py +287 -0
  44. webscout/Provider/TTI/utils.py +2 -1
  45. webscout/Provider/TTS/README.md +1 -0
  46. webscout/Provider/TTS/__init__.py +2 -1
  47. webscout/Provider/TTS/freetts.py +140 -0
  48. webscout/Provider/UNFINISHED/ChutesAI.py +314 -0
  49. webscout/Provider/UNFINISHED/fetch_together_models.py +95 -0
  50. webscout/Provider/__init__.py +3 -0
  51. webscout/Provider/scira_chat.py +3 -2
  52. webscout/Provider/toolbaz.py +0 -1
  53. webscout/litagent/Readme.md +12 -3
  54. webscout/litagent/agent.py +99 -62
  55. webscout/version.py +1 -1
  56. {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/METADATA +1 -1
  57. {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/RECORD +61 -51
  58. webscout/Provider/TTI/artbit.py +0 -0
  59. {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/WHEEL +0 -0
  60. {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/entry_points.txt +0 -0
  61. {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/licenses/LICENSE.md +0 -0
  62. {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/top_level.txt +0 -0
webscout/AIauto.py CHANGED
@@ -6,7 +6,7 @@ API keys or cookies.
6
6
 
7
7
  from webscout.AIbase import Provider
8
8
  from webscout.exceptions import AllProvidersFailure
9
- from typing import Union, Any, Dict, Generator
9
+ from typing import Union, Any, Dict, Generator, Optional
10
10
  import importlib
11
11
  import pkgutil
12
12
  import random
@@ -71,7 +71,7 @@ class AUTO(Provider):
71
71
  proxies: dict = {},
72
72
  history_offset: int = 10250,
73
73
  act: str = None,
74
- exclude: list[str] = [],
74
+ exclude: Optional[list[str]] = None,
75
75
  print_provider_info: bool = False,
76
76
  ):
77
77
  """
@@ -90,7 +90,7 @@ class AUTO(Provider):
90
90
  proxies (dict): Proxies for requests. Defaults to {}.
91
91
  history_offset (int): History character offset limit. Defaults to 10250.
92
92
  act (str, optional): Awesome prompt key. Defaults to None.
93
- exclude (list[str]): List of provider names (uppercase) to exclude. Defaults to [].
93
+ exclude (Optional[list[str]]): List of provider names (uppercase) to exclude. Defaults to None.
94
94
  print_provider_info (bool): Whether to print the name of the successful provider. Defaults to False.
95
95
  """
96
96
  self.provider = None # type: Provider
@@ -104,7 +104,7 @@ class AUTO(Provider):
104
104
  self.proxies: dict = proxies
105
105
  self.history_offset: int = history_offset
106
106
  self.act: str = act
107
- self.exclude: list[str] = [e.upper() for e in exclude]
107
+ self.exclude: list[str] = [e.upper() for e in exclude] if exclude else []
108
108
  self.print_provider_info: bool = print_provider_info
109
109
 
110
110
 
webscout/AIbase.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from pathlib import Path
3
- from typing import AsyncGenerator, Dict, List, Union, Generator, Optional
3
+ from typing import AsyncGenerator, Dict, List, Union, Generator, Optional, Any
4
4
  from typing_extensions import TypeAlias
5
5
 
6
6
  # Type aliases for better readability
@@ -178,6 +178,66 @@ class AsyncTTSProvider(ABC):
178
178
  """
179
179
  raise NotImplementedError("Method needs to be implemented in subclass")
180
180
 
181
+
182
+ class STTProvider(ABC):
183
+ """Abstract base class for Speech-to-Text providers."""
184
+
185
+ @abstractmethod
186
+ def transcribe(self, audio_path: Union[str, Path], **kwargs) -> Dict[str, Any]:
187
+ """Transcribe audio file to text.
188
+
189
+ Args:
190
+ audio_path (Union[str, Path]): Path to the audio file
191
+ **kwargs: Additional provider-specific parameters
192
+
193
+ Returns:
194
+ Dict[str, Any]: Transcription result in OpenAI Whisper format
195
+ """
196
+ raise NotImplementedError("Method needs to be implemented in subclass")
197
+
198
+ @abstractmethod
199
+ def transcribe_from_url(self, audio_url: str, **kwargs) -> Dict[str, Any]:
200
+ """Transcribe audio from URL to text.
201
+
202
+ Args:
203
+ audio_url (str): URL of the audio file
204
+ **kwargs: Additional provider-specific parameters
205
+
206
+ Returns:
207
+ Dict[str, Any]: Transcription result in OpenAI Whisper format
208
+ """
209
+ raise NotImplementedError("Method needs to be implemented in subclass")
210
+
211
+
212
+ class AsyncSTTProvider(ABC):
213
+ """Abstract base class for asynchronous Speech-to-Text providers."""
214
+
215
+ @abstractmethod
216
+ async def transcribe(self, audio_path: Union[str, Path], **kwargs) -> Dict[str, Any]:
217
+ """Transcribe audio file to text asynchronously.
218
+
219
+ Args:
220
+ audio_path (Union[str, Path]): Path to the audio file
221
+ **kwargs: Additional provider-specific parameters
222
+
223
+ Returns:
224
+ Dict[str, Any]: Transcription result in OpenAI Whisper format
225
+ """
226
+ raise NotImplementedError("Method needs to be implemented in subclass")
227
+
228
+ @abstractmethod
229
+ async def transcribe_from_url(self, audio_url: str, **kwargs) -> Dict[str, Any]:
230
+ """Transcribe audio from URL to text asynchronously.
231
+
232
+ Args:
233
+ audio_url (str): URL of the audio file
234
+ **kwargs: Additional provider-specific parameters
235
+
236
+ Returns:
237
+ Dict[str, Any]: Transcription result in OpenAI Whisper format
238
+ """
239
+ raise NotImplementedError("Method needs to be implemented in subclass")
240
+
181
241
  async def save_audio(self, audio_file: str, destination: str = None, verbose: bool = False) -> str:
182
242
  """Save audio to a specific destination asynchronously.
183
243
 
@@ -2,60 +2,60 @@ import re
2
2
 
3
3
 
4
4
  class _ChannelPatterns:
5
- name = re.compile('channelMetadataRenderer\":{\"title\":\"(.*?)\"')
6
- id = re.compile('channelId\":\"(.*?)\"')
7
- verified = re.compile('"label":"Verified"')
8
- check_live = re.compile('{"text":"LIVE"}')
9
- live = re.compile("thumbnailOverlays\":\[(.*?)]")
10
- video_id = re.compile('videoId\":\"(.*?)\"')
11
- uploads = re.compile("gridVideoRenderer\":{\"videoId\":\"(.*?)\"")
12
- subscribers = re.compile("\"subscriberCountText\":{\"accessibility\":(.*?),")
13
- views = re.compile("viewCountText\":{\"simpleText\":\"(.*?)\"}")
14
- creation = re.compile("{\"text\":\"Joined \"},{\"text\":\"(.*?)\"}")
15
- country = re.compile("country\":{\"simpleText\":\"(.*?)\"}")
16
- custom_url = re.compile("canonicalChannelUrl\":\"(.*?)\"")
17
- description = re.compile("{\"description\":{\"simpleText\":\"(.*?)\"}")
18
- avatar = re.compile("height\":88},{\"url\":\"(.*?)\"")
19
- banner = re.compile("width\":1280,\"height\":351},{\"url\":\"(.*?)\"")
20
- playlists = re.compile("{\"url\":\"/playlist\\?list=(.*?)\"")
21
- video_count = re.compile("videoCountText\":{\"runs\":\[{\"text\":(.*?)}")
22
- socials = re.compile("q=https%3A%2F%2F(.*?)\"")
23
- upload_ids = re.compile("videoId\":\"(.*?)\"")
24
- stream_ids = re.compile("videoId\":\"(.*?)\"")
25
- upload_chunk = re.compile("gridVideoRenderer\":{(.*?)\"navigationEndpoint")
26
- upload_chunk_fl_1 = re.compile("simpleText\":\"Streamed")
27
- upload_chunk_fl_2 = re.compile("default_live.")
28
- upcoming_check = re.compile("\"title\":\"Upcoming live streams\"")
29
- upcoming = re.compile("gridVideoRenderer\":{\"videoId\":\"(.*?)\"")
5
+ name = re.compile(r'channelMetadataRenderer\":{\"title\":\"(.*?)\"')
6
+ id = re.compile(r'channelId\":\"(.*?)\"')
7
+ verified = re.compile(r'"label":"Verified"')
8
+ check_live = re.compile(r'{"text":"LIVE"}')
9
+ live = re.compile(r"thumbnailOverlays\":\[(.*?)]")
10
+ video_id = re.compile(r'videoId\":\"(.*?)\"')
11
+ uploads = re.compile(r"gridVideoRenderer\":{\"videoId\":\"(.*?)\"")
12
+ subscribers = re.compile(r"\"subscriberCountText\":{\"accessibility\":(.*?),")
13
+ views = re.compile(r"viewCountText\":{\"simpleText\":\"(.*?)\"}")
14
+ creation = re.compile(r"{\"text\":\"Joined \"},{\"text\":\"(.*?)\"}")
15
+ country = re.compile(r"country\":{\"simpleText\":\"(.*?)\"}")
16
+ custom_url = re.compile(r"canonicalChannelUrl\":\"(.*?)\"")
17
+ description = re.compile(r"{\"description\":{\"simpleText\":\"(.*?)\"}")
18
+ avatar = re.compile(r"height\":88},{\"url\":\"(.*?)\"")
19
+ banner = re.compile(r"width\":1280,\"height\":351},{\"url\":\"(.*?)\"")
20
+ playlists = re.compile(r"{\"url\":\"/playlist\?list=(.*?)\"")
21
+ video_count = re.compile(r"videoCountText\":{\"runs\":\[{\"text\":(.*?)}")
22
+ socials = re.compile(r"q=https%3A%2F%2F(.*?)\"")
23
+ upload_ids = re.compile(r"videoId\":\"(.*?)\"")
24
+ stream_ids = re.compile(r"videoId\":\"(.*?)\"")
25
+ upload_chunk = re.compile(r"gridVideoRenderer\":{(.*?)\"navigationEndpoint")
26
+ upload_chunk_fl_1 = re.compile(r"simpleText\":\"Streamed")
27
+ upload_chunk_fl_2 = re.compile(r"default_live.")
28
+ upcoming_check = re.compile(r"\"title\":\"Upcoming live streams\"")
29
+ upcoming = re.compile(r"gridVideoRenderer\":{\"videoId\":\"(.*?)\"")
30
30
 
31
31
 
32
32
  class _VideoPatterns:
33
- video_id = re.compile('videoId\":\"(.*?)\"')
34
- title = re.compile("title\":\"(.*?)\"")
35
- duration = re.compile("approxDurationMs\":\"(.*?)\"")
36
- upload_date = re.compile("uploadDate\":\"(.*?)\"")
37
- author_id = re.compile("channelIds\":\[\"(.*?)\"")
38
- description = re.compile("shortDescription\":\"(.*)\",\"isCrawlable")
39
- tags = re.compile("<meta name=\"keywords\" content=\"(.*?)\">")
40
- is_streamed = re.compile("simpleText\":\"Streamed live")
41
- is_premiered = re.compile("dateText\":{\"simpleText\":\"Premiered")
42
- views = re.compile("videoViewCountRenderer\":{\"viewCount\":{\"simpleText\":\"(.*?)\"")
43
- likes = re.compile("toggledText\":{\"accessibility\":{\"accessibilityData\":{\"label\":\"(.*?) ")
44
- thumbnail = re.compile("playerMicroformatRenderer\":{\"thumbnail\":{\"thumbnails\":\[{\"url\":\"(.*?)\"")
33
+ video_id = re.compile(r'videoId\":\"(.*?)\"')
34
+ title = re.compile(r"title\":\"(.*?)\"")
35
+ duration = re.compile(r"approxDurationMs\":\"(.*?)\"")
36
+ upload_date = re.compile(r"uploadDate\":\"(.*?)\"")
37
+ author_id = re.compile(r"channelIds\":\[\"(.*?)\"")
38
+ description = re.compile(r"shortDescription\":\"(.*)\",\"isCrawlable")
39
+ tags = re.compile(r"<meta name=\"keywords\" content=\"(.*?)\">")
40
+ is_streamed = re.compile(r"simpleText\":\"Streamed live")
41
+ is_premiered = re.compile(r"dateText\":{\"simpleText\":\"Premiered")
42
+ views = re.compile(r"videoViewCountRenderer\":{\"viewCount\":{\"simpleText\":\"(.*?)\"")
43
+ likes = re.compile(r"toggledText\":{\"accessibility\":{\"accessibilityData\":{\"label\":\"(.*?) ")
44
+ thumbnail = re.compile(r"playerMicroformatRenderer\":{\"thumbnail\":{\"thumbnails\":\[{\"url\":\"(.*?)\"")
45
45
 
46
46
 
47
47
  class _PlaylistPatterns:
48
- name = re.compile("{\"title\":\"(.*?)\"")
49
- video_count = re.compile("stats\":\[{\"runs\":\[{\"text\":\"(.*?)\"")
50
- video_id = re.compile("videoId\":\"(.*?)\"")
51
- thumbnail = re.compile("og:image\" content=\"(.*?)\?")
48
+ name = re.compile(r"{\"title\":\"(.*?)\"")
49
+ video_count = re.compile(r"stats\":\[{\"runs\":\[{\"text\":\"(.*?)\"")
50
+ video_id = re.compile(r"videoId\":\"(.*?)\"")
51
+ thumbnail = re.compile(r"og:image\" content=\"(.*?)\?")
52
52
 
53
53
 
54
54
  class _ExtraPatterns:
55
- video_id = re.compile("videoId\":\"(.*?)\"")
55
+ video_id = re.compile(r"videoId\":\"(.*?)\"")
56
56
 
57
57
 
58
58
  class _QueryPatterns:
59
- channel_id = re.compile("channelId\":\"(.*?)\"")
60
- video_id = re.compile("videoId\":\"(.*?)\"")
61
- playlist_id = re.compile("playlistId\":\"(.*?)\"")
59
+ channel_id = re.compile(r"channelId\":\"(.*?)\"")
60
+ video_id = re.compile(r"videoId\":\"(.*?)\"")
61
+ playlist_id = re.compile(r"playlistId\":\"(.*?)\"")
@@ -11,7 +11,7 @@ class Video:
11
11
  _HEAD = 'https://www.youtube.com/watch?v='
12
12
 
13
13
  def __init__(self, video_id: str):
14
- pattern = re.compile('.be/(.*?)$|=(.*?)$|^(\w{11})$') # noqa
14
+ pattern = re.compile(r'.be/(.*?)$|=(.*?)$|^(\w{11})$') # noqa
15
15
  self._matched_id = (
16
16
  pattern.search(video_id).group(1)
17
17
  or pattern.search(video_id).group(2)
@@ -17,7 +17,7 @@ class Video:
17
17
  video_id : str
18
18
  The id or url of the video
19
19
  """
20
- pattern = re.compile('.be/(.*?)$|=(.*?)$|^(\w{11})$') # noqa
20
+ pattern = re.compile(r'.be/(.*?)$|=(.*?)$|^(\w{11})$') # noqa
21
21
  match = pattern.search(video_id)
22
22
 
23
23
  if not match:
@@ -33,7 +33,7 @@ class Video:
33
33
  self._url = self._HEAD + self._matched_id
34
34
  self._video_data = video_data(self._matched_id)
35
35
  # Extract basic info for fallback
36
- title_match = re.search('<title>(.*?) - YouTube</title>', self._video_data)
36
+ title_match = re.search(r'<title>(.*?) - YouTube</title>', self._video_data)
37
37
  self.title = title_match.group(1) if title_match else None
38
38
  self.id = self._matched_id
39
39
  else:
@@ -55,19 +55,19 @@ class Video:
55
55
  """
56
56
  # Multiple patterns to try for video details extraction for robustness
57
57
  details_patterns = [
58
- re.compile('videoDetails\":(.*?)\"isLiveContent\":.*?}'),
59
- re.compile('videoDetails\":(.*?),\"playerConfig'),
60
- re.compile('videoDetails\":(.*?),\"playabilityStatus')
58
+ re.compile(r'videoDetails\":(.*?)\"isLiveContent\":.*?}'),
59
+ re.compile(r'videoDetails\":(.*?),\"playerConfig'),
60
+ re.compile(r'videoDetails\":(.*?),\"playabilityStatus')
61
61
  ]
62
62
 
63
63
  # Other metadata patterns
64
- upload_date_pattern = re.compile("<meta itemprop=\"uploadDate\" content=\"(.*?)\">")
65
- genre_pattern = re.compile("<meta itemprop=\"genre\" content=\"(.*?)\">")
64
+ upload_date_pattern = re.compile(r"<meta itemprop=\"uploadDate\" content=\"(.*?)\">")
65
+ genre_pattern = re.compile(r"<meta itemprop=\"genre\" content=\"(.*?)\">")
66
66
  like_count_patterns = [
67
- re.compile("iconType\":\"LIKE\"},\"defaultText\":(.*?)}"),
68
- re.compile('\"likeCount\":\"(\\d+)\"')
67
+ re.compile(r"iconType\":\"LIKE\"},\"defaultText\":(.*?)}"),
68
+ re.compile(r'\"likeCount\":\"(\d+)\"')
69
69
  ]
70
- channel_name_pattern = re.compile('"ownerChannelName":"(.*?)"')
70
+ channel_name_pattern = re.compile(r'"ownerChannelName":"(.*?)"')
71
71
 
72
72
  # Try each pattern for video details
73
73
  raw_details_match = None
@@ -131,7 +131,7 @@ def get_intro_prompt(name: str = "Vortex") -> str:
131
131
  - Actively clean up any temporary processes or files you use.
132
132
  - When looking through files, use git as available to skip files, and skip hidden files (.env, .git, etc) by default.
133
133
  - You can plot anything with matplotlib using Python code.
134
- - **IMPORTANT**: ALWAYS Return your SCRIPT inside of a single pair of \`\`\` delimiters. This SCRIPT can be a mix of Python code and `!`-prefixed shell commands. Only the console output from this SCRIPT (Python prints or `!` command stdout/stderr) is visible to the user, so ensure it's complete.
134
+ - **IMPORTANT**: ALWAYS Return your SCRIPT inside of a single pair of ``` delimiters. This SCRIPT can be a mix of Python code and `!`-prefixed shell commands. Only the console output from this SCRIPT (Python prints or `!` command stdout/stderr) is visible to the user, so ensure it's complete.
135
135
  </conventions>
136
136
 
137
137
  <examples>
@@ -1,4 +1,13 @@
1
1
  DEFAULT_FORMAT = "{time} | {level} | {name} | {message}"
2
2
 
3
+ SIMPLE_FORMAT = "{level}: {message}"
4
+
5
+ DETAILED_FORMAT = "{time} | {level} | {name} | {message} | Thread: {thread} | Process: {process}"
6
+
7
+ JSON_FORMAT = '{{"time": "{time}", "level": "{level}", "name": "{name}", "message": "{message}"}}'
8
+
3
9
  class LogFormat:
4
10
  DEFAULT = DEFAULT_FORMAT
11
+ SIMPLE = SIMPLE_FORMAT
12
+ DETAILED = DETAILED_FORMAT
13
+ JSON = JSON_FORMAT
@@ -101,3 +101,21 @@ class TCPHandler(Handler):
101
101
  return
102
102
  with socket.create_connection((self.host, self.port), timeout=5) as sock:
103
103
  sock.sendall(message.encode() + b"\n")
104
+
105
+ class JSONFileHandler(FileHandler):
106
+ def __init__(self, path: str, level: LogLevel = LogLevel.DEBUG, max_bytes: int = 0, backups: int = 0):
107
+ super().__init__(path, level, max_bytes, backups)
108
+
109
+ def emit(self, message: str, level: LogLevel):
110
+ # Expect message to be a JSON string or dict
111
+ if level < self.level:
112
+ return
113
+ import json
114
+ if isinstance(message, dict):
115
+ log_entry = json.dumps(message)
116
+ else:
117
+ log_entry = message
118
+ self._file.write(log_entry + "\n")
119
+ self._file.flush()
120
+ if self.max_bytes and self._file.tell() >= self.max_bytes:
121
+ self._rotate()
@@ -16,16 +16,58 @@ class Logger:
16
16
  handlers: Optional[List[Handler]] = None,
17
17
  fmt: str = LogFormat.DEFAULT, # <--- use LogFormat.DEFAULT
18
18
  async_mode: bool = False,
19
+ include_context: bool = False, # New flag to include thread/process info
19
20
  ):
21
+ import threading
22
+ import multiprocessing
23
+
20
24
  self.name = name
21
25
  self.level = level
22
26
  self.format = fmt
23
27
  self.async_mode = async_mode
28
+ self.include_context = include_context
24
29
  self.handlers = handlers or [ConsoleHandler()]
30
+ self._thread = threading
31
+ self._multiprocessing = multiprocessing
25
32
 
26
33
  def _format(self, level: LogLevel, message: str) -> str:
27
34
  now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
28
- return self.format.format(time=now, level=level.name, name=self.name, message=message)
35
+ if self.include_context:
36
+ thread_name = self._thread.current_thread().name
37
+ process_id = self._multiprocessing.current_process().pid
38
+ # Check if format is JSON format
39
+ if self.format.strip().startswith('{') and self.format.strip().endswith('}'):
40
+ # Format as JSON string with extra fields
41
+ return self.format.format(
42
+ time=now,
43
+ level=level.name,
44
+ name=self.name,
45
+ message=message,
46
+ thread=thread_name,
47
+ process=process_id
48
+ )
49
+ else:
50
+ # For non-JSON formats, add thread and process info if placeholders exist
51
+ try:
52
+ return self.format.format(
53
+ time=now,
54
+ level=level.name,
55
+ name=self.name,
56
+ message=message,
57
+ thread=thread_name,
58
+ process=process_id
59
+ )
60
+ except KeyError:
61
+ # If thread/process placeholders not in format, append them manually
62
+ base = self.format.format(time=now, level=level.name, name=self.name, message=message)
63
+ return f"{base} | Thread: {thread_name} | Process: {process_id}"
64
+ else:
65
+ return self.format.format(time=now, level=level.name, name=self.name, message=message)
66
+
67
+ def set_format(self, fmt: str, include_context: bool = False):
68
+ """Dynamically change the log format and context inclusion."""
69
+ self.format = fmt
70
+ self.include_context = include_context
29
71
 
30
72
  def _should_log(self, level: LogLevel) -> bool:
31
73
  return level >= self.level
@@ -45,12 +45,13 @@ class Scira(AISearch):
45
45
  AVAILABLE_MODELS = {
46
46
  "scira-default": "Grok3-mini", # thinking model
47
47
  "scira-grok-3": "Grok3",
48
- "scira-anthropic": "Sonnet 3.7 thinking",
48
+ "scira-anthropic": "Claude 4 Sonnet",
49
+ "scira-anthropic-thinking": "Claude 4 Sonnet Thinking", # thinking model
49
50
  "scira-vision" : "Grok2-Vision", # vision model
50
51
  "scira-4o": "GPT4o",
51
52
  "scira-qwq": "QWQ-32B",
52
53
  "scira-o4-mini": "o4-mini",
53
- "scira-google": "gemini 2.5 flash",
54
+ "scira-google": "gemini 2.5 flash Thinking", # thinking model
54
55
  "scira-google-pro": "gemini 2.5 pro",
55
56
  "scira-llama-4": "llama 4 Maverick",
56
57
  }
@@ -23,11 +23,17 @@ class LambdaChat(Provider):
23
23
  "deepseek-llama3.3-70b",
24
24
  "apriel-5b-instruct",
25
25
  "deepseek-r1",
26
+ "deepseek-v3-0324",
27
+ "deepseek-r1-0528",
26
28
  "hermes-3-llama-3.1-405b-fp8",
27
29
  "llama3.1-nemotron-70b-instruct",
28
30
  "lfm-40b",
29
31
  "llama3.3-70b-instruct-fp8",
30
- "qwen25-coder-32b-instruct"
32
+ "qwen25-coder-32b-instruct",
33
+ "qwen3-32b-fp8",
34
+ "llama-4-maverick-70b-128e-instruct-fp8",
35
+ "llama-4-scout-17b-16e-instruct"
36
+
31
37
  ]
32
38
 
33
39
  def __init__(