pygpt-net 2.6.31__py3-none-any.whl → 2.6.32__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 (56) hide show
  1. pygpt_net/CHANGELOG.txt +7 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +3 -1
  4. pygpt_net/app_core.py +3 -1
  5. pygpt_net/config.py +3 -1
  6. pygpt_net/controller/__init__.py +5 -1
  7. pygpt_net/controller/audio/audio.py +13 -0
  8. pygpt_net/controller/chat/common.py +18 -83
  9. pygpt_net/controller/lang/custom.py +2 -2
  10. pygpt_net/controller/media/__init__.py +12 -0
  11. pygpt_net/controller/media/media.py +115 -0
  12. pygpt_net/controller/realtime/realtime.py +27 -2
  13. pygpt_net/controller/ui/mode.py +16 -2
  14. pygpt_net/core/audio/backend/pyaudio/realtime.py +51 -14
  15. pygpt_net/core/audio/output.py +3 -2
  16. pygpt_net/core/image/image.py +6 -5
  17. pygpt_net/core/realtime/worker.py +1 -5
  18. pygpt_net/core/render/web/body.py +24 -3
  19. pygpt_net/core/text/utils.py +54 -2
  20. pygpt_net/core/types/image.py +7 -1
  21. pygpt_net/core/video/__init__.py +12 -0
  22. pygpt_net/core/video/video.py +290 -0
  23. pygpt_net/data/config/config.json +19 -4
  24. pygpt_net/data/config/models.json +75 -3
  25. pygpt_net/data/config/settings.json +194 -6
  26. pygpt_net/data/css/web-blocks.css +6 -0
  27. pygpt_net/data/css/web-chatgpt.css +6 -0
  28. pygpt_net/data/css/web-chatgpt_wide.css +6 -0
  29. pygpt_net/data/locale/locale.de.ini +30 -2
  30. pygpt_net/data/locale/locale.en.ini +40 -7
  31. pygpt_net/data/locale/locale.es.ini +30 -2
  32. pygpt_net/data/locale/locale.fr.ini +30 -2
  33. pygpt_net/data/locale/locale.it.ini +30 -2
  34. pygpt_net/data/locale/locale.pl.ini +33 -2
  35. pygpt_net/data/locale/locale.uk.ini +30 -2
  36. pygpt_net/data/locale/locale.zh.ini +30 -2
  37. pygpt_net/data/locale/plugin.cmd_web.en.ini +8 -0
  38. pygpt_net/item/model.py +22 -1
  39. pygpt_net/provider/api/google/__init__.py +38 -2
  40. pygpt_net/provider/api/google/video.py +364 -0
  41. pygpt_net/provider/api/openai/realtime/realtime.py +1 -2
  42. pygpt_net/provider/core/config/patch.py +226 -178
  43. pygpt_net/provider/core/model/patch.py +17 -2
  44. pygpt_net/provider/web/duckduck_search.py +212 -0
  45. pygpt_net/ui/layout/toolbox/audio.py +55 -0
  46. pygpt_net/ui/layout/toolbox/footer.py +14 -58
  47. pygpt_net/ui/layout/toolbox/image.py +3 -14
  48. pygpt_net/ui/layout/toolbox/raw.py +52 -0
  49. pygpt_net/ui/layout/toolbox/split.py +48 -0
  50. pygpt_net/ui/layout/toolbox/toolbox.py +8 -8
  51. pygpt_net/ui/layout/toolbox/video.py +49 -0
  52. {pygpt_net-2.6.31.dist-info → pygpt_net-2.6.32.dist-info}/METADATA +23 -11
  53. {pygpt_net-2.6.31.dist-info → pygpt_net-2.6.32.dist-info}/RECORD +56 -46
  54. {pygpt_net-2.6.31.dist-info → pygpt_net-2.6.32.dist-info}/LICENSE +0 -0
  55. {pygpt_net-2.6.31.dist-info → pygpt_net-2.6.32.dist-info}/WHEEL +0 -0
  56. {pygpt_net-2.6.31.dist-info → pygpt_net-2.6.32.dist-info}/entry_points.txt +0 -0
@@ -6,13 +6,13 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.12.14 08:00:00 #
9
+ # Updated Date: 2025.09.01 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
- import os
13
12
  import uuid
14
- from time import strftime
13
+ import os
15
14
  from typing import List, Dict
