pygpt-net 2.6.67__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 (69) hide show
  1. pygpt_net/CHANGELOG.txt +12 -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/ctx/common.py +27 -17
  11. pygpt_net/controller/ctx/ctx.py +182 -101
  12. pygpt_net/controller/files/files.py +101 -41
  13. pygpt_net/controller/idx/indexer.py +87 -31
  14. pygpt_net/controller/kernel/kernel.py +13 -2
  15. pygpt_net/controller/mode/mode.py +3 -3
  16. pygpt_net/controller/model/editor.py +70 -15
  17. pygpt_net/controller/model/importer.py +153 -54
  18. pygpt_net/controller/painter/painter.py +2 -2
  19. pygpt_net/controller/presets/experts.py +68 -15
  20. pygpt_net/controller/presets/presets.py +72 -36
  21. pygpt_net/controller/settings/profile.py +76 -35
  22. pygpt_net/controller/settings/workdir.py +70 -39
  23. pygpt_net/core/assistants/files.py +20 -18
  24. pygpt_net/core/filesystem/actions.py +111 -10
  25. pygpt_net/core/filesystem/filesystem.py +2 -1
  26. pygpt_net/core/idx/idx.py +12 -11
  27. pygpt_net/core/idx/worker.py +13 -1
  28. pygpt_net/core/models/models.py +4 -4
  29. pygpt_net/core/profile/profile.py +13 -3
  30. pygpt_net/data/config/config.json +3 -3
  31. pygpt_net/data/config/models.json +3 -3
  32. pygpt_net/data/css/style.dark.css +39 -1
  33. pygpt_net/data/css/style.light.css +39 -1
  34. pygpt_net/data/locale/locale.de.ini +3 -1
  35. pygpt_net/data/locale/locale.en.ini +3 -1
  36. pygpt_net/data/locale/locale.es.ini +3 -1
  37. pygpt_net/data/locale/locale.fr.ini +3 -1
  38. pygpt_net/data/locale/locale.it.ini +3 -1
  39. pygpt_net/data/locale/locale.pl.ini +4 -2
  40. pygpt_net/data/locale/locale.uk.ini +3 -1
  41. pygpt_net/data/locale/locale.zh.ini +3 -1
  42. pygpt_net/provider/api/openai/__init__.py +4 -2
  43. pygpt_net/provider/core/config/patch.py +9 -1
  44. pygpt_net/tools/image_viewer/tool.py +17 -0
  45. pygpt_net/tools/text_editor/tool.py +9 -0
  46. pygpt_net/ui/__init__.py +2 -2
  47. pygpt_net/ui/layout/ctx/ctx_list.py +16 -6
  48. pygpt_net/ui/main.py +3 -1
  49. pygpt_net/ui/widget/calendar/select.py +3 -3
  50. pygpt_net/ui/widget/filesystem/explorer.py +1082 -142
  51. pygpt_net/ui/widget/lists/assistant.py +185 -24
  52. pygpt_net/ui/widget/lists/assistant_store.py +245 -42
  53. pygpt_net/ui/widget/lists/attachment.py +230 -47
  54. pygpt_net/ui/widget/lists/attachment_ctx.py +189 -33
  55. pygpt_net/ui/widget/lists/base_list_combo.py +2 -2
  56. pygpt_net/ui/widget/lists/context.py +1253 -70
  57. pygpt_net/ui/widget/lists/experts.py +110 -8
  58. pygpt_net/ui/widget/lists/model_editor.py +217 -14
  59. pygpt_net/ui/widget/lists/model_importer.py +125 -6
  60. pygpt_net/ui/widget/lists/preset.py +460 -71
  61. pygpt_net/ui/widget/lists/profile.py +149 -27
  62. pygpt_net/ui/widget/lists/uploaded.py +230 -38
  63. pygpt_net/ui/widget/option/combo.py +1046 -32
  64. pygpt_net/ui/widget/option/dictionary.py +35 -7
  65. {pygpt_net-2.6.67.dist-info → pygpt_net-2.7.0.dist-info}/METADATA +14 -57
  66. {pygpt_net-2.6.67.dist-info → pygpt_net-2.7.0.dist-info}/RECORD +69 -69
  67. {pygpt_net-2.6.67.dist-info → pygpt_net-2.7.0.dist-info}/LICENSE +0 -0
  68. {pygpt_net-2.6.67.dist-info → pygpt_net-2.7.0.dist-info}/WHEEL +0 -0
  69. {pygpt_net-2.6.67.dist-info → pygpt_net-2.7.0.dist-info}/entry_points.txt +0 -0
