pygpt-net 2.4.41__py3-none-any.whl → 2.4.44__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 (183) hide show
  1. CHANGELOG.md +19 -0
  2. README.md +151 -71
  3. pygpt_net/CHANGELOG.txt +19 -0
  4. pygpt_net/__init__.py +3 -3
  5. pygpt_net/app.py +3 -1
  6. pygpt_net/controller/attachment.py +31 -3
  7. pygpt_net/controller/audio/__init__.py +2 -2
  8. pygpt_net/controller/camera.py +1 -10
  9. pygpt_net/controller/chat/attachment.py +37 -36
  10. pygpt_net/controller/chat/audio.py +2 -2
  11. pygpt_net/controller/config/placeholder.py +20 -4
  12. pygpt_net/controller/idx/common.py +7 -3
  13. pygpt_net/controller/ui/mode.py +16 -21
  14. pygpt_net/core/attachments/__init__.py +7 -2
  15. pygpt_net/core/attachments/context.py +52 -34
  16. pygpt_net/core/audio/__init__.py +4 -1
  17. pygpt_net/core/audio/whisper.py +37 -0
  18. pygpt_net/core/bridge/worker.py +2 -2
  19. pygpt_net/core/db/__init__.py +2 -1
  20. pygpt_net/core/debug/attachments.py +1 -0
  21. pygpt_net/core/debug/events.py +22 -10
  22. pygpt_net/core/debug/tabs.py +6 -3
  23. pygpt_net/core/history.py +3 -2
  24. pygpt_net/core/idx/__init__.py +23 -6
  25. pygpt_net/core/idx/chat.py +15 -5
  26. pygpt_net/core/idx/indexing.py +47 -14
  27. pygpt_net/core/idx/ui/__init__.py +22 -0
  28. pygpt_net/core/idx/ui/loaders.py +217 -0
  29. pygpt_net/core/installer.py +2 -4
  30. pygpt_net/core/models.py +62 -17
  31. pygpt_net/core/modes.py +11 -13
  32. pygpt_net/core/notepad.py +4 -4
  33. pygpt_net/core/plugins.py +27 -16
  34. pygpt_net/core/presets.py +20 -9
  35. pygpt_net/core/profile.py +11 -3
  36. pygpt_net/core/render/web/parser.py +3 -1
  37. pygpt_net/core/settings.py +5 -5
  38. pygpt_net/core/tabs/tab.py +10 -2
  39. pygpt_net/core/tokens.py +8 -6
  40. pygpt_net/core/web/__init__.py +105 -0
  41. pygpt_net/core/{web.py → web/helpers.py} +93 -67
  42. pygpt_net/data/config/config.json +4 -4
  43. pygpt_net/data/config/models.json +3 -3
  44. pygpt_net/data/config/modes.json +3 -3
  45. pygpt_net/data/config/settings.json +5 -5
  46. pygpt_net/data/locale/locale.de.ini +3 -3
  47. pygpt_net/data/locale/locale.en.ini +12 -9
  48. pygpt_net/data/locale/locale.es.ini +3 -3
  49. pygpt_net/data/locale/locale.fr.ini +3 -3
  50. pygpt_net/data/locale/locale.it.ini +3 -3
  51. pygpt_net/data/locale/locale.pl.ini +3 -3
  52. pygpt_net/data/locale/locale.uk.ini +3 -3
  53. pygpt_net/data/locale/locale.zh.ini +3 -3
  54. pygpt_net/data/locale/plugin.cmd_web.en.ini +2 -0
  55. pygpt_net/data/locale/plugin.mailer.en.ini +21 -0
  56. pygpt_net/item/attachment.py +5 -1
  57. pygpt_net/item/ctx.py +111 -3
  58. pygpt_net/migrations/Version20241215110000.py +25 -0
  59. pygpt_net/migrations/__init__.py +3 -1
  60. pygpt_net/plugin/agent/__init__.py +7 -2
  61. pygpt_net/plugin/audio_output/__init__.py +6 -1
  62. pygpt_net/plugin/base/plugin.py +58 -26
  63. pygpt_net/plugin/base/worker.py +20 -17
  64. pygpt_net/plugin/cmd_files/__init__.py +3 -2
  65. pygpt_net/plugin/cmd_history/config.py +2 -2
  66. pygpt_net/plugin/cmd_web/__init__.py +3 -4
  67. pygpt_net/plugin/cmd_web/config.py +71 -3
  68. pygpt_net/plugin/cmd_web/websearch.py +20 -12
  69. pygpt_net/plugin/cmd_web/worker.py +67 -4
  70. pygpt_net/plugin/idx_llama_index/config.py +3 -3
  71. pygpt_net/plugin/mailer/__init__.py +123 -0
  72. pygpt_net/plugin/mailer/config.py +149 -0
  73. pygpt_net/plugin/mailer/runner.py +285 -0
  74. pygpt_net/plugin/mailer/worker.py +123 -0
  75. pygpt_net/provider/agents/base.py +5 -2
  76. pygpt_net/provider/agents/openai.py +4 -2
  77. pygpt_net/provider/agents/openai_assistant.py +4 -2
  78. pygpt_net/provider/agents/planner.py +4 -2
  79. pygpt_net/provider/agents/react.py +4 -2
  80. pygpt_net/provider/audio_output/openai_tts.py +5 -11
  81. pygpt_net/provider/core/assistant/base.py +5 -3
  82. pygpt_net/provider/core/assistant/json_file.py +8 -5
  83. pygpt_net/provider/core/assistant_file/base.py +4 -3
  84. pygpt_net/provider/core/assistant_file/db_sqlite/__init__.py +4 -3
  85. pygpt_net/provider/core/assistant_file/db_sqlite/storage.py +3 -2
  86. pygpt_net/provider/core/assistant_store/base.py +6 -4
  87. pygpt_net/provider/core/assistant_store/db_sqlite/__init__.py +5 -4
  88. pygpt_net/provider/core/assistant_store/db_sqlite/storage.py +5 -3
  89. pygpt_net/provider/core/attachment/base.py +5 -3
  90. pygpt_net/provider/core/attachment/json_file.py +7 -3
  91. pygpt_net/provider/core/calendar/base.py +5 -3
  92. pygpt_net/provider/core/calendar/db_sqlite/__init__.py +6 -5
  93. pygpt_net/provider/core/calendar/db_sqlite/storage.py +5 -4
  94. pygpt_net/provider/core/config/base.py +8 -6
  95. pygpt_net/provider/core/config/json_file.py +9 -7
  96. pygpt_net/provider/core/config/patch.py +6 -0
  97. pygpt_net/provider/core/ctx/base.py +27 -25
  98. pygpt_net/provider/core/ctx/db_sqlite/__init__.py +51 -35
  99. pygpt_net/provider/core/ctx/db_sqlite/storage.py +92 -38
  100. pygpt_net/provider/core/ctx/db_sqlite/utils.py +37 -11
  101. pygpt_net/provider/core/index/base.py +129 -23
  102. pygpt_net/provider/core/index/db_sqlite/__init__.py +130 -23
  103. pygpt_net/provider/core/index/db_sqlite/storage.py +130 -23
  104. pygpt_net/provider/core/index/db_sqlite/utils.py +4 -2
  105. pygpt_net/provider/core/mode/base.py +5 -3
  106. pygpt_net/provider/core/mode/json_file.py +7 -6
  107. pygpt_net/provider/core/model/base.py +6 -4
  108. pygpt_net/provider/core/model/json_file.py +9 -7
  109. pygpt_net/provider/core/notepad/base.py +5 -3
  110. pygpt_net/provider/core/notepad/db_sqlite/__init__.py +5 -4
  111. pygpt_net/provider/core/notepad/db_sqlite/storage.py +4 -3
  112. pygpt_net/provider/core/plugin_preset/base.py +4 -2
  113. pygpt_net/provider/core/plugin_preset/json_file.py +5 -3
  114. pygpt_net/provider/core/preset/base.py +6 -4
  115. pygpt_net/provider/core/preset/json_file.py +9 -9
  116. pygpt_net/provider/core/prompt/base.py +6 -3
  117. pygpt_net/provider/core/prompt/json_file.py +11 -6
  118. pygpt_net/provider/gpt/assistants.py +15 -6
  119. pygpt_net/provider/gpt/audio.py +5 -5
  120. pygpt_net/provider/gpt/chat.py +7 -5
  121. pygpt_net/provider/gpt/completion.py +8 -4
  122. pygpt_net/provider/gpt/image.py +3 -3
  123. pygpt_net/provider/gpt/store.py +46 -12
  124. pygpt_net/provider/gpt/vision.py +16 -11
  125. pygpt_net/provider/llms/anthropic.py +7 -2
  126. pygpt_net/provider/llms/azure_openai.py +26 -5
  127. pygpt_net/provider/llms/base.py +47 -9
  128. pygpt_net/provider/llms/google.py +7 -2
  129. pygpt_net/provider/llms/hugging_face.py +13 -3
  130. pygpt_net/provider/llms/hugging_face_api.py +18 -4
  131. pygpt_net/provider/llms/local.py +7 -2
  132. pygpt_net/provider/llms/ollama.py +30 -6
  133. pygpt_net/provider/llms/openai.py +32 -6
  134. pygpt_net/provider/loaders/base.py +14 -0
  135. pygpt_net/provider/loaders/hub/yt/base.py +5 -0
  136. pygpt_net/provider/loaders/web_database.py +13 -5
  137. pygpt_net/provider/loaders/web_github_issues.py +5 -1
  138. pygpt_net/provider/loaders/web_google_calendar.py +9 -1
  139. pygpt_net/provider/loaders/web_google_docs.py +6 -1
  140. pygpt_net/provider/loaders/web_google_drive.py +10 -1
  141. pygpt_net/provider/loaders/web_google_gmail.py +2 -1
  142. pygpt_net/provider/loaders/web_google_keep.py +5 -1
  143. pygpt_net/provider/loaders/web_google_sheets.py +5 -1
  144. pygpt_net/provider/loaders/web_microsoft_onedrive.py +15 -1
  145. pygpt_net/provider/loaders/web_page.py +4 -2
  146. pygpt_net/provider/loaders/web_rss.py +2 -1
  147. pygpt_net/provider/loaders/web_sitemap.py +2 -1
  148. pygpt_net/provider/loaders/web_twitter.py +4 -2
  149. pygpt_net/provider/loaders/web_yt.py +17 -2
  150. pygpt_net/provider/vector_stores/__init__.py +45 -14
  151. pygpt_net/provider/vector_stores/base.py +35 -8
  152. pygpt_net/provider/vector_stores/chroma.py +13 -3
  153. pygpt_net/provider/vector_stores/ctx_attachment.py +32 -13
  154. pygpt_net/provider/vector_stores/elasticsearch.py +12 -3
  155. pygpt_net/provider/vector_stores/pinecode.py +12 -3
  156. pygpt_net/provider/vector_stores/redis.py +12 -3
  157. pygpt_net/provider/vector_stores/simple.py +12 -3
  158. pygpt_net/provider/vector_stores/temp.py +16 -4
  159. pygpt_net/provider/web/base.py +10 -3
  160. pygpt_net/provider/web/google_custom_search.py +9 -3
  161. pygpt_net/provider/web/microsoft_bing.py +9 -3
  162. pygpt_net/tools/__init__.py +13 -5
  163. pygpt_net/tools/audio_transcriber/__init__.py +4 -3
  164. pygpt_net/tools/base.py +15 -8
  165. pygpt_net/tools/code_interpreter/__init__.py +4 -3
  166. pygpt_net/tools/html_canvas/__init__.py +4 -3
  167. pygpt_net/tools/image_viewer/__init__.py +10 -4
  168. pygpt_net/tools/indexer/__init__.py +15 -46
  169. pygpt_net/tools/indexer/ui/web.py +20 -78
  170. pygpt_net/tools/media_player/__init__.py +4 -3
  171. pygpt_net/tools/text_editor/__init__.py +36 -10
  172. pygpt_net/ui/layout/chat/output.py +2 -2
  173. pygpt_net/ui/layout/ctx/ctx_list.py +86 -18
  174. pygpt_net/ui/menu/audio.py +12 -1
  175. pygpt_net/ui/widget/dialog/url.py +151 -14
  176. pygpt_net/ui/widget/element/group.py +15 -2
  177. pygpt_net/ui/widget/lists/context.py +23 -9
  178. pygpt_net/utils.py +1 -1
  179. {pygpt_net-2.4.41.dist-info → pygpt_net-2.4.44.dist-info}/METADATA +152 -72
  180. {pygpt_net-2.4.41.dist-info → pygpt_net-2.4.44.dist-info}/RECORD +183 -173
  181. {pygpt_net-2.4.41.dist-info → pygpt_net-2.4.44.dist-info}/LICENSE +0 -0
  182. {pygpt_net-2.4.41.dist-info → pygpt_net-2.4.44.dist-info}/WHEEL +0 -0
  183. {pygpt_net-2.4.41.dist-info → pygpt_net-2.4.44.dist-info}/entry_points.txt +0 -0
