pygpt-net 2.6.36__py3-none-any.whl → 2.6.38__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 (96) hide show
  1. pygpt_net/CHANGELOG.txt +12 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/controller/chat/handler/anthropic_stream.py +164 -0
  4. pygpt_net/controller/chat/handler/google_stream.py +181 -0
  5. pygpt_net/controller/chat/handler/langchain_stream.py +24 -0
  6. pygpt_net/controller/chat/handler/llamaindex_stream.py +47 -0
  7. pygpt_net/controller/chat/handler/openai_stream.py +260 -0
  8. pygpt_net/controller/chat/handler/utils.py +210 -0
  9. pygpt_net/controller/chat/handler/worker.py +570 -0
  10. pygpt_net/controller/chat/handler/xai_stream.py +135 -0
  11. pygpt_net/controller/chat/stream.py +1 -1
  12. pygpt_net/controller/ctx/ctx.py +1 -1
  13. pygpt_net/controller/debug/debug.py +6 -6
  14. pygpt_net/controller/model/editor.py +3 -0
  15. pygpt_net/controller/model/importer.py +9 -2
  16. pygpt_net/controller/plugins/plugins.py +11 -3
  17. pygpt_net/controller/presets/presets.py +2 -2
  18. pygpt_net/core/bridge/context.py +35 -35
  19. pygpt_net/core/bridge/worker.py +40 -16
  20. pygpt_net/core/ctx/bag.py +7 -2
  21. pygpt_net/core/ctx/reply.py +17 -2
  22. pygpt_net/core/db/viewer.py +19 -34
  23. pygpt_net/core/render/plain/pid.py +12 -1
  24. pygpt_net/core/render/web/body.py +30 -39
  25. pygpt_net/core/tabs/tab.py +24 -1
  26. pygpt_net/data/config/config.json +10 -3
  27. pygpt_net/data/config/models.json +3 -3
  28. pygpt_net/data/config/settings.json +105 -0
  29. pygpt_net/data/css/style.dark.css +2 -3
  30. pygpt_net/data/css/style.light.css +2 -3
  31. pygpt_net/data/locale/locale.de.ini +3 -1
  32. pygpt_net/data/locale/locale.en.ini +19 -1
  33. pygpt_net/data/locale/locale.es.ini +3 -1
  34. pygpt_net/data/locale/locale.fr.ini +3 -1
  35. pygpt_net/data/locale/locale.it.ini +3 -1
  36. pygpt_net/data/locale/locale.pl.ini +4 -2
  37. pygpt_net/data/locale/locale.uk.ini +3 -1
  38. pygpt_net/data/locale/locale.zh.ini +3 -1
  39. pygpt_net/item/assistant.py +51 -2
  40. pygpt_net/item/attachment.py +21 -20
  41. pygpt_net/item/calendar_note.py +19 -2
  42. pygpt_net/item/ctx.py +115 -2
  43. pygpt_net/item/index.py +9 -2
  44. pygpt_net/item/mode.py +9 -6
  45. pygpt_net/item/model.py +20 -3
  46. pygpt_net/item/notepad.py +14 -2
  47. pygpt_net/item/preset.py +42 -2
  48. pygpt_net/item/prompt.py +8 -2
  49. pygpt_net/plugin/cmd_files/plugin.py +2 -2
  50. pygpt_net/provider/api/__init__.py +5 -3
  51. pygpt_net/provider/api/anthropic/__init__.py +190 -29
  52. pygpt_net/provider/api/anthropic/audio.py +30 -0
  53. pygpt_net/provider/api/anthropic/chat.py +341 -0
  54. pygpt_net/provider/api/anthropic/image.py +25 -0
  55. pygpt_net/provider/api/anthropic/tools.py +266 -0
  56. pygpt_net/provider/api/anthropic/vision.py +142 -0
  57. pygpt_net/provider/api/google/chat.py +2 -2
  58. pygpt_net/provider/api/google/realtime/client.py +2 -2
  59. pygpt_net/provider/api/google/tools.py +58 -48
  60. pygpt_net/provider/api/google/vision.py +7 -1
  61. pygpt_net/provider/api/openai/chat.py +1 -0
  62. pygpt_net/provider/api/openai/vision.py +6 -0
  63. pygpt_net/provider/api/x_ai/__init__.py +247 -0
  64. pygpt_net/provider/api/x_ai/audio.py +32 -0
  65. pygpt_net/provider/api/x_ai/chat.py +968 -0
  66. pygpt_net/provider/api/x_ai/image.py +208 -0
  67. pygpt_net/provider/api/x_ai/remote.py +262 -0
  68. pygpt_net/provider/api/x_ai/tools.py +120 -0
  69. pygpt_net/provider/api/x_ai/vision.py +119 -0
  70. pygpt_net/provider/core/attachment/json_file.py +2 -2
  71. pygpt_net/provider/core/config/patch.py +28 -0
  72. pygpt_net/provider/llms/anthropic.py +4 -2
  73. pygpt_net/tools/text_editor/tool.py +4 -1
  74. pygpt_net/tools/text_editor/ui/dialogs.py +1 -1
  75. pygpt_net/ui/base/config_dialog.py +5 -11
  76. pygpt_net/ui/dialog/db.py +177 -59
  77. pygpt_net/ui/dialog/dictionary.py +57 -59
  78. pygpt_net/ui/dialog/editor.py +3 -2
  79. pygpt_net/ui/dialog/image.py +1 -1
  80. pygpt_net/ui/dialog/logger.py +3 -2
  81. pygpt_net/ui/dialog/models.py +16 -16
  82. pygpt_net/ui/dialog/plugins.py +63 -60
  83. pygpt_net/ui/layout/ctx/ctx_list.py +3 -4
  84. pygpt_net/ui/layout/toolbox/__init__.py +2 -2
  85. pygpt_net/ui/layout/toolbox/assistants.py +8 -9
  86. pygpt_net/ui/layout/toolbox/presets.py +2 -2
  87. pygpt_net/ui/main.py +9 -4
  88. pygpt_net/ui/widget/element/labels.py +20 -4
  89. pygpt_net/ui/widget/textarea/editor.py +0 -4
  90. pygpt_net/ui/widget/textarea/web.py +1 -1
  91. {pygpt_net-2.6.36.dist-info → pygpt_net-2.6.38.dist-info}/METADATA +18 -6
  92. {pygpt_net-2.6.36.dist-info → pygpt_net-2.6.38.dist-info}/RECORD +95 -76
  93. pygpt_net/controller/chat/handler/stream_worker.py +0 -1136
  94. {pygpt_net-2.6.36.dist-info → pygpt_net-2.6.38.dist-info}/LICENSE +0 -0
  95. {pygpt_net-2.6.36.dist-info → pygpt_net-2.6.38.dist-info}/WHEEL +0 -0
  96. {pygpt_net-2.6.36.dist-info → pygpt_net-2.6.38.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.03.12 06:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QPushButton, QHBoxLayout, QVBoxLayout, QScrollArea, QWidget, QSizePolicy
@@ -47,87 +47,85 @@ class Dictionary(BaseConfigDialog):
47
47
 
48
48
  def setup(self):
49
49
  """Setup dictionary editor dialogs"""
50
- for dict_id in self.dicts:
51
- parent_id = self.id + "." + dict_id
50
+ ui = self.window.ui
51
+ controller = self.window.controller
52
+ save_text = trans("dialog.preset.btn.save")
53
+ dismiss_text = trans("dialog.rename.dismiss")
54
+ edit_title = trans('action.edit')
55
+
56
+ for dict_id, data in self.dicts.items():
57
+ parent_id = f"{self.id}.{dict_id}"
52
58
  option_key = self.keys[dict_id]
53
59
  parent = self.parents[dict_id]
54
- data = self.dicts[dict_id]
55
- self.window.ui.config[parent_id] = {}
60
+ ui.config[parent_id] = {}
56
61
 
57
- # widgets
58
62
  fields = {}
59
-
60
- # option type: dict
61
63
  if data["type"] == 'dict':
62
- fields = self.window.controller.config.dictionary.to_options(
64
+ fields = controller.config.dictionary.to_options(
63
65
  parent_id,
64
66
  data,
65
- ) # item to options
66
-
67
- # option type: cmd
67
+ )
68
68
  elif data["type"] == 'cmd':
69
- fields = self.window.controller.config.cmd.to_options(
69
+ fields = controller.config.cmd.to_options(
70
70
  parent_id,
71
71
  data,
72
- ) # item to options
72
+ )
73
73
 
74
74
  widgets = self.build_widgets(
75
75
  parent_id,
76
76
  fields,
77
77
  stretch=True,
78
- ) # from base config dialog
79
-
80
- for key in widgets:
81
- self.window.ui.config[parent_id][key] = widgets[key]
82
-
83
- # apply widgets to layouts
84
- options = {}
85
- is_stretch = False
86
- for key in widgets:
87
- if fields[key]["type"] == 'int' or fields[key]["type"] == 'float':
88
- options[key] = self.add_option(widgets[key], fields[key])
89
- elif fields[key]["type"] == 'text' or fields[key]["type"] == 'textarea':
90
- options[key] = self.add_row_option(widgets[key], fields[key])
91
- elif fields[key]["type"] == 'bool':
92
- options[key] = self.add_raw_option(widgets[key], fields[key])
93
- elif fields[key]["type"] == 'dict':
94
- options[key] = self.add_row_option(widgets[key], fields[key])
95
- elif fields[key]["type"] == 'combo':
96
- options[key] = self.add_row_option(widgets[key], fields[key])
97
-
98
- # stretch all only if textarea is present
99
- if fields[key]["type"] == 'textarea':
100
- is_stretch = True
78
+ )
79
+ ui.config[parent_id] = widgets
101
80
 
