pygpt-net 2.6.33__py3-none-any.whl → 2.6.36__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 +18 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/controller/assistant/batch.py +14 -4
- pygpt_net/controller/assistant/files.py +1 -0
- pygpt_net/controller/assistant/store.py +195 -1
- pygpt_net/controller/camera/camera.py +1 -1
- pygpt_net/controller/chat/common.py +58 -48
- pygpt_net/controller/chat/handler/stream_worker.py +55 -43
- pygpt_net/controller/config/placeholder.py +95 -75
- pygpt_net/controller/dialogs/confirm.py +3 -1
- pygpt_net/controller/media/media.py +11 -3
- pygpt_net/controller/painter/common.py +243 -13
- pygpt_net/controller/painter/painter.py +11 -2
- pygpt_net/core/assistants/files.py +18 -0
- pygpt_net/core/bridge/bridge.py +1 -5
- pygpt_net/core/bridge/context.py +81 -36
- pygpt_net/core/bridge/worker.py +3 -1
- pygpt_net/core/camera/camera.py +31 -402
- pygpt_net/core/camera/worker.py +430 -0
- pygpt_net/core/ctx/bag.py +4 -0
- pygpt_net/core/events/app.py +10 -17
- pygpt_net/core/events/base.py +17 -25
- pygpt_net/core/events/control.py +9 -17
- pygpt_net/core/events/event.py +9 -62
- pygpt_net/core/events/kernel.py +8 -17
- pygpt_net/core/events/realtime.py +8 -17
- pygpt_net/core/events/render.py +9 -17
- pygpt_net/core/filesystem/url.py +3 -0
- pygpt_net/core/render/web/body.py +483 -40
- pygpt_net/core/render/web/pid.py +39 -24
- pygpt_net/core/render/web/renderer.py +142 -36
- pygpt_net/core/text/utils.py +3 -0
- pygpt_net/data/config/config.json +4 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/settings.json +10 -5
- pygpt_net/data/css/web-blocks.css +4 -3
- pygpt_net/data/css/web-chatgpt.css +4 -2
- pygpt_net/data/css/web-chatgpt_wide.css +4 -2
- pygpt_net/data/locale/locale.de.ini +9 -7
- pygpt_net/data/locale/locale.en.ini +10 -6
- pygpt_net/data/locale/locale.es.ini +9 -7
- pygpt_net/data/locale/locale.fr.ini +9 -7
- pygpt_net/data/locale/locale.it.ini +9 -7
- pygpt_net/data/locale/locale.pl.ini +9 -7
- pygpt_net/data/locale/locale.uk.ini +9 -7
- pygpt_net/data/locale/locale.zh.ini +9 -7
- pygpt_net/item/assistant.py +13 -1
- pygpt_net/provider/api/google/__init__.py +46 -28
- pygpt_net/provider/api/openai/__init__.py +13 -10
- pygpt_net/provider/api/openai/store.py +45 -1
- pygpt_net/provider/core/config/patch.py +18 -0
- pygpt_net/provider/llms/google.py +4 -0
- pygpt_net/ui/dialog/assistant_store.py +213 -203
- pygpt_net/ui/layout/chat/input.py +3 -3
- pygpt_net/ui/layout/chat/painter.py +63 -4
- pygpt_net/ui/widget/draw/painter.py +715 -104
- pygpt_net/ui/widget/option/combo.py +5 -1
- pygpt_net/ui/widget/textarea/input.py +273 -3
- pygpt_net/ui/widget/textarea/web.py +2 -0
- {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.36.dist-info}/METADATA +20 -2
- {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.36.dist-info}/RECORD +64 -63
- {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.36.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.36.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.36.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.09.02 16:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
@@ -109,6 +109,8 @@ class OptionCombo(QWidget):
|
|
|
109
109
|
for item in self.keys:
|
|
110
110
|
if type(item) is dict:
|
|
111
111
|
for key, value in item.items():
|
|
112
|
+
if not isinstance(key, str):
|
|
113
|
+
key = str(key)
|
|
112
114
|
if key.startswith("separator::"):
|
|
113
115
|
self.combo.addSeparator(value)
|
|
114
116
|
else:
|
|
@@ -117,6 +119,8 @@ class OptionCombo(QWidget):
|
|
|
117
119
|
self.combo.addItem(item, item)
|
|
118
120
|
elif type(self.keys) is dict:
|
|
119
121
|
for key, value in self.keys.items():
|
|
122
|
+
if not isinstance(key, str):
|
|
123
|
+
key = str(key)
|
|
120
124
|
if key.startswith("separator::"):
|
|
121
125
|
self.combo.addSeparator(value)
|
|
122
126
|
else:
|
|
@@ -6,12 +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: 2025.
|
|
9
|
+
# Updated Date: 2025.09.03 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Optional
|
|
13
|
+
import math
|
|
13
14
|
|
|
14
|
-
from PySide6.QtCore import Qt, QSize
|
|
15
|
+
from PySide6.QtCore import Qt, QSize, QTimer, QEvent
|
|
15
16
|
from PySide6.QtGui import QAction, QIcon, QImage
|
|
16
17
|
from PySide6.QtWidgets import (
|
|
17
18
|
QTextEdit,
|
|
@@ -90,6 +91,24 @@ class ChatInput(QTextEdit):
|
|
|
90
91
|
# Apply initial margins (top padding + left space for icons)
|
|
91
92
|
self._apply_margins()
|
|
92
93
|
|
|
94
|
+
# ---- Auto-resize config (input in splitter) ----
|
|
95
|
+
self._auto_max_lines = 10 # max lines for auto-expansion
|
|
96
|
+
self._auto_max_ratio = 0.25 # max fraction of main window height
|
|
97
|
+
self._auto_debounce_ms = 0 # coalesce updates in next event loop turn
|
|
98
|
+
self._auto_updating = False # reentrancy guard
|
|
99
|
+
self._splitter_resize_in_progress = False
|
|
100
|
+
self._splitter_connected = False
|
|
101
|
+
self._user_adjusting_splitter = False
|
|
102
|
+
self._auto_pause_ms_after_user_drag = 350
|
|
103
|
+
self._last_target_container_h = None
|
|
104
|
+
|
|
105
|
+
self._auto_timer = QTimer(self)
|
|
106
|
+
self._auto_timer.setSingleShot(True)
|
|
107
|
+
self._auto_timer.timeout.connect(self._auto_resize_tick)
|
|
108
|
+
|
|
109
|
+
# Trigger auto-resize on text changes (tokens update stays intact)
|
|
110
|
+
self.textChanged.connect(self._schedule_auto_resize)
|
|
111
|
+
|
|
93
112
|
def _apply_text_top_padding(self):
|
|
94
113
|
"""Apply extra top padding inside the text area by using viewport margins."""
|
|
95
114
|
# Left margin is computed in _apply_margins()
|
|
@@ -213,6 +232,9 @@ class ChatInput(QTextEdit):
|
|
|
213
232
|
self.window.controller.chat.input.send_input()
|
|
214
233
|
handled = True
|
|
215
234
|
self.setFocus()
|
|
235
|
+
# Collapse to minimum after sending
|
|
236
|
+
if handled:
|
|
237
|
+
QTimer.singleShot(0, self.collapse_to_min)
|
|
216
238
|
elif key == Qt.Key_Escape and self.window.controller.ctx.extra.is_editing():
|
|
217
239
|
self.window.controller.ctx.extra.edit_cancel()
|
|
218
240
|
handled = True
|
|
@@ -240,10 +262,17 @@ class ChatInput(QTextEdit):
|
|
|
240
262
|
self.window.core.config.data['font_size.input'] = self.value
|
|
241
263
|
self.window.core.config.save()
|
|
242
264
|
self.window.controller.ui.update_font_size()
|
|
265
|
+
# Reflow may change number of lines; adjust auto-height next tick
|
|
266
|
+
QTimer.singleShot(0, self._schedule_auto_resize)
|
|
243
267
|
event.accept()
|
|
244
268
|
return
|
|
245
269
|
super().wheelEvent(event)
|
|
246
270
|
|
|
271
|
+
def changeEvent(self, event):
|
|
272
|
+
super().changeEvent(event)
|
|
273
|
+
if event.type() == QEvent.FontChange:
|
|
274
|
+
self._schedule_auto_resize()
|
|
275
|
+
|
|
247
276
|
def action_add_attachment(self):
|
|
248
277
|
"""Add attachment (button click)."""
|
|
249
278
|
self.window.controller.attachment.open_add()
|
|
@@ -672,4 +701,245 @@ class ChatInput(QTextEdit):
|
|
|
672
701
|
try:
|
|
673
702
|
self._reposition_icon_bar()
|
|
674
703
|
except Exception:
|
|
675
|
-
pass
|
|
704
|
+
pass
|
|
705
|
+
# Recompute on width changes (word wrap may change line count)
|
|
706
|
+
if not self._splitter_resize_in_progress:
|
|
707
|
+
if self.hasFocus():
|
|
708
|
+
self._schedule_auto_resize()
|
|
709
|
+
else:
|
|
710
|
+
# Allow shrinking to minimum when content is single line
|
|
711
|
+
self._schedule_auto_resize(enforce_minimize_if_single=True)
|
|
712
|
+
|
|
713
|
+
# ================== Auto-resize inside QSplitter ==================
|
|
714
|
+
|
|
715
|
+
def _ensure_splitter_hook(self):
|
|
716
|
+
"""Lazy-connect to main splitter to detect manual drags."""
|
|
717
|
+
if self._splitter_connected:
|
|
718
|
+
return
|
|
719
|
+
splitter = self._get_main_splitter()
|
|
720
|
+
if splitter is not None:
|
|
721
|
+
try:
|
|
722
|
+
splitter.splitterMoved.connect(self._on_splitter_moved_by_user)
|
|
723
|
+
self._splitter_connected = True
|
|
724
|
+
except Exception:
|
|
725
|
+
pass
|
|
726
|
+
|
|
727
|
+
def _on_splitter_moved_by_user(self, pos, index):
|
|
728
|
+
"""Pause auto-resize briefly while the user drags the splitter."""
|
|
729
|
+
self._user_adjusting_splitter = True
|
|
730
|
+
QTimer.singleShot(self._auto_pause_ms_after_user_drag, self._reset_user_adjusting_flag)
|
|
731
|
+
|
|
732
|
+
def _reset_user_adjusting_flag(self):
|
|
733
|
+
self._user_adjusting_splitter = False
|
|
734
|
+
|
|
735
|
+
def _get_main_splitter(self):
|
|
736
|
+
"""Get main vertical splitter from window registry."""
|
|
737
|
+
try:
|
|
738
|
+
return self.window.ui.splitters.get('main.output')
|
|
739
|
+
except Exception:
|
|
740
|
+
return None
|
|
741
|
+
|
|
742
|
+
def _find_container_in_splitter(self, splitter):
|
|
743
|
+
"""Find the direct child of splitter that contains this ChatInput."""
|
|
744
|
+
if splitter is None:
|
|
745
|
+
return None, -1
|
|
746
|
+
for i in range(splitter.count()):
|
|
747
|
+
w = splitter.widget(i)
|
|
748
|
+
if w and w.isAncestorOf(self):
|
|
749
|
+
return w, i
|
|
750
|
+
return None, -1
|
|
751
|
+
|
|
752
|
+
def _schedule_auto_resize(self, force: bool = False, enforce_minimize_if_single: bool = False):
|
|
753
|
+
"""Schedule auto-resize; multiple calls are coalesced."""
|
|
754
|
+
# Store flags for the next tick
|
|
755
|
+
self._pending_force = getattr(self, "_pending_force", False) or bool(force)
|
|
756
|
+
self._pending_minimize_if_single = getattr(self, "_pending_minimize_if_single", False) or bool(
|
|
757
|
+
enforce_minimize_if_single)
|
|
758
|
+
# Avoid scheduling when splitter drag in progress
|
|
759
|
+
if self._user_adjusting_splitter or self._splitter_resize_in_progress:
|
|
760
|
+
return
|
|
761
|
+
self._ensure_splitter_hook()
|
|
762
|
+
# Debounce to next event loop to ensure document layout is up to date
|
|
763
|
+
if not self._auto_timer.isActive():
|
|
764
|
+
self._auto_timer.start(self._auto_debounce_ms)
|
|
765
|
+
|
|
766
|
+
def _auto_resize_tick(self):
|
|
767
|
+
"""Execute auto-resize once after debounce."""
|
|
768
|
+
force = getattr(self, "_pending_force", False)
|
|
769
|
+
minimize_if_single = getattr(self, "_pending_minimize_if_single", False)
|
|
770
|
+
self._pending_force = False
|
|
771
|
+
self._pending_minimize_if_single = False
|
|
772
|
+
try:
|
|
773
|
+
self._update_auto_height(force=force, minimize_if_single=minimize_if_single)
|
|
774
|
+
except Exception:
|
|
775
|
+
# Never break input pipeline on errors
|
|
776
|
+
pass
|
|
777
|
+
|
|
778
|
+
def _document_content_height(self) -> int:
|
|
779
|
+
"""Return QTextDocument layout height in pixels."""
|
|
780
|
+
doc = self.document()
|
|
781
|
+
layout = doc.documentLayout()
|
|
782
|
+
if layout is not None:
|
|
783
|
+
h = layout.documentSize().height()
|
|
784
|
+
else:
|
|
785
|
+
h = doc.size().height()
|
|
786
|
+
return int(math.ceil(h))
|
|
787
|
+
|
|
788
|
+
def _line_spacing(self) -> int:
|
|
789
|
+
"""Return current line spacing for font."""
|
|
790
|
+
return int(math.ceil(self.fontMetrics().lineSpacing()))
|
|
791
|
+
|
|
792
|
+
def _effective_lines(self, doc_h: int, line_h: int, doc_margin: float) -> float:
|
|
793
|
+
"""Rough estimate of visible line count from document height."""
|
|
794
|
+
base = max(0.0, doc_h - 2.0 * float(doc_margin))
|
|
795
|
+
if line_h <= 0:
|
|
796
|
+
return 1.0
|
|
797
|
+
return max(1.0, base / float(line_h))
|
|
798
|
+
|
|
799
|
+
def _min_input_widget_height(self, non_viewport_h: int) -> int:
|
|
800
|
+
"""Height of QTextEdit widget required to fit a single line without scrollbars."""
|
|
801
|
+
line_h = self._line_spacing()
|
|
802
|
+
doc_margin = float(self.document().documentMargin())
|
|
803
|
+
min_viewport_h = int(math.ceil(2.0 * doc_margin + line_h))
|
|
804
|
+
# Respect current minimum size hint to avoid jitter on some styles
|
|
805
|
+
min_hint = max(self.minimumSizeHint().height(), 0)
|
|
806
|
+
return max(min_hint, min_viewport_h + non_viewport_h)
|
|
807
|
+
|
|
808
|
+
def _max_input_widget_height_by_lines(self, non_viewport_h: int) -> int:
|
|
809
|
+
"""Max widget height allowed by line count cap."""
|
|
810
|
+
line_h = self._line_spacing()
|
|
811
|
+
doc_margin = float(self.document().documentMargin())
|
|
812
|
+
max_viewport_h = int(math.ceil(2.0 * doc_margin + self._auto_max_lines * line_h))
|
|
813
|
+
return max_viewport_h + non_viewport_h
|
|
814
|
+
|
|
815
|
+
def _should_shrink_to_min(self, doc_h: int) -> bool:
|
|
816
|
+
"""Decide if we should collapse to minimum (single line or empty)."""
|
|
817
|
+
line_h = self._line_spacing()
|
|
818
|
+
doc_margin = float(self.document().documentMargin())
|
|
819
|
+
threshold = 2.0 * doc_margin + 1.25 * line_h # small slack for layout rounding
|
|
820
|
+
return doc_h <= threshold
|
|
821
|
+
|
|
822
|
+
def _update_auto_height(self, force: bool = False, minimize_if_single: bool = False):
|
|
823
|
+
"""
|
|
824
|
+
Core auto-resize routine:
|
|
825
|
+
- expand only when the input has focus (unless force=True),
|
|
826
|
+
- cap by max lines and 1/4 of main window height,
|
|
827
|
+
- shrink back to minimal only after send or when text is effectively one line.
|
|
828
|
+
"""
|
|
829
|
+
if self._auto_updating or self._splitter_resize_in_progress:
|
|
830
|
+
return
|
|
831
|
+
|
|
832
|
+
splitter = self._get_main_splitter()
|
|
833
|
+
container, idx = self._find_container_in_splitter(splitter)
|
|
834
|
+
if splitter is None or container is None or idx < 0:
|
|
835
|
+
return # Not yet attached to the splitter
|
|
836
|
+
|
|
837
|
+
# Expansion only with focus unless forced
|
|
838
|
+
has_focus = self.hasFocus()
|
|
839
|
+
can_expand = force or has_focus
|
|
840
|
+
if self._user_adjusting_splitter and not force:
|
|
841
|
+
return
|
|
842
|
+
|
|
843
|
+
# Measure current layout and targets
|
|
844
|
+
doc_h = self._document_content_height()
|
|
845
|
+
non_viewport_h = self.height() - self.viewport().height()
|
|
846
|
+
needed_input_h = int(math.ceil(doc_h + non_viewport_h))
|
|
847
|
+
min_input_h = self._min_input_widget_height(non_viewport_h)
|
|
848
|
+
max_input_by_lines = self._max_input_widget_height_by_lines(non_viewport_h)
|
|
849
|
+
|
|
850
|
+
# Container overhead above the inner QTextEdit
|
|
851
|
+
container_overhead = max(0, container.height() - self.height())
|
|
852
|
+
needed_container_h = needed_input_h + container_overhead
|
|
853
|
+
min_container_h = min_input_h + container_overhead
|
|
854
|
+
|
|
855
|
+
# Max cap by window fraction
|
|
856
|
+
try:
|
|
857
|
+
max_container_by_ratio = int(self.window.height() * self._auto_max_ratio)
|
|
858
|
+
except Exception:
|
|
859
|
+
max_container_by_ratio = 0 # fallback disables ratio cap if window unavailable
|
|
860
|
+
|
|
861
|
+
max_container_by_lines = max_input_by_lines + container_overhead
|
|
862
|
+
cap_container_max = max_container_by_lines
|
|
863
|
+
if max_container_by_ratio > 0:
|
|
864
|
+
cap_container_max = min(cap_container_max, max_container_by_ratio)
|
|
865
|
+
|
|
866
|
+
current_sizes = splitter.sizes()
|
|
867
|
+
if idx >= len(current_sizes):
|
|
868
|
+
return
|
|
869
|
+
current_container_h = current_sizes[idx]
|
|
870
|
+
|
|
871
|
+
# Decide on action
|
|
872
|
+
target_container_h = None
|
|
873
|
+
|
|
874
|
+
# Shrink only when requested or effectively single line
|
|
875
|
+
if minimize_if_single or self._should_shrink_to_min(doc_h):
|
|
876
|
+
if current_container_h > min_container_h + 1:
|
|
877
|
+
target_container_h = min_container_h
|
|
878
|
+
|
|
879
|
+
# Expand if focused (or forced), but only up to caps
|
|
880
|
+
elif can_expand:
|
|
881
|
+
desired = min(needed_container_h, cap_container_max)
|
|
882
|
+
if desired > current_container_h + 1:
|
|
883
|
+
target_container_h = desired
|
|
884
|
+
|
|
885
|
+
# Apply if needed
|
|
886
|
+
if target_container_h is None:
|
|
887
|
+
return
|
|
888
|
+
|
|
889
|
+
total = sum(current_sizes)
|
|
890
|
+
# Clamp to splitter total height
|
|
891
|
+
target_container_h = max(0, min(target_container_h, total))
|
|
892
|
+
|
|
893
|
+
if abs(target_container_h - current_container_h) <= 1:
|
|
894
|
+
return
|
|
895
|
+
|
|
896
|
+
# Prepare new sizes (2 widgets expected: output at 0, input at 1)
|
|
897
|
+
new_sizes = list(current_sizes)
|
|
898
|
+
# Distribute delta to other panes; here we have exactly 2
|
|
899
|
+
other_total = total - current_container_h
|
|
900
|
+
new_other_total = total - target_container_h
|
|
901
|
+
if other_total <= 0:
|
|
902
|
+
# degenerate case; just set directly
|
|
903
|
+
pass
|
|
904
|
+
else:
|
|
905
|
+
# Scale other widgets proportionally
|
|
906
|
+
scale = new_other_total / float(other_total) if other_total > 0 else 1.0
|
|
907
|
+
for i in range(len(new_sizes)):
|
|
908
|
+
if i != idx:
|
|
909
|
+
new_sizes[i] = int(round(new_sizes[i] * scale))
|
|
910
|
+
new_sizes[idx] = int(target_container_h)
|
|
911
|
+
|
|
912
|
+
# Final clamp to preserve sum
|
|
913
|
+
diff = total - sum(new_sizes)
|
|
914
|
+
if diff != 0 and len(new_sizes) > 0:
|
|
915
|
+
# Adjust the first non-target pane to fix rounding
|
|
916
|
+
for i in range(len(new_sizes)):
|
|
917
|
+
if i != idx:
|
|
918
|
+
new_sizes[i] += diff
|
|
919
|
+
break
|
|
920
|
+
|
|
921
|
+
self._splitter_resize_in_progress = True
|
|
922
|
+
try:
|
|
923
|
+
old_block = splitter.blockSignals(True)
|
|
924
|
+
splitter.setSizes(new_sizes)
|
|
925
|
+
splitter.blockSignals(old_block)
|
|
926
|
+
finally:
|
|
927
|
+
self._splitter_resize_in_progress = False
|
|
928
|
+
|
|
929
|
+
# Keep stored sizes in sync with app expectations (mirrors ChatMain.on_splitter_moved)
|
|
930
|
+
try:
|
|
931
|
+
tabs = self.window.ui.tabs
|
|
932
|
+
if "input" in tabs:
|
|
933
|
+
t_idx = tabs['input'].currentIndex()
|
|
934
|
+
if t_idx != 0:
|
|
935
|
+
self.window.controller.ui.splitter_output_size_files = new_sizes
|
|
936
|
+
else:
|
|
937
|
+
self.window.controller.ui.splitter_output_size_input = new_sizes
|
|
938
|
+
except Exception:
|
|
939
|
+
pass
|
|
940
|
+
|
|
941
|
+
self._last_target_container_h = target_container_h
|
|
942
|
+
|
|
943
|
+
def collapse_to_min(self):
|
|
944
|
+
"""Public helper to collapse input area to minimal height."""
|
|
945
|
+
self._schedule_auto_resize(force=True, enforce_minimize_if_single=True)
|
|
@@ -566,6 +566,8 @@ class Bridge(QObject):
|
|
|
566
566
|
self.window = window
|
|
567
567
|
|
|
568
568
|
chunk = Signal(str, str, str, bool, bool) # name, buffer, chunk, replace, is_code
|
|
569
|
+
node = Signal(str) # content
|
|
570
|
+
nodeReplace = Signal(str) # content
|
|
569
571
|
readyChanged = Signal(bool)
|
|
570
572
|
|
|
571
573
|
@Slot()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pygpt-net
|
|
3
|
-
Version: 2.6.
|
|
3
|
+
Version: 2.6.36
|
|
4
4
|
Summary: Desktop AI Assistant powered by: OpenAI GPT-5, GPT-4, o1, o3, 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: ai,api,api key,app,assistant,bielik,chat,chatbot,chatgpt,claude,dall-e,deepseek,desktop,gemini,gpt,gpt-3.5,gpt-4,gpt-4-vision,gpt-4o,gpt-5,gpt-oss,gpt3.5,gpt4,grok,langchain,llama-index,llama3,mistral,o1,o3,ollama,openai,presets,py-gpt,py_gpt,pygpt,pyside,qt,text completion,tts,ui,vision,whisper
|
|
@@ -118,7 +118,7 @@ Description-Content-Type: text/markdown
|
|
|
118
118
|
|
|
119
119
|
[](https://snapcraft.io/pygpt)
|
|
120
120
|
|
|
121
|
-
Release: **2.6.
|
|
121
|
+
Release: **2.6.36** | build: **2025-09-04** | Python: **>=3.10, <3.14**
|
|
122
122
|
|
|
123
123
|
> Official website: https://pygpt.net | Documentation: https://pygpt.readthedocs.io
|
|
124
124
|
>
|
|
@@ -3565,6 +3565,24 @@ may consume additional tokens that are not displayed in the main window.
|
|
|
3565
3565
|
|
|
3566
3566
|
## Recent changes:
|
|
3567
3567
|
|
|
3568
|
+
**2.6.36 (2025-09-04)**
|
|
3569
|
+
|
|
3570
|
+
- Optimized rendering of large code blocks.
|
|
3571
|
+
|
|
3572
|
+
**2.6.35 (2025-09-04)**
|
|
3573
|
+
|
|
3574
|
+
- Improved responsiveness in chat streaming.
|
|
3575
|
+
- Added zoom, fit, and auto-scroll on crop in Painter.
|
|
3576
|
+
- Added a scroll-up button in the chat window.
|
|
3577
|
+
- Optimized memory usage.
|
|
3578
|
+
|
|
3579
|
+
**2.6.34 (2025-09-03)**
|
|
3580
|
+
|
|
3581
|
+
- Added auto-resizing for input.
|
|
3582
|
+
- Added file management to the OpenAI Vector Stores tool.
|
|
3583
|
+
- Implemented removal of script tags when exporting HTML.
|
|
3584
|
+
- Enabled maintaining custom resolution in Painter.
|
|
3585
|
+
|
|
3568
3586
|
**2.6.33 (2025-09-02)**
|
|
3569
3587
|
|
|
3570
3588
|
- Added a "crop" option, auto-resize canvas, and layer support to Painter.
|