pygpt-net 2.6.66__py3-none-any.whl → 2.7.0__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 (81) hide show
  1. pygpt_net/CHANGELOG.txt +18 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/controller/assistant/assistant.py +13 -8
  4. pygpt_net/controller/assistant/batch.py +29 -15
  5. pygpt_net/controller/assistant/files.py +19 -14
  6. pygpt_net/controller/assistant/store.py +63 -41
  7. pygpt_net/controller/attachment/attachment.py +45 -35
  8. pygpt_net/controller/chat/attachment.py +50 -39
  9. pygpt_net/controller/config/field/dictionary.py +26 -14
  10. pygpt_net/controller/config/field/textarea.py +2 -2
  11. pygpt_net/controller/ctx/common.py +27 -17
  12. pygpt_net/controller/ctx/ctx.py +182 -101
  13. pygpt_net/controller/dialogs/info.py +2 -2
  14. pygpt_net/controller/files/files.py +101 -41
  15. pygpt_net/controller/idx/indexer.py +87 -31
  16. pygpt_net/controller/kernel/kernel.py +13 -2
  17. pygpt_net/controller/media/media.py +29 -1
  18. pygpt_net/controller/mode/mode.py +3 -3
  19. pygpt_net/controller/model/editor.py +141 -21
  20. pygpt_net/controller/model/importer.py +153 -54
  21. pygpt_net/controller/painter/painter.py +2 -2
  22. pygpt_net/controller/presets/experts.py +68 -15
  23. pygpt_net/controller/presets/presets.py +72 -36
  24. pygpt_net/controller/settings/editor.py +25 -1
  25. pygpt_net/controller/settings/profile.py +76 -35
  26. pygpt_net/controller/settings/workdir.py +70 -39
  27. pygpt_net/core/assistants/files.py +20 -18
  28. pygpt_net/core/filesystem/actions.py +111 -10
  29. pygpt_net/core/filesystem/filesystem.py +2 -1
  30. pygpt_net/core/idx/idx.py +12 -11
  31. pygpt_net/core/idx/worker.py +13 -1
  32. pygpt_net/core/models/models.py +4 -4
  33. pygpt_net/core/profile/profile.py +13 -3
  34. pygpt_net/core/types/image.py +10 -1
  35. pygpt_net/core/video/video.py +43 -3
  36. pygpt_net/data/config/config.json +3 -3
  37. pygpt_net/data/config/models.json +25 -14
  38. pygpt_net/data/css/style.dark.css +39 -1
  39. pygpt_net/data/css/style.light.css +39 -1
  40. pygpt_net/data/locale/locale.de.ini +4 -1
  41. pygpt_net/data/locale/locale.en.ini +4 -1
  42. pygpt_net/data/locale/locale.es.ini +4 -1
  43. pygpt_net/data/locale/locale.fr.ini +4 -1
  44. pygpt_net/data/locale/locale.it.ini +4 -1
  45. pygpt_net/data/locale/locale.pl.ini +5 -2
  46. pygpt_net/data/locale/locale.uk.ini +4 -1
  47. pygpt_net/data/locale/locale.zh.ini +4 -1
  48. pygpt_net/item/model.py +1 -1
  49. pygpt_net/provider/api/openai/__init__.py +4 -2
  50. pygpt_net/provider/api/openai/video.py +2 -2
  51. pygpt_net/provider/core/config/patch.py +9 -1
  52. pygpt_net/provider/core/model/patch.py +26 -1
  53. pygpt_net/tools/image_viewer/tool.py +17 -0
  54. pygpt_net/tools/text_editor/tool.py +9 -0
  55. pygpt_net/ui/__init__.py +2 -2
  56. pygpt_net/ui/dialog/models.py +10 -1
  57. pygpt_net/ui/layout/ctx/ctx_list.py +16 -6
  58. pygpt_net/ui/layout/toolbox/video.py +14 -6
  59. pygpt_net/ui/main.py +3 -1
  60. pygpt_net/ui/widget/calendar/select.py +3 -3
  61. pygpt_net/ui/widget/filesystem/explorer.py +1082 -142
  62. pygpt_net/ui/widget/lists/assistant.py +185 -24
  63. pygpt_net/ui/widget/lists/assistant_store.py +245 -42
  64. pygpt_net/ui/widget/lists/attachment.py +230 -47
  65. pygpt_net/ui/widget/lists/attachment_ctx.py +189 -33
  66. pygpt_net/ui/widget/lists/base_list_combo.py +2 -2
  67. pygpt_net/ui/widget/lists/context.py +1253 -70
  68. pygpt_net/ui/widget/lists/experts.py +110 -8
  69. pygpt_net/ui/widget/lists/model_editor.py +217 -14
  70. pygpt_net/ui/widget/lists/model_importer.py +125 -6
  71. pygpt_net/ui/widget/lists/preset.py +460 -71
  72. pygpt_net/ui/widget/lists/profile.py +149 -27
  73. pygpt_net/ui/widget/lists/uploaded.py +230 -38
  74. pygpt_net/ui/widget/option/combo.py +1046 -32
  75. pygpt_net/ui/widget/option/dictionary.py +35 -7
  76. pygpt_net/ui/widget/option/input.py +3 -1
  77. {pygpt_net-2.6.66.dist-info → pygpt_net-2.7.0.dist-info}/METADATA +20 -57
  78. {pygpt_net-2.6.66.dist-info → pygpt_net-2.7.0.dist-info}/RECORD +81 -81
  79. {pygpt_net-2.6.66.dist-info → pygpt_net-2.7.0.dist-info}/LICENSE +0 -0
  80. {pygpt_net-2.6.66.dist-info → pygpt_net-2.7.0.dist-info}/WHEEL +0 -0
  81. {pygpt_net-2.6.66.dist-info → pygpt_net-2.7.0.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.09.26 03:00:00 #
9
+ # Updated Date: 2025.12.27 02:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -183,6 +183,14 @@ class Patch:
183
183
  data["img_mode"] = "image"
184
184
  updated = True
185
185
 
186
+ # < 2.7.0
187
+ if old < parse_version("2.7.0"):
188
+ print("Migrating config from < 2.7.0...")
189
+ # add: combo boxes css
190
+ patch_css('style.light.css', True)
191
+ patch_css('style.dark.css', True)
192
+ updated = True
193
+
186
194
  # update file
187
195
  migrated = False
188
196
  if updated:
@@ -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.12.25 20:00:00 #
9
+ # Updated Date: 2025.12.26 11:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from packaging.version import parse as parse_version, Version
@@ -77,6 +77,31 @@ class Patch:
77
77
  data[model] = base_model
78
78
  updated = True
79
79
 
80
+ # < 2.6.67 <--- add missing image input
81
+ if old < parse_version("2.6.67"):
82
+ print("Migrating models from < 2.6.67...")
83
+ models_to_update = [
84
+ "claude-opus-4-5",
85
+ "claude-sonnet-4-5",
86
+ "gemini-3-flash-preview",
87
+ "gemini-3-pro-image-preview",
88
+ "gemini-3-pro-preview",
89
+ "gpt-5.2-low",
90
+ "gpt-5.2-medium",
91
+ "gpt-5.2-high",
92
+ "gpt-image-1.5",
93
+ "nano-banana-pro-preview",
94
+ "sora-2",
95
+ "veo-3.1-fast-generate-preview",
96
+ "veo-3.1-generate-preview"
97
+ ]
98
+ for model in models_to_update:
99
+ if model in data:
100
+ m = data[model]
101
+ if not m.is_image_input():
102
+ m.input.append("image")
103
+ updated = True
104
+
80
105
  # update file
81
106
  if updated:
82
107
  # fix empty/broken data
@@ -70,6 +70,23 @@ class ImageViewer(BaseTool):
70
70
  if path:
71
71
  self.open_preview(path, id, auto_close)
72
72
 
73
+ def open_preview_batch(
74
+ self,
75
+ paths: list,
76
+ current_id: str = None,
77
+ auto_close: bool = True):
78
+ """
79
+ Open image preview batch in dialog
80
+
81
+ :param paths:
82
+ :param current_id:
83
+ :param auto_close:
84
+ :return:
85
+ """
86
+ for path in paths:
87
+ self.open_preview(path, current_id, auto_close)
88
+
89
+
73
90
  def open_preview(
74
91
  self,
75
92
  path: str = None,
@@ -112,6 +112,15 @@ class TextEditor(BaseTool):
112
112
  """Open new instance"""
113
113
  self.open()
114
114
 
115
+ def open_batch(self, files: list):
116
+ """
117
+ Open multiple files in editor instances
118
+
119
+ :param files: list of files to open
120
+ """
121
+ for file in files:
122
+ self.open(file, auto_close=False, force=True)
123
+
115
124
  def open(
116
125
  self,
117
126
  file: Optional[str] = None,
pygpt_net/ui/__init__.py CHANGED
@@ -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.20 20:00:00 #
9
+ # Updated Date: 2025.12.27 17:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -143,7 +143,7 @@ class UI:
143
143
  suffix = self.window.core.platforms.get_env_suffix()
144
144
  profile_name = self.window.core.config.profile.get_current_name()
145
145
  self.window.setWindowTitle(
146
- f"PyGPT - Desktop AI Assistant {self.window.meta['version']} | "
146
+ f"PyGPT {self.window.meta['version']} | "
147
147
  f"build {self.window.meta['build'].replace('.', '-')}{suffix} ({profile_name})"
148
148
  )
149
149
 
@@ -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.09.05 18:00:00 #
9
+ # Updated Date: 2025.12.26 13:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -166,11 +166,20 @@ class Models:
166
166
  self.window.ui.nodes['models.editor.search'].on_search = self._on_search_models
167
167
  self.window.ui.nodes['models.editor.search'].on_clear = self._on_clear_models # clear via "X" button
168
168
 
169
+ # provider select
170
+ option_provider = self.window.controller.model.editor.get_provider_option()
171
+ self.window.ui.config[parent_id]['provider_global'] = OptionCombo(self.window, parent_id, 'provider_global',
172
+ option_provider)
173
+ provider_keys = self.window.controller.config.placeholder.apply_by_id('llm_providers')
174
+ provider_keys.insert(0, {"-": trans("list.all")}) # add "All" option
175
+ self.window.ui.config[parent_id]['provider_global'].set_keys(provider_keys)
176
+
169
177
  # container for search + list (left panel)
170
178
  left_layout = QVBoxLayout()
171
179
  left_layout.setContentsMargins(0, 0, 0, 0)
172
180
  left_layout.setSpacing(6)
173
181
  left_layout.addWidget(self.window.ui.nodes['models.editor.search'])
182
+ left_layout.addWidget(self.window.ui.config[parent_id]['provider_global'])
174
183
  left_layout.addWidget(self.window.ui.nodes[id])
175
184
  left_widget = QWidget()
176
185
  left_widget.setLayout(left_layout)
@@ -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.09.15 22:00:00 #
9
+ # Updated Date: 2025.12.28 12:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6 import QtCore
@@ -69,6 +69,14 @@ class CtxList:
69
69
  models[ctx_id] = model
70
70
  ctx_list.setModel(model)
71
71
 
72
+ # Expose force scroll on model as a thin proxy to the view method for external callers
73
+ try:
74
+ def _model_force_scroll_to_current(*args, **kwargs):
75
+ return ctx_list.force_scroll_to_current(*args, **kwargs)
76
+ setattr(model, "force_scroll_to_current", _model_force_scroll_to_current)
77
+ except Exception:
78
+ pass
79
+
72
80
  ctx = self.window.controller.ctx
73
81
  ctx_list.selectionModel().selectionChanged.connect(lambda *_: ctx.selection_change())
74
82
 
@@ -265,14 +273,17 @@ class CtxList:
265
273
  if c == 0 and search_string:
266
274
  continue
267
275
 
268
- suffix = f" ({c})" if c > 0 else ""
276
+ # Display only the group name; the counter is drawn by delegate on the right
269
277
  is_attachment = group.has_additional_ctx()
270
- group_name = group.name + suffix
278
+ group_name = group.name
271
279
  group_item = GroupItem(folder_icon, group_name, group.id)
272
280
  group_item.hasAttachments = is_attachment
281
+
282
+ # Provide all metadata required by the delegate
273
283
  custom_data = {
274
284
  "is_group": True,
275
285
  "is_attachment": is_attachment,
286
+ "count": c,
276
287
  }
277
288
 
278
289
  if is_attachment:
@@ -299,12 +310,11 @@ class CtxList:
299
310
 
300
311
  model.appendRow(group_item)
301
312
 
313
+ # Always reflect persisted expansion state so groups stay open after actions
302
314
  desired = group.id in node.expanded_items
303
315
  idx = group_item.index()
304
- if node.isExpanded(idx) != desired and expand:
316
+ if node.isExpanded(idx) != desired:
305
317
  node.setExpanded(idx, desired)
306
- else:
307
- node.setExpanded(idx, False)
308
318
 
309
319
  def count_in_group(self, group_id: int, data: dict) -> int:
310
320
  """
@@ -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.09.01 23:00:00 #
9
+ # Updated Date: 2025.12.26 12:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from PySide6.QtWidgets import QVBoxLayout, QWidget
12
+ from PySide6.QtWidgets import QWidget, QHBoxLayout
13
13
 
14
14
  from pygpt_net.ui.widget.option.combo import OptionCombo
15
+ from pygpt_net.ui.widget.option.input import OptionInput
15
16
 
16
17
 
17
18
  class Video:
@@ -36,11 +37,18 @@ class Video:
36
37
  container = QWidget()
37
38
  ui.nodes['video.options'] = container
38
39
 
39
- option_resolutions = self.window.core.video.get_aspect_ratio_option()
40
- conf_global['video.aspect_ratio'] = OptionCombo(self.window, 'global', 'video.aspect_ratio', option_resolutions)
40
+ option_ratio = self.window.core.video.get_aspect_ratio_option()
41
+ option_resolution = self.window.core.video.get_resolution_option()
42
+ option_duration = self.window.core.video.get_duration_option()
41
43
 
42
- rows = QVBoxLayout()
43
- rows.addWidget(conf_global['video.aspect_ratio'])
44
+ conf_global['video.aspect_ratio'] = OptionCombo(self.window, 'global', 'video.aspect_ratio', option_ratio)
45
+ conf_global['video.resolution'] = OptionCombo(self.window, 'global', 'video.resolution', option_resolution)
46
+ conf_global['video.duration'] = OptionInput(self.window, 'global', 'video.duration', option_duration)
47
+
48
+ rows = QHBoxLayout()
49
+ rows.addWidget(conf_global['video.resolution'], 2)
50
+ rows.addWidget(conf_global['video.aspect_ratio'], 2)
51
+ rows.addWidget(conf_global['video.duration'], 1)
44
52
  rows.setContentsMargins(2, 5, 5, 5)
45
53
 
46
54
  container.setLayout(rows)
pygpt_net/ui/main.py CHANGED
@@ -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.09.05 18:00:00 #
9
+ # Updated Date: 2025.12.28 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -335,6 +335,8 @@ class MainWindow(QMainWindow, QtStyleTools):
335
335
  self.controller.plugins.save_all()
336
336
  print("Saving tools...")
337
337
  self.tools.on_exit()
338
+ print("Closing clients...")
339
+ self.controller.kernel.close_clients()
338
340
  print("Saving layout state...")
339
341
  self.controller.layout.save()
340
342
  print("Stopping timers...")
@@ -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.24 23:00:00 #
9
+ # Updated Date: 2025.12.27 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Tuple
@@ -98,10 +98,10 @@ class CalendarSelect(QCalendarWidget):
98
98
  if theme != self._theme_cached:
99
99
  self._theme_cached = theme
100
100
  if isinstance(theme, str) and theme.startswith('dark'):
101
- self._counter_bg = QColor(40, 40, 40)
101
+ self._counter_bg = QColor(70, 70, 70)
102
102
  self._counter_font = QColor(255, 255, 255)
103
103
  else:
104
- self._counter_bg = QColor(240, 240, 240)
104
+ self._counter_bg = QColor(200, 200, 200)
105
105
  self._counter_font = QColor(0, 0, 0)
106
106
 
107
107
  def paintCell(self, painter, rect, date: QDate):