pygpt-net 2.6.33__py3-none-any.whl → 2.6.34__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 (42) hide show
  1. pygpt_net/CHANGELOG.txt +7 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/controller/assistant/batch.py +14 -4
  4. pygpt_net/controller/assistant/files.py +1 -0
  5. pygpt_net/controller/assistant/store.py +195 -1
  6. pygpt_net/controller/camera/camera.py +1 -1
  7. pygpt_net/controller/chat/common.py +50 -46
  8. pygpt_net/controller/config/placeholder.py +95 -75
  9. pygpt_net/controller/dialogs/confirm.py +3 -1
  10. pygpt_net/controller/media/media.py +11 -3
  11. pygpt_net/controller/painter/common.py +231 -13
  12. pygpt_net/core/assistants/files.py +18 -0
  13. pygpt_net/core/camera/camera.py +31 -402
  14. pygpt_net/core/camera/worker.py +430 -0
  15. pygpt_net/core/filesystem/url.py +3 -0
  16. pygpt_net/core/render/web/body.py +65 -9
  17. pygpt_net/core/text/utils.py +3 -0
  18. pygpt_net/data/config/config.json +3 -3
  19. pygpt_net/data/config/models.json +3 -3
  20. pygpt_net/data/config/settings.json +10 -5
  21. pygpt_net/data/locale/locale.de.ini +8 -7
  22. pygpt_net/data/locale/locale.en.ini +9 -6
  23. pygpt_net/data/locale/locale.es.ini +8 -7
  24. pygpt_net/data/locale/locale.fr.ini +8 -7
  25. pygpt_net/data/locale/locale.it.ini +8 -7
  26. pygpt_net/data/locale/locale.pl.ini +8 -7
  27. pygpt_net/data/locale/locale.uk.ini +8 -7
  28. pygpt_net/data/locale/locale.zh.ini +8 -7
  29. pygpt_net/item/assistant.py +13 -1
  30. pygpt_net/provider/api/google/__init__.py +32 -23
  31. pygpt_net/provider/api/openai/store.py +45 -1
  32. pygpt_net/provider/llms/google.py +4 -0
  33. pygpt_net/ui/dialog/assistant_store.py +213 -203
  34. pygpt_net/ui/layout/chat/input.py +3 -3
  35. pygpt_net/ui/widget/draw/painter.py +16 -1
  36. pygpt_net/ui/widget/option/combo.py +5 -1
  37. pygpt_net/ui/widget/textarea/input.py +273 -3
  38. {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.34.dist-info}/METADATA +9 -2
  39. {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.34.dist-info}/RECORD +42 -41
  40. {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.34.dist-info}/LICENSE +0 -0
  41. {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.34.dist-info}/WHEEL +0 -0
  42. {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.34.dist-info}/entry_points.txt +0 -0
@@ -6,15 +6,15 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.06.29 18:00:00 #
9
+ # Updated Date: 2025.09.02 22:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
13
13
 
14
- from PySide6.QtCore import Qt
14
+ from PySide6.QtCore import Qt, QPoint
15
15
  from PySide6.QtGui import QStandardItemModel, QAction, QIcon
16
16
  from PySide6.QtWidgets import QPushButton, QHBoxLayout, QLabel, QVBoxLayout, QScrollArea, QWidget, QTabWidget, QFrame, \
17
- QSplitter, QSizePolicy, QMenuBar, QCheckBox
17
+ QSplitter, QSizePolicy, QMenuBar, QCheckBox, QMenu, QListView
18
18
 
19
19
  from pygpt_net.ui.widget.dialog.assistant_store import AssistantVectorStoreDialog
20
20
  from pygpt_net.ui.widget.element.group import CollapsedGroup
@@ -46,122 +46,83 @@ class AssistantVectorStore:
46
46
 
47
47
  :param idx: current model tab index