@@ -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.12.12 04:00:00 #
9
+ # Updated Date: 2024.12.16 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6 import QtCore
@@ -81,9 +81,11 @@ class CtxList:
81
81
  self.window.ui.models[id].removeRows(0, self.window.ui.models[id].rowCount())
82
82
 
83
83
  if self.window.core.config.get("ctx.records.folders.top"):
84
+ self.update_items_pinned(id, data)
84
85
  self.update_groups(id, data)
85
86
  self.update_items(id, data)
86
87
  else:
88
+ self.update_items_pinned(id, data)
87
89
  self.update_items(id, data)
88
90
  self.update_groups(id, data)
89
91
 
@@ -96,13 +98,43 @@ class CtxList:
96
98
  """
97
99
  i = 0
98
100
  last_dt_str = None
101
+ separators = self.window.core.config.get("ctx.records.separators")
102
+ pinned_separators = self.window.core.config.get("ctx.records.pinned.separators")
99
103
  for meta_id in data:
100
104
  if data[meta_id].group_id is None or data[meta_id].group_id == 0:
105
+ if data[meta_id].important:
106
+ continue
101
107
  item = self.build_item(meta_id, data[meta_id], is_group=False)
102
- if self.window.core.config.get("ctx.records.separators"):
103
- if not item.isPinned or self.window.core.config.get("ctx.records.pinned.separators"):
108
+ if separators:
109
+ if not item.isPinned or pinned_separators:
104
110
  if i == 0 or last_dt_str != item.dt:
105
- section = self.build_date_section(item.dt)
111
+ section = self.build_date_section(item.dt, group=False)
112
+ if section:
113
+ self.window.ui.models[id].appendRow(section)
114
+ last_dt_str = item.dt
115
+ self.window.ui.models[id].appendRow(item)
116
+ i += 1
117
+
118
+ def update_items_pinned(self, id, data):
119
+ """
120
+ Update items pinned
121
+
122
+ :param id: ID of the list
123
+ :param data: Data to update
124
+ """
125
+ i = 0
126
+ last_dt_str = None
127
+ separators = self.window.core.config.get("ctx.records.separators")
128
+ pinned_separators = self.window.core.config.get("ctx.records.pinned.separators")
129
+ for meta_id in data:
130
+ if data[meta_id].group_id is None or data[meta_id].group_id == 0:
131
+ if not data[meta_id].important:
132
+ continue
133
+ item = self.build_item(meta_id, data[meta_id], is_group=False)
134
+ if separators:
135
+ if pinned_separators:
136
+ if i == 0 or last_dt_str != item.dt:
137
+ section = self.build_date_section(item.dt, group=False)
106
138
  if section:
107
139
  self.window.ui.models[id].appendRow(section)
108
140
  last_dt_str = item.dt
@@ -116,21 +148,37 @@ class CtxList:
116
148
  :param id: ID of the list
117
149
  :param data: Data to update
118
150
  """
