pygpt-net 2.6.20__py3-none-any.whl → 2.6.22__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 (221) hide show
  1. pygpt_net/CHANGELOG.txt +13 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +3 -1
  4. pygpt_net/controller/__init__.py +4 -8
  5. pygpt_net/controller/access/voice.py +2 -2
  6. pygpt_net/controller/agent/agent.py +130 -2
  7. pygpt_net/controller/agent/experts.py +93 -96
  8. pygpt_net/controller/agent/llama.py +2 -1
  9. pygpt_net/controller/assistant/assistant.py +18 -1
  10. pygpt_net/controller/assistant/batch.py +2 -3
  11. pygpt_net/controller/assistant/editor.py +2 -2
  12. pygpt_net/controller/assistant/files.py +2 -3
  13. pygpt_net/controller/assistant/store.py +2 -2
  14. pygpt_net/controller/attachment/attachment.py +17 -1
  15. pygpt_net/controller/audio/audio.py +2 -2
  16. pygpt_net/controller/camera/camera.py +15 -7
  17. pygpt_net/controller/chat/chat.py +2 -2
  18. pygpt_net/controller/chat/common.py +50 -33
  19. pygpt_net/controller/chat/image.py +67 -77
  20. pygpt_net/controller/chat/input.py +94 -166
  21. pygpt_net/controller/chat/output.py +83 -140
  22. pygpt_net/controller/chat/response.py +83 -102
  23. pygpt_net/controller/chat/text.py +116 -149
  24. pygpt_net/controller/ctx/common.py +2 -1
  25. pygpt_net/controller/ctx/ctx.py +87 -6
  26. pygpt_net/controller/files/files.py +13 -1
  27. pygpt_net/controller/idx/idx.py +26 -2
  28. pygpt_net/controller/idx/indexer.py +85 -76
  29. pygpt_net/controller/kernel/reply.py +53 -66
  30. pygpt_net/controller/kernel/stack.py +16 -16
  31. pygpt_net/controller/lang/lang.py +52 -34
  32. pygpt_net/controller/model/importer.py +3 -2
  33. pygpt_net/controller/model/model.py +62 -3
  34. pygpt_net/controller/notepad/notepad.py +86 -84
  35. pygpt_net/controller/plugins/settings.py +3 -4
  36. pygpt_net/controller/settings/editor.py +4 -4
  37. pygpt_net/controller/settings/profile.py +105 -124
  38. pygpt_net/controller/theme/menu.py +154 -57
  39. pygpt_net/controller/theme/nodes.py +51 -44
  40. pygpt_net/controller/theme/theme.py +33 -9
  41. pygpt_net/controller/tools/tools.py +2 -2
  42. pygpt_net/controller/ui/tabs.py +2 -3
  43. pygpt_net/controller/ui/ui.py +16 -2
  44. pygpt_net/core/agents/observer/evaluation.py +3 -3
  45. pygpt_net/core/agents/provider.py +25 -3
  46. pygpt_net/core/agents/runner.py +4 -1
  47. pygpt_net/core/agents/runners/llama_workflow.py +19 -7
  48. pygpt_net/core/agents/runners/loop.py +3 -1
  49. pygpt_net/core/agents/runners/openai_workflow.py +17 -3
  50. pygpt_net/core/agents/tools.py +4 -1
  51. pygpt_net/core/bridge/context.py +34 -37
  52. pygpt_net/core/ctx/container.py +13 -12
  53. pygpt_net/core/ctx/ctx.py +1 -1
  54. pygpt_net/core/ctx/output.py +7 -4
  55. pygpt_net/core/db/database.py +2 -2
  56. pygpt_net/core/debug/console/console.py +2 -2
  57. pygpt_net/core/debug/debug.py +12 -1
  58. pygpt_net/core/dispatcher/dispatcher.py +24 -1
  59. pygpt_net/core/events/app.py +7 -7
  60. pygpt_net/core/events/control.py +26 -26
  61. pygpt_net/core/events/event.py +6 -3
  62. pygpt_net/core/events/kernel.py +2 -2
  63. pygpt_net/core/events/render.py +13 -13
  64. pygpt_net/core/experts/experts.py +76 -82
  65. pygpt_net/core/experts/worker.py +12 -12
  66. pygpt_net/core/filesystem/actions.py +1 -2
  67. pygpt_net/core/models/models.py +5 -1
  68. pygpt_net/core/models/ollama.py +14 -5
  69. pygpt_net/core/render/plain/helpers.py +2 -5
  70. pygpt_net/core/render/plain/renderer.py +26 -30
  71. pygpt_net/core/render/web/body.py +1 -1
  72. pygpt_net/core/render/web/helpers.py +2 -2
  73. pygpt_net/core/render/web/renderer.py +4 -4
  74. pygpt_net/core/settings/settings.py +43 -13
  75. pygpt_net/core/tabs/tabs.py +20 -13
  76. pygpt_net/core/types/__init__.py +2 -1
  77. pygpt_net/core/types/agent.py +4 -4
  78. pygpt_net/core/types/base.py +19 -0
  79. pygpt_net/core/types/console.py +6 -6
  80. pygpt_net/core/types/mode.py +8 -8
  81. pygpt_net/core/types/multimodal.py +3 -3
  82. pygpt_net/core/types/openai.py +2 -1
  83. pygpt_net/data/config/config.json +5 -5
  84. pygpt_net/data/config/models.json +19 -3
  85. pygpt_net/data/config/settings.json +14 -14
  86. pygpt_net/data/locale/locale.de.ini +4 -1
  87. pygpt_net/data/locale/locale.en.ini +6 -3
  88. pygpt_net/data/locale/locale.es.ini +4 -1
  89. pygpt_net/data/locale/locale.fr.ini +4 -1
  90. pygpt_net/data/locale/locale.it.ini +4 -1
  91. pygpt_net/data/locale/locale.pl.ini +5 -4
  92. pygpt_net/data/locale/locale.uk.ini +4 -1
  93. pygpt_net/data/locale/locale.zh.ini +4 -1
  94. pygpt_net/item/ctx.py +256 -240
  95. pygpt_net/item/model.py +59 -116
  96. pygpt_net/item/preset.py +122 -105
  97. pygpt_net/plugin/twitter/plugin.py +2 -2
  98. pygpt_net/provider/agents/llama_index/workflow/planner.py +3 -3
  99. pygpt_net/provider/agents/openai/agent.py +4 -12
  100. pygpt_net/provider/agents/openai/agent_b2b.py +10 -15
  101. pygpt_net/provider/agents/openai/agent_planner.py +4 -4
  102. pygpt_net/provider/agents/openai/agent_with_experts.py +3 -7
  103. pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +4 -8
  104. pygpt_net/provider/agents/openai/agent_with_feedback.py +4 -8
  105. pygpt_net/provider/agents/openai/bot_researcher.py +2 -18
  106. pygpt_net/provider/agents/openai/bots/__init__.py +0 -0
  107. pygpt_net/provider/agents/openai/bots/research_bot/__init__.py +0 -0
  108. pygpt_net/provider/agents/openai/bots/research_bot/agents/__init__.py +0 -0
  109. pygpt_net/provider/agents/openai/bots/research_bot/agents/planner_agent.py +1 -1
  110. pygpt_net/provider/agents/openai/bots/research_bot/agents/search_agent.py +1 -0
  111. pygpt_net/provider/agents/openai/bots/research_bot/agents/writer_agent.py +1 -1
  112. pygpt_net/provider/agents/openai/bots/research_bot/manager.py +1 -10
  113. pygpt_net/provider/agents/openai/evolve.py +5 -9
  114. pygpt_net/provider/agents/openai/supervisor.py +4 -8
  115. pygpt_net/provider/core/config/patch.py +10 -3
  116. pygpt_net/provider/core/ctx/db_sqlite/utils.py +43 -43
  117. pygpt_net/provider/core/model/patch.py +11 -1
  118. pygpt_net/provider/core/preset/json_file.py +47 -49
  119. pygpt_net/provider/gpt/agents/experts.py +2 -2
  120. pygpt_net/tools/audio_transcriber/ui/dialogs.py +44 -54
  121. pygpt_net/tools/code_interpreter/body.py +1 -2
  122. pygpt_net/tools/code_interpreter/tool.py +7 -4
  123. pygpt_net/tools/code_interpreter/ui/html.py +1 -3
  124. pygpt_net/tools/code_interpreter/ui/widgets.py +2 -3
  125. pygpt_net/tools/html_canvas/ui/widgets.py +1 -3
  126. pygpt_net/tools/image_viewer/ui/dialogs.py +40 -37
  127. pygpt_net/tools/indexer/ui/widgets.py +2 -4
  128. pygpt_net/tools/media_player/tool.py +2 -5
  129. pygpt_net/tools/media_player/ui/widgets.py +60 -36
  130. pygpt_net/tools/text_editor/ui/widgets.py +18 -19
  131. pygpt_net/tools/translator/ui/widgets.py +39 -35
  132. pygpt_net/ui/base/context_menu.py +9 -4
  133. pygpt_net/ui/dialog/db.py +1 -3
  134. pygpt_net/ui/dialog/models.py +1 -3
  135. pygpt_net/ui/dialog/models_importer.py +2 -4
  136. pygpt_net/ui/dialogs.py +34 -30
  137. pygpt_net/ui/layout/chat/attachments.py +72 -84
  138. pygpt_net/ui/layout/chat/attachments_ctx.py +40 -44
  139. pygpt_net/ui/layout/chat/attachments_uploaded.py +36 -39
  140. pygpt_net/ui/layout/chat/calendar.py +100 -70
  141. pygpt_net/ui/layout/chat/chat.py +23 -17
  142. pygpt_net/ui/layout/chat/input.py +95 -118
  143. pygpt_net/ui/layout/chat/output.py +100 -162
  144. pygpt_net/ui/layout/chat/painter.py +89 -61
  145. pygpt_net/ui/layout/ctx/ctx_list.py +43 -52
  146. pygpt_net/ui/layout/status.py +23 -14
  147. pygpt_net/ui/layout/toolbox/agent.py +27 -38
  148. pygpt_net/ui/layout/toolbox/agent_llama.py +42 -45
  149. pygpt_net/ui/layout/toolbox/assistants.py +42 -38
  150. pygpt_net/ui/layout/toolbox/computer_env.py +32 -23
  151. pygpt_net/ui/layout/toolbox/footer.py +13 -16
  152. pygpt_net/ui/layout/toolbox/image.py +18 -21
  153. pygpt_net/ui/layout/toolbox/indexes.py +46 -89
  154. pygpt_net/ui/layout/toolbox/mode.py +20 -7
  155. pygpt_net/ui/layout/toolbox/model.py +12 -10
  156. pygpt_net/ui/layout/toolbox/presets.py +68 -52
  157. pygpt_net/ui/layout/toolbox/prompt.py +31 -58
  158. pygpt_net/ui/layout/toolbox/toolbox.py +25 -21
  159. pygpt_net/ui/layout/toolbox/vision.py +20 -22
  160. pygpt_net/ui/main.py +2 -4
  161. pygpt_net/ui/menu/about.py +64 -84
  162. pygpt_net/ui/menu/audio.py +87 -63
  163. pygpt_net/ui/menu/config.py +121 -127
  164. pygpt_net/ui/menu/debug.py +69 -76
  165. pygpt_net/ui/menu/file.py +32 -35
  166. pygpt_net/ui/menu/menu.py +2 -3
  167. pygpt_net/ui/menu/plugins.py +69 -33
  168. pygpt_net/ui/menu/theme.py +45 -46
  169. pygpt_net/ui/menu/tools.py +56 -60
  170. pygpt_net/ui/menu/video.py +20 -25
  171. pygpt_net/ui/tray.py +1 -2
  172. pygpt_net/ui/widget/audio/bar.py +1 -3
  173. pygpt_net/ui/widget/audio/input_button.py +3 -4
  174. pygpt_net/ui/widget/calendar/select.py +1 -2
  175. pygpt_net/ui/widget/dialog/base.py +12 -9
  176. pygpt_net/ui/widget/dialog/editor_file.py +20 -23
  177. pygpt_net/ui/widget/dialog/find.py +25 -24
  178. pygpt_net/ui/widget/dialog/profile.py +57 -53
  179. pygpt_net/ui/widget/draw/painter.py +62 -93
  180. pygpt_net/ui/widget/element/button.py +42 -30
  181. pygpt_net/ui/widget/element/checkbox.py +23 -15
  182. pygpt_net/ui/widget/element/group.py +6 -5
  183. pygpt_net/ui/widget/element/labels.py +1 -2
  184. pygpt_net/ui/widget/filesystem/explorer.py +93 -102
  185. pygpt_net/ui/widget/image/display.py +1 -2
  186. pygpt_net/ui/widget/lists/assistant.py +1 -2
  187. pygpt_net/ui/widget/lists/attachment.py +1 -2
  188. pygpt_net/ui/widget/lists/attachment_ctx.py +1 -2
  189. pygpt_net/ui/widget/lists/context.py +2 -4
  190. pygpt_net/ui/widget/lists/index.py +1 -2
  191. pygpt_net/ui/widget/lists/model.py +1 -2
  192. pygpt_net/ui/widget/lists/model_editor.py +1 -2
  193. pygpt_net/ui/widget/lists/model_importer.py +1 -2
  194. pygpt_net/ui/widget/lists/preset.py +1 -2
  195. pygpt_net/ui/widget/lists/preset_plugins.py +1 -2
  196. pygpt_net/ui/widget/lists/profile.py +1 -2
  197. pygpt_net/ui/widget/lists/uploaded.py +1 -2
  198. pygpt_net/ui/widget/option/checkbox.py +2 -4
  199. pygpt_net/ui/widget/option/checkbox_list.py +1 -4
  200. pygpt_net/ui/widget/option/cmd.py +1 -4
  201. pygpt_net/ui/widget/option/dictionary.py +25 -28
  202. pygpt_net/ui/widget/option/input.py +1 -3
  203. pygpt_net/ui/widget/tabs/Input.py +16 -12
  204. pygpt_net/ui/widget/tabs/body.py +5 -3
  205. pygpt_net/ui/widget/tabs/layout.py +36 -25
  206. pygpt_net/ui/widget/tabs/output.py +96 -74
  207. pygpt_net/ui/widget/textarea/calendar_note.py +1 -2
  208. pygpt_net/ui/widget/textarea/editor.py +41 -73
  209. pygpt_net/ui/widget/textarea/find.py +11 -10
  210. pygpt_net/ui/widget/textarea/html.py +3 -6
  211. pygpt_net/ui/widget/textarea/input.py +63 -64
  212. pygpt_net/ui/widget/textarea/notepad.py +54 -38
  213. pygpt_net/ui/widget/textarea/output.py +65 -54
  214. pygpt_net/ui/widget/textarea/search_input.py +5 -4
  215. pygpt_net/ui/widget/textarea/web.py +2 -4
  216. pygpt_net/ui/widget/vision/camera.py +2 -31
  217. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/METADATA +25 -154
  218. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/RECORD +218 -217
  219. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/LICENSE +0 -0
  220. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/WHEEL +0 -0
  221. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/entry_points.txt +0 -0