48
48
  """
49
- self.window.ui.nodes['assistant.store.btn.new'] = \
50
- QPushButton(trans("dialog.assistant.store.btn.new"))
51
- self.window.ui.nodes['assistant.store.btn.save'] = \
52
- QPushButton(trans("dialog.assistant.store.btn.save"))
53
- self.window.ui.nodes['assistant.store.btn.refresh_status'] = \
54
- QPushButton(QIcon(":/icons/reload.svg"), "")
55
- self.window.ui.nodes['assistant.store.btn.close'] = \
56
- QPushButton(trans("dialog.assistant.store.btn.close"))
57
-
58
- self.window.ui.nodes['assistant.store.btn.refresh_status'].setToolTip(
49
+ ui = self.window.ui
50
+ nodes = ui.nodes
51
+ models = ui.models
52
+ splitters = ui.splitters
53
+ controller = self.window.controller
54
+ core = self.window.core
55
+
56
+ nodes['assistant.store.btn.new'] = QPushButton(trans("dialog.assistant.store.btn.new"))
57
+ nodes['assistant.store.btn.save'] = QPushButton(trans("dialog.assistant.store.btn.save"))
58
+ nodes['assistant.store.btn.refresh_status'] = QPushButton(QIcon(":/icons/reload.svg"), "")
59
+ nodes['assistant.store.btn.close'] = QPushButton(trans("dialog.assistant.store.btn.close"))
60
+
61
+ nodes['assistant.store.btn.refresh_status'].setToolTip(
59
62
  trans("dialog.assistant.store.btn.refresh_status")
60
63
  )
61
- self.window.ui.nodes['assistant.store.btn.refresh_status'].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
62
-
63
- self.window.ui.nodes['assistant.store.btn.upload.files'] = \
64
- QPushButton(trans("dialog.assistant.store.btn.upload.files"))
65
- self.window.ui.nodes['assistant.store.btn.upload.dir'] = \
66
- QPushButton(trans("dialog.assistant.store.btn.upload.dir"))
67
-
68
- self.window.ui.nodes['assistant.store.hide_thread'] = QCheckBox(trans("assistant.store.hide_threads"))
69
- self.window.ui.nodes['assistant.store.hide_thread'].setChecked(True)
70
- self.window.ui.nodes['assistant.store.hide_thread'].stateChanged.connect(
71
- lambda: self.window.controller.assistant.store.set_hide_thread(
72
- self.window.ui.nodes['assistant.store.hide_thread'].isChecked()
73
- )
74
- )
75
- if not self.window.core.config.get("assistant.store.hide_threads"):
76
- self.window.ui.nodes['assistant.store.hide_thread'].setChecked(False)
64
+ nodes['assistant.store.btn.refresh_status'].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
77
65
 
78
- self.window.ui.nodes['assistant.store.btn.new'].clicked.connect(
79
- lambda: self.window.controller.assistant.store.new()
80
- )
81
- self.window.ui.nodes['assistant.store.btn.save'].clicked.connect(
82
- lambda: self.window.controller.assistant.store.save()
83
- )
84
- self.window.ui.nodes['assistant.store.btn.close'].clicked.connect(
85
- lambda: self.window.controller.assistant.store.close()
86
- )
87
- self.window.ui.nodes['assistant.store.btn.refresh_status'].clicked.connect(
88
- lambda: self.window.controller.assistant.store.refresh_status()
89
- )
90
- self.window.ui.nodes['assistant.store.btn.upload.files'].clicked.connect(
91
- lambda: self.window.controller.assistant.batch.open_upload_files()
92
- )
93
- self.window.ui.nodes['assistant.store.btn.upload.dir'].clicked.connect(
94
- lambda: self.window.controller.assistant.batch.open_upload_dir()
66
+ nodes['assistant.store.btn.upload.files'] = QPushButton(trans("dialog.assistant.store.btn.upload.files"))
67
+ nodes['assistant.store.btn.upload.dir'] = QPushButton(trans("dialog.assistant.store.btn.upload.dir"))
68
+
69
+ nodes['assistant.store.hide_thread'] = QCheckBox(trans("assistant.store.hide_threads"))
70
+ nodes['assistant.store.hide_thread'].setChecked(bool(core.config.get("assistant.store.hide_threads")))
71
+ nodes['assistant.store.hide_thread'].toggled.connect(
72
+ controller.assistant.store.set_hide_thread
95
73
  )
96
74
 
97
- # set enter key to save button
98
- self.window.ui.nodes['assistant.store.btn.new'].setAutoDefault(False)
99
- self.window.ui.nodes['assistant.store.btn.refresh_status'].setAutoDefault(False)
100
- self.window.ui.nodes['assistant.store.btn.save'].setAutoDefault(True)
75
+ nodes['assistant.store.btn.new'].clicked.connect(controller.assistant.store.new)
76
+ nodes['assistant.store.btn.save'].clicked.connect(controller.assistant.store.save_btn)
77
+ nodes['assistant.store.btn.close'].clicked.connect(controller.assistant.store.close)
78
+ nodes['assistant.store.btn.refresh_status'].clicked.connect(controller.assistant.store.refresh_status)
79
+ nodes['assistant.store.btn.upload.files'].clicked.connect(controller.assistant.batch.open_upload_files)
80
+ nodes['assistant.store.btn.upload.dir'].clicked.connect(controller.assistant.batch.open_upload_dir)
101
81
 
102
- # footer buttons
103
- footer = QHBoxLayout()
104
- footer.addWidget(self.window.ui.nodes['assistant.store.btn.close'])
105
- footer.addWidget(self.window.ui.nodes['assistant.store.btn.save'])
82
+ nodes['assistant.store.btn.new'].setAutoDefault(False)
83
+ nodes['assistant.store.btn.refresh_status'].setAutoDefault(False)
84
+ nodes['assistant.store.btn.save'].setAutoDefault(True)
106
85
 
107
- # upload buttons
108
- upload_layout = QHBoxLayout()
109
- upload_layout.addWidget(self.window.ui.nodes['assistant.store.btn.refresh_status']) # reload status btn
110
- upload_layout.addWidget(self.window.ui.nodes['assistant.store.btn.upload.files'])
111
- upload_layout.addWidget(self.window.ui.nodes['assistant.store.btn.upload.dir'])
112
- upload_layout.setContentsMargins(0, 0, 0, 0)
86
+ footer = QHBoxLayout()
87
+ footer.addWidget(nodes['assistant.store.btn.close'])
88
+ footer.addWidget(nodes['assistant.store.btn.save'])
113
89
 
114
- # editor tabs
115
- self.window.ui.tabs['assistant.store'] = QTabWidget()
90
+ ui.tabs['assistant.store'] = QTabWidget()
116
91
 
117
- # build settings tabs
118
92
  parent_id = "assistant.store"
119
93
 
120
94
  scroll = QScrollArea()
121
95
  scroll.setWidgetResizable(True)
122
96
  content = QVBoxLayout()
123
97
 
124
- # create config options entry if not exists
125
- if parent_id not in self.window.ui.config:
126
- self.window.ui.config[parent_id] = {}
98
+ if parent_id not in ui.config:
99
+ ui.config[parent_id] = {}
127
100
 
128
- # get options
129
- options = copy.deepcopy(self.window.controller.assistant.store.get_options())
101
+ options = copy.deepcopy(controller.assistant.store.get_options())
130
102
  widgets = self.build_widgets(options)
131
- advanced_keys = []
132
- for key in options:
133
- if 'advanced' in options[key] and options[key]['advanced']:
134
- advanced_keys.append(key)
103
+ advanced_keys = [k for k, v in options.items() if v.get('advanced')]
135
104
 
136
- # apply settings widgets
137
- for key in widgets:
138
- self.window.ui.config[parent_id][key] = widgets[key]
105
+ for key, w in widgets.items():
106
+ ui.config[parent_id][key] = w
139
107
 
140
108
  for key in widgets:
141
- if key in advanced_keys: # hide advanced options
109
+ if key in advanced_keys:
142
110
  continue
143
- content.addLayout(self.add_option(widgets[key], options[key])) # add to scroll
111
+ content.addLayout(self.add_option(widgets[key], options[key]))
144
112
 
145
- # append advanced options at the end
146
- if len(advanced_keys) > 0:
113
+ if advanced_keys:
147
114
  group_id = 'assistant.store.advanced'
148
- self.window.ui.groups[group_id] = CollapsedGroup(self.window, group_id, None, False, None)
149
- self.window.ui.groups[group_id].box.setText(trans('settings.advanced.collapse'))
115
+ ui.groups[group_id] = CollapsedGroup(self.window, group_id, None, False, None)
116
+ ui.groups[group_id].box.setText(trans('settings.advanced.collapse'))
150
117
  for key in widgets:
151
- if key not in advanced_keys: # ignore non-advanced options
118
+ if key not in advanced_keys:
152
119
  continue
120
+ option = self.add_option(widgets[key], options[key])
121
+ ui.groups[group_id].add_layout(option)
122
+ content.addWidget(ui.groups[group_id])
153
123
 
154
- option = self.add_option(widgets[key], options[key]) # build option
155
- self.window.ui.groups[group_id].add_layout(option) # add option to group
156
-
157
- # add advanced options group to scroll
158
- content.addWidget(self.window.ui.groups[group_id])
159
-
160
-
161
- content.addLayout(upload_layout) # upload buttons
162
124
  content.setContentsMargins(0, 0, 0, 0)
163
125
 
164
- # scroll widget
165
126
  scroll_widget = QWidget()
166
127
  scroll_widget.setLayout(content)
167
128
  scroll.setWidget(scroll_widget)
@@ -173,65 +134,93 @@ class AssistantVectorStore:
173
134
  area_widget = QWidget()
174
135
  area_widget.setLayout(area)
175
136
 
176
- data = {}
177
- for store_id in self.window.core.assistants.store.items.keys():
178
- store = self.window.core.assistants.store.items[store_id]
179
- data[store_id] = store
180
-
181
- # storage list
182
- id = 'assistant.store.list'
183
- self.window.ui.nodes[id] = AssistantVectorStoreEditorList(self.window, id)
184
- self.window.ui.models[id] = self.create_model(self.window)
185
- self.window.ui.nodes[id].setModel(self.window.ui.models[id])
137
+ list_id = 'assistant.store.list'
138
+ nodes[list_id] = AssistantVectorStoreEditorList(self.window, list_id)
139
+ models[list_id] = self.create_model(self.window)
140
+ nodes[list_id].setModel(models[list_id])
141
+ nodes[list_id].setMinimumWidth(250)
186
142
 
187
- # left widget
188
143
  left_layout = QVBoxLayout()
189
- left_layout.addWidget(self.window.ui.nodes['assistant.store.btn.new'])
190
- left_layout.addWidget(self.window.ui.nodes[id])
191
- left_layout.addWidget(self.window.ui.nodes['assistant.store.hide_thread'])
192
-
144
+ left_layout.addWidget(nodes['assistant.store.btn.new'])
145
+ left_layout.addWidget(nodes[list_id])
146
+ left_layout.addWidget(nodes['assistant.store.hide_thread'])
193
147
  left_layout.setContentsMargins(0, 0, 0, 0)
194
148
  left_widget = QWidget()
195
149
  left_widget.setLayout(left_layout)
196
150
 
197
- # update models list
198
- self.update_list(id, data)
199
-
200
- # set max width to list
201
- self.window.ui.nodes[id].setMinimumWidth(250)
202
-
203
- # splitter
204
- self.window.ui.splitters['dialog.assistant.store'] = QSplitter(Qt.Horizontal)
205
- self.window.ui.splitters['dialog.assistant.store'].addWidget(left_widget) # list
206
- self.window.ui.splitters['dialog.assistant.store'].addWidget(area_widget) # tabs
207
- self.window.ui.splitters['dialog.assistant.store'].setStretchFactor(0, 2)
208
- self.window.ui.splitters['dialog.assistant.store'].setStretchFactor(1, 5)
209
- self.window.ui.splitters['dialog.assistant.store'].setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
151
+ self.update_list(list_id, core.assistants.store.items)
152
+
153
+ # ==================== Files panel ====================
154
+ files_panel = QVBoxLayout()
155
+ files_label = QLabel("Files")
156
+ files_label.setStyleSheet("font-weight: bold;")
157
+ files_panel.addWidget(files_label)
158
+
159
+ files_list_id = 'assistant.store.files.list'
160
+ nodes[files_list_id] = QListView()
161
+ nodes[files_list_id].setEditTriggers(QListView.NoEditTriggers)
162
+ nodes[files_list_id].setSelectionMode(QListView.SingleSelection)
163
+ nodes[files_list_id].setContextMenuPolicy(Qt.CustomContextMenu)
164
+ nodes[files_list_id].customContextMenuRequested.connect(self.on_files_context_menu)
165
+
166
+ models[files_list_id] = self.create_model(self.window)
167
+ nodes[files_list_id].setModel(models[files_list_id])
168
+ nodes[files_list_id].setMinimumWidth(280)
169
+
170
+ files_bottom = QHBoxLayout()
171
+ files_bottom.setContentsMargins(0, 0, 0, 0)
172
+ files_bottom.addWidget(nodes['assistant.store.btn.upload.files'])
173
+ files_bottom.addWidget(nodes['assistant.store.btn.upload.dir'])
174
+ files_bottom.addWidget(nodes['assistant.store.btn.refresh_status'])
175
+
176
+ files_panel.addWidget(nodes[files_list_id])
177
+ files_panel.setContentsMargins(0, 0, 0, 5)
178
+ files_panel.addLayout(files_bottom)
179
+ files_widget = QWidget()
180
+ files_widget.setLayout(files_panel)
181
+ # ========================================================================
182
+
183
+ splitters['dialog.assistant.store'] = QSplitter(Qt.Horizontal)
184
+ splitters['dialog.assistant.store'].addWidget(left_widget)
185
+
186
+ splitters['dialog.assistant.store.right'] = QSplitter(Qt.Horizontal)
187
+ splitters['dialog.assistant.store.right'].addWidget(files_widget)
188
+ splitters['dialog.assistant.store.right'].addWidget(area_widget)
189
+ splitters['dialog.assistant.store.right'].setStretchFactor(1, 7)
190
+ splitters['dialog.assistant.store.right'].setStretchFactor(0, 3)
191
+
192
+ splitters['dialog.assistant.store'].addWidget(splitters['dialog.assistant.store.right'])
193
+ splitters['dialog.assistant.store'].setStretchFactor(0, 2)
194
+ splitters['dialog.assistant.store'].setStretchFactor(1, 8)
195
+ splitters['dialog.assistant.store'].setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
210
196
 
211
197
  main_layout = QHBoxLayout()
212
- main_layout.addWidget(self.window.ui.splitters['dialog.assistant.store'])
198
+ main_layout.addWidget(splitters['dialog.assistant.store'])
213
199
  main_layout.setContentsMargins(0, 0, 0, 0)
214
200
 
215
201
  layout = QVBoxLayout()
216
- layout.addLayout(main_layout) # list + model tabs
217
- layout.addLayout(footer) # bottom buttons (save, defaults)
202
+ layout.addLayout(main_layout)
203
+ layout.addLayout(footer)
218
204
 
219
- # add menu
220
205
  layout.setMenuBar(self.setup_menu())
221
206
 
222
- self.window.ui.dialog[self.dialog_id] = AssistantVectorStoreDialog(self.window, self.dialog_id)
223
- self.window.ui.dialog[self.dialog_id].setLayout(layout)
224
- self.window.ui.dialog[self.dialog_id].setWindowTitle(trans('dialog.assistant.store'))
207
+ ui.dialog[self.dialog_id] = AssistantVectorStoreDialog(self.window, self.dialog_id)
208
+ ui.dialog[self.dialog_id].setLayout(layout)
209
+ ui.dialog[self.dialog_id].setWindowTitle(trans('dialog.assistant.store'))
225
210
 
226
- # restore current opened tab if idx is set
227
211
  if idx is not None:
228
212
  try:
229
- self.window.controller.assistant.store.set_by_tab(idx)
213
+ controller.assistant.store.set_by_tab(idx)
230
214
  except Exception as e:
231
215
  print('Failed restore store editor tab: {}'.format(idx))
232
216
  else:
233
- if self.window.controller.assistant.store.current is None:
234
- self.window.controller.assistant.store.set_by_tab(0)
217
+ if controller.assistant.store.current is None:
218
+ controller.assistant.store.set_by_tab(0)
219
+
220
+ try:
221
+ controller.assistant.store.update_files_list()
222
+ except Exception:
223
+ pass
235
224
 
236
225
  def setup_menu(self) -> QMenuBar:
237
226
  """