@@ -6,11 +6,11 @@
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.18 01:00:00 #
9
+ # Updated Date: 2025.12.27 21:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
13
- from typing import List, Dict, Any
13
+ from typing import List, Dict, Any, Union
14
14
 
15
15
  from PySide6.QtCore import Slot, QObject
16
16
 
@@ -384,14 +384,14 @@ class Attachment(QObject):
384
384
 
385
385
  def delete_by_idx(
386
386
  self,
387
- idx: int,
387
+ idx: Union[int, list],
388
388
  force: bool = False,
389
389
  remove_local: bool = True
390
390
  ):
391
391
  """
392
392
  Delete attachment by index
393
393
 
394
- :param idx: Index on list
394
+ :param idx: Index on list or list of indices
395
395
  :param force: Force delete
396
396
  :param remove_local: Remove local copies
397
397
  """
@@ -406,9 +406,14 @@ class Attachment(QObject):
406
406
  if meta is None or not meta.has_additional_ctx():
407
407
  return
408
408
  items = self.window.core.attachments.context.get_all(meta)
409
- if idx < len(items):
410
- item = items[idx]
411
- self.window.core.attachments.context.delete(meta, item, delete_files=remove_local)
409
+ ids = idx if isinstance(idx, list) else [idx]
410
+ updated = False
411
+ for idx in sorted(ids, reverse=True):
412
+ if idx < len(items):
413
+ item = items[idx]
414
+ self.window.core.attachments.context.delete(meta, item, delete_files=remove_local)
415
+ updated = True
416
+ if updated:
412
417
  self.update_list(meta)
413
418
  self.window.controller.ctx.update()
414
419
 
@@ -448,62 +453,68 @@ class Attachment(QObject):
448
453
  """
449
454
  pass
450
455
 
451
- def open_by_idx(self, idx: int):
456
+ def open_by_idx(self, idx: Union[int, list]):
452
457
  """
453
458
  Open attachment by index
454
459
 
455
- :param idx: Index on list
460
+ :param idx: Index on list or list of indices
456
461
  """
457
462
  meta = self.window.core.ctx.get_current_meta()
458
463
  if meta is None or not meta.has_additional_ctx():
459
464
  return
460
465
  items = self.window.core.attachments.context.get_all(meta)
461
- if idx < len(items):
462
- item = items[idx]
463
- path = item["path"]
464
- if "real_path" in item:
465
- path = item["real_path"]
466
- if os.path.exists(path) and os.path.isfile(path):
467
- print(f"Opening attachment: {path}")
468
- self.window.controller.files.open(path)
469
-
470
- def open_dir_src_by_idx(self, idx: int):
466
+ ids = idx if isinstance(idx, list) else [idx]
467
+ for idx in ids:
468
+ if idx < len(items):
469
+ item = items[idx]
470
+ path = item["path"]
471
+ if "real_path" in item:
472
+ path = item["real_path"]
473
+ if os.path.exists(path) and os.path.isfile(path):
474
+ print(f"Opening attachment: {path}")
475
+ self.window.controller.files.open(path)
476
+
477
+ def open_dir_src_by_idx(self, idx: Union[int, list]):
471
478
  """
472
479
  Open source directory by index
473
480
 