119
- # get groups
120
- groups = self.window.core.ctx.get_groups()
121
-
151
+ groups = self.window.core.ctx.get_groups() # get groups
122
152
  for group_id in groups:
123
153
  last_dt_str = None
124
154
  group = groups[group_id]
125
155
  c = self.count_in_group(group.id, data)
126
- if c == 0 and self.window.core.ctx.get_search_string() is not None and self.window.core.ctx.get_search_string() != "":
156
+ if (c == 0 and self.window.core.ctx.get_search_string() is not None
157
+ and self.window.core.ctx.get_search_string() != ""):
127
158
  continue # skip empty groups when searching
128
159
 
129
160
  suffix = ""
130
161
  if c > 0:
131
162
  suffix = " (" + str(c) + ")"
163
+ is_attachment = group.has_additional_ctx()
132
164
  group_name = group.name + suffix
133
165
  group_item = GroupItem(QIcon(":/icons/folder_filled.svg"), group_name, group.id)
166
+ group_item.hasAttachments = group.has_additional_ctx()
167
+ custom_data = {
168
+ "is_group": True,
169
+ "is_attachment": is_attachment,
170
+ }
171
+ if is_attachment:
172
+ files = group.get_attachment_names()
173
+ num = len(files)
174
+ files_str = ", ".join(files)
175
+ if len(files_str) > 40:
176
+ files_str = files_str[:40] + '...'
177
+ tooltip_str = trans("attachments.ctx.tooltip.list").format(num=num) + ": " + files_str
178
+ group_item.setToolTip(tooltip_str)
179
+
180
+ group_item.setData(custom_data, QtCore.Qt.ItemDataRole.UserRole)
181
+
134
182
  i = 0