@@ -246,107 +235,104 @@ class AssistantVectorStore:
246
235
  self.menu["current"] = self.menu_bar.addMenu(trans("dialog.assistant.store.menu.current"))
247
236
  self.menu["all"] = self.menu_bar.addMenu(trans("dialog.assistant.store.menu.all"))
248
237
 
249
- # --------------------------------------------
250
-
251
- # import files (current)
252
238
  self.actions["current.import_files"] = QAction(QIcon(":/icons/download.svg"),
253
- trans("dialog.assistant.store.menu.current.import_files"))
239
+ trans("dialog.assistant.store.menu.current.import_files"),
240
+ self.menu_bar)
254
241
  self.actions["current.import_files"].triggered.connect(
255
242
  lambda: self.window.controller.assistant.batch.import_store_files(
256
- self.window.controller.assistant.store.current # current selected store
243
+ self.window.controller.assistant.store.current
257
244
  )
258
245
  )
259
246
 
260
- # refresh (current)
261
247
  self.actions["current.refresh_store"] = QAction(QIcon(":/icons/reload.svg"),
262
- trans("dialog.assistant.store.menu.current.refresh_store"))
248
+ trans("dialog.assistant.store.menu.current.refresh_store"),
249
+ self.menu_bar)
263
250
  self.actions["current.refresh_store"].triggered.connect(
264
- lambda: self.window.controller.assistant.store.refresh_status()
251
+ self.window.controller.assistant.store.refresh_status
265
252
  )