pygpt_net/ui/dialogs.py CHANGED
@@ -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.01 03:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import threading
@@ -129,11 +129,12 @@ class Dialogs:
129
129
  :param msg: message to show
130
130
  :param parent_object: parent object
131
131
  """
132
- self.window.ui.dialog['confirm'].type = type
133
- self.window.ui.dialog['confirm'].id = id
134
- self.window.ui.dialog['confirm'].message.setText(msg)
135
- self.window.ui.dialog['confirm'].parent_object = parent_object
136
- self.window.ui.dialog['confirm'].show()
132
+ confirm = self.window.ui.dialog.get('confirm')
133
+ confirm.type = type
134
+ confirm.id = id
135
+ confirm.message.setText(msg)
136
+ confirm.parent_object = parent_object
137
+ confirm.show()
137
138
 
138
139
  def alert(self, msg: any):
139
140
  """
@@ -159,9 +160,10 @@ class Dialogs:
159
160
  """
160
161
  if id not in self.window.ui.dialog:
161
162
  return
162
- self.window.ui.dialog[id].resize(width, height)
163
- self.window.ui.dialog[id].data_id = data_id
164
- self.window.ui.dialog[id].show()
163
+ dialog = self.window.ui.dialog[id]
164
+ dialog.resize(width, height)
165
+ dialog.data_id = data_id
166
+ dialog.show()
165
167
 
166
168
  def open_dictionary_editor(
167
169
  self,
@@ -187,10 +189,11 @@ class Dialogs:
187
189
  print("Dialog not found: " + dialog_id)
188
190
  return
189
191
  self.window.controller.config.dictionary.append_editor(id, option, data)
190
- self.window.ui.dialog[dialog_id].resize(width, height)
191
- self.window.ui.dialog[dialog_id].data = data # store editing data
192
- self.window.ui.dialog[dialog_id].idx = idx # store editing record idx
193
- self.window.ui.dialog[dialog_id].show()
192
+ dialog = self.window.ui.dialog[dialog_id]
193
+ dialog.resize(width, height)
194
+ dialog.data = data # store editing data
195
+ dialog.idx = idx # store editing record idx
196
+ dialog.show()
194
197
 
195
198
  def register_dictionary(
196
199
  self,
@@ -205,8 +208,7 @@ class Dialogs:
205
208
  :param parent: parent id
206
209
  :param option: dictionary option keys
207
210
  """
