pygpt-net 2.6.65__py3-none-any.whl → 2.6.66__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.
- pygpt_net/CHANGELOG.txt +11 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +2 -0
- pygpt_net/controller/chat/chat.py +0 -0
- pygpt_net/controller/chat/handler/openai_stream.py +137 -7
- pygpt_net/controller/chat/render.py +0 -0
- pygpt_net/controller/config/field/checkbox_list.py +34 -1
- pygpt_net/controller/media/media.py +20 -1
- pygpt_net/controller/presets/presets.py +4 -1
- pygpt_net/controller/ui/mode.py +14 -10
- pygpt_net/controller/ui/ui.py +18 -1
- pygpt_net/core/image/image.py +34 -1
- pygpt_net/core/tabs/tabs.py +0 -0
- pygpt_net/core/types/image.py +61 -3
- pygpt_net/data/config/config.json +4 -3
- pygpt_net/data/config/models.json +629 -41
- pygpt_net/data/locale/locale.de.ini +4 -0
- pygpt_net/data/locale/locale.en.ini +4 -0
- pygpt_net/data/locale/locale.es.ini +4 -0
- pygpt_net/data/locale/locale.fr.ini +4 -0
- pygpt_net/data/locale/locale.it.ini +4 -0
- pygpt_net/data/locale/locale.pl.ini +4 -0
- pygpt_net/data/locale/locale.uk.ini +4 -0
- pygpt_net/data/locale/locale.zh.ini +4 -0
- pygpt_net/item/model.py +15 -19
- pygpt_net/provider/agents/openai/agent.py +0 -0
- pygpt_net/provider/api/google/__init__.py +20 -9
- pygpt_net/provider/api/google/image.py +161 -28
- pygpt_net/provider/api/google/video.py +73 -36
- pygpt_net/provider/api/openai/__init__.py +21 -11
- pygpt_net/provider/api/openai/agents/client.py +0 -0
- pygpt_net/provider/api/openai/video.py +562 -0
- pygpt_net/provider/core/config/patch.py +7 -0
- pygpt_net/provider/core/model/patch.py +29 -3
- pygpt_net/provider/vector_stores/qdrant.py +117 -0
- pygpt_net/ui/layout/toolbox/raw.py +7 -1
- pygpt_net/ui/widget/option/checkbox_list.py +14 -2
- {pygpt_net-2.6.65.dist-info → pygpt_net-2.6.66.dist-info}/METADATA +66 -25
- {pygpt_net-2.6.65.dist-info → pygpt_net-2.6.66.dist-info}/RECORD +37 -35
- {pygpt_net-2.6.65.dist-info → pygpt_net-2.6.66.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.65.dist-info → pygpt_net-2.6.66.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.65.dist-info → pygpt_net-2.6.66.dist-info}/entry_points.txt +0 -0
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.12.25 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import base64, datetime, os, requests
|
|
@@ -150,11 +150,10 @@ class VideoWorker(QRunnable):
|
|
|
150
150
|
self.fps = 24
|
|
151
151
|
self.seed: Optional[int] = None
|
|
152
152
|
self.negative_prompt: Optional[str] = None
|
|
153
|
-
self.generate_audio: bool = False # Veo 3
|
|
154
|
-
self.resolution: str = "720p" # Veo
|
|
153
|
+
self.generate_audio: bool = False # generation includes audio by default on Veo 3.x
|
|
154
|
+
self.resolution: str = "720p" # Veo supports 720p/1080p depending on variant
|
|
155
155
|
|
|
156
156
|
# limits / capabilities
|
|
157
|
-
# self.veo_max_num = 4 # Veo returns up to 4 videos
|
|
158
157
|
self.veo_max_num = 1 # limit to 1 in Gemini API
|
|
159
158
|
|
|
160
159
|
# fallbacks
|
|
@@ -187,42 +186,52 @@ class VideoWorker(QRunnable):
|
|
|
187
186
|
num = min(self.num, self.veo_max_num)
|
|
188
187
|
cfg_kwargs = {
|
|
189
188
|
"number_of_videos": num,
|
|
190
|
-
#"duration_seconds": self._duration_for_model(self.model, self.duration_seconds),
|
|
191
189
|
}
|
|
192
|
-
|
|
193
|
-
|
|
190
|
+
|
|
191
|
+
# normalize and set aspect ratio
|
|
192
|
+
ar = self._normalize_aspect_ratio(self.aspect_ratio)
|
|
193
|
+
if ar:
|
|
194
|
+
cfg_kwargs["aspect_ratio"] = ar
|
|
195
|
+
|
|
196
|
+
# normalize and set resolution if supported
|
|
197
|
+
res = self._normalize_resolution(self.resolution)
|
|
198
|
+
if res:
|
|
199
|
+
cfg_kwargs["resolution"] = res
|
|
200
|
+
|
|
201
|
+
# set optional controls
|
|
194
202
|
if self.seed is not None:
|
|
195
203
|
cfg_kwargs["seed"] = int(self.seed)
|
|
196
204
|
if self.negative_prompt:
|
|
197
205
|
cfg_kwargs["negative_prompt"] = self.negative_prompt
|
|
198
|
-
if self._is_veo3(self.model):
|
|
199
|
-
# Veo 3 supports audio and resolution
|
|
200
|
-
# WARN: but not Gemini API:
|
|
201
|
-
pass
|
|
202
|
-
"""
|
|
203
|
-
cfg_kwargs["generate_audio"] = bool(self.generate_audio)
|
|
204
|
-
if self.resolution:
|
|
205
|
-
cfg_kwargs["resolution"] = self.resolution
|
|
206
|
-
"""
|
|
207
|
-
|
|
208
|
-
config = gtypes.GenerateVideosConfig(**cfg_kwargs)
|
|
209
|
-
|
|
210
|
-
# build request
|
|
211
|
-
req_kwargs = {
|
|
212
|
-
"model": self.model or self.DEFAULT_VEO_MODEL,
|
|
213
|
-
"prompt": self.input_prompt or "",
|
|
214
|
-
"config": config,
|
|
215
|
-
}
|
|
216
206
|
|
|
217
|
-
#
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
req_kwargs["image"] = gtypes.Image.from_file(location=base_img)
|
|
207
|
+
# set durationSeconds when supported; fall back gracefully if rejected by model
|
|
208
|
+
cfg_try = dict(cfg_kwargs)
|
|
209
|
+
cfg_try["duration_seconds"] = int(self._duration_for_model(self.model, self.duration_seconds))
|
|
221
210
|
|
|
222
211
|
self.signals.status.emit(trans('vid.status.generating') + f": {self.input_prompt}...")
|
|
223
212
|
|
|
224
|
-
|
|
225
|
-
|
|
213
|
+
try:
|
|
214
|
+
config = gtypes.GenerateVideosConfig(**cfg_try)
|
|
215
|
+
operation = self.client.models.generate_videos(
|
|
216
|
+
model=self.model or self.DEFAULT_VEO_MODEL,
|
|
217
|
+
prompt=self.input_prompt or "",
|
|
218
|
+
config=config,
|
|
219
|
+
image=self._image_part_if_needed(),
|
|
220
|
+
video=None,
|
|
221
|
+
)
|
|
222
|
+
except Exception as e:
|
|
223
|
+
if "durationSeconds isn't supported" in str(e) or "Unrecognized" in str(e):
|
|
224
|
+
# retry without duration_seconds
|
|
225
|
+
config = gtypes.GenerateVideosConfig(**cfg_kwargs)
|
|
226
|
+
operation = self.client.models.generate_videos(
|
|
227
|
+
model=self.model or self.DEFAULT_VEO_MODEL,
|
|
228
|
+
prompt=self.input_prompt or "",
|
|
229
|
+
config=config,
|
|
230
|
+
image=self._image_part_if_needed(),
|
|
231
|
+
video=None,
|
|
232
|
+
)
|
|
233
|
+
else:
|
|
234
|
+
raise
|
|
226
235
|
|
|
227
236
|
# poll until done
|
|
228
237
|
while not getattr(operation, "done", False):
|
|
@@ -258,6 +267,22 @@ class VideoWorker(QRunnable):
|
|
|
258
267
|
|
|
259
268
|
# ---------- helpers ----------
|
|
260
269
|
|
|
270
|
+
def _normalize_aspect_ratio(self, ar: str) -> str:
|
|
271
|
+
"""Normalize aspect ratio to Veo-supported values."""
|
|
272
|
+
val = (ar or "").strip()
|
|
273
|
+
return val if val in ("16:9", "9:16") else "16:9"
|
|
274
|
+
|
|
275
|
+
def _normalize_resolution(self, res: str) -> Optional[str]:
|
|
276
|
+
"""Normalize resolution to '720p' or '1080p'."""
|
|
277
|
+
val = (res or "").lower().replace(" ", "")
|
|
278
|
+
if val in ("720p", "1080p"):
|
|
279
|
+
return val
|
|
280
|
+
if val in ("1280x720", "720x1280"):
|
|
281
|
+
return "720p"
|
|
282
|
+
if val in ("1920x1080", "1080x1920"):
|
|
283
|
+
return "1080p"
|
|
284
|
+
return None
|
|
285
|
+
|
|
261
286
|
def _is_veo3(self, model_id: str) -> bool:
|
|
262
287
|
mid = str(model_id or "").lower()
|
|
263
288
|
return mid.startswith("veo-3.")
|
|
@@ -265,20 +290,32 @@ class VideoWorker(QRunnable):
|
|
|
265
290
|
def _supports_image_to_video(self, model_id: str) -> bool:
|
|
266
291
|
"""Return True if the model supports image->video."""
|
|
267
292
|
mid = str(model_id or "").lower()
|
|
268
|
-
|
|
269
|
-
|
|
293
|
+
return any(p in mid for p in (
|
|
294
|
+
"veo-2.0",
|
|
295
|
+
"veo-3.0-generate",
|
|
296
|
+
"veo-3.0-fast-generate",
|
|
297
|
+
"veo-3.1-generate",
|
|
298
|
+
"veo-3.1-fast-generate",
|
|
299
|
+
))
|
|
270
300
|
|
|
271
301
|
def _duration_for_model(self, model_id: str, requested: int) -> int:
|
|
272
302
|
"""Adjust duration constraints to model-specific limits."""
|
|
273
303
|
mid = str(model_id or "").lower()
|
|
274
304
|
if "veo-2.0" in mid:
|
|
275
|
-
# Veo 2 supports 5–8s, default 8s.
|
|
276
305
|
return max(5, min(8, int(requested or 8)))
|
|
306
|
+
if "veo-3.1" in mid:
|
|
307
|
+
return max(4, min(8, int(requested or 8)))
|
|
277
308
|
if "veo-3.0" in mid:
|
|
278
|
-
|
|
279
|
-
return int(requested or 8)
|
|
309
|
+
return max(4, min(8, int(requested or 8)))
|
|
280
310
|
return int(requested or 8)
|
|
281
311
|
|
|
312
|
+
def _image_part_if_needed(self) -> Optional[gtypes.Image]:
|
|
313
|
+
"""Return Image part when in image-to-video mode and supported."""
|
|
314
|
+
if self.mode != Video.MODE_IMAGE_TO_VIDEO:
|
|
315
|
+
return None
|
|
316
|
+
base_img = self._first_image_attachment(self.attachments)
|
|
317
|
+
return gtypes.Image.from_file(location=base_img) if base_img else None
|
|
318
|
+
|
|
282
319
|
def _first_image_attachment(self, attachments: Dict[str, Any]) -> Optional[str]:
|
|
283
320
|
"""Return path of the first image attachment, if any."""
|
|
284
321
|
for _, att in (attachments or {}).items():
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.12.25 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from openai import OpenAI
|
|
@@ -38,6 +38,7 @@ from .store import Store
|
|
|
38
38
|
from .summarizer import Summarizer
|
|
39
39
|
from .tools import Tools
|
|
40
40
|
from .vision import Vision
|
|
41
|
+
from .video import Video
|
|
41
42
|
|
|
42
43
|
|
|
43
44
|
class ApiOpenAI:
|
|
@@ -63,6 +64,7 @@ class ApiOpenAI:
|
|
|
63
64
|
self.summarizer = Summarizer(window)
|
|
64
65
|
self.tools = Tools(window)
|
|
65
66
|
self.vision = Vision(window)
|
|
67
|
+
self.video = Video(window)
|
|
66
68
|
self.client = None
|
|
67
69
|
self.locked = False
|
|
68
70
|
self.last_client_args = None # last client args used, for debug purposes
|
|
@@ -87,7 +89,7 @@ class ApiOpenAI:
|
|
|
87
89
|
self,
|
|
88
90
|
context: BridgeContext,
|
|
89
91
|
extra: dict = None,
|
|
90
|
-
rt_signals
|
|
92
|
+
rt_signals=None
|
|
91
93
|
) -> bool:
|
|
92
94
|
"""
|
|
93
95
|
Call OpenAI API
|
|
@@ -157,7 +159,7 @@ class ApiOpenAI:
|
|
|
157
159
|
if is_realtime:
|
|
158
160
|
return True
|
|
159
161
|
|
|
160
|
-
if fixtures.is_enabled("stream"):
|
|
162
|
+
if fixtures.is_enabled("stream"): # fake stream for testing
|
|
161
163
|
use_responses_api = False
|
|
162
164
|
response = fixtures.get_stream_generator(ctx)
|
|
163
165
|
else:
|
|
@@ -181,12 +183,20 @@ class ApiOpenAI:
|
|
|
181
183
|
|
|
182
184
|
self.vision.append_images(ctx) # append images to ctx if provided
|
|
183
185
|
|
|
184
|
-
# image
|
|
186
|
+
# image / video
|
|
185
187
|
elif mode == MODE_IMAGE:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
media_mode = self.window.controller.media.get_mode()
|
|
189
|
+
if media_mode == "video":
|
|
190
|
+
if context.model and context.model.is_video_output():
|
|
191
|
+
return self.video.generate(
|
|
192
|
+
context=context,
|
|
193
|
+
extra=extra,
|
|
194
|
+
) # async handled if allowed
|
|
195
|
+
elif media_mode == "image":
|
|
196
|
+
return self.image.generate(
|
|
197
|
+
context=context,
|
|
198
|
+
extra=extra,
|
|
199
|
+
)
|
|
190
200
|
|
|
191
201
|
# vision
|
|
192
202
|
elif mode == MODE_VISION:
|
|
@@ -294,13 +304,13 @@ class ApiOpenAI:
|
|
|
294
304
|
messages.append({"role": "user", "content": prompt})
|
|
295
305
|
additional_kwargs = {}
|
|
296
306
|
# if max_tokens > 0:
|
|
297
|
-
|
|
307
|
+
# additional_kwargs["max_tokens"] = max_tokens
|
|
298
308
|
|
|
299
309
|
# tools / functions
|
|
300
310
|
tools = self.window.core.api.openai.tools.prepare(model, functions)
|
|
301
311
|
if len(tools) > 0 and "disable_tools" not in extra:
|
|
302
312
|
additional_kwargs["tools"] = tools
|
|
303
|
-
|
|
313
|
+
|
|
304
314
|
try:
|
|
305
315
|
response = client.chat.completions.create(
|
|
306
316
|
messages=messages,
|
|
@@ -349,4 +359,4 @@ class ApiOpenAI:
|
|
|
349
359
|
self.client = None
|
|
350
360
|
except Exception as e:
|
|
351
361
|
self.window.core.debug.log(e)
|
|
352
|
-
print("Error closing client:", e)
|
|
362
|
+
print("Error closing client:", e)
|
|
File without changes
|