266
253
 
267
- # clear files (current, local)
268
254
  self.actions["current.clear_files"] = QAction(QIcon(":/icons/close.svg"),
269
- trans("dialog.assistant.store.menu.current.clear_files"))
255
+ trans("dialog.assistant.store.menu.current.clear_files"),
256
+ self.menu_bar)
270
257
  self.actions["current.clear_files"].triggered.connect(
271
258
  lambda: self.window.controller.assistant.batch.clear_store_files(
272
- self.window.controller.assistant.store.current # current selected store
259
+ self.window.controller.assistant.store.current
273
260
  )
274
261
  )
275
262
 
276
- # truncate files (current, local + remote)
277
263
  self.actions["current.truncate_files"] = QAction(QIcon(":/icons/delete.svg"),
278
- trans("dialog.assistant.store.menu.current.truncate_files"))
264
+ trans("dialog.assistant.store.menu.current.truncate_files"),
265
+ self.menu_bar)
279
266
  self.actions["current.truncate_files"].triggered.connect(
280
267
  lambda: self.window.controller.assistant.batch.truncate_store_files(
281
- self.window.controller.assistant.store.current # current selected store
268
+ self.window.controller.assistant.store.current
282
269
  )
283
270
  )
284
271
 
285
- # delete (current, local + remote)
286
272
  self.actions["current.delete"] = QAction(QIcon(":/icons/delete.svg"),
287
- trans("dialog.assistant.store.menu.current.delete"))
273
+ trans("dialog.assistant.store.menu.current.delete"),
274
+ self.menu_bar)
288
275
  self.actions["current.delete"].triggered.connect(
289
276
  lambda: self.window.controller.assistant.store.delete(
290
- self.window.controller.assistant.store.current # current selected store
277
+ self.window.controller.assistant.store.current
291
278
  )
292
279
  )