208
- dict_id = parent + "." + key
209
- self.dictionary.register(dict_id, key, parent, option)
211
+ self.dictionary.register(f"{parent}.{key}", key, parent, option)
210
212
 
211
213
  def open(
212
214
  self,
@@ -223,18 +225,19 @@ class Dialogs:
223
225
  """
224
226
  if id not in self.window.ui.dialog:
225
227
  return
226
- self.window.ui.dialog[id].resize(width, height)
227
- self.window.ui.dialog[id].setSizeGripEnabled(True)
228
- self.window.ui.dialog[id].setWindowFlags(
229
- self.window.ui.dialog[id].windowFlags() | Qt.WindowMaximizeButtonHint
228
+ dialog = self.window.ui.dialog[id]
229
+ dialog.resize(width, height)
230
+ dialog.setSizeGripEnabled(True)
231
+ dialog.setWindowFlags(
232
+ dialog.windowFlags() | Qt.WindowMaximizeButtonHint
230
233
  )
231
- qr = self.window.ui.dialog[id].frameGeometry()
234
+ qr = dialog.frameGeometry()
232
235
  cp = self.window.screen().availableGeometry().center()
233
236
  qr.moveCenter(cp)
234
- self.window.ui.dialog[id].move(qr.topLeft())
235
- self.window.ui.dialog[id].show()
236
- self.window.ui.dialog[id].activateWindow()
237
- self.window.ui.dialog[id].setFocus()
237
+ dialog.move(qr.topLeft())
238
+ dialog.show()
239
+ dialog.activateWindow()
240
+ dialog.setFocus()
238
241
 
239
242
  def open_instance(self,
240
243
  id: str,
@@ -256,14 +259,15 @@ class Dialogs:
256
259
  else:
257
260
  print("Dialog not found: " + id)
258
261
  return
259
- self.window.ui.dialog[id].resize(width, height)
260
- qr = self.window.ui.dialog[id].frameGeometry()
262
+ dialog = self.window.ui.dialog[id]
263
+ dialog.resize(width, height)
264
+ qr = dialog.frameGeometry()
261
265
  cp = self.window.screen().availableGeometry().center()
262
266
  qr.moveCenter(cp)
263
- self.window.ui.dialog[id].move(qr.topLeft())
264
- self.window.ui.dialog[id].show()
265
- self.window.ui.dialog[id].activateWindow()
266
- self.window.ui.dialog[id].setFocus()
267
+ dialog.move(qr.topLeft())
268
+ dialog.show()
269
+ dialog.activateWindow()
270
+ dialog.setFocus()
267
271
 
268
272
  def close(self, id: str):
269
273
  """
@@ -6,12 +6,14 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.26 02:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
13
+ from functools import partial
13
14
 
14
- from PySide6.QtGui import QStandardItemModel, Qt, QIcon
15
+ from PySide6.QtGui import QStandardItemModel, QIcon
16
+ from PySide6.QtCore import Qt
15
17
  from PySide6.QtWidgets import QVBoxLayout, QPushButton, QHBoxLayout, QCheckBox, QWidget
16
18
 
17
19
  from pygpt_net.item.attachment import AttachmentItem
@@ -19,8 +21,6 @@ from pygpt_net.ui.widget.element.labels import HelpLabel
19
21
  from pygpt_net.ui.widget.lists.attachment import AttachmentList
20
22
  from pygpt_net.utils import trans
21
23
 
22
- import pygpt_net.icons_rc
23
-
24
24
  class Attachments:
25
25
  def __init__(self, window=None):
26
26
  """
@@ -40,17 +40,16 @@ class Attachments:
40
40
  self.setup_attachments()
41
41
  self.setup_buttons()
42
42
 
43
- empty_widget = QWidget()
44
- self.window.ui.nodes['input.attachments.options.label'] = HelpLabel(trans("attachments.options.label"))
43
+ empty_widget = QWidget(self.window)
44
+ self.window.ui.nodes['input.attachments.options.label'] = HelpLabel(trans("attachments.options.label"), self.window)
45
45
 
46
- # buttons layout
47
46
  buttons = QHBoxLayout()
48
- buttons.addWidget(self.window.ui.nodes['attachments.btn.add'])
49
- buttons.addWidget(self.window.ui.nodes['attachments.btn.add_url'])
50
- buttons.addWidget(self.window.ui.nodes['attachments.btn.clear'])
47
+ nodes = self.window.ui.nodes
48
+ buttons.addWidget(nodes['attachments.btn.add'])
49
+ buttons.addWidget(nodes['attachments.btn.add_url'])
50
+ buttons.addWidget(nodes['attachments.btn.clear'])
51
51
  buttons.addWidget(empty_widget)
52
- buttons.addWidget(self.window.ui.nodes['input.attachments.options.label'])
53
-
52
+ buttons.addWidget(nodes['input.attachments.options.label'])
54
53
  buttons.addWidget(self.setup_auto_index())
55
54
  buttons.addWidget(self.setup_send_clear())
56
55
  buttons.addWidget(self.setup_capture_clear())
@@ -58,30 +57,29 @@ class Attachments:
58
57
 
59
58
  self.window.ui.nodes['tip.input.attachments'] = HelpLabel(trans('tip.input.attachments'), self.window)
60
59
 
61
- # layout
62
60
  layout = QVBoxLayout()
63
61
  layout.addWidget(self.window.ui.nodes['tip.input.attachments'])
64
62
  layout.addWidget(self.window.ui.nodes['attachments'])
65
-
66
63
  layout.addLayout(buttons)
67
64
 
68
65
  return layout
69
66
 
67
+ def _centered_container(self, child: QWidget) -> QWidget:
68
+ layout = QHBoxLayout()
69
+ layout.setContentsMargins(0, 0, 0, 0)
70
+ layout.setAlignment(Qt.AlignCenter)
71
+ layout.addWidget(child)
72
+ widget = QWidget(self.window)
73
+ widget.setLayout(layout)
74
+ return widget
75
+
70
76
  def setup_send_clear(self) -> QWidget:
71
77
  """
72
78
  Setup send clear checkbox
73
79
 
74
80
  :return: QWidget
75
81
  """
76
- layout = QHBoxLayout()
77
- layout.setContentsMargins(0, 0, 0, 0)
78
- layout.setAlignment(Qt.AlignCenter)
79
- layout.addWidget(self.window.ui.nodes['attachments.send_clear'])
80
-
81
- widget = QWidget()
82
- widget.setLayout(layout)
83
-
84
- return widget
82
+ return self._centered_container(self.window.ui.nodes['attachments.send_clear'])
85
83
 
86
84
  def setup_capture_clear(self) -> QWidget:
87
85
  """
@@ -89,15 +87,7 @@ class Attachments:
89
87
 
90
88
  :return: QWidget
91
89
  """
92
- layout = QHBoxLayout()
93
- layout.setContentsMargins(0, 0, 0, 0)
94
- layout.setAlignment(Qt.AlignCenter)
95
- layout.addWidget(self.window.ui.nodes['attachments.capture_clear'])
96
-
97
- widget = QWidget()
98
- widget.setLayout(layout)
99
-
100
- return widget
90
+ return self._centered_container(self.window.ui.nodes['attachments.capture_clear'])
101
91
 
102
92
  def setup_auto_index(self) -> QWidget:
103
93
  """
@@ -105,45 +95,34 @@ class Attachments:
105
95
 
106
96
  :return: QWidget
107
97
  """
108
- layout = QHBoxLayout()
109
- layout.setContentsMargins(0, 0, 0, 0)
110
- layout.setAlignment(Qt.AlignCenter)
111
- layout.addWidget(self.window.ui.nodes['attachments.auto_index'])
112
-
113
- widget = QWidget()
114
- widget.setLayout(layout)
115
-
116
- return widget
98
+ return self._centered_container(self.window.ui.nodes['attachments.auto_index'])
117
99
 
118
100
  def setup_buttons(self):
119
101
  """
120
102
  Setup buttons
121
103
  """
122
- self.window.ui.nodes['attachments.btn.add'] = QPushButton(QIcon(":/icons/add.svg"), trans('attachments.btn.add'))
123
- self.window.ui.nodes['attachments.btn.add_url'] = QPushButton(QIcon(":/icons/add.svg"), trans('attachments.btn.add_url'))
124
- self.window.ui.nodes['attachments.btn.clear'] = QPushButton(QIcon(":/icons/close.svg"), trans('attachments.btn.clear'))
104
+ nodes = self.window.ui.nodes
105
+ ctrl = self.window.controller.attachment
106
+
107
+ icon_add = QIcon(":/icons/add.svg")
108
+ icon_close = QIcon(":/icons/close.svg")
125
109
 
126
- self.window.ui.nodes['attachments.btn.add'].clicked.connect(
127
- lambda: self.window.controller.attachment.open_add())
128
- self.window.ui.nodes['attachments.btn.add_url'].clicked.connect(
129
- lambda: self.window.controller.attachment.open_add_url())
130
- self.window.ui.nodes['attachments.btn.clear'].clicked.connect(
131
- lambda: self.window.controller.attachment.clear(remove_local=True))
110
+ nodes['attachments.btn.add'] = QPushButton(icon_add, trans('attachments.btn.add'), self.window)
111
+ nodes['attachments.btn.add_url'] = QPushButton(icon_add, trans('attachments.btn.add_url'), self.window)
112
+ nodes['attachments.btn.clear'] = QPushButton(icon_close, trans('attachments.btn.clear'), self.window)
132
113
 
133
- self.window.ui.nodes['attachments.send_clear'] = QCheckBox(trans('attachments.send_clear'))
134
- self.window.ui.nodes['attachments.send_clear'].stateChanged.connect(
135
- lambda: self.window.controller.attachment.toggle_send_clear(
136
- self.window.ui.nodes['attachments.send_clear'].isChecked()))
114
+ nodes['attachments.btn.add'].clicked.connect(ctrl.open_add)
115
+ nodes['attachments.btn.add_url'].clicked.connect(ctrl.open_add_url)
116
+ nodes['attachments.btn.clear'].clicked.connect(partial(ctrl.clear, remove_local=True))
137
117
 
138
- self.window.ui.nodes['attachments.capture_clear'] = QCheckBox(trans('attachments.capture_clear'))
139
- self.window.ui.nodes['attachments.capture_clear'].stateChanged.connect(
140
- lambda: self.window.controller.attachment.toggle_capture_clear(
141
- self.window.ui.nodes['attachments.capture_clear'].isChecked()))
118
+ nodes['attachments.send_clear'] = QCheckBox(trans('attachments.send_clear'), self.window)
119
+ nodes['attachments.send_clear'].toggled.connect(ctrl.toggle_send_clear)
142
120
 
143
- self.window.ui.nodes['attachments.auto_index'] = QCheckBox(trans('attachments.auto_index'))
144
- self.window.ui.nodes['attachments.auto_index'].stateChanged.connect(
145
- lambda: self.window.controller.attachment.toggle_auto_index(
146
- self.window.ui.nodes['attachments.auto_index'].isChecked()))
121
+ nodes['attachments.capture_clear'] = QCheckBox(trans('attachments.capture_clear'), self.window)
122
+ nodes['attachments.capture_clear'].toggled.connect(ctrl.toggle_capture_clear)
123
+
124
+ nodes['attachments.auto_index'] = QCheckBox(trans('attachments.auto_index'), self.window)
125
+ nodes['attachments.auto_index'].toggled.connect(ctrl.toggle_auto_index)
147
126
 
148
127
  def setup_attachments(self):
149
128
  """
@@ -161,11 +140,12 @@ class Attachments:
161
140
  :return: QStandardItemModel
162
141
  """
163
142
  model = QStandardItemModel(0, 4, parent)
164
- model.setHeaderData(0, Qt.Horizontal, trans('attachments.header.name'))
165
- model.setHeaderData(1, Qt.Horizontal, trans('attachments.header.path'))
166
- model.setHeaderData(2, Qt.Horizontal, trans('attachments.header.size'))
167
- model.setHeaderData(3, Qt.Horizontal, trans('attachments.header.ctx'))
168
-
143
+ model.setHorizontalHeaderLabels([
144
+ trans('attachments.header.name'),
145
+ trans('attachments.header.path'),
146
+ trans('attachments.header.size'),
147
+ trans('attachments.header.ctx'),
148
+ ])
169
149
  return model
170
150
 
171
151
  def update(self, data):
@@ -174,20 +154,28 @@ class Attachments:
174
154
 
175
155
  :param data: Data to update
176
156
  """
177
- self.window.ui.models[self.id].removeRows(0, self.window.ui.models[self.id].rowCount())
178
- i = 0
179
- for id in data:
180
- path = data[id].path
181
- size = ""
182
- if data[id].type == AttachmentItem.TYPE_FILE:
183
- if path and os.path.exists(path):
184
- size = self.window.core.filesystem.sizeof_fmt(os.path.getsize(path))
185
- ctx_str = ""
186
- if data[id].ctx:
187
- ctx_str = "YES"
188
- self.window.ui.models[self.id].insertRow(i)
189
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 0), data[id].name)
190
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 1),path)
191
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 2), size)
192
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 3), ctx_str)
193
- i += 1
157
+ model = self.window.ui.models[self.id]
158
+ rows = len(data)
159
+ model.setRowCount(rows)
160
+
161
+ exists = os.path.exists
162
+ getsize = os.path.getsize
163
+ sizeof_fmt = self.window.core.filesystem.sizeof_fmt
164
+
165
+ model.beginResetModel()
166
+ for i, (_, item) in enumerate(data.items()):
167
+ path = item.path
168
+ if item.type == AttachmentItem.TYPE_FILE and path and exists(path):
169
+ size = sizeof_fmt(getsize(path))
170
+ else:
171
+ size = ""
172
+ ctx_str = "YES" if item.ctx else ""
173
+
174
+ model.setData(model.index(i, 0), item.name)
175
+ model.setData(model.index(i, 1), path)
176
+ model.setData(model.index(i, 2), size)
177
+ model.setData(model.index(i, 3), ctx_str)
178
+ model.endResetModel()
179
+
180
+ if rows:
181
+ model.dataChanged.emit(model.index(0, 0), model.index(rows - 1, 3), [])
@@ -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: 2024.11.26 02:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -19,8 +19,6 @@ from pygpt_net.ui.widget.element.labels import HelpLabel
19
19
  from pygpt_net.ui.widget.lists.attachment_ctx import AttachmentCtxList