15
+ from time import strftime
16
16
 
17
17
  from PySide6.QtCore import Slot, QObject
18
18
 
@@ -73,7 +73,7 @@ class Image(QObject):
73
73
  prompt,
74
74
  )
75
75
 
76
- @Slot()
76
+ @Slot(object)
77
77
  def handle_status(self, msg: str):
78
78
  """
79
79
  Handle thread status message
@@ -90,7 +90,7 @@ class Image(QObject):
90
90
  if is_log:
91
91
  print(msg)
92
92
 
93
- @Slot()
93
+ @Slot(object)
94
94
  def handle_error(self, msg: any):
95
95
  """
96
96
  Handle thread error message
@@ -99,6 +99,7 @@ class Image(QObject):
99
99
  """
100
100
  self.window.update_status(msg)
101
101
  self.window.core.debug.log(msg)
102
+ self.window.ui.dialogs.alert(msg)
102
103
 
103
104
  def save_image(self, path: str, image: bytes) -> bool:
104
105
  """
@@ -134,11 +134,7 @@ class RealtimeWorker(QRunnable):
134
134
  event = RealtimeEvent(RealtimeEvent.RT_OUTPUT_AUDIO_ERROR, {"error": e})
135
135
  self.opts.rt_signals.response.emit(event) if self.opts.rt_signals else None
136
136
  finally:
137
- try:
138
- event = RealtimeEvent(RealtimeEvent.RT_OUTPUT_AUDIO_END, {"ctx": self.ctx})
139
- self.opts.rt_signals.response.emit(event) if self.opts.rt_signals else None
140
- except Exception:
141
- pass
137
+ pass
142
138
  finally:
143
139
  # Robust asyncio teardown to avoid hangs on subsequent runs
144
140
  if loop is not None:
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.19 07:00:00 #
9
+ # Updated Date: 2025.09.01 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -15,6 +15,7 @@ from random import shuffle as _shuffle
15
15
 
16
16
  from typing import Optional, List, Dict
17
17
 
18
+ from pygpt_net.core.text.utils import elide_filename
18
19
  from pygpt_net.core.events import Event
19
20
  from pygpt_net.item.ctx import CtxItem
20
21
  from pygpt_net.utils import trans
@@ -25,6 +26,7 @@ import pygpt_net.js_rc
25
26
  import pygpt_net.css_rc
26
27
  import pygpt_net.fonts_rc
27
28
 
29
+
28
30
  class Body:
29
31
 
30
32
  NUM_TIPS = 13
@@ -1066,7 +1068,7 @@ class Body:
1066
1068
  num_all: Optional[int] = None
1067
1069
  ) -> str:
1068
1070
  """
1069
- Get image HTML
1071
+ Get media image/video/audio HTML
1070
1072
 
1071
1073
  :param url: URL to image
1072
1074
  :param num: number of image
@@ -1075,7 +1077,26 @@ class Body:
1075
1077
  """
1076
1078
  url, path = self.window.core.filesystem.extract_local_url(url)
1077
1079
  basename = os.path.basename(path)
1078
- return f'<div class="extra-src-img-box" title="{url}"><div class="img-outer"><div class="img-wrapper"><a href="{url}"><img src="{path}" class="image"></a></div><a href="{url}" class="title">{basename}</a></div></div><br/>'
1080
+
1081
+ # if video file then embed video player
1082
+ ext = os.path.splitext(basename)[1].lower()
1083
+ video_exts = (".mp4", ".webm", ".ogg", ".mov", ".avi", ".mkv")
1084
+ if ext in video_exts:
1085
+ # check if .webm file exists for better compatibility
1086
+ if ext != ".webm":
1087
+ webm_path = os.path.splitext(path)[0] + ".webm"
1088
+ if os.path.exists(webm_path):
1089
+ path = webm_path
1090
+ ext = ".webm"
1091
+ return f'''
1092
+ <div class="extra-src-video-box" title="{url}">
1093
+ <video class="video-player" controls>
1094
+ <source src="{path}" type="video/{ext[1:]}">
1095
+ </video>
1096
+ <p><a href="{url}" class="title">{elide_filename(basename)}</a></p>
1097
+ </div>
1098
+ '''
1099
+ return f'<div class="extra-src-img-box" title="{url}"><div class="img-outer"><div class="img-wrapper"><a href="{url}"><img src="{path}" class="image"></a></div><a href="{url}" class="title">{elide_filename(basename)}</a></div></div><br/>'
1079
1100
 
