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.
Files changed (120) hide show
  1. pygpt_net/CHANGELOG.txt +13 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +5 -1
  4. pygpt_net/controller/assistant/batch.py +2 -2
  5. pygpt_net/controller/assistant/files.py +7 -6
  6. pygpt_net/controller/assistant/threads.py +0 -0
  7. pygpt_net/controller/chat/command.py +0 -0
  8. pygpt_net/controller/chat/remote_tools.py +3 -9
  9. pygpt_net/controller/chat/stream.py +2 -2
  10. pygpt_net/controller/chat/{handler/worker.py → stream_worker.py} +13 -35
  11. pygpt_net/controller/dialogs/confirm.py +35 -58
  12. pygpt_net/controller/lang/mapping.py +9 -9
  13. pygpt_net/controller/remote_store/{google/batch.py → batch.py} +209 -252
  14. pygpt_net/controller/remote_store/remote_store.py +982 -13
  15. pygpt_net/core/command/command.py +0 -0
  16. pygpt_net/core/db/viewer.py +1 -1
  17. pygpt_net/core/debug/models.py +2 -2
  18. pygpt_net/core/realtime/worker.py +3 -1
  19. pygpt_net/{controller/remote_store/google → core/remote_store/anthropic}/__init__.py +0 -1
  20. pygpt_net/core/remote_store/anthropic/files.py +211 -0
  21. pygpt_net/core/remote_store/anthropic/store.py +208 -0
  22. pygpt_net/core/remote_store/openai/store.py +5 -4
  23. pygpt_net/core/remote_store/remote_store.py +5 -1
  24. pygpt_net/{controller/remote_store/openai → core/remote_store/xai}/__init__.py +0 -1
  25. pygpt_net/core/remote_store/xai/files.py +225 -0
  26. pygpt_net/core/remote_store/xai/store.py +219 -0
  27. pygpt_net/data/config/config.json +18 -5
  28. pygpt_net/data/config/models.json +193 -4
  29. pygpt_net/data/config/settings.json +179 -36
  30. pygpt_net/data/icons/folder_eye.svg +1 -0
  31. pygpt_net/data/icons/folder_eye_filled.svg +1 -0
  32. pygpt_net/data/icons/folder_open.svg +1 -0
  33. pygpt_net/data/icons/folder_open_filled.svg +1 -0
  34. pygpt_net/data/locale/locale.de.ini +6 -3
  35. pygpt_net/data/locale/locale.en.ini +46 -12
  36. pygpt_net/data/locale/locale.es.ini +6 -3
  37. pygpt_net/data/locale/locale.fr.ini +6 -3
  38. pygpt_net/data/locale/locale.it.ini +6 -3
  39. pygpt_net/data/locale/locale.pl.ini +7 -4
  40. pygpt_net/data/locale/locale.uk.ini +6 -3
  41. pygpt_net/data/locale/locale.zh.ini +6 -3
  42. pygpt_net/icons.qrc +4 -0
  43. pygpt_net/icons_rc.py +282 -138
  44. pygpt_net/plugin/cmd_mouse_control/worker.py +2 -1
  45. pygpt_net/plugin/cmd_mouse_control/worker_sandbox.py +2 -1
  46. pygpt_net/provider/api/anthropic/__init__.py +10 -3
  47. pygpt_net/provider/api/anthropic/chat.py +342 -11
  48. pygpt_net/provider/api/anthropic/computer.py +844 -0
  49. pygpt_net/provider/api/anthropic/remote_tools.py +172 -0
  50. pygpt_net/provider/api/anthropic/store.py +307 -0
  51. pygpt_net/{controller/chat/handler/anthropic_stream.py → provider/api/anthropic/stream.py} +99 -10
  52. pygpt_net/provider/api/anthropic/tools.py +32 -77
  53. pygpt_net/provider/api/anthropic/utils.py +30 -0
  54. pygpt_net/{controller/chat/handler → provider/api/anthropic/worker}/__init__.py +0 -0
  55. pygpt_net/provider/api/anthropic/worker/importer.py +278 -0
  56. pygpt_net/provider/api/google/chat.py +62 -9
  57. pygpt_net/provider/api/google/store.py +124 -3
  58. pygpt_net/{controller/chat/handler/google_stream.py → provider/api/google/stream.py} +92 -25
  59. pygpt_net/provider/api/google/utils.py +185 -0
  60. pygpt_net/provider/api/google/worker/importer.py +16 -28
  61. pygpt_net/provider/api/langchain/__init__.py +0 -0
  62. pygpt_net/{controller/chat/handler/langchain_stream.py → provider/api/langchain/stream.py} +1 -1
  63. pygpt_net/provider/api/llama_index/__init__.py +0 -0
  64. pygpt_net/{controller/chat/handler/llamaindex_stream.py → provider/api/llama_index/stream.py} +1 -1
  65. pygpt_net/provider/api/openai/assistants.py +2 -2
  66. pygpt_net/provider/api/openai/image.py +2 -2
  67. pygpt_net/provider/api/openai/store.py +4 -1
  68. pygpt_net/{controller/chat/handler/openai_stream.py → provider/api/openai/stream.py} +1 -1
  69. pygpt_net/provider/api/openai/utils.py +69 -3
  70. pygpt_net/provider/api/openai/worker/importer.py +19 -61
  71. pygpt_net/provider/api/openai/worker/importer_assistants.py +230 -0
  72. pygpt_net/provider/api/x_ai/__init__.py +138 -15
  73. pygpt_net/provider/api/x_ai/audio.py +43 -11
  74. pygpt_net/provider/api/x_ai/chat.py +92 -4
  75. pygpt_net/provider/api/x_ai/image.py +149 -47
  76. pygpt_net/provider/api/x_ai/realtime/__init__.py +12 -0
  77. pygpt_net/provider/api/x_ai/realtime/client.py +1825 -0
  78. pygpt_net/provider/api/x_ai/realtime/realtime.py +198 -0
  79. pygpt_net/provider/api/x_ai/{remote.py → remote_tools.py} +183 -70
  80. pygpt_net/provider/api/x_ai/responses.py +507 -0
  81. pygpt_net/provider/api/x_ai/store.py +610 -0
  82. pygpt_net/{controller/chat/handler/xai_stream.py → provider/api/x_ai/stream.py} +42 -10
  83. pygpt_net/provider/api/x_ai/tools.py +59 -8
  84. pygpt_net/{controller/chat/handler → provider/api/x_ai}/utils.py +1 -2
  85. pygpt_net/provider/api/x_ai/vision.py +1 -4
  86. pygpt_net/provider/api/x_ai/worker/importer.py +308 -0
  87. pygpt_net/provider/audio_input/xai_grok_voice.py +390 -0
  88. pygpt_net/provider/audio_output/xai_tts.py +325 -0
  89. pygpt_net/provider/core/config/patch.py +39 -3
  90. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +2 -2
  91. pygpt_net/provider/core/model/patch.py +39 -1
  92. pygpt_net/tools/image_viewer/tool.py +334 -34
  93. pygpt_net/tools/image_viewer/ui/dialogs.py +319 -22
  94. pygpt_net/tools/text_editor/ui/dialogs.py +3 -2
  95. pygpt_net/tools/text_editor/ui/widgets.py +0 -0
  96. pygpt_net/ui/dialog/assistant.py +1 -1
  97. pygpt_net/ui/dialog/plugins.py +13 -5
  98. pygpt_net/ui/dialog/remote_store.py +552 -0
  99. pygpt_net/ui/dialogs.py +3 -5
  100. pygpt_net/ui/layout/ctx/ctx_list.py +58 -7
  101. pygpt_net/ui/menu/tools.py +6 -13
  102. pygpt_net/ui/widget/dialog/base.py +16 -5
  103. pygpt_net/ui/widget/dialog/{remote_store_google.py → remote_store.py} +10 -10
  104. pygpt_net/ui/widget/element/button.py +4 -4
  105. pygpt_net/ui/widget/image/display.py +2 -2
  106. pygpt_net/ui/widget/lists/context.py +2 -2
  107. pygpt_net/ui/widget/textarea/editor.py +0 -0
  108. {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/METADATA +15 -2
  109. {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/RECORD +107 -89
  110. pygpt_net/controller/remote_store/google/store.py +0 -615
  111. pygpt_net/controller/remote_store/openai/batch.py +0 -524
  112. pygpt_net/controller/remote_store/openai/store.py +0 -699
  113. pygpt_net/ui/dialog/remote_store_google.py +0 -539
  114. pygpt_net/ui/dialog/remote_store_openai.py +0 -539
  115. pygpt_net/ui/widget/dialog/remote_store_openai.py +0 -56
  116. pygpt_net/ui/widget/lists/remote_store_google.py +0 -248
  117. pygpt_net/ui/widget/lists/remote_store_openai.py +0 -317
  118. {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/LICENSE +0 -0
  119. {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/WHEEL +0 -0
  120. {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/entry_points.txt +0 -0
@@ -6,397 +6,354 @@
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.02 20:00:00 #
9
+ # Updated Date: 2026.01.06 06:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from typing import Optional, Any, Union
12
+ from typing import Optional, Union, Any, List
13
13
 
14
14
  from PySide6.QtCore import QTimer
15
- from PySide6.QtWidgets import QFileDialog, QApplication
15
+ from PySide6.QtWidgets import QApplication, QFileDialog
16
16
 
17
17
  from pygpt_net.utils import trans
18
18
 
19
-
20
19
  class Batch:
21
- def __init__(self, window=None):
22
- """
23
- File Search stores batch controller (Google)
20
+ """
21
+ Proxy for batch operations for a specific remote store provider used by Confirm router
22
+ """
24
23
 
25
- :param window: Window instance
26
- """
27
- self.window = window
28
- self.files_to_upload = []
24
+ def __init__(self, ctrl):
25
+ self.ctrl = ctrl
26
+ self.window = ctrl.window
27
+ self.files_to_upload: List[str] = []
29
28
 
30
- def import_stores(self, force: bool = False):
31
- """
32
- Import File Search stores
29
+ # ---------- Starters (invoked by Confirm router) ----------
33
30
 
34
- :param force: if true, imports without confirmation
35
- """
31
+ def import_stores(self, force: bool = False):
36
32
  if not force:
37
33
  self.window.ui.dialogs.confirm(
38
- type='remote_store.google.import',
34
+ type=f'remote_store.import',
39
35
  id='',
40
36
  msg=trans('confirm.remote_store.import'),
41
37
  )
42
38
  return
43
- self.window.update_status("Importing File Search stores...please wait...")
44
- self.window.core.remote_store.google.truncate()
45
- self.window.core.api.google.store.importer.import_vector_stores()
46
- self.window.controller.remote_store.google.update()
39
+ self.window.update_status("Importing stores...please wait...")
40
+ self.ctrl._core_for(self._get_provider()).truncate()
41
+ self._importer().import_vector_stores()
42
+
43
+ def truncate_stores(self, force: bool = False):
44
+ if not force:
45
+ self.window.ui.dialogs.confirm(
46
+ type=f'remote_store.truncate',
47
+ id='',
48
+ msg=trans('confirm.remote_store.truncate'),
49
+ )
50
+ return
51
+ self.window.update_status("Removing stores...please wait...")
52
+ QApplication.processEvents()
53
+ self._importer().truncate_vector_stores()
54
+
55
+ def refresh_stores(self, force: bool = False):
56
+ if not force:
57
+ self.window.ui.dialogs.confirm(
58
+ type=f'remote_store.refresh',
59
+ id='',
60
+ msg=trans('confirm.remote_store.refresh'),
61
+ )
62
+ return
63
+ self.window.update_status("Refreshing stores...please wait...")
64
+ QApplication.processEvents()
65
+ self._importer().refresh_vector_stores()
66
+
67
+ def import_files_assistant_current(self, force: bool = False):
68
+ """Import assistant files from API for current assistant"""
69
+ id = self.window.core.config.get('assistant')
70
+ if id is None or id == "":
71
+ return
72
+ if self.window.core.assistants.has(id):
73
+ assistant = self.window.core.assistants.get_by_id(id)
74
+ store_id = assistant.vector_store
75
+ if store_id is None or store_id == "":
76
+ self.window.ui.dialogs.alert(trans("dialog.remote_store.alert.assign"))
77
+ return
78
+ if not force:
79
+ self.window.ui.dialogs.confirm(
80
+ type=f'remote_store.files.import.assistant.current',
81
+ id=store_id,
82
+ msg=trans('confirm.remote_store.import_files.store'),
83
+ )
84
+ return
85
+ self.window.core.api.openai.store.importer.import_files(store_id) # for current assistant
86
+
87
+ def import_files_assistant_all(self, force: bool = False):
88
+ """Import assistant files from API for all assistants"""
89
+ if not force:
90
+ self.window.ui.dialogs.confirm(
91
+ type=f'remote_store.files.import.assistant.all',
92
+ id='',
93
+ msg=trans('confirm.remote_store.import_files'),
94
+ )
95
+ return
96
+ self.window.core.api.openai.store.importer.import_files() # all
47
97
 
48
98
  def import_files(self, force: bool = False):
49
- """
50
- Sync documents with API (all)
51
- """
52
99
  if not force:
53
100
  self.window.ui.dialogs.confirm(
54
- type='remote_store.google.files.import.all',
101
+ type=f'remote_store.files.import.all',
55
102
  id='',
56
103
  msg=trans('confirm.remote_store.import_files'),
57
104
  )
58
105
  return
59
- try:
60
- self.window.core.api.google.store.importer.import_files() # all
61
- except Exception as e:
62
- self.window.core.debug.log(e)
63
- self.window.ui.dialogs.alert(e)
106
+ self._importer().import_files() # all
64
107
 
65
108
  def import_store_files(self, store_id: str, force: bool = False):
66
- """
67
- Sync documents with API (store)
68
-
69
- :param store_id: store name ('fileSearchStores/...').
70
- """
71
109
  if store_id is None:
72
110
  self.window.ui.dialogs.alert(trans("dialog.remote_store.alert.select"))
73
111
  return
74
-
75
112
  if not force:
76
113
  self.window.ui.dialogs.confirm(
77
- type='remote_store.google.files.import.store',
114
+ type=f'remote_store.files.import.store',
78
115
  id=store_id,
79
116
  msg=trans('confirm.remote_store.import_files.store'),
80
117
  )
81
118
  return
82
- try:
83
- self.window.core.api.google.store.importer.import_files(store_id) # by store
84
- except Exception as e:
85
- self.window.core.debug.log(e)
86
- self.window.ui.dialogs.alert(e)
119
+ self._importer().import_files(store_id)
87
120
 
88
121
  def truncate_files(self, force: bool = False):
89
- """
90
- Truncate all documents in API
91
- """
92
122
  if not force:
123
+ key = 'confirm.remote_store.files.truncate'
93
124
  self.window.ui.dialogs.confirm(
94
- type='remote_store.google.files.truncate',
125
+ type=f'remote_store.files.truncate',
95
126
  id='',
96
- msg=trans('confirm.remote_store.openai.files.truncate'), # reuse same translation key
127
+ msg=trans(key),
97
128
  )
98
129
  return
99
- self.window.update_status("Removing documents...please wait...")
130
+ self.window.update_status("Removing files...please wait...")
100
131
  QApplication.processEvents()
101
- self.window.core.api.google.store.importer.truncate_files()
102
-
103
- def truncate_store_files_by_idx(self, idx: Union[int, list], force: bool = False):
104
- """
105
- Truncate all documents in API (store)
106
- """
107
- store_ids = []
108
- ids = idx if isinstance(idx, list) else [idx]
109
- for i in ids:
110
- store_id = self.window.controller.remote_store.google.get_by_tab_idx(i)
111
- store_ids.append(store_id)
112
- self.truncate_store_files(store_ids, force)
132
+ self._importer().truncate_files()
113
133
 
114
134
  def truncate_store_files(self, store_id: Union[str, list], force: bool = False):
115
- """
116
- Truncate all documents in API (store)
117
- """
118
135
  if store_id is None:
119
136
  self.window.ui.dialogs.alert(trans("dialog.remote_store.alert.select"))
120
137
  return
121
-
122
138
  if not force:
139
+ key = 'confirm.remote_store.files.truncate.store'
123
140
  self.window.ui.dialogs.confirm(
124
- type='remote_store.google.files.truncate.store',
141
+ type=f'remote_store.files.truncate.store',
125
142
  id=store_id,
126
- msg=trans('confirm.remote_store.openai.files.truncate.store'),
143
+ msg=trans(key),
127
144
  )
128
145
  return
129
- self.window.update_status("Removing documents...please wait...")
146
+ self.window.update_status("Removing files...please wait...")
130
147
  QApplication.processEvents()
131
148
  ids = store_id if isinstance(store_id, list) else [store_id]
132
149
  for sid in ids:
133
- self.window.core.api.google.store.importer.truncate_files(sid)
134
-
135
- def clear_store_files_by_idx(self, idx: Union[int, list], force: bool = False):
136
- """
137
- Clear documents (store, local only)
138
- """
139
- store_ids = []
140
- ids = idx if isinstance(idx, list) else [idx]
141
- for i in ids:
142
- store_id = self.window.controller.remote_store.google.get_by_tab_idx(i)
143
- store_ids.append(store_id)
144
- self.clear_store_files(store_ids, force)
145
-
146
- def clear_store_files(self, store_id: Optional[Union[str, list]] = None, force: bool = False):
147
- """
148
- Clear documents (store, local only)
149
- """
150
- if store_id is None:
151
- self.window.ui.dialogs.alert(trans("dialog.remote_store.alert.select"))
152
- return
150
+ self._importer().truncate_files(sid)
153
151
 
152
+ def clear_stores(self, force: bool = False):
154
153
  if not force:
155
154
  self.window.ui.dialogs.confirm(
156
- type='remote_store.google.files.clear.store',
157
- id=store_id,
158
- msg=trans('confirm.assistant.files.clear'),
155
+ type=f'remote_store.clear',
156
+ id='',
157
+ msg=trans('confirm.remote_store.clear'),
159
158
  )
160
159
  return
161
- self.window.update_status("Clearing store documents...please wait...")
160
+ self.window.update_status("Clearing stores...please wait...")
162
161
  QApplication.processEvents()
163
-
164
- ids = store_id if isinstance(store_id, list) else [store_id]
165
- for sid in ids:
166
- self.window.core.remote_store.google.files.truncate_local(sid)
167
-
168
- # Immediate UI refresh from local DB
169
- try:
170
- self.window.controller.remote_store.google.update_files_list()
171
- except Exception:
172
- pass
173
-
174
- self.window.update_status("OK. Store documents cleared.")
175
- self.window.ui.dialogs.alert(trans("status.finished"))
162
+ self.ctrl._core_for(self._get_provider()).truncate()
163
+ self.ctrl.update()
164
+ self.ctrl.current = None
165
+ self.ctrl.init()
166
+ self.window.update_status("OK. All stores cleared.")
167
+ if self._get_provider() == "openai":
168
+ self.window.controller.assistant.files.update()
176
169
 
177
170
  def clear_files(self, force: bool = False):
178
- """
179
- Clear documents (all, local only)
180
- """
181
171
  if not force:
182
172
  self.window.ui.dialogs.confirm(
183
- type='remote_store.google.files.clear.all',
173
+ type=f'remote_store.files.clear.all',
184
174
  id='',
185
175
  msg=trans('confirm.assistant.files.clear'),
186
176
  )
187
177
  return
188
178
  self.window.update_status("Clearing documents...please wait...")
189
179
  QApplication.processEvents()
190
- self.window.core.remote_store.google.files.truncate_local()
180
+ self.ctrl._files_core_for(self._get_provider()).truncate_local()
181
+ self.ctrl.update()
191
182
  self.window.update_status("OK. All documents cleared.")
192
183
  self.window.ui.dialogs.alert(trans("status.finished"))
193
184
 
194
- def clear_stores(self, force: bool = False):
195
- """
196
- Clear File Search stores (local only)
197
- """
185
+ def clear_store_files(self, store_id: Optional[Union[str, list]] = None, force: bool = False):
186
+ if store_id is None:
187
+ self.window.ui.dialogs.alert(trans("dialog.remote_store.alert.select"))
188
+ return
198
189
  if not force:
199
190
  self.window.ui.dialogs.confirm(
200
- type='remote_store.google.clear',
201
- id='',
202
- msg=trans('confirm.remote_store.clear'),
191
+ type=f'remote_store.files.clear.store',
192
+ id=store_id,
193
+ msg=trans('confirm.assistant.files.clear'),
203
194
  )
204
195
  return
205
- self.window.update_status("Clearing File Search stores...please wait...")
196
+ self.window.update_status("Clearing store documents...please wait...")
206
197
  QApplication.processEvents()
207
- self.window.core.remote_store.google.truncate()
208
- self.window.controller.remote_store.google.update()
209
- self.window.update_status("OK. All stores cleared.")
210
- self.window.controller.remote_store.google.current = None
211
- self.window.controller.remote_store.google.init()
198
+ ids = store_id if isinstance(store_id, list) else [store_id]
199
+ for sid in ids:
200
+ self.ctrl._files_core_for(self._get_provider()).truncate_local(sid)
201
+ self.ctrl.update()
202
+ self.window.update_status("OK. Store documents cleared.")
203
+ self.window.ui.dialogs.alert(trans("status.finished"))
212
204
 
213
- def truncate_stores(self, force: bool = False):
214
- """
215
- Truncate File Search stores in API
216
- """
217
- if not force:
205
+ # ---------- Upload ----------
206
+
207
+ def open_upload_files(self):
208
+ if self.ctrl.current is None:
209
+ self.window.ui.dialogs.alert(trans("dialog.remote_store.alert.select"))
210
+ return
211
+ options = QFileDialog.Options()
212
+ files, _ = QFileDialog.getOpenFileNames(
213
+ self.window,
214
+ "Select file(s)...",
215
+ "",
216
+ "All Files (*)",
217
+ options=options
218
+ )
219
+ if files:
220
+ self.files_to_upload = files
221
+ if self.files_to_upload:
222
+ msg = "Are you sure you want to upload {} file(s)?".format(len(self.files_to_upload))
218
223
  self.window.ui.dialogs.confirm(
219
- type='remote_store.google.truncate',
220
- id='',
221
- msg=trans('confirm.remote_store.truncate'),
224
+ type=f"remote_store.files.upload",
225
+ id=0,
226
+ msg=msg,
222
227
  )
223
- return
224
- self.window.update_status("Removing File Search stores...please wait...")
225
- QApplication.processEvents()
226
- self.window.core.remote_store.google.truncate()
227
- self.window.core.api.google.store.importer.truncate_vector_stores()
228
- self.window.controller.remote_store.google.update()
229
- self.window.controller.remote_store.google.current = None
230
- self.window.controller.remote_store.google.init()
231
228
 
232
- def refresh_stores(self, force: bool = False):
233
- """
234
- Refresh all File Search stores
235
- """
236
- if not force:
229
+ def open_upload_dir(self):
230
+ if self.ctrl.current is None:
231
+ self.window.ui.dialogs.alert(trans("dialog.remote_store.alert.select"))
232
+ return
233
+ options = QFileDialog.Options()
234
+ directory = QFileDialog.getExistingDirectory(self.window, "Select directory...", options=options)
235
+ if directory:
236
+ self.files_to_upload = self.window.core.filesystem.get_files_from_dir(directory)
237
+ if self.files_to_upload:
238
+ msg = ("Are you sure you want to upload {} file(s) from directory {}?".
239
+ format(len(self.files_to_upload), directory))
237
240
  self.window.ui.dialogs.confirm(
238
- type='remote_store.google.refresh',
239
- id='',
240
- msg=trans('confirm.remote_store.refresh'),
241
+ type=f"remote_store.files.upload",
242
+ id=0,
243
+ msg=msg,
241
244
  )
245
+
246
+ def upload(self, force: bool = False):
247
+ if self.ctrl.current is None:
248
+ self.window.ui.dialogs.alert(trans("dialog.remote_store.alert.select"))
242
249
  return
243
- self.window.update_status("Refreshing File Search stores...please wait...")
250
+ self.window.update_status("Uploading files...please wait...")
244
251
  QApplication.processEvents()
245
- self.window.core.api.google.store.importer.refresh_vector_stores()
252
+ self._importer().upload_files(self.ctrl.current, self.files_to_upload)
253
+ self.files_to_upload = []
254
+
255
+ def refresh_delayed(self, ms: int = 1000, all: bool = False):
256
+ if all:
257
+ self.window.update_status("Refreshing all stores...")
258
+ QTimer.singleShot(ms, lambda: self.refresh_stores(force=True))
259
+ return
260
+ self.window.update_status("Refreshing status...")
261
+ QTimer.singleShot(ms, lambda: self.ctrl.refresh_status())
262
+
263
+ # ---------- Importer signal handlers (called by provider Importer) ----------
246
264
 
247
265
  def handle_refreshed_stores(self, num: int):
248
- self.window.controller.remote_store.google.update_current()
249
- self.window.controller.remote_store.google.update()
266
+ self.ctrl.update()
250
267
  self.window.ui.dialogs.alert(trans("status.finished"))
251
268
  self.window.update_status("OK. All stores refreshed.")
252
269
 
253
270
  def handle_refreshed_stores_failed(self, error: Any):
254
271
  self.window.core.debug.log(error)
255
- print("Error refreshing stores", error)
256
- self.window.controller.remote_store.google.update()
272
+ self.ctrl.update()
257
273
  self.window.update_status("Error refreshing stores.")
258
274
  self.window.ui.dialogs.alert(error)
259
275
 
260
276
  def handle_imported_stores(self, num: int):
261
- self.window.controller.remote_store.google.update()
277
+ self.ctrl.after_imported_stores(self._get_provider())
278
+ self.ctrl.update()
262
279
  self.window.update_status("OK. Imported stores: " + str(num) + ".")
263
280
 
264
281
  def handle_imported_stores_failed(self, error: Any):
265
282
  self.window.core.debug.log(error)
266
- self.window.controller.remote_store.google.update()
267
- print("Error importing stores", error)
283
+ self.ctrl.update()
268
284
  self.window.update_status("Error importing stores.")
269
285
  self.window.ui.dialogs.alert(error)
270
286
 
271
287
  def handle_truncated_stores(self, num: int):
272
- """
273
- Handle truncated stores
274
-
275
- :param num: number of removed files
276
- """
277
- self.window.core.remote_store.google.truncate()
278
- self.window.controller.remote_store.google.update()
279
- self.window.controller.remote_store.google.current = None
280
- self.window.controller.remote_store.google.init()
288
+ # Clear local DB for provider
289
+ core = self.ctrl._core_for(self._get_provider())
290
+ core.truncate()
291
+ self.ctrl.after_truncated_stores(self._get_provider())
292
+ self.ctrl.update()
293
+ self.ctrl.current = None
294
+ self.ctrl.init()
281
295
  self.window.update_status("OK. Removed stores: " + str(num) + ".")
282
296
  self.window.ui.dialogs.alert(trans("status.finished"))
283
297
 
284
298
  def handle_truncated_stores_failed(self, error: Any):
285
- """
286
- Handle error on truncating stores
287
-
288
- :param error: error message
289
- """
290
299
  self.window.core.debug.log(error)
291
- print("Error removing stores", error)
292
- self.window.controller.remote_store.google.update()
300
+ self.ctrl.update()
293
301
  self.window.update_status("Error removing stores.")
294
302
  self.window.ui.dialogs.alert(error)
295
303
 
296
304
  def handle_imported_files(self, num: int):
297
- self.window.update_status("OK. Imported documents: " + str(num) + ".")
305
+ try:
306
+ self.window.controller.assistant.files.update()
307
+ except Exception:
308
+ pass
309
+ self.window.update_status("OK. Imported files: " + str(num) + ".")
298
310
  self.window.ui.dialogs.alert(trans("status.finished"))
299
311
 
300
312
  def handle_imported_files_failed(self, error: Any):
301
313
  self.window.core.debug.log(error)
302
- print("Error importing documents")
303
314
  self.window.update_status("Error importing documents.")
304
315
  self.window.ui.dialogs.alert(error)
305
316
 
306
317
  def handle_truncated_files(self, store_id: Optional[str] = None, num: int = 0):
307
318
  self.window.update_status("OK. Truncated documents: " + str(num) + ".")
308
- # Immediate UI refresh of files panel (local DB is already truncated)
309
319
  try:
310
- self.window.controller.remote_store.google.update_files_list()
311
- except Exception:
312
- pass
313
-
314
- # Refresh remote status to update counts and bytes
315
- if store_id is not None:
316
- self.window.controller.remote_store.google.refresh_by_store_id(store_id)
317
- else:
318
- self.refresh_delayed(1200)
319
- self.window.ui.dialogs.alert(trans("status.finished"))
320
+ if store_id :
321
+ self.ctrl.refresh_by_store_id_provider(self._get_provider(), store_id)
322
+ else:
323
+ self.refresh_delayed(1200, all=True)
324
+ except Exception as e:
325
+ print(e)
326
+ finally:
327
+ self.window.ui.dialogs.alert(trans("status.finished"))
320
328
 
321
329
  def handle_truncated_files_failed(self, error: Any):
322
330
  self.window.core.debug.log(error)
323
- print("Error truncating documents")
324
331
  self.window.update_status("Error truncating documents.")
325
332
  self.window.ui.dialogs.alert(error)
326
333
 
327
- def open_upload_files(self):
328
- """Open upload files dialog"""
329
- if self.window.controller.remote_store.google.current is None:
330
- self.window.ui.dialogs.alert("Please select File Search store first.")
331
- return
332
-
333
- options = QFileDialog.Options()
334
- files, _ = QFileDialog.getOpenFileNames(
335
- self.window,
336
- "Select file(s)...",
337
- "",
338
- "All Files (*)",
339
- options=options
340
- )
341
- if files:
342
- self.files_to_upload = files
343
-
344
- if self.files_to_upload:
345
- msg = "Are you sure you want to upload {} file(s)?".format(len(self.files_to_upload))
346
- self.window.ui.dialogs.confirm(
347
- type="remote_store.google.files.upload",
348
- id=0,
349
- msg=msg,
350
- )
351
-
352
- def open_upload_dir(self):
353
- """Open upload directory dialog"""
354
- if self.window.controller.remote_store.google.current is None:
355
- self.window.ui.dialogs.alert("Please select File Search store first.")
356
- return
357
-
358
- options = QFileDialog.Options()
359
- directory = QFileDialog.getExistingDirectory(
360
- self.window,
361
- "Select directory...",
362
- options=options
363
- )
364
- if directory:
365
- self.files_to_upload = self.window.core.filesystem.get_files_from_dir(directory)
366
-
367
- if self.files_to_upload:
368
- msg = ("Are you sure you want to upload {} file(s) from directory {}?".
369
- format(len(self.files_to_upload), directory))
370
- self.window.ui.dialogs.confirm(
371
- type="remote_store.google.files.upload",
372
- id=0,
373
- msg=msg,
374
- )
375
-
376
- def refresh_delayed(self, ms: int = 1000):
377
- self.window.update_status("Refreshing status...")
378
- QTimer.singleShot(ms, lambda: self.window.controller.remote_store.google.refresh_status())
379
-
380
- def upload(self, force: bool = False):
381
- """Upload files to File Search store"""
382
- if self.window.controller.remote_store.google.current is None:
383
- self.window.ui.dialogs.alert(trans("dialog.remote_store.alert.select"))
384
- return
385
-
386
- store_id = self.window.controller.remote_store.google.current
387
- self.window.update_status("Uploading files...please wait...")
388
- QApplication.processEvents()
389
- self.window.core.api.google.store.importer.upload_files(store_id, self.files_to_upload)
390
- self.files_to_upload = []
391
-
392
334
  def handle_uploaded_files(self, num: int):
335
+ try:
336
+ self.window.controller.assistant.files.update()
337
+ except Exception:
338
+ pass
393
339
  self.window.update_status("OK. Uploaded files: " + str(num) + ".")
394
340
  self.window.ui.dialogs.alert("OK. Uploaded files: " + str(num) + ".")
395
341
  self.refresh_delayed(1500)
396
342
 
397
343
  def handle_uploaded_files_failed(self, error: Any):
398
344
  self.window.core.debug.log(error)
399
- print("Error uploading files")
400
345
  self.refresh_delayed(1500)
401
346
  self.window.update_status("Error uploading files.")
402
- self.window.ui.dialogs.alert(error)
347
+ self.window.ui.dialogs.alert(error)
348
+
349
+ # ---------- Internal ----------
350
+
351
+ def _get_provider(self):
352
+ return self.ctrl.provider_key
353
+
354
+ def _importer(self):
355
+ provider = self._get_provider()
356
+ if (hasattr(self.window.core.api, provider)
357
+ and hasattr(getattr(self.window.core.api, provider), 'store')
358
+ and hasattr(getattr(self.window.core.api, provider).store, 'importer')):
359
+ return getattr(self.window.core.api, provider).store.importer