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.
- pygpt_net/CHANGELOG.txt +18 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/controller/assistant/assistant.py +13 -8
- pygpt_net/controller/assistant/batch.py +29 -15
- pygpt_net/controller/assistant/files.py +19 -14
- pygpt_net/controller/assistant/store.py +63 -41
- pygpt_net/controller/attachment/attachment.py +45 -35
- pygpt_net/controller/chat/attachment.py +50 -39
- pygpt_net/controller/config/field/dictionary.py +26 -14
- pygpt_net/controller/config/field/textarea.py +2 -2
- pygpt_net/controller/ctx/common.py +27 -17
- pygpt_net/controller/ctx/ctx.py +182 -101
- pygpt_net/controller/dialogs/info.py +2 -2
- pygpt_net/controller/files/files.py +101 -41
- pygpt_net/controller/idx/indexer.py +87 -31
- pygpt_net/controller/kernel/kernel.py +13 -2
- pygpt_net/controller/media/media.py +29 -1
- pygpt_net/controller/mode/mode.py +3 -3
- pygpt_net/controller/model/editor.py +141 -21
- pygpt_net/controller/model/importer.py +153 -54
- pygpt_net/controller/painter/painter.py +2 -2
- pygpt_net/controller/presets/experts.py +68 -15
- pygpt_net/controller/presets/presets.py +72 -36
- pygpt_net/controller/settings/editor.py +25 -1
- pygpt_net/controller/settings/profile.py +76 -35
- pygpt_net/controller/settings/workdir.py +70 -39
- pygpt_net/core/assistants/files.py +20 -18
- pygpt_net/core/filesystem/actions.py +111 -10
- pygpt_net/core/filesystem/filesystem.py +2 -1
- pygpt_net/core/idx/idx.py +12 -11
- pygpt_net/core/idx/worker.py +13 -1
- pygpt_net/core/models/models.py +4 -4
- pygpt_net/core/profile/profile.py +13 -3
- pygpt_net/core/types/image.py +10 -1
- pygpt_net/core/video/video.py +43 -3
- pygpt_net/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +25 -14
- pygpt_net/data/css/style.dark.css +39 -1
- pygpt_net/data/css/style.light.css +39 -1
- pygpt_net/data/locale/locale.de.ini +4 -1
- pygpt_net/data/locale/locale.en.ini +4 -1
- pygpt_net/data/locale/locale.es.ini +4 -1
- pygpt_net/data/locale/locale.fr.ini +4 -1
- pygpt_net/data/locale/locale.it.ini +4 -1
- pygpt_net/data/locale/locale.pl.ini +5 -2
- pygpt_net/data/locale/locale.uk.ini +4 -1
- pygpt_net/data/locale/locale.zh.ini +4 -1
- pygpt_net/item/model.py +1 -1
- pygpt_net/provider/api/openai/__init__.py +4 -2
- pygpt_net/provider/api/openai/video.py +2 -2
- pygpt_net/provider/core/config/patch.py +9 -1
- pygpt_net/provider/core/model/patch.py +26 -1
- pygpt_net/tools/image_viewer/tool.py +17 -0
- pygpt_net/tools/text_editor/tool.py +9 -0
- pygpt_net/ui/__init__.py +2 -2
- pygpt_net/ui/dialog/models.py +10 -1
- pygpt_net/ui/layout/ctx/ctx_list.py +16 -6
- pygpt_net/ui/layout/toolbox/video.py +14 -6
- pygpt_net/ui/main.py +3 -1
- pygpt_net/ui/widget/calendar/select.py +3 -3
- pygpt_net/ui/widget/filesystem/explorer.py +1082 -142
- pygpt_net/ui/widget/lists/assistant.py +185 -24
- pygpt_net/ui/widget/lists/assistant_store.py +245 -42
- pygpt_net/ui/widget/lists/attachment.py +230 -47
- pygpt_net/ui/widget/lists/attachment_ctx.py +189 -33
- pygpt_net/ui/widget/lists/base_list_combo.py +2 -2
- pygpt_net/ui/widget/lists/context.py +1253 -70
- pygpt_net/ui/widget/lists/experts.py +110 -8
- pygpt_net/ui/widget/lists/model_editor.py +217 -14
- pygpt_net/ui/widget/lists/model_importer.py +125 -6
- pygpt_net/ui/widget/lists/preset.py +460 -71
- pygpt_net/ui/widget/lists/profile.py +149 -27
- pygpt_net/ui/widget/lists/uploaded.py +230 -38
- pygpt_net/ui/widget/option/combo.py +1046 -32
- pygpt_net/ui/widget/option/dictionary.py +35 -7
- pygpt_net/ui/widget/option/input.py +3 -1
- {pygpt_net-2.6.66.dist-info → pygpt_net-2.7.0.dist-info}/METADATA +20 -57
- {pygpt_net-2.6.66.dist-info → pygpt_net-2.7.0.dist-info}/RECORD +81 -81
- {pygpt_net-2.6.66.dist-info → pygpt_net-2.7.0.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.66.dist-info → pygpt_net-2.7.0.dist-info}/WHEEL +0 -0
- {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.
|
|
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
|
|
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(
|
|
225
|
-
self.current = list(
|
|
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
|
|
230
|
-
model =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
367
|
-
self.
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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.
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
if
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
if
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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(
|
|
56
|
+
self.window.controller.ui.tabs.switch_tab(Tab.TAB_TOOL_PAINTER)
|
|
57
57
|
|
|
58
58
|
def save(self):
|
|
59
59
|
"""Store current image"""
|