102
- rows = QVBoxLayout()
103
- for key in options:
104
- rows.addLayout(options[key])
81
+ add_option = self.add_option
82
+ add_row_option = self.add_row_option
83
+ add_raw_option = self.add_raw_option
105
84
 
106
- if not is_stretch:
85
+ rows = QVBoxLayout()
86
+ has_textarea = False
87
+
88
+ for k, f in fields.items():
89
+ t = f.get("type")
90
+ w = widgets.get(k)
91
+ if t in ('int', 'float'):
92
+ row = add_option(w, f)
93
+ elif t in ('text', 'textarea', 'dict', 'combo'):
94
+ row = add_row_option(w, f)
95
+ elif t == 'bool':
96
+ row = add_raw_option(w, f)
97
+ else:
98
+ continue
99
+ rows.addLayout(row)
100
+ if t == 'textarea':
101
+ has_textarea = True
102
+
103
+ if not has_textarea:
107
104
  rows.addStretch()
108
105
 
109
- # footer
110
- self.window.ui.nodes[parent_id + '.btn.save'] = QPushButton(trans("dialog.preset.btn.save"))
111
- self.window.ui.nodes[parent_id + '.btn.save'].clicked.connect(
106
+ save_key = f"{parent_id}.btn.save"
107
+ dismiss_key = f"{parent_id}.btn.dismiss"
108
+
109
+ ui.nodes[save_key] = QPushButton(save_text)
110
+ ui.nodes[save_key].clicked.connect(
112
111
  lambda checked=True, option_key=option_key, parent=parent, fields=fields:
113
- self.window.controller.config.dictionary.save_editor(
112
+ controller.config.dictionary.save_editor(
114
113
  option_key,
115
114
  parent,
116
115
  fields,
117
116
  ))
118
- self.window.ui.nodes[parent_id + '.btn.save'].setAutoDefault(True)
117
+ ui.nodes[save_key].setAutoDefault(True)
119
118
 
120
- self.window.ui.nodes[parent_id + '.btn.dismiss'] = QPushButton(trans("dialog.rename.dismiss"))
121
- self.window.ui.nodes[parent_id + '.btn.dismiss'].clicked.connect(
122
- lambda checked=True, parent_id=parent_id: self.window.ui.dialogs.close('editor.' + parent_id)
119
+ ui.nodes[dismiss_key] = QPushButton(dismiss_text)
120
+ ui.nodes[dismiss_key].clicked.connect(
121
+ lambda checked=True, pid=parent_id: ui.dialogs.close(f'editor.{pid}')
123
122
  )
124
- self.window.ui.nodes[parent_id + '.btn.dismiss'].setAutoDefault(False)
123
+ ui.nodes[dismiss_key].setAutoDefault(False)
125
124
 
126
125
  footer = QHBoxLayout()
127
- footer.addWidget(self.window.ui.nodes[parent_id + '.btn.dismiss'])
128
- footer.addWidget(self.window.ui.nodes[parent_id + '.btn.save'])
126
+ footer.addWidget(ui.nodes[dismiss_key])
127
+ footer.addWidget(ui.nodes[save_key])
129
128
 
130
- # scroll area
131
129
  scroll = QScrollArea()
132
130
  scroll.setWidgetResizable(True)
133
131
  scroll.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
@@ -138,10 +136,10 @@ class Dictionary(BaseConfigDialog):
138
136
  widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
139
137
 
140
138
  layout = QVBoxLayout()
141
- layout.addWidget(scroll) # options
142
- layout.addLayout(footer) # footer
139
+ layout.addWidget(scroll)
140
+ layout.addLayout(footer)
143
141
 
144
- # dialog
145
- self.window.ui.dialog['editor.' + parent_id] = EditorDialog(self.window, parent_id) # current idx here
146
- self.window.ui.dialog['editor.' + parent_id].setLayout(layout)
147
- self.window.ui.dialog['editor.' + parent_id].setWindowTitle(trans('action.edit'))
142
+ dialog_key = f'editor.{parent_id}'
143
+ ui.dialog[dialog_key] = EditorDialog(self.window, parent_id)
144
+ ui.dialog[dialog_key].setLayout(layout)
145
+ ui.dialog[dialog_key].setWindowTitle(edit_title)
@@ -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.04.09 23:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QPushButton, QHBoxLayout, QLabel, QVBoxLayout
@@ -45,7 +45,8 @@ class Editor:
45
45
  lambda: self.window.controller.settings.editor.load_editor_defaults_app()
46
46
  )
47
47
  self.window.ui.nodes['editor.btn.save'].clicked.connect(
48
- lambda: self.window.core.settings.save_editor())
48
+ lambda: self.window.core.settings.save_editor()
49
+ )
49
50
 
50
51
  bottom_layout = QHBoxLayout()
51
52
  bottom_layout.addWidget(self.window.ui.nodes['editor.btn.default'])
@@ -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.03.26 15:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QCheckBox
@@ -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.11 18:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QPushButton, QHBoxLayout, QVBoxLayout
@@ -44,7 +44,8 @@ class Logger:
44
44
 
45
45
  self.window.ui.nodes['logger.btn.clear'] = QPushButton(trans("dialog.logger.btn.clear"))
46
46
  self.window.ui.nodes['logger.btn.clear'].clicked.connect(
47
- lambda: self.window.controller.debug.clear_logger())
47
+ lambda: self.window.controller.debug.clear_logger()
48
+ )
48
49
 