20
20
  from pygpt_net.utils import trans
21
21
 
22
- import pygpt_net.icons_rc
23
-
24
22
  class AttachmentsCtx:
25
23
  def __init__(self, window=None):
26
24
  """
@@ -65,11 +63,9 @@ class AttachmentsCtx:
65
63
  self.window.controller.chat.attachment.MODE_DISABLED
66
64
  ))
67
65
 
68
- # buttons layout
69
66
  buttons_layout = QHBoxLayout()
70
67
  buttons_layout.addWidget(self.window.ui.nodes['attachments_ctx.btn.clear'])
71
68
  buttons_layout.addWidget(empty_widget)
72
- # buttons_layout.addStretch()
73
69
  buttons_layout.addWidget(self.window.ui.nodes['input.attachments.ctx.mode.label'])
74
70
  buttons_layout.addWidget(self.window.ui.nodes['input.attachments.ctx.mode.full'])
75
71
  buttons_layout.addWidget(self.window.ui.nodes['input.attachments.ctx.mode.query'])
@@ -77,7 +73,6 @@ class AttachmentsCtx:
77
73
  buttons_layout.addWidget(self.window.ui.nodes['input.attachments.ctx.mode.off'])
78
74
  buttons_layout.addStretch()
79
75
 
80
- # layout
81
76
  layout = QVBoxLayout()
82
77
  layout.addWidget(self.window.ui.nodes['tip.input.attachments.ctx'])
83
78
  layout.addWidget(self.window.ui.nodes['attachments_ctx'])
@@ -87,16 +82,14 @@ class AttachmentsCtx:
87
82
 
88
83
  def setup_attachments(self):
89
84
  """Setup attachments uploaded list"""
90
- # attachments
91
85
  self.window.ui.nodes[self.id] = AttachmentCtxList(self.window)
92
86
 
93
- # buttons
94
87
  self.window.ui.nodes['attachments_ctx.btn.clear'] = QPushButton(QIcon(":/icons/close.svg"), trans('attachments_uploaded.btn.clear'))
95
88
  self.window.ui.nodes['attachments_ctx.btn.clear'].clicked.connect(
96
89
  lambda: self.window.controller.chat.attachment.clear()
97
90
  )
98
91
 
99
- self.window.ui.models[self.id] = self.create_model(self.window)
92
+ self.window.ui.models[self.id] = self.create_model(self.window.ui.nodes[self.id])
100
93
  self.window.ui.nodes[self.id].setModel(self.window.ui.models[self.id])
101
94
 
102
95
  def create_model(self, parent) -> QStandardItemModel:
@@ -120,43 +113,46 @@ class AttachmentsCtx:
120
113
 
121
114
  :param data: Data to update
122
115
  """