293
280
 
294
- # --------------------------------------------
295
-
296
- # import all (stores + files)
297
- self.actions["all.import_all"] = QAction(QIcon(":/icons/download.svg"), trans("dialog.assistant.store.menu.all.import_all"))
281
+ self.actions["all.import_all"] = QAction(QIcon(":/icons/download.svg"),
282
+ trans("dialog.assistant.store.menu.all.import_all"),
283
+ self.menu_bar)
298
284
  self.actions["all.import_all"].triggered.connect(
299
- lambda: self.window.controller.assistant.batch.import_stores()
285
+ self.window.controller.assistant.batch.import_stores
300
286
  )
301
287
 
302
- # import files (all)
303
288
  self.actions["all.import_files"] = QAction(QIcon(":/icons/download.svg"),
304
- trans("dialog.assistant.store.menu.all.import_files"))
289
+ trans("dialog.assistant.store.menu.all.import_files"),
290
+ self.menu_bar)
305
291
  self.actions["all.import_files"].triggered.connect(
306
- lambda: self.window.controller.assistant.batch.import_files()
292
+ self.window.controller.assistant.batch.import_files
307
293
  )
308
294
 
309
- # refresh (local)
310
295
  self.actions["all.refresh_stores"] = QAction(QIcon(":/icons/reload.svg"),
311
- trans("dialog.assistant.store.menu.all.refresh_store"))
296
+ trans("dialog.assistant.store.menu.all.refresh_store"),
297
+ self.menu_bar)
312
298
  self.actions["all.refresh_stores"].triggered.connect(
313
- lambda: self.window.controller.assistant.batch.refresh_stores()
299
+ self.window.controller.assistant.batch.refresh_stores
314
300
  )
