pygpt-net 2.5.98.post1__py3-none-any.whl → 2.6.0.post1__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 +9 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/controller/ctx/ctx.py +7 -2
- pygpt_net/core/agents/runners/openai_workflow.py +9 -6
- pygpt_net/core/render/plain/pid.py +3 -2
- pygpt_net/core/render/web/body.py +21 -5
- pygpt_net/core/render/web/pid.py +26 -6
- pygpt_net/core/render/web/renderer.py +4 -10
- pygpt_net/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/css/style.dark.css +13 -0
- pygpt_net/data/css/style.light.css +29 -0
- pygpt_net/data/icon.ico +0 -0
- pygpt_net/data/icon.png +0 -0
- pygpt_net/data/locale/locale.de.ini +1 -1
- pygpt_net/data/locale/locale.en.ini +1 -1
- pygpt_net/data/locale/locale.es.ini +1 -1
- pygpt_net/data/locale/locale.fr.ini +1 -1
- pygpt_net/data/locale/locale.it.ini +1 -1
- pygpt_net/data/locale/locale.pl.ini +1 -1
- pygpt_net/data/locale/locale.uk.ini +1 -1
- pygpt_net/data/locale/locale.zh.ini +1 -1
- pygpt_net/provider/agents/llama_index/code_act.py +5 -4
- pygpt_net/provider/agents/llama_index/openai.py +3 -3
- pygpt_net/provider/agents/llama_index/openai_assistant.py +3 -3
- pygpt_net/provider/agents/llama_index/planner.py +6 -6
- pygpt_net/provider/agents/llama_index/react.py +3 -7
- pygpt_net/provider/agents/llama_index/react_workflow.py +4 -7
- pygpt_net/provider/agents/openai/agent_b2b.py +45 -10
- pygpt_net/provider/agents/openai/agent_planner.py +24 -2
- pygpt_net/provider/agents/openai/agent_with_experts.py +1 -38
- pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +23 -38
- pygpt_net/provider/agents/openai/agent_with_feedback.py +23 -2
- pygpt_net/provider/agents/openai/bot_researcher.py +2 -6
- pygpt_net/provider/agents/openai/evolve.py +23 -2
- pygpt_net/provider/core/config/patch.py +7 -0
- pygpt_net/provider/gpt/__init__.py +21 -3
- pygpt_net/tools/html_canvas/ui/widgets.py +4 -0
- pygpt_net/tools/media_player/tool.py +11 -2
- pygpt_net/tools/media_player/ui/widgets.py +99 -94
- pygpt_net/ui/widget/calendar/select.py +10 -2
- pygpt_net/ui/widget/filesystem/explorer.py +1 -0
- {pygpt_net-2.5.98.post1.dist-info → pygpt_net-2.6.0.post1.dist-info}/METADATA +14 -147
- {pygpt_net-2.5.98.post1.dist-info → pygpt_net-2.6.0.post1.dist-info}/RECORD +47 -47
- {pygpt_net-2.5.98.post1.dist-info → pygpt_net-2.6.0.post1.dist-info}/LICENSE +0 -0
- {pygpt_net-2.5.98.post1.dist-info → pygpt_net-2.6.0.post1.dist-info}/WHEEL +0 -0
- {pygpt_net-2.5.98.post1.dist-info → pygpt_net-2.6.0.post1.dist-info}/entry_points.txt +0 -0
|
@@ -6,15 +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:
|
|
9
|
+
# Updated Date: 2025.08.12 15:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
13
13
|
|
|
14
14
|
from PySide6.QtCore import Qt, QUrl, QTimer
|
|
15
15
|
from PySide6.QtGui import QIcon, QAction
|
|
16
|
-
from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
|
|
17
|
-
from PySide6.QtMultimediaWidgets import QVideoWidget
|
|
18
16
|
from PySide6.QtWidgets import (QWidget, QPushButton, QHBoxLayout, QVBoxLayout,
|
|
19
17
|
QFileDialog, QSlider, QLabel, QStyle, QSizePolicy, QMenu, QMessageBox)
|
|
20
18
|
|
|
@@ -27,7 +25,12 @@ class VideoPlayerWidget(QWidget):
|
|
|
27
25
|
def __init__(self, window=None):
|
|
28
26
|
super().__init__(window)
|
|
29
27
|
self.window = window
|
|
30
|
-
self.player =
|
|
28
|
+
self.player = None
|
|
29
|
+
self.audio = None
|
|
30
|
+
self.video = None
|
|
31
|
+
self._QMediaPlayer = None
|
|
32
|
+
self._QAudioOutput = None
|
|
33
|
+
self._QVideoWidget = None
|
|
31
34
|
self.loaded = False
|
|
32
35
|
self.path = None
|
|
33
36
|
self.stopped = False
|
|
@@ -41,10 +44,9 @@ class VideoPlayerWidget(QWidget):
|
|
|
41
44
|
self.autoplay_timer.setSingleShot(True)
|
|
42
45
|
self.autoplay_timer.timeout.connect(self.after_loaded)
|
|
43
46
|
|
|
44
|
-
self.
|
|
45
|
-
self.
|
|
46
|
-
self.
|
|
47
|
-
self.video.setMinimumSize(640, 480)
|
|
47
|
+
self._video_placeholder = QWidget(window)
|
|
48
|
+
self._video_placeholder.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
49
|
+
self._video_placeholder.setMinimumSize(640, 480)
|
|
48
50
|
|
|
49
51
|
self.btn_play_pause = QPushButton()
|
|
50
52
|
self.btn_play_pause.setEnabled(False)
|
|
@@ -81,7 +83,6 @@ class VideoPlayerWidget(QWidget):
|
|
|
81
83
|
layout_bottom.addStretch()
|
|
82
84
|
layout_bottom.addLayout(layout_volume)
|
|
83
85
|
|
|
84
|
-
# layout
|
|
85
86
|
layout_controls = QHBoxLayout()
|
|
86
87
|
layout_controls.addWidget(self.btn_open)
|
|
87
88
|
layout_controls.addWidget(self.btn_play_pause)
|
|
@@ -93,10 +94,11 @@ class VideoPlayerWidget(QWidget):
|
|
|
93
94
|
layout_slider.addWidget(self.label_duration)
|
|
94
95
|
|
|
95
96
|
layout = QVBoxLayout()
|
|
96
|
-
layout.addWidget(self.
|
|
97
|
+
layout.addWidget(self._video_placeholder)
|
|
97
98
|
layout.addLayout(layout_slider)
|
|
98
99
|
layout.addLayout(layout_controls)
|
|
99
100
|
layout.addLayout(layout_bottom)
|
|
101
|
+
self._layout_main = layout
|
|
100
102
|
|
|
101
103
|
self.setLayout(layout)
|
|
102
104
|
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
@@ -105,33 +107,59 @@ class VideoPlayerWidget(QWidget):
|
|
|
105
107
|
self.update_play_pause_icon()
|
|
106
108
|
self.bind_signals()
|
|
107
109
|
|
|
108
|
-
def
|
|
109
|
-
"""
|
|
110
|
-
self.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
def _ensure_multimedia(self):
|
|
111
|
+
"""Ensure multimedia components are loaded"""
|
|
112
|
+
if self.player is not None:
|
|
113
|
+
return
|
|
114
|
+
from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
|
|
115
|
+
from PySide6.QtMultimediaWidgets import QVideoWidget
|
|
116
|
+
self._QMediaPlayer = QMediaPlayer
|
|
117
|
+
self._QAudioOutput = QAudioOutput
|
|
118
|
+
self._QVideoWidget = QVideoWidget
|
|
119
|
+
self.player = QMediaPlayer(self.window)
|
|
120
|
+
self.audio = QAudioOutput()
|
|
121
|
+
self.player.setAudioOutput(self.audio)
|
|
122
|
+
self.video = QVideoWidget(self.window)
|
|
123
|
+
self.video.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
124
|
+
self.video.setMinimumSize(640, 480)
|
|
125
|
+
idx = self._layout_main.indexOf(self._video_placeholder)
|
|
126
|
+
if idx != -1:
|
|
127
|
+
self._layout_main.removeWidget(self._video_placeholder)
|
|
128
|
+
self._video_placeholder.setParent(None)
|
|
129
|
+
self._layout_main.insertWidget(idx, self.video)
|
|
130
|
+
else:
|
|
131
|
+
self._layout_main.insertWidget(0, self.video)
|
|
132
|
+
self.video.mousePressEvent = self.video_widget_clicked
|
|
133
|
+
self.player.setVideoOutput(self.video)
|
|
114
134
|
self.player.positionChanged.connect(self.position_changed)
|
|
115
135
|
self.player.durationChanged.connect(self.duration_changed)
|
|
116
136
|
self.player.playbackStateChanged.connect(self.on_playback_state_changed)
|
|
117
137
|
self.player.mediaStatusChanged.connect(self.media_status_changed)
|
|
118
|
-
self.
|
|
138
|
+
self.update_mute_icon()
|
|
139
|
+
self.update_volume_slider()
|
|
140
|
+
self.adjust_volume(self.volume_slider.value())
|
|
141
|
+
self.set_muted(self.btn_mute.isChecked())
|
|
119
142
|
|
|
120
|
-
|
|
121
|
-
|
|
143
|
+
def bind_signals(self):
|
|
144
|
+
"""Bind signals to player controls"""
|
|
145
|
+
self.btn_open.clicked.connect(self.open_file)
|
|
146
|
+
self.btn_play_pause.clicked.connect(self.toggle_play_pause)
|
|
147
|
+
self.btn_stop.clicked.connect(self.stop_video)
|
|
148
|
+
self.btn_mute.toggled.connect(self.set_muted)
|
|
122
149
|
self.volume_slider.valueChanged.connect(self.adjust_volume)
|
|
123
150
|
self.slider.sliderPressed.connect(self.on_slider_pressed)
|
|
124
151
|
self.slider.sliderReleased.connect(self.on_slider_released)
|
|
152
|
+
self._video_placeholder.mousePressEvent = self.video_widget_clicked
|
|
125
153
|
self.seeking = False
|
|
126
154
|
|
|
127
155
|
def update(self):
|
|
128
|
-
"""Update player"""
|
|
156
|
+
"""Update player state"""
|
|
129
157
|
self.update_mute_icon()
|
|
130
158
|
self.update_volume_slider()
|
|
131
159
|
self.update_label_path()
|
|
132
160
|
|
|
133
161
|
def open_file(self):
|
|
134
|
-
"""Open file"""
|
|
162
|
+
"""Open video file dialog"""
|
|
135
163
|
path, _ = QFileDialog.getOpenFileName(
|
|
136
164
|
self,
|
|
137
165
|
trans("action.video.open"),
|
|
@@ -152,12 +180,13 @@ class VideoPlayerWidget(QWidget):
|
|
|
152
180
|
self.stopped = False
|
|
153
181
|
self.seeking = False
|
|
154
182
|
self.reset()
|
|
183
|
+
self._ensure_multimedia()
|
|
155
184
|
self.player.setSource(QUrl.fromLocalFile(path))
|
|
156
185
|
self.set_path(path)
|
|
157
186
|
self.enable()
|
|
158
187
|
self.autoplay_timer.start()
|
|
159
188
|
self.update()
|
|
160
|
-
self.window.tools.get("player").store_path(path)
|
|
189
|
+
self.window.tools.get("player").store_path(path)
|
|
161
190
|
|
|
162
191
|
def enable(self):
|
|
163
192
|
"""Enable player"""
|
|
@@ -170,12 +199,13 @@ class VideoPlayerWidget(QWidget):
|
|
|
170
199
|
if not self.loaded and self.path:
|
|
171
200
|
self.open(self.path)
|
|
172
201
|
else:
|
|
202
|
+
self._ensure_multimedia()
|
|
173
203
|
self.update_audio()
|
|
174
204
|
self.player.play()
|
|
175
205
|
|
|
176
206
|
def stop_video(self):
|
|
177
207
|
"""Stop video"""
|
|
178
|
-
if self.player.source():
|
|
208
|
+
if self.player and self.player.source():
|
|
179
209
|
self.player.stop()
|
|
180
210
|
self.stopped = True
|
|
181
211
|
self.slider.setValue(0)
|
|
@@ -185,16 +215,15 @@ class VideoPlayerWidget(QWidget):
|
|
|
185
215
|
|
|
186
216
|
def toggle_play_pause(self):
|
|
187
217
|
"""Toggle play/pause"""
|
|
188
|
-
if self.player.playbackState() ==
|
|
218
|
+
if self.player and self._QMediaPlayer and self.player.playbackState() == self._QMediaPlayer.PlayingState:
|
|
189
219
|
self.player.pause()
|
|
190
220
|
else:
|
|
191
221
|
self.play_video()
|
|
192
|
-
|
|
193
222
|
self.on_playback_state_changed()
|
|
194
223
|
|
|
195
224
|
def reset(self):
|
|
196
225
|
"""Reset player to default state"""
|
|
197
|
-
if self.player.source():
|
|
226
|
+
if self.player and self.player.source():
|
|
198
227
|
self.player.stop()
|
|
199
228
|
self.player.setSource(QUrl())
|
|
200
229
|
self.btn_play_pause.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
|
|
@@ -206,7 +235,7 @@ class VideoPlayerWidget(QWidget):
|
|
|
206
235
|
def reset_player(self):
|
|
207
236
|
"""Reset player to default state"""
|
|
208
237
|
if self.player is not None:
|
|
209
|
-
if self.player.playbackState() ==
|
|
238
|
+
if self._QMediaPlayer and self.player.playbackState() == self._QMediaPlayer.PlayingState:
|
|
210
239
|
self.player.stop()
|
|
211
240
|
self.player.setSource(QUrl())
|
|
212
241
|
|
|
@@ -216,60 +245,23 @@ class VideoPlayerWidget(QWidget):
|
|
|
216
245
|
|
|
217
246
|
:param point: point
|
|
218
247
|
"""
|
|
219
|
-
if not self.path or not self.player.source():
|
|
248
|
+
if not self.path or not (self.player and self.player.source()):
|
|
220
249
|
return
|
|
221
|
-
|
|
222
|
-
# TODO: implement grab screenshot
|
|
223
|
-
"""
|
|
224
|
-
use_actions = []
|
|
225
|
-
action_as_attachment = QAction(
|
|
226
|
-
QIcon(":/icons/attachment.svg"),
|
|
227
|
-
trans('action.use.attachment'),
|
|
228
|
-
self,
|
|
229
|
-
)
|
|
230
|
-
action_as_attachment.triggered.connect(
|
|
231
|
-
lambda: self.use_as_attachment(),
|
|
232
|
-
)
|
|
233
|
-
action_as_image = QAction(
|
|
234
|
-
QIcon(":/icons/image.svg"),
|
|
235
|
-
trans('action.use.image'),
|
|
236
|
-
self,
|
|
237
|
-
)
|
|
238
|
-
action_as_image.triggered.connect(
|
|
239
|
-
lambda: self.use_as_image(),
|
|
240
|
-
)
|
|
241
|
-
use_actions.append(action_as_image)
|
|
242
|
-
use_actions.append(action_as_attachment)
|
|
243
|
-
|
|
244
|
-
# use by type
|
|
245
|
-
if use_actions:
|
|
246
|
-
# use menu
|
|
247
|
-
use_menu = QMenu(trans('action.use'), self)
|
|
248
|
-
for action in use_actions:
|
|
249
|
-
use_menu.addAction(action)
|
|
250
|
-
context_menu.addMenu(use_menu)
|
|
251
|
-
"""
|
|
252
250
|
context_menu = QMenu(self)
|
|
253
251
|
save_as_action = QAction(QIcon(":/icons/save.svg"), trans("action.save_as"), self)
|
|
254
252
|
save_as_action.triggered.connect(
|
|
255
253
|
lambda: self.window.tools.get("player").save_as_file()
|
|
256
254
|
)
|
|
257
|
-
# full_screen_action = QAction('Fullscreen', self)
|
|
258
|
-
# full_screen_action.triggered.connect(self.toggle_fullscreen)
|
|
259
255
|
context_menu.addAction(save_as_action)
|
|
260
|
-
|
|
261
|
-
# context_menu.addAction(full_screen_action)
|
|
262
256
|
context_menu.exec(self.mapToGlobal(point))
|
|
263
257
|
|
|
264
258
|
def use_as_attachment(self):
|
|
265
259
|
"""Use as attachment"""
|
|
266
|
-
# TODO: implement grab screenshot
|
|
267
260
|
path = self.window.tools.get("player").grab_frame()
|
|
268
261
|
self.window.controller.files.use_attachment(path)
|
|
269
262
|
|
|
270
263
|
def use_as_image(self):
|
|
271
264
|
"""Use as image"""
|
|
272
|
-
# TODO: implement grab screenshot
|
|
273
265
|
path = self.window.tools.get("player").grab_frame()
|
|
274
266
|
self.window.controller.painter.open_external(path)
|
|
275
267
|
|
|
@@ -279,9 +271,10 @@ class VideoPlayerWidget(QWidget):
|
|
|
279
271
|
|
|
280
272
|
:param value: volume value
|
|
281
273
|
"""
|
|
282
|
-
self.window.core.config.set("video.player.volume", value)
|
|
283
|
-
|
|
284
|
-
|
|
274
|
+
self.window.core.config.set("video.player.volume", value)
|
|
275
|
+
if self.audio:
|
|
276
|
+
volume = value / 100.0
|
|
277
|
+
self.audio.setVolume(volume)
|
|
285
278
|
|
|
286
279
|
def set_path(self, path: str):
|
|
287
280
|
"""
|
|
@@ -292,7 +285,8 @@ class VideoPlayerWidget(QWidget):
|
|
|
292
285
|
self.path = path
|
|
293
286
|
self.update_label_path()
|
|
294
287
|
if not self.loaded and self.path:
|
|
295
|
-
if not self.player.source():
|
|
288
|
+
if not self.player or not self.player.source():
|
|
289
|
+
self._ensure_multimedia()
|
|
296
290
|
self.reset()
|
|
297
291
|
self.player.setSource(QUrl.fromLocalFile(self.path))
|
|
298
292
|
self.enable()
|
|
@@ -305,7 +299,6 @@ class VideoPlayerWidget(QWidget):
|
|
|
305
299
|
|
|
306
300
|
def toggle_fullscreen(self):
|
|
307
301
|
"""Toggle fullscreen"""
|
|
308
|
-
# TODO: implement fullscreen
|
|
309
302
|
if self.window.ui.dialog['video_player'].isFullScreen():
|
|
310
303
|
self.window.ui.dialog['video_player'].showNormal()
|
|
311
304
|
else:
|
|
@@ -317,24 +310,27 @@ class VideoPlayerWidget(QWidget):
|
|
|
317
310
|
|
|
318
311
|
:param status: status
|
|
319
312
|
"""
|
|
320
|
-
if
|
|
313
|
+
if not self._QMediaPlayer:
|
|
314
|
+
return
|
|
315
|
+
if status == self._QMediaPlayer.LoadedMedia:
|
|
321
316
|
self.loaded = True
|
|
322
317
|
self.force_resize()
|
|
323
|
-
elif status ==
|
|
318
|
+
elif status == self._QMediaPlayer.InvalidMedia:
|
|
324
319
|
self.loaded = False
|
|
325
320
|
self.window.ui.dialogs.alert("Failed to load media file, missing video codec?")
|
|
326
321
|
|
|
327
322
|
def after_loaded(self):
|
|
328
323
|
"""Start playback after loaded"""
|
|
329
|
-
if self.player.mediaStatus() ==
|
|
324
|
+
if self.player and self._QMediaPlayer and self.player.mediaStatus() == self._QMediaPlayer.LoadedMedia:
|
|
330
325
|
self.loaded = True
|
|
331
|
-
if self.player.playbackState() !=
|
|
326
|
+
if self.player.playbackState() != self._QMediaPlayer.PlayingState:
|
|
332
327
|
self.toggle_play_pause()
|
|
333
328
|
self.force_resize()
|
|
334
329
|
|
|
335
330
|
def force_resize(self):
|
|
336
331
|
"""Force resize fix"""
|
|
337
|
-
|
|
332
|
+
if not self.video:
|
|
333
|
+
return
|
|
338
334
|
self.video.hide()
|
|
339
335
|
self.video.show()
|
|
340
336
|
self.video.update()
|
|
@@ -377,8 +373,9 @@ class VideoPlayerWidget(QWidget):
|
|
|
377
373
|
|
|
378
374
|
:param state: muted
|
|
379
375
|
"""
|
|
380
|
-
self.audio
|
|
381
|
-
|
|
376
|
+
if self.audio:
|
|
377
|
+
self.audio.setMuted(state)
|
|
378
|
+
self.window.core.config.set("video.player.volume.mute", state)
|
|
382
379
|
|
|
383
380
|
def set_position(self, position):
|
|
384
381
|
"""
|
|
@@ -386,23 +383,26 @@ class VideoPlayerWidget(QWidget):
|
|
|
386
383
|
|
|
387
384
|
:param position: position
|
|
388
385
|
"""
|
|
389
|
-
if self.player
|
|
386
|
+
if not self.player:
|
|
387
|
+
return
|
|
388
|
+
if self._QMediaPlayer and self.player.playbackState() == self._QMediaPlayer.PlayingState:
|
|
390
389
|
self.player.pause()
|
|
391
|
-
|
|
392
390
|
self.player.positionChanged.connect(self.on_position_set)
|
|
393
391
|
self.player.setPosition(position)
|
|
394
392
|
|
|
395
393
|
def on_slider_pressed(self):
|
|
396
394
|
"""Slider pressed"""
|
|
397
395
|
self.seeking = True
|
|
398
|
-
self.player
|
|
396
|
+
if self.player:
|
|
397
|
+
self.player.pause()
|
|
399
398
|
|
|
400
399
|
def on_slider_released(self):
|
|
401
400
|
"""Slider released"""
|
|
402
401
|
if self.seeking:
|
|
403
|
-
self.player
|
|
404
|
-
|
|
405
|
-
|
|
402
|
+
if self.player:
|
|
403
|
+
self.player.setPosition(self.slider.value())
|
|
404
|
+
self.update_audio()
|
|
405
|
+
self.player.play()
|
|
406
406
|
self.seeking = False
|
|
407
407
|
|
|
408
408
|
def position_changed(self, position):
|
|
@@ -421,9 +421,12 @@ class VideoPlayerWidget(QWidget):
|
|
|
421
421
|
|
|
422
422
|
:param position: position
|
|
423
423
|
"""
|
|
424
|
-
if self.seeking:
|
|
425
|
-
|
|
426
|
-
|
|
424
|
+
if self.seeking and self.player:
|
|
425
|
+
try:
|
|
426
|
+
self.player.positionChanged.disconnect(self.on_position_set)
|
|
427
|
+
except Exception:
|
|
428
|
+
pass
|
|
429
|
+
if self._QMediaPlayer and self.player.playbackState() == self._QMediaPlayer.PausedState:
|
|
427
430
|
QTimer.singleShot(100, self.player.play)
|
|
428
431
|
self.seeking = False
|
|
429
432
|
|
|
@@ -436,7 +439,7 @@ class VideoPlayerWidget(QWidget):
|
|
|
436
439
|
|
|
437
440
|
def update_play_pause_icon(self):
|
|
438
441
|
"""Update play/pause icon"""
|
|
439
|
-
if self.player.playbackState() ==
|
|
442
|
+
if self.player and self._QMediaPlayer and self.player.playbackState() == self._QMediaPlayer.PlayingState:
|
|
440
443
|
self.btn_play_pause.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
|
|
441
444
|
self.update_timer.start()
|
|
442
445
|
else:
|
|
@@ -445,21 +448,24 @@ class VideoPlayerWidget(QWidget):
|
|
|
445
448
|
|
|
446
449
|
def update_audio(self):
|
|
447
450
|
"""Re-assign audio"""
|
|
448
|
-
self.player.
|
|
449
|
-
|
|
451
|
+
if self.player and self.audio:
|
|
452
|
+
self.player.setAudioOutput(None)
|
|
453
|
+
self.player.setAudioOutput(self.audio)
|
|
450
454
|
|
|
451
455
|
def update_mute_icon(self):
|
|
452
456
|
"""Update mute icon"""
|
|
453
|
-
self.
|
|
457
|
+
if self.audio:
|
|
458
|
+
self.btn_mute.setChecked(self.audio.isMuted())
|
|
454
459
|
|
|
455
460
|
def update_volume_slider(self):
|
|
456
461
|
"""Update volume slider"""
|
|
457
|
-
self.
|
|
462
|
+
if self.audio:
|
|
463
|
+
self.volume_slider.setValue(int(self.audio.volume() * 100))
|
|
458
464
|
|
|
459
465
|
def update_ui(self):
|
|
460
466
|
"""Update UI"""
|
|
461
|
-
|
|
462
|
-
|
|
467
|
+
if self.player:
|
|
468
|
+
self.position_changed(self.player.position())
|
|
463
469
|
|
|
464
470
|
def format_time(self, ms):
|
|
465
471
|
"""
|
|
@@ -467,7 +473,6 @@ class VideoPlayerWidget(QWidget):
|
|
|
467
473
|
|
|
468
474
|
:param ms: milliseconds
|
|
469
475
|
"""
|
|
470
|
-
# convert ms to H:M:S format
|
|
471
476
|
seconds = (ms // 1000) % 60
|
|
472
477
|
minutes = (ms // 60000) % 60
|
|
473
478
|
hours = (ms // 3600000)
|
|
@@ -90,6 +90,13 @@ class CalendarSelect(QCalendarWidget):
|
|
|
90
90
|
:param rect: Rectangle
|
|
91
91
|
:param date: Date
|
|
92
92
|
"""
|
|
93
|
+
theme = self.window.core.config.get("theme")
|
|
94
|
+
if theme.startswith('dark'):
|
|
95
|
+
counter_bg = QColor(40, 40, 40)
|
|
96
|
+
counter_font = QColor(255, 255, 255)
|
|
97
|
+
else:
|
|
98
|
+
counter_bg = QColor(240, 240, 240)
|
|
99
|
+
counter_font = QColor(0, 0, 0)
|
|
93
100
|
|
|
94
101
|
super().paintCell(painter, rect, date)
|
|
95
102
|
|
|
@@ -112,9 +119,10 @@ class CalendarSelect(QCalendarWidget):
|
|
|
112
119
|
20,
|
|
113
120
|
)
|
|
114
121
|
painter.save()
|
|
115
|
-
painter.setBrush(QBrush(
|
|
122
|
+
painter.setBrush(QBrush(counter_bg))
|
|
123
|
+
painter.setPen(Qt.NoPen)
|
|
116
124
|
painter.drawRect(task_rect)
|
|
117
|
-
painter.setPen(
|
|
125
|
+
painter.setPen(counter_font)
|
|
118
126
|
painter.setFont(QFont('Lato', self.font_size))
|
|
119
127
|
painter.drawText(
|
|
120
128
|
task_rect,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pygpt-net
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0.post1
|
|
4
4
|
Summary: Desktop AI Assistant powered by: OpenAI GPT-5, o1, o3, GPT-4, Gemini, Claude, Grok, DeepSeek, and other models supported by Llama Index, and Ollama. Chatbot, agents, completion, image generation, vision analysis, speech-to-text, plugins, internet access, file handling, command execution and more.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: py_gpt,py-gpt,pygpt,desktop,app,o1,o3,gpt-5,gpt,gpt4,gpt-4o,gpt-4v,gpt3.5,gpt-4,gpt-4-vision,gpt-3.5,llama3,mistral,gemini,grok,deepseek,bielik,claude,tts,whisper,vision,chatgpt,dall-e,chat,chatbot,assistant,text completion,image generation,ai,api,openai,api key,langchain,llama-index,ollama,presets,ui,qt,pyside
|
|
@@ -98,13 +98,15 @@ Requires-Dist: youtube-transcript-api (>=0.6.3,<0.7.0)
|
|
|
98
98
|
Project-URL: Documentation, https://pygpt.readthedocs.io/
|
|
99
99
|
Project-URL: Homepage, https://pygpt.net
|
|
100
100
|
Project-URL: Repository, https://github.com/szczyglis-dev/py-gpt
|
|
101
|
+
Project-URL: changelog, https://github.com/szczyglis-dev/py-gpt/blob/master/CHANGELOG.md
|
|
102
|
+
Project-URL: issues, https://github.com/szczyglis-dev/py-gpt/issues
|
|
101
103
|
Description-Content-Type: text/markdown
|
|
102
104
|
|
|
103
105
|
# PyGPT - Desktop AI Assistant
|
|
104
106
|
|
|
105
107
|
[](https://snapcraft.io/pygpt)
|
|
106
108
|
|
|
107
|
-
Release: **2.
|
|
109
|
+
Release: **2.6.0** | build: **2025-08-13** | Python: **>=3.10, <3.14**
|
|
108
110
|
|
|
109
111
|
> Official website: https://pygpt.net | Documentation: https://pygpt.readthedocs.io
|
|
110
112
|
>
|
|
@@ -3384,7 +3386,7 @@ Enable/disable remote tools, like Web Search or Image generation to use in OpenA
|
|
|
3384
3386
|
|
|
3385
3387
|
- `Verbose` - enables verbose mode.
|
|
3386
3388
|
|
|
3387
|
-
- `Split response messages` - Split response messages to separated context items in OpenAI Agents mode.
|
|
3389
|
+
- `Split response messages` - Split response messages to separated context items in OpenAI Agents mode.
|
|
3388
3390
|
|
|
3389
3391
|
**Autonomous (Legacy agents)**
|
|
3390
3392
|
|
|
@@ -4369,6 +4371,15 @@ may consume additional tokens that are not displayed in the main window.
|
|
|
4369
4371
|
|
|
4370
4372
|
## Recent changes:
|
|
4371
4373
|
|
|
4374
|
+
**2.6.0 (2025-08-13)**
|
|
4375
|
+
|
|
4376
|
+
- Added split responses to the OpenAI Agents in non-streaming mode.
|
|
4377
|
+
- Disabled auto-scroll when manually scrolled to the top.
|
|
4378
|
+
- Increased scrollbar width in the light theme.
|
|
4379
|
+
- Optimized the clearing of the streaming buffer.
|
|
4380
|
+
- Optimized imports.
|
|
4381
|
+
- Made CSS improvements.
|
|
4382
|
+
|
|
4372
4383
|
**2.5.98 (2025-08-12)**
|
|
4373
4384
|
|
|
4374
4385
|
- Added support for GPT-5 in LlamaIndex/Chat with Files mode.
|
|
@@ -4400,150 +4411,6 @@ may consume additional tokens that are not displayed in the main window.
|
|
|
4400
4411
|
- Fixed: Storing the last used context ID when empty.
|
|
4401
4412
|
- Fixed: Reloading items when an agent run is stopped.
|
|
4402
4413
|
|
|
4403
|
-
**2.5.93 (2025-08-08)**
|
|
4404
|
-
|
|
4405
|
-
- Added a new tool: Translate - in menu Tools - feature #123.
|
|
4406
|
-
|
|
4407
|
-
**2.5.92 (2025-08-08)**
|
|
4408
|
-
|
|
4409
|
-
- Added max files to store config option in Audio -> Cache.
|
|
4410
|
-
|
|
4411
|
-
**2.5.91 (2025-08-08)**
|
|
4412
|
-
|
|
4413
|
-
- Added GPT-5.
|
|
4414
|
-
- Added audio cache - #118.
|
|
4415
|
-
|
|
4416
|
-
**2.5.90 (2025-08-07)**
|
|
4417
|
-
|
|
4418
|
-
- Fix: Initialize context summary if a conversation starts with a tool call.
|
|
4419
|
-
- Fix: Store splitter positions even if the object is deleted from memory.
|
|
4420
|
-
- Update: CSS improvements.
|
|
4421
|
-
|
|
4422
|
-
**2.5.89 (2025-08-07)**
|
|
4423
|
-
|
|
4424
|
-
- Added audio output device selection in Config -> Audio - issue #117
|
|
4425
|
-
- Added audio input and output backend selections in Config -> Audio.
|
|
4426
|
-
|
|
4427
|
-
**2.5.88 (2025-08-06)**
|
|
4428
|
-
|
|
4429
|
-
- Optimized the process of unloading tabs from memory.
|
|
4430
|
-
- Reduced initial RAM usage on launch.
|
|
4431
|
-
- Added a handler for the SIGTERM signal.
|
|
4432
|
-
|
|
4433
|
-
**2.5.87 (2025-08-05)**
|
|
4434
|
-
|
|
4435
|
-
- Optimized memory cleanup.
|
|
4436
|
-
|
|
4437
|
-
**2.5.86 (2025-08-04)**
|
|
4438
|
-
|
|
4439
|
-
- Optimized CPU and memory usage.
|
|
4440
|
-
- OpenAI vector stores tool added to Tools menu.
|
|
4441
|
-
- Fixed schema parsing for tools in Agent (OpenAI) mode.
|
|
4442
|
-
- Fixed multi-threading when uploading to remote vector store.
|
|
4443
|
-
- Fixed Urls open in Snap.
|
|
4444
|
-
|
|
4445
|
-
**2.5.85 (2025-08-02)**
|
|
4446
|
-
|
|
4447
|
-
- Added importer for models from providers in Config -> Models -> Import... - #127
|
|
4448
|
-
- Fix: options save in models editor.
|
|
4449
|
-
|
|
4450
|
-
**2.5.84 (2025-08-02)**
|
|
4451
|
-
|
|
4452
|
-
- Added a new OpenAI agent mode: Evolve. You can find its description in the documentation under the section Modes -> Agent (OpenAI).
|
|
4453
|
-
|
|
4454
|
-
**2.5.83 (2025-08-01)**
|
|
4455
|
-
|
|
4456
|
-
- Improved streaming in Agent (OpenAI) mode.
|
|
4457
|
-
- Improved loading of default options in presets.
|
|
4458
|
-
- Added context summary event if the kernel stopped.
|
|
4459
|
-
- Implemented dynamic width for opened combo boxes.
|
|
4460
|
-
|
|
4461
|
-
**2.5.82 (2025-08-01)**
|
|
4462
|
-
|
|
4463
|
-
- Added a new OpenAI agents: researcher, planner, feedback.
|
|
4464
|
-
|
|
4465
|
-
**2.5.81 (2025-07-31)**
|
|
4466
|
-
|
|
4467
|
-
- Disabled remote tools by default in non-OpenAI providers in Agent (OpenAI) mode.
|
|
4468
|
-
- Removed unsupported models from Agent (OpenAI) mode.
|
|
4469
|
-
|
|
4470
|
-
**2.5.80 (2025-07-30)**
|
|
4471
|
-
|
|
4472
|
-
- Improved the stop command in Agent mode: added saving of current output on break.
|
|
4473
|
-
- Fixed the experts providers list in Agent (LlamaIndex): now only Llama providers are displayed.
|
|
4474
|
-
- Added max_turns configuration to Agent (OpenAI), shared with Agents -> Max steps configuration option value.
|
|
4475
|
-
|
|
4476
|
-
**2.5.79 (2025-07-30)**
|
|
4477
|
-
|
|
4478
|
-
- Added prevent text break on input send when the cursor is inside the text.
|
|
4479
|
-
- Fix: updating expert IDs on preset save.
|
|
4480
|
-
- Added notification if no agent is selected.
|
|
4481
|
-
- Disabled FFmpeg warnings.
|
|
4482
|
-
|
|
4483
|
-
**2.5.78 (2025-07-30)**
|
|
4484
|
-
|
|
4485
|
-
- Added support for Python 3.13.
|
|
4486
|
-
- Fixed history prepare in Agent (LlamaIndex) mode.
|
|
4487
|
-
|
|
4488
|
-
**2.5.77 (2025-07-30)**
|
|
4489
|
-
|
|
4490
|
-
- Fix: history prepare in Agent (OpenAI) mode.
|
|
4491
|
-
- Agent response evaluation splitted into two modes: by the percentage of task completion and by accuracy.
|
|
4492
|
-
|
|
4493
|
-
**2.5.76 (2025-07-30)**
|
|
4494
|
-
|
|
4495
|
-
- Added a new mode: Agent (OpenAI) and integrated `openai-agents` into the app (beta).
|
|
4496
|
-
|
|
4497
|
-
**2.5.75 (2025-07-28)**
|
|
4498
|
-
|
|
4499
|
-
- Fix: context append in LlamaIndex agents.
|
|
4500
|
-
|
|
4501
|
-
**2.5.74 (2025-07-28)**
|
|
4502
|
-
|
|
4503
|
-
- Fix: mouse buttons handling in Computer use mode.
|
|
4504
|
-
- Added double click handler.
|
|
4505
|
-
|
|
4506
|
-
**2.5.73 (2025-07-28)**
|
|
4507
|
-
|
|
4508
|
-
- Stream rendering optimization, reduced CPU usage.
|
|
4509
|
-
- Fix: < and > rendering in math formulas.
|
|
4510
|
-
- Fix: do not automatically add tools to the agent when the inline plugin is enabled but the Tools option is not enabled.
|
|
4511
|
-
- Fix: corrected saving of the last context element when the stream is interrupted.
|
|
4512
|
-
- Fix: unlock response regeneration after stopped event.
|
|
4513
|
-
|
|
4514
|
-
**2.5.72 (2025-07-27)**
|
|
4515
|
-
|
|
4516
|
-
- Improved stop command.
|
|
4517
|
-
|
|
4518
|
-
**2.5.71 (2025-07-27)**
|
|
4519
|
-
|
|
4520
|
-
- Added a new working mode: `Computer use` for autonomous navigation in the user's environment (beta; utilizes the `Computer use` remote tool and the model `computer-use-preview`).
|
|
4521
|
-
- Added a new remote tool: `Remote MCP` (with configuration in Settings -> Remote Tools).
|
|
4522
|
-
- Added a new remote tool: `File Search` (with configuration in Settings -> Remote Tools).
|
|
4523
|
-
- Added a new option `Always continue...` to Agent (autonomous) plugin settings.
|
|
4524
|
-
|
|
4525
|
-
**2.5.70 (2025-07-26)**
|
|
4526
|
-
|
|
4527
|
-
- Added separate config for Responses API for expert instances.
|
|
4528
|
-
- Added a new model: mistral-small3.1.
|
|
4529
|
-
|
|
4530
|
-
**2.5.69 (2025-07-25)**
|
|
4531
|
-
|
|
4532
|
-
- The Responses API and remote tools are now allowed in Agent (autonomous) and Expert modes. Default: disabled.
|
|
4533
|
-
- Added separate options in the configuration for enabling the Responses API in: Config -> Agents and Experts.
|
|
4534
|
-
- Improved expert and agents system prompt.
|
|
4535
|
-
|
|
4536
|
-
**2.5.68 (2025-07-25)**
|
|
4537
|
-
|
|
4538
|
-
- Added a separate configuration to enable or disable native function calls in both agent and expert modes.
|
|
4539
|
-
|
|
4540
|
-
**2.5.67 (2025-07-25)**
|
|
4541
|
-
|
|
4542
|
-
- Added native tool call functionality in Experts.
|
|
4543
|
-
- Enhanced the use of multiple tool calls in Experts.
|
|
4544
|
-
- Fixed the display of Expert names when responding.
|
|
4545
|
-
- Improved context history handling in Experts.
|
|
4546
|
-
|
|
4547
4414
|
# Credits and links
|
|
4548
4415
|
|
|
4549
4416
|
**Official website:** <https://pygpt.net>
|