49
50
  bottom_layout = QHBoxLayout()
50
51
  bottom_layout.addWidget(self.window.ui.nodes['logger.btn.clear'])
@@ -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.24 23:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -16,10 +16,9 @@ from PySide6.QtGui import QStandardItemModel, QIcon
16
16
  from PySide6.QtWidgets import QPushButton, QHBoxLayout, QLabel, QVBoxLayout, QScrollArea, QWidget, QTabWidget, QFrame, \
17
17
  QSplitter, QSizePolicy
18
18
 
19
- from pygpt_net.item.model import ModelItem
20
19
  from pygpt_net.ui.widget.dialog.model import ModelDialog
21
20
  from pygpt_net.ui.widget.element.group import CollapsedGroup
22
- from pygpt_net.ui.widget.element.labels import UrlLabel
21
+ from pygpt_net.ui.widget.element.labels import UrlLabel, DescLabel
23
22
  from pygpt_net.ui.widget.lists.model_editor import ModelEditorList
24
23
  from pygpt_net.ui.widget.option.checkbox import OptionCheckbox
25
24
  from pygpt_net.ui.widget.option.checkbox_list import OptionCheckboxList
@@ -56,13 +55,17 @@ class Models:
56
55
  QPushButton(trans("dialog.models.editor.btn.save"))