123
- self.window.ui.models[self.id].removeRows(0, self.window.ui.models[self.id].rowCount())
124
- i = 0
125
- for item in data:
126
- indexed = False
127
- name = "No name"
128
- if 'name' in item:
129
- name = item['name']
130
- size = "-"
131
- path = "No path"
132
- if 'path' in item:
133
- path = item['path']
134
- uuid = ""
135
- if 'uuid' in item:
136
- uuid = item['uuid']
116
+ model = self.window.ui.models[self.id]
117
+ row_count = len(data)
118
+ model.beginResetModel()
119
+ model.setRowCount(row_count)
120
+
121
+ m_index = model.index
122
+ m_setData = model.setData
123
+ tooltip_role = QtCore.Qt.ToolTipRole
124
+ trans_indexed = trans("attachments.ctx.indexed")
125
+ sizeof_fmt = self.window.core.filesystem.sizeof_fmt
126
+ stat = os.stat
127
+
128
+ for i, item in enumerate(data):
129
+ name = item.get('name', "No name")
130
+ path = item.get('path', "No path")
131
+ uuid = item.get('uuid', "")
137
132
  length = "-"
138
133
  if 'length' in item:
139
134
  length = str(item['length'])
140
135
  if 'tokens' in item:
141
136
  length += ' / ~' + str(item['tokens'])