474
- :param idx: Index on list
481
+ :param idx: Index on list or list of indices
475
482
  """
476
483
  meta = self.window.core.ctx.get_current_meta()
477
484
  if meta is None or not meta.has_additional_ctx():
478
485
  return
479
486
  items = self.window.core.attachments.context.get_all(meta)
480
- if idx < len(items):
481
- item = items[idx]
482
- path = item["path"]
483
- if "real_path" in item:
484
- path = item["real_path"]
485
- dir = os.path.dirname(path)
486
- if os.path.exists(dir) and os.path.isdir(dir):
487
- print(f"Opening source directory: {dir}")
488
- self.window.controller.files.open(dir)
489
-
490
- def open_dir_dest_by_idx(self, idx: int):
487
+ ids = idx if isinstance(idx, list) else [idx]
488
+ for idx in ids:
489
+ if idx < len(items):
490
+ item = items[idx]
491
+ path = item["path"]
492
+ if "real_path" in item:
493
+ path = item["real_path"]
494
+ dir = os.path.dirname(path)
495
+ if os.path.exists(dir) and os.path.isdir(dir):
496
+ print(f"Opening source directory: {dir}")
497
+ self.window.controller.files.open(dir)
498
+
499
+ def open_dir_dest_by_idx(self, idx: Union[int, list]):
491
500
  """
492
501
  Open destination directory by index
493
502
 
494
- :param idx: Index on list
503
+ :param idx: Index on list or list of indices
495
504
  """
496
505
  meta = self.window.core.ctx.get_current_meta()
497
506
  if meta is None or not meta.has_additional_ctx():
498
507
  return
499
508
  items = self.window.core.attachments.context.get_all(meta)
500
- if idx < len(items):
501
- item = items[idx]
502
- root_dir = self.window.core.attachments.context.get_dir(meta)
503
- dir = os.path.join(root_dir, item["uuid"])
504
- if os.path.exists(dir) and os.path.isdir(dir):
505
- self.window.controller.files.open(dir)
506
- print(f"Opening destination directory: {dir}")
509
+ ids = idx if isinstance(idx, list) else [idx]
510
+ for idx in ids:
511
+ if idx < len(items):
512
+ item = items[idx]
513
+ root_dir = self.window.core.attachments.context.get_dir(meta)
514
+ dir = os.path.join(root_dir, item["uuid"])
515
+ if os.path.exists(dir) and os.path.isdir(dir):
516
+ self.window.controller.files.open(dir)
517
+ print(f"Opening destination directory: {dir}")
507
518
 
508
519
  def has_file_by_idx(self, idx: int) -> bool:
509
520
  """
@@ -6,10 +6,10 @@
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.15 23:00:00 #
9
+ # Updated Date: 2025.12.27 21:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from typing import Dict, Any, List
12
+ from typing import Dict, Any, List, Union
13
13
 
14
14
  from pygpt_net.ui.widget.option.dictionary import OptionDict
15
15
  from pygpt_net.utils import trans
@@ -85,7 +85,7 @@ class Dictionary:
85
85
  def delete_item(
86
86
  self,
87
87
  parent_object: OptionDict,
88
- id: str,
88
+ id, # Union[int, list[int]]
89
89
  force: bool = False,
90
90
  hooks: bool = True
91
91
  ):
@@ -93,11 +93,12 @@ class Dictionary:
93
93
  Show delete item (from dict list) confirmation dialog or executes delete
94
94
 
95
95
  :param parent_object: parent object
96
- :param id: item id
96
+ :param id: item id or list of ids (row indexes)
97
97
  :param force: force delete
98
98
  :param hooks: run hooks
