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.
- pygpt_net/CHANGELOG.txt +12 -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/ctx/common.py +27 -17
- pygpt_net/controller/ctx/ctx.py +182 -101
- 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/mode/mode.py +3 -3
- pygpt_net/controller/model/editor.py +70 -15
- 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/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/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +3 -3
- 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 +3 -1
- pygpt_net/data/locale/locale.en.ini +3 -1
- pygpt_net/data/locale/locale.es.ini +3 -1
- pygpt_net/data/locale/locale.fr.ini +3 -1
- pygpt_net/data/locale/locale.it.ini +3 -1
- pygpt_net/data/locale/locale.pl.ini +4 -2
- pygpt_net/data/locale/locale.uk.ini +3 -1
- pygpt_net/data/locale/locale.zh.ini +3 -1
- pygpt_net/provider/api/openai/__init__.py +4 -2
- pygpt_net/provider/core/config/patch.py +9 -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/layout/ctx/ctx_list.py +16 -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-2.6.67.dist-info → pygpt_net-2.7.0.dist-info}/METADATA +14 -57
- {pygpt_net-2.6.67.dist-info → pygpt_net-2.7.0.dist-info}/RECORD +69 -69
- {pygpt_net-2.6.67.dist-info → pygpt_net-2.7.0.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.67.dist-info → pygpt_net-2.7.0.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.67.dist-info → pygpt_net-2.7.0.dist-info}/entry_points.txt +0 -0
|
@@ -6,13 +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.12.27 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
13
13
|
import os
|
|
14
14
|
import shutil
|
|
15
|
-
from typing import Optional
|
|
15
|
+
from typing import Optional, Union
|
|
16
16
|
from shutil import copy2
|
|
17
17
|
|
|
18
18
|
from PySide6.QtWidgets import QFileDialog, QApplication
|
|
@@ -99,7 +99,7 @@ class Files:
|
|
|
99
99
|
|
|
100
100
|
def delete(
|
|
101
101
|
self,
|
|
102
|
-
path: str,
|
|
102
|
+
path: Union[str, list],
|
|
103
103
|
force: bool = False
|
|
104
104
|
):
|
|
105
105
|
"""
|
|
@@ -116,6 +116,11 @@ class Files:
|
|
|
116
116
|
)
|
|
117
117
|
return
|
|
118
118
|
|
|
119
|
+
if isinstance(path, list):
|
|
120
|
+
for p in path:
|
|
121
|
+
self.delete(p, True)
|
|
122
|
+
return
|
|
123
|
+
|
|
119
124
|
if os.path.isdir(path):
|
|
120
125
|
# check if directory is not empty
|
|
121
126
|
if os.listdir(path):
|
|
@@ -136,17 +141,27 @@ class Files:
|
|
|
136
141
|
|
|
137
142
|
def duplicate_local(
|
|
138
143
|
self,
|
|
139
|
-
path: str,
|
|
144
|
+
path: Union[str, list],
|
|
140
145
|
name: str,
|
|
141
146
|
force: bool = False
|
|
142
147
|
):
|
|
143
148
|
"""
|
|
144
149
|
Duplicate file or directory
|
|
145
150
|
|
|
146
|
-
:param path: path to file
|
|
151
|
+
:param path: path to file or list of files
|
|
147
152
|
:param name: new file name
|
|
148
153
|
:param force: force duplicate
|
|
149
154
|
"""
|
|
155
|
+
if isinstance(path, list):
|
|
156
|
+
for p in path:
|
|
157
|
+
if os.path.isdir(p):
|
|
158
|
+
new_name = os.path.basename(p) + "_copy"
|
|
159
|
+
else:
|
|
160
|
+
new_name = os.path.splitext(os.path.basename(p))[0] \
|
|
161
|
+
+ "_copy" + os.path.splitext(p)[1]
|
|
162
|
+
self.duplicate_local(p, new_name, True)
|
|
163
|
+
return
|
|
164
|
+
|
|
150
165
|
if not force:
|
|
151
166
|
if os.path.isdir(path):
|
|
152
167
|
new_name = os.path.basename(path) + "_copy"
|
|
@@ -168,10 +183,10 @@ class Files:
|
|
|
168
183
|
new_path = os.path.join(parent_dir, name)
|
|
169
184
|
|
|
170
185
|
if os.path.exists(new_path):
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
186
|
+
# prefix with timestamp if file exists
|
|
187
|
+
ts_prefix = self.make_ts_prefix()
|
|
188
|
+
name = f"{ts_prefix}_{name}"
|
|
189
|
+
new_path = os.path.join(parent_dir, name)
|
|
175
190
|
|
|
176
191
|
if os.path.isdir(path):
|
|
177
192
|
shutil.copytree(path, new_path)
|
|
@@ -185,12 +200,16 @@ class Files:
|
|
|
185
200
|
self.window.core.debug.log(e)
|
|
186
201
|
print(f"Error duplicating file: {path} - {e}")
|
|
187
202
|
|
|
188
|
-
def download_local(self, path: str):
|
|
203
|
+
def download_local(self, path: Union[str, list]):
|
|
189
204
|
"""
|
|
190
205
|
Download (copy) file or directory to local filesystem
|
|
191
206
|
|
|
192
|
-
:param path: path to source file
|
|
207
|
+
:param path: path to source file or list of files
|
|
193
208
|
"""
|
|
209
|
+
if isinstance(path, list):
|
|
210
|
+
for p in path:
|
|
211
|
+
self.download_local(p)
|
|
212
|
+
return
|
|
194
213
|
last_dir = self.window.core.config.get_last_used_dir()
|
|
195
214
|
dialog = QFileDialog(self.window)
|
|
196
215
|
dialog.setDirectory(last_dir)
|
|
@@ -339,12 +358,16 @@ class Files:
|
|
|
339
358
|
self.window.update_status(f"[OK] Uploaded: {copied} files.")
|
|
340
359
|
self.update_explorer()
|
|
341
360
|
|
|
342
|
-
def rename(self, path: str):
|
|
361
|
+
def rename(self, path: Union[str, list]):
|
|
343
362
|
"""
|
|
344
363
|
Rename file or directory
|
|
345
364
|
|
|
346
|
-
:param path: path to file
|
|
365
|
+
:param path: path to file or list of files
|
|
347
366
|
"""
|
|
367
|
+
if isinstance(path, list):
|
|
368
|
+
for p in path:
|
|
369
|
+
self.rename(p)
|
|
370
|
+
return
|
|
348
371
|
self.window.ui.dialog['rename'].id = 'output_file'
|
|
349
372
|
self.window.ui.dialog['rename'].input.setText(os.path.basename(path))
|
|
350
373
|
self.window.ui.dialog['rename'].current = path
|
|
@@ -377,23 +400,36 @@ class Files:
|
|
|
377
400
|
|
|
378
401
|
def open_dir(
|
|
379
402
|
self,
|
|
380
|
-
path: str,
|
|
403
|
+
path: Union[str, list],
|
|
381
404
|
select: bool = False
|
|
382
405
|
):
|
|
383
406
|
"""
|
|
384
407
|
Open file or directory in file manager
|
|
385
408
|
|
|
386
|
-
:param path: path to file or directory
|
|
409
|
+
:param path: path to file or directory or list of paths
|
|
387
410
|
:param select: select file in file manager
|
|
388
411
|
"""
|
|
412
|
+
if isinstance(path, list):
|
|
413
|
+
parents = []
|
|
414
|
+
for p in path:
|
|
415
|
+
parent = os.path.dirname(p)
|
|
416
|
+
if parent not in parents:
|
|
417
|
+
self.open_in_file_manager(p, select)
|
|
418
|
+
if parent not in parents:
|
|
419
|
+
parents.append(parent)
|
|
420
|
+
return
|
|
389
421
|
self.open_in_file_manager(path, select)
|
|
390
422
|
|
|
391
|
-
def open(self, path: str):
|
|
423
|
+
def open(self, path: Union[str, list]):
|
|
392
424
|
"""
|
|
393
425
|
Open path in file manager or with default application
|
|
394
426
|
|
|
395
|
-
:param path: path to file or directory
|
|
427
|
+
:param path: path to file or directory or list of paths
|
|
396
428
|
"""
|
|
429
|
+
if isinstance(path, list):
|
|
430
|
+
for p in path:
|
|
431
|
+
self.open(p)
|
|
432
|
+
return
|
|
397
433
|
path = self.window.core.filesystem.get_path(path)
|
|
398
434
|
Opener.open_path(path, reveal=False)
|
|
399
435
|
|
|
@@ -468,52 +504,76 @@ class Files:
|
|
|
468
504
|
self.window.ui.nodes['output_files'].path_label.setText(root)
|
|
469
505
|
self.window.ui.nodes['output_files'].model.update_idx_status(data)
|
|
470
506
|
|
|
471
|
-
def use_attachment(self, path: str):
|
|
507
|
+
def use_attachment(self, path: Union[str, list]):
|
|
472
508
|
"""
|
|
473
509
|
Use file as attachment
|
|
474
510
|
|
|
475
|
-
:param path: path to file
|
|
476
|
-
"""
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
mode=mode
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
511
|
+
:param path: path to file or list of files
|
|
512
|
+
"""
|
|
513
|
+
paths = path if isinstance(path, list) else [path]
|
|
514
|
+
for p in paths:
|
|
515
|
+
title = os.path.basename(p)
|
|
516
|
+
mode = self.window.core.config.get("mode")
|
|
517
|
+
self.window.core.attachments.new(
|
|
518
|
+
mode=mode,
|
|
519
|
+
name=title,
|
|
520
|
+
path=p,
|
|
521
|
+
auto_save=False,
|
|
522
|
+
)
|
|
485
523
|
self.window.core.attachments.save()
|
|
486
524
|
self.window.controller.attachment.update()
|
|
487
525
|
|
|
488
|
-
def copy_sys_path(self, path: str):
|
|
526
|
+
def copy_sys_path(self, path: Union[str, list]):
|
|
489
527
|
"""
|
|
490
528
|
Copy system path to clipboard
|
|
491
529
|
|
|
492
|
-
:param path: path to file
|
|
530
|
+
:param path: path to file or list of files
|
|
493
531
|
"""
|
|
494
|
-
|
|
495
|
-
|
|
532
|
+
if isinstance(path, list):
|
|
533
|
+
paths = [self.window.core.filesystem.get_path(p) for p in path]
|
|
534
|
+
path_str = "\n".join(paths)
|
|
535
|
+
else:
|
|
536
|
+
path_str = self.window.core.filesystem.get_path(path)
|
|
537
|
+
QApplication.clipboard().setText(path_str)
|
|
538
|
+
self.window.controller.chat.common.append_to_input(path_str)
|
|
496
539
|
|
|
497
|
-
def copy_work_path(self, path: str):
|
|
540
|
+
def copy_work_path(self, path: Union[str, list]):
|
|
498
541
|
"""
|
|
499
542
|
Copy work path to clipboard
|
|
500
543
|
|
|
501
|
-
:param path: path to file
|
|
544
|
+
:param path: path to file or list of files
|
|
502
545
|
"""
|
|
503
|
-
|
|
546
|
+
if isinstance(path, list):
|
|
547
|
+
paths = [self.strip_work_path(p) for p in path]
|
|
548
|
+
path = "\n".join(paths)
|
|
549
|
+
else:
|
|
550
|
+
path = self.strip_work_path(path)
|
|
504
551
|
QApplication.clipboard().setText(path)
|
|
505
552
|
self.window.controller.chat.common.append_to_input(path)
|
|
506
553
|
|
|
507
|
-
def make_read_cmd(self, path: str):
|
|
554
|
+
def make_read_cmd(self, path: Union[str, list]):
|
|
508
555
|
"""
|
|
509
556
|
Make read command for file or directory and append to input
|
|
510
557
|
|
|
511
|
-
:param path: path to file
|
|
558
|
+
:param path: path to file or list of files
|
|
512
559
|
"""
|
|
513
|
-
if
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
560
|
+
files_list = path if isinstance(path, list) else [path]
|
|
561
|
+
cmd = ""
|
|
562
|
+
cmd_dir = []
|
|
563
|
+
cmd_current = []
|
|
564
|
+
for path in files_list:
|
|
565
|
+
if os.path.isdir(path):
|
|
566
|
+
cmd_dir.append(self.strip_work_path(path))
|
|
567
|
+
else:
|
|
568
|
+
cmd_current.append(self.strip_work_path(path))
|
|
569
|
+
if len(cmd_dir) > 1:
|
|
570
|
+
cmd = "Please list files from directories: " + ", ".join(cmd_dir)
|
|
571
|
+
elif len(cmd_dir) == 1:
|
|
572
|
+
cmd = f"Please list files from directory: {cmd_dir[0]}"
|
|
573
|
+
if len(cmd_current) > 1:
|
|
574
|
+
cmd = "Please read these files from current directory: " + ", ".join(cmd_current)
|
|
575
|
+
elif len(cmd_current) == 1:
|
|
576
|
+
cmd = f"Please read this file from current directory: {cmd_current[0]}"
|
|
517
577
|
self.window.controller.chat.common.append_to_input(cmd)
|
|
518
578
|
|
|
519
579
|
def make_ts_prefix(self) -> str:
|
|
@@ -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 19:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
13
13
|
import os
|
|
14
|
-
from typing import Any, List, Dict
|
|
14
|
+
from typing import Any, List, Dict, Union
|
|
15
15
|
|
|
16
16
|
from PySide6.QtCore import Slot, QObject
|
|
17
17
|
from PySide6.QtWidgets import QApplication
|
|
@@ -70,14 +70,14 @@ class Indexer(QObject):
|
|
|
70
70
|
|
|
71
71
|
def index_ctx_meta(
|
|
72
72
|
self,
|
|
73
|
-
meta_id: int,
|
|
73
|
+
meta_id: Union[int, list],
|
|
74
74
|
idx: str,
|
|
75
75
|
force: bool = False
|
|
76
76
|
):
|
|
77
77
|
"""
|
|
78
78
|
Index context meta (threaded)
|
|
79
79
|
|
|
80
|
-
:param meta_id: context meta id
|
|
80
|
+
:param meta_id: context meta id or list of ids
|
|
81
81
|
:param idx: index name
|
|
82
82
|
:param force: force index
|
|
83
83
|
"""
|
|
@@ -91,20 +91,36 @@ class Indexer(QObject):
|
|
|
91
91
|
)
|
|
92
92
|
return
|
|
93
93
|
|
|
94
|
-
meta = self.window.core.ctx.get_meta_by_id(meta_id)
|
|
95
|
-
self.window.update_status(trans('idx.status.indexing'))
|
|
96
|
-
|
|
97
94
|
worker = IndexWorker()
|
|
98
95
|
worker.window = self.window
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
self.window.update_status(trans('idx.status.indexing'))
|
|
97
|
+
|
|
98
|
+
# batch indexing
|
|
99
|
+
if isinstance(meta_id, list):
|
|
100
|
+
ts_batch = {}
|
|
101
|
+
ids = []
|
|
102
|
+
for mid in meta_id:
|
|
103
|
+
meta = self.window.core.ctx.get_meta_by_id(mid)
|
|
104
|
+
if meta is None:
|
|
105
|
+
continue
|
|
106
|
+
ids.append(mid)
|
|
107
|
+
ts_batch[mid] = meta.indexed
|
|
108
|
+
worker.content = ids
|
|
109
|
+
worker.idx = idx
|
|
110
|
+
worker.type = "db_meta_batch"
|
|
111
|
+
worker.from_ts_batch = ts_batch
|
|
112
|
+
else:
|
|
113
|
+
# single indexing
|
|
114
|
+
meta = self.window.core.ctx.get_meta_by_id(meta_id)
|
|
115
|
+
worker.content = meta_id
|
|
116
|
+
worker.idx = idx
|
|
117
|
+
worker.type = "db_meta"
|
|
118
|
+
worker.from_ts = meta.indexed
|
|
119
|
+
|
|
103
120
|
worker.signals.finished.connect(self.handle_finished_db_meta)
|
|
104
121
|
worker.signals.error.connect(self.handle_error)
|
|
105
122
|
self.window.threadpool.start(worker)
|
|
106
123
|
self.worker = worker
|
|
107
|
-
|
|
108
124
|
self.window.controller.idx.on_idx_start() # on start
|
|
109
125
|
|
|
110
126
|
def index_ctx_current(
|
|
@@ -306,34 +322,46 @@ class Indexer(QObject):
|
|
|
306
322
|
return
|
|
307
323
|
self.index_path(path, idx)
|
|
308
324
|
|
|
309
|
-
def index_file_confirm(self, path: str):
|
|
325
|
+
def index_file_confirm(self, path: Union[str, list]):
|
|
310
326
|
"""
|
|
311
327
|
Index file (force execute)
|
|
312
328
|
|
|
313
|
-
:param path: path to file or directory
|
|
329
|
+
:param path: path to file or directory or list of paths
|
|
314
330
|
"""
|
|
315
331
|
# get stored index name
|
|
316
332
|
if self.tmp_idx is None:
|
|
317
333
|
return
|
|
318
334
|
self.window.update_status(trans('idx.status.indexing'))
|
|
335
|
+
if isinstance(path, list):
|
|
336
|
+
self.index_paths(
|
|
337
|
+
path,
|
|
338
|
+
self.tmp_idx,
|
|
339
|
+
replace=False,
|
|
340
|
+
recursive=False,
|
|
341
|
+
)
|
|
342
|
+
return
|
|
319
343
|
self.index_path(path, self.tmp_idx)
|
|
320
344
|
|
|
321
345
|
def index_file(
|
|
322
346
|
self,
|
|
323
|
-
path: str,
|
|
347
|
+
path: Union[str, list],
|
|
324
348
|
idx: str = "base",
|
|
325
349
|
force: bool = False
|
|
326
350
|
):
|
|
327
351
|
"""
|
|
328
352
|
Index file or directory (threaded)
|
|
329
353
|
|
|
330
|
-
:param path: path to file or directory
|
|
354
|
+
:param path: path to file or directory or list of paths
|
|
331
355
|
:param idx: index name
|
|
332
356
|
:param force: force index
|
|
333
357
|
"""
|
|
334
358
|
self.tmp_idx = idx # store tmp index name (for confirmation)
|
|
335
359
|
if not force:
|
|
336
|
-
|
|
360
|
+
dir_srt = str(path)
|
|
361
|
+
# strip to max 50 chars
|
|
362
|
+
if len(dir_srt) > 50:
|
|
363
|
+
dir_srt = dir_srt[:25] + "..." + dir_srt[-25:]
|
|
364
|
+
content = trans('idx.confirm.file.content').replace('{dir}', dir_srt) \
|
|
337
365
|
+ "\n" + trans('idx.token.warn')
|
|
338
366
|
self.window.ui.dialogs.confirm(
|
|
339
367
|
type='idx.index.file',
|
|
@@ -341,23 +369,42 @@ class Indexer(QObject):
|
|
|
341
369
|
msg=content,
|
|
342
370
|
)
|
|
343
371
|
return
|
|
372
|
+
if isinstance(path, list):
|
|
373
|
+
self.index_paths(
|
|
374
|
+
path,
|
|
375
|
+
idx,
|
|
376
|
+
replace=False,
|
|
377
|
+
recursive=False,
|
|
378
|
+
)
|
|
379
|
+
return
|
|
344
380
|
self.index_path(path, idx)
|
|
345
381
|
|
|
346
|
-
def index_file_remove_confirm(self, path: str):
|
|
382
|
+
def index_file_remove_confirm(self, path: Union[str, list]):
|
|
347
383
|
"""
|
|
348
384
|
Remove file (force execute)
|
|
349
385
|
|
|
350
|
-
:param path: path to
|
|
386
|
+
:param path: path to indexed file or directory or list of paths
|
|
351
387
|
"""
|
|
352
388
|
# get stored index name
|
|
353
389
|
if self.tmp_idx is None:
|
|
354
390
|
return
|
|
355
391
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
392
|
+
dir_srt = str(path)
|
|
393
|
+
# strip to max 50 chars
|
|
394
|
+
if len(dir_srt) > 50:
|
|
395
|
+
dir_srt = dir_srt[:25] + "..." + dir_srt[-25:]
|
|
396
|
+
|
|
397
|
+
paths = []
|
|
398
|
+
if isinstance(path, list):
|
|
399
|
+
paths = path
|
|
400
|
+
else:
|
|
401
|
+
paths = [path]
|
|
402
|
+
for path in paths:
|
|
403
|
+
self.window.core.idx.remove_file(
|
|
404
|
+
self.tmp_idx,
|
|
405
|
+
path,
|
|
406
|
+
)
|
|
407
|
+
self.window.update_status(trans('status.deleted') + ": " + dir_srt)
|
|
361
408
|
self.tmp_idx = None
|
|
362
409
|
self.update_explorer() # update file status in explorer
|
|
363
410
|
|
|
@@ -376,7 +423,11 @@ class Indexer(QObject):
|
|
|
376
423
|
"""
|
|
377
424
|
self.tmp_idx = idx # store tmp index name (for confirmation)
|
|
378
425
|
if not force:
|
|
379
|
-
|
|
426
|
+
dir_srt = str(path)
|
|
427
|
+
# strip to max 50 chars
|
|
428
|
+
if len(dir_srt) > 50:
|
|
429
|
+
dir_srt = dir_srt[:25] + "..." + dir_srt[-25:]
|
|
430
|
+
content = trans('idx.confirm.file.remove.content').replace('{dir}', dir_srt)
|
|
380
431
|
self.window.ui.dialogs.confirm(
|
|
381
432
|
type='idx.index.file.remove',
|
|
382
433
|
id=path,
|
|
@@ -423,14 +474,14 @@ class Indexer(QObject):
|
|
|
423
474
|
def index_ctx_meta_remove(
|
|
424
475
|
self,
|
|
425
476
|
idx: str,
|
|
426
|
-
meta_id: int,
|
|
477
|
+
meta_id: Union[int, list],
|
|
427
478
|
force: bool = False
|
|
428
479
|
):
|
|
429
480
|
"""
|
|
430
481
|
Remove ctx meta from index
|
|
431
482
|
|
|
432
483
|
:param idx: index name
|
|
433
|
-
:param meta_id: meta id
|
|
484
|
+
:param meta_id: meta id or list of ids
|
|
434
485
|
:param force: force index
|
|
435
486
|
"""
|
|
436
487
|
if not force:
|
|
@@ -444,11 +495,16 @@ class Indexer(QObject):
|
|
|
444
495
|
return
|
|
445
496
|
|
|
446
497
|
store = self.window.core.idx.get_current_store()
|
|
447
|
-
if
|
|
448
|
-
|
|
498
|
+
ids = meta_id if isinstance(meta_id, list) else [meta_id]
|
|
499
|
+
updated = False
|
|
500
|
+
for meta_id in ids:
|
|
501
|
+
if self.window.core.ctx.idx.remove_meta_from_indexed(store, meta_id, self.tmp_idx):
|
|
502
|
+
self.window.update_status(trans('status.deleted') + ": " + str(meta_id))
|
|
503
|
+
updated = True
|
|
504
|
+
|
|
505
|
+
if updated:
|
|
449
506
|
self.window.controller.ctx.update() # update ctx list
|
|
450
|
-
|
|
451
|
-
self.window.tools.get("indexer").refresh()
|
|
507
|
+
self.window.tools.get("indexer").refresh()
|
|
452
508
|
|
|
453
509
|
def clear_by_idx(self, idx: int):
|
|
454
510
|
"""
|
|
@@ -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.28 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import threading
|
|
@@ -401,4 +401,15 @@ class Kernel:
|
|
|
401
401
|
|
|
402
402
|
:return: bool: True if the current thread is the main thread, False otherwise.
|
|
403
403
|
"""
|
|
404
|
-
return threading.current_thread() is threading.main_thread()
|
|
404
|
+
return threading.current_thread() is threading.main_thread()
|
|
405
|
+
|
|
406
|
+
def close_clients(self):
|
|
407
|
+
"""
|
|
408
|
+
Close all active clients associated with the kernel.
|
|
409
|
+
"""
|
|
410
|
+
w = self.window
|
|
411
|
+
try:
|
|
412
|
+
w.core.api.openai.safe_close()
|
|
413
|
+
w.core.api.google.safe_close()
|
|
414
|
+
except Exception:
|
|
415
|
+
pass
|
|
@@ -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.28 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from pygpt_net.core.events import Event, AppEvent
|
|
@@ -81,6 +81,8 @@ class Mode:
|
|
|
81
81
|
c = w.controller
|
|
82
82
|
core = w.core
|
|
83
83
|
cfg = core.config
|
|
84
|
+
cfg.set('mode', mode) # must be set before assistant prompt update!
|
|
85
|
+
|
|
84
86
|
try:
|
|
85
87
|
if mode == MODE_ASSISTANT:
|
|
86
88
|
c.presets.select_default()
|
|
@@ -95,8 +97,6 @@ class Mode:
|
|
|
95
97
|
elif mode == MODE_AUDIO:
|
|
96
98
|
c.audio.set_muted(False) # un-mute and show audio output icon by default
|
|
97
99
|
|
|
98
|
-
cfg.set('mode', mode)
|
|
99
|
-
|
|
100
100
|
# reset model and preset at start
|
|
101
101
|
cfg.set('model', "")
|
|
102
102
|
cfg.set('preset', "")
|
|
@@ -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.12.
|
|
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
|
|
@@ -242,7 +242,8 @@ class Editor:
|
|
|
242
242
|
height=self.height,
|
|
243
243
|
)
|
|
244
244
|
self.dialog = True
|
|
245
|
-
self.window.ui.nodes
|
|
245
|
+
if "models.editor.search" in self.window.ui.nodes:
|
|
246
|
+
self.window.ui.nodes['models.editor.search'].setFocus() # focus on search
|
|
246
247
|
self.locked = False
|
|
247
248
|
|
|
248
249
|
def undo(self):
|
|
@@ -394,15 +395,15 @@ class Editor:
|
|
|
394
395
|
"""Create new model"""
|
|
395
396
|
self.locked = True
|
|
396
397
|
self.save(persist=False)
|
|
397
|
-
model = self.window.core.models.create_empty()
|
|
398
|
-
|
|
398
|
+
model, new_id = self.window.core.models.create_empty()
|
|
399
|
+
if self.provider != "-":
|
|
400
|
+
model.provider = self.provider
|
|
399
401
|
self.window.core.models.sort_items()
|
|
400
402
|
self.window.core.models.save()
|
|
401
403
|
self.reload_items()
|
|
402
404
|
|
|
403
405
|
# switch to created model
|
|
404
406
|
self.current = model.id
|
|
405
|
-
# sort here
|
|
406
407
|
idx = self.get_tab_by_id(self.current)
|
|
407
408
|
self.set_by_tab(idx)
|
|
408
409
|
self.init()
|
|
@@ -410,17 +411,15 @@ class Editor:
|
|
|
410
411
|
|
|
411
412
|
def delete_by_idx(
|
|
412
413
|
self,
|
|
413
|
-
idx: int,
|
|
414
|
+
idx: Union[int, list],
|
|
414
415
|
force: bool = False
|
|
415
416
|
):
|
|
416
417
|
"""
|
|
417
418
|
Delete model by idx
|
|
418
419
|
|
|
419
|
-
:param idx: model idx
|
|
420
|
+
:param idx: model idx or list of idxs
|
|
420
421
|
:param force: force delete
|
|
421
422
|
"""
|
|
422
|
-
self.locked = True
|
|
423
|
-
model = self.get_model_by_tab_idx(idx)
|
|
424
423
|
if not force:
|
|
425
424
|
self.window.ui.dialogs.confirm(
|
|
426
425
|
type="models.editor.delete",
|
|
@@ -428,14 +427,70 @@ class Editor:
|
|
|
428
427
|
msg=trans("dialog.models.editor.delete.confirm"),
|
|
429
428
|
)
|
|
430
429
|
return
|
|
431
|
-
|
|
432
|
-
self.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
+
|
|
436
455
|
self.init()
|
|
437
456
|
self.locked = False
|
|
438
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
|
+
|
|
439
494
|
def load_defaults_user(self, force: bool = False):
|
|
440
495
|
"""
|
|
441
496
|
Load models editor user defaults
|