142
- if 'indexed' in item and item['indexed']:
143
- indexed = True
144
-
145
- idx_str = ""
146
- if indexed:
147
- idx_str = trans("attachments.ctx.indexed")
148
-
149
- if os.path.exists(path):
150
- size = self.window.core.filesystem.sizeof_fmt(os.path.getsize(path))
151
- elif 'size' in item:
152
- size = self.window.core.filesystem.sizeof_fmt(item['size'])
153
-
154
- self.window.ui.models[self.id].insertRow(i)
155
- index = self.window.ui.models[self.id].index(i, 0)
156
- self.window.ui.models[self.id].setData(index, "uuid: " + str(uuid), QtCore.Qt.ToolTipRole)
157
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 0), name)
158
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 1), path)
159
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 2), size)
160
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 3), length)
161
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 4), idx_str)
162
- i += 1
137
+ idx_str = trans_indexed if item.get('indexed') else ""
138
+
139
+ size = "-"
140
+ if isinstance(path, str):
141
+ try:
142
+ st = stat(path)
143
+ except (OSError, ValueError, TypeError):
144
+ pass
145
+ else:
146
+ size = sizeof_fmt(st.st_size)
147
+ if size == "-" and 'size' in item:
148
+ size = sizeof_fmt(item['size'])
149
+
150
+ idx0 = m_index(i, 0)
151
+ m_setData(idx0, "uuid: " + str(uuid), tooltip_role)
152
+ m_setData(idx0, name)
153
+ m_setData(m_index(i, 1), path)
154
+ m_setData(m_index(i, 2), size)
155
+ m_setData(m_index(i, 3), length)
156
+ m_setData(m_index(i, 4), idx_str)
157
+
158
+ model.endResetModel()
@@ -6,22 +6,20 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.04.29 16:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
13
13
 