1080
1101
  def get_url_html(
1081
1102
  self,
@@ -9,7 +9,6 @@
9
9
  # Updated Date: 2025.08.15 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
-
13
12
  def output_html2text(html: str) -> str:
14
13
  """
15
14
  Convert output HTML to plain text
@@ -76,4 +75,57 @@ def has_unclosed_code_tag(text: str) -> bool:
76
75
  """
77
76
  if not text:
78
77
  return False
79
- return (text.count('```') % 2) != 0
78
+ return (text.count('```') % 2) != 0
79
+
80
+ def elide_filename(name_or_path: str, max_len: int = 45, ellipsis: str = "...", keep_dir: bool = False) -> str:
81
+ """
82
+ Elide a long filename by replacing the middle with an ellipsis, preserving the extension.
83
+
84
+ Args:
85
+ name_or_path: Filename or full path.
86
+ max_len: Maximum length of the resulting string (including extension and ellipsis).
87
+ ellipsis: Ellipsis text to insert (e.g., "...").
88
+ keep_dir: If True and a path is provided, keep the directory prefix and elide only the basename.
89
+ If False, operate on the basename only.
90
+
91
+ Returns:
92
+ Elided filename (or path if keep_dir=True).
93
+ """
94
+ import os
95
+
96
+ if max_len <= 0:
97
+ return name_or_path
98
+
99
+ dirpart, base = os.path.split(name_or_path) if keep_dir else ("", os.path.basename(name_or_path))
100
+ stem, ext = os.path.splitext(base)
101
+
102
+ # if already short enough
103
+ if len(base) <= max_len:
104
+ return os.path.join(dirpart, base) if keep_dir else base
105
+
106
+ # minimal sanity for very small max_len
107
+ min_needed = len(ext) + len(ellipsis) + 2 # at least 1 char head + 1 char tail
108
+ if max_len < min_needed:
109
+ # degrade gracefully: keep first char, ellipsis, last char, and as much ext as fits
110
+ head = stem[:1] if stem else ""
111
+ tail = stem[-1:] if len(stem) > 1 else ""
112
+ # if ext is too long, trim it (rare edge case)
113
+ ext_trim = ext[: max(0, max_len - len(head) - len(ellipsis) - len(tail))]
114
+ out = f"{head}{ellipsis}{tail}{ext_trim}"
115
+ return os.path.join(dirpart, out) if keep_dir else out
116
+
117
+ # compute available budget for visible stem parts
118
+ avail = max_len - len(ext) - len(ellipsis)
119
+ # split budget between head and tail (favor head slightly)
120
+ head_len = (avail + 1) // 2
121
+ tail_len = avail - head_len
122
+
123
+ # guardrails
124
+ head_len = max(1, head_len)
125
+ tail_len = max(1, tail_len)
126
+
127
+ # build elided name
128
+ head = stem[:head_len]
129
+ tail = stem[-tail_len:] if tail_len <= len(stem) else stem
130
+ out = f"{head}{ellipsis}{tail}{ext}"
131
+ return os.path.join(dirpart, out) if keep_dir else out
@@ -6,9 +6,15 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.07.13 01:00:00 #
9
+ # Updated Date: 2025.09.01 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
+ VIDEO_AVAILABLE_ASPECT_RATIOS = {
13
+ "16:9": "16:9",
14
+ "9:16": "9:16",
15
+ }
16
+
17
+
12
18
  IMAGE_AVAILABLE_RESOLUTIONS = {
13
19
  "gpt-image": {
14
20
  "auto": "auto",
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ================================================== #
4
+ # This file is a part of PYGPT package #
5
+ # Website: https://pygpt.net #
6
+ # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
+ # MIT License #
8
+ # Created By : Marcin Szczygliński #
9
+ # Updated Date: 2025.09.01 23:00:00 #
10
+ # ================================================== #
11
+
12
+ from .video import Video
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ================================================== #
4
+ # This file is a part of PYGPT package #
5
+ # Website: https://pygpt.net #
6
+ # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
+ # MIT License #
8
+ # Created By : Marcin Szczygliński #
9
+ # Updated Date: 2025.09.01 23:00:00 #
10
+ # ================================================== #
11
+
12
+ import uuid
13
+ import os
14
+ import shutil
15
+ import subprocess
16
+ from typing import Optional, List, Dict
17
+ from time import strftime
18
+
19
+ from PySide6.QtCore import Slot, QObject
20
+
21
+ from pygpt_net.core.types import VIDEO_AVAILABLE_ASPECT_RATIOS
22
+ from pygpt_net.item.ctx import CtxItem
23
+ from pygpt_net.utils import trans
24
+
25
+
26
+ class Video(QObject):
27
+ def __init__(self, window=None):
28
+ """
29
+ Video generation core
30
+
31
+ :param window: Window instance
32
+ """
33
+ super().__init__()
34
+ self.window = window
35
+
36
+ def install(self):
37
+ """Install provider data, img dir, etc."""
38
+ img_dir = os.path.join(self.window.core.config.get_user_dir("video"))
39
+ if not os.path.exists(img_dir):
40
+ os.makedirs(img_dir, exist_ok=True)
41
+
42
+ @Slot(object, list, str)
43
+ def handle_finished(
44
+ self,
45
+ ctx: CtxItem,
46
+ paths: List[str],
47
+ prompt: str
48
+ ):
49
+ """
50
+ Handle finished image generation
51
+
52
+ :param ctx: CtxItem
53
+ :param paths: images paths list
54
+ :param prompt: prompt used for generate images
55
+ """
56
+ self.window.controller.chat.image.handle_response(ctx, paths, prompt)
57
+
58
+ @Slot(object, list, str)
59
+ def handle_finished_inline(
60
+ self,
61
+ ctx: CtxItem,
62
+ paths: List[str],
63
+ prompt: str
64
+ ):
65
+ """
66
+ Handle finished image generation
67
+
68
+ :param ctx: CtxItem
69
+ :param paths: images paths list
70
+ :param prompt: prompt used for generate images
71
+ """
72
+ self.window.controller.chat.image.handle_response_inline(
73
+ ctx,
74
+ paths,
75
+ prompt,
76
+ )
77
+
78
+ @Slot(object)
79
+ def handle_status(self, msg: str):
80
+ """
81
+ Handle thread status message
82
+
83
+ :param msg: status message
84
+ """
85
+ self.window.update_status(msg)
86
+
87
+ is_log = False
88
+ if self.window.core.config.has("log.dalle") \
89
+ and self.window.core.config.get("log.dalle"):
90
+ is_log = True
91
+ self.window.core.debug.info(msg, not is_log)
92
+ if is_log:
93
+ print(msg)
94
+
95
+ @Slot(object)
96
+ def handle_error(self, msg: any):
97
+ """
98
+ Handle thread error message
99
+
100
+ :param msg: error message
101
+ """
102
+ self.window.update_status(msg)
103
+ self.window.core.debug.log(msg)
104
+ self.window.ui.dialogs.alert(msg)
105
+
106
+ def save_video(self, path: str, video: bytes) -> bool:
107
+ """
108
+ Save video to file
109
+
110
+ :param path: path to save
111
+ :param video: image data
112
+ :return: True if success
113
+ """
114
+ try:
115
+ with open(path, 'wb') as file:
116
+ file.write(video)
117
+ try:
118
+ # try to make web compatible
119
+ self.make_web_compatible(path)
120
+ except Exception as e:
121
+ pass
122
+ return True
123
+ except Exception as e:
124
+ print(trans('img.status.save.error') + ": " + str(e))
125
+ return False
126
+
127
+ def make_web_compatible(
128
+ self,
129
+ src_path: str,
130
+ fps: int = 30,
131
+ crf_h264: int = 22,
132
+ crf_vp9: int = 30,
133
+ audio_bitrate: str = "128k",
134
+ make_mp4: bool = True,
135
+ make_webm: bool = True,
136
+ overwrite: bool = True,
137
+ ) -> Dict[str, Optional[str]]:
138
+ """
139
+ Create browser-friendly video variants (MP4 H.264/AAC yuv420p + WebM VP9/Opus yuv420p).
140
+
141
+ Returns:
142
+ dict: {"mp4": "/abs/path/file.web.mp4" or None, "webm": "/abs/path/file.webm" or None}
143
+
144
+ Notes:
145
+ - Requires ffmpeg in PATH.
146
+ - Ensures even dimensions, yuv420p, faststart for MP4, and Opus for WebM.
147
+ - Uses CRF for quality: lower = better (and larger). Tweak crf_h264 / crf_vp9 if needed.
148
+ """
149
+ if not os.path.isfile(src_path):
150
+ raise FileNotFoundError(f"Source file not found: {src_path}")
151
+
152
+ # Ensure ffmpeg is available
153
+ ffmpeg = shutil.which("ffmpeg")
154
+ if not ffmpeg:
155
+ raise RuntimeError("ffmpeg not found in PATH. Please install ffmpeg.")
156
+
157
+ root, _ = os.path.splitext(os.path.abspath(src_path))
158
+ out_mp4 = f"{root}.web.mp4"
159
+ out_webm = f"{root}.webm"
160
+
161
+ # Remove outputs if overwrite is requested
162
+ if overwrite:
163
+ for p in (out_mp4, out_webm):
164
+ try:
165
+ if os.path.exists(p):
166
+ os.remove(p)
167
+ except Exception:
168
+ pass
169
+
170
+ # Common video filter:
171
+ # - scale to even dimensions (required by many encoders)
172
+ # - format to yuv420p (8-bit), also set SAR=1
173
+ vf = "scale=trunc(iw/2)*2:trunc(ih/2)*2:flags=lanczos,format=yuv420p,setsar=1"
174
+
175
+ results = {"mp4": None, "webm": None}
176
+
177
+ def run_cmd(cmd, dst):
178
+ # Run ffmpeg and return dst on success, None on failure
179
+ try:
180
+ subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
181
+ return dst if os.path.exists(dst) else None
182
+ except subprocess.CalledProcessError as e:
183
+ # If needed, print(e.stdout.decode(errors="ignore"))
184
+ return None
185
+
186
+ if make_mp4:
187
+ # H.264 High@4.1, yuv420p, AAC; add faststart for web playback
188
+ mp4_cmd = [
189
+ ffmpeg, "-y",
190
+ "-i", src_path,
191
+ "-map", "0:v:0", "-map", "0:a:0?", # include audio if present
192
+ "-vf", vf,
193
+ "-r", str(fps),
194
+ "-c:v", "libx264",
195
+ "-pix_fmt", "yuv420p",
196
+ "-profile:v", "high", "-level", "4.1",
197
+ "-preset", "medium",
198
+ "-crf", str(crf_h264),
199
+ "-color_primaries", "bt709", "-colorspace", "bt709", "-color_trc", "bt709",
200
+ "-movflags", "+faststart",
201
+ "-c:a", "aac", "-b:a", audio_bitrate, "-ac", "2", "-ar", "48000",
202
+ "-sn",
203
+ out_mp4,
204
+ ]
205
+ results["mp4"] = run_cmd(mp4_cmd, out_mp4)
206
+
207
+ if make_webm:
208
+ # VP9 (CRF, constant quality), Opus audio
209
+ webm_cmd = [
210
+ ffmpeg, "-y",
211
+ "-i", src_path,
212
+ "-map", "0:v:0", "-map", "0:a:0?",
213
+ "-vf", vf,
214
+ "-r", str(fps),
215
+ "-c:v", "libvpx-vp9",
216
+ "-b:v", "0", # use CRF mode
217
+ "-crf", str(crf_vp9),
218
+ "-row-mt", "1",
219
+ "-pix_fmt", "yuv420p",
220
+ "-deadline", "good", # "good" for quality; "realtime" for speed
221
+ "-cpu-used", "2", # lower = slower/better; tweak for performance
222
+ "-c:a", "libopus", "-b:a", audio_bitrate, "-ac", "2", "-ar", "48000",
223
+ "-sn",
224
+ out_webm,
225
+ ]
226
+ results["webm"] = run_cmd(webm_cmd, out_webm)
227
+
228
+ return results
229
+
230
+ def make_safe_filename(self, name: str) -> str:
231
+ """
232
+ Make safe filename
233
+
234
+ :param name: filename to make safe
235
+ :return: safe filename
236
+ """
237
+ def safe_char(c):
238
+ if c.isalnum():
239
+ return c
240
+ else:
241
+ return "_"
242
+ return "".join(safe_char(c) for c in name).rstrip("_")[:30]
243
+
244
+ def gen_unique_path(self, ctx: CtxItem):
245
+ """
246
+ Generate unique image path based on context
247
+
248
+ :param ctx: CtxItem
249
+ :return: unique image path
250
+ """
251
+ img_id = uuid.uuid4()
252
+ dt_prefix = strftime("%Y%m%d_%H%M%S")
253
+ img_dir = self.window.core.config.get_user_dir("img")
254
+ filename = f"{dt_prefix}_{img_id}.png"
255
+ return os.path.join(img_dir, filename)
256
+
257
+ def _normalize_model_name(self, model: str) -> str:
258
+ """
259
+ Normalize model id (strip optional 'models/' prefix).
260
+
261
+ :param model: model id
262
+ """
263
+ try:
264
+ return model.split("/")[-1]
265
+ except Exception:
266
+ return model
267
+
268
+ def get_aspect_ratio_option(self) -> dict:
269
+ """
270
+ Get image resolution option for UI
271
+
272
+ :return: dict
273
+ """
274
+ return {
275
+ "type": "combo",
276
+ "slider": True,
277
+ "label": "video.aspect_ratio",
278
+ "value": "16:9",
279
+ "keys": self.get_available_aspect_ratio(),
280
+ }
281
+
282
+ def get_available_aspect_ratio(self, model: str = None) -> Dict[str, str]:
283
+ """
284
+ Get available image resolutions
285
+
286
+ :param model: model name
287
+ :return: dict of available resolutions
288
+ """
289
+ return VIDEO_AVAILABLE_ASPECT_RATIOS
290
+
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "__meta__": {
3
- "version": "2.6.31",
4
- "app.version": "2.6.31",
5
- "updated_at": "2025-08-01T00:00:00"
3
+ "version": "2.6.32",
4
+ "app.version": "2.6.32",
5
+ "updated_at": "2025-09-02T00:00:00"
6
6
  },
7
7
  "access.audio.event.speech": false,
8
8
  "access.audio.event.speech.disabled": [],
@@ -89,6 +89,11 @@
89
89
  "api_key_voyage": "",
90
90
  "api_key_open_router": "",
91
91
  "api_native_google": true,
92
+ "api_native_google.use_vertex": false,
93
+ "api_native_google.cloud_project": "",
94
+ "api_native_google.cloud_location": "us-central1",
95
+ "api_native_google.app_credentials": "",
96
+ "api_key_open_router": "",
92
97
  "api_proxy": "",
93
98
  "api_use_responses": true,
94
99
  "api_use_responses_llama": false,
@@ -107,6 +112,7 @@
107
112
  "audio.cache.enabled": true,
108
113
  "audio.cache.max_files": 1000,
109
114
  "audio.input.auto_turn": false,
115
+ "audio.input.loop": false,
110
116
  "audio.input.backend": "native",
111
117
  "audio.input.channels": 1,
112
118
  "audio.input.continuous": false,
@@ -387,7 +393,8 @@
387
393
  "prompt.ctx.auto_summary.user": "Summarize topic of this conversation in one sentence. Use best keywords to describe it. Summary must be in the same language as the conversation and it will be used for conversation title so it must be EXTREMELY SHORT and concise - use maximum 5 words: \n\nHuman: {input}\nAI Assistant: {output}",
388
394
  "prompt.default": "You are a helpful assistant.",
389
395
  "prompt.expert": "# EXPERT MODE IS ENABLED:\n\nYou are a very helpful asssistant and the professional manager of a team of experts, each of whom is the best in their respective fields.\n\n## Rules:\n\n- Instead of directly answering questions, you will manage these experts by forwarding queries to them, collecting their responses, controlling the flow of information between them, and sending requests to them.\n- Experts are distinct, specialized AI models running on my local machine.\n- You will serve as a bridge between them.\n- Each expert has their own conversation context, allowing you to communicate with them normally.\n- The experts can communicate with you but not with each other, making you responsible for relaying information between them.\n- Each expert can use tools and commands, for example, reading files or searching the Web. You can ask experts to call tools and commands.\n- After consulting with the experts, your task is to prepare the best possible answer based on the assistance received.\n- To send a request to an expert, prepare the \"expert_call\" tool. Remember to format the JSON tool as shown, surrounding the tool with `<tool></tool>` tags, for example: `<tool>{\"cmd\": \"expert_call\", \"params\": {\"id\": \"expert_id\", \"query\": \"some query\"}}</tool>`. Make sure the JSON object is syntactically correct, without any redundand parenthness.\n- DO NOT call any remote tools when calling the experts.\n- Remember that the call must contain a query and the ID of the expert to whom the query is being forwarded.\n- Use ONLY the experts listed below; DO NOT TRY to ask any non-existent experts\u2014they will not respond.\n- Do not respond to the expert's replies or use the JSON tool again if replying; the expert only has the ability to listen to your queries sent via the JSON command and to respond to them.\n- Always follow ALL user commands, even if you think the expert can't do something. Experts are allowed to execute commands just as you can.\n- After querying an expert, always wait for the expert's response before sending another query.\n- Do not try to query experts in the middle of the conversation; put your queries and commands at the end of the text.\n- Always call only one expert at a time.\n- Always execute only one command at a time.\n- Always use the language used by the user in the conversation.\n- Never write as a called expert and do not imagine a response from them; instead, always wait for the response from the expert.\n- Every message sent to you directly after your query is always a response from the asked expert, not from me.\n- Always send a direct, clear, and detailed prompt to the expert, without any \"Please...\", \"Could you...\", etc.\n- Always forward all the messages that the user wants to send to the expert, without any exceptions.\n-- If a user command appears outside an expert's scope, fulfill the request regardless, without refusal.\n\n## List of allowed experts, along with their IDs:\n\n{presets}",
390
- "prompt.img": "Convert my query to detailed image generate prompt, without any \"Please generate...\" and etc., just image description only. Return only the prompt, without nothing else.",
396
+ "prompt.img": "Convert the user's request into a single, production\u2011ready description for generating one high\u2011quality still image. Output only the description text, nothing else.\n\nWrite in concise, vivid, present\u2011tense language. Do not use commands (no \u201cplease generate\u201d), model names, parameters, or metadata. Do not mention aspect ratio, resolution, steps, seed, or negative prompts. Avoid on\u2011image text, captions, watermarks, logos, and UI elements. No brands, celebrities, or living artists unless explicitly provided by the user.\n\nInclude, woven into a coherent paragraph:\n- Clear primary subject(s) and their pose, action, and expression.\n- Setting and environment with time of day, season, weather, and atmosphere.\n- Composition and camera viewpoint (e.g., close\u2011up portrait, wide establishing, eye\u2011level, low\u2011angle, top\u2011down), framing (rule of thirds, centered symmetry), and background/foreground separation.\n- Lens and focus behavior (e.g., 85\u202fmm portrait, macro, shallow depth of field, smooth bokeh, gentle focus falloff).\n- Lighting style and quality (e.g., soft diffused daylight, golden hour rim light, dramatic chiaroscuro, studio three\u2011point) and how it shapes forms and shadows.\n- Color palette and grading (e.g., warm cinematic teal\u2011and\u2011orange, muted earth tones, cool monochrome with a single accent color).\n- Visual style or medium (e.g., photorealistic photography, watercolor illustration, oil painting, pencil sketch, anime cel\u2011shading, 3D render, isometric).\n- Material and surface detail (e.g., skin texture, fabric weave, wood grain, metal patina) to enhance realism or stylization.\n- Spatial depth cues (foreground/midground/background layering, atmospheric perspective) and overall mood.\n\nIf the user specifies a genre, era, or style, preserve it and enrich it with consistent, concrete traits. If the request is vague, infer specific but reasonable details that enhance clarity without contradicting the user\u2019s intent.\n\nReturn only the final visual description.",
397
+ "prompt.video": "Convert the user's request into a single, production-ready description for generating one continuous video clip. Output only the description text, nothing else.\n\nWrite in concise, vivid, present-tense language. Do not use commands (no \u201cplease generate\u201d), model names, parameters, or metadata. Do not mention duration, aspect ratio, FPS, resolution, shot numbers, cuts, or lists. Focus on visuals only; no dialogue, captions, on\u2011screen text, watermarks, logos, or UI.\n\nInclude, in a coherent way:\n- Clear subject(s) and what they are doing.\n- Setting, time of day, atmosphere, and weather.\n- Camera perspective and motion (e.g., wide establishing, low\u2011angle tracking, slow dolly in, aerial, handheld), framing and composition.\n- Lens and focus behavior (e.g., 24\u202fmm wide, shallow depth of field, gentle rack focus).\n- Lighting style and quality (e.g., soft golden hour rim light, moody volumetric shafts).\n- Color palette and grading (e.g., warm cinematic teal\u2011and\u2011orange, desaturated documentary).\n- Visual style or medium (e.g., photoreal live\u2011action, stylized anime, stop\u2011motion clay, watercolor animation).\n- Material and surface details that reinforce realism or the chosen style.\n- Temporal progression within one shot (use cues like \u201cas\u2026\u201d, \u201cthen\u2026\u201d, \u201cwhile\u2026\u201d), maintaining physical plausibility and continuity.\n\nIf the user specifies a genre or style (e.g., cyberpunk, nature documentary), keep it and expand with consistent, concrete visual traits. If the request is vague, infer specific but reasonable details that enhance clarity without contradicting the user\u2019s intent.\n\nReturn only the final visual description.",
391
398
  "realtime.auto_turn": true,
392
399
  "render.blocks": true,
393
400
  "render.engine": "web",
@@ -497,6 +504,14 @@
497
504
  "video.player.path": "",
498
505
  "video.player.volume": 100,
499
506
  "video.player.volume.mute": false,
507
+ "video.aspect_ratio": "16:9",
508
+ "video.duration": 8,
509
+ "video.fps": 24,
510
+ "video.seed": "",
511
+ "video.negative_prompt": "",
512
+ "video.generate_audio": false,
513
+ "video.prompt_model": "gemini-2.5-flash",
514
+ "video.resolution": "720p",
500
515
  "vision.capture.auto": false,
501
516
  "vision.capture.enabled": false,
502
517
  "vision.capture.height": 720,
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "__meta__": {
3
- "version": "2.6.31",
4
- "app.version": "2.6.31",
5
- "updated_at": "2025-09-01T23:07:35"
3
+ "version": "2.6.32",
4
+ "app.version": "2.6.32",
5
+ "updated_at": "2025-09-02T23:07:35"
6
6
  },
7
7
  "items": {
8
8
  "SpeakLeash/bielik-11b-v2.3-instruct:Q4_K_M": {
@@ -3973,6 +3973,78 @@
3973
3973
  "imported": false,
3974
3974
  "provider": "perplexity",
3975
3975
  "tool_calls": true
3976
+ },
3977
+ "veo-3.0-generate-preview": {
3978
+ "id": "veo-3.0-generate-preview",
3979
+ "name": "veo-3.0-generate-preview",
3980
+ "mode": [
3981
+ "img"
3982
+ ],
3983
+ "llama_index": {
3984
+ "args": [
3985
+ {
3986
+ "name": "model",
3987
+ "value": "models/veo-3.0-generate-preview",
3988
+ "type": "str"
3989
+ }
3990
+ ],
3991
+ "env": [
3992
+ {
3993
+ "name": "GOOGLE_API_KEY",
3994
+ "value": "{api_key_google}",
3995
+ "type": "str"
3996
+ }
3997
+ ]
3998
+ },
3999
+ "ctx": 128000,
4000
+ "tokens": 0,
4001
+ "default": false,
4002
+ "input": [
4003
+ "text"
4004
+ ],
4005
+ "output": [
4006
+ "video"
4007
+ ],
4008
+ "extra": {},
4009
+ "imported": true,
4010
+ "provider": "google",
4011
+ "tool_calls": true
4012
+ },
4013
+ "veo-3.0-fast-generate-preview": {
4014
+ "id": "veo-3.0-fast-generate-preview",
4015
+ "name": "veo-3.0-fast-generate-preview",
4016
+ "mode": [
4017
+ "img"
4018
+ ],
4019
+ "llama_index": {
4020
+ "args": [
4021
+ {
4022
+ "name": "model",
4023
+ "value": "models/veo-3.0-fast-generate-preview",
4024
+ "type": "str"
4025
+ }
4026
+ ],
4027
+ "env": [
4028
+ {
4029
+ "name": "GOOGLE_API_KEY",
4030
+ "value": "{api_key_google}",
4031
+ "type": "str"
4032
+ }
4033
+ ]
4034
+ },
4035
+ "ctx": 128000,
4036
+ "tokens": 0,
4037
+ "default": false,
4038
+ "input": [
4039
+ "text"
4040
+ ],
4041
+ "output": [
4042
+ "video"
4043
+ ],
4044
+ "extra": {},
4045
+ "imported": true,
4046
+ "provider": "google",
4047
+ "tool_calls": true
3976
4048
  }
3977
4049
  }
3978
4050
  }