pygpt-net 2.7.4__py3-none-any.whl → 2.7.6__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.
Files changed (159) hide show
  1. pygpt_net/CHANGELOG.txt +15 -0
  2. pygpt_net/__init__.py +4 -4
  3. pygpt_net/app_core.py +4 -2
  4. pygpt_net/controller/__init__.py +5 -1
  5. pygpt_net/controller/assistant/assistant.py +1 -4
  6. pygpt_net/controller/assistant/batch.py +5 -504
  7. pygpt_net/controller/assistant/editor.py +5 -5
  8. pygpt_net/controller/assistant/files.py +16 -16
  9. pygpt_net/controller/chat/handler/google_stream.py +307 -1
  10. pygpt_net/controller/chat/handler/worker.py +10 -25
  11. pygpt_net/controller/chat/handler/xai_stream.py +621 -52
  12. pygpt_net/controller/chat/image.py +2 -2
  13. pygpt_net/controller/debug/fixtures.py +3 -2
  14. pygpt_net/controller/dialogs/confirm.py +73 -101
  15. pygpt_net/controller/files/files.py +65 -4
  16. pygpt_net/controller/lang/mapping.py +9 -9
  17. pygpt_net/controller/painter/capture.py +50 -1
  18. pygpt_net/controller/presets/presets.py +2 -1
  19. pygpt_net/controller/remote_store/__init__.py +12 -0
  20. pygpt_net/{provider/core/assistant_file/db_sqlite → controller/remote_store/google}/__init__.py +2 -2
  21. pygpt_net/controller/remote_store/google/batch.py +402 -0
  22. pygpt_net/controller/remote_store/google/store.py +615 -0
  23. pygpt_net/controller/remote_store/openai/__init__.py +12 -0
  24. pygpt_net/controller/remote_store/openai/batch.py +524 -0
  25. pygpt_net/controller/{assistant → remote_store/openai}/store.py +63 -60
  26. pygpt_net/controller/remote_store/remote_store.py +35 -0
  27. pygpt_net/controller/ui/ui.py +20 -1
  28. pygpt_net/core/assistants/assistants.py +3 -15
  29. pygpt_net/core/db/database.py +5 -3
  30. pygpt_net/core/filesystem/url.py +4 -1
  31. pygpt_net/core/locale/placeholder.py +35 -0
  32. pygpt_net/core/remote_store/__init__.py +12 -0
  33. pygpt_net/core/remote_store/google/__init__.py +11 -0
  34. pygpt_net/core/remote_store/google/files.py +224 -0
  35. pygpt_net/core/remote_store/google/store.py +248 -0
  36. pygpt_net/core/remote_store/openai/__init__.py +11 -0
  37. pygpt_net/core/{assistants → remote_store/openai}/files.py +26 -19
  38. pygpt_net/core/{assistants → remote_store/openai}/store.py +32 -15
  39. pygpt_net/core/remote_store/remote_store.py +24 -0
  40. pygpt_net/core/render/web/body.py +3 -2
  41. pygpt_net/core/types/chunk.py +27 -0
  42. pygpt_net/data/config/config.json +8 -4
  43. pygpt_net/data/config/models.json +77 -3
  44. pygpt_net/data/config/settings.json +45 -0
  45. pygpt_net/data/js/app/template.js +1 -1
  46. pygpt_net/data/js/app.min.js +2 -2
  47. pygpt_net/data/locale/locale.de.ini +44 -41
  48. pygpt_net/data/locale/locale.en.ini +56 -43
  49. pygpt_net/data/locale/locale.es.ini +44 -41
  50. pygpt_net/data/locale/locale.fr.ini +44 -41
  51. pygpt_net/data/locale/locale.it.ini +44 -41
  52. pygpt_net/data/locale/locale.pl.ini +45 -42
  53. pygpt_net/data/locale/locale.uk.ini +44 -41
  54. pygpt_net/data/locale/locale.zh.ini +44 -41
  55. pygpt_net/data/locale/plugin.cmd_history.de.ini +1 -1
  56. pygpt_net/data/locale/plugin.cmd_history.en.ini +1 -1
  57. pygpt_net/data/locale/plugin.cmd_history.es.ini +1 -1
  58. pygpt_net/data/locale/plugin.cmd_history.fr.ini +1 -1
  59. pygpt_net/data/locale/plugin.cmd_history.it.ini +1 -1
  60. pygpt_net/data/locale/plugin.cmd_history.pl.ini +1 -1
  61. pygpt_net/data/locale/plugin.cmd_history.uk.ini +1 -1
  62. pygpt_net/data/locale/plugin.cmd_history.zh.ini +1 -1
  63. pygpt_net/data/locale/plugin.cmd_mouse_control.en.ini +14 -0
  64. pygpt_net/data/locale/plugin.cmd_web.de.ini +1 -1
  65. pygpt_net/data/locale/plugin.cmd_web.en.ini +1 -1
  66. pygpt_net/data/locale/plugin.cmd_web.es.ini +1 -1
  67. pygpt_net/data/locale/plugin.cmd_web.fr.ini +1 -1
  68. pygpt_net/data/locale/plugin.cmd_web.it.ini +1 -1
  69. pygpt_net/data/locale/plugin.cmd_web.pl.ini +1 -1
  70. pygpt_net/data/locale/plugin.cmd_web.uk.ini +1 -1
  71. pygpt_net/data/locale/plugin.cmd_web.zh.ini +1 -1
  72. pygpt_net/data/locale/plugin.idx_llama_index.de.ini +2 -2
  73. pygpt_net/data/locale/plugin.idx_llama_index.en.ini +2 -2
  74. pygpt_net/data/locale/plugin.idx_llama_index.es.ini +2 -2
  75. pygpt_net/data/locale/plugin.idx_llama_index.fr.ini +2 -2
  76. pygpt_net/data/locale/plugin.idx_llama_index.it.ini +2 -2
  77. pygpt_net/data/locale/plugin.idx_llama_index.pl.ini +2 -2
  78. pygpt_net/data/locale/plugin.idx_llama_index.uk.ini +2 -2
  79. pygpt_net/data/locale/plugin.idx_llama_index.zh.ini +2 -2
  80. pygpt_net/item/assistant.py +1 -211
  81. pygpt_net/item/ctx.py +3 -3
  82. pygpt_net/item/store.py +238 -0
  83. pygpt_net/js_rc.py +2449 -2447
  84. pygpt_net/migrations/Version20260102190000.py +35 -0
  85. pygpt_net/migrations/__init__.py +3 -1
  86. pygpt_net/plugin/cmd_mouse_control/config.py +471 -1
  87. pygpt_net/plugin/cmd_mouse_control/plugin.py +487 -22
  88. pygpt_net/plugin/cmd_mouse_control/worker.py +464 -87
  89. pygpt_net/plugin/cmd_mouse_control/worker_sandbox.py +729 -0
  90. pygpt_net/plugin/idx_llama_index/config.py +2 -2
  91. pygpt_net/provider/api/anthropic/__init__.py +10 -8
  92. pygpt_net/provider/api/google/__init__.py +21 -58
  93. pygpt_net/provider/api/google/chat.py +545 -129
  94. pygpt_net/provider/api/google/computer.py +190 -0
  95. pygpt_net/provider/api/google/realtime/realtime.py +2 -2
  96. pygpt_net/provider/api/google/remote_tools.py +93 -0
  97. pygpt_net/provider/api/google/store.py +546 -0
  98. pygpt_net/provider/api/google/worker/__init__.py +0 -0
  99. pygpt_net/provider/api/google/worker/importer.py +392 -0
  100. pygpt_net/provider/api/openai/__init__.py +7 -3
  101. pygpt_net/provider/api/openai/computer.py +10 -1
  102. pygpt_net/provider/api/openai/responses.py +0 -0
  103. pygpt_net/provider/api/openai/store.py +6 -6
  104. pygpt_net/provider/api/openai/worker/importer.py +24 -24
  105. pygpt_net/provider/api/x_ai/__init__.py +10 -9
  106. pygpt_net/provider/api/x_ai/chat.py +272 -102
  107. pygpt_net/provider/core/config/patch.py +16 -1
  108. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +3 -3
  109. pygpt_net/provider/core/model/patch.py +17 -3
  110. pygpt_net/provider/core/preset/json_file.py +13 -7
  111. pygpt_net/provider/core/{assistant_file → remote_file}/__init__.py +1 -1
  112. pygpt_net/provider/core/{assistant_file → remote_file}/base.py +9 -9
  113. pygpt_net/provider/core/remote_file/db_sqlite/__init__.py +12 -0
  114. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/patch.py +1 -1
  115. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/provider.py +23 -20
  116. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/storage.py +35 -27
  117. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/utils.py +5 -4
  118. pygpt_net/provider/core/{assistant_store → remote_store}/__init__.py +1 -1
  119. pygpt_net/provider/core/{assistant_store → remote_store}/base.py +10 -10
  120. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/__init__.py +1 -1
  121. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/patch.py +1 -1
  122. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/provider.py +16 -15
  123. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/storage.py +30 -23
  124. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/utils.py +5 -4
  125. pygpt_net/provider/core/{assistant_store → remote_store}/json_file.py +9 -9
  126. pygpt_net/provider/llms/google.py +2 -2
  127. pygpt_net/tools/image_viewer/ui/dialogs.py +298 -12
  128. pygpt_net/tools/text_editor/ui/widgets.py +5 -1
  129. pygpt_net/ui/base/config_dialog.py +3 -2
  130. pygpt_net/ui/base/context_menu.py +44 -1
  131. pygpt_net/ui/dialog/assistant.py +3 -3
  132. pygpt_net/ui/dialog/plugins.py +3 -1
  133. pygpt_net/ui/dialog/remote_store_google.py +539 -0
  134. pygpt_net/ui/dialog/{assistant_store.py → remote_store_openai.py} +95 -95
  135. pygpt_net/ui/dialogs.py +5 -3
  136. pygpt_net/ui/layout/chat/attachments_uploaded.py +3 -3
  137. pygpt_net/ui/layout/toolbox/computer_env.py +26 -8
  138. pygpt_net/ui/layout/toolbox/indexes.py +22 -19
  139. pygpt_net/ui/layout/toolbox/model.py +28 -5
  140. pygpt_net/ui/menu/tools.py +13 -5
  141. pygpt_net/ui/widget/dialog/remote_store_google.py +56 -0
  142. pygpt_net/ui/widget/dialog/{assistant_store.py → remote_store_openai.py} +9 -9
  143. pygpt_net/ui/widget/element/button.py +4 -4
  144. pygpt_net/ui/widget/image/display.py +25 -8
  145. pygpt_net/ui/widget/lists/remote_store_google.py +248 -0
  146. pygpt_net/ui/widget/lists/{assistant_store.py → remote_store_openai.py} +21 -21
  147. pygpt_net/ui/widget/option/checkbox_list.py +47 -9
  148. pygpt_net/ui/widget/option/combo.py +39 -3
  149. pygpt_net/ui/widget/tabs/output.py +9 -1
  150. pygpt_net/ui/widget/textarea/editor.py +14 -1
  151. pygpt_net/ui/widget/textarea/input.py +20 -7
  152. pygpt_net/ui/widget/textarea/notepad.py +24 -1
  153. pygpt_net/ui/widget/textarea/output.py +23 -1
  154. pygpt_net/ui/widget/textarea/web.py +16 -1
  155. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/METADATA +41 -2
  156. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/RECORD +158 -132
  157. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/LICENSE +0 -0
  158. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/WHEEL +0 -0
  159. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,392 @@
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.02 20: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 (Google File Search)
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
+ if mode == "import_files":
37
+ self.window.controller.remote_store.google.batch.handle_imported_files_failed(err)
38
+ elif mode == "truncate_files":
39
+ self.window.controller.remote_store.google.batch.handle_truncated_files_failed(err)
40
+ elif mode == "upload_files":
41
+ self.window.controller.remote_store.google.batch.handle_uploaded_files_failed(err)
42
+ elif mode in "vector_stores":
43
+ self.window.controller.remote_store.google.batch.handle_imported_stores_failed(err)
44
+ elif mode in "truncate_vector_stores":
45
+ self.window.controller.remote_store.google.batch.handle_truncated_stores_failed(err)
46
+ elif mode in "refresh_vector_stores":
47
+ self.window.controller.remote_store.google.batch.handle_refreshed_stores_failed(err)
48
+
49
+ @Slot(str, str, int)
50
+ def handle_finished(self, mode: str, store_id: str = None, num: int = 0):
51
+ """
52
+ Handle thread finished signal
53
+
54
+ :param mode: mode
55
+ :param store_id: store ID
56
+ :param num: number of affected items
57
+ """
58
+ if mode == "import_files":
59
+ self.window.controller.remote_store.google.batch.handle_imported_files(num)
60
+ elif mode == "truncate_files":
61
+ self.window.controller.remote_store.google.batch.handle_truncated_files(store_id, num)
62
+ elif mode == "upload_files":
63
+ self.window.controller.remote_store.google.batch.handle_uploaded_files(num)
64
+ elif mode == "vector_stores":
65
+ self.window.controller.remote_store.google.batch.handle_imported_stores(num)
66
+ elif mode == "truncate_vector_stores":
67
+ self.window.controller.remote_store.google.batch.handle_truncated_stores(num)
68
+ elif mode == "refresh_vector_stores":
69
+ self.window.controller.remote_store.google.batch.handle_refreshed_stores(num)
70
+
71
+ @Slot(str, str)
72
+ def handle_status(self, mode: str, msg: str):
73
+ """
74
+ Handle thread status change signal
75
+
76
+ :param mode: mode
77
+ :param msg: message
78
+ """
79
+ self.window.controller.assistant.batch.handle_status_change(mode, msg)
80
+
81
+ @Slot(str, str)
82
+ def handle_log(self, mode: str, msg: str):
83
+ """
84
+ Handle thread log message signal
85
+
86
+ :param mode: mode
87
+ :param msg: message
88
+ """
89
+ self.window.controller.assistant.threads.log(mode + ": " + msg)
90
+
91
+ def import_assistants(self):
92
+ """Import assistants (kept for parity; no-op for Google if unused)"""
93
+ self.worker = ImportWorker()
94
+ self.worker.window = self.window
95
+ self.worker.mode = "assistants"
96
+ self.connect_signals(self.worker)
97
+ self.window.threadpool.start(self.worker)
98
+
99
+ def import_vector_stores(self):
100
+ """Import File Search stores"""
101
+ self.worker = ImportWorker()
102
+ self.worker.window = self.window
103
+ self.worker.mode = "vector_stores"
104
+ self.connect_signals(self.worker)
105
+ self.window.threadpool.start(self.worker)
106
+
107
+ def truncate_vector_stores(self):
108
+ """Truncate File Search stores"""
109
+ self.worker = ImportWorker()
110
+ self.worker.window = self.window
111
+ self.worker.mode = "truncate_vector_stores"
112
+ self.connect_signals(self.worker)
113
+ self.window.threadpool.start(self.worker)
114
+
115
+ def truncate_files(self, store_id: str = None):
116
+ """
117
+ Truncate documents
118
+
119
+ :param store_id: store name ('fileSearchStores/...').
120
+ """
121
+ self.worker = ImportWorker()
122
+ self.worker.window = self.window
123
+ self.worker.mode = "truncate_files"
124
+ self.worker.store_id = store_id
125
+ self.connect_signals(self.worker)
126
+ self.window.threadpool.start(self.worker)
127
+
128
+ def upload_files(self, store_id: str, files: list = None):
129
+ """
130
+ Upload files
131
+
132
+ :param store_id: store name ('fileSearchStores/...').
133
+ :param files: list of file paths
134
+ """
135
+ print("Uploading files: {}".format(files))
136
+ print("Store ID: {}".format(store_id))
137
+ self.worker = ImportWorker()
138
+ self.worker.window = self.window
139
+ self.worker.mode = "upload_files"
140
+ self.worker.store_id = store_id
141
+ self.worker.files = files or []
142
+ self.connect_signals(self.worker)
143
+ self.window.threadpool.start(self.worker)
144
+
145
+ def refresh_vector_stores(self):
146
+ """Refresh File Search stores"""
147
+ self.worker = ImportWorker()
148
+ self.worker.window = self.window
149
+ self.worker.mode = "refresh_vector_stores"
150
+ self.connect_signals(self.worker)
151
+ self.window.threadpool.start(self.worker)
152
+
153
+ def import_files(self, store_id: str = None):
154
+ """
155
+ Import File Search documents
156
+
157
+ :param store_id: store name ('fileSearchStores/...').
158
+ """
159
+ self.worker = ImportWorker()
160
+ self.worker.window = self.window
161
+ self.worker.mode = "import_files"
162
+ self.worker.store_id = store_id
163
+ self.connect_signals(self.worker)
164
+ self.window.threadpool.start(self.worker)
165
+
166
+ def connect_signals(self, worker):
167
+ """
168
+ Connect signals
169
+
170
+ :param worker: worker instance
171
+ """
172
+ worker.signals.finished.connect(self.handle_finished)
173
+ worker.signals.error.connect(self.handle_error)
174
+ worker.signals.status.connect(self.handle_status)
175
+ worker.signals.log.connect(self.handle_log)
176
+
177
+
178
+ class ImportWorkerSignals(QObject):
179
+ """Import worker signals"""
180
+ status = Signal(str, str) # mode, message
181
+ finished = Signal(str, str, int) # mode, store_id, num
182
+ error = Signal(str, object) # mode, error
183
+ log = Signal(str, str) # mode, message
184
+
185
+
186
+ class ImportWorker(QRunnable):
187
+ """Import worker (Google)"""
188
+ def __init__(self, *args, **kwargs):
189
+ super().__init__()
190
+ self.signals = ImportWorkerSignals()
191
+ self.window = None
192
+ self.mode = "assistants"
193
+ self.assistant = None
194
+ self.store_id = None
195
+ self.files = []
196
+
197
+ @Slot()
198
+ def run(self):
199
+ """Importer thread"""
200
+ try:
201
+ if self.mode == "vector_stores":
202
+ if self.import_vector_stores():
203
+ self.import_files()
204
+ elif self.mode == "truncate_vector_stores":
205
+ self.truncate_vector_stores()
206
+ elif self.mode == "refresh_vector_stores":
207
+ self.refresh_vector_stores()
208
+ elif self.mode == "truncate_files":
209
+ self.truncate_files()
210
+ elif self.mode == "import_files":
211
+ self.import_files()
212
+ elif self.mode == "upload_files":
213
+ self.upload_files()
214
+ except Exception as e:
215
+ self.signals.error.emit(self.mode, e)
216
+ finally:
217
+ self.cleanup()
218
+
219
+ def import_assistants(self, silent: bool = False) -> bool:
220
+ """
221
+ Import assistants (not used for Google by default; kept for parity)
222
+
223
+ :param silent: silent mode
224
+ """
225
+ try:
226
+ if not silent:
227
+ self.signals.finished.emit("assistants", self.store_id, 0)
228
+ return True
229
+ except Exception as e:
230
+ self.signals.error.emit("assistants", e)
231
+ return False
232
+
233
+ def import_vector_stores(self, silent: bool = False) -> bool:
234
+ """
235
+ Import File Search stores
236
+
237
+ :param silent: silent mode (no signals emit)
238
+ """
239
+ try:
240
+ self.log("Importing File Search stores...")
241
+ self.window.core.remote_store.google.clear()
242
+ items = {}
243
+ self.window.core.api.google.store.import_stores(items, callback=self.callback)
244
+ self.window.core.remote_store.google.import_items(items)
245
+ if not silent:
246
+ self.signals.finished.emit("vector_stores", self.store_id, len(items))
247
+ return True
248
+ except Exception as e:
249
+ self.log("API error: {}".format(e))
250
+ self.signals.error.emit("vector_stores", e)
251
+ return False
252
+
253
+ def truncate_vector_stores(self, silent: bool = False) -> bool:
254
+ """
255
+ Truncate all File Search stores in API
256
+
257
+ :param silent: silent mode
258
+ """
259
+ try:
260
+ self.log("Truncating stores...")
261
+ num = self.window.core.api.google.store.remove_all(callback=self.callback)
262
+ self.window.core.remote_store.google.items = {}
263
+ self.window.core.remote_store.google.save()
264
+ if not silent:
265
+ self.signals.finished.emit("truncate_vector_stores", self.store_id, num)
266
+ return True
267
+ except Exception as e:
268
+ self.log("API error: {}".format(e))
269
+ self.signals.error.emit("truncate_vector_stores", e)
270
+ return False
271
+
272
+ def refresh_vector_stores(self, silent: bool = False) -> bool:
273
+ """
274
+ Refresh all File Search stores in API
275
+
276
+ :param silent: silent mode
277
+ """
278
+ try:
279
+ self.log("Refreshing stores...")
280
+ num = 0
281
+ stores = self.window.core.remote_store.google.items
282
+ for id in stores:
283
+ store = stores[id]
284
+ try:
285
+ self.window.controller.remote_store.google.refresh_store(store, update=False)
286
+ num += 1
287
+ except Exception as e:
288
+ self.log("Failed to refresh store: {}".format(id))
289
+ self.window.core.debug.log(e)
290
+ if not silent:
291
+ self.signals.finished.emit("refresh_vector_stores", self.store_id, num)
292
+ return True
293
+ except Exception as e:
294
+ self.log("API error: {}".format(e))
295
+ self.signals.error.emit("refresh_vector_stores", e)
296
+ return False
297
+
298
+ def truncate_files(self, silent: bool = False) -> bool:
299
+ """
300
+ Truncate documents in API
301
+
302
+ :param silent: silent mode
303
+ """
304
+ try:
305
+ if self.store_id is None:
306
+ self.log("Truncating all documents in all stores...")
307
+ self.window.core.remote_store.google.files.truncate() # clear all locally and remote
308
+ num = self.window.core.api.google.store.remove_from_stores()
309
+ else:
310
+ self.log("Truncating documents for store: {}".format(self.store_id))
311
+ self.window.core.remote_store.google.files.truncate(self.store_id)
312
+ num = self.window.core.api.google.store.remove_from_store(self.store_id)
313
+ if not silent:
314
+ self.signals.finished.emit("truncate_files", self.store_id, num)
315
+ return True
316
+ except Exception as e:
317
+ self.log("API error: {}".format(e))
318
+ self.signals.error.emit("truncate_files", e)
319
+ return False
320
+
321
+ def upload_files(self, silent: bool = False) -> bool:
322
+ """
323
+ Upload files directly to a File Search store (creates Documents)
324
+
325
+ :param silent: silent mode
326
+ """
327
+ num = 0
328
+ try:
329
+ self.log("Uploading files to File Search store...")
330
+ for file in self.files:
331
+ try:
332
+ doc = self.window.core.api.google.store.upload_to_store(self.store_id, file)
333
+ if doc is not None:
334
+ self.window.core.remote_store.google.files.insert(self.store_id, doc)
335
+ msg = "Uploaded file: {}/{}".format((num + 1), len(self.files))
336
+ self.signals.status.emit("upload_files", msg)
337
+ self.log(msg)
338
+ num += 1
339
+ else:
340
+ self.signals.status.emit("upload_files", "Failed to upload file: {}".format(os.path.basename(file)))
341
+ except Exception as e:
342
+ self.window.core.debug.log(e)
343
+ self.signals.status.emit("upload_files", "Failed to upload file: {}".format(os.path.basename(file)))
344
+ if not silent:
345
+ self.signals.finished.emit("upload_files", self.store_id, num)
346
+ return True
347
+ except Exception as e:
348
+ self.log("API error: {}".format(e))
349
+ self.signals.error.emit("upload_files", e)
350
+ return False
351
+
352
+ def import_files(self, silent: bool = False) -> bool:
353
+ """
354
+ Import documents from API
355
+
356
+ :param silent: silent mode
357
+ """
358
+ try:
359
+ if self.store_id is None:
360
+ self.log("Importing all documents...")
361
+ self.window.core.remote_store.google.files.truncate_local() # clear local DB (all)
362
+ num = self.window.core.api.google.store.import_stores_files(self.callback) # import all
363
+ else:
364
+ self.log("Importing documents for store: {}".format(self.store_id))
365
+ self.window.core.remote_store.google.files.truncate_local(self.store_id)
366
+ items = self.window.core.api.google.store.import_store_files(self.store_id, [], callback=self.callback)
367
+ num = len(items)
368
+ if not silent:
369
+ self.signals.finished.emit("import_files", self.store_id, num)
370
+ return True
371
+ except Exception as e:
372
+ self.log("API error: {}".format(e))
373
+ self.signals.error.emit("import_files", e)
374
+ return False
375
+
376
+ def callback(self, msg: str):
377
+ """Log callback"""
378
+ self.log(msg)
379
+
380
+ def log(self, msg: str):
381
+ """Log message"""
382
+ self.signals.log.emit(self.mode, msg)
383
+
384
+ def cleanup(self):
385
+ """Cleanup resources after worker execution."""
386
+ sig = self.signals
387
+ self.signals = None
388
+ if sig is not None:
389
+ try:
390
+ sig.deleteLater()
391
+ except RuntimeError:
392
+ 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.12.28 00:00:00 #
9
+ # Updated Date: 2026.01.03 17:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from openai import OpenAI
@@ -22,6 +22,7 @@ from pygpt_net.core.types import (
22
22
  MODE_COMPUTER,
23
23
  )
