pygpt-net 2.6.59__py3-none-any.whl → 2.6.61__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.
- pygpt_net/CHANGELOG.txt +11 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +9 -5
- pygpt_net/controller/__init__.py +1 -0
- pygpt_net/controller/chat/common.py +115 -6
- pygpt_net/controller/chat/input.py +4 -1
- pygpt_net/controller/presets/editor.py +442 -39
- pygpt_net/controller/presets/presets.py +121 -6
- pygpt_net/controller/settings/editor.py +0 -15
- pygpt_net/controller/theme/markdown.py +2 -5
- pygpt_net/controller/ui/ui.py +4 -7
- pygpt_net/core/agents/custom/__init__.py +281 -0
- pygpt_net/core/agents/custom/debug.py +64 -0
- pygpt_net/core/agents/custom/factory.py +109 -0
- pygpt_net/core/agents/custom/graph.py +71 -0
- pygpt_net/core/agents/custom/llama_index/__init__.py +10 -0
- pygpt_net/core/agents/custom/llama_index/factory.py +100 -0
- pygpt_net/core/agents/custom/llama_index/router_streamer.py +106 -0
- pygpt_net/core/agents/custom/llama_index/runner.py +562 -0
- pygpt_net/core/agents/custom/llama_index/stream.py +56 -0
- pygpt_net/core/agents/custom/llama_index/utils.py +253 -0
- pygpt_net/core/agents/custom/logging.py +50 -0
- pygpt_net/core/agents/custom/memory.py +51 -0
- pygpt_net/core/agents/custom/router.py +155 -0
- pygpt_net/core/agents/custom/router_streamer.py +187 -0
- pygpt_net/core/agents/custom/runner.py +455 -0
- pygpt_net/core/agents/custom/schema.py +127 -0
- pygpt_net/core/agents/custom/utils.py +193 -0
- pygpt_net/core/agents/provider.py +72 -7
- pygpt_net/core/agents/runner.py +7 -4
- pygpt_net/core/agents/runners/helpers.py +1 -1
- pygpt_net/core/agents/runners/llama_workflow.py +3 -0
- pygpt_net/core/agents/runners/openai_workflow.py +8 -1
- pygpt_net/core/db/viewer.py +11 -5
- pygpt_net/{ui/widget/builder → core/node_editor}/__init__.py +2 -2
- pygpt_net/core/{builder → node_editor}/graph.py +28 -226
- pygpt_net/core/node_editor/models.py +118 -0
- pygpt_net/core/node_editor/types.py +78 -0
- pygpt_net/core/node_editor/utils.py +17 -0
- pygpt_net/core/presets/presets.py +216 -29
- pygpt_net/core/render/markdown/parser.py +0 -2
- pygpt_net/core/render/web/renderer.py +10 -8
- pygpt_net/data/config/config.json +5 -6
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/settings.json +2 -38
- pygpt_net/data/locale/locale.de.ini +64 -1
- pygpt_net/data/locale/locale.en.ini +63 -4
- pygpt_net/data/locale/locale.es.ini +64 -1
- pygpt_net/data/locale/locale.fr.ini +64 -1
- pygpt_net/data/locale/locale.it.ini +64 -1
- pygpt_net/data/locale/locale.pl.ini +65 -2
- pygpt_net/data/locale/locale.uk.ini +64 -1
- pygpt_net/data/locale/locale.zh.ini +64 -1
- pygpt_net/data/locale/plugin.cmd_system.en.ini +62 -66
- pygpt_net/item/agent.py +5 -1
- pygpt_net/item/preset.py +19 -1
- pygpt_net/provider/agents/base.py +33 -2
- pygpt_net/provider/agents/llama_index/flow_from_schema.py +92 -0
- pygpt_net/provider/agents/openai/flow_from_schema.py +96 -0
- pygpt_net/provider/core/agent/json_file.py +11 -5
- pygpt_net/provider/core/config/patch.py +10 -1
- pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +0 -6
- pygpt_net/tools/agent_builder/tool.py +233 -52
- pygpt_net/tools/agent_builder/ui/dialogs.py +172 -28
- pygpt_net/tools/agent_builder/ui/list.py +37 -10
- pygpt_net/ui/__init__.py +2 -4
- pygpt_net/ui/dialog/about.py +58 -38
- pygpt_net/ui/dialog/db.py +142 -3
- pygpt_net/ui/dialog/preset.py +62 -8
- pygpt_net/ui/layout/toolbox/presets.py +52 -16
- pygpt_net/ui/main.py +1 -1
- pygpt_net/ui/widget/dialog/db.py +0 -0
- pygpt_net/ui/widget/lists/preset.py +644 -60
- pygpt_net/{core/builder → ui/widget/node_editor}/__init__.py +2 -2
- pygpt_net/ui/widget/node_editor/command.py +373 -0
- pygpt_net/ui/widget/node_editor/config.py +157 -0
- pygpt_net/ui/widget/node_editor/editor.py +2070 -0
- pygpt_net/ui/widget/node_editor/item.py +493 -0
- pygpt_net/ui/widget/node_editor/node.py +1460 -0
- pygpt_net/ui/widget/node_editor/utils.py +17 -0
- pygpt_net/ui/widget/node_editor/view.py +364 -0
- pygpt_net/ui/widget/tabs/output.py +1 -1
- pygpt_net/ui/widget/textarea/input.py +2 -2
- pygpt_net/utils.py +114 -2
- {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/METADATA +80 -93
- {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/RECORD +88 -61
- pygpt_net/core/agents/custom.py +0 -150
- pygpt_net/ui/widget/builder/editor.py +0 -2001
- {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/entry_points.txt +0 -0
|
@@ -6,18 +6,20 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.09.
|
|
9
|
+
# Updated Date: 2025.09.24 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
|
-
from PySide6.QtCore import Qt
|
|
12
|
+
from PySide6.QtCore import Qt, QEvent
|
|
13
13
|
from PySide6.QtGui import QAction, QIcon
|
|
14
|
-
from PySide6.QtWidgets import QVBoxLayout, QMenuBar, QSplitter
|
|
14
|
+
from PySide6.QtWidgets import QVBoxLayout, QMenuBar, QSplitter, QSizePolicy, QWidget
|
|
15
15
|
|
|
16
|
-
from pygpt_net.
|
|
17
|
-
from pygpt_net.ui.widget.
|
|
16
|
+
from pygpt_net.ui.widget.element.labels import HelpLabel
|
|
17
|
+
from pygpt_net.ui.widget.node_editor.editor import NodeEditor
|
|
18
18
|
from pygpt_net.ui.widget.dialog.base import BaseDialog
|
|
19
19
|
from pygpt_net.utils import trans
|
|
20
20
|
|
|
21
|
+
from .list import AgentsWidget
|
|
22
|
+
|
|
21
23
|
|
|
22
24
|
class Builder:
|
|
23
25
|
|
|
@@ -34,18 +36,18 @@ class Builder:
|
|
|
34
36
|
self.actions = {}
|
|
35
37
|
|
|
36
38
|
def setup_menu(self, parent=None) -> QMenuBar:
|
|
37
|
-
"""Setup
|
|
39
|
+
"""Setup agent_builder dialog menu"""
|
|
38
40
|
self.menu_bar = QMenuBar(parent)
|
|
39
41
|
self.file_menu = self.menu_bar.addMenu(trans("menu.file"))
|
|
40
42
|
t = self.tool
|
|
41
43
|
|
|
42
|
-
self.actions["open"] = QAction(QIcon(":/icons/
|
|
44
|
+
self.actions["open"] = QAction(QIcon(":/icons/reload.svg"), trans("action.reload"), self.menu_bar)
|
|
43
45
|
self.actions["open"].triggered.connect(lambda checked=False, t=t: t.load())
|
|
44
46
|
|
|
45
47
|
self.actions["save"] = QAction(QIcon(":/icons/save.svg"), trans("action.save"), self.menu_bar)
|
|
46
48
|
self.actions["save"].triggered.connect(lambda checked=False, t=t: t.save())
|
|
47
49
|
|
|
48
|
-
self.actions["clear"] = QAction(QIcon(":/icons/
|
|
50
|
+
self.actions["clear"] = QAction(QIcon(":/icons/close.svg"), trans("action.clear"), self.menu_bar)
|
|
49
51
|
self.actions["clear"].triggered.connect(lambda checked=False, t=t: t.clear())
|
|
50
52
|
|
|
51
53
|
self.file_menu.addAction(self.actions["open"])
|
|
@@ -55,60 +57,124 @@ class Builder:
|
|
|
55
57
|
|
|
56
58
|
def setup(self):
|
|
57
59
|
"""Setup agent_builder dialog"""
|
|
60
|
+
# Ensure previous deferred deletions are flushed before building a new editor instance
|
|
61
|
+
try:
|
|
62
|
+
from PySide6.QtWidgets import QApplication
|
|
63
|
+
QApplication.sendPostedEvents(None, QEvent.DeferredDelete)
|
|
64
|
+
QApplication.processEvents()
|
|
65
|
+
except Exception:
|
|
66
|
+
pass
|
|
67
|
+
|
|
58
68
|
id = 'agent.builder'
|
|
59
69
|
u = self.window.ui
|
|
60
70
|
|
|
61
71
|
u.dialog['agent.builder'] = BuilderDialog(self.window)
|
|
62
72
|
dlg = u.dialog['agent.builder']
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
editor
|
|
74
|
+
registry = self.tool.get_registry() # node types
|
|
75
|
+
editor = NodeEditor(
|
|
76
|
+
parent=dlg,
|
|
77
|
+
registry=registry
|
|
78
|
+
) # parent == dialog
|
|
79
|
+
|
|
80
|
+
theme = self.window.core.config.get("theme")
|
|
81
|
+
if theme.startswith("light"):
|
|
82
|
+
style = """
|
|
83
|
+
NodeEditor {
|
|
84
|
+
qproperty-gridBackColor: #ffffff;
|
|
85
|
+
qproperty-gridPenColor: #eaeaea;
|
|
86
|
+
|
|
87
|
+
qproperty-nodeBackgroundColor: #2d2f34;
|
|
88
|
+
qproperty-nodeBorderColor: #4b4f57;
|
|
89
|
+
qproperty-nodeSelectionColor: #ff9900;
|
|
90
|
+
qproperty-nodeTitleColor: #3a3d44;
|
|
91
|
+
|
|
92
|
+
qproperty-portInputColor: #66b2ff;
|
|
93
|
+
qproperty-portOutputColor: #70e070;
|
|
94
|
+
qproperty-portConnectedColor: #ffd166;
|
|
95
|
+
|
|
96
|
+
qproperty-edgeColor: #c0c0c0;
|
|
97
|
+
qproperty-edgeSelectedColor: #ff8a5c;
|
|
98
|
+
}
|
|
99
|
+
"""
|
|
100
|
+
else:
|
|
101
|
+
style = """
|
|
66
102
|
NodeEditor {
|
|
67
103
|
qproperty-gridBackColor: #242629;
|
|
68
104
|
qproperty-gridPenColor: #3b3f46;
|
|
69
|
-
|
|
105
|
+
|
|
70
106
|
qproperty-nodeBackgroundColor: #2d2f34;
|
|
71
107
|
qproperty-nodeBorderColor: #4b4f57;
|
|
72
108
|
qproperty-nodeSelectionColor: #ff9900;
|
|
73
109
|
qproperty-nodeTitleColor: #3a3d44;
|
|
74
|
-
|
|
110
|
+
|
|
75
111
|
qproperty-portInputColor: #66b2ff;
|
|
76
112
|
qproperty-portOutputColor: #70e070;
|
|
77
113
|
qproperty-portConnectedColor: #ffd166;
|
|
78
|
-
|
|
114
|
+
|
|
79
115
|
qproperty-edgeColor: #c0c0c0;
|
|
80
116
|
qproperty-edgeSelectedColor: #ff8a5c;
|
|
81
117
|
}
|
|
82
|
-
"""
|
|
83
|
-
|
|
84
|
-
# Demo: add a couple of nodes
|
|
85
|
-
#editor.add_node("Value/Float", QPointF(100, 120))
|
|
86
|
-
#editor.add_node("Value/Float", QPointF(100, 240))
|
|
87
|
-
#editor.add_node("Math/Add", QPointF(420, 180))
|
|
118
|
+
"""
|
|
119
|
+
editor.setStyleSheet(style)
|
|
88
120
|
editor.on_clear = self.tool.clear
|
|
121
|
+
editor.editing_allowed = self.tool.editing_allowed
|
|
89
122
|
|
|
90
123
|
u.editor[id] = editor
|
|
91
124
|
|
|
92
125
|
layout = QVBoxLayout()
|
|
93
126
|
layout.setMenuBar(self.setup_menu(dlg))
|
|
94
127
|
|
|
95
|
-
agents_list = AgentsWidget(self.window, tool=self.tool)
|
|
128
|
+
agents_list = AgentsWidget(self.window, tool=self.tool, parent=dlg)
|
|
96
129
|
list_widget = agents_list.setup()
|
|
97
|
-
|
|
130
|
+
|
|
131
|
+
# Left side container: list fills all space, help label stays at the bottom
|
|
132
|
+
left_side = QWidget(dlg)
|
|
133
|
+
left_layout = QVBoxLayout(left_side)
|
|
134
|
+
left_layout.setContentsMargins(0, 0, 0, 0)
|
|
135
|
+
left_layout.setSpacing(6)
|
|
136
|
+
|
|
137
|
+
left_help_label = HelpLabel(trans("node.editor.list.tip"))
|
|
138
|
+
left_help_label.setWordWrap(True)
|
|
139
|
+
left_help_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
|
140
|
+
left_help_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
|
141
|
+
left_help_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum)
|
|
142
|
+
|
|
143
|
+
left_layout.addWidget(list_widget)
|
|
144
|
+
left_layout.addWidget(left_help_label)
|
|
145
|
+
left_layout.setStretch(0, 1) # list -> fills all vertical space
|
|
146
|
+
|
|
147
|
+
# Fix the width of the whole left panel (not only the list)
|
|
148
|
+
left_side.setFixedWidth(250)
|
|
149
|
+
|
|
98
150
|
center_splitter = QSplitter(Qt.Horizontal)
|
|
99
|
-
center_splitter.addWidget(
|
|
151
|
+
center_splitter.addWidget(left_side)
|
|
100
152
|
center_splitter.addWidget(u.editor[id])
|
|
101
153
|
center_splitter.setStretchFactor(0, 1)
|
|
102
154
|
center_splitter.setStretchFactor(1, 8)
|
|
103
155
|
layout.addWidget(center_splitter)
|
|
156
|
+
# Make the splitter take all extra vertical space so the legend stays compact at the bottom
|
|
157
|
+
layout.setStretch(0, 1)
|
|
104
158
|
|
|
159
|
+
# Bottom legend as a compact, centered help label
|
|
160
|
+
legend_label = HelpLabel(trans("node.editor.bottom.tip"))
|
|
161
|
+
legend_label.setAlignment(Qt.AlignCenter)
|
|
162
|
+
legend_label.setWordWrap(True)
|
|
163
|
+
legend_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
|
164
|
+
legend_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum)
|
|
165
|
+
|
|
166
|
+
layout.addWidget(legend_label)
|
|
167
|
+
|
|
168
|
+
u.nodes["agent.builder.splitter"] = center_splitter
|
|
105
169
|
u.nodes["agent.builder.list"] = agents_list
|
|
170
|
+
u.nodes["agent.builder.legend"] = legend_label
|
|
171
|
+
u.nodes["agent.builder.list.help"] = left_help_label
|
|
106
172
|
|
|
107
173
|
dlg.setLayout(layout)
|
|
108
174
|
dlg.setWindowTitle(trans("agent.builder.title"))
|
|
109
175
|
|
|
110
176
|
def clear(self):
|
|
111
|
-
"""Clear
|
|
177
|
+
"""Clear dialog"""
|
|
112
178
|
pass
|
|
113
179
|
|
|
114
180
|
|
|
@@ -123,6 +189,7 @@ class BuilderDialog(BaseDialog):
|
|
|
123
189
|
super(BuilderDialog, self).__init__(window, id)
|
|
124
190
|
self.window = window
|
|
125
191
|
self.id = id # id
|
|
192
|
+
self._cleaned = False
|
|
126
193
|
|
|
127
194
|
def closeEvent(self, event):
|
|
128
195
|
"""
|
|
@@ -140,13 +207,90 @@ class BuilderDialog(BaseDialog):
|
|
|
140
207
|
:param event: key press event
|
|
141
208
|
"""
|
|
142
209
|
if event.key() == Qt.Key_Escape:
|
|
143
|
-
self.cleanup()
|
|
144
210
|
self.close()
|
|
145
211
|
else:
|
|
146
212
|
super(BuilderDialog, self).keyPressEvent(event)
|
|
147
213
|
|
|
148
214
|
def cleanup(self):
|
|
149
|
-
"""
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
self.
|
|
215
|
+
"""Cleanup on dialog close"""
|
|
216
|
+
if self._cleaned:
|
|
217
|
+
return
|
|
218
|
+
self._cleaned = True
|
|
219
|
+
|
|
220
|
+
tool = self.window.tools.get("agent_builder")
|
|
221
|
+
if tool:
|
|
222
|
+
try:
|
|
223
|
+
tool.on_close() # store current state (layout, nodes positions, etc.)
|
|
224
|
+
except Exception:
|
|
225
|
+
pass
|
|
226
|
+
|
|
227
|
+
u = self.window.ui
|
|
228
|
+
|
|
229
|
+
try:
|
|
230
|
+
u.nodes["agent.builder.list"].cleanup()
|
|
231
|
+
except Exception as e:
|
|
232
|
+
print("Agents list close failed:", e)
|
|
233
|
+
|
|
234
|
+
splitter = u.nodes.get("agent.builder.splitter")
|
|
235
|
+
editor = u.editor.pop('agent.builder', None)
|
|
236
|
+
|
|
237
|
+
# Detach editor from splitter before closing/deleting it to avoid dangling pointers in QSplitter
|
|
238
|
+
try:
|
|
239
|
+
if splitter is not None and editor is not None:
|
|
240
|
+
# Ensure the editor actually belongs to this splitter
|
|
241
|
+
idx = splitter.indexOf(editor) if editor.parent() is splitter else -1
|
|
242
|
+
if idx != -1:
|
|
243
|
+
# Create the replacement without a parent; QSplitter will adopt it.
|
|
244
|
+
from PySide6.QtWidgets import QWidget as _QW
|
|
245
|
+
placeholder = _QW()
|
|
246
|
+
splitter.replaceWidget(idx, placeholder)
|
|
247
|
+
except Exception as e:
|
|
248
|
+
print("Splitter detach failed:", e)
|
|
249
|
+
|
|
250
|
+
if editor is not None:
|
|
251
|
+
try:
|
|
252
|
+
editor.setStyleSheet("")
|
|
253
|
+
except Exception:
|
|
254
|
+
pass
|
|
255
|
+
try:
|
|
256
|
+
editor.close()
|
|
257
|
+
except Exception as e:
|
|
258
|
+
print("NodeEditor close failed:", e)
|
|
259
|
+
try:
|
|
260
|
+
editor.setParent(None)
|
|
261
|
+
editor.deleteLater()
|
|
262
|
+
except Exception:
|
|
263
|
+
pass
|
|
264
|
+
|
|
265
|
+
# Dispose legend label safely
|
|
266
|
+
legend = u.nodes.pop("agent.builder.legend", None)
|
|
267
|
+
if legend is not None:
|
|
268
|
+
try:
|
|
269
|
+
legend.setParent(None)
|
|
270
|
+
legend.deleteLater()
|
|
271
|
+
except Exception:
|
|
272
|
+
pass
|
|
273
|
+
|
|
274
|
+
# Dispose left-side help label safely
|
|
275
|
+
try:
|
|
276
|
+
help_lbl = u.nodes.pop("agent.builder.list.help", None)
|
|
277
|
+
if help_lbl is not None:
|
|
278
|
+
help_lbl.setParent(None)
|
|
279
|
+
help_lbl.deleteLater()
|
|
280
|
+
except Exception:
|
|
281
|
+
pass
|
|
282
|
+
|
|
283
|
+
# Drop splitter reference
|
|
284
|
+
try:
|
|
285
|
+
u.nodes.pop("agent.builder.splitter", None)
|
|
286
|
+
except Exception:
|
|
287
|
+
pass
|
|
288
|
+
|
|
289
|
+
# Ensure all deferred deletions are processed (do it a few rounds to be safe)
|
|
290
|
+
try:
|
|
291
|
+
from PySide6.QtWidgets import QApplication
|
|
292
|
+
for _ in range(3):
|
|
293
|
+
QApplication.sendPostedEvents(None, QEvent.DeferredDelete)
|
|
294
|
+
QApplication.processEvents()
|
|
295
|
+
except Exception:
|
|
296
|
+
pass
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.09.24 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6 import QtCore
|
|
@@ -19,7 +19,7 @@ from pygpt_net.utils import trans
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class AgentsWidget:
|
|
22
|
-
def __init__(self, window=None, tool=None):
|
|
22
|
+
def __init__(self, window=None, tool=None, parent=None):
|
|
23
23
|
"""
|
|
24
24
|
Agents select widget
|
|
25
25
|
|
|
@@ -27,6 +27,7 @@ class AgentsWidget:
|
|
|
27
27
|
:param tool: tool instance
|
|
28
28
|
"""
|
|
29
29
|
self.window = window
|
|
30
|
+
self.parent = parent
|
|
30
31
|
self.tool = tool
|
|
31
32
|
self.id = "agent.builder.list"
|
|
32
33
|
self.list = None
|
|
@@ -37,21 +38,42 @@ class AgentsWidget:
|
|
|
37
38
|
|
|
38
39
|
:return: QWidget
|
|
39
40
|
"""
|
|
40
|
-
new_btn = QPushButton(QIcon(":/icons/add.svg"), "")
|
|
41
|
+
new_btn = QPushButton(QIcon(":/icons/add.svg"), "", self.parent)
|
|
41
42
|
new_btn.clicked.connect(self.action_new)
|
|
42
43
|
|
|
43
|
-
self.list = AgentsList(self.
|
|
44
|
+
self.list = AgentsList(self.parent, tool=self.tool, id=self.id)
|
|
44
45
|
layout = QVBoxLayout()
|
|
45
46
|
layout.addWidget(new_btn)
|
|
46
47
|
layout.addWidget(self.list)
|
|
47
48
|
|
|
48
|
-
self.window.ui.models[self.id] = self.create_model(self.
|
|
49
|
+
self.window.ui.models[self.id] = self.create_model(self.parent)
|
|
49
50
|
self.list.setModel(self.window.ui.models[self.id])
|
|
50
51
|
|
|
51
52
|
widget = QWidget()
|
|
52
53
|
widget.setLayout(layout)
|
|
53
54
|
return widget
|
|
54
55
|
|
|
56
|
+
def cleanup(self):
|
|
57
|
+
"""Cleanup agents list widget"""
|
|
58
|
+
# Defensively detach the model and schedule deletion only if the view still exists
|
|
59
|
+
try:
|
|
60
|
+
if self.list is not None:
|
|
61
|
+
try:
|
|
62
|
+
self.list.setModel(None)
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
try:
|
|
66
|
+
self.list.deleteLater()
|
|
67
|
+
except Exception:
|
|
68
|
+
pass
|
|
69
|
+
finally:
|
|
70
|
+
# Drop model reference in UI registry
|
|
71
|
+
try:
|
|
72
|
+
self.window.ui.models.pop(self.id, None)
|
|
73
|
+
except Exception:
|
|
74
|
+
pass
|
|
75
|
+
self.list = None
|
|
76
|
+
|
|
55
77
|
def action_new(self):
|
|
56
78
|
"""
|
|
57
79
|
New agent action
|
|
@@ -64,7 +86,7 @@ class AgentsWidget:
|
|
|
64
86
|
:param parent: parent widget
|
|
65
87
|
:return: QStandardItemModel
|
|
66
88
|
"""
|
|
67
|
-
return QStandardItemModel(0, 1, parent)
|
|
89
|
+
return QStandardItemModel(0, 1, parent=parent)
|
|
68
90
|
|
|
69
91
|
def update_list(self, data):
|
|
70
92
|
"""
|
|
@@ -75,13 +97,18 @@ class AgentsWidget:
|
|
|
75
97
|
nodes = self.window.ui.nodes
|
|
76
98
|
models = self.window.ui.models
|
|
77
99
|
|
|
78
|
-
|
|
79
|
-
|
|
100
|
+
# Guard: dialog may be closed; widget/list may not exist anymore
|
|
101
|
+
widget = nodes.get(self.id)
|
|
102
|
+
if widget is None or widget.list is None:
|
|
103
|
+
return
|
|
80
104
|
|
|
105
|
+
model = models.get(self.id)
|
|
81
106
|
if model is None:
|
|
82
|
-
model
|
|
107
|
+
# When reopened after cleanup, re-create model and re-bind it to the view
|
|
108
|
+
model = self.create_model(self.parent)
|
|
83
109
|
models[self.id] = model
|
|
84
|
-
|
|
110
|
+
widget.list.setModel(model)
|
|
111
|
+
|
|
85
112
|
try:
|
|
86
113
|
if not data:
|
|
87
114
|
model.setRowCount(0)
|
pygpt_net/ui/__init__.py
CHANGED
|
@@ -172,15 +172,13 @@ class UI:
|
|
|
172
172
|
|
|
173
173
|
def show_loading(self):
|
|
174
174
|
"""Show loading"""
|
|
175
|
-
|
|
176
|
-
return
|
|
175
|
+
return
|
|
177
176
|
self.window.ui.nodes['anim.loading'].start_anim()
|
|
178
177
|
self.window.ui.nodes['anim.loading'].show()
|
|
179
178
|
|
|
180
179
|
def hide_loading(self):
|
|
181
180
|
"""Hide loading"""
|
|
182
|
-
|
|
183
|
-
return
|
|
181
|
+
return
|
|
184
182
|
self.window.ui.nodes['anim.loading'].stop_anim()
|
|
185
183
|
self.window.ui.nodes['anim.loading'].hide()
|
|
186
184
|
|
pygpt_net/ui/dialog/about.py
CHANGED
|
@@ -19,6 +19,9 @@ from pygpt_net.utils import trans
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class About:
|
|
22
|
+
|
|
23
|
+
RELEASE_YEAR = 2025
|
|
24
|
+
|
|
22
25
|
def __init__(self, window=None):
|
|
23
26
|
"""
|
|
24
27
|
About dialog
|
|
@@ -36,34 +39,65 @@ class About:
|
|
|
36
39
|
"""
|
|
37
40
|
return self.window.core.updater.get_fetch_thanks()
|
|
38
41
|
|
|
42
|
+
def build_versions_str(self, lib_versions: dict, break_after: str = "LlamaIndex") -> str:
|
|
43
|
+
parts = []
|
|
44
|
+
line = []
|
|
45
|
+
for k, v in lib_versions.items():
|
|
46
|
+
line.append(f"{k}: {v}")
|
|
47
|
+
if k == break_after:
|
|
48
|
+
parts.append(", ".join(line))
|
|
49
|
+
line = []
|
|
50
|
+
if line:
|
|
51
|
+
parts.append(", ".join(line))
|
|
52
|
+
return "\n".join(parts)
|
|
53
|
+
|
|
39
54
|
def prepare_content(self) -> str:
|
|
40
55
|
"""
|
|
41
56
|
Get info text
|
|
42
57
|
|
|
43
58
|
:return: info text
|
|
44
59
|
"""
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
60
|
+
lib_versions = {}
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
import platform
|
|
64
|
+
lib_versions['Python'] = platform.python_version()
|
|
65
|
+
except ImportError:
|
|
66
|
+
pass
|
|
49
67
|
|
|
50
68
|
try:
|
|
51
|
-
from llama_index.core import __version__ as llama_index_version
|
|
52
|
-
# from langchain import __version__ as langchain_version
|
|
53
69
|
from openai.version import VERSION as openai_version
|
|
70
|
+
lib_versions['OpenAI API'] = openai_version
|
|
71
|
+
except ImportError:
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
from llama_index.core import __version__ as llama_index_version
|
|
76
|
+
lib_versions['LlamaIndex'] = llama_index_version
|
|
77
|
+
except ImportError:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
from anthropic import __version__ as anthropic_version
|
|
82
|
+
lib_versions['Anthropic API'] = anthropic_version
|
|
83
|
+
except ImportError:
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
from google.genai import __version__ as google_genai_version
|
|
88
|
+
lib_versions['Google API'] = google_genai_version
|
|
54
89
|
except ImportError:
|
|
55
90
|
pass
|
|
56
91
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
92
|
+
try:
|
|
93
|
+
from xai_sdk import __version__ as xai_sdk_version
|
|
94
|
+
lib_versions['xAI API'] = xai_sdk_version
|
|
95
|
+
except ImportError:
|
|
96
|
+
pass
|
|
60
97
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# langchain_version,
|
|
65
|
-
llama_index_version,
|
|
66
|
-
)
|
|
98
|
+
versions_str = ""
|
|
99
|
+
if lib_versions:
|
|
100
|
+
versions_str = self.build_versions_str(lib_versions, break_after="LlamaIndex")
|
|
67
101
|
|
|
68
102
|
platform = self.window.core.platforms.get_as_string()
|
|
69
103
|
version = self.window.meta['version']
|
|
@@ -80,29 +114,14 @@ class About:
|
|
|
80
114
|
label_github = trans("dialog.about.github")
|
|
81
115
|
label_docs = trans("dialog.about.docs")
|
|
82
116
|
|
|
83
|
-
data = "{label_version}: {version}, {platform}\n" \
|
|
84
|
-
"{label_build}: {build}\n" \
|
|
85
|
-
"{
|
|
86
|
-
"{label_website}: {website}\n" \
|
|
87
|
-
"{label_github}: {github}\n" \
|
|
88
|
-
"{label_docs}: {docs}\n\n" \
|
|
89
|
-
"(c)
|
|
90
|
-
"{email}\n"
|
|
91
|
-
label_version=label_version,
|
|
92
|
-
version=version,
|
|
93
|
-
platform=platform,
|
|
94
|
-
label_build=label_build,
|
|
95
|
-
build=build.replace('.', '-'),
|
|
96
|
-
label_website=label_website,
|
|
97
|
-
website=website,
|
|
98
|
-
label_github=label_github,
|
|
99
|
-
github=github,
|
|
100
|
-
label_docs=label_docs,
|
|
101
|
-
docs=docs,
|
|
102
|
-
author=author,
|
|
103
|
-
email=email,
|
|
104
|
-
lib_versions=lib_versions,
|
|
105
|
-
)
|
|
117
|
+
data = f"{label_version}: {version}, {platform}\n" \
|
|
118
|
+
f"{label_build}: {build.replace('.', '-')}\n\n" \
|
|
119
|
+
f"{versions_str}\n\n" \
|
|
120
|
+
f"{label_website}: {website}\n" \
|
|
121
|
+
f"{label_github}: {github}\n" \
|
|
122
|
+
f"{label_docs}: {docs}\n\n" \
|
|
123
|
+
f"(c) {self.RELEASE_YEAR} {author}\n" \
|
|
124
|
+
f"{email}\n"
|
|
106
125
|
return data
|
|
107
126
|
|
|
108
127
|
def setup(self):
|
|
@@ -139,6 +158,7 @@ class About:
|
|
|
139
158
|
string = self.prepare_content()
|
|
140
159
|
content = QLabel(string)
|
|
141
160
|
content.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
|
161
|
+
content.setWordWrap(True)
|
|
142
162
|
self.window.ui.nodes['dialog.about.content'] = content
|
|
143
163
|
|
|
144
164
|
thanks = QLabel(trans('about.thanks'))
|