135
183
  for meta_id in data:
136
184
  if data[meta_id].group_id != group.id:
@@ -139,7 +187,7 @@ class CtxList:
139
187
  if self.window.core.config.get("ctx.records.groups.separators"):
140
188
  if not item.isPinned or self.window.core.config.get("ctx.records.pinned.separators"):
141
189
  if i == 0 or last_dt_str != item.dt:
142
- section = self.build_date_section(item.dt)
190
+ section = self.build_date_section(item.dt, group=True)
143
191
  if section:
144
192
  group_item.appendRow(section)
145
193
  last_dt_str = item.dt
@@ -178,6 +226,17 @@ class CtxList:
178
226
  :return: Item
179
227
  """
180
228
  append_dt = True
229
+ is_important = False
230
+ is_attachment = False
231
+ in_group = False
232
+ label = data.label
233
+ if data.important:
234
+ is_important = True
235
+ if data.has_additional_ctx():
236
+ is_attachment = True
237
+ if data.group:
238
+ in_group = True
239
+
181
240
  if is_group:
182
241
  if self.window.core.config.get("ctx.records.groups.separators"):
183
242
  append_dt = False
@@ -206,35 +265,44 @@ class CtxList:
206
265
  mode_str,
207
266
  id,
208
267
  )
268
+
269
+ # append attachments to tooltip
270
+ if is_attachment:
271
+ files = data.get_attachment_names()
272
+ num = len(files)
273
+ files_str = ", ".join(files)
274
+ if len(files_str) > 40:
275
+ files_str = files_str[:40] + '...'
276
+ tooltip_str = trans("attachments.ctx.tooltip.list").format(num=num) + ": " + files_str
277
+ tooltip_text += "\n" + tooltip_str
278
+
209
279
  item = Item(name, id)
210
280
  item.id = id
211
281
  item.dt = dt
212
282
  item.isPinned = data.important
213
283
  item.setData(tooltip_text, QtCore.Qt.ToolTipRole)
214
- is_important = False
215
- is_attachment = False
216
- label = data.label
217
- if data.important:
218
- is_important = True
219
- if data.additional_ctx and len(data.additional_ctx) > 0:
220
- is_attachment = True
284
+
221
285
  custom_data = {
222
286
  "label": label,
223
287
  "is_important": is_important,
224
288
  "is_attachment": is_attachment,
289
+ "in_group": in_group,
225
290
  }
226
291
  item.setData(custom_data, QtCore.Qt.ItemDataRole.UserRole)
227
292
  item.setData(name)
228
293
  return item
229
294
 
230
- def build_date_section(self, dt: str) -> SectionItem:
295
+ def build_date_section(self, dt: str, group: bool = False) -> SectionItem:
231
296
  """