315
301
 
316
- # clear stores (all, local)
317
302
  self.actions["all.clear_stores"] = QAction(QIcon(":/icons/close.svg"),
318
- trans("dialog.assistant.store.menu.all.clear_store"))
303
+ trans("dialog.assistant.store.menu.all.clear_store"),
304
+ self.menu_bar)
319
305
  self.actions["all.clear_stores"].triggered.connect(
320
- lambda: self.window.controller.assistant.batch.clear_stores()
306
+ self.window.controller.assistant.batch.clear_stores
321
307
  )
322
308
 
323
- # clear files (all, local)
324
309
  self.actions["all.clear_files"] = QAction(QIcon(":/icons/close.svg"),
325
- trans("dialog.assistant.store.menu.all.clear_files"))
310
+ trans("dialog.assistant.store.menu.all.clear_files"),
311
+ self.menu_bar)
326
312
  self.actions["all.clear_files"].triggered.connect(
327
- lambda: self.window.controller.assistant.batch.clear_files()
313
+ self.window.controller.assistant.batch.clear_files
328
314
  )
329
315
 
330
- # truncate stores (all, local + remote)
331
- self.actions["all.truncate_stores"] = QAction(QIcon(":/icons/delete.svg"), trans("dialog.assistant.store.menu.all.truncate_store"))
316
+ self.actions["all.truncate_stores"] = QAction(QIcon(":/icons/delete.svg"),
317
+ trans("dialog.assistant.store.menu.all.truncate_store"),
318
+ self.menu_bar)
332
319
  self.actions["all.truncate_stores"].triggered.connect(
333
- lambda: self.window.controller.assistant.batch.truncate_stores()
320
+ self.window.controller.assistant.batch.truncate_stores
334
321
  )