24
24
  from pygpt_net.core.bridge.context import BridgeContext
25
+ from pygpt_net.core.types.chunk import ChunkType
25
26
  from pygpt_net.item.model import ModelItem
26
27
 
27
28
  from .audio import Audio
@@ -116,10 +117,12 @@ class ApiOpenAI:
116
117
  ctx = context.ctx
117
118
  ai_name = ctx.output_name
118
119
  thread_id = ctx.thread # from ctx
120
+ ctx.chunk_type = ChunkType.API_CHAT # default: ChatCompletion API
119
121
 
120
122
  # --- Responses API ----
121
123
  use_responses_api = self.responses.is_enabled(model, mode, parent_mode, is_expert_call, preset)
122
- ctx.use_responses_api = use_responses_api # set in context
124
+ if use_responses_api:
125
+ ctx.chunk_type = ChunkType.API_CHAT_RESPONSES # Responses API
123
126
 
124
127
  fixtures = self.window.controller.debug.fixtures
125
128
 
@@ -137,13 +140,14 @@ class ApiOpenAI:
137
140
 
138
141
  # completion
139
142
  if mode == MODE_COMPLETION:
143
+ ctx.chunk_type = ChunkType.API_COMPLETION
140
144
  response = self.completion.send(
141
145
  context=context,
142
146
  extra=extra,
143
147
  )
