pygpt-net 2.4.30__py3-none-any.whl → 2.4.35__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.
- CHANGELOG.md +32 -0
- README.md +2105 -1892
- pygpt_net/CHANGELOG.txt +32 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/controller/access/__init__.py +5 -5
- pygpt_net/controller/access/control.py +3 -2
- pygpt_net/controller/attachment.py +68 -1
- pygpt_net/controller/audio/__init__.py +34 -6
- pygpt_net/controller/chat/__init__.py +3 -1
- pygpt_net/controller/chat/attachment.py +263 -38
- pygpt_net/controller/chat/audio.py +99 -0
- pygpt_net/controller/chat/input.py +10 -3
- pygpt_net/controller/chat/output.py +4 -1
- pygpt_net/controller/chat/text.py +7 -3
- pygpt_net/controller/dialogs/confirm.py +17 -1
- pygpt_net/controller/lang/custom.py +3 -1
- pygpt_net/controller/mode.py +2 -1
- pygpt_net/controller/painter/capture.py +2 -2
- pygpt_net/controller/presets/editor.py +15 -2
- pygpt_net/controller/ui/__init__.py +4 -1
- pygpt_net/core/access/voice.py +2 -2
- pygpt_net/core/agents/legacy.py +3 -1
- pygpt_net/core/attachments/__init__.py +14 -9
- pygpt_net/core/attachments/context.py +226 -44
- pygpt_net/core/{audio.py → audio/__init__.py} +1 -1
- pygpt_net/core/audio/context.py +34 -0
- pygpt_net/core/bridge/context.py +29 -1
- pygpt_net/core/ctx/__init__.py +4 -1
- pygpt_net/core/db/__init__.py +4 -2
- pygpt_net/core/debug/attachments.py +3 -1
- pygpt_net/core/debug/context.py +5 -1
- pygpt_net/core/debug/presets.py +3 -1
- pygpt_net/core/events/event.py +2 -1
- pygpt_net/core/experts/__init__.py +3 -1
- pygpt_net/core/idx/chat.py +28 -6
- pygpt_net/core/idx/indexing.py +123 -15
- pygpt_net/core/modes.py +3 -1
- pygpt_net/core/presets.py +13 -2
- pygpt_net/core/render/markdown/pid.py +2 -1
- pygpt_net/core/render/plain/pid.py +2 -1
- pygpt_net/core/render/web/body.py +34 -12
- pygpt_net/core/render/web/pid.py +2 -1
- pygpt_net/core/render/web/renderer.py +8 -3
- pygpt_net/core/tokens.py +4 -2
- pygpt_net/core/types/mode.py +2 -1
- pygpt_net/data/config/config.json +7 -5
- pygpt_net/data/config/models.json +190 -5
- pygpt_net/data/config/modes.json +11 -5
- pygpt_net/data/config/presets/current.audio.json +34 -0
- pygpt_net/data/config/settings.json +15 -1
- pygpt_net/data/css/web.css +70 -0
- pygpt_net/data/css/web.dark.css +4 -1
- pygpt_net/data/css/web.light.css +1 -1
- pygpt_net/data/locale/locale.de.ini +27 -14
- pygpt_net/data/locale/locale.en.ini +63 -47
- pygpt_net/data/locale/locale.es.ini +27 -14
- pygpt_net/data/locale/locale.fr.ini +29 -16
- pygpt_net/data/locale/locale.it.ini +27 -14
- pygpt_net/data/locale/locale.pl.ini +31 -18
- pygpt_net/data/locale/locale.uk.ini +27 -14
- pygpt_net/data/locale/locale.zh.ini +34 -21
- pygpt_net/data/locale/plugin.cmd_files.de.ini +4 -4
- pygpt_net/data/locale/plugin.cmd_files.en.ini +4 -4
- pygpt_net/data/locale/plugin.cmd_files.es.ini +4 -4
- pygpt_net/data/locale/plugin.cmd_files.fr.ini +4 -4
- pygpt_net/data/locale/plugin.cmd_files.it.ini +4 -4
- pygpt_net/data/locale/plugin.cmd_files.pl.ini +4 -4
- pygpt_net/data/locale/plugin.cmd_files.uk.ini +4 -4
- pygpt_net/data/locale/plugin.cmd_files.zh.ini +4 -4
- pygpt_net/data/locale/plugin.cmd_web.de.ini +5 -5
- pygpt_net/data/locale/plugin.cmd_web.en.ini +5 -5
- pygpt_net/data/locale/plugin.cmd_web.es.ini +5 -5
- pygpt_net/data/locale/plugin.cmd_web.fr.ini +5 -5
- pygpt_net/data/locale/plugin.cmd_web.it.ini +5 -5
- pygpt_net/data/locale/plugin.cmd_web.pl.ini +5 -5
- pygpt_net/data/locale/plugin.cmd_web.uk.ini +5 -5
- pygpt_net/data/locale/plugin.cmd_web.zh.ini +5 -5
- pygpt_net/data/locale/plugin.idx_llama_index.de.ini +12 -12
- pygpt_net/data/locale/plugin.idx_llama_index.en.ini +12 -12
- pygpt_net/data/locale/plugin.idx_llama_index.es.ini +12 -12
- pygpt_net/data/locale/plugin.idx_llama_index.fr.ini +12 -12
- pygpt_net/data/locale/plugin.idx_llama_index.it.ini +12 -12
- pygpt_net/data/locale/plugin.idx_llama_index.pl.ini +12 -12
- pygpt_net/data/locale/plugin.idx_llama_index.uk.ini +12 -12
- pygpt_net/data/locale/plugin.idx_llama_index.zh.ini +12 -12
- pygpt_net/data/win32/USER-LICENSE.rtf +0 -0
- pygpt_net/data/win32/banner.bmp +0 -0
- pygpt_net/data/win32/banner_welcome.bmp +0 -0
- pygpt_net/item/attachment.py +9 -1
- pygpt_net/item/ctx.py +9 -1
- pygpt_net/item/preset.py +5 -1
- pygpt_net/launcher.py +3 -1
- pygpt_net/migrations/Version20241126170000.py +28 -0
- pygpt_net/migrations/__init__.py +3 -1
- pygpt_net/plugin/audio_input/__init__.py +11 -1
- pygpt_net/plugin/audio_input/worker.py +9 -1
- pygpt_net/plugin/audio_output/__init__.py +37 -7
- pygpt_net/plugin/audio_output/worker.py +38 -41
- pygpt_net/plugin/cmd_code_interpreter/runner.py +2 -2
- pygpt_net/plugin/cmd_mouse_control/__init__.py +4 -2
- pygpt_net/plugin/openai_dalle/__init__.py +3 -1
- pygpt_net/plugin/openai_vision/__init__.py +3 -1
- pygpt_net/provider/core/attachment/json_file.py +4 -1
- pygpt_net/provider/core/config/patch.py +22 -0
- pygpt_net/provider/core/ctx/db_sqlite/storage.py +14 -4
- pygpt_net/provider/core/ctx/db_sqlite/utils.py +19 -2
- pygpt_net/provider/core/model/patch.py +7 -1
- pygpt_net/provider/core/preset/json_file.py +5 -1
- pygpt_net/provider/gpt/__init__.py +14 -2
- pygpt_net/provider/gpt/audio.py +63 -0
- pygpt_net/provider/gpt/chat.py +76 -44
- pygpt_net/provider/gpt/utils.py +27 -0
- pygpt_net/provider/gpt/vision.py +37 -15
- pygpt_net/provider/loaders/base.py +10 -1
- pygpt_net/provider/loaders/web_yt.py +19 -1
- pygpt_net/tools/image_viewer/ui/dialogs.py +3 -1
- pygpt_net/ui/dialog/about.py +1 -1
- pygpt_net/ui/dialog/preset.py +3 -1
- pygpt_net/ui/dialog/url.py +29 -0
- pygpt_net/ui/dialogs.py +5 -1
- pygpt_net/ui/layout/chat/attachments.py +42 -6
- pygpt_net/ui/layout/chat/attachments_ctx.py +14 -4
- pygpt_net/ui/layout/chat/attachments_uploaded.py +8 -4
- pygpt_net/ui/widget/anims/toggles.py +2 -2
- pygpt_net/ui/widget/dialog/url.py +59 -0
- pygpt_net/ui/widget/lists/attachment.py +22 -17
- pygpt_net/ui/widget/lists/attachment_ctx.py +65 -3
- pygpt_net/ui/widget/option/checkbox.py +1 -3
- pygpt_net/ui/widget/option/toggle.py +1 -0
- pygpt_net/ui/widget/textarea/url.py +43 -0
- {pygpt_net-2.4.30.dist-info → pygpt_net-2.4.35.dist-info}/METADATA +2107 -1894
- {pygpt_net-2.4.30.dist-info → pygpt_net-2.4.35.dist-info}/RECORD +135 -124
- {pygpt_net-2.4.30.dist-info → pygpt_net-2.4.35.dist-info}/LICENSE +0 -0
- {pygpt_net-2.4.30.dist-info → pygpt_net-2.4.35.dist-info}/WHEEL +0 -0
- {pygpt_net-2.4.30.dist-info → pygpt_net-2.4.35.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
@@ -1,3 +1,35 @@
|
|
1
|
+
2.4.35 (2024-11-28)
|
2
|
+
|
3
|
+
- Docker removed from dependencies in Snap version #82
|
4
|
+
- Refactored documentation.
|
5
|
+
- Fix: toggles real-time update hook.
|
6
|
+
- Fix: missing edit icons.
|
7
|
+
- Added tokens from attachments to counters if mode == Full context.
|
8
|
+
|
9
|
+
2.4.34 (2024-11-26)
|
10
|
+
|
11
|
+
- Added a new mode: Chat with Audio, with built-in multimodal support for audio input/output. Currently in beta, the execution of commands and tools in this mode is temporarily unavailable.
|
12
|
+
- Added new models: gpt-4o-audio-preview, gpt-4o-2024-11-20, chatgpt-4o-latest.
|
13
|
+
- Force disabled integration with the native system menu.
|
14
|
+
|
15
|
+
2.4.33 (2024-11-26)
|
16
|
+
|
17
|
+
- Improved CSS and rendering of file and image lists.
|
18
|
+
- Added displaying of used attachments in the chat window.
|
19
|
+
|
20
|
+
2.4.32 (2024-11-26)
|
21
|
+
|
22
|
+
- The "Add URL" option added to the "Attachments" tab allows users to include content from a given website as additional context. Currently, it only supports standard web pages and video transcription for YouTube links. More "web" options will be added in the future.
|
23
|
+
- Added UTF-8 as default in attachments content text read/write.
|
24
|
+
|
25
|
+
2.4.31 (2024-11-25)
|
26
|
+
|
27
|
+
- Added an option checkbox `Auto-index on upload` in the `Attachments` tab:
|
28
|
+
|
29
|
+
Tip: To use the `Query only` mode, the file must be indexed in the vector database. This occurs automatically at the time of upload if the `Auto-index on upload` option in the `Attachments` tab is enabled. When uploading large files, such indexing might take a while - therefore, if you are using the `Full context` option, which does not use the index, you can disable the `Auto-index` option to speed up the upload of the attachment. In this case, it will only be indexed when the `Query only` option is called for the first time, and until then, attachment will be available in the form of `Full context` and `Summary`.
|
30
|
+
|
31
|
+
- Added context menu options in `Uploaded attachments` tab: `Open`, `Open Source directory` and `Open Storage directory`.
|
32
|
+
|
1
33
|
2.4.30 (2024-11-25)
|
2
34
|
|
3
35
|
- Added instruction to model about mapped data directory in both legacy and IPython code interepreter.
|
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: 2024.11.
|
9
|
+
# Updated Date: 2024.11.28 01:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
__author__ = "Marcin Szczygliński"
|
13
13
|
__copyright__ = "Copyright 2024, Marcin Szczygliński"
|
14
14
|
__credits__ = ["Marcin Szczygliński"]
|
15
15
|
__license__ = "MIT"
|
16
|
-
__version__ = "2.4.
|
17
|
-
__build__ = "2024.11.
|
16
|
+
__version__ = "2.4.35"
|
17
|
+
__build__ = "2024.11.28"
|
18
18
|
__maintainer__ = "Marcin Szczygliński"
|
19
19
|
__github__ = "https://github.com/szczyglis-dev/py-gpt"
|
20
20
|
__website__ = "https://pygpt.net"
|
@@ -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: 2024.11.
|
9
|
+
# Updated Date: 2024.11.26 19:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
from pygpt_net.core.events import BaseEvent, ControlEvent, AppEvent
|
@@ -73,9 +73,9 @@ class Access:
|
|
73
73
|
self.window.core.plugins.get("audio_input").handler_simple.stop_recording(timeout=True)
|
74
74
|
|
75
75
|
# stop audio output if playing
|
76
|
-
if self.window.controller.audio.is_playing():
|
77
|
-
|
76
|
+
#if self.window.controller.audio.is_playing():
|
77
|
+
self.window.controller.audio.stop_output()
|
78
78
|
|
79
79
|
# stop generating if active
|
80
|
-
if self.window.controller.chat.input.generating:
|
81
|
-
|
80
|
+
#if self.window.controller.chat.input.generating:
|
81
|
+
self.window.controller.kernel.stop()
|
@@ -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: 2024.11.
|
9
|
+
# Updated Date: 2024.11.26 19:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import re
|
@@ -22,7 +22,8 @@ from pygpt_net.core.types import (
|
|
22
22
|
MODE_EXPERT,
|
23
23
|
MODE_LANGCHAIN,
|
24
24
|
MODE_LLAMA_INDEX,
|
25
|
-
MODE_VISION,
|
25
|
+
MODE_VISION,
|
26
|
+
MODE_IMAGE,
|
26
27
|
)
|
27
28
|
from pygpt_net.core.tabs.tab import Tab
|
28
29
|
from pygpt_net.core.events import ControlEvent
|
@@ -6,11 +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: 2024.11.
|
9
|
+
# Updated Date: 2024.11.26 02:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import os
|
13
13
|
from datetime import datetime
|
14
|
+
from urllib.parse import urlparse
|
14
15
|
|
15
16
|
from PySide6.QtGui import QImage
|
16
17
|
from PySide6.QtWidgets import QFileDialog, QApplication
|
@@ -51,6 +52,13 @@ class Attachment:
|
|
51
52
|
else:
|
52
53
|
self.window.ui.nodes['attachments.capture_clear'].setChecked(False)
|
53
54
|
|
55
|
+
# auto-index
|
56
|
+
if self.window.core.config.has('attachments_auto_index') \
|
57
|
+
and self.window.core.config.get('attachments_auto_index'):
|
58
|
+
self.window.ui.nodes['attachments.auto_index'].setChecked(True)
|
59
|
+
else:
|
60
|
+
self.window.ui.nodes['attachments.auto_index'].setChecked(False)
|
61
|
+
|
54
62
|
self.window.core.attachments.load()
|
55
63
|
self.update()
|
56
64
|
|
@@ -224,6 +232,7 @@ class Attachment:
|
|
224
232
|
mode=mode,
|
225
233
|
remove_local=remove_local,
|
226
234
|
auto=auto,
|
235
|
+
force=force,
|
227
236
|
)
|
228
237
|
self.window.controller.chat.vision.unavailable() # set no content to provide
|
229
238
|
self.update()
|
@@ -268,6 +277,38 @@ class Attachment:
|
|
268
277
|
self.window.core.attachments.save()
|
269
278
|
self.update()
|
270
279
|
|
280
|
+
def open_add_url(self):
|
281
|
+
"""Open add attachment URL dialog"""
|
282
|
+
self.window.ui.dialog['url'].id = "attachment"
|
283
|
+
self.window.ui.dialog['url'].input.setText("")
|
284
|
+
self.window.ui.dialog['url'].current = ""
|
285
|
+
self.window.ui.dialog['url'].show()
|
286
|
+
self.window.ui.dialog['url'].input.setFocus()
|
287
|
+
|
288
|
+
def add_url(self, url: str):
|
289
|
+
"""
|
290
|
+
Add URL
|
291
|
+
|
292
|
+
:param url: URL
|
293
|
+
"""
|
294
|
+
if not url:
|
295
|
+
return
|
296
|
+
mode = self.window.core.config.get('mode')
|
297
|
+
try:
|
298
|
+
domain = urlparse(url).netloc
|
299
|
+
except Exception as e:
|
300
|
+
domain = os.path.basename(url)
|
301
|
+
attachment = self.window.core.attachments.new(
|
302
|
+
mode=mode,
|
303
|
+
name=domain,
|
304
|
+
path=url,
|
305
|
+
auto_save=False,
|
306
|
+
type=AttachmentItem.TYPE_URL,
|
307
|
+
)
|
308
|
+
self.window.core.attachments.save()
|
309
|
+
self.update()
|
310
|
+
self.window.ui.dialog['url'].close()
|
311
|
+
|
271
312
|
def open_dir(self, mode: str, idx: int):
|
272
313
|
"""
|
273
314
|
Open in directory
|
@@ -321,6 +362,24 @@ class Attachment:
|
|
321
362
|
return ''
|
322
363
|
return data.path
|
323
364
|
|
365
|
+
def get_by_idx(self, mode: str, idx: int) -> str:
|
366
|
+
"""
|
367
|
+
Get attachment by index
|
368
|
+
|
369
|
+
:param mode: mode
|
370
|
+
:param idx: index
|
371
|
+
:return: path
|
372
|
+
"""
|
373
|
+
file_id = self.window.core.attachments.get_id_by_idx(
|
374
|
+
mode=mode,
|
375
|
+
idx=idx,
|
376
|
+
)
|
377
|
+
data = self.window.core.attachments.get_by_id(
|
378
|
+
mode=mode,
|
379
|
+
id=file_id,
|
380
|
+
)
|
381
|
+
return data
|
382
|
+
|
324
383
|
def has(self, mode: str) -> bool:
|
325
384
|
"""
|
326
385
|
Return True if current mode has attachments
|
@@ -411,6 +470,14 @@ class Attachment:
|
|
411
470
|
"""
|
412
471
|
self.window.core.config.set('attachments_capture_clear', value)
|
413
472
|
|
473
|
+
def toggle_auto_index(self, value: bool):
|
474
|
+
"""
|
475
|
+
Toggle auto index
|
476
|
+
|
477
|
+
:param value: value of the checkbox
|
478
|
+
"""
|
479
|
+
self.window.core.config.set('attachments_auto_index', value)
|
480
|
+
|
414
481
|
def is_capture_clear(self) -> bool:
|
415
482
|
"""
|
416
483
|
Return True if capture clear is enabled
|
@@ -6,13 +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: 2024.11.
|
9
|
+
# Updated Date: 2024.11.26 19:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import os
|
13
13
|
|
14
14
|
from pygpt_net.core.events import Event, BaseEvent
|
15
|
-
from pygpt_net.plugin.audio_output.worker import PlayWorker
|
16
15
|
from pygpt_net.item.ctx import CtxItem
|
17
16
|
from pygpt_net.utils import trans
|
18
17
|
|
@@ -64,6 +63,12 @@ class Audio:
|
|
64
63
|
self.window.core.config.save()
|
65
64
|
self.update()
|
66
65
|
|
66
|
+
def enable_input(self):
|
67
|
+
"""Enable audio input"""
|
68
|
+
self.window.controller.plugins.enable('audio_input')
|
69
|
+
self.window.core.config.save()
|
70
|
+
self.update()
|
71
|
+
|
67
72
|
def disable_input(self, update: bool = True):
|
68
73
|
"""
|
69
74
|
Disable audio input
|
@@ -104,6 +109,16 @@ class Audio:
|
|
104
109
|
return True
|
105
110
|
return False
|
106
111
|
|
112
|
+
def is_input_enabled(self) -> bool:
|
113
|
+
"""
|
114
|
+
Check if any audio input is enabled
|
115
|
+
|
116
|
+
:return: True if enabled
|
117
|
+
"""
|
118
|
+
if self.window.controller.plugins.is_enabled('audio_input'):
|
119
|
+
return True
|
120
|
+
return False
|
121
|
+
|
107
122
|
def update_listeners(self):
|
108
123
|
"""Update audio listeners"""
|
109
124
|
is_output = False
|
@@ -161,16 +176,29 @@ class Audio:
|
|
161
176
|
}
|
162
177
|
self.window.dispatch(event, all=all)
|
163
178
|
|
179
|
+
def play_chat_audio(self, path: str):
|
180
|
+
"""
|
181
|
+
Play audio file (chat multimodal response)
|
182
|
+
|
183
|
+
:param path: audio file path
|
184
|
+
"""
|
185
|
+
if not self.is_output_enabled():
|
186
|
+
return
|
187
|
+
self.play_audio(path)
|
188
|
+
|
164
189
|
def play_audio(self, path: str):
|
165
190
|
"""
|
166
191
|
Play audio file
|
167
192
|
|
168
193
|
:param path: audio file path
|
169
194
|
"""
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
195
|
+
ctx = CtxItem()
|
196
|
+
event = Event(Event.AUDIO_PLAYBACK)
|
197
|
+
event.ctx = ctx
|
198
|
+
event.data = {
|
199
|
+
'audio_file': path,
|
200
|
+
}
|
201
|
+
self.window.dispatch(event, all=True)
|
174
202
|
|
175
203
|
def play_sound(self, filename: str):
|
176
204
|
"""
|
@@ -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: 2024.11.
|
9
|
+
# Updated Date: 2024.11.26 19:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
from pygpt_net.core.events import AppEvent
|
13
13
|
from pygpt_net.item.ctx import CtxItem
|
14
14
|
|
15
15
|
from .attachment import Attachment
|
16
|
+
from .audio import Audio
|
16
17
|
from .command import Command
|
17
18
|
from .common import Common
|
18
19
|
from .files import Files
|
@@ -35,6 +36,7 @@ class Chat:
|
|
35
36
|
"""
|
36
37
|
self.window = window
|
37
38
|
self.attachment = Attachment(window)
|
39
|
+
self.audio = Audio(window)
|
38
40
|
self.command = Command(window)
|
39
41
|
self.common = Common(window)
|
40
42
|
self.files = Files(window)
|
@@ -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: 2024.11.
|
9
|
+
# Updated Date: 2024.11.26 04:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import os
|
@@ -114,52 +114,123 @@ class Attachment(QObject):
|
|
114
114
|
:return: True if uploaded
|
115
115
|
"""
|
116
116
|
self.uploaded = False
|
117
|
+
auto_index = self.window.core.config.get("attachments_auto_index", False)
|
117
118
|
attachments = self.window.core.attachments.get_all(mode, only_files=True)
|
119
|
+
|
118
120
|
if self.is_verbose() and len(attachments) > 0:
|
119
121
|
print("\nUploading attachments...\nWork Mode: {}".format(mode))
|
122
|
+
|
120
123
|
for uuid in attachments:
|
121
124
|
attachment = attachments[uuid]
|
122
|
-
if
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
print("Uploading unpacked from archive: {}".format(path_relative))
|
140
|
-
item = self.window.core.attachments.context.upload(meta, sub_attachment, prompt)
|
141
|
-
if item:
|
142
|
-
item["path"] = os.path.basename(attachment.path) + "/" + path_relative
|
143
|
-
item["size"] = os.path.getsize(path)
|
144
|
-
if meta.additional_ctx is None:
|
145
|
-
meta.additional_ctx = []
|
146
|
-
meta.additional_ctx.append(item)
|
147
|
-
self.uploaded = True
|
148
|
-
sub_attachment.consumed = True
|
149
|
-
attachment.consumed = True
|
150
|
-
self.window.core.filesystem.packer.remove_tmp(tmp_path) # clean
|
151
|
-
else:
|
152
|
-
item = self.window.core.attachments.context.upload(meta, attachment, prompt)
|
153
|
-
if item:
|
154
|
-
if meta.additional_ctx is None:
|
155
|
-
meta.additional_ctx = []
|
156
|
-
meta.additional_ctx.append(item)
|
157
|
-
attachment.consumed = True # allow for deletion
|
125
|
+
if attachment.type == AttachmentItem.TYPE_FILE:
|
126
|
+
result = self.upload_file(
|
127
|
+
attachment=attachment,
|
128
|
+
meta=meta,
|
129
|
+
prompt=prompt,
|
130
|
+
auto_index=auto_index,
|
131
|
+
)
|
132
|
+
if result:
|
133
|
+
self.uploaded = True
|
134
|
+
elif attachment.type == AttachmentItem.TYPE_URL:
|
135
|
+
result = self.upload_web(
|
136
|
+
attachment=attachment,
|
137
|
+
meta=meta,
|
138
|
+
prompt=prompt,
|
139
|
+
auto_index=auto_index,
|
140
|
+
)
|
141
|
+
if result:
|
158
142
|
self.uploaded = True
|
159
143
|
if self.uploaded:
|
160
144
|
self.window.core.ctx.save(meta.id) # save meta
|
161
145
|
return self.uploaded
|
162
146
|
|
147
|
+
def upload_file(
|
148
|
+
self,
|
149
|
+
attachment: AttachmentItem,
|
150
|
+
meta: CtxMeta,
|
151
|
+
prompt: str,
|
152
|
+
auto_index: bool
|
153
|
+
) -> bool:
|
154
|
+
"""
|
155
|
+
Upload file attachment
|
156
|
+
|
157
|
+
:param attachment: AttachmentItem
|
158
|
+
:param meta: CtxMeta
|
159
|
+
:param prompt: User input prompt
|
160
|
+
:param auto_index: Auto index
|
161
|
+
:return: True if uploaded
|
162
|
+
"""
|
163
|
+
uploaded = False
|
164
|
+
if not self.is_allowed(attachment.path):
|
165
|
+
return False
|
166
|
+
if self.window.core.filesystem.packer.is_archive(attachment.path):
|
167
|
+
if self.is_verbose():
|
168
|
+
print("Unpacking archive: {}".format(attachment.path))
|
169
|
+
tmp_path = self.window.core.filesystem.packer.unpack(attachment.path)
|
170
|
+
if tmp_path:
|
171
|
+
for root, dirs, files in os.walk(tmp_path):
|
172
|
+
for file in files:
|
173
|
+
path = os.path.join(root, file)
|
174
|
+
sub_attachment = AttachmentItem()
|
175
|
+
sub_attachment.path = path
|
176
|
+
sub_attachment.name = os.path.basename(path)
|
177
|
+
sub_attachment.consumed = False
|
178
|
+
path_relative = os.path.relpath(path, tmp_path)
|
179
|
+
if self.is_allowed(str(path)):
|
180
|
+
if self.is_verbose():
|
181
|
+
print("Uploading unpacked from archive: {}".format(path_relative))
|
182
|
+
item = self.window.core.attachments.context.upload(
|
183
|
+
meta=meta,
|
184
|
+
attachment=sub_attachment,
|
185
|
+
prompt=prompt,
|
186
|
+
real_path=attachment.path,
|
187
|
+
auto_index=auto_index,
|
188
|
+
)
|
189
|
+
if item:
|
190
|
+
item["path"] = os.path.basename(attachment.path) + "/" + path_relative
|
191
|
+
item["size"] = os.path.getsize(path)
|
192
|
+
if meta.additional_ctx is None:
|
193
|
+
meta.additional_ctx = []
|
194
|
+
meta.additional_ctx.append(item)
|
195
|
+
uploaded = True
|
196
|
+
sub_attachment.consumed = True
|
197
|
+
attachment.consumed = True
|
198
|
+
self.window.core.filesystem.packer.remove_tmp(tmp_path) # clean
|
199
|
+
else:
|
200
|
+
item = self.window.core.attachments.context.upload(
|
201
|
+
meta=meta,
|
202
|
+
attachment=attachment,
|
203
|
+
prompt=prompt,
|
204
|
+
real_path=attachment.path,
|
205
|
+
auto_index=auto_index,
|
206
|
+
)
|
207
|
+
if item:
|
208
|
+
if meta.additional_ctx is None:
|
209
|
+
meta.additional_ctx = []
|
210
|
+
meta.additional_ctx.append(item)
|
211
|
+
attachment.consumed = True # allow for deletion
|
212
|
+
uploaded = True
|
213
|
+
|
214
|
+
return uploaded
|
215
|
+
|
216
|
+
def upload_web(
|
217
|
+
self,
|
218
|
+
attachment: AttachmentItem,
|
219
|
+
meta: CtxMeta,
|
220
|
+
prompt: str,
|
221
|
+
auto_index: bool
|
222
|
+
) -> bool:
|
223
|
+
"""
|
224
|
+
Upload web attachment
|
225
|
+
|
226
|
+
:param attachment: AttachmentItem
|
227
|
+
:param meta: CtxMeta
|
228
|
+
:param prompt: User input prompt
|
229
|
+
:param auto_index: Auto index
|
230
|
+
:return: True if uploaded
|
231
|
+
"""
|
232
|
+
return self.upload_file(attachment, meta, prompt, auto_index)
|
233
|
+
|
163
234
|
def has_context(self, meta: CtxMeta) -> bool:
|
164
235
|
"""
|
165
236
|
Check if has additional context for attachment
|
@@ -203,6 +274,7 @@ class Attachment(QObject):
|
|
203
274
|
if self.is_verbose():
|
204
275
|
print("\nPreparing additional context...\nContext Mode: {}".format(self.mode))
|
205
276
|
|
277
|
+
self.window.core.attachments.context.reset()
|
206
278
|
if self.mode == self.MODE_FULL_CONTEXT:
|
207
279
|
content = self.get_full_context(ctx)
|
208
280
|
elif self.mode == self.MODE_QUERY_CONTEXT:
|
@@ -210,6 +282,14 @@ class Attachment(QObject):
|
|
210
282
|
elif self.mode == self.MODE_QUERY_CONTEXT_SUMMARY:
|
211
283
|
content = self.get_context_summary(ctx)
|
212
284
|
|
285
|
+
# append used files and urls to context
|
286
|
+
files = self.window.core.attachments.context.get_used_files()
|
287
|
+
urls = self.window.core.attachments.context.get_used_urls()
|
288
|
+
if files:
|
289
|
+
ctx.files = files
|
290
|
+
if urls:
|
291
|
+
ctx.urls = urls
|
292
|
+
|
213
293
|
if content:
|
214
294
|
if self.is_verbose():
|
215
295
|
print("\n--- Using additional context ---\n\n{}".format(content))
|
@@ -362,6 +442,150 @@ class Attachment(QObject):
|
|
362
442
|
self.window.core.attachments.context.clear(meta, delete_files=remove_local)
|
363
443
|
self.update_list(meta)
|
364
444
|
|
445
|
+
def select(self, idx: int):
|
446
|
+
"""
|
447
|
+
Select uploaded file
|
448
|
+
|
449
|
+
:param idx: index of file
|
450
|
+
"""
|
451
|
+
pass
|
452
|
+
|
453
|
+
def open_by_idx(self, idx: int):
|
454
|
+
"""
|
455
|
+
Open attachment by index
|
456
|
+
|
457
|
+
:param idx: Index on list
|
458
|
+
"""
|
459
|
+
meta = self.window.core.ctx.get_current_meta()
|
460
|
+
if meta is None or meta.additional_ctx is None:
|
461
|
+
return
|
462
|
+
items = self.window.core.attachments.context.get_all(meta)
|
463
|
+
if idx < len(items):
|
464
|
+
item = items[idx]
|
465
|
+
path = item["path"]
|
466
|
+
if "real_path" in item:
|
467
|
+
path = item["real_path"]
|
468
|
+
if os.path.exists(path) and os.path.isfile(path):
|
469
|
+
print("Opening attachment: {}".format(path))
|
470
|
+
self.window.controller.files.open(path)
|
471
|
+
|
472
|
+
def open_dir_src_by_idx(self, idx: int):
|
473
|
+
"""
|
474
|
+
Open source directory by index
|
475
|
+
|
476
|
+
:param idx: Index on list
|
477
|
+
"""
|
478
|
+
meta = self.window.core.ctx.get_current_meta()
|
479
|
+
if meta is None or meta.additional_ctx is None:
|
480
|
+
return
|
481
|
+
items = self.window.core.attachments.context.get_all(meta)
|
482
|
+
if idx < len(items):
|
483
|
+
item = items[idx]
|
484
|
+
path = item["path"]
|
485
|
+
if "real_path" in item:
|
486
|
+
path = item["real_path"]
|
487
|
+
dir = os.path.dirname(path)
|
488
|
+
if os.path.exists(dir) and os.path.isdir(dir):
|
489
|
+
print("Opening source directory: {}".format(dir))
|
490
|
+
self.window.controller.files.open(dir)
|
491
|
+
|
492
|
+
def open_dir_dest_by_idx(self, idx: int):
|
493
|
+
"""
|
494
|
+
Open destination directory by index
|
495
|
+
|
496
|
+
:param idx: Index on list
|
497
|
+
"""
|
498
|
+
meta = self.window.core.ctx.get_current_meta()
|
499
|
+
if meta is None or meta.additional_ctx is None:
|
500
|
+
return
|
501
|
+
items = self.window.core.attachments.context.get_all(meta)
|
502
|
+
if idx < len(items):
|
503
|
+
item = items[idx]
|
504
|
+
root_dir = self.window.core.attachments.context.get_dir(meta)
|
505
|
+
dir = os.path.join(root_dir, item["uuid"])
|
506
|
+
if os.path.exists(dir) and os.path.isdir(dir):
|
507
|
+
self.window.controller.files.open(dir)
|
508
|
+
print("Opening destination directory: {}".format(dir))
|
509
|
+
|
510
|
+
def has_file_by_idx(self, idx: int) -> bool:
|
511
|
+
"""
|
512
|
+
Check if has file by index
|
513
|
+
|
514
|
+
:param idx: Index on list
|
515
|
+
:return: True if has file
|
516
|
+
"""
|
517
|
+
meta = self.window.core.ctx.get_current_meta()
|
518
|
+
if meta is None or meta.additional_ctx is None:
|
519
|
+
return False
|
520
|
+
items = self.window.core.attachments.context.get_all(meta)
|
521
|
+
if idx < len(items):
|
522
|
+
item = items[idx]
|
523
|
+
path = item["path"]
|
524
|
+
if "real_path" in item:
|
525
|
+
path = item["real_path"]
|
526
|
+
return os.path.exists(path) and os.path.isfile(path)
|
527
|
+
return False
|
528
|
+
|
529
|
+
def has_src_by_idx(self, idx: int) -> bool:
|
530
|
+
"""
|
531
|
+
Check if has source directory by index
|
532
|
+
|
533
|
+
:param idx: Index on list
|
534
|
+
:return: True if has source directory
|
535
|
+
"""
|
536
|
+
meta = self.window.core.ctx.get_current_meta()
|
537
|
+
if meta is None or meta.additional_ctx is None:
|
538
|
+
return False
|
539
|
+
items = self.window.core.attachments.context.get_all(meta)
|
540
|
+
if idx < len(items):
|
541
|
+
item = items[idx]
|
542
|
+
path = item["path"]
|
543
|
+
if "real_path" in item:
|
544
|
+
path = item["real_path"]
|
545
|
+
dir = os.path.dirname(path)
|
546
|
+
return os.path.exists(dir) and os.path.isdir(dir)
|
547
|
+
return False
|
548
|
+
|
549
|
+
def has_dest_by_idx(self, idx: int) -> bool:
|
550
|
+
"""
|
551
|
+
Check if has destination directory by index
|
552
|
+
|
553
|
+
:param idx: Index on list
|
554
|
+
:return: True if has destination directory
|
555
|
+
"""
|
556
|
+
meta = self.window.core.ctx.get_current_meta()
|
557
|
+
if meta is None or meta.additional_ctx is None:
|
558
|
+
return False
|
559
|
+
items = self.window.core.attachments.context.get_all(meta)
|
560
|
+
if idx < len(items):
|
561
|
+
item = items[idx]
|
562
|
+
root_dir = self.window.core.attachments.context.get_dir(meta)
|
563
|
+
dir = os.path.join(root_dir, item["uuid"])
|
564
|
+
return os.path.exists(dir) and os.path.isdir(dir)
|
565
|
+
return False
|
566
|
+
|
567
|
+
def get_current_tokens(self) -> int:
|
568
|
+
"""
|
569
|
+
Get current tokens
|
570
|
+
|
571
|
+
:return: Current attachments tokens
|
572
|
+
"""
|
573
|
+
if self.mode != self.MODE_FULL_CONTEXT:
|
574
|
+
return 0
|
575
|
+
meta = self.window.core.ctx.get_current_meta()
|
576
|
+
if meta is None:
|
577
|
+
return 0
|
578
|
+
if meta.additional_ctx is None:
|
579
|
+
return 0
|
580
|
+
tokens = 0
|
581
|
+
for item in meta.additional_ctx:
|
582
|
+
if "tokens" in item:
|
583
|
+
try:
|
584
|
+
tokens += int(item["tokens"])
|
585
|
+
except Exception as e:
|
586
|
+
pass
|
587
|
+
return tokens
|
588
|
+
|
365
589
|
@Slot(object)
|
366
590
|
def handle_upload_error(self, error: Exception):
|
367
591
|
"""
|
@@ -397,4 +621,5 @@ class Attachment(QObject):
|
|
397
621
|
"""
|
398
622
|
self.mode = mode
|
399
623
|
self.window.core.config.set("ctx.attachment.mode", mode)
|
400
|
-
self.window.core.config.save()
|
624
|
+
self.window.core.config.save()
|
625
|
+
self.window.controller.ui.update_tokens()
|