232
297
  Build date section
233
298
 
234
299
  :param dt: date section string
300
+ :param group: is group
235
301
  :return: SectionItem
236
302
  """
237
- return SectionItem(dt)
303
+ item = SectionItem(dt, group=group)
304
+ # item.setToolTip(dt)
305
+ return item
238
306
 
239
307
  def convert_date(self, timestamp: int) -> str:
240
308
  """
@@ -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.05.05 12:00:00 #
9
+ # Updated Date: 2024.12.14 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtGui import QAction, QIcon
@@ -52,6 +52,13 @@ class Audio:
52
52
  checkable=False,
53
53
  )
54
54
 
55
+ self.window.ui.menu['audio.stop'] = QAction(
56
+ QIcon(":/icons/mute.svg"),
57
+ trans("menu.audio.stop"),
58
+ self.window,
59
+ checkable=False,
60
+ )
61
+
55
62
  self.window.ui.menu['audio.output'].triggered.connect(
56
63
  lambda: self.window.controller.plugins.toggle('audio_output')
57
64
  )
@@ -67,6 +74,9 @@ class Audio:
67
74
  self.window.ui.menu['audio.cache.clear'].triggered.connect(
68
75
  lambda: self.window.controller.audio.clear_cache()
69
76
  )
77
+ self.window.ui.menu['audio.stop'].triggered.connect(
78
+ lambda: self.window.controller.audio.stop_audio()
79
+ )
70
80
 
71
81
  self.window.ui.menu['menu.audio'] = self.window.menuBar().addMenu(trans("menu.audio"))
72
82
  self.window.ui.menu['menu.audio'].addAction(self.window.ui.menu['audio.input'])
@@ -76,3 +86,4 @@ class Audio:
76
86
  self.window.ui.menu['menu.audio'].addAction(self.window.ui.menu['audio.control.global'])
77
87
  self.window.ui.menu['menu.audio'].addSeparator()
78
88
  self.window.ui.menu['menu.audio'].addAction(self.window.ui.menu['audio.cache.clear'])
89
+ self.window.ui.menu['menu.audio'].addAction(self.window.ui.menu['audio.stop'])
@@ -6,12 +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: 2024.11.26 02:00:00 #
9
+ # Updated Date: 2024.12.16 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from PySide6.QtWidgets import QDialog, QLabel, QHBoxLayout, QVBoxLayout, QPushButton
12
+ from PySide6.QtCore import Qt
13
+ from PySide6.QtWidgets import QDialog, QLabel, QHBoxLayout, QVBoxLayout, QPushButton, QScrollArea, QWidget, QSizePolicy
13
14
 
14
- from pygpt_net.ui.widget.element.labels import HelpLabel
15
+ from pygpt_net.ui.widget.element.group import QHLine
16
+ from pygpt_net.ui.widget.element.labels import HelpLabel, UrlLabel
17
+ from pygpt_net.ui.widget.option.combo import OptionCombo
15
18
  from pygpt_net.utils import trans
16
19
  from pygpt_net.ui.widget.textarea.url import UrlInput
17
20
 
@@ -28,15 +31,19 @@ class UrlDialog(QDialog):
28
31
  self.window = window
29
32
  self.id = id
30
33
  self.current = None
31
- self.input = UrlInput(window, id)
32
- self.input.setMinimumWidth(400)
34
+ #self.input = UrlInput(window, id)
35
+ #self.input.setMinimumWidth(400)
36
+ self.initialized = False
37
+ self.params_scroll = None
38
+
39
+ def init(self):
40
+ """Initialize dialog"""
41
+ if self.initialized:
42
+ return
33
43
 
34
44
  self.window.ui.nodes['dialog.url.btn.update'] = QPushButton(trans('dialog.url.update'))