57
56
 
58
57
  self.window.ui.nodes['models.editor.btn.new'].clicked.connect(
59
- lambda: self.window.controller.model.editor.new())
58
+ lambda: self.window.controller.model.editor.new()
59
+ )
60
60
  self.window.ui.nodes['models.editor.btn.defaults.user'].clicked.connect(
61
- lambda: self.window.controller.model.editor.load_defaults_user())
61
+ lambda: self.window.controller.model.editor.load_defaults_user()
62
+ )
62
63
  self.window.ui.nodes['models.editor.btn.defaults.app'].clicked.connect(
63
- lambda: self.window.controller.model.editor.load_defaults_app())
64
+ lambda: self.window.controller.model.editor.load_defaults_app()
65
+ )
64
66
  self.window.ui.nodes['models.editor.btn.save'].clicked.connect(
65
- lambda: self.window.controller.model.editor.save())
67
+ lambda: self.window.controller.model.editor.save()
68
+ )
66
69
 
67
70
  # set enter key to save button
68
71
  self.window.ui.nodes['models.editor.btn.new'].setAutoDefault(False)
@@ -111,17 +114,16 @@ class Models:
111
114
  # append advanced options at the end
112
115
  if len(advanced_keys) > 0:
113
116
  group_id = 'models.editor.advanced'
114
- self.window.ui.groups[group_id] = CollapsedGroup(self.window, group_id, None, False, None)
115
- self.window.ui.groups[group_id].box.setText(trans('settings.advanced.collapse'))
117
+ group = CollapsedGroup(self.window, group_id, None, False, None)
118
+ group.box.setText(trans('settings.advanced.collapse'))
116
119
  for key in widgets:
117
120
  if key not in advanced_keys: # ignore non-advanced options
118
121
  continue
119
-
120
- option = self.add_option(widgets[key], options[key]) # build option
121
- self.window.ui.groups[group_id].add_layout(option) # add option to group
122
+ group.add_layout(self.add_option(widgets[key], options[key])) # add option to group
122
123
 
123
124
  # add advanced options group to scroll
124
- content.addWidget(self.window.ui.groups[group_id])
125
+ content.addWidget(group)
126
+ self.window.ui.groups[group_id] = group
125
127
 