99
99
  """
100
100
  if not force:
101
+ # Pass list as-is for batch confirmation
101
102
  self.window.ui.dialogs.confirm(
102
103
  type="settings.dict.delete",
103
104
  id=id,
@@ -107,16 +108,27 @@ class Dictionary:
107
108
  return
108
109
 
109
110
  if parent_object is not None:
110
- parent_object.delete_item_execute(id)
111
- if hooks:
112
- hook_name = f"update.{parent_object}.{id}"
113
- ui = self.window.ui
114
- if ui.has_hook(hook_name):
115
- hook = ui.get_hook(hook_name)
116
- try:
117
- hook(id, {}, "dictionary")
118
- except Exception as e:
119
- self.window.core.debug.log(e)
111
+ # Normalize to unique integer indexes and sort descending to avoid index shift on delete
112
+ ids = id if isinstance(id, list) else [id]
113
+ normalized = []
114
+ for v in ids:
115
+ try:
116
+ normalized.append(int(v))
117
+ except Exception:
118
+ continue
119
+ normalized = sorted(set(normalized), reverse=True)
120
+
121
+ ui = self.window.ui
122
+ for idx in normalized:
123
+ parent_object.delete_item_execute(idx)
124
+ if hooks:
125
+ hook_name = f"update.{parent_object}.{idx}"
126
+ if ui.has_hook(hook_name):
127
+ hook = ui.get_hook(hook_name)
128
+ try:
129
+ hook(idx, {}, "dictionary")
130
+ except Exception as e:
131
+ self.window.core.debug.log(e)
120
132
 
121
133
  def to_options(
122
134
  self,
@@ -6,10 +6,10 @@
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.23 15:00:00 #
9
+ # Updated Date: 2025.12.27 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from typing import Optional
12
+ from typing import Optional, Union
13
13
 
14
14
  from PySide6.QtCore import QTimer, QSignalBlocker
15
15
  from PySide6.QtWidgets import QApplication
@@ -75,16 +75,22 @@ class Common:
75
75
  label = f'{label} ({assistant.name})'
76
76
  self.window.controller.ui.update_ctx_label(label)
77
77
 
78
- def duplicate(self, meta_id: int):
78
+ def duplicate(self, meta_id: Union[int, list]):
79
79
  """
80
80
  Duplicate context by meta id
81
81
 
82
- :param meta_id: context id
82
+ :param meta_id: context id or list of ids
83
83
  """
84
- new_id = self.window.core.ctx.duplicate(meta_id)
85
- if new_id is not None:
86
- self.window.core.attachments.context.duplicate(meta_id, new_id)
87
- self.window.update_status(f"Context duplicated, new ctx id: {new_id}")
84
+ is_updated = False
85
+ ids = meta_id if isinstance(meta_id, list) else [meta_id]
86
+ for meta_id in ids:
87
+ new_id = self.window.core.ctx.duplicate(meta_id)
88
+ if new_id is not None:
89
+ self.window.core.attachments.context.duplicate(meta_id, new_id)
90
+ self.window.update_status(f"Context duplicated, new ctx id: {new_id}")
91
+ is_updated = True
92
+
93
+ if is_updated:
88
94
  QTimer.singleShot(10, self._update_ctx_no_scroll)
89
95
 
90
96
  def dismiss_rename(self):
@@ -162,25 +168,27 @@ class Common:
162
168
  self.window.core.ctx.set_display_filters(filters)
163
169
  self.window.controller.ctx.update()
164
170
 
165
- def copy_id(self, id: int):
171
+ def copy_id(self, id: Union[int, list]):
166
172
  """
167
173
  Copy id into clipboard and to iinput
168
174
 
169
- :param id: context list idx
175
+ :param id: context list idx or list of ids
170
176
  """
171
- value = f"@{id}"
177
+ ids = id if isinstance(id, list) else [id]
178
+ values = [f"@{i}" for i in ids]
179
+ value = " ".join(values)
172
180
  self.window.controller.chat.common.append_to_input(value, separator=" ")
173
181
  QApplication.clipboard().setText(value)
174
182
 
175
183
  def reset(
176
184
  self,
177
- meta_id: int,
185
+ meta_id: Union[int, list],
178
186
  force: bool = False
179
187
  ):
180
188
  """
181
189
  Reset by meta id
182
190
 
183
- :param meta_id: context id
191
+ :param meta_id: context id or list of ids
184
192
  :param force: True to force reset
185
193
  """
186
194
  if not force:
@@ -190,7 +198,9 @@ class Common:
190
198
  msg=trans('ctx.reset_meta.confirm'),
191
199
  )
192
200
  return
193
- self.window.core.ctx.reset_meta(meta_id)
194
- self.window.core.attachments.context.reset_by_meta_id(meta_id, delete_files=True)
195
- if self.window.core.ctx.get_current() == meta_id:
196
- self.window.controller.ctx.load(meta_id)
201
+ ids = meta_id if isinstance(meta_id, list) else [meta_id]
202
+ for meta_id in ids:
203
+ self.window.core.ctx.reset_meta(meta_id)
204
+ self.window.core.attachments.context.reset_by_meta_id(meta_id, delete_files=True)
205
+ if self.window.core.ctx.get_current() == meta_id:
206
+ self.window.controller.ctx.load(meta_id)