35
45
  self.window.ui.nodes['dialog.url.btn.update'].clicked.connect(
36
- lambda: self.window.controller.dialogs.confirm.accept_url(
37
- self.id,
38
- self.window.ui.dialog['url'].current,
39
- self.input.text()),
46
+ lambda: self.window.controller.attachment.attach_url()
40
47
  )
41
48
 
42
49
  self.window.ui.nodes['dialog.url.btn.dismiss'] = QPushButton(trans('dialog.url.dismiss'))
@@ -47,13 +54,143 @@ class UrlDialog(QDialog):
47
54
  bottom.addWidget(self.window.ui.nodes['dialog.url.btn.dismiss'])
48
55
  bottom.addWidget(self.window.ui.nodes['dialog.url.btn.update'])
49
56
 
50
- self.window.ui.nodes['dialog.url.label'] = QLabel(trans("dialog.url.title"))
51
- self.window.ui.nodes['dialog.url.tip'] = HelpLabel(trans("dialog.url.tip"))
57
+ #self.window.ui.nodes['dialog.url.label'] = QLabel(trans("dialog.url.title"))
58
+ #self.window.ui.nodes['dialog.url.tip'] = HelpLabel(trans("dialog.url.tip"))
59
+
60
+ # -----------------
61
+
62
+ loaders = self.window.controller.config.placeholder.apply_by_id("llama_index_loaders_web")
63
+ loaders_list = []
64
+ for loader in loaders:
65
+ k = list(loader.keys())[0]
66
+ key = k.replace("web_", "")
67
+ value = loader[k]
68
+ loaders_list.append({
69
+ key: value,
70
+ })
71
+
72
+ self.window.ui.nodes["dialog.url.loader"] = OptionCombo(
73
+ self.window,
74
+ "dialog.url",
75
+ "web.loader",
76
+ {
77
+ "label": trans("tool.indexer.tab.web.loader"),
78
+ "keys": loaders_list,
79
+ "value": "webpage",
80
+ }
81
+ )
82
+
83
+ self.window.ui.nodes["dialog.url.loader"].layout.setContentsMargins(0, 0, 0, 0)
84
+ self.window.ui.nodes["dialog.url.loader.label"] = HelpLabel(trans("tool.indexer.tab.web.loader"))
85
+ self.window.ui.add_hook("update.dialog.url.web.loader", self.hook_loader_change)
86
+
87
+ self.window.ui.nodes["dialog.url.options.label"] = HelpLabel(trans("tool.indexer.tab.web.source"))
88
+ self.window.ui.nodes["dialog.url.config.label"] = HelpLabel(trans("tool.indexer.tab.web.cfg"))
89
+ self.window.ui.nodes["dialog.url.config.help"] = UrlLabel(
90
+ trans("tool.indexer.tab.web.help"),
91
+ "https://pygpt.readthedocs.io/en/latest/configuration.html#data-loaders")
92
+
93
+ params_layout = QVBoxLayout()
94
+ params_layout.setContentsMargins(0, 0, 0, 0)
95
+
96
+ self.params_scroll = QScrollArea()
97
+ self.params_scroll.setWidgetResizable(True)
98
+ self.window.ui.nodes["dialog.url.loader.option"] = {}
99
+ self.window.ui.nodes["dialog.url.loader.option_group"] = {}
100
+ self.window.ui.nodes["dialog.url.loader.config"] = {}
101
+ self.window.ui.nodes["dialog.url.loader.config_group"] = {}
102
+
103
+ # params
104
+ params_layout.addWidget(self.window.ui.nodes["dialog.url.options.label"])
105
+ inputs, groups = self.window.core.idx.ui.loaders.setup_loader_options()
106
+
107
+ for loader in inputs:
108
+ for k in inputs[loader]:
109
+ self.window.ui.nodes["dialog.url.loader.option." + loader + "." + k] = inputs[loader][k]
110
+ for loader in groups:
111
+ self.window.ui.nodes["dialog.url.loader.option_group"][loader] = groups[loader]
112
+ params_layout.addWidget(self.window.ui.nodes["dialog.url.loader.option_group"][loader])
113
+ self.window.ui.nodes["dialog.url.loader.option_group"][loader].hide() # hide on start
114
+
115
+ # separator
116
+ params_layout.addWidget(QHLine())
117
+
118
+ # config
119
+ params_layout.addWidget(self.window.ui.nodes["dialog.url.config.label"])
120
+ self.window.ui.nodes["dialog.url.config.label"].setAlignment(Qt.AlignCenter)
121
+ inputs, groups = self.window.core.idx.ui.loaders.setup_loader_config()
122
+
123
+ for loader in inputs:
124
+ for k in inputs[loader]:
125
+ self.window.ui.nodes["dialog.url.loader.config." + loader + "." + k] = inputs[loader][k]
126
+ for loader in groups:
127
+ self.window.ui.nodes["dialog.url.loader.config_group"][loader] = groups[loader]
128
+ params_layout.addWidget(self.window.ui.nodes["dialog.url.loader.config_group"][loader])
129
+ self.window.ui.nodes["dialog.url.loader.config_group"][loader].hide() # hide on start
130
+ params_layout.addWidget(self.window.ui.nodes["dialog.url.config.help"], alignment=Qt.AlignCenter)
131
+
132
+ # stretch
133
+ params_layout.addStretch(1)
134
+
135
+ self.params_widget = QWidget()
136
+ self.params_widget.setLayout(params_layout)
137
+ self.params_scroll.setWidget(self.params_widget)
138
+
139
+ # resize
140
+ self.params_scroll.setWidgetResizable(True)
141
+ self.params_scroll.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
142
+
143
+ # -----------------
52
144
 
