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,12 +6,12 @@
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.27 20:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
13
13
  import json
14
- from typing import Optional, Any
14
+ from typing import Optional, Any, Union
15
15
 
16
16
  from pygpt_net.core.events import Event
17
17
  from pygpt_net.utils import trans
@@ -33,6 +33,7 @@ class Editor:
33
33
  self.height = 500
34
34
  self.selected = []
35
35
  self.locked = False
36
+ self.provider = "-" # all providers by default
36
37
  self.options = {
37
38
  "id": {
38
39
  "type": "text",
@@ -137,12 +138,40 @@ class Editor:
137
138
  if key in self.options:
138
139
  return self.options[key]
139
140
 
141
+ def get_provider_option(self) -> dict:
142
+ """
143
+ Get provider option
144
+
145
+ :return: provider option
146
+ """
147
+ return {
148
+ "type": "combo",
149
+ "use": "llm_providers",
150
+ "label": "model.provider",
151
+ "description": "model.provider.desc",
152
+ }
153
+
140
154
  def setup(self):
141
155
  """Set up editor"""
142
156
  idx = None
143
157
  self.window.model_settings.setup(idx) # widget dialog setup
144
158
  self.window.ui.add_hook("update.model.name", self.hook_update)
145
159
  self.window.ui.add_hook("update.model.mode", self.hook_update)
160
+ self.update_provider(self.provider)
161
+ self.window.ui.add_hook("update.model.provider_global", self.hook_update)
162
+
163
+ def update_provider(self, provider: str):
164
+ """
165
+ Set provider
166
+
167
+ :param provider: provider name
168
+ """
169
+ self.window.controller.config.apply_value(
170
+ parent_id="model",
171
+ key="provider_global",
172
+ option=self.get_provider_option(),
173
+ value=provider,
174
+ )
146
175
 
147
176
  def hook_update(
148
177
  self,
@@ -163,6 +192,21 @@ class Editor:
163
192
  """
164
193
  if self.window.controller.reloading or self.locked:
165
194
  return # ignore hooks during reloading process
195
+
196
+ if key == "provider_global":
197
+ # update provider option dynamically
198
+ if self.provider == value:
199
+ return
200
+ self.save(persist=False)
201
+ self.locked = True
202
+ self.current = None
203
+ self.provider = value
204
+ self.reload_items()
205
+ if self.current is None:
206
+ self.init()
207
+ self.locked = False
208
+ return
209
+
166
210
  if key in ["id", "name", "mode"]:
167
211
  self.save(persist=False)
168
212
  self.reload_items()
@@ -184,6 +228,7 @@ class Editor:
184
228
 
185
229
  :param force: force open dialog
186
230
  """
231
+ self.locked = True
187
232
  if not self.config_initialized:
188
233
  self.setup()
189
234
  self.config_initialized = True
@@ -197,6 +242,9 @@ class Editor:
197
242
  height=self.height,
198
243
  )
199
244
  self.dialog = True
245
+ if "models.editor.search" in self.window.ui.nodes:
246
+ self.window.ui.nodes['models.editor.search'].setFocus() # focus on search
247
+ self.locked = False
200
248
 
201
249
  def undo(self):
202
250
  """Undo last changes in models editor"""
@@ -219,15 +267,16 @@ class Editor:
219
267
  self.window.core.models.sort_items()
220
268
  self.reload_items()
221
269
 
222
- # select the first plugin on list if no plugin selected yet
270
+ # select the first model on list if no model selected yet
271
+ items = self.prepare_items()
223
272
  if self.current is None:
224
- if len(self.window.core.models.items) > 0:
225
- self.current = list(self.window.core.models.items.keys())[0]
273
+ if len(items) > 0:
274
+ self.current = list(items.keys())[0]
226
275
 
227
276
  # assign model options to config dialog fields
228
277
  options = copy.deepcopy(self.get_options()) # copy options
229
- if self.current in self.window.core.models.items:
230
- model = self.window.core.models.items[self.current]
278
+ if self.current in items:
279
+ model = items[self.current]
231
280
  data_dict = model.to_dict()
232
281
  for key in options:
233
282
  if key in data_dict:
@@ -237,7 +286,7 @@ class Editor:
237
286
  # custom fields
238
287
  options["extra_json"]["value"] = json.dumps(model.extra, indent=4) if model.extra else ""
239
288
 
240
- if self.current is not None and self.current in self.window.core.models.items:
289
+ if self.current is not None and self.current in items:
241
290
  self.set_tab_by_id(self.current)
242
291
 
243
292
  # load and apply options to config dialog
@@ -313,10 +362,26 @@ class Editor:
313
362
  event = Event(Event.MODELS_CHANGED)
314
363
  self.window.dispatch(event, all=True)
315
364
 
365
+ def prepare_items(self) -> dict:
366
+ """
367
+ Prepare items by provider
368
+
369
+ :return: items by provider
370
+ """
371
+ items = self.window.core.models.items
372
+ if self.provider == "-":
373
+ return items # all providers
374
+ items_by_provider = {}
375
+ for model_id, model in items.items():
376
+ provider = model.provider
377
+ if provider != self.provider:
378
+ continue
379
+ items_by_provider[model_id] = model
380
+ return items_by_provider
381
+
316
382
  def reload_items(self):
317
383
  """Reload items"""
318
- items = self.window.core.models.items
319
- self.window.model_settings.update_list("models.list", items)
384
+ self.window.model_settings.update_list("models.list", self.prepare_items())
320
385
 
321
386
  def select(self, idx: int):
322
387
  """Select model"""
@@ -330,14 +395,15 @@ class Editor:
330
395
  """Create new model"""
331
396
  self.locked = True
332
397
  self.save(persist=False)
333
- model = self.window.core.models.create_empty()
398
+ model, new_id = self.window.core.models.create_empty()
399
+ if self.provider != "-":
400
+ model.provider = self.provider
334
401
  self.window.core.models.sort_items()
335
402
  self.window.core.models.save()
336
403
  self.reload_items()
337
404
 
338
405
  # switch to created model
339
406
  self.current = model.id
340
- # sort here
341
407
  idx = self.get_tab_by_id(self.current)
342
408
  self.set_by_tab(idx)
343
409
  self.init()
@@ -345,17 +411,15 @@ class Editor:
345
411
 
346
412
  def delete_by_idx(
347
413
  self,
348
- idx: int,
414
+ idx: Union[int, list],
349
415
  force: bool = False
350
416
  ):
351
417
  """
352
418
  Delete model by idx
353
419
 
354
- :param idx: model idx
420
+ :param idx: model idx or list of idxs
355
421
  :param force: force delete
356
422
  """
357
- self.locked = True
358
- model = self.get_model_by_tab_idx(idx)
359
423
  if not force:
360
424
  self.window.ui.dialogs.confirm(
361
425
  type="models.editor.delete",
@@ -363,14 +427,70 @@ class Editor:
363
427
  msg=trans("dialog.models.editor.delete.confirm"),
364
428
  )
365
429
  return
366
- self.window.core.models.delete(model)
367
- self.window.core.models.save()
368
- self.reload_items()
369
- if self.current == model:
370
- self.current = None
430
+
431
+ self.locked = True
432
+ models = []
433
+ last_idx = None
434
+ ids = idx if isinstance(idx, list) else [idx]
435
+ for i in ids:
436
+ model = self.get_model_by_tab_idx(i)
437
+ if model:
438
+ models.append(model)
439
+ last_idx = i
440
+
441
+ for model in models:
442
+ self.window.core.models.delete(model)
443
+ self.window.core.models.save()
444
+ self.reload_items()
445
+ if self.current == model:
446
+ self.current = None
447
+
448
+ # switch to previous model if available
449
+ items = self.prepare_items()
450
+ if len(items) > 0:
451
+ model = self.get_model_by_tab_idx(last_idx - 1)
452
+ if model:
453
+ self.current = model
454
+
371
455
  self.init()
372
456
  self.locked = False
373
457
 
458
+ def duplicate_by_idx(
459
+ self,
460
+ idx: Union[int, list]
461
+ ):
462
+ """
463
+ Duplicate model by idx
464
+
465
+ :param idx: model idx or list of idxs
466
+ """
467
+ self.locked = True
468
+ self.save(persist=False)
469
+ ids = idx if isinstance(idx, list) else [idx]
470
+ models = []
471
+ for i in ids:
472
+ model = self.get_model_by_tab_idx(i)
473
+ if model:
474
+ models.append(model)
475
+
476
+ for model in models:
477
+ if model:
478
+ new_model, new_id = self.window.core.models.create_empty()
479
+ new_model.from_dict(self.window.core.models.items[model].to_dict())
480
+ new_model.name += " (Copy)"
481
+ self.window.core.models.sort_items()
482
+ self.window.core.models.save()
483
+ self.reload_items()
484
+
485
+ # switch to created model if only one duplicated
486
+ if len(models) == 1:
487
+ self.current = new_id
488
+ idx = self.get_tab_by_id(self.current)
489
+ self.set_by_tab(idx)
490
+ self.init()
491
+
492
+ self.locked = False
493
+
374
494
  def load_defaults_user(self, force: bool = False):
375
495
  """
376
496
  Load models editor user defaults
@@ -6,13 +6,14 @@
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.27 21:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
13
13
  import os
14
14
  from typing import List, Dict, Optional, Any
15
15
 
16
+ from PySide6 import QtCore
16
17
  from PySide6.QtWidgets import QApplication
17
18
 
18
19
  from pygpt_net.core.events import Event
@@ -45,8 +46,10 @@ class Importer:
45
46
  self.items_current = {} # current models in use
46
47
  self.pending = {} # waiting to be imported models
47
48
  self.removed = {} # waiting to be removed models
48
- self.selected_available = None # selected available model
49
- self.selected_current = None # selected current model
49
+ self.selected_available = None # selected available model (single)
50
+ self.selected_current = None # selected current model (single)
51
+ self.selected_available_ids: List[str] = [] # multi-selected available models
52
+ self.selected_current_ids: List[str] = [] # multi-selected current models
50
53
  self.all = False # show all models, not only available for import
51
54
  self.provider = "_" # default provider
52
55
 
@@ -69,43 +72,71 @@ class Importer:
69
72
 
70
73
  def change_available(self):
71
74
  """On change available model selection"""
72
- val = self.window.ui.nodes["models.importer.available"].selectionModel().currentIndex()
73
- idx = val.row()
74
- if idx < 0:
75
+ view = self.window.ui.nodes["models.importer.available"]
76
+ sel_model = view.selectionModel()
77
+ rows = sel_model.selectedRows() if sel_model else []
78
+ # collect selected model keys by reading stored tooltip role (stable ID)
79
+ selected_ids = []
80
+ for ix in rows:
81
+ key = ix.data(QtCore.Qt.ToolTipRole)
82
+ if not key:
83
+ key = self.get_by_idx(ix.row(), self.items_available)
84
+ if key and key not in selected_ids:
85
+ selected_ids.append(key)
86
+
87
+ self.selected_available_ids = selected_ids
88
+ if not selected_ids:
75
89
  self.selected_available = None
76
90
  self.window.ui.nodes["models.importer.add"].setEnabled(False)
77
- else:
78
- self.selected_available = self.get_by_idx(idx, self.items_available)
79
- if self.items_available.get(self.selected_available) is None:
80
- self.selected_available = None
81
- self.window.ui.nodes["models.importer.add"].setEnabled(False)
82
- else:
83
- # if not in current then enable add button
84
- if not self.in_current(self.selected_available):
85
- self.window.ui.nodes["models.importer.add"].setEnabled(True)
86
- else:
87
- self.window.ui.nodes["models.importer.add"].setEnabled(False)
91
+ return
92
+
93
+ # keep a single reference for backward compatibility (last selected)
94
+ self.selected_available = selected_ids[-1]
95
+
96
+ # enable add if any of selected can be added (not already in current)
97
+ can_add = False
98
+ for key in selected_ids:
99
+ if key is None:
100
+ continue
101
+ if not self.in_current(key) and self.items_available.get(key) is not None:
102
+ can_add = True
103
+ break
104
+ self.window.ui.nodes["models.importer.add"].setEnabled(can_add)
88
105
 
89
106
  def change_current(self):
90
107
  """On change current model selection"""
91
108
  if self.provider not in self.items_current:
92
109
  self.items_current[self.provider] = {}
93
- val = self.window.ui.nodes["models.importer.current"].selectionModel().currentIndex()
94
- idx = val.row()
95
- if idx < 0:
110
+ view = self.window.ui.nodes["models.importer.current"]
111
+ sel_model = view.selectionModel()
112
+ rows = sel_model.selectedRows() if sel_model else []
113
+ # collect selected model keys by reading stored tooltip role (stable ID)
114
+ selected_ids = []
115
+ for ix in rows:
116
+ key = ix.data(QtCore.Qt.ToolTipRole)
117
+ if not key:
118
+ key = self.get_by_idx(ix.row(), self.items_current[self.provider])
119
+ if key and key not in selected_ids:
120
+ selected_ids.append(key)
121
+
122
+ self.selected_current_ids = selected_ids
123
+ if not selected_ids:
96
124
  self.selected_current = None
97
125
  self.window.ui.nodes["models.importer.remove"].setEnabled(False)
98
- else:
99
- self.selected_current = self.get_by_idx(idx, self.items_current[self.provider])
100
- if self.items_current[self.provider].get(self.selected_current) is None:
101
- self.selected_current = None
102
- self.window.ui.nodes["models.importer.remove"].setEnabled(False)
103
- else:
104
- if (self.selected_current in self.items_current[self.provider]
105
- and self.in_available(self.selected_current)):
106
- self.window.ui.nodes["models.importer.remove"].setEnabled(True)
107
- else:
108
- self.window.ui.nodes["models.importer.remove"].setEnabled(False)
126
+ return
127
+
128
+ # keep a single reference for backward compatibility (last selected)
129
+ self.selected_current = selected_ids[-1]
130
+
131
+ # enable remove if any of selected can be removed
132
+ can_remove = False
133
+ for key in selected_ids:
134
+ if key is None:
135
+ continue
136
+ if (key in self.items_current[self.provider]) and self.in_available(key):
137
+ can_remove = True
138
+ break
139
+ self.window.ui.nodes["models.importer.remove"].setEnabled(can_remove)
109
140
 
110
141
  def in_available(self, model: str) -> bool:
111
142
  """
@@ -122,43 +153,111 @@ class Importer:
122
153
  return True
123
154
  return False
124
155
 
156
+ def _selected_keys_from_view(self, node_id: str, items: Dict) -> List[str]:
157
+ """
158
+ Resolve selected model keys from a given list view using stored tooltip role,
159
+ falling back to index->dict mapping when needed.
160
+
161
+ :param node_id: ui node id
162
+ :param items: dict used to build the view's model
163
+ :return: list of selected keys
164
+ """
165
+ keys: List[str] = []
166
+ try:
167
+ view = self.window.ui.nodes[node_id]
168
+ sel_model = view.selectionModel()
169
+ rows = sel_model.selectedRows() if sel_model else []
170
+ for ix in rows:
171
+ key = ix.data(QtCore.Qt.ToolTipRole)
172
+ if not key:
173
+ key = self.get_by_idx(ix.row(), items)
174
+ if key and key not in keys:
175
+ keys.append(key)
176
+ except Exception:
177
+ pass
178
+ return keys
179
+
125
180
  def add(self):
126
- """Add model to current list"""
181
+ """Add model(s) to current list"""
127
182
  if self.provider not in self.items_current:
128
183
  self.items_current[self.provider] = {}
129
- if self.selected_available is None:
184
+
185
+ # collect multi-selection; fallback to single selection
186
+ keys = self._selected_keys_from_view(
187
+ 'models.importer.available',
188
+ self.items_available,
189
+ )
190
+ if not keys and self.selected_available is not None:
191
+ keys = [self.selected_available]
192
+
193
+ if not keys:
130
194
  self.set_status(trans('models.importer.error.add.no_model'))
131
195
  return
132
- if self.in_current(self.selected_available):
196
+
197
+ any_added = False
198
+ for key in list(keys):
199
+ if key is None:
200
+ continue
201
+ if self.in_current(key):
202
+ continue
203
+ model = self.items_available.get(key)
204
+ if model is None:
205
+ continue
206
+ self.items_current[self.provider][key] = model
207
+ if key not in self.pending:
208
+ self.pending[key] = model
209
+ if key in self.removed:
210
+ del self.removed[key]
211
+ if not self.all and key in self.items_available:
212
+ del self.items_available[key]
213
+ any_added = True
214
+
215
+ if not any_added:
133
216
  self.set_status(trans('models.importer.error.add.not_exists'))
134
217
  return
135
- model = self.items_available[self.selected_available]
136
- self.items_current[self.provider][self.selected_available] = model
137
- if self.selected_available not in self.pending:
138
- self.pending[self.selected_available] = model
139
- if self.selected_available in self.removed:
140
- del self.removed[self.selected_available]
141
- if not self.all:
142
- del self.items_available[self.selected_available]
218
+
143
219
  self.refresh()
144
220
 
145
221
  def remove(self):
146
- """Remove model from current list"""
222
+ """Remove model(s) from current list"""
147
223
  if self.provider not in self.items_current:
148
224
  self.items_current[self.provider] = {}
149
- if self.selected_current is None:
225
+
226
+ # collect multi-selection; fallback to single selection
227
+ keys = self._selected_keys_from_view(
228
+ 'models.importer.current',
229
+ self.items_current[self.provider],
230
+ )
231
+ if not keys and self.selected_current is not None:
232
+ keys = [self.selected_current]
233
+
234
+ if not keys:
150
235
  self.set_status(trans('models.importer.error.remove.no_model'))
151
236
  return
152
- if not self.in_current(self.selected_current):
237
+
238
+ any_removed = False
239
+ for key in list(keys):
240
+ if key is None:
241
+ continue
242
+ if not self.in_current(key):
243
+ continue
244
+ model = self.items_current[self.provider].get(key)
245
+ if model is None:
246
+ continue
247
+ # return to available list
248
+ self.items_available[key] = model
249
+ if key not in self.removed:
250
+ self.removed[key] = model
251
+ if key in self.items_current[self.provider]:
252
+ del self.items_current[self.provider][key]
253
+ if key in self.pending:
254
+ del self.pending[key]
255
+ any_removed = True
256
+
257
+ if not any_removed:
153
258
  self.set_status(trans('models.importer.error.remove.not_exists'))
154
259
  return
155
- model = self.items_current[self.provider][self.selected_current]
156
- self.items_available[self.selected_current] = model
157
- if self.selected_current not in self.removed:
158
- self.removed[self.selected_current] = model
159
- del self.items_current[self.provider][self.selected_current]
160
- if self.selected_current in self.pending:
161
- del self.pending[self.selected_current]
260
+
162
261
  self.refresh()
163
262
 
164
263
  def setup(self):
@@ -328,7 +427,7 @@ class Importer:
328
427
  name = model.get('id')
329
428
  if "name" in model:
330
429
  name = model.get('name')
331
- m = self.window.core.models.create_empty(append=False)
430
+ m, _ = self.window.core.models.create_empty(append=False)
332
431
  m.id = id
333
432
  m.name = name
334
433
  m.mode = [
@@ -504,7 +603,7 @@ class Importer:
504
603
  else:
505
604
  for model in ollama_models:
506
605
  name = model.get('name')
507
- m = self.window.core.models.create_empty(append=False)
606
+ m, _ = self.window.core.models.create_empty(append=False)
508
607
  m.id = name
509
608
  m.name = name
510
609
  m.mode = [
@@ -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.02 15:00:00 #
9
+ # Updated Date: 2025.12.27 17:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -53,7 +53,7 @@ class Painter:
53
53
  """
54
54
  self.open(path)
55
55
  if not self.is_active():
56
- self.window.controller.ui.switch_tab('draw')
56
+ self.window.controller.ui.tabs.switch_tab(Tab.TAB_TOOL_PAINTER)
57
57
 
58
58
  def save(self):
59
59
  """Store current image"""