pygpt-net 2.6.22__py3-none-any.whl → 2.6.24__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 +16 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/controller/agent/llama.py +3 -0
- pygpt_net/controller/chat/response.py +6 -1
- pygpt_net/controller/files/files.py +24 -55
- pygpt_net/controller/theme/theme.py +3 -3
- pygpt_net/core/agents/observer/evaluation.py +2 -2
- pygpt_net/core/agents/runners/loop.py +1 -0
- pygpt_net/core/attachments/context.py +4 -4
- pygpt_net/core/bridge/bridge.py +2 -0
- pygpt_net/core/filesystem/opener.py +261 -0
- pygpt_net/core/filesystem/url.py +13 -10
- pygpt_net/core/idx/chat.py +1 -1
- pygpt_net/core/idx/indexing.py +3 -3
- pygpt_net/core/idx/llm.py +61 -2
- pygpt_net/core/platforms/platforms.py +5 -4
- pygpt_net/data/config/config.json +21 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/settings.json +18 -0
- pygpt_net/data/css/web-blocks.dark.css +7 -1
- pygpt_net/data/css/web-blocks.light.css +5 -2
- pygpt_net/data/css/web-chatgpt.dark.css +7 -1
- pygpt_net/data/css/web-chatgpt.light.css +3 -0
- pygpt_net/data/css/web-chatgpt_wide.dark.css +7 -1
- pygpt_net/data/css/web-chatgpt_wide.light.css +3 -0
- pygpt_net/data/locale/locale.de.ini +47 -0
- pygpt_net/data/locale/locale.en.ini +50 -1
- pygpt_net/data/locale/locale.es.ini +47 -0
- pygpt_net/data/locale/locale.fr.ini +47 -0
- pygpt_net/data/locale/locale.it.ini +47 -0
- pygpt_net/data/locale/locale.pl.ini +47 -0
- pygpt_net/data/locale/locale.uk.ini +47 -0
- pygpt_net/data/locale/locale.zh.ini +47 -0
- pygpt_net/provider/agents/llama_index/codeact_workflow.py +8 -7
- pygpt_net/provider/agents/llama_index/planner_workflow.py +11 -10
- pygpt_net/provider/agents/llama_index/supervisor_workflow.py +9 -8
- pygpt_net/provider/agents/openai/agent_b2b.py +30 -17
- pygpt_net/provider/agents/openai/agent_planner.py +29 -29
- pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +21 -23
- pygpt_net/provider/agents/openai/agent_with_feedback.py +21 -23
- pygpt_net/provider/agents/openai/bot_researcher.py +25 -30
- pygpt_net/provider/agents/openai/evolve.py +37 -39
- pygpt_net/provider/agents/openai/supervisor.py +16 -18
- pygpt_net/provider/core/config/patch.py +20 -1
- pygpt_net/provider/llms/anthropic.py +5 -4
- pygpt_net/provider/llms/google.py +2 -2
- pygpt_net/ui/layout/toolbox/agent_llama.py +2 -3
- pygpt_net/ui/widget/tabs/layout.py +6 -4
- pygpt_net/ui/widget/tabs/output.py +348 -13
- pygpt_net/ui/widget/textarea/input.py +74 -8
- {pygpt_net-2.6.22.dist-info → pygpt_net-2.6.24.dist-info}/METADATA +34 -25
- {pygpt_net-2.6.22.dist-info → pygpt_net-2.6.24.dist-info}/RECORD +55 -54
- {pygpt_net-2.6.22.dist-info → pygpt_net-2.6.24.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.22.dist-info → pygpt_net-2.6.24.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.22.dist-info → pygpt_net-2.6.24.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
2.6.24 (2025-08-26)
|
|
2
|
+
|
|
3
|
+
- Added a new option: LlamaIndex -> Embeddings -> Default embedding providers for attachments.
|
|
4
|
+
- The same model provider is now used for both embedding and RAG query in attachment indexing.
|
|
5
|
+
- Translations have been added to Agents.
|
|
6
|
+
- Fixed fetching Anthropic models list.
|
|
7
|
+
- Added Google GenAI Embeddings.
|
|
8
|
+
|
|
9
|
+
2.6.23 (2025-08-25)
|
|
10
|
+
|
|
11
|
+
- Added an inline "Add a new chat" button to the right of the tabs.
|
|
12
|
+
- Added an "Add Attachment" button in the input field.
|
|
13
|
+
- Improved file open in the system's file manager
|
|
14
|
+
- Fixed the restoration of input text color when changing themes from light to dark.
|
|
15
|
+
- Fixed last eval step finish if 100% complete.
|
|
16
|
+
|
|
1
17
|
2.6.22 (2025-08-25)
|
|
2
18
|
|
|
3
19
|
- UI refactor and optimizations.
|
pygpt_net/__init__.py
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
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.
|
|
9
|
+
# Updated Date: 2025.08.26 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
__author__ = "Marcin Szczygliński"
|
|
13
13
|
__copyright__ = "Copyright 2025, Marcin Szczygliński"
|
|
14
14
|
__credits__ = ["Marcin Szczygliński"]
|
|
15
15
|
__license__ = "MIT"
|
|
16
|
-
__version__ = "2.6.
|
|
17
|
-
__build__ = "2025-08-
|
|
16
|
+
__version__ = "2.6.24"
|
|
17
|
+
__build__ = "2025-08-26"
|
|
18
18
|
__maintainer__ = "Marcin Szczygliński"
|
|
19
19
|
__github__ = "https://github.com/szczyglis-dev/py-gpt"
|
|
20
20
|
__report__ = "https://github.com/szczyglis-dev/py-gpt/issues"
|
|
@@ -104,6 +104,9 @@ class Llama:
|
|
|
104
104
|
|
|
105
105
|
def on_end(self):
|
|
106
106
|
"""End of run"""
|
|
107
|
+
self.window.dispatch(KernelEvent(KernelEvent.STATE_IDLE, {
|
|
108
|
+
"id": "agent",
|
|
109
|
+
}))
|
|
107
110
|
self.eval_step = 0 # reset evaluation step
|
|
108
111
|
if self.window.core.config.get("agent.goal.notify"):
|
|
109
112
|
# show notification if enabled and mode is not llama_index
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.08.
|
|
9
|
+
# Updated Date: 2025.08.26 01:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Dict, Any
|
|
@@ -264,6 +264,11 @@ class Response:
|
|
|
264
264
|
if global_mode not in self.AGENT_MODES_ALLOWED:
|
|
265
265
|
return # no agent mode, nothing to do
|
|
266
266
|
|
|
267
|
+
# agent evaluation finish
|
|
268
|
+
if ctx.extra is not None and (isinstance(ctx.extra, dict) and "agent_eval_finish" in ctx.extra):
|
|
269
|
+
controller.agent.llama.on_end(ctx)
|
|
270
|
+
return
|
|
271
|
+
|
|
267
272
|
# not agent final response
|
|
268
273
|
if ctx.extra is None or (isinstance(ctx.extra, dict) and "agent_finish" not in ctx.extra):
|
|
269
274
|
self.window.update_status(trans("status.agent.reasoning"))
|
|
@@ -6,21 +6,18 @@
|
|
|
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.
|
|
9
|
+
# Updated Date: 2025.08.25 18:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
13
13
|
import os
|
|
14
14
|
import shutil
|
|
15
15
|
from typing import Optional
|
|
16
|
+
from shutil import copy2
|
|
16
17
|
|
|
17
|
-
from PySide6.QtCore import QUrl
|
|
18
|
-
from PySide6.QtGui import QDesktopServices
|
|
19
18
|
from PySide6.QtWidgets import QFileDialog, QApplication
|
|
20
|
-
from showinfm import show_in_file_manager
|
|
21
|
-
from shutil import copy2
|
|
22
|
-
import subprocess
|
|
23
19
|
|
|
20
|
+
from pygpt_net.core.filesystem.opener import Opener
|
|
24
21
|
from pygpt_net.utils import trans
|
|
25
22
|
|
|
26
23
|
|
|
@@ -60,12 +57,12 @@ class Files:
|
|
|
60
57
|
try:
|
|
61
58
|
shutil.rmtree(path) # delete directory with all files
|
|
62
59
|
self.window.update_status(
|
|
63
|
-
"[OK] Deleted directory: {
|
|
60
|
+
f"[OK] Deleted directory: {os.path.basename(path)}"
|
|
64
61
|
)
|
|
65
62
|
self.update_explorer()
|
|
66
63
|
except Exception as e:
|
|
67
64
|
self.window.core.debug.log(e)
|
|
68
|
-
print("Error deleting directory: {} - {}"
|
|
65
|
+
print(f"Error deleting directory: {path} - {e}")
|
|
69
66
|
|
|
70
67
|
def touch_file(
|
|
71
68
|
self,
|
|
@@ -93,12 +90,12 @@ class Files:
|
|
|
93
90
|
filepath = os.path.join(path, name)
|
|
94
91
|
open(filepath, 'a').close()
|
|
95
92
|
self.window.update_status(
|
|
96
|
-
"[OK] Created file: {}"
|
|
93
|
+
f"[OK] Created file: {filepath}"
|
|
97
94
|
)
|
|
98
95
|
self.update_explorer()
|
|
99
96
|
except Exception as e:
|
|
100
97
|
self.window.core.debug.log(e)
|
|
101
|
-
print("Error creating file: {} - {}"
|
|
98
|
+
print(f"Error creating file: {path} - {e}")
|
|
102
99
|
|
|
103
100
|
def delete(
|
|
104
101
|
self,
|
|
@@ -130,12 +127,12 @@ class Files:
|
|
|
130
127
|
try:
|
|
131
128
|
os.remove(path)
|
|
132
129
|
self.window.update_status(
|
|
133
|
-
|
|
130
|
+
f"[OK] Deleted file: {os.path.basename(path)}"
|
|
134
131
|
)
|
|
135
132
|
self.update_explorer()
|
|
136
133
|
except Exception as e:
|
|
137
134
|
self.window.core.debug.log(e)
|
|
138
|
-
print("Error deleting file: {} - {}"
|
|
135
|
+
print(f"Error deleting file: {path} - {e}")
|
|
139
136
|
|
|
140
137
|
def duplicate_local(
|
|
141
138
|
self,
|
|
@@ -172,7 +169,7 @@ class Files:
|
|
|
172
169
|
|
|
173
170
|
if os.path.exists(new_path):
|
|
174
171
|
self.window.update_status(
|
|
175
|
-
"[ERROR] File already exists: {
|
|
172
|
+
f"[ERROR] File already exists: {os.path.basename(new_path)}"
|
|
176
173
|
)
|
|
177
174
|
return
|
|
178
175
|
|
|
@@ -181,12 +178,12 @@ class Files:
|
|
|
181
178
|
else:
|
|
182
179
|
copy2(path, new_path)
|
|
183
180
|
self.window.update_status(
|
|
184
|
-
"[OK] Duplicated file: {
|
|
181
|
+
f"[OK] Duplicated file: {os.path.basename(path)} -> {name}"
|
|
185
182
|
)
|
|
186
183
|
self.update_explorer()
|
|
187
184
|
except Exception as e:
|
|
188
185
|
self.window.core.debug.log(e)
|
|
189
|
-
print("Error duplicating file: {} - {}"
|
|
186
|
+
print(f"Error duplicating file: {path} - {e}")
|
|
190
187
|
|
|
191
188
|
def download_local(self, path: str):
|
|
192
189
|
"""
|
|
@@ -211,11 +208,11 @@ class Files:
|
|
|
211
208
|
else:
|
|
212
209
|
shutil.copy2(path, files[0])
|
|
213
210
|
self.window.update_status(
|
|
214
|
-
"[OK] Downloaded file: {
|
|
211
|
+
f"[OK] Downloaded file: {os.path.basename(path)}"
|
|
215
212
|
)
|
|
216
213
|
except Exception as e:
|
|
217
214
|
self.window.core.debug.log(e)
|
|
218
|
-
print("Error downloading file: {} - {}"
|
|
215
|
+
print(f"Error downloading file: {path} - {e}")
|
|
219
216
|
|
|
220
217
|
def upload_local(
|
|
221
218
|
self,
|
|
@@ -268,9 +265,9 @@ class Files:
|
|
|
268
265
|
num += 1
|
|
269
266
|
except Exception as e:
|
|
270
267
|
self.window.core.debug.log(e)
|
|
271
|
-
print("Error copying file {}: {}"
|
|
268
|
+
print(f"Error copying file {file_path}: {e}")
|
|
272
269
|
if num > 0:
|
|
273
|
-
self.window.update_status("[OK] Uploaded: {} files."
|
|
270
|
+
self.window.update_status(f"[OK] Uploaded: {num} files.")
|
|
274
271
|
self.update_explorer()
|
|
275
272
|
|
|
276
273
|
def rename(self, path: str):
|
|
@@ -299,12 +296,12 @@ class Files:
|
|
|
299
296
|
new_path = os.path.join(os.path.dirname(path), name)
|
|
300
297
|
if os.path.exists(new_path):
|
|
301
298
|
self.window.update_status(
|
|
302
|
-
"[ERROR] File already exists: {
|
|
299
|
+
f"[ERROR] File already exists: {os.path.basename(new_path)}"
|
|
303
300
|
)
|
|
304
301
|
return
|
|
305
302
|
os.rename(path, new_path)
|
|
306
303
|
self.window.update_status(
|
|
307
|
-
|
|
304
|
+
f"[OK] Renamed: {os.path.basename(path)} -> {name}"
|
|
308
305
|
)
|
|
309
306
|
self.window.ui.dialog['rename'].close()
|
|
310
307
|
self.update_explorer()
|
|
@@ -328,24 +325,8 @@ class Files:
|
|
|
328
325
|
|
|
329
326
|
:param path: path to file or directory
|
|
330
327
|
"""
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
url = QUrl.fromLocalFile(path)
|
|
334
|
-
QDesktopServices.openUrl(url)
|
|
335
|
-
else:
|
|
336
|
-
url = QUrl.fromLocalFile(path)
|
|
337
|
-
if not QDesktopServices.openUrl(url):
|
|
338
|
-
subprocess.run(['gio', 'open', path])
|
|
339
|
-
else:
|
|
340
|
-
if not self.window.core.platforms.is_snap():
|
|
341
|
-
path = self.window.core.filesystem.get_path(path)
|
|
342
|
-
url = QUrl.fromLocalFile(path)
|
|
343
|
-
QDesktopServices.openUrl(url)
|
|
344
|
-
else:
|
|
345
|
-
path = self.window.core.filesystem.get_path(path)
|
|
346
|
-
url = QUrl.fromLocalFile(path)
|
|
347
|
-
QDesktopServices.openUrl(url)
|
|
348
|
-
#subprocess.run(['gio', 'open', path])
|
|
328
|
+
path = self.window.core.filesystem.get_path(path)
|
|
329
|
+
Opener.open_path(path, reveal=False)
|
|
349
330
|
|
|
350
331
|
def open_in_file_manager(
|
|
351
332
|
self,
|
|
@@ -359,20 +340,8 @@ class Files:
|
|
|
359
340
|
:param select: select file in file manager
|
|
360
341
|
"""
|
|
361
342
|
path = self.window.core.filesystem.get_path(path)
|
|
362
|
-
if select: # path to file: open containing directory
|
|
363
|
-
path = self.window.core.filesystem.get_path(
|
|
364
|
-
os.path.dirname(path)
|
|
365
|
-
)
|
|
366
|
-
|
|
367
343
|
if os.path.exists(path):
|
|
368
|
-
|
|
369
|
-
url = self.window.core.filesystem.get_url(path)
|
|
370
|
-
QDesktopServices.openUrl(url)
|
|
371
|
-
# show_in_file_manager(path, select)
|
|
372
|
-
else:
|
|
373
|
-
url = QUrl.fromLocalFile(path)
|
|
374
|
-
if not QDesktopServices.openUrl(url):
|
|
375
|
-
subprocess.run(['gio', 'open', path])
|
|
344
|
+
Opener.open_path(path, reveal=select)
|
|
376
345
|
|
|
377
346
|
def make_dir_dialog(self, path: str):
|
|
378
347
|
"""
|
|
@@ -411,7 +380,7 @@ class Files:
|
|
|
411
380
|
return
|
|
412
381
|
os.makedirs(path_dir, exist_ok=True)
|
|
413
382
|
self.window.update_status(
|
|
414
|
-
"[OK] Directory created: {}"
|
|
383
|
+
f"[OK] Directory created: {name}"
|
|
415
384
|
)
|
|
416
385
|
self.update_explorer()
|
|
417
386
|
|
|
@@ -473,9 +442,9 @@ class Files:
|
|
|
473
442
|
:param path: path to file
|
|
474
443
|
"""
|
|
475
444
|
if os.path.isdir(path):
|
|
476
|
-
cmd = "Please list files from directory: {
|
|
445
|
+
cmd = f"Please list files from directory: {self.strip_work_path(path)}"
|
|
477
446
|
else:
|
|
478
|
-
cmd = "Please read this file from current directory: {
|
|
447
|
+
cmd = f"Please read this file from current directory: {self.strip_work_path(path)}"
|
|
479
448
|
self.window.controller.chat.common.append_to_input(cmd)
|
|
480
449
|
|
|
481
450
|
def make_ts_prefix(self) -> str:
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.08.
|
|
9
|
+
# Updated Date: 2025.08.25 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -50,7 +50,7 @@ class Theme:
|
|
|
50
50
|
|
|
51
51
|
:param name: theme name
|
|
52
52
|
"""
|
|
53
|
-
self.window.update_status(trans("status.
|
|
53
|
+
self.window.update_status(trans("status.reloading"))
|
|
54
54
|
QApplication.processEvents()
|
|
55
55
|
self.toggle(name, force=True)
|
|
56
56
|
self.window.update_status("")
|
|
@@ -62,7 +62,7 @@ class Theme:
|
|
|
62
62
|
:param name: option name
|
|
63
63
|
:param value: option value
|
|
64
64
|
"""
|
|
65
|
-
self.window.update_status(trans("status.
|
|
65
|
+
self.window.update_status(trans("status.reloading"))
|
|
66
66
|
QApplication.processEvents()
|
|
67
67
|
self.toggle_option(name, value)
|
|
68
68
|
self.window.update_status("")
|
|
@@ -212,7 +212,7 @@ class Evaluation:
|
|
|
212
212
|
return prompt.format(
|
|
213
213
|
task=main_task,
|
|
214
214
|
input=last_input,
|
|
215
|
-
output=final_response
|
|
215
|
+
output=final_response,
|
|
216
216
|
)
|
|
217
217
|
|
|
218
218
|
def get_prompt_complete(self, history: List[CtxItem]) -> str:
|
|
@@ -229,7 +229,7 @@ class Evaluation:
|
|
|
229
229
|
return prompt.format(
|
|
230
230
|
task=main_task,
|
|
231
231
|
input=last_input,
|
|
232
|
-
output=final_response
|
|
232
|
+
output=final_response,
|
|
233
233
|
)
|
|
234
234
|
|
|
235
235
|
def get_tools(self) -> List[FunctionTool]:
|
|
@@ -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.08.26 01:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import copy
|
|
@@ -496,12 +496,12 @@ class Context:
|
|
|
496
496
|
:param documents: list of documents (optional)
|
|
497
497
|
:return: list of doc IDs
|
|
498
498
|
"""
|
|
499
|
-
model =
|
|
499
|
+
model, model_item = self.get_selected_model("query")
|
|
500
500
|
doc_ids = []
|
|
501
501
|
if type == AttachmentItem.TYPE_FILE:
|
|
502
|
-
doc_ids = self.window.core.idx.indexing.index_attachment(source, idx_path,
|
|
502
|
+
doc_ids = self.window.core.idx.indexing.index_attachment(source, idx_path, model_item, documents)
|
|
503
503
|
elif type == AttachmentItem.TYPE_URL:
|
|
504
|
-
doc_ids = self.window.core.idx.indexing.index_attachment_web(source, idx_path,
|
|
504
|
+
doc_ids = self.window.core.idx.indexing.index_attachment_web(source, idx_path, model_item, documents)
|
|
505
505
|
if self.is_verbose():
|
|
506
506
|
print("Attachments: indexed. Doc IDs: {}".format(doc_ids))
|
|
507
507
|
return doc_ids
|
pygpt_net/core/bridge/bridge.py
CHANGED
|
@@ -150,6 +150,7 @@ class Bridge:
|
|
|
150
150
|
# async call
|
|
151
151
|
self.window.core.debug.info("[bridge] Starting worker (async)...")
|
|
152
152
|
self.window.threadpool.start(worker)
|
|
153
|
+
self.worker = worker
|
|
153
154
|
return True
|
|
154
155
|
|
|
155
156
|
def request_next(
|
|
@@ -177,6 +178,7 @@ class Bridge:
|
|
|
177
178
|
|
|
178
179
|
# async call
|
|
179
180
|
self.window.threadpool.start(worker)
|
|
181
|
+
self.worker = worker
|
|
180
182
|
return True
|
|
181
183
|
|
|
182
184
|
def call(
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# ================================================== #
|
|
4
|
+
# This file is a part of PYGPT package #
|
|
5
|
+
# Website: https://pygpt.net #
|
|
6
|
+
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
|
+
# MIT License #
|
|
8
|
+
# Created By : Marcin Szczygliński #
|
|
9
|
+
# Updated Date: 2025.08.25 18:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import shutil
|
|
15
|
+
import subprocess
|
|
16
|
+
|
|
17
|
+
from PySide6.QtCore import QUrl
|
|
18
|
+
from PySide6.QtGui import QDesktopServices
|
|
19
|
+
|
|
20
|
+
# QtDBus is optional and may not be available on all systems
|
|
21
|
+
try:
|
|
22
|
+
from PySide6.QtDBus import QDBusInterface, QDBusConnection, QDBusMessage
|
|
23
|
+
HAS_QT_DBUS = True
|
|
24
|
+
except Exception:
|
|
25
|
+
HAS_QT_DBUS = False
|
|
26
|
+
|
|
27
|
+
IS_WINDOWS = sys.platform.startswith("win")
|
|
28
|
+
IS_MAC = sys.platform == "darwin"
|
|
29
|
+
IS_LINUX = sys.platform.startswith("linux")
|
|
30
|
+
|
|
31
|
+
class Opener:
|
|
32
|
+
# ===== public API =====
|
|
33
|
+
@staticmethod
|
|
34
|
+
def open_path(path: str, reveal: bool = False) -> bool:
|
|
35
|
+
"""
|
|
36
|
+
Open file or directory in the system file manager or default application.
|
|
37
|
+
|
|
38
|
+
:param path: Path to file or directory
|
|
39
|
+
:param reveal: If True and path is a file, reveal it in the file manager
|
|
40
|
+
:return: True if successful, False otherwise
|
|
41
|
+
"""
|
|
42
|
+
p = os.path.abspath(path)
|
|
43
|
+
|
|
44
|
+
if IS_WINDOWS:
|
|
45
|
+
if reveal and os.path.isfile(p):
|
|
46
|
+
return Opener._reveal_windows(p)
|
|
47
|
+
if os.path.isdir(p):
|
|
48
|
+
return Opener._open_dir_windows(p)
|
|
49
|
+
return Opener._open_file_windows(p)
|
|
50
|
+
|
|
51
|
+
if IS_MAC:
|
|
52
|
+
if reveal and os.path.isfile(p):
|
|
53
|
+
return Opener._reveal_mac(p)
|
|
54
|
+
if os.path.isdir(p):
|
|
55
|
+
return Opener._open_dir_mac(p)
|
|
56
|
+
return Opener._open_file_mac(p)
|
|
57
|
+
|
|
58
|
+
# Linux
|
|
59
|
+
if reveal and os.path.isfile(p):
|
|
60
|
+
if Opener._reveal_linux(p):
|
|
61
|
+
return True
|
|
62
|
+
# fallback:
|
|
63
|
+
return Opener._open_dir_linux(os.path.dirname(p) or "/")
|
|
64
|
+
if os.path.isdir(p):
|
|
65
|
+
return Opener._open_dir_linux(p)
|
|
66
|
+
return Opener._open_file_linux(p)
|
|
67
|
+
|
|
68
|
+
# ===== Windows =====
|
|
69
|
+
@staticmethod
|
|
70
|
+
def _open_dir_windows(path: str) -> bool:
|
|
71
|
+
"""
|
|
72
|
+
Open directory in Windows Explorer
|
|
73
|
+
|
|
74
|
+
:param path: Path to directory
|
|
75
|
+
:return: True if successful, False otherwise
|
|
76
|
+
"""
|
|
77
|
+
try:
|
|
78
|
+
os.startfile(path)
|
|
79
|
+
return True
|
|
80
|
+
except Exception:
|
|
81
|
+
try:
|
|
82
|
+
subprocess.Popen(["explorer", path])
|
|
83
|
+
return True
|
|
84
|
+
except Exception:
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def _reveal_windows(path: str) -> bool:
|
|
89
|
+
"""
|
|
90
|
+
Reveal file in Windows Explorer
|
|
91
|
+
|
|
92
|
+
:param path: Path to file
|
|
93
|
+
:return: True if successful, False otherwise
|
|
94
|
+
"""
|
|
95
|
+
try:
|
|
96
|
+
subprocess.Popen(["explorer", "/select,", os.path.normpath(path)])
|
|
97
|
+
return True
|
|
98
|
+
except Exception:
|
|
99
|
+
return Opener._open_dir_windows(os.path.dirname(path) or "C:\\")
|
|
100
|
+
|
|
101
|
+
@staticmethod
|
|
102
|
+
def _open_file_windows(path: str) -> bool:
|
|
103
|
+
"""
|
|
104
|
+
Open file with default application
|
|
105
|
+
|
|
106
|
+
:param path: Path to file
|
|
107
|
+
:return: True if successful, False otherwise
|
|
108
|
+
"""
|
|
109
|
+
try:
|
|
110
|
+
os.startfile(path)
|
|
111
|
+
return True
|
|
112
|
+
except Exception:
|
|
113
|
+
# Qt
|
|
114
|
+
return QDesktopServices.openUrl(QUrl.fromLocalFile(path))
|
|
115
|
+
|
|
116
|
+
# ===== macOS =====
|
|
117
|
+
@staticmethod
|
|
118
|
+
def _open_dir_mac(path: str) -> bool:
|
|
119
|
+
"""
|
|
120
|
+
Open directory in Finder
|
|
121
|
+
|
|
122
|
+
:param path: Path to directory
|
|
123
|
+
:return: True if successful, False otherwise
|
|
124
|
+
"""
|
|
125
|
+
try:
|
|
126
|
+
subprocess.Popen(["open", path])
|
|
127
|
+
return True
|
|
128
|
+
except Exception:
|
|
129
|
+
return QDesktopServices.openUrl(QUrl.fromLocalFile(path))
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def _reveal_mac(path: str) -> bool:
|
|
133
|
+
"""
|
|
134
|
+
Reveal file in Finder
|
|
135
|
+
|
|
136
|
+
:param path: Path to file
|
|
137
|
+
:return: True if successful, False otherwise
|
|
138
|
+
"""
|
|
139
|
+
try:
|
|
140
|
+
subprocess.Popen(["open", "-R", path])
|
|
141
|
+
return True
|
|
142
|
+
except Exception:
|
|
143
|
+
return Opener._open_dir_mac(os.path.dirname(path) or "/")
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def _open_file_mac(path: str) -> bool:
|
|
147
|
+
"""
|
|
148
|
+
Open file with default application
|
|
149
|
+
|
|
150
|
+
:param path: Path to file
|
|
151
|
+
:return: True if successful, False otherwise
|
|
152
|
+
"""
|
|
153
|
+
try:
|
|
154
|
+
subprocess.Popen(["open", path])
|
|
155
|
+
return True
|
|
156
|
+
except Exception:
|
|
157
|
+
return QDesktopServices.openUrl(QUrl.fromLocalFile(path))
|
|
158
|
+
|
|
159
|
+
# ===== Linux =====
|
|
160
|
+
@staticmethod
|
|
161
|
+
def _fm_iface():
|
|
162
|
+
"""
|
|
163
|
+
Get FileManager1 D-Bus interface if available
|
|
164
|
+
|
|
165
|
+
:return: QDBusInterface or None
|
|
166
|
+
"""
|
|
167
|
+
if not (IS_LINUX and HAS_QT_DBUS):
|
|
168
|
+
return None
|
|
169
|
+
bus = QDBusConnection.sessionBus()
|
|
170
|
+
iface = QDBusInterface(
|
|
171
|
+
"org.freedesktop.FileManager1",
|
|
172
|
+
"/org/freedesktop/FileManager1",
|
|
173
|
+
"org.freedesktop.FileManager1",
|
|
174
|
+
bus,
|
|
175
|
+
)
|
|
176
|
+
return iface if iface.isValid() else None
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def _show_folders_dbus(paths):
|
|
180
|
+
"""
|
|
181
|
+
Show folders using D-Bus FileManager1 interface
|
|
182
|
+
|
|
183
|
+
:param paths: List of folder paths
|
|
184
|
+
:return: True if successful, False otherwise
|
|
185
|
+
"""
|
|
186
|
+
iface = Opener._fm_iface()
|
|
187
|
+
if not iface:
|
|
188
|
+
return False
|
|
189
|
+
uris = [QUrl.fromLocalFile(p).toString() for p in paths]
|
|
190
|
+
reply = iface.call("ShowFolders", uris, "")
|
|
191
|
+
return reply.type() != QDBusMessage.ErrorMessage
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def _show_items_dbus(paths):
|
|
195
|
+
"""
|
|
196
|
+
Show items using D-Bus FileManager1 interface
|
|
197
|
+
|
|
198
|
+
:param paths: List of item paths
|
|
199
|
+
:return: True if successful, False otherwise
|
|
200
|
+
"""
|
|
201
|
+
iface = Opener._fm_iface()
|
|
202
|
+
if not iface:
|
|
203
|
+
return False
|
|
204
|
+
uris = [QUrl.fromLocalFile(p).toString() for p in paths]
|
|
205
|
+
reply = iface.call("ShowItems", uris, "")
|
|
206
|
+
return reply.type() != QDBusMessage.ErrorMessage
|
|
207
|
+
|
|
208
|
+
@staticmethod
|
|
209
|
+
def _open_with_cli_linux(path):
|
|
210
|
+
"""
|
|
211
|
+
Open path using common CLI tools (xdg-open, gio)
|
|
212
|
+
|
|
213
|
+
:param path: Path to file or directory
|
|
214
|
+
:return: True if successful, False otherwise
|
|
215
|
+
"""
|
|
216
|
+
for cmd in (["xdg-open", path], ["gio", "open", path]):
|
|
217
|
+
if shutil.which(cmd[0]):
|
|
218
|
+
try:
|
|
219
|
+
subprocess.Popen(cmd)
|
|
220
|
+
return True
|
|
221
|
+
except Exception:
|
|
222
|
+
pass
|
|
223
|
+
return False
|
|
224
|
+
|
|
225
|
+
@staticmethod
|
|
226
|
+
def _open_dir_linux(path: str) -> bool:
|
|
227
|
+
"""
|
|
228
|
+
Open directory in default file manager
|
|
229
|
+
|
|
230
|
+
:param path: Path to directory
|
|
231
|
+
:return: True if successful, False otherwise
|
|
232
|
+
"""
|
|
233
|
+
if Opener._show_folders_dbus([path]):
|
|
234
|
+
return True
|
|
235
|
+
if QDesktopServices.openUrl(QUrl.fromLocalFile(path)):
|
|
236
|
+
return True
|
|
237
|
+
return Opener._open_with_cli_linux(path)
|
|
238
|
+
|
|
239
|
+
@staticmethod
|
|
240
|
+
def _reveal_linux(path: str) -> bool:
|
|
241
|
+
"""
|
|
242
|
+
Reveal file in default file manager
|
|
243
|
+
|
|
244
|
+
:param path: Path to file
|
|
245
|
+
:return: True if successful, False otherwise
|
|
246
|
+
"""
|
|
247
|
+
if Opener._show_items_dbus([path]):
|
|
248
|
+
return True
|
|
249
|
+
return False
|
|
250
|
+
|
|
251
|
+
@staticmethod
|
|
252
|
+
def _open_file_linux(path: str) -> bool:
|
|
253
|
+
"""
|
|
254
|
+
Open file with default application
|
|
255
|
+
|
|
256
|
+
:param path: Path to file
|
|
257
|
+
:return: True if successful, False otherwise
|
|
258
|
+
"""
|
|
259
|
+
if QDesktopServices.openUrl(QUrl.fromLocalFile(path)):
|
|
260
|
+
return True
|
|
261
|
+
return Opener._open_with_cli_linux(path)
|
pygpt_net/core/filesystem/url.py
CHANGED
|
@@ -23,19 +23,12 @@ class Url:
|
|
|
23
23
|
|
|
24
24
|
def handle(self, url: QUrl):
|
|
25
25
|
"""
|
|
26
|
-
Handle URL
|
|
26
|
+
Handle URL, bridge action or local file
|
|
27
27
|
|
|
28
28
|
:param url: url
|
|
29
29
|
"""
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
'extra-code-copy',
|
|
33
|
-
'extra-copy',
|
|
34
|
-
'extra-delete',
|
|
35
|
-
'extra-edit',
|
|
36
|
-
'extra-join',
|
|
37
|
-
'extra-replay',
|
|
38
|
-
]
|
|
30
|
+
if not url.toString().strip():
|
|
31
|
+
return
|
|
39
32
|
|
|
40
33
|
# JS bridge
|
|
41
34
|
if url.toString().startswith('bridge://open_find'):
|
|
@@ -50,8 +43,18 @@ class Url:
|
|
|
50
43
|
pid = self.window.controller.ui.tabs.get_current_pid()
|
|
51
44
|
if pid in self.window.ui.nodes['output']:
|
|
52
45
|
self.window.ui.nodes['output'][pid].on_focus_js()
|
|
46
|
+
return
|
|
53
47
|
|
|
54
48
|
# -------------
|
|
49
|
+
extra_schemes = (
|
|
50
|
+
'extra-audio-read',
|
|
51
|
+
'extra-code-copy',
|
|
52
|
+
'extra-copy',
|
|
53
|
+
'extra-delete',
|
|
54
|
+
'extra-edit',
|
|
55
|
+
'extra-join',
|
|
56
|
+
'extra-replay'
|
|
57
|
+
)
|
|
55
58
|
|
|
56
59
|
# local file
|
|
57
60
|
if not url.scheme().startswith('http') and url.scheme() not in extra_schemes:
|
pygpt_net/core/idx/chat.py
CHANGED
|
@@ -656,7 +656,7 @@ class Chat:
|
|
|
656
656
|
"""
|
|
657
657
|
if model is None:
|
|
658
658
|
model = self.window.core.models.from_defaults()
|
|
659
|
-
llm, embed_model = self.window.core.idx.llm.get_service_context(model=model, stream=False)
|
|
659
|
+
llm, embed_model = self.window.core.idx.llm.get_service_context(model=model, stream=False, auto_embed=True)
|
|
660
660
|
index = self.storage.get_ctx_idx(path, llm, embed_model)
|
|
661
661
|
|
|
662
662
|
# 1. try to retrieve directly from index
|