144
148
  used_tokens = self.completion.get_used_tokens()
145
149
 
146
- # chat, audio (OpenAI) | research (Perplexity)
150
+ # chat, audio (OpenAI) | research (deep research, Perplexity)
147
151
  elif mode in [
148
152
  MODE_CHAT,
149
153
  MODE_AUDIO,
@@ -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.07.26 18:00:00 #
9
+ # Updated Date: 2026.01.02 02:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import json
@@ -39,11 +39,20 @@ class Computer:
39
39
  Get Computer use tool
40
40
  :return: dict
41
41
  """
42
+ is_sandbox = bool(self.window.core.config.get("remote_tools.computer_use.sandbox", False))
42
43
  env = self.get_current_env()
43
44
  screen = self.window.app.primaryScreen()
44
45
  size = screen.size()
45
46
  screen_x = size.width()
46
47
  screen_y = size.height()
48
+
49
+ # if sandbox, get resolution from plugin settings (Playwright viewport)
50
+ if is_sandbox:
51
+ try:
52
+ screen_x = int(self.window.core.plugins.get_option("cmd_mouse_control", "sandbox_viewport_w"))
53
+ screen_y = int(self.window.core.plugins.get_option("cmd_mouse_control", "sandbox_viewport_h"))
54
+ except Exception:
55
+ pass
47
56
  return {
48
57
  "type": "computer_use_preview",
49
58
  "display_width": screen_x,
File without changes
@@ -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.09.02 22:00:00 #
9
+ # Updated Date: 2026.01.02 20:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
13
13
  from typing import Optional, List
14
14
 
15
- from pygpt_net.item.assistant import AssistantStoreItem
15
+ from pygpt_net.item.store import RemoteStoreItem
16
16
 
17
17
 
18
18
  class Store:
@@ -273,7 +273,7 @@ class Store:
273
273
  for remote in stores.data:
274
274
  id = remote.id
275
275
  if id not in items:
276
- items[id] = AssistantStoreItem()
276
+ items[id] = RemoteStoreItem()
277
277
  tmp_name = remote.name
278
278
  if tmp_name is None:
279
279
  items[id].is_thread = True # tmp store for thread
@@ -281,8 +281,8 @@ class Store:
281
281
  items[id].id = id
282
282
  items[id].name = tmp_name
283
283
  items[id].file_ids = []
284
- items[id].status = self.window.core.assistants.store.parse_status(remote)
285
- self.window.core.assistants.store.append_status(items[id], items[id].status)
284
+ items[id].status = self.window.core.remote_store.openai.parse_status(remote)
285
+ self.window.core.remote_store.openai.append_status(items[id], items[id].status)
286
286
  self.log("Imported vector store: " + id, callback)
287
287
  # next page
288
288
  if stores.has_more:
@@ -633,7 +633,7 @@ class Store:
633
633
  if id not in items:
634
634
  items.append(id)
635
635
  data = self.get_file(remote.id)
636
- self.window.core.assistants.files.insert(store_id, data) # add remote file to DB
636
+ self.window.core.remote_store.openai.files.insert(store_id, data) # add remote file to DB
637
637
  msg = "Imported file ID {} to store {}".format(remote.id, store_id)
638
638
  self.log(msg, callback)
639
639
  except Exception as e:
@@ -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.11 14:00:00 #
9
+ # Updated Date: 2026.01.02 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -36,17 +36,17 @@ class Importer(QObject):
36
36
  if mode == "assistants":
37
37
  self.window.controller.assistant.batch.handle_imported_assistants_failed(err)
38
38
  elif mode == "import_files":
39
- self.window.controller.assistant.batch.handle_imported_files_failed(err)
39
+ self.window.controller.remote_store.openai.batch.handle_imported_files_failed(err)
40
40
  elif mode == "truncate_files":
41
- self.window.controller.assistant.batch.handle_truncated_files_failed(err)
41
+ self.window.controller.remote_store.openai.batch.handle_truncated_files_failed(err)
42
42
  elif mode == "upload_files":
43
- self.window.controller.assistant.batch.handle_uploaded_files_failed(err)
43
+ self.window.controller.remote_store.openai.batch.handle_uploaded_files_failed(err)
44
44
  elif mode in "vector_stores":
45
- self.window.controller.assistant.batch.handle_imported_stores_failed(err)
45
+ self.window.controller.remote_store.openai.batch.handle_imported_stores_failed(err)
46
46
  elif mode in "truncate_vector_stores":
47
- self.window.controller.assistant.batch.handle_truncated_stores_failed(err)
47
+ self.window.controller.remote_store.openai.batch.handle_truncated_stores_failed(err)
48
48
  elif mode in "refresh_vector_stores":
49
- self.window.controller.assistant.batch.handle_refreshed_stores_failed(err)
49
+ self.window.controller.remote_store.openai.batch.handle_refreshed_stores_failed(err)
50
50
 
51
51
  @Slot(str, str, int)
52
52
  def handle_finished(self, mode: str, store_id: str = None, num: int = 0):
@@ -60,17 +60,17 @@ class Importer(QObject):
60
60
  if mode == "assistants":
61
61
  self.window.controller.assistant.batch.handle_imported_assistants(num)
62
62
  elif mode == "import_files":
63
- self.window.controller.assistant.batch.handle_imported_files(num)
63
+ self.window.controller.remote_store.openai.batch.handle_imported_files(num)
64
64
  elif mode == "truncate_files":
65
- self.window.controller.assistant.batch.handle_truncated_files(store_id, num)
65
+ self.window.controller.remote_store.openai.batch.handle_truncated_files(store_id, num)
66
66
  elif mode == "upload_files":
67
- self.window.controller.assistant.batch.handle_uploaded_files(num)
67
+ self.window.controller.remote_store.openai.batch.handle_uploaded_files(num)
68
68
  elif mode == "vector_stores":
69
- self.window.controller.assistant.batch.handle_imported_stores(num)
69
+ self.window.controller.remote_store.openai.batch.handle_imported_stores(num)
70
70
  elif mode == "truncate_vector_stores":
71
- self.window.controller.assistant.batch.handle_truncated_stores(num)
71
+ self.window.controller.remote_store.openai.batch.handle_truncated_stores(num)
72
72
  elif mode == "refresh_vector_stores":
73
- self.window.controller.assistant.batch.handle_refreshed_stores(num)
73
+ self.window.controller.remote_store.openai.batch.handle_refreshed_stores(num)
74
74
 
75
75
  @Slot(str, str)
76
76
  def handle_status(self, mode: str, msg: str):
@@ -264,10 +264,10 @@ class ImportWorker(QRunnable):
264
264
  """
265
265
  try:
266
266
  self.log("Importing vector stores...")
267
- self.window.core.assistants.store.clear()
267
+ self.window.core.remote_store.openai.clear()
268
268
  items = {}
269
269
  self.window.core.api.openai.store.import_stores(items, callback=self.callback)
270
- self.window.core.assistants.store.import_items(items)
270
+ self.window.core.remote_store.openai.import_items(items)
271
271
  if not silent:
272
272
  self.signals.finished.emit("vector_stores", self.store_id, len(items))
273
273
  return True
@@ -286,8 +286,8 @@ class ImportWorker(QRunnable):
286
286
  try:
287
287
  self.log("Truncating stores...")
288
288
  num = self.window.core.api.openai.store.remove_all(callback=self.callback)
289
- self.window.core.assistants.store.items = {}
290
- self.window.core.assistants.store.save()
289
+ self.window.core.remote_store.openai.items = {}
290
+ self.window.core.remote_store.openai.save()
291
291
  if not silent:
292
292
  self.signals.finished.emit("truncate_vector_stores", self.store_id, num)
293
293
  return True
@@ -306,11 +306,11 @@ class ImportWorker(QRunnable):
306
306
  try:
307
307
  self.log("Refreshing stores...")
308
308
  num = 0
309
- stores = self.window.core.assistants.store.items
309
+ stores = self.window.core.remote_store.openai.items
310
310
  for id in stores:
311
311
  store = stores[id]
312
312
  try:
313
- self.window.controller.assistant.store.refresh_store(store, update=False)
313
+ self.window.controller.remote_store.openai.refresh_store(store, update=False)
314
314
  num += 1
315
315
  except Exception as e:
316
316
  self.log("Failed to refresh store: {}".format(id))
@@ -334,12 +334,12 @@ class ImportWorker(QRunnable):
334
334
  # if empty store_id, truncate all files, otherwise truncate only store files
335
335
  if self.store_id is None:
336
336
  self.log("Truncating all files...")
337
- self.window.core.assistants.files.truncate() # clear all files
337
+ self.window.core.remote_store.openai.files.truncate() # clear all files
338
338
  # remove all files in API
339
339
  num = self.window.core.api.openai.store.remove_files(callback=self.callback)
340
340
  else:
341
341
  self.log("Truncating files for store: {}".format(self.store_id))
342
- self.window.core.assistants.files.truncate(self.store_id) # clear store files, remove from stores / DB
342
+ self.window.core.remote_store.openai.files.truncate(self.store_id) # clear store files, remove from stores / DB
343
343
  # remove store files in API
344
344
  num = self.window.core.api.openai.store.remove_store_files(
345
345
  self.store_id,
@@ -373,7 +373,7 @@ class ImportWorker(QRunnable):
373
373
  )
374
374
  if stored_file is not None:
375
375
  data = self.window.core.api.openai.store.get_file(file_id)
376
- self.window.core.assistants.files.insert(self.store_id, data) # insert to DB
376
+ self.window.core.remote_store.openai.files.insert(self.store_id, data) # insert to DB
377
377
  msg = "Uploaded file: {}/{}".format((num + 1), len(self.files))
378
378
  self.signals.status.emit("upload_files", msg)
379
379
  self.log(msg)
@@ -402,11 +402,11 @@ class ImportWorker(QRunnable):
402
402
  try:
403
403
  if self.store_id is None:
404
404
  self.log("Importing all files...")
405
- self.window.core.assistants.files.truncate_local() # clear local DB (all)
405
+ self.window.core.remote_store.openai.files.truncate_local() # clear local DB (all)
406
406
  num = self.window.core.api.openai.store.import_stores_files(self.callback) # import all files
407
407
  else:
408
408
  self.log("Importing files for store: {}".format(self.store_id))
409
- self.window.core.assistants.files.truncate_local(self.store_id) # clear local DB (all)
409
+ self.window.core.remote_store.openai.files.truncate_local(self.store_id) # clear local DB (all)
410
410
  items = self.window.core.api.openai.store.import_store_files(
411
411
  self.store_id,
412
412
  [],