335
322
 
336
- # truncate files (all, local + remote)
337
- self.actions["all.truncate_files"] = QAction(QIcon(":/icons/delete.svg"), trans("dialog.assistant.store.menu.all.truncate_files"))
323
+ self.actions["all.truncate_files"] = QAction(QIcon(":/icons/delete.svg"),
324
+ trans("dialog.assistant.store.menu.all.truncate_files"),
325
+ self.menu_bar)
338
326
  self.actions["all.truncate_files"].triggered.connect(
339
- lambda: self.window.controller.assistant.batch.truncate_files()
327
+ self.window.controller.assistant.batch.truncate_files
340
328
  )
341
329
 
342
- # current
343
330
  self.menu["current"].addAction(self.actions["current.import_files"])
344
331
  self.menu["current"].addAction(self.actions["current.refresh_store"])
345
332
  self.menu["current"].addAction(self.actions["current.clear_files"])
346
333
  self.menu["current"].addAction(self.actions["current.truncate_files"])
347
334
  self.menu["current"].addAction(self.actions["current.delete"])
348
335
 
349
- # all
350
336
  self.menu["all"].addAction(self.actions["all.import_all"])
351
337
  self.menu["all"].addAction(self.actions["all.import_files"])
352
338
  self.menu["all"].addAction(self.actions["all.refresh_stores"])
@@ -370,30 +356,29 @@ class AssistantVectorStore:
370
356
  for key in options:
371
357
  option = options[key]
372
358
  option['id'] = key
373
- # create widget by option type
374
359
  if option['type'] == 'text' or option['type'] == 'int' or option['type'] == 'float':
375
360
  if 'slider' in option and option['slider'] \
376
361
  and (option['type'] == 'int' or option['type'] == 'float'):
377
- widgets[key] = OptionSlider(self.window, parent, key, option) # slider + text input
362
+ widgets[key] = OptionSlider(self.window, parent, key, option)
378
363
  else:
379
364
  if 'secret' in option and option['secret']:
380
- widgets[key] = PasswordInput(self.window, parent, key, option) # password input
365
+ widgets[key] = PasswordInput(self.window, parent, key, option)
381
366
  else:
382
- widgets[key] = OptionInput(self.window, parent, key, option) # text input
367
+ widgets[key] = OptionInput(self.window, parent, key, option)
383
368
  elif option['type'] == 'textarea':
384
- widgets[key] = OptionTextarea(self.window, parent, key, option) # textarea
369
+ widgets[key] = OptionTextarea(self.window, parent, key, option)
385
370
  elif option['type'] == 'bool':
386
- widgets[key] = OptionCheckbox(self.window, parent, key, option) # checkbox
371
+ widgets[key] = OptionCheckbox(self.window, parent, key, option)
387
372
  elif option['type'] == 'bool_list':
388
373
  self.window.controller.config.placeholder.apply(option)
389
- widgets[key] = OptionCheckboxList(self.window, parent, key, option) # checkbox list
374
+ widgets[key] = OptionCheckboxList(self.window, parent, key, option)
390
375
  elif option['type'] == 'dict':
391
376
  self.window.controller.config.placeholder.apply(option)
392
- widgets[key] = OptionDict(self.window, parent, key, option) # dictionary
377
+ widgets[key] = OptionDict(self.window, parent, key, option)
393
378
  widgets[key].setMinimumHeight(200)
394
379
  elif option['type'] == 'combo':
395
380
  self.window.controller.config.placeholder.apply(option)
396
- widgets[key] = OptionCombo(self.window, parent, key, option) # combobox
381
+ widgets[key] = OptionCombo(self.window, parent, key, option)
397
382
 
398
383
  return widgets
399
384
 
@@ -448,10 +433,9 @@ class AssistantVectorStore:
448
433
  desc = trans(option['description'])
449
434
  desc_key = 'model.' + key + '.desc'
450
435
 
451
- # 2-columns layout
452
436
  if option['type'] not in one_column_types:
453
437
  cols = QHBoxLayout()
454
- cols.addWidget(self.window.ui.nodes[label_key]) # disable label in bool type
438
+ cols.addWidget(self.window.ui.nodes[label_key])
455
439
  cols.addWidget(widget)
456
440
 
457
441
  cols_widget = QWidget()
@@ -461,12 +445,11 @@ class AssistantVectorStore:
461
445
  layout = QVBoxLayout()
462
446
  layout.addWidget(cols_widget)