53
145
  layout = QVBoxLayout()
54
- layout.addWidget(self.window.ui.nodes['dialog.url.label'])
55
- layout.addWidget(self.input)
56
- layout.addWidget(self.window.ui.nodes['dialog.url.tip'])
146
+ #layout.addWidget(self.window.ui.nodes['dialog.url.label'])
147
+ #layout.addWidget(self.input)
148
+ #layout.addWidget(self.window.ui.nodes['dialog.url.tip'])
149
+ layout.addWidget(self.window.ui.nodes['dialog.url.loader.label'])
150
+ layout.addWidget(self.window.ui.nodes['dialog.url.loader'])
151
+ layout.addWidget(self.params_scroll)
57
152
  layout.addLayout(bottom)
58
153
 
154
+ # defaults
155
+ self.window.ui.nodes["dialog.url.loader"].set_value("webpage")
156
+
59
157
  self.setLayout(layout)
158
+
159
+ self.initialized = True
160
+
161
+ def hook_loader_change(self, key, value, caller, *args, **kwargs):
162
+ """
163
+ Hook: on loader change
164
+
165
+ :param key: Option key
166
+ :param value: Option value
167
+ :param caller: Caller
168
+ :param args: Args
169
+ :param kwargs: Kwargs
170
+ """
171
+ # hide/show options
172
+ for loader in self.window.ui.nodes["dialog.url.loader.option_group"]:
173
+ self.window.ui.nodes["dialog.url.loader.option_group"][loader].hide()
174
+ if value in self.window.ui.nodes["dialog.url.loader.option_group"]:
175
+ self.window.ui.nodes["dialog.url.loader.option_group"][value].show()
176
+
177
+ # show/hide label if options are available
178
+ self.window.ui.nodes["dialog.url.options.label"].hide()
179
+ if value in self.window.ui.nodes["dialog.url.loader.option_group"]:
180
+ self.window.ui.nodes["dialog.url.options.label"].show()
181
+
182
+ # hide/show config
183
+ for loader in self.window.ui.nodes["dialog.url.loader.config_group"]:
184
+ self.window.ui.nodes["dialog.url.loader.config_group"][loader].hide()
185
+ if value in self.window.ui.nodes["dialog.url.loader.config_group"]:
186
+ self.window.ui.nodes["dialog.url.loader.config_group"][value].show()
187
+
188
+ # show/hide label if config are available
189
+ self.window.ui.nodes["dialog.url.config.label"].hide()
190
+ self.window.ui.nodes["dialog.url.config.help"].hide()
191
+ if value in self.window.ui.nodes["dialog.url.loader.config_group"]:
192
+ self.window.ui.nodes["dialog.url.config.label"].show()
193
+ self.window.ui.nodes["dialog.url.config.help"].show()
194
+
195
+ self.params_widget.adjustSize()
196
+ self.params_scroll.update()
@@ -6,11 +6,12 @@
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.24 22:00:00 #
9
+ # Updated Date: 2024.12.16 01:00:00 #
10
10
  # ================================================== #
11
+
11
12
  from PySide6.QtCore import Qt
12
13
  from PySide6.QtGui import QIcon
13
- from PySide6.QtWidgets import QCheckBox, QWidget, QVBoxLayout
14
+ from PySide6.QtWidgets import QCheckBox, QWidget, QVBoxLayout, QFrame
14
15
 
15
16
  import pygpt_net.icons_rc
16
17
 
@@ -105,3 +106,15 @@ class CollapsedGroup(QWidget):
105
106
  :param option: option widget