126
128
  content.addStretch()
127
129
 
@@ -303,10 +305,8 @@ class Models:
303
305
  layout.addWidget(widget)
304
306
 
305
307
  if desc:
306
- self.window.ui.nodes[desc_key] = QLabel(desc)
307
- self.window.ui.nodes[desc_key].setWordWrap(True)
308
+ self.window.ui.nodes[desc_key] = DescLabel(desc)
308
309
  self.window.ui.nodes[desc_key].setMaximumHeight(40)
309
- self.window.ui.nodes[desc_key].setStyleSheet("font-size: 10px;")
310
310
  layout.addWidget(self.window.ui.nodes[desc_key])
311
311
 
312
312
  line = self.add_line() # TODO: change name to separator
@@ -6,18 +6,18 @@
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.07.18 18:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt
13
13
  from PySide6.QtGui import QStandardItemModel
14
- from PySide6.QtWidgets import QPushButton, QHBoxLayout, QLabel, QVBoxLayout, QScrollArea, QWidget, QTabWidget, QFrame, \
14
+ from PySide6.QtWidgets import QPushButton, QHBoxLayout, QVBoxLayout, QScrollArea, QWidget, QTabWidget, QFrame, \
15
15
  QSplitter, QSizePolicy
16
16
 
17
17
  from pygpt_net.plugin.base.plugin import BasePlugin
18
18
  from pygpt_net.ui.widget.dialog.settings_plugin import PluginSettingsDialog
19
19
  from pygpt_net.ui.widget.element.group import CollapsedGroup
20
- from pygpt_net.ui.widget.element.labels import UrlLabel, HelpLabel
20
+ from pygpt_net.ui.widget.element.labels import UrlLabel, HelpLabel, DescLabel, BaseLabel
21
21
  from pygpt_net.ui.widget.lists.plugin import PluginList
22
22
  from pygpt_net.ui.widget.option.checkbox import OptionCheckbox
23
23
  from pygpt_net.ui.widget.option.checkbox_list import OptionCheckboxList
@@ -55,11 +55,14 @@ class Plugins:
55
55
  QPushButton(trans("dialog.plugin.settings.btn.save"))
56
56
 
57
57
  self.window.ui.nodes['plugin.settings.btn.defaults.user'].clicked.connect(
58
- lambda: self.window.controller.plugins.settings.load_defaults_user())
58
+ lambda: self.window.controller.plugins.settings.load_defaults_user()
59
+ )
59
60
  self.window.ui.nodes['plugin.settings.btn.defaults.app'].clicked.connect(
60
- lambda: self.window.controller.plugins.settings.load_defaults_app())
61
+ lambda: self.window.controller.plugins.settings.load_defaults_app()
62
+ )
61
63
  self.window.ui.nodes['plugin.settings.btn.save'].clicked.connect(
62
- lambda: self.window.controller.plugins.settings.save())
64
+ lambda: self.window.controller.plugins.settings.save()
65
+ )
63
66
 
64
67
  # set enter key to save button
65
68
  self.window.ui.nodes['plugin.settings.btn.defaults.user'].setAutoDefault(False)
@@ -77,14 +80,15 @@ class Plugins:
77
80
  self.window.ui.tabs['plugin.settings.tabs'] = {}
78
81
 
79
82
  sorted_ids = self.window.core.plugins.get_ids(sort=True)
83
+ get_plugin = self.window.core.plugins.get
80
84
 
81
85
  # build plugin settings tabs
82
86
  for id in sorted_ids:
83
87
  content_tabs = {}
84
88
  scroll_tabs = {}
85
89
 
86
- plugin = self.window.core.plugins.get(id)
87
- parent_id = "plugin." + id
90
+ plugin = get_plugin(id)
91
+ parent_id = f"plugin.{id}"
88
92
 
89
93
  # create plugin options entry if not exists
90
94
  if parent_id not in self.window.ui.config:
@@ -137,7 +141,7 @@ class Plugins:
137
141
  # append advanced options at the end
138
142
  if len(advanced_keys) > 0:
139
143
  groups = {}
140
- group_id = 'plugin.settings.advanced' + '.' + id
144
+ group_id = f"plugin.settings.advanced.{id}"
141
145
  for key in widgets:
142
146
  if key not in advanced_keys: # ignore non-advanced options
143
147
  continue
@@ -157,7 +161,7 @@ class Plugins:
157
161
 
158
162
  # add advanced options group to scrolls
159
163
  for tab_id in groups:
160
- full_id = group_id + '.' + tab_id
164
+ full_id = f"{group_id}.{tab_id}"
161
165
  content_tabs[tab_id].addWidget(groups[tab_id])