463
447
  else:
464
- # 1-column layout: textarea and dict fields
465
448
  layout = QVBoxLayout()
466
449
  if option['type'] != 'bool':
467
450
  layout.addWidget(self.window.ui.nodes[label_key])
468
451
  else:
469
- widget.box.setText(label) # set checkbox label
452
+ widget.box.setText(label)
470
453
  layout.addWidget(widget)
471
454
 
472
455
  if desc:
@@ -507,23 +490,50 @@ class AssistantVectorStore:
507
490
  :param id: ID of the list
508
491
  :param data: Data to update
509
492
  """
510
- if id not in self.window.ui.models:
493
+ models = self.window.ui.models
494
+ if id not in models:
511
495
  return
512
- self.window.ui.models[id].removeRows(0, self.window.ui.models[id].rowCount())
513
- i = 0
514
- for n in data:
515
- name = data[n].name
516
- extra = ""
517
- if name == "" or name is None:
518
- if self.window.core.config.get("assistant.store.hide_threads"):
496
+ model = models[id]
497
+ hide_threads = bool(self.window.core.config.get("assistant.store.hide_threads"))
498
+ suffix = trans("assistant.store.files.suffix")
499
+
500
+ names = []
501
+ for n, store in data.items():
502
+ num_files = store.get_file_count()
503
+ name = store.name
504
+ extras = []
505
+ if not name:
506
+ if hide_threads:
519
507
  continue
520
- name = data[n].id
521
- if data[n].num_files > 0:
522
- extra += "{} {}".format(data[n].num_files, trans("assistant.store.files.suffix"))
523
- if data[n].usage_bytes > 0:
524
- extra += " - {}".format(self.window.core.filesystem.sizeof_fmt(data[n].usage_bytes))
525
- if extra != "":
526
- name += " ({})".format(extra)
527
- self.window.ui.models[id].insertRow(i)
528
- self.window.ui.models[id].setData(self.window.ui.models[id].index(i, 0), name)
529
- i += 1
508
+ name = store.id
509
+ if num_files > 0:
510
+ extras.append(f"{num_files} {suffix}")
511
+ if store.usage_bytes > 0:
512
+ extras.append(self.window.core.filesystem.sizeof_fmt(store.usage_bytes))
513
+ if extras:
514
+ name = f"{name} ({' - '.join(extras)})"
515
+ names.append(name)
516
+
517
+ model.setRowCount(len(names))
518
+ for i, name in enumerate(names):
519
+ model.setData(model.index(i, 0), name)
520
+
521
+ # ==================== files context menu handler ====================
522
+ def on_files_context_menu(self, pos: QPoint):
523
+ """
524
+ Context menu for the files list: provides Delete action.
525
+ """
526
+ view_id = 'assistant.store.files.list'
527
+ if view_id not in self.window.ui.nodes:
528
+ return
529
+ view = self.window.ui.nodes[view_id]
530
+ index = view.indexAt(pos)
531
+ if not index.isValid():
532
+ return
533
+
534
+ row = index.row()
535
+ menu = QMenu(view)
536
+ act_delete = QAction(QIcon(":/icons/delete.svg"), trans("assistant.store.menu.file.delete") if hasattr(self.window, 'tr') else "Delete", view)
537
+ act_delete.triggered.connect(lambda r=row: self.window.controller.assistant.store.delete_file_by_idx(r))
538
+ menu.addAction(act_delete)
539
+ menu.exec(view.mapToGlobal(pos))
@@ -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.27 07:00:00 #
9
+ # Updated Date: 2025.09.03 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from functools import partial
@@ -246,7 +246,7 @@ class Input:
246
246
 
247
247
  idx = tabs.currentIndex()
248
248
  if idx == 0:
249
- nodes['input'].setMinimumHeight(self.min_height_input)
249
+ # nodes['input'].setMinimumHeight(self.min_height_input)
250
250
  tabs.setMinimumHeight(self.min_height_input_tab)
251
251
  sizes = controller_ui.splitter_output_size_input
252
252
  if sizes and sizes != [0, 0]:
@@ -255,5 +255,5 @@ class Input:
255
255
  sizes = controller_ui.splitter_output_size_files
256
256
  if sizes and controller_ui.splitter_output_size_input != [0, 0]:
257
257
  splitters['main.output'].setSizes(sizes)
258
- nodes['input'].setMinimumHeight(self.min_height_files_tab)
258
+ # nodes['input'].setMinimumHeight(self.min_height_files_tab)
259
259
  tabs.setMinimumHeight(self.min_height_files_tab + 90)