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,615 @@
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 copy
13
+ import json
14
+ from typing import Optional, Union
15
+
16
+ from PySide6.QtWidgets import QApplication
17
+ from PySide6.QtGui import QStandardItem
18
+ from PySide6.QtCore import Qt, QTimer
19
+
20
+ from pygpt_net.item.store import RemoteStoreItem
21
+ from pygpt_net.utils import trans
22
+
23
+ from .batch import Batch
24
+
25
+
26
+ class GoogleRemoteStore:
27
+ def __init__(self, window=None):
28
+ """
29
+ Google File Search store editor controller
30
+
31
+ :param window: Window instance
32
+ """
33
+ self.window = window
34
+ self.batch = Batch(window)
35
+ self.dialog = False
36
+ self.config_initialized = False
37
+ self.current = None
38
+ self.width = 800
39
+ self.height = 500
40
+ self.id = "remote_store.google"
41
+ self.options = {
42
+ "id": {
43
+ "type": "text",
44
+ "label": "remote_store.id",
45
+ "read_only": True,
46
+ "value": "",
47
+ },
48
+ "name": {
49
+ "type": "text",
50
+ "label": "remote_store.name",
51
+ "value": "",
52
+ },
53
+ "status": {
54
+ "type": "textarea",
55
+ "label": "remote_store.status",
56
+ "read_only": True,
57
+ "value": "",
58
+ },
59
+ }
60
+ self._files_row_to_id = []
61
+
62
+ def get_options(self) -> dict:
63
+ return self.options
64
+
65
+ def get_option(self, key: str) -> Optional[dict]:
66
+ if key in self.options:
67
+ return self.options[key]
68
+
69
+ def setup(self):
70
+ """Set up store editor"""
71
+ idx = None
72
+ self.window.remote_store_google.setup(idx)
73
+
74
+ def toggle_editor(self):
75
+ """Toggle editor dialog"""
76
+ if self.dialog:
77
+ self.close()
78
+ else:
79
+ self.open()
80
+
81
+ def reset(self):
82
+ """Reset store editor"""
83
+ self.current = None
84
+ if self.dialog:
85
+ self.init()
86
+
87
+ def open(self, force: bool = False):
88
+ """
89
+ Open store editor dialog
90
+
91
+ :param force: force open dialog
92
+ """
93
+ if not self.config_initialized:
94
+ self.setup()
95
+ self.config_initialized = True
96
+ if not self.dialog or force:
97
+ self.current = self.window.controller.assistant.editor.get_selected_store_id()
98
+ self.init()
99
+ self.window.ui.dialogs.open(
100
+ "remote_store.google",
101
+ width=self.width,
102
+ height=self.height,
103
+ )
104
+ self.dialog = True
105
+
106
+ def close(self):
107
+ """Close editor dialog"""
108
+ if self.dialog:
109
+ self.window.ui.dialogs.close('remote_store.google')
110
+ self.dialog = False
111
+
112
+ def init(self):
113
+ """Initialize editor options"""
114
+ self.reload_items()
115
+
116
+ if self.current is None:
117
+ self.current = self.get_first_visible()
118
+
119
+ options = copy.deepcopy(self.get_options())
120
+ if self.current is not None and self.window.core.remote_store.google.has(self.current):
121
+ store = self.window.core.remote_store.google.items[self.current]
122
+ data_dict = store.to_dict()
123
+ for key in options:
124
+ if key in data_dict:
125
+ value = data_dict[key]
126
+ options[key]["value"] = value
127
+ if key == "status":
128
+ options[key]["value"] = json.dumps(value, indent=4)
129
+
130
+ self.set_tab_by_id(self.current)
131
+ self.window.controller.config.load_options(self.id, options)
132
+ else:
133
+ self.current = None
134
+ self.window.controller.config.load_options(self.id, options)
135
+
136
+ self.update_files_list()
137
+
138
+ def refresh_status(self):
139
+ """Reload store status"""
140
+ if self.current is not None:
141
+ if self.window.core.remote_store.google.has(self.current):
142
+ self.window.update_status(trans('status.sending'))
143
+ QApplication.processEvents()
144
+ store = self.window.core.remote_store.google.items[self.current]
145
+ self.refresh_store(store)
146
+ self.window.update_status(trans('status.finished'))
147
+ self.update()
148
+ self.update_files_list()
149
+
150
+ def refresh_store(self, store: RemoteStoreItem, update: bool = True):
151
+ """
152
+ Refresh store by item
153
+
154
+ :param store: store object
155
+ :param update: apply updates to current
156
+ """
157
+ self.window.core.remote_store.google.update_status(store.id)
158
+ self.window.core.remote_store.google.update(store)
159
+ if update and store.id == self.current:
160
+ self.update_current()
161
+
162
+ def refresh_by_idx(self, idx: Union[int, list]):
163
+ """
164
+ Refresh store by list index or list of indexes
165
+
166
+ :param idx: index or list
167
+ """
168
+ store_ids = []
169
+ ids = idx if isinstance(idx, list) else [idx]
170
+ for i in ids:
171
+ store_id = self.get_by_tab_idx(i)
172
+ if store_id is not None:
173
+ store_ids.append(store_id)
174
+ self.refresh_by_store_id(store_ids)
175
+
176
+ def refresh_by_store_id(self, store_id: Union[str, list]):
177
+ """
178
+ Refresh store by ID(s)
179
+
180
+ :param store_id: store name or list
181
+ """
182
+ ids = store_id if isinstance(store_id, list) else [store_id]
183
+ updated = False
184
+ is_current = False
185
+ for sid in ids:
186
+ if sid is not None and sid in self.window.core.remote_store.google.items:
187
+ store = self.window.core.remote_store.google.items[sid]
188
+ if store is not None:
189
+ self.window.update_status(trans('status.sending'))
190
+ QApplication.processEvents()
191
+ self.refresh_store(store)
192
+ updated = True
193
+ if self.current == sid:
194
+ is_current = True
195
+ if updated:
196
+ self.window.update_status(trans('status.finished'))
197
+ self.update()
198
+ if is_current:
199
+ self.update_files_list()
200
+
201
+ def update_current(self):
202
+ """Update current store values in the UI"""
203
+ if self.current is not None and self.window.core.remote_store.google.has(self.current):
204
+ store = self.window.core.remote_store.google.items[self.current]
205
+ option = copy.deepcopy(self.get_option("status"))
206
+ option["value"] = json.dumps(store.status, indent=4)
207
+ self.window.controller.config.apply(self.id, "status", option)
208
+
209
+ option = copy.deepcopy(self.get_option("name"))
210
+ option["value"] = store.name
211
+ self.window.controller.config.apply(self.id, "name", option)
212
+
213
+ def save_btn(self):
214
+ """Save editor and refresh"""
215
+ self.window.update_status("Saving...")
216
+ self.save()
217
+ self.refresh_status()
218
+ self.window.update_status("Saved.")
219
+
220
+ def save(self, persist: bool = True):
221
+ """
222
+ Save editor data to local store and (if possible) remote
223
+
224
+ :param persist: persist to file and close dialog
225
+ """
226
+ if self.current is not None:
227
+ current = self.window.core.remote_store.google.items[self.current].to_dict()
228
+ options = copy.deepcopy(self.get_options())
229
+ data_dict = {}
230
+ for key in options:
231
+ if key == "status":
232
+ data_dict[key] = current[key]
233
+ continue
234
+ value = self.window.controller.config.get_value(
235
+ parent_id="remote_store.google",
236
+ key=key,
237
+ option=options[key],
238
+ )
239
+ data_dict[key] = value
240
+ self.window.core.remote_store.google.items[self.current].from_dict(data_dict)
241
+
242
+ if persist:
243
+ self.window.update_status(trans('status.sending'))
244
+ QApplication.processEvents()
245
+ if self.current is not None:
246
+ # No remote patch endpoint; we save locally and fetch remote for status
247
+ store = self.window.core.remote_store.google.update(
248
+ self.window.core.remote_store.google.items[self.current]
249
+ )
250
+ if store is None:
251
+ self.window.update_status(trans('status.error'))
252
+ self.window.ui.dialogs.alert("Failed to save File Search store")
253
+ return
254
+
255
+ self.update()
256
+ self.window.update_status(trans("info.settings.saved"))
257
+ self.restore_selection()
258
+ self.update_files_list()
259
+
260
+ def reload_items(self):
261
+ """Reload list items"""
262
+ items = self.window.core.remote_store.google.items
263
+ self.window.remote_store_google.update_list("remote_store.google.list", items)
264
+ self.restore_selection()
265
+
266
+ def restore_selection(self):
267
+ """Restore selection"""
268
+ if self.current is not None:
269
+ idx = self.get_tab_by_id(self.current)
270
+ if idx is not None:
271
+ self.set_by_tab(idx)
272
+
273
+ def select(self, idx: int):
274
+ """Select store by index"""
275
+ self.save(persist=False)
276
+ self.current = self.get_by_tab_idx(idx)
277
+ self.init()
278
+ self.update_files_list()
279
+
280
+ def new(self, name = "", force: bool = False):
281
+ """
282
+ Create new File Search store
283
+
284
+ :param name: store name
285
+ :param force: force create without confirmation
286
+ """
287
+ if not force:
288
+ self.window.ui.dialog['create'].id = 'remote_store.google.new'
289
+ self.window.ui.dialog['create'].input.setText("New file search store")
290
+ self.window.ui.dialog['create'].current = "New file search store"
291
+ self.window.ui.dialog['create'].show()
292
+ return
293
+
294
+ self.window.ui.dialog['create'].close()
295
+ self.window.update_status(trans('status.sending'))
296
+ QApplication.processEvents()
297
+
298
+ store = self.window.core.remote_store.google.create(name)
299
+ if store is None:
300
+ self.window.update_status(trans('status.error'))
301
+ self.window.ui.dialogs.alert("Failed to create new File Search store")
302
+ return
303
+
304
+ self.window.update_status(trans('status.finished'))
305
+
306
+ self.window.core.remote_store.google.update(store)
307
+ self.update()
308
+
309
+ self.current = store.id
310
+ idx = self.get_tab_by_id(self.current)
311
+ self.set_by_tab(idx)
312
+ self.init()
313
+ self.restore_selection()
314
+ self.refresh_by_store_id(store.id)
315
+ self.update_files_list()
316
+
317
+ def delete_by_idx(self, idx: Union[int, list], force: bool = False):
318
+ """
319
+ Delete store(s) by index
320
+
321
+ :param idx: index or list of indexes
322
+ :param force: force confirm
323
+ """
324
+ store_ids = []
325
+ ids = idx if isinstance(idx, list) else [idx]
326
+ for i in ids:
327
+ store_id = self.get_by_tab_idx(i)
328
+ if store_id is not None:
329
+ store_ids.append(store_id)
330
+ self.delete(store_ids, force=force)
331
+
332
+ def delete(self, store_id: Optional[Union[str, list]] = None, force: bool = False):
333
+ """
334
+ Delete store(s) by ID
335
+
336
+ :param store_id: id or list
337
+ :param force: force confirm
338
+ """
339
+ if not force:
340
+ self.window.ui.dialogs.confirm(
341
+ type="remote_store.google.delete",
342
+ id=store_id,
343
+ msg=trans("dialog.remote_store.delete.confirm"),
344
+ )
345
+ return
346
+
347
+ if store_id is None:
348
+ self.window.ui.dialogs.alert("Please select File Search store first.")
349
+ return
350
+
351
+ self.window.update_status(trans('status.sending'))
352
+ updated = False
353
+ QApplication.processEvents()
354
+ ids = store_id if isinstance(store_id, list) else [store_id]
355
+ for sid in ids:
356
+ if self.current == sid:
357
+ self.current = None
358
+ try:
359
+ print("Deleting store: {}".format(sid))
360
+ if self.window.core.remote_store.google.delete(sid):
361
+ self.window.controller.assistant.batch.remove_store_from_assistants(sid)
362
+ self.window.update_status(trans('status.deleted'))
363
+ self.window.core.remote_store.google.save()
364
+ updated = True
365
+ else:
366
+ self.window.update_status(trans('status.error'))
367
+ except Exception as e:
368
+ self.window.update_status(trans('status.error'))
369
+ self.window.ui.dialogs.alert(e)
370
+ if updated:
371
+ self.window.controller.assistant.files.update()
372
+ self.update()
373
+ self.init()
374
+ self.restore_selection()
375
+ self.update_files_list()
376
+
377
+ def set_by_tab(self, idx: int):
378
+ """Set current by list index"""
379
+ store_idx = 0
380
+ for id in self.window.core.remote_store.google.get_ids():
381
+ if self.window.core.remote_store.google.is_hidden(id):
382
+ continue
383
+ if store_idx == idx:
384
+ self.current = id
385
+ break
386
+ store_idx += 1
387
+ current = self.window.ui.models['remote_store.google.list'].index(idx, 0)
388
+ self.window.ui.nodes['remote_store.google.list'].setCurrentIndex(current)
389
+
390
+ def set_tab_by_id(self, store_id: str):
391
+ """Set current list to id"""
392
+ idx = self.get_tab_idx(store_id)
393
+ current = self.window.ui.models['remote_store.google.list'].index(idx, 0)
394
+ self.window.ui.nodes['remote_store.google.list'].setCurrentIndex(current)
395
+
396
+ def get_tab_idx(self, store_id: str) -> int:
397
+ """Get list index by id"""
398
+ store_idx = None
399
+ i = 0
400
+ for id in self.window.core.remote_store.google.get_ids():
401
+ if self.window.core.remote_store.google.is_hidden(id):
402
+ continue
403
+ if id == store_id:
404
+ store_idx = i
405
+ break
406
+ i += 1
407
+ return store_idx
408
+
409
+ def get_tab_by_id(self, store_id: str) -> int:
410
+ """Get list index by id"""
411
+ idx = None
412
+ i = 0
413
+ for id in self.window.core.remote_store.google.get_ids():
414
+ if self.window.core.remote_store.google.is_hidden(id):
415
+ continue
416
+ if id == store_id:
417
+ idx = i
418
+ break
419
+ i += 1
420
+ return idx
421
+
422
+ def get_by_tab_idx(self, idx: int) -> Optional[str]:
423
+ """Get id by list index"""
424
+ store_idx = 0
425
+ for id in self.window.core.remote_store.google.get_ids():
426
+ if self.window.core.remote_store.google.is_hidden(id):
427
+ continue
428
+ if store_idx == idx:
429
+ return id
430
+ store_idx += 1
431
+ return None
432
+
433
+ def get_first_visible(self) -> Optional[str]:
434
+ """Get first visible store id"""
435
+ for id in self.window.core.remote_store.google.get_ids():
436
+ if not self.window.core.remote_store.google.is_hidden(id):
437
+ return id
438
+ return None
439
+
440
+ def open_by_idx(self, idx: int):
441
+ store = self.window.core.remote_store.google.get_by_idx(idx)
442
+ if store is None:
443
+ return
444
+ self.current = store
445
+ self.open(force=True)
446
+
447
+ def update(self):
448
+ """Update editor"""
449
+ self.reload_items()
450
+ self.window.controller.assistant.editor.update_store_list()
451
+ self.update_files_list()
452
+
453
+ def set_hide_thread(self, state: bool):
454
+ """Toggle show thread stores"""
455
+ self.window.core.config.set("remote_store.google.hide_threads", state)
456
+ self.update()
457
+
458
+ # ==================== Files (Documents) ====================
459
+
460
+ def update_files_list(self):
461
+ """
462
+ Update files list (documents) for the current store from local DB
463
+ """
464
+ model_id = 'remote_store.google.files.list'
465
+ if model_id not in self.window.ui.models:
466
+ return
467
+ model = self.window.ui.models[model_id]
468
+ try:
469
+ model.removeRows(0, model.rowCount())
470
+ except Exception:
471
+ pass
472
+
473
+ self._files_row_to_id = []
474
+
475
+ if self.current is None:
476
+ return
477
+
478
+ files_db = self.window.core.remote_store.google.files
479
+ if files_db is None:
480
+ return
481
+
482
+ try:
483
+ store_files = files_db.get_by_store_or_thread(self.current, None) or {}
484
+ except Exception as e:
485
+ self.window.core.debug.log(e)
486
+ store_files = {}
487
+
488
+ i = 0
489
+ for file_id, file_obj in store_files.items():
490
+ if isinstance(file_obj, dict):
491
+ data = file_obj
492
+ else:
493
+ data = {}
494
+ for key in ('id', 'file_id', 'name', 'filename', 'bytes', 'size', 'usage_bytes', 'status'):
495
+ try:
496
+ if hasattr(file_obj, key):
497
+ data[key] = getattr(file_obj, key)
498
+ except Exception:
499
+ pass
500
+ if not data and hasattr(file_obj, 'to_dict'):
501
+ try:
502
+ data = file_obj.to_dict()
503
+ except Exception:
504
+ data = {}
505
+
506
+ name = data.get('name') or data.get('filename') or file_id
507
+ size_val = data.get('size')
508
+
509
+ size_txt = ""
510
+ try:
511
+ if size_val:
512
+ size_txt = self.window.core.filesystem.sizeof_fmt(int(size_val))
513
+ except Exception:
514
+ pass
515
+
516
+ extra = []
517
+ if size_txt:
518
+ extra.append(size_txt)
519
+ label = name
520
+ if extra:
521
+ label += " ({})".format(", ".join(extra))
522
+
523
+ item = QStandardItem(label)
524
+ item.setEditable(False)
525
+ item.setData(file_id, Qt.UserRole)
526
+ model.setItem(i, 0, item)
527
+ self._files_row_to_id.append(data['file_id'] if 'file_id' in data else file_id)
528
+ i += 1
529
+
530
+ def delete_file_by_idx(self, idx: int, force: bool = False):
531
+ """
532
+ Delete a single document from the current store.
533
+
534
+ :param idx: row index
535
+ :param force: skip confirm
536
+ """
537
+ if self.current is None:
538
+ self.window.ui.dialogs.alert("Please select File Search store first.")
539
+ return
540
+
541
+ if not force:
542
+ self.window.ui.dialogs.confirm(
543
+ type='remote_store.google.file.delete',
544
+ id=idx,
545
+ msg=trans('confirm.remote_store.file.delete'),
546
+ )
547
+ return
548
+
549
+ model_id = 'remote_store.google.files.list'
550
+ if model_id not in self.window.ui.models:
551
+ return
552
+ if idx < 0 or idx >= len(self._files_row_to_id):
553
+ return
554
+
555
+ file_id = self._files_row_to_id[idx]
556
+ if not file_id:
557
+ return
558
+
559
+ self.window.update_status(trans('status.sending'))
560
+ QApplication.processEvents()
561
+
562
+ try:
563
+ api = self.window.core.api.google.store
564
+ removed = False
565
+
566
+ if hasattr(api, 'remove_store_file'):
567
+ try:
568
+ api.remove_store_file(self.current, file_id)
569
+ removed = True
570
+ except Exception as e:
571
+ self.window.core.debug.log(e)
572
+
573
+ if not removed and hasattr(api, 'delete_store_file'):
574
+ try:
575
+ api.delete_store_file(self.current, file_id)
576
+ removed = True
577
+ except Exception as e:
578
+ self.window.core.debug.log(e)
579
+
580
+ if not removed:
581
+ raise RuntimeError("Remove file API not available.")
582
+
583
+ try:
584
+ self.window.core.remote_store.google.files.delete_by_file_id(file_id)
585
+ except Exception as e:
586
+ self.window.core.debug.log(e)
587
+
588
+ try:
589
+ self.window.ui.models[model_id].removeRow(idx)
590
+ try:
591
+ del self._files_row_to_id[idx]
592
+ except Exception:
593
+ pass
594
+ except Exception:
595
+ pass
596
+
597
+ # Ensure the list panel is in sync immediately
598
+ try:
599
+ self.update_files_list()
600
+ except Exception:
601
+ pass
602
+
603
+ try:
604
+ self.window.update_status("Refreshing status...")
605
+ QTimer.singleShot(1000, lambda: self.window.controller.remote_store.google.refresh_status())
606
+ except Exception as e:
607
+ self.window.core.debug.log(e)
608
+
609
+ self.window.update_status(trans('status.deleted'))
610
+
611
+ except Exception as e:
612
+ self.window.update_status(trans('status.error'))
613
+ self.window.ui.dialogs.alert("Failed to delete file: {}".format(e))
614
+ self.window.core.debug.log(e)
615
+ self.update_files_list()
@@ -0,0 +1,12 @@
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
+ from .store import *