162
166
  self.window.ui.groups[full_id] = groups[tab_id]
163
167
 
@@ -167,17 +171,16 @@ class Plugins:
167
171
 
168
172
  # set description, translate if localization is enabled
169
173
  name_txt = plugin.name
170
- desc_key = 'plugin.settings.' + id + '.desc'
174
+ desc_key = f"plugin.settings.{id}.desc"
171
175
  desc_txt = plugin.description
172
176
  if plugin.use_locale:
173
- domain = 'plugin.' + plugin.id
177
+ domain = f"plugin.{plugin.id}"
174
178
  name_txt = trans('plugin.name', False, domain)
175
179
  desc_txt = trans('plugin.description', False, domain)
176
180
 
177
- self.window.ui.nodes[desc_key] = QLabel(desc_txt)
178
- self.window.ui.nodes[desc_key].setWordWrap(True)
181
+ self.window.ui.nodes[desc_key] = DescLabel(desc_txt)
179
182
  self.window.ui.nodes[desc_key].setAlignment(Qt.AlignCenter)
180
- self.window.ui.nodes[desc_key].setStyleSheet("font-weight: bold;")
183
+ #self.window.ui.nodes[desc_key].setStyleSheet("font-weight: bold;")
181
184
 
182
185
  line = self.add_line()
183
186
 
@@ -234,8 +237,7 @@ class Plugins:
234
237
 
235
238
  data = {}
236
239
  for plugin_id in sorted_ids:
237
- plugin = self.window.core.plugins.get(plugin_id)
238
- data[plugin_id] = plugin
240
+ data[plugin_id] = get_plugin(plugin_id)
239
241
 
240
242
  # plugins list
241
243
  id = 'plugin.list'
@@ -250,12 +252,13 @@ class Plugins:
250
252
  self.window.ui.nodes[id].setMinimumWidth(self.max_list_width)
251
253
 
252
254
  # splitter
253
- self.window.ui.splitters['dialog.plugins'] = QSplitter(Qt.Horizontal)
254
- self.window.ui.splitters['dialog.plugins'].addWidget(self.window.ui.nodes[id]) # list
255
- self.window.ui.splitters['dialog.plugins'].addWidget(self.window.ui.tabs['plugin.settings']) # tabs
256
- self.window.ui.splitters['dialog.plugins'].setStretchFactor(0, 2)
257
- self.window.ui.splitters['dialog.plugins'].setStretchFactor(1, 5)
258
- self.window.ui.splitters['dialog.plugins'].setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
255
+ splitter = QSplitter(Qt.Horizontal)
256
+ splitter.addWidget(self.window.ui.nodes[id]) # list
257
+ splitter.addWidget(self.window.ui.tabs['plugin.settings']) # tabs
258
+ splitter.setStretchFactor(0, 2)
259
+ splitter.setStretchFactor(1, 5)
260
+ splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
261
+ self.window.ui.splitters['dialog.plugins'] = splitter
259
262
 
260
263
  main_layout = QHBoxLayout()
261
264
  main_layout.addWidget(self.window.ui.splitters['dialog.plugins'])
@@ -279,7 +282,7 @@ class Plugins:
279
282
  self.window.ui.tabs['plugin.settings'].setCurrentIndex(idx)
280
283
  self.window.controller.plugins.set_by_tab(idx)
281
284
  except Exception as e:
282
- print('Failed restore plugin settings tab: {}'.format(idx))
285
+ print(f"Failed restore plugin settings tab: {idx}", e)
283
286
  else:
284
287
  self.window.controller.plugins.set_by_tab(0)
285
288
 
@@ -317,36 +320,39 @@ class Plugins:
317
320
  :return: dict of widgets