14
14
  from PySide6 import QtCore
15
15
  from PySide6.QtGui import QStandardItemModel, Qt, QIcon
16
- from PySide6.QtWidgets import QVBoxLayout, QPushButton, QHBoxLayout, QCheckBox, QLabel, QWidget
16
+ from PySide6.QtWidgets import QVBoxLayout, QPushButton, QHBoxLayout, QWidget
17
17
 
18
18
  from pygpt_net.ui.widget.element.button import SyncButton
19
19
  from pygpt_net.ui.widget.element.labels import HelpLabel
20
20
  from pygpt_net.ui.widget.lists.uploaded import UploadedFileList
21
21
  from pygpt_net.utils import trans
22
22
 
23
- import pygpt_net.icons_rc
24
-
25
23
  class AttachmentsUploaded:
26
24
  def __init__(self, window=None):
27
25
  """
@@ -39,16 +37,15 @@ class AttachmentsUploaded:
39
37
  :return: QVBoxLayout
40
38
  """
41
39
  self.setup_attachments()
42
- empty_widget = QWidget()
40
+ empty_widget = QWidget(self.window)
43
41
 
44
- self.window.ui.nodes['attachments_uploaded.sync.tip'] = HelpLabel(trans('attachments_uploaded.sync.tip'))
42
+ self.window.ui.nodes['attachments_uploaded.sync.tip'] = HelpLabel(trans('attachments_uploaded.sync.tip'), self.window)
45
43
  self.window.ui.nodes['attachments_uploaded.sync.tip'].setWordWrap(False)
