pygpt-net 2.4.42__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 (67) hide show
  1. CHANGELOG.md +11 -0
  2. README.md +17 -2
  3. pygpt_net/CHANGELOG.txt +11 -0
  4. pygpt_net/__init__.py +3 -3
  5. pygpt_net/controller/attachment.py +31 -3
  6. pygpt_net/controller/chat/attachment.py +37 -36
  7. pygpt_net/controller/config/placeholder.py +6 -4
  8. pygpt_net/controller/idx/common.py +7 -3
  9. pygpt_net/core/attachments/__init__.py +7 -2
  10. pygpt_net/core/attachments/context.py +52 -34
  11. pygpt_net/core/db/__init__.py +2 -1
  12. pygpt_net/core/debug/attachments.py +1 -0
  13. pygpt_net/core/idx/__init__.py +8 -3
  14. pygpt_net/core/idx/indexing.py +24 -7
  15. pygpt_net/core/idx/ui/__init__.py +22 -0
  16. pygpt_net/core/idx/ui/loaders.py +217 -0
  17. pygpt_net/data/config/config.json +4 -4
  18. pygpt_net/data/config/models.json +3 -3
  19. pygpt_net/data/config/modes.json +3 -3
  20. pygpt_net/data/config/settings.json +5 -5
  21. pygpt_net/data/locale/locale.de.ini +3 -3
  22. pygpt_net/data/locale/locale.en.ini +11 -9
  23. pygpt_net/data/locale/locale.es.ini +3 -3
  24. pygpt_net/data/locale/locale.fr.ini +3 -3
  25. pygpt_net/data/locale/locale.it.ini +3 -3
  26. pygpt_net/data/locale/locale.pl.ini +3 -3
  27. pygpt_net/data/locale/locale.uk.ini +3 -3
  28. pygpt_net/data/locale/locale.zh.ini +3 -3
  29. pygpt_net/data/locale/plugin.mailer.en.ini +5 -5
  30. pygpt_net/item/attachment.py +5 -1
  31. pygpt_net/item/ctx.py +99 -2
  32. pygpt_net/migrations/Version20241215110000.py +25 -0
  33. pygpt_net/migrations/__init__.py +3 -1
  34. pygpt_net/plugin/cmd_files/__init__.py +3 -2
  35. pygpt_net/provider/core/attachment/json_file.py +4 -1
  36. pygpt_net/provider/core/config/patch.py +6 -0
  37. pygpt_net/provider/core/ctx/db_sqlite/storage.py +50 -7
  38. pygpt_net/provider/core/ctx/db_sqlite/utils.py +29 -5
  39. pygpt_net/provider/loaders/base.py +14 -0
  40. pygpt_net/provider/loaders/hub/yt/base.py +5 -0
  41. pygpt_net/provider/loaders/web_database.py +13 -5
  42. pygpt_net/provider/loaders/web_github_issues.py +5 -1
  43. pygpt_net/provider/loaders/web_google_calendar.py +9 -1
  44. pygpt_net/provider/loaders/web_google_docs.py +6 -1
  45. pygpt_net/provider/loaders/web_google_drive.py +10 -1
  46. pygpt_net/provider/loaders/web_google_gmail.py +2 -1
  47. pygpt_net/provider/loaders/web_google_keep.py +5 -1
  48. pygpt_net/provider/loaders/web_google_sheets.py +5 -1
  49. pygpt_net/provider/loaders/web_microsoft_onedrive.py +15 -1
  50. pygpt_net/provider/loaders/web_page.py +4 -2
  51. pygpt_net/provider/loaders/web_rss.py +2 -1
  52. pygpt_net/provider/loaders/web_sitemap.py +2 -1
  53. pygpt_net/provider/loaders/web_twitter.py +4 -2
  54. pygpt_net/provider/loaders/web_yt.py +17 -2
  55. pygpt_net/provider/vector_stores/ctx_attachment.py +1 -1
  56. pygpt_net/tools/indexer/__init__.py +8 -40
  57. pygpt_net/tools/indexer/ui/web.py +20 -78
  58. pygpt_net/ui/layout/ctx/ctx_list.py +86 -18
  59. pygpt_net/ui/widget/dialog/url.py +151 -14
  60. pygpt_net/ui/widget/element/group.py +15 -2
  61. pygpt_net/ui/widget/lists/context.py +23 -9
  62. pygpt_net/utils.py +1 -1
  63. {pygpt_net-2.4.42.dist-info → pygpt_net-2.4.44.dist-info}/METADATA +18 -3
  64. {pygpt_net-2.4.42.dist-info → pygpt_net-2.4.44.dist-info}/RECORD +67 -64
  65. {pygpt_net-2.4.42.dist-info → pygpt_net-2.4.44.dist-info}/LICENSE +0 -0
  66. {pygpt_net-2.4.42.dist-info → pygpt_net-2.4.44.dist-info}/WHEEL +0 -0
  67. {pygpt_net-2.4.42.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.14 22:00:00 #
9
+ # Updated Date: 2024.12.16 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import datetime
@@ -311,48 +311,16 @@ class IndexerTool(BaseTool):
311
311
 
312
312
  :param force: force indexing
313
313
  """
314
- input_params = {}
315
- input_config = {}
316
- is_replace = self.window.ui.nodes["tool.indexer.web.options.replace"].isChecked()
317
- loader = self.window.ui.nodes["tool.indexer.web.loader"].get_value()
318
- if not loader:
314
+ result, loader, input_params, input_config = self.window.core.idx.ui.loaders.handle_options(
315
+ self.window.ui.nodes["tool.indexer.web.loader"],
316
+ "tool.indexer.web.loader.option",
317
+ "tool.indexer.web.loader.config",
318
+ )
319
+ if not result:
319
320
  self.window.ui.dialogs.alert(trans("tool.indexer.alert.no_loader"))
320
321
  return
321
- loaders = self.window.core.idx.indexing.get_external_instructions()
322
- if loader in loaders:
323
- params = loaders[loader]
324
- for k in params["args"]:
325
- key_path = "tool.indexer.web.loader.option." + loader + "." + k
326
- if key_path in self.window.ui.nodes:
327
- input_params[k] = self.window.ui.nodes[key_path].text()
328
322
 
329
- loaders = self.window.core.idx.indexing.get_external_config()
330
- if loader in loaders:
331
- params = loaders[loader]
332
- for k in params:
333
- key_path = "tool.indexer.web.loader.config." + loader + "." + k
334
- type = params[k]["type"]
335
- if key_path in self.window.ui.nodes:
336
- tmp_value = self.window.ui.nodes[key_path].text()
337
- try:
338
- if tmp_value:
339
- if type == "int":
340
- tmp_value = int(tmp_value)
341
- elif type == "float":
342
- tmp_value = float(tmp_value)
343
- elif type == "bool":
344
- if tmp_value.lower() in ["true", "1"]:
345
- tmp_value = True
346
- else:
347
- tmp_value = False
348
- elif type == "list":
349
- tmp_value = tmp_value.split(",")
350
- elif type == "dict":
351
- tmp_value = json.loads(tmp_value)
352
- input_config[k] = tmp_value
353
- except Exception as e:
354
- self.window.core.debug.log(e)
355
- self.window.ui.dialogs.alert(e)
323
+ is_replace = self.window.ui.nodes["tool.indexer.web.options.replace"].isChecked()
356
324
  if not force:
357
325
  self.window.ui.dialogs.confirm(
358
326
  type="idx.tool.index",
@@ -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.08.19 20:00:00 #
9
+ # Updated Date: 2024.12.16 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import json
@@ -15,6 +15,7 @@ from PySide6.QtCore import Qt
15
15
  from PySide6.QtWidgets import QLabel, QVBoxLayout, QWidget, QCheckBox, QHBoxLayout, QScrollArea, \
16
16
  QSizePolicy
17
17
 
18
+ from pygpt_net.ui.widget.element.group import QVLine, QHLine
18
19
  from pygpt_net.ui.widget.element.labels import HelpLabel, UrlLabel
19
20
  from pygpt_net.ui.widget.option.combo import OptionCombo
20
21
  from pygpt_net.ui.widget.option.input import OptionInput
@@ -33,15 +34,17 @@ class WebTab:
33
34
  self.params_widget = None
34
35
 
35
36
  def setup(self):
36
- """
37
- Setup tab widget
38
- """
37
+ """Setup tab widget"""
39
38
  # get loaders list
40
39
  loaders = self.window.controller.config.placeholder.apply_by_id("llama_index_loaders_web")
41
40
  loaders_list = []
42
41
  for loader in loaders:
43
- key = list(loader.keys())[0]
44
- loaders_list.append(key.replace("web_", ""))
42
+ k = list(loader.keys())[0]
43
+ key = k.replace("web_", "")
44
+ value = loader[k]
45
+ loaders_list.append({
46
+ key: value,
47
+ })
45
48
 
46
49
  self.window.ui.nodes["tool.indexer.web.loader"] = OptionCombo(
47
50
  self.window,
@@ -55,14 +58,14 @@ class WebTab:
55
58
  )
56
59
 
57
60
  self.window.ui.nodes["tool.indexer.web.loader"].layout.setContentsMargins(0, 0, 0, 0)
58
- self.window.ui.nodes["tool.indexer.web.loader.label"] = QLabel(trans("tool.indexer.tab.web.loader"))
61
+ self.window.ui.nodes["tool.indexer.web.loader.label"] = HelpLabel(trans("tool.indexer.tab.web.loader"))
59
62
  self.window.ui.add_hook("update.tool.indexer.web.loader", self.hook_loader_change)
60
63
 
61
64
  self.window.ui.nodes["tool.indexer.web.options.label"] = HelpLabel(trans("tool.indexer.tab.web.source"))
62
65
  self.window.ui.nodes["tool.indexer.web.config.label"] = HelpLabel(trans("tool.indexer.tab.web.cfg"))
63
66
  self.window.ui.nodes["tool.indexer.web.config.help"] = UrlLabel(
64
67
  trans("tool.indexer.tab.web.help"),
65
- "https://pygpt.readthedocs.io/en/latest/modes.html#chat-with-files-llama-index")
68
+ "https://pygpt.readthedocs.io/en/latest/configuration.html#data-loaders")
66
69
 
67
70
  # --------------------------------------------------
68
71
 
@@ -78,7 +81,7 @@ class WebTab:
78
81
 
79
82
  # params
80
83
  params_layout.addWidget(self.window.ui.nodes["tool.indexer.web.options.label"])
81
- inputs, groups = self.setup_loader_options()
84
+ inputs, groups = self.window.core.idx.ui.loaders.setup_loader_options()
82
85
  for loader in inputs:
83
86
  for k in inputs[loader]:
84
87
  self.window.ui.nodes["tool.indexer.web.loader.option." + loader + "." + k] = inputs[loader][k]
@@ -87,9 +90,13 @@ class WebTab:
87
90
  params_layout.addWidget(self.window.ui.nodes["tool.indexer.web.loader.option_group"][loader])
88
91
  self.window.ui.nodes["tool.indexer.web.loader.option_group"][loader].hide() # hide on start
89
92
 
93
+ # separator
94
+ params_layout.addWidget(QHLine())
95
+
90
96
  # config
91
97
  params_layout.addWidget(self.window.ui.nodes["tool.indexer.web.config.label"])
92
- inputs, groups = self.setup_loader_config()
98
+ self.window.ui.nodes["tool.indexer.web.config.label"].setAlignment(Qt.AlignCenter)
99
+ inputs, groups = self.window.core.idx.ui.loaders.setup_loader_config()
93
100
  for loader in inputs:
94
101
  for k in inputs[loader]:
95
102
  self.window.ui.nodes["tool.indexer.web.loader.config." + loader + "." + k] = inputs[loader][k]
@@ -99,6 +106,7 @@ class WebTab:
99
106
  self.window.ui.nodes["tool.indexer.web.loader.config_group"][loader].hide() # hide on start
100
107
  params_layout.addWidget(self.window.ui.nodes["tool.indexer.web.config.help"], alignment=Qt.AlignCenter)
101
108
 
109
+ # stretch
102
110
  params_layout.addStretch(1)
103
111
 
104
112
  self.params_widget = QWidget()
@@ -117,9 +125,10 @@ class WebTab:
117
125
 
118
126
  self.window.ui.nodes["tool.indexer.provider"] = HelpLabel(self.window.core.config.get("llama.idx.storage"))
119
127
 
120
- loader_layout = QHBoxLayout()
128
+ loader_layout = QVBoxLayout()
121
129
  loader_layout.addWidget(self.window.ui.nodes["tool.indexer.web.loader.label"])
122
130
  loader_layout.addWidget(self.window.ui.nodes["tool.indexer.web.loader"])
131
+ loader_layout.setContentsMargins(0, 10, 0, 0)
123
132
 
124
133
  options_layout = QVBoxLayout()
125
134
  options_layout.addWidget(self.window.ui.nodes["tool.indexer.web.options.replace"])
@@ -177,70 +186,3 @@ class WebTab:
177
186
 
178
187
  self.params_widget.adjustSize()
179
188
  self.params_scroll.update()
180
-
181
- def setup_loader_options(self):
182
- """
183
- Setup loader options
184
- """
185
- inputs = {}
186
- groups = {}
187
- loaders = self.window.core.idx.indexing.get_external_instructions()
188
- for loader in loaders:
189
- params = loaders[loader]
190
- inputs[loader] = {}
191
- group = QVBoxLayout()
192
- for k in params["args"]:
193
- widget = OptionInput(self.window, "tool.indexer", f"web.loader.{loader}.option.{k}", {
194
- "label": k,
195
- "value": "",
196
- })
197
- widget.setPlaceholderText(params["args"][k]["type"])
198
- inputs[loader][k] = widget
199
- row = QHBoxLayout() # cols
200
- row.addWidget(QLabel(k))
201
- row.addWidget(widget)
202
- group.addLayout(row)
203
- group_widget = QWidget()
204
- group_widget.setLayout(group)
205
- groups[loader] = group_widget
206
-
207
- return inputs, groups
208
-
209
- def setup_loader_config(self):
210
- """
211
- Setup loader config
212
- """
213
- inputs = {}
214
- groups = {}
215
- loaders = self.window.core.idx.indexing.get_external_config()
216
- for loader in loaders:
217
- params = loaders[loader]
218
- inputs[loader] = {}
219
- group = QVBoxLayout()
220
- for k in params:
221
- widget = OptionInput(self.window, "tool.indexer", f"web.loader.{loader}.config.{k}", {
222
- "label": k,
223
- "value": params[k]["value"],
224
- })
225
- try:
226
- if params[k]["value"] is not None:
227
- if params[k]["type"] == "list" and isinstance(params[k]["value"], list):
228
- widget.setText(", ".join(params[k]["value"]))
229
- elif params[k]["type"] == "dict" and isinstance(params[k]["value"], dict):
230
- widget.setText(json.dumps(params[k]["value"]))
231
- else:
232
- widget.setText(str(params[k]["value"]))
233
- except Exception as e:
234
- self.window.core.debug.log(e)
235
-
236
- widget.setPlaceholderText(params[k]["type"])
237
- inputs[loader][k] = widget
238
- row = QHBoxLayout() # cols
239
- row.addWidget(QLabel(k))
240
- row.addWidget(widget)
241
- group.addLayout(row)
242
- group_widget = QWidget()
243
- group_widget.setLayout(group)
244
- groups[loader] = group_widget
245
-
246
- return inputs, groups
@@ -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,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)