pygpt-net 2.7.6__py3-none-any.whl → 2.7.8__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 +13 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +5 -1
- pygpt_net/controller/assistant/batch.py +2 -2
- pygpt_net/controller/assistant/files.py +7 -6
- pygpt_net/controller/assistant/threads.py +0 -0
- pygpt_net/controller/chat/command.py +0 -0
- pygpt_net/controller/chat/remote_tools.py +3 -9
- pygpt_net/controller/chat/stream.py +2 -2
- pygpt_net/controller/chat/{handler/worker.py → stream_worker.py} +13 -35
- pygpt_net/controller/dialogs/confirm.py +35 -58
- pygpt_net/controller/lang/mapping.py +9 -9
- pygpt_net/controller/remote_store/{google/batch.py → batch.py} +209 -252
- pygpt_net/controller/remote_store/remote_store.py +982 -13
- pygpt_net/core/command/command.py +0 -0
- pygpt_net/core/db/viewer.py +1 -1
- pygpt_net/core/debug/models.py +2 -2
- pygpt_net/core/realtime/worker.py +3 -1
- pygpt_net/{controller/remote_store/google → core/remote_store/anthropic}/__init__.py +0 -1
- pygpt_net/core/remote_store/anthropic/files.py +211 -0
- pygpt_net/core/remote_store/anthropic/store.py +208 -0
- pygpt_net/core/remote_store/openai/store.py +5 -4
- pygpt_net/core/remote_store/remote_store.py +5 -1
- pygpt_net/{controller/remote_store/openai → core/remote_store/xai}/__init__.py +0 -1
- pygpt_net/core/remote_store/xai/files.py +225 -0
- pygpt_net/core/remote_store/xai/store.py +219 -0
- pygpt_net/data/config/config.json +18 -5
- pygpt_net/data/config/models.json +193 -4
- pygpt_net/data/config/settings.json +179 -36
- pygpt_net/data/icons/folder_eye.svg +1 -0
- pygpt_net/data/icons/folder_eye_filled.svg +1 -0
- pygpt_net/data/icons/folder_open.svg +1 -0
- pygpt_net/data/icons/folder_open_filled.svg +1 -0
- pygpt_net/data/locale/locale.de.ini +6 -3
- pygpt_net/data/locale/locale.en.ini +46 -12
- pygpt_net/data/locale/locale.es.ini +6 -3
- pygpt_net/data/locale/locale.fr.ini +6 -3
- pygpt_net/data/locale/locale.it.ini +6 -3
- pygpt_net/data/locale/locale.pl.ini +7 -4
- pygpt_net/data/locale/locale.uk.ini +6 -3
- pygpt_net/data/locale/locale.zh.ini +6 -3
- pygpt_net/icons.qrc +4 -0
- pygpt_net/icons_rc.py +282 -138
- pygpt_net/plugin/cmd_mouse_control/worker.py +2 -1
- pygpt_net/plugin/cmd_mouse_control/worker_sandbox.py +2 -1
- pygpt_net/provider/api/anthropic/__init__.py +10 -3
- pygpt_net/provider/api/anthropic/chat.py +342 -11
- pygpt_net/provider/api/anthropic/computer.py +844 -0
- pygpt_net/provider/api/anthropic/remote_tools.py +172 -0
- pygpt_net/provider/api/anthropic/store.py +307 -0
- pygpt_net/{controller/chat/handler/anthropic_stream.py → provider/api/anthropic/stream.py} +99 -10
- pygpt_net/provider/api/anthropic/tools.py +32 -77
- pygpt_net/provider/api/anthropic/utils.py +30 -0
- pygpt_net/{controller/chat/handler → provider/api/anthropic/worker}/__init__.py +0 -0
- pygpt_net/provider/api/anthropic/worker/importer.py +278 -0
- pygpt_net/provider/api/google/chat.py +62 -9
- pygpt_net/provider/api/google/store.py +124 -3
- pygpt_net/{controller/chat/handler/google_stream.py → provider/api/google/stream.py} +92 -25
- pygpt_net/provider/api/google/utils.py +185 -0
- pygpt_net/provider/api/google/worker/importer.py +16 -28
- pygpt_net/provider/api/langchain/__init__.py +0 -0
- pygpt_net/{controller/chat/handler/langchain_stream.py → provider/api/langchain/stream.py} +1 -1
- pygpt_net/provider/api/llama_index/__init__.py +0 -0
- pygpt_net/{controller/chat/handler/llamaindex_stream.py → provider/api/llama_index/stream.py} +1 -1
- pygpt_net/provider/api/openai/assistants.py +2 -2
- pygpt_net/provider/api/openai/image.py +2 -2
- pygpt_net/provider/api/openai/store.py +4 -1
- pygpt_net/{controller/chat/handler/openai_stream.py → provider/api/openai/stream.py} +1 -1
- pygpt_net/provider/api/openai/utils.py +69 -3
- pygpt_net/provider/api/openai/worker/importer.py +19 -61
- pygpt_net/provider/api/openai/worker/importer_assistants.py +230 -0
- pygpt_net/provider/api/x_ai/__init__.py +138 -15
- pygpt_net/provider/api/x_ai/audio.py +43 -11
- pygpt_net/provider/api/x_ai/chat.py +92 -4
- pygpt_net/provider/api/x_ai/image.py +149 -47
- pygpt_net/provider/api/x_ai/realtime/__init__.py +12 -0
- pygpt_net/provider/api/x_ai/realtime/client.py +1825 -0
- pygpt_net/provider/api/x_ai/realtime/realtime.py +198 -0
- pygpt_net/provider/api/x_ai/{remote.py → remote_tools.py} +183 -70
- pygpt_net/provider/api/x_ai/responses.py +507 -0
- pygpt_net/provider/api/x_ai/store.py +610 -0
- pygpt_net/{controller/chat/handler/xai_stream.py → provider/api/x_ai/stream.py} +42 -10
- pygpt_net/provider/api/x_ai/tools.py +59 -8
- pygpt_net/{controller/chat/handler → provider/api/x_ai}/utils.py +1 -2
- pygpt_net/provider/api/x_ai/vision.py +1 -4
- pygpt_net/provider/api/x_ai/worker/importer.py +308 -0
- pygpt_net/provider/audio_input/xai_grok_voice.py +390 -0
- pygpt_net/provider/audio_output/xai_tts.py +325 -0
- pygpt_net/provider/core/config/patch.py +39 -3
- pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +2 -2
- pygpt_net/provider/core/model/patch.py +39 -1
- pygpt_net/tools/image_viewer/tool.py +334 -34
- pygpt_net/tools/image_viewer/ui/dialogs.py +319 -22
- pygpt_net/tools/text_editor/ui/dialogs.py +3 -2
- pygpt_net/tools/text_editor/ui/widgets.py +0 -0
- pygpt_net/ui/dialog/assistant.py +1 -1
- pygpt_net/ui/dialog/plugins.py +13 -5
- pygpt_net/ui/dialog/remote_store.py +552 -0
- pygpt_net/ui/dialogs.py +3 -5
- pygpt_net/ui/layout/ctx/ctx_list.py +58 -7
- pygpt_net/ui/menu/tools.py +6 -13
- pygpt_net/ui/widget/dialog/base.py +16 -5
- pygpt_net/ui/widget/dialog/{remote_store_google.py → remote_store.py} +10 -10
- pygpt_net/ui/widget/element/button.py +4 -4
- pygpt_net/ui/widget/image/display.py +2 -2
- pygpt_net/ui/widget/lists/context.py +2 -2
- pygpt_net/ui/widget/textarea/editor.py +0 -0
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/METADATA +15 -2
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/RECORD +107 -89
- pygpt_net/controller/remote_store/google/store.py +0 -615
- pygpt_net/controller/remote_store/openai/batch.py +0 -524
- pygpt_net/controller/remote_store/openai/store.py +0 -699
- pygpt_net/ui/dialog/remote_store_google.py +0 -539
- pygpt_net/ui/dialog/remote_store_openai.py +0 -539
- pygpt_net/ui/widget/dialog/remote_store_openai.py +0 -56
- pygpt_net/ui/widget/lists/remote_store_google.py +0 -248
- pygpt_net/ui/widget/lists/remote_store_openai.py +0 -317
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/LICENSE +0 -0
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/WHEEL +0 -0
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/entry_points.txt +0 -0
|
@@ -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:
|
|
9
|
+
# Updated Date: 2026.01.05 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import base64
|
|
@@ -177,7 +177,7 @@ class ImageWorker(QRunnable):
|
|
|
177
177
|
|
|
178
178
|
def _is_gpt_image_model(self, model_id: Optional[str] = None) -> bool:
|
|
179
179
|
mid = (model_id or self.model or "").lower()
|
|
180
|
-
return mid.startswith("gpt-image-1")
|
|
180
|
+
return mid.startswith("gpt-image-1") or mid.startswith("chatgpt-image")
|
|
181
181
|
|
|
182
182
|
def _is_dalle2(self, model_id: Optional[str] = None) -> bool:
|
|
183
183
|
mid = (model_id or self.model or "").lower()
|
|
@@ -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: 2026.01.
|
|
9
|
+
# Updated Date: 2026.01.05 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -14,6 +14,8 @@ from typing import Optional, List
|
|
|
14
14
|
|
|
15
15
|
from pygpt_net.item.store import RemoteStoreItem
|
|
16
16
|
|
|
17
|
+
from .worker.importer import Importer
|
|
18
|
+
|
|
17
19
|
|
|
18
20
|
class Store:
|
|
19
21
|
def __init__(self, window=None):
|
|
@@ -23,6 +25,7 @@ class Store:
|
|
|
23
25
|
:param window: Window instance
|
|
24
26
|
"""
|
|
25
27
|
self.window = window
|
|
28
|
+
self.importer = Importer(window)
|
|
26
29
|
|
|
27
30
|
def get_client(self):
|
|
28
31
|
"""
|
|
@@ -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:
|
|
9
|
+
# Updated Date: 2026.01.05 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import base64
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.05 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import re
|
|
13
|
-
|
|
13
|
+
from typing import Optional, Any
|
|
14
14
|
|
|
15
15
|
def sanitize_name(name: str) -> str:
|
|
16
16
|
"""
|
|
@@ -24,4 +24,70 @@ def sanitize_name(name: str) -> str:
|
|
|
24
24
|
# allowed characters: a-z, A-Z, 0-9, _, and -
|
|
25
25
|
name = name.strip().lower()
|
|
26
26
|
sanitized_name = re.sub(r'[^a-z0-9_-]', '_', name)
|
|
27
|
-
return sanitized_name[:64] # limit to 64 characters
|
|
27
|
+
return sanitized_name[:64] # limit to 64 characters
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def capture_openai_usage(state, u_obj: Any):
|
|
31
|
+
"""
|
|
32
|
+
Extract usage for OpenAI/xAI-compatible chunks.
|
|
33
|
+
|
|
34
|
+
:param state: Chat state
|
|
35
|
+
:param u_obj: Usage object/dict
|
|
36
|
+
"""
|
|
37
|
+
if not u_obj:
|
|
38
|
+
return
|
|
39
|
+
state.usage_vendor = "openai"
|
|
40
|
+
in_tok = as_int(safe_get(u_obj, "input_tokens")) or as_int(safe_get(u_obj, "prompt_tokens"))
|
|
41
|
+
out_tok = as_int(safe_get(u_obj, "output_tokens")) or as_int(safe_get(u_obj, "completion_tokens"))
|
|
42
|
+
total = as_int(safe_get(u_obj, "total_tokens"))
|
|
43
|
+
reasoning = (
|
|
44
|
+
as_int(safe_get(u_obj, "output_tokens_details.reasoning_tokens")) or
|
|
45
|
+
as_int(safe_get(u_obj, "completion_tokens_details.reasoning_tokens")) or
|
|
46
|
+
as_int(safe_get(u_obj, "reasoning_tokens")) or
|
|
47
|
+
0
|
|
48
|
+
)
|
|
49
|
+
out_with_reason = (out_tok or 0) + (reasoning or 0)
|
|
50
|
+
state.usage_payload = {"in": in_tok, "out": out_with_reason, "reasoning": reasoning or 0, "total": total}
|
|
51
|
+
|
|
52
|
+
def safe_get(obj: Any, path: str) -> Any:
|
|
53
|
+
"""
|
|
54
|
+
Dot-path getter for dicts and objects.
|
|
55
|
+
|
|
56
|
+
:param obj: Source object or dict
|
|
57
|
+
:param path: Dot-separated path, e.g. 'a.b.0.c'
|
|
58
|
+
:return: Value at path or None
|
|
59
|
+
"""
|
|
60
|
+
cur = obj
|
|
61
|
+
for seg in path.split("."):
|
|
62
|
+
if cur is None:
|
|
63
|
+
return None
|
|
64
|
+
if isinstance(cur, dict):
|
|
65
|
+
cur = cur.get(seg)
|
|
66
|
+
else:
|
|
67
|
+
if seg.isdigit() and isinstance(cur, (list, tuple)):
|
|
68
|
+
idx = int(seg)
|
|
69
|
+
if 0 <= idx < len(cur):
|
|
70
|
+
cur = cur[idx]
|
|
71
|
+
else:
|
|
72
|
+
return None
|
|
73
|
+
else:
|
|
74
|
+
cur = getattr(cur, seg, None)
|
|
75
|
+
return cur
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def as_int(val: Any) -> Optional[int]:
|
|
79
|
+
"""
|
|
80
|
+
Coerce to int if possible, else None.
|
|
81
|
+
|
|
82
|
+
:param val: Input value
|
|
83
|
+
:return: int or None
|
|
84
|
+
"""
|
|
85
|
+
if val is None:
|
|
86
|
+
return None
|
|
87
|
+
try:
|
|
88
|
+
return int(val)
|
|
89
|
+
except Exception:
|
|
90
|
+
try:
|
|
91
|
+
return int(float(val))
|
|
92
|
+
except Exception:
|
|
93
|
+
return None
|
|
@@ -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: 2026.01.
|
|
9
|
+
# Updated Date: 2026.01.05 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -33,20 +33,19 @@ class Importer(QObject):
|
|
|
33
33
|
:param mode: mode
|
|
34
34
|
:param err: error message
|
|
35
35
|
"""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
self.window.controller.remote_store.openai.batch.handle_imported_files_failed(err)
|
|
36
|
+
batch = self.window.controller.remote_store.batch
|
|
37
|
+
if mode == "import_files":
|
|
38
|
+
batch.handle_imported_files_failed(err)
|
|
40
39
|
elif mode == "truncate_files":
|
|
41
|
-
|
|
40
|
+
batch.handle_truncated_files_failed(err)
|
|
42
41
|
elif mode == "upload_files":
|
|
43
|
-
|
|
42
|
+
batch.handle_uploaded_files_failed(err)
|
|
44
43
|
elif mode in "vector_stores":
|
|
45
|
-
|
|
44
|
+
batch.handle_imported_stores_failed(err)
|
|
46
45
|
elif mode in "truncate_vector_stores":
|
|
47
|
-
|
|
46
|
+
batch.handle_truncated_stores_failed(err)
|
|
48
47
|
elif mode in "refresh_vector_stores":
|
|
49
|
-
|
|
48
|
+
batch.handle_refreshed_stores_failed(err)
|
|
50
49
|
|
|
51
50
|
@Slot(str, str, int)
|
|
52
51
|
def handle_finished(self, mode: str, store_id: str = None, num: int = 0):
|
|
@@ -57,20 +56,19 @@ class Importer(QObject):
|
|
|
57
56
|
:param store_id: store ID
|
|
58
57
|
:param num: number of affected items
|
|
59
58
|
"""
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
self.window.controller.remote_store.openai.batch.handle_imported_files(num)
|
|
59
|
+
batch = self.window.controller.remote_store.batch
|
|
60
|
+
if mode == "import_files":
|
|
61
|
+
batch.handle_imported_files(num)
|
|
64
62
|
elif mode == "truncate_files":
|
|
65
|
-
|
|
63
|
+
batch.handle_truncated_files(store_id, num)
|
|
66
64
|
elif mode == "upload_files":
|
|
67
|
-
|
|
65
|
+
batch.handle_uploaded_files(num)
|
|
68
66
|
elif mode == "vector_stores":
|
|
69
|
-
|
|
67
|
+
batch.handle_imported_stores(num)
|
|
70
68
|
elif mode == "truncate_vector_stores":
|
|
71
|
-
|
|
69
|
+
batch.handle_truncated_stores(num)
|
|
72
70
|
elif mode == "refresh_vector_stores":
|
|
73
|
-
|
|
71
|
+
batch.handle_refreshed_stores(num)
|
|
74
72
|
|
|
75
73
|
@Slot(str, str)
|
|
76
74
|
def handle_status(self, mode: str, msg: str):
|
|
@@ -92,14 +90,6 @@ class Importer(QObject):
|
|
|
92
90
|
"""
|
|
93
91
|
self.window.controller.assistant.threads.log(mode + ": " + msg)
|
|
94
92
|
|
|
95
|
-
def import_assistants(self):
|
|
96
|
-
"""Import assistants"""
|
|
97
|
-
self.worker = ImportWorker()
|
|
98
|
-
self.worker.window = self.window
|
|
99
|
-
self.worker.mode = "assistants"
|
|
100
|
-
self.connect_signals(self.worker)
|
|
101
|
-
self.window.threadpool.start(self.worker)
|
|
102
|
-
|
|
103
93
|
def import_vector_stores(self):
|
|
104
94
|
"""Import vector stores"""
|
|
105
95
|
self.worker = ImportWorker()
|
|
@@ -203,9 +193,7 @@ class ImportWorker(QRunnable):
|
|
|
203
193
|
"""Importer thread"""
|
|
204
194
|
try:
|
|
205
195
|
# import data
|
|
206
|
-
if self.mode == "
|
|
207
|
-
self.import_assistants()
|
|
208
|
-
elif self.mode == "vector_stores":
|
|
196
|
+
if self.mode == "vector_stores":
|
|
209
197
|
if self.import_vector_stores():
|
|
210
198
|
self.import_files()
|
|
211
199
|
elif self.mode == "truncate_vector_stores":
|
|
@@ -225,36 +213,6 @@ class ImportWorker(QRunnable):
|
|
|
225
213
|
finally:
|
|
226
214
|
self.cleanup()
|
|
227
215
|
|
|
228
|
-
def import_assistants(self, silent: bool = False) -> bool:
|
|
229
|
-
"""
|
|
230
|
-
Import assistants from API
|
|
231
|
-
|
|
232
|
-
:param silent: silent mode (no signals)
|
|
233
|
-
:return: result
|
|
234
|
-
"""
|
|
235
|
-
try:
|
|
236
|
-
# import assistants
|
|
237
|
-
self.log("Importing assistants...")
|
|
238
|
-
self.window.core.assistants.clear()
|
|
239
|
-
items = self.window.core.assistants.get_all()
|
|
240
|
-
self.window.core.api.openai.assistants.import_all(items, callback=self.callback)
|
|
241
|
-
self.window.core.assistants.items = items
|
|
242
|
-
self.window.core.assistants.save()
|
|
243
|
-
|
|
244
|
-
# import vector stores
|
|
245
|
-
self.import_vector_stores(True)
|
|
246
|
-
|
|
247
|
-
# import files
|
|
248
|
-
self.import_files(True)
|
|
249
|
-
|
|
250
|
-
if not silent:
|
|
251
|
-
self.signals.finished.emit("assistants", self.store_id, len(items))
|
|
252
|
-
return True
|
|
253
|
-
except Exception as e:
|
|
254
|
-
self.log("API error: {}".format(e))
|
|
255
|
-
self.signals.error.emit("assistants", e)
|
|
256
|
-
return False
|
|
257
|
-
|
|
258
216
|
def import_vector_stores(self, silent: bool = False) -> bool:
|
|
259
217
|
"""
|
|
260
218
|
Import vector stores from API
|
|
@@ -310,7 +268,7 @@ class ImportWorker(QRunnable):
|
|
|
310
268
|
for id in stores:
|
|
311
269
|
store = stores[id]
|
|
312
270
|
try:
|
|
313
|
-
self.window.controller.remote_store.
|
|
271
|
+
self.window.controller.remote_store.refresh_store(store, update=False, provider="openai")
|
|
314
272
|
num += 1
|
|
315
273
|
except Exception as e:
|
|
316
274
|
self.log("Failed to refresh store: {}".format(id))
|
|
@@ -0,0 +1,230 @@
|
|
|
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: 2026.01.05 17:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
from PySide6.QtCore import QObject, Signal, QRunnable, Slot
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Importer(QObject):
|
|
18
|
+
def __init__(self, window=None):
|
|
19
|
+
"""
|
|
20
|
+
Importer core
|
|
21
|
+
|
|
22
|
+
:param window: Window instance
|
|
23
|
+
"""
|
|
24
|
+
super(Importer, self).__init__()
|
|
25
|
+
self.window = window
|
|
26
|
+
self.worker = None
|
|
27
|
+
|
|
28
|
+
@Slot(str, object)
|
|
29
|
+
def handle_error(self, mode: str, err: any):
|
|
30
|
+
"""
|
|
31
|
+
Handle thread error signal
|
|
32
|
+
|
|
33
|
+
:param mode: mode
|
|
34
|
+
:param err: error message
|
|
35
|
+
"""
|
|
36
|
+
batch = self.window.controller.remote_store.batch
|
|
37
|
+
if mode == "assistants":
|
|
38
|
+
self.window.controller.assistant.batch.handle_imported_assistants_failed(err)
|
|
39
|
+
|
|
40
|
+
@Slot(str, str, int)
|
|
41
|
+
def handle_finished(self, mode: str, store_id: str = None, num: int = 0):
|
|
42
|
+
"""
|
|
43
|
+
Handle thread finished signal
|
|
44
|
+
|
|
45
|
+
:param mode: mode
|
|
46
|
+
:param store_id: store ID
|
|
47
|
+
:param num: number of affected items
|
|
48
|
+
"""
|
|
49
|
+
batch = self.window.controller.remote_store.batch
|
|
50
|
+
if mode == "assistants":
|
|
51
|
+
self.window.controller.assistant.batch.handle_imported_assistants(num)
|
|
52
|
+
|
|
53
|
+
@Slot(str, str)
|
|
54
|
+
def handle_status(self, mode: str, msg: str):
|
|
55
|
+
"""
|
|
56
|
+
Handle thread status change signal
|
|
57
|
+
|
|
58
|
+
:param mode: mode
|
|
59
|
+
:param msg: message
|
|
60
|
+
"""
|
|
61
|
+
self.window.controller.assistant.batch.handle_status_change(mode, msg)
|
|
62
|
+
|
|
63
|
+
@Slot(str, str)
|
|
64
|
+
def handle_log(self, mode: str, msg: str):
|
|
65
|
+
"""
|
|
66
|
+
Handle thread log message signal
|
|
67
|
+
|
|
68
|
+
:param mode: mode
|
|
69
|
+
:param msg: message
|
|
70
|
+
"""
|
|
71
|
+
self.window.controller.assistant.threads.log(mode + ": " + msg)
|
|
72
|
+
|
|
73
|
+
def import_assistants(self):
|
|
74
|
+
"""Import assistants"""
|
|
75
|
+
self.worker = ImportWorker()
|
|
76
|
+
self.worker.window = self.window
|
|
77
|
+
self.worker.mode = "assistants"
|
|
78
|
+
self.connect_signals(self.worker)
|
|
79
|
+
self.window.threadpool.start(self.worker)
|
|
80
|
+
|
|
81
|
+
def connect_signals(self, worker):
|
|
82
|
+
"""
|
|
83
|
+
Connect signals
|
|
84
|
+
|
|
85
|
+
:param worker: worker instance
|
|
86
|
+
"""
|
|
87
|
+
worker.signals.finished.connect(self.handle_finished)
|
|
88
|
+
worker.signals.error.connect(self.handle_error)
|
|
89
|
+
worker.signals.status.connect(self.handle_status)
|
|
90
|
+
worker.signals.log.connect(self.handle_log)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class ImportWorkerSignals(QObject):
|
|
94
|
+
"""Import worker signals"""
|
|
95
|
+
status = Signal(str, str) # mode, message
|
|
96
|
+
finished = Signal(str, str, int) # mode, store_id, num
|
|
97
|
+
error = Signal(str, object) # mode, error
|
|
98
|
+
log = Signal(str, str) # mode, message
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ImportWorker(QRunnable):
|
|
102
|
+
"""Import worker"""
|
|
103
|
+
def __init__(self, *args, **kwargs):
|
|
104
|
+
super().__init__()
|
|
105
|
+
self.signals = ImportWorkerSignals()
|
|
106
|
+
self.window = None
|
|
107
|
+
self.mode = "assistants"
|
|
108
|
+
self.assistant = None
|
|
109
|
+
self.store_id = None
|
|
110
|
+
self.files = []
|
|
111
|
+
|
|
112
|
+
@Slot()
|
|
113
|
+
def run(self):
|
|
114
|
+
"""Importer thread"""
|
|
115
|
+
try:
|
|
116
|
+
# import data
|
|
117
|
+
if self.mode == "assistants":
|
|
118
|
+
self.import_assistants()
|
|
119
|
+
|
|
120
|
+
except Exception as e:
|
|
121
|
+
self.signals.error.emit(self.mode, e)
|
|
122
|
+
|
|
123
|
+
finally:
|
|
124
|
+
self.cleanup()
|
|
125
|
+
|
|
126
|
+
def import_assistants(self, silent: bool = False) -> bool:
|
|
127
|
+
"""
|
|
128
|
+
Import assistants from API
|
|
129
|
+
|
|
130
|
+
:param silent: silent mode (no signals)
|
|
131
|
+
:return: result
|
|
132
|
+
"""
|
|
133
|
+
try:
|
|
134
|
+
# import assistants
|
|
135
|
+
self.log("Importing assistants...")
|
|
136
|
+
self.window.core.assistants.clear()
|
|
137
|
+
items = self.window.core.assistants.get_all()
|
|
138
|
+
self.window.core.api.openai.assistants.import_all(items, callback=self.callback)
|
|
139
|
+
self.window.core.assistants.items = items
|
|
140
|
+
self.window.core.assistants.save()
|
|
141
|
+
|
|
142
|
+
# import vector stores
|
|
143
|
+
self.import_vector_stores(True)
|
|
144
|
+
|
|
145
|
+
# import files
|
|
146
|
+
self.import_files(True)
|
|
147
|
+
|
|
148
|
+
if not silent:
|
|
149
|
+
self.signals.finished.emit("assistants", self.store_id, len(items))
|
|
150
|
+
return True
|
|
151
|
+
except Exception as e:
|
|
152
|
+
self.log("API error: {}".format(e))
|
|
153
|
+
self.signals.error.emit("assistants", e)
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
def import_vector_stores(self, silent: bool = False) -> bool:
|
|
157
|
+
"""
|
|
158
|
+
Import vector stores from API
|
|
159
|
+
|
|
160
|
+
:param silent: silent mode (no signals emit)
|
|
161
|
+
:return: result
|
|
162
|
+
"""
|
|
163
|
+
try:
|
|
164
|
+
self.log("Importing vector stores...")
|
|
165
|
+
self.window.core.remote_store.openai.clear()
|
|
166
|
+
items = {}
|
|
167
|
+
self.window.core.api.openai.store.import_stores(items, callback=self.callback)
|
|
168
|
+
self.window.core.remote_store.openai.import_items(items)
|
|
169
|
+
if not silent:
|
|
170
|
+
self.signals.finished.emit("vector_stores", self.store_id, len(items))
|
|
171
|
+
return True
|
|
172
|
+
except Exception as e:
|
|
173
|
+
self.log("API error: {}".format(e))
|
|
174
|
+
self.signals.error.emit("vector_stores", e)
|
|
175
|
+
return False
|
|
176
|
+
|
|
177
|
+
def import_files(self, silent: bool = False) -> bool:
|
|
178
|
+
"""
|
|
179
|
+
Import assistant files from API
|
|
180
|
+
|
|
181
|
+
:param silent: silent mode (no signals emit)
|
|
182
|
+
:return: result
|
|
183
|
+
"""
|
|
184
|
+
try:
|
|
185
|
+
if self.store_id is None:
|
|
186
|
+
self.log("Importing all files...")
|
|
187
|
+
self.window.core.remote_store.openai.files.truncate_local() # clear local DB (all)
|
|
188
|
+
num = self.window.core.api.openai.store.import_stores_files(self.callback) # import all files
|
|
189
|
+
else:
|
|
190
|
+
self.log("Importing files for store: {}".format(self.store_id))
|
|
191
|
+
self.window.core.remote_store.openai.files.truncate_local(self.store_id) # clear local DB (all)
|
|
192
|
+
items = self.window.core.api.openai.store.import_store_files(
|
|
193
|
+
self.store_id,
|
|
194
|
+
[],
|
|
195
|
+
callback=self.callback,
|
|
196
|
+
) # import store files
|
|
197
|
+
num = len(items)
|
|
198
|
+
if not silent:
|
|
199
|
+
self.signals.finished.emit("import_files", self.store_id, num)
|
|
200
|
+
return True
|
|
201
|
+
except Exception as e:
|
|
202
|
+
self.log("API error: {}".format(e))
|
|
203
|
+
self.signals.error.emit("import_files", e)
|
|
204
|
+
return False
|
|
205
|
+
|
|
206
|
+
def callback(self, msg: str):
|
|
207
|
+
"""
|
|
208
|
+
Log callback
|
|
209
|
+
|
|
210
|
+
:param msg: message
|
|
211
|
+
"""
|
|
212
|
+
self.log(msg)
|
|
213
|
+
|
|
214
|
+
def log(self, msg: str):
|
|
215
|
+
"""
|
|
216
|
+
Log message
|
|
217
|
+
|
|
218
|
+
:param msg: message
|
|
219
|
+
"""
|
|
220
|
+
self.signals.log.emit(self.mode, msg)
|
|
221
|
+
|
|
222
|
+
def cleanup(self):
|
|
223
|
+
"""Cleanup resources after worker execution."""
|
|
224
|
+
sig = self.signals
|
|
225
|
+
self.signals = None
|
|
226
|
+
if sig is not None:
|
|
227
|
+
try:
|
|
228
|
+
sig.deleteLater()
|
|
229
|
+
except RuntimeError:
|
|
230
|
+
pass
|