106
107
  """
107
108
  self.options.addWidget(option)
109
+
110
+ class QHLine(QFrame):
111
+ def __init__(self):
112
+ super(QHLine, self).__init__()
113
+ self.setFrameShape(QFrame.HLine)
114
+ self.setFrameShadow(QFrame.Sunken)
115
+
116
+ class QVLine(QFrame):
117
+ def __init__(self):
118
+ super(QVLine, self).__init__()
119
+ self.setFrameShape(QFrame.VLine)
120
+ self.setFrameShadow(QFrame.Sunken)
@@ -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.12.12 04:00:00 #
9
+ # Updated Date: 2024.12.16 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import datetime
@@ -14,6 +14,7 @@ import datetime
14
14
  from PySide6 import QtWidgets, QtCore, QtGui
15
15
  from PySide6.QtGui import QAction, QIcon, QColor, QPixmap, QStandardItem
16
16
  from PySide6.QtWidgets import QMenu
17
+ from overrides import overrides
17
18
 
18
19
  from pygpt_net.ui.widget.lists.base import BaseList
19
20
  from pygpt_net.utils import trans
@@ -31,13 +32,13 @@ class ContextList(BaseList):
31
32
  super(ContextList, self).__init__(window)
32
33
  self.window = window
33
34
  self.id = id
34
- self.clicked.connect(self.click)
35
35
  self.expanded_items = set()
36
36
  self.setItemDelegate(ImportantItemDelegate())
37
37
 
38
+
38
39
  def click(self, index):
39
40
  """
40
- Click event
41
+ Click event (override, connected in BaseList class)
41
42
 
42
43
  :param index: index
43
44
  """
@@ -49,8 +50,10 @@ class ContextList(BaseList):
49
50
  self.window.controller.ctx.set_group(item.id)
50
51
  if self.window.ui.nodes['ctx.list'].isExpanded(index):
51
52
  self.expanded_items.discard(item.id)
53
+ self.window.ui.nodes['ctx.list'].collapse(index)
52
54
  else:
53
55
  self.expanded_items.add(item.id)
56
+ self.window.ui.nodes['ctx.list'].expand(index)
54
57
  else:
55
58
  self.window.controller.ctx.select_by_id(item.id)
56
59
  else:
@@ -76,7 +79,7 @@ class ContextList(BaseList):
76
79
 
77
80
  :param index: index
78
81
  """
79
- pass
82
+ print("dblclick")
80
83
 
81
84
  def mousePressEvent(self, event):
82
85
  """
@@ -407,20 +410,28 @@ class ImportantItemDelegate(QtWidgets.QStyledItemDelegate):
407
410
  label = 0
408
411
  is_important = False
409
412
  is_attachment = False
413
+ is_group = False
414
+ in_group = False
415
+
410
416
  if "label" in data:
411
417
  label = data["label"]
412
418
  if "is_important" in data and data["is_important"]:
413
419
  is_important = True
414
420
  if "is_attachment" in data and data["is_attachment"]:
415
421
  is_attachment = True
422
+ if "is_group" in data and data["is_group"]:
423
+ is_group = True
424
+ if "in_group" in data and data["in_group"]:
425
+ in_group = True
416
426
 
417
427
  painter.save()
418
428
 
419
429
  if is_attachment:
420
430
  icon = QtGui.QIcon(":/icons/attachment.svg")
421
431
  icon_size = option.decorationSize or QtCore.QSize(16, 16)
432
+ icon_pos = option.rect.right() - icon_size.width()
422
433
  icon_rect = QtCore.QRect(
423
- option.rect.right() - icon_size.width(),
434
+ icon_pos,
424
435
  option.rect.top() + (option.rect.height() - icon_size.height()) / 2,
425
436
  icon_size.width(),
426
437
  icon_size.height()
@@ -447,8 +458,6 @@ class ImportantItemDelegate(QtWidgets.QStyledItemDelegate):
447
458
  )
448
459
  painter.drawRect(square_rect)
449
460
 
450
- #label = label - 10 # remove pin status
451
-
452
461
  # label (0-9)
453
462
  if label > 0:
454
463
  color = self.get_color_for_status(label)
@@ -495,6 +504,7 @@ class GroupItem(QStandardItem):
495
504
  self.name = name
496
505
  self.isFolder = True
497
506
  self.isPinned = False
507
+ self.hasAttachments = False
498
508
  self.dt = None
499
509
 
500
510
  class Item(QStandardItem):
@@ -507,12 +517,16 @@ class Item(QStandardItem):
507
517
  self.dt = None
508
518
 
509
519
  class SectionItem(QStandardItem):
510
- def __init__(self, title):
520
+ def __init__(self, title, group: bool = False):
511
521
  super().__init__(title)
512
522
  self.title = title
523
+ self.group = group
513
524
  self.setSelectable(False)
514
525
  self.setEnabled(False)
515
- self.setTextAlignment(QtCore.Qt.AlignRight)
526
+ if self.group:
527
+ self.setTextAlignment(QtCore.Qt.AlignLeft)
528
+ else:
529
+ self.setTextAlignment(QtCore.Qt.AlignRight)
516
530
  font = self.font()
517
531
  font.setBold(True)
518
532
  self.setFont(font)
pygpt_net/utils.py CHANGED
@@ -139,7 +139,7 @@ def parse_args(data: list) -> dict:
139
139
  elif type == 'dict':
140
140
  try:
141
141
  args[key] = json.loads(value)
142
- except json.JSONDecodeError:
142
+ except:
143
143
  args[key] = {}
144
144
  elif type == 'list':
145
145
  args[key] = [x.strip() for x in value.split(',')]