46
44
  self.window.ui.nodes['attachments_uploaded.sync.tip'].setAlignment(Qt.AlignCenter)
47
45
 
48
46
  self.window.ui.nodes['tip.input.attachments.uploaded'] = HelpLabel(trans('tip.input.attachments.uploaded'),
49
47
  self.window)
50
48
 
51
- # buttons layout
52
49
  buttons_layout = QHBoxLayout()
53
50
  buttons_layout.addWidget(self.window.ui.nodes['attachments_uploaded.btn.sync'])
54
51
  buttons_layout.addWidget(self.window.ui.nodes['attachments_uploaded.btn.clear'])
@@ -56,8 +53,6 @@ class AttachmentsUploaded:
56
53
  buttons_layout.addWidget(self.window.ui.nodes['attachments_uploaded.sync.tip'])
57
54
  buttons_layout.addStretch()
58
55
 
59
-
60
- # layout
61
56
  layout = QVBoxLayout()
62
57
  layout.addWidget(self.window.ui.nodes['tip.input.attachments.uploaded'])
63
58
  layout.addWidget(self.window.ui.nodes['attachments_uploaded'])
@@ -69,10 +64,8 @@ class AttachmentsUploaded:
69
64
  """
70
65
  Setup attachments uploaded list
71
66
  """
72
- # attachments
73
67
  self.window.ui.nodes[self.id] = UploadedFileList(self.window)
74
68
 
75
- # buttons
76
69
  self.window.ui.nodes['attachments_uploaded.btn.sync'] = SyncButton(trans('attachments_uploaded.btn.sync'), self.window)
77
70
  self.window.ui.nodes['attachments_uploaded.btn.clear'] = QPushButton(QIcon(":/icons/close.svg"), trans('attachments_uploaded.btn.clear'))
78
71
  self.window.ui.nodes['attachments_uploaded.btn.clear'].clicked.connect(
@@ -102,32 +95,36 @@ class AttachmentsUploaded:
102
95
 
103
96
  :param data: Data to update
104
97
  """
98
+ model = self.window.ui.models[self.id]
105
99
  store_names = self.window.core.assistants.store.get_names()
106
- self.window.ui.models[self.id].removeRows(0, self.window.ui.models[self.id].rowCount())
107
- i = 0
108
- for id in data:
109
- item = data[id]
110
- size = "-"
111
- path = item.path
112
-
113
- # size
114
- if item.size is not None:
115
- size = self.window.core.filesystem.sizeof_fmt(item.size)
116
- else:
117
- if path and os.path.exists(path):
118
- size = self.window.core.filesystem.sizeof_fmt(os.path.getsize(path))
119
-
120
- # vector stores
121
- if item.store_id is not None and item.store_id in store_names:
122
- vector_store = store_names[item.store_id]
123
- else:
124
- vector_store = trans("assistant.store.thread_only")
125
-
126
- self.window.ui.models[self.id].insertRow(i)
127
- index = self.window.ui.models[self.id].index(i, 0)
128
- self.window.ui.models[self.id].setData(index, "file_id: " + str(item.file_id), QtCore.Qt.ToolTipRole)
129
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 0), item.name)
130
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 1), path)
131
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 2), size)
132
- self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 3), vector_store)
133
- i += 1
100
+ thread_only_label = trans("assistant.store.thread_only")
101
+ fs = self.window.core.filesystem
102
+
103
+ model.beginResetModel()
104
+ model.setRowCount(0)
105
+ count = len(data)
106
+ if count:
107
+ model.setRowCount(count)
108
+ for i, item in enumerate(data.values()):
109
+ path = item.path
110
+ if item.size is not None:
111
+ size_str = fs.sizeof_fmt(item.size)
112
+ else:
113
+ size_str = "-"
114
+ if path:
115
+ try:
116
+ st = os.stat(path)
117
+ except OSError:
118
+ pass
119
+ else:
120
+ size_str = fs.sizeof_fmt(st.st_size)
121
+
122
+ store_name = store_names.get(item.store_id, thread_only_label)
123
+
124
+ idx0 = model.index(i, 0)
125
+ model.setData(idx0, item.name)
126
+ model.setData(idx0, f"file_id: {item.file_id}", QtCore.Qt.ToolTipRole)
127
+ model.setData(model.index(i, 1), path)
128
+ model.setData(model.index(i, 2), size_str)
129
+ model.setData(model.index(i, 3), store_name)
130
+ model.endResetModel()