318
321
  """
319
322
  id = plugin.id
320
- parent = "plugin." + id # parent id for plugins is in format: plugin.<plugin_id>
323
+ parent = f"plugin.{id}" # parent id for plugins is in format: plugin.<plugin_id>
321
324
  widgets = {}
325
+ base_types = ('text', 'int', 'float')
326
+ number_types = ('int', 'float')
327
+ apply = self.window.controller.config.placeholder.apply
322
328
 
323
329
  for key in options:
324
330
  option = options[key]
331
+ t = option['type']
325
332
  # create widget by option type
326
- if option['type'] == 'text' or option['type'] == 'int' or option['type'] == 'float':
327
- if 'slider' in option and option['slider'] \
328
- and (option['type'] == 'int' or option['type'] == 'float'):
333
+ if t in base_types:
334
+ if 'slider' in option and option['slider'] and t in number_types:
329
335
  widgets[key] = OptionSlider(self.window, parent, key, option) # slider + text input
330
336
  else:
331
337
  if 'secret' in option and option['secret']:
332
338
  widgets[key] = PasswordInput(self.window, parent, key, option) # password input
333
339
  else:
334
340
  widgets[key] = OptionInput(self.window, parent, key, option) # text input
335
- elif option['type'] == 'textarea':
341
+ elif t == 'textarea':
336
342
  widgets[key] = OptionTextarea(self.window, parent, key, option) # textarea
337
- elif option['type'] == 'bool':
343
+ elif t == 'bool':
338
344
  widgets[key] = OptionCheckbox(self.window, parent, key, option) # checkbox
339
- elif option['type'] == 'bool_list':
340
- self.window.controller.config.placeholder.apply(option)
345
+ elif t == 'bool_list':
346
+ apply(option)
341
347
  widgets[key] = OptionCheckboxList(self.window, parent, key, option) # checkbox list
342
- elif option['type'] == 'dict':
343
- self.window.controller.config.placeholder.apply(option)
348
+ elif t == 'dict':
349
+ apply(option)
344
350
  widgets[key] = OptionDict(self.window, parent, key, option) # dictionary
345
- elif option['type'] == 'combo':
346
- self.window.controller.config.placeholder.apply(option)
351
+ elif t == 'combo':
352
+ apply(option)
347
353
  widgets[key] = OptionCombo(self.window, parent, key, option) # combobox
348
- elif option['type'] == 'cmd':
349
- self.window.controller.config.placeholder.apply(option)
354
+ elif t == 'cmd':
355
+ apply(option)
350
356
  widgets[key] = OptionCmd(self.window, plugin, parent, key, option) # command
351
357
 
352
358
  return widgets
@@ -396,14 +402,14 @@ class Plugins:
396
402
  :param option: option dict
397
403
  :return: QVBoxLayout
398
404
  """
399
- one_column_types = ['textarea', 'dict', 'bool', 'cmd']
400
- no_label_types = ['bool', 'cmd']
401
- no_desc_types = ['cmd']
405
+ one_column_types = ('textarea', 'dict', 'bool', 'cmd')
406
+ no_label_types = ('bool', 'cmd')
407
+ no_desc_types = 'cmd'
402
408
  allow_locale = True
403
409
 
404
410
  key = option['id']
405
- label_key = 'plugin.' + plugin.id + '.' + key + '.label'
406
- desc_key = 'plugin.' + plugin.id + '.' + key + '.desc'
411
+ label_key = f"plugin.{plugin.id}.{key}.label"
412
+ desc_key = f"plugin.{plugin.id}.{key}.desc"
407
413
 
408
414
  # get option label and description
409
415
  txt_title = option['label']
@@ -415,14 +421,14 @@ class Plugins:
415
421
 
416
422
  # translate if localization is enabled
417
423
  if plugin.use_locale and allow_locale:
418
- domain = 'plugin.' + plugin.id
419
- txt_title = trans(key + '.label', False, domain)
420
- txt_desc = trans(key + '.description', False, domain)
421
- txt_tooltip = trans(key + '.tooltip', False, domain)
424
+ domain = f"plugin.{plugin.id}"
425
+ txt_title = trans(f"{key}.label", False, domain)
426
+ txt_desc = trans(f"{key}.description", False, domain)
427
+ # txt_tooltip = trans(f"{key}.tooltip", False, domain)
422
428
 
423
429
  # if empty tooltip then use description
424
- if txt_tooltip == key + '.tooltip':
425
- txt_tooltip = txt_desc
430
+ # if txt_tooltip == f"{key}.tooltip":
431
+ # txt_tooltip = txt_desc
426
432
 
427
433
 
428
434
  """
@@ -431,7 +437,7 @@ class Plugins:
431
437
  """
432
438
 
433
439
  if option['type'] not in no_label_types:
434
- self.window.ui.nodes[label_key] = QLabel(txt_title)
440
+ self.window.ui.nodes[label_key] = BaseLabel(txt_title)
435
441
  self.window.ui.nodes[label_key].setStyleSheet("font-weight: bold;")
436
442
 
437
443
  # 2-columns layout
@@ -447,10 +453,8 @@ class Plugins:
447
453
  cols_widget.setLayout(cols)
448
454
  # cols_widget.setMaximumHeight(90)
449
455
 
450
- self.window.ui.nodes[desc_key] = QLabel(txt_desc)
451
- self.window.ui.nodes[desc_key].setWordWrap(True)
456
+ self.window.ui.nodes[desc_key] = DescLabel(txt_desc)
452
457
  self.window.ui.nodes[desc_key].setMaximumHeight(40)
453
- self.window.ui.nodes[desc_key].setStyleSheet("font-size: 10px;")
454
458
  # self.window.ui.nodes[desc_key].setToolTip(txt_tooltip)
455
459
 
456
460
  layout = QVBoxLayout()
@@ -468,10 +472,8 @@ class Plugins:
468
472
  layout.addWidget(widget)
469
473
 
470
474
  if option['type'] not in no_desc_types:
471
- self.window.ui.nodes[desc_key] = QLabel(txt_desc)
472
- self.window.ui.nodes[desc_key].setWordWrap(True)
475
+ self.window.ui.nodes[desc_key] = DescLabel(txt_desc)
473
476
  self.window.ui.nodes[desc_key].setMaximumHeight(40)
474
- self.window.ui.nodes[desc_key].setStyleSheet("font-size: 10px;")
475
477
  # self.window.ui.nodes[desc_key].setToolTip(txt_tooltip)
476
478
  layout.addWidget(self.window.ui.nodes[desc_key])
477
479
 
@@ -511,12 +513,13 @@ class Plugins:
511
513
  :param id: ID of the list
512
514
  :param data: Data to update
513
515
  """
514
- self.window.ui.models[id].removeRows(0, self.window.ui.models[id].rowCount())
516
+ model = self.window.ui.models[id]
517
+ model.removeRows(0, model.rowCount())
515
518
  i = 0
516
519
  for n in data:
517
- self.window.ui.models[id].insertRow(i)
520
+ model.insertRow(i)
518
521
  name = self.window.core.plugins.get_name(data[n].id)
519
522
  tooltip = self.window.core.plugins.get_desc(data[n].id)
520
- self.window.ui.models[id].setData(self.window.ui.models[id].index(i, 0), name)
521
- self.window.ui.models[id].setData(self.window.ui.models[id].index(i, 0), tooltip, Qt.ToolTipRole)
523
+ model.setData(model.index(i, 0), name)
524
+ model.setData(model.index(i, 0), tooltip, Qt.ToolTipRole)
522
525
  i += 1
@@ -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.24 23:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6 import QtCore
@@ -210,7 +210,7 @@ class CtxList:
210
210
  files_str = ", ".join(files)
211
211
  if len(files_str) > 40:
212
212
  files_str = files_str[:40] + '...'
213
- tooltip_str = trans("attachments.ctx.tooltip.list").format(num=len(files)) + ": " + files_str
213
+ tooltip_str = f"{trans('attachments.ctx.tooltip.list').format(num=len(files))}: {files_str}"
214
214
  group_item.setToolTip(tooltip_str)
215
215
 
216
216
  group_item.setData(custom_data, QtCore.Qt.ItemDataRole.UserRole)
@@ -282,8 +282,7 @@ class CtxList:
282
282
  files_str = ", ".join(files)
283
283
  if len(files_str) > 40:
284
284
  files_str = files_str[:40] + '...'
285
- tooltip_str = trans("attachments.ctx.tooltip.list").format(num=len(files)) + ": " + files_str
286
- tooltip_text += "\n" + tooltip_str
285
+ tooltip_text += f"\n{trans('attachments.ctx.tooltip.list').format(num=len(files))}: {files_str}"
287
286
 
288
287
  item = Item(name, id)
289
288
  item.id = id
@@ -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.17 03:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from .toolbox import *
12
+ from .toolbox import ToolboxMain
@@ -115,15 +115,14 @@ class Assistants:
115
115
  view.backup_selection()
116
116
  view.setUpdatesEnabled(False)
117
117
  try:
118
- with QtCore.QSignalBlocker(model):
119
- model.setRowCount(0)
120
- count = len(data)
121
- if count:
122
- model.setRowCount(count)
123
- for i, item in enumerate(data.values()):
124
- index = model.index(i, 0)
125
- model.setData(index, "ID: " + item.id, QtCore.Qt.ToolTipRole)
126
- model.setData(index, item.name)
118
+ model.setRowCount(0)
119
+ count = len(data)
120
+ if count:
121
+ model.setRowCount(count)
122
+ for i, item in enumerate(data.values()):
123
+ index = model.index(i, 0)
124
+ model.setData(index, "ID: " + item.id, QtCore.Qt.ToolTipRole)
125
+ model.setData(index, item.name)
127
126
  finally:
128
127
  view.setUpdatesEnabled(True)
129
128
 
@@ -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.24 23:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6 import QtCore
@@ -144,7 +144,7 @@ class Presets:
144
144
  for i, (key, item) in enumerate(data.items()):
145
145
  name = item.name
146
146
  if is_expert_mode and item.enabled and not key.startswith(startswith_current):
147
- name = "[x] " + name
147
+ name = f"[x] {name}"
148
148
  elif is_agent_mode:
149
149
  num_experts = count_experts(key)
150
150
  if num_experts > 0: