pygpt-net 2.6.55__py3-none-any.whl → 2.6.57__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 +12 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +26 -22
- pygpt_net/config.py +44 -0
- pygpt_net/controller/audio/audio.py +0 -0
- pygpt_net/controller/calendar/calendar.py +0 -0
- pygpt_net/controller/calendar/note.py +0 -0
- pygpt_net/controller/chat/chat.py +0 -0
- pygpt_net/controller/chat/handler/openai_stream.py +2 -1
- pygpt_net/controller/chat/handler/worker.py +0 -0
- pygpt_net/controller/chat/remote_tools.py +5 -3
- pygpt_net/controller/chat/render.py +0 -0
- pygpt_net/controller/chat/text.py +0 -0
- pygpt_net/controller/ctx/common.py +0 -0
- pygpt_net/controller/debug/debug.py +26 -2
- pygpt_net/controller/debug/fixtures.py +1 -1
- pygpt_net/controller/dialogs/confirm.py +15 -1
- pygpt_net/controller/dialogs/debug.py +2 -0
- pygpt_net/controller/lang/mapping.py +0 -0
- pygpt_net/controller/launcher/launcher.py +0 -0
- pygpt_net/controller/mode/mode.py +0 -0
- pygpt_net/controller/presets/presets.py +0 -0
- pygpt_net/controller/realtime/realtime.py +0 -0
- pygpt_net/controller/theme/theme.py +0 -0
- pygpt_net/controller/ui/mode.py +5 -3
- pygpt_net/controller/ui/tabs.py +0 -0
- pygpt_net/core/agents/agents.py +3 -1
- pygpt_net/core/agents/custom.py +150 -0
- pygpt_net/core/agents/provider.py +0 -0
- pygpt_net/core/builder/__init__.py +12 -0
- pygpt_net/core/builder/graph.py +478 -0
- pygpt_net/core/calendar/calendar.py +0 -0
- pygpt_net/core/ctx/ctx.py +0 -0
- pygpt_net/core/ctx/output.py +0 -0
- pygpt_net/core/debug/agent.py +0 -0
- pygpt_net/core/debug/agent_builder.py +29 -0
- pygpt_net/core/debug/console/console.py +0 -0
- pygpt_net/core/debug/db.py +0 -0
- pygpt_net/core/debug/debug.py +0 -0
- pygpt_net/core/debug/events.py +0 -0
- pygpt_net/core/debug/indexes.py +0 -0
- pygpt_net/core/debug/kernel.py +0 -0
- pygpt_net/core/debug/tabs.py +0 -0
- pygpt_net/core/filesystem/filesystem.py +0 -0
- pygpt_net/core/fixtures/__init__ +0 -0
- pygpt_net/core/fixtures/stream/__init__.py +0 -0
- pygpt_net/core/fixtures/stream/generator.py +0 -0
- pygpt_net/core/models/models.py +2 -1
- pygpt_net/core/plugins/plugins.py +60 -0
- pygpt_net/core/render/plain/pid.py +0 -0
- pygpt_net/core/render/plain/renderer.py +26 -4
- pygpt_net/core/render/web/body.py +46 -4
- pygpt_net/core/render/web/debug.py +0 -0
- pygpt_net/core/render/web/helpers.py +0 -0
- pygpt_net/core/render/web/pid.py +0 -0
- pygpt_net/core/render/web/renderer.py +15 -20
- pygpt_net/core/tabs/tab.py +0 -0
- pygpt_net/core/tabs/tabs.py +0 -0
- pygpt_net/core/text/utils.py +0 -0
- pygpt_net/css.qrc +0 -0
- pygpt_net/css_rc.py +0 -0
- pygpt_net/data/config/config.json +8 -7
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/settings.json +14 -0
- pygpt_net/data/css/web-blocks.css +9 -0
- pygpt_net/data/css/web-blocks.dark.css +6 -0
- pygpt_net/data/css/web-blocks.darkest.css +6 -0
- pygpt_net/data/css/web-chatgpt.css +14 -6
- pygpt_net/data/css/web-chatgpt.dark.css +6 -0
- pygpt_net/data/css/web-chatgpt.darkest.css +6 -0
- pygpt_net/data/css/web-chatgpt.light.css +6 -0
- pygpt_net/data/css/web-chatgpt_wide.css +14 -6
- pygpt_net/data/css/web-chatgpt_wide.dark.css +6 -0
- pygpt_net/data/css/web-chatgpt_wide.darkest.css +6 -0
- pygpt_net/data/css/web-chatgpt_wide.light.css +6 -0
- pygpt_net/data/fixtures/fake_stream.txt +14 -1
- pygpt_net/data/icons/case.svg +0 -0
- pygpt_net/data/icons/chat1.svg +0 -0
- pygpt_net/data/icons/chat2.svg +0 -0
- pygpt_net/data/icons/chat3.svg +0 -0
- pygpt_net/data/icons/chat4.svg +0 -0
- pygpt_net/data/icons/fit.svg +0 -0
- pygpt_net/data/icons/note1.svg +0 -0
- pygpt_net/data/icons/note2.svg +0 -0
- pygpt_net/data/icons/note3.svg +0 -0
- pygpt_net/data/icons/stt.svg +0 -0
- pygpt_net/data/icons/translate.svg +0 -0
- pygpt_net/data/icons/tts.svg +0 -0
- pygpt_net/data/icons/url.svg +0 -0
- pygpt_net/data/icons/vision.svg +0 -0
- pygpt_net/data/icons/web_off.svg +0 -0
- pygpt_net/data/icons/web_on.svg +0 -0
- pygpt_net/data/js/app/async.js +166 -0
- pygpt_net/data/js/app/bridge.js +88 -0
- pygpt_net/data/js/app/common.js +212 -0
- pygpt_net/data/js/app/config.js +223 -0
- pygpt_net/data/js/app/custom.js +961 -0
- pygpt_net/data/js/app/data.js +84 -0
- pygpt_net/data/js/app/dom.js +322 -0
- pygpt_net/data/js/app/events.js +400 -0
- pygpt_net/data/js/app/highlight.js +542 -0
- pygpt_net/data/js/app/logger.js +305 -0
- pygpt_net/data/js/app/markdown.js +1137 -0
- pygpt_net/data/js/app/math.js +167 -0
- pygpt_net/data/js/app/nodes.js +395 -0
- pygpt_net/data/js/app/queue.js +260 -0
- pygpt_net/data/js/app/raf.js +250 -0
- pygpt_net/data/js/app/runtime.js +582 -0
- pygpt_net/data/js/app/scroll.js +433 -0
- pygpt_net/data/js/app/stream.js +2708 -0
- pygpt_net/data/js/app/template.js +287 -0
- pygpt_net/data/js/app/tool.js +87 -0
- pygpt_net/data/js/app/ui.js +86 -0
- pygpt_net/data/js/app/user.js +380 -0
- pygpt_net/data/js/app/utils.js +64 -0
- pygpt_net/data/js/app.min.js +880 -0
- pygpt_net/data/js/markdown-it/markdown-it-katex.min.js +1 -1
- pygpt_net/data/js/markdown-it/markdown-it.min.js +0 -0
- pygpt_net/data/locale/locale.de.ini +3 -1
- pygpt_net/data/locale/locale.en.ini +8 -0
- pygpt_net/data/locale/locale.es.ini +2 -0
- pygpt_net/data/locale/locale.fr.ini +2 -0
- pygpt_net/data/locale/locale.it.ini +2 -0
- pygpt_net/data/locale/locale.pl.ini +3 -1
- pygpt_net/data/locale/locale.uk.ini +3 -1
- pygpt_net/data/locale/locale.zh.ini +2 -0
- pygpt_net/data/locale/plugin.osm.en.ini +24 -24
- pygpt_net/data/locale/plugin.wolfram.en.ini +9 -9
- pygpt_net/fonts.qrc +0 -0
- pygpt_net/fonts_rc.py +0 -0
- pygpt_net/icons.qrc +0 -0
- pygpt_net/icons_rc.py +0 -0
- pygpt_net/item/agent.py +62 -0
- pygpt_net/item/builder_layout.py +62 -0
- pygpt_net/js.qrc +24 -1
- pygpt_net/js_rc.py +51394 -33687
- pygpt_net/plugin/base/worker.py +0 -0
- pygpt_net/plugin/cmd_web/config.py +17 -17
- pygpt_net/plugin/cmd_web/worker.py +325 -171
- pygpt_net/plugin/mcp/__init__.py +0 -0
- pygpt_net/plugin/mcp/config.py +0 -0
- pygpt_net/plugin/mcp/plugin.py +0 -0
- pygpt_net/plugin/mcp/worker.py +0 -0
- pygpt_net/plugin/osm/__init__.py +0 -0
- pygpt_net/plugin/osm/config.py +0 -0
- pygpt_net/plugin/osm/plugin.py +0 -0
- pygpt_net/plugin/osm/worker.py +0 -0
- pygpt_net/plugin/wolfram/__init__.py +0 -0
- pygpt_net/plugin/wolfram/config.py +0 -0
- pygpt_net/plugin/wolfram/plugin.py +0 -0
- pygpt_net/plugin/wolfram/worker.py +0 -0
- pygpt_net/provider/api/anthropic/tools.py +0 -0
- pygpt_net/provider/api/google/__init__.py +0 -0
- pygpt_net/provider/api/google/video.py +0 -0
- pygpt_net/provider/api/openai/agents/experts.py +0 -0
- pygpt_net/provider/api/openai/agents/remote_tools.py +0 -0
- pygpt_net/provider/api/openai/remote_tools.py +0 -0
- pygpt_net/provider/api/openai/responses.py +0 -0
- pygpt_net/provider/api/x_ai/__init__.py +2 -0
- pygpt_net/provider/api/x_ai/remote.py +0 -0
- pygpt_net/provider/core/agent/__init__.py +10 -0
- pygpt_net/provider/core/agent/base.py +51 -0
- pygpt_net/provider/core/agent/json_file.py +200 -0
- pygpt_net/provider/core/config/patch.py +33 -0
- pygpt_net/provider/core/config/patches/__init__.py +0 -0
- pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +1 -0
- pygpt_net/provider/core/ctx/db_sqlite/storage.py +0 -0
- pygpt_net/provider/core/model/patches/__init__.py +0 -0
- pygpt_net/provider/core/model/patches/patch_before_2_6_42.py +0 -0
- pygpt_net/provider/core/preset/patch.py +0 -0
- pygpt_net/provider/core/preset/patches/__init__.py +0 -0
- pygpt_net/provider/core/preset/patches/patch_before_2_6_42.py +0 -0
- pygpt_net/provider/llms/anthropic.py +4 -0
- pygpt_net/provider/llms/base.py +2 -0
- pygpt_net/provider/llms/deepseek_api.py +2 -0
- pygpt_net/provider/llms/google.py +2 -0
- pygpt_net/provider/llms/hugging_face_api.py +4 -0
- pygpt_net/provider/llms/hugging_face_embedding.py +0 -0
- pygpt_net/provider/llms/hugging_face_router.py +2 -0
- pygpt_net/provider/llms/local.py +0 -0
- pygpt_net/provider/llms/mistral.py +4 -0
- pygpt_net/provider/llms/open_router.py +0 -0
- pygpt_net/provider/llms/perplexity.py +2 -0
- pygpt_net/provider/llms/utils.py +0 -0
- pygpt_net/provider/llms/voyage.py +0 -0
- pygpt_net/provider/llms/x_ai.py +2 -0
- pygpt_net/tools/agent_builder/__init__.py +12 -0
- pygpt_net/tools/agent_builder/tool.py +292 -0
- pygpt_net/tools/agent_builder/ui/__init__.py +0 -0
- pygpt_net/tools/agent_builder/ui/dialogs.py +152 -0
- pygpt_net/tools/agent_builder/ui/list.py +228 -0
- pygpt_net/tools/code_interpreter/ui/html.py +0 -0
- pygpt_net/tools/code_interpreter/ui/widgets.py +0 -0
- pygpt_net/tools/html_canvas/tool.py +23 -6
- pygpt_net/tools/html_canvas/ui/widgets.py +224 -2
- pygpt_net/ui/layout/chat/chat.py +0 -0
- pygpt_net/ui/layout/chat/output.py +5 -5
- pygpt_net/ui/main.py +10 -9
- pygpt_net/ui/menu/debug.py +39 -1
- pygpt_net/ui/widget/builder/__init__.py +12 -0
- pygpt_net/ui/widget/builder/editor.py +2001 -0
- pygpt_net/ui/widget/dialog/base.py +4 -1
- pygpt_net/ui/widget/draw/painter.py +0 -0
- pygpt_net/ui/widget/element/labels.py +9 -4
- pygpt_net/ui/widget/lists/db.py +0 -0
- pygpt_net/ui/widget/lists/debug.py +0 -0
- pygpt_net/ui/widget/tabs/body.py +0 -0
- pygpt_net/ui/widget/textarea/html.py +1 -0
- pygpt_net/ui/widget/textarea/input.py +28 -10
- pygpt_net/ui/widget/textarea/output.py +21 -1
- pygpt_net/ui/widget/textarea/web.py +31 -3
- pygpt_net/utils.py +40 -0
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/METADATA +16 -2
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/RECORD +116 -77
- pygpt_net/data/js/app.js +0 -5869
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# ================================================== #
|
|
4
|
+
# This file is a part of PYGPT package #
|
|
5
|
+
# Website: https://pygpt.net #
|
|
6
|
+
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
|
+
# MIT License #
|
|
8
|
+
# Created By : Marcin Szczygliński #
|
|
9
|
+
# Updated Date: 2025.09.19 00:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from typing import Dict
|
|
14
|
+
|
|
15
|
+
from PySide6 import QtCore
|
|
16
|
+
from PySide6.QtGui import QAction, QIcon
|
|
17
|
+
|
|
18
|
+
from pygpt_net.tools.base import BaseTool
|
|
19
|
+
from pygpt_net.utils import trans
|
|
20
|
+
|
|
21
|
+
from .ui.dialogs import Builder
|
|
22
|
+
|
|
23
|
+
class AgentBuilder(BaseTool):
|
|
24
|
+
def __init__(self, *args, **kwargs):
|
|
25
|
+
"""
|
|
26
|
+
Agent builder (nodes editor)
|
|
27
|
+
|
|
28
|
+
:param window: Window instance
|
|
29
|
+
"""
|
|
30
|
+
super(AgentBuilder, self).__init__(*args, **kwargs)
|
|
31
|
+
self.id = "agent_builder"
|
|
32
|
+
self.opened = False
|
|
33
|
+
self.dialog = None # dialog
|
|
34
|
+
self.initialized = False
|
|
35
|
+
self.current_agent = None # current agent ID
|
|
36
|
+
|
|
37
|
+
def setup(self):
|
|
38
|
+
"""Setup controller"""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
def update(self):
|
|
42
|
+
"""Update"""
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
def save(self):
|
|
46
|
+
"""Save layout to file"""
|
|
47
|
+
if "agent.builder" not in self.window.ui.editor:
|
|
48
|
+
return
|
|
49
|
+
custom = self.window.core.agents.custom
|
|
50
|
+
layout = self.window.ui.editor["agent.builder"].save_layout()
|
|
51
|
+
custom.update_layout(layout)
|
|
52
|
+
if self.current_agent:
|
|
53
|
+
custom.update_agent_layout(self.current_agent, layout)
|
|
54
|
+
custom.save()
|
|
55
|
+
self.window.update_status(f"Saved at: {datetime.now().strftime('%H:%M:%S')}")
|
|
56
|
+
|
|
57
|
+
def load(self):
|
|
58
|
+
"""Load layout from file"""
|
|
59
|
+
self.restore()
|
|
60
|
+
|
|
61
|
+
def open(self):
|
|
62
|
+
"""Open dialog"""
|
|
63
|
+
if not self.initialized:
|
|
64
|
+
self.dialog = Builder(self.window, tool=self)
|
|
65
|
+
self.dialog.setup()
|
|
66
|
+
self.restore()
|
|
67
|
+
self.initialized = True
|
|
68
|
+
self.window.ui.dialogs.open('agent.builder', width=900, height=600)
|
|
69
|
+
self.opened = True
|
|
70
|
+
self.update()
|
|
71
|
+
|
|
72
|
+
def close(self):
|
|
73
|
+
"""Close dialog"""
|
|
74
|
+
self.window.ui.dialogs.close('agent.builder')
|
|
75
|
+
self.opened = False
|
|
76
|
+
|
|
77
|
+
def toggle(self):
|
|
78
|
+
"""Toggle dialog window"""
|
|
79
|
+
if self.opened:
|
|
80
|
+
self.close()
|
|
81
|
+
else:
|
|
82
|
+
self.open()
|
|
83
|
+
|
|
84
|
+
def store_current(self):
|
|
85
|
+
"""Store current to file"""
|
|
86
|
+
self.save()
|
|
87
|
+
|
|
88
|
+
def restore(self):
|
|
89
|
+
"""Restore layout and agents from file"""
|
|
90
|
+
layout = self.window.core.agents.custom.get_layout()
|
|
91
|
+
if layout:
|
|
92
|
+
data = layout.data
|
|
93
|
+
if data:
|
|
94
|
+
self.window.ui.editor["agent.builder"].load_layout(data)
|
|
95
|
+
self.window.update_status(f"Loaded layout at: {datetime.now().strftime('%H:%M:%S')}")
|
|
96
|
+
|
|
97
|
+
self.update_list()
|
|
98
|
+
|
|
99
|
+
# select first agent on list
|
|
100
|
+
agents = self.window.core.agents.custom.get_agents()
|
|
101
|
+
if agents and len(agents) > 0:
|
|
102
|
+
first_agent_id = list(agents.keys())[0]
|
|
103
|
+
self.edit_agent(first_agent_id)
|
|
104
|
+
|
|
105
|
+
def show_hide(self, show: bool = True):
|
|
106
|
+
"""
|
|
107
|
+
Show/hide dialog window
|
|
108
|
+
|
|
109
|
+
:param show: show/hide
|
|
110
|
+
"""
|
|
111
|
+
if show:
|
|
112
|
+
self.open()
|
|
113
|
+
else:
|
|
114
|
+
self.close()
|
|
115
|
+
|
|
116
|
+
def on_close(self):
|
|
117
|
+
"""On window close"""
|
|
118
|
+
self.opened = False
|
|
119
|
+
self.update()
|
|
120
|
+
|
|
121
|
+
def on_exit(self):
|
|
122
|
+
"""On exit"""
|
|
123
|
+
self.store_current()
|
|
124
|
+
|
|
125
|
+
def clear(self, force: bool = False):
|
|
126
|
+
"""
|
|
127
|
+
Clear layout
|
|
128
|
+
|
|
129
|
+
:param force: force clear
|
|
130
|
+
"""
|
|
131
|
+
if not force:
|
|
132
|
+
self.window.ui.dialogs.confirm(
|
|
133
|
+
type='agent.builder.agent.clear',
|
|
134
|
+
id=0,
|
|
135
|
+
msg=trans('agent.builder.confirm.clear.msg'),
|
|
136
|
+
)
|
|
137
|
+
return
|
|
138
|
+
if self.window.ui.editor["agent.builder"].clear(ask_user=False):
|
|
139
|
+
self.save()
|
|
140
|
+
|
|
141
|
+
def add_agent(self, name: str = None):
|
|
142
|
+
"""Add new agent"""
|
|
143
|
+
if name is None:
|
|
144
|
+
self.window.ui.dialog['create'].id = 'agent.builder.agent'
|
|
145
|
+
self.window.ui.dialog['create'].input.setText("")
|
|
146
|
+
self.window.ui.dialog['create'].current = ""
|
|
147
|
+
self.window.ui.dialog['create'].show()
|
|
148
|
+
self.window.ui.dialog['create'].input.setFocus()
|
|
149
|
+
return
|
|
150
|
+
uuid = self.window.core.agents.custom.new_agent(name)
|
|
151
|
+
if uuid:
|
|
152
|
+
self.update_list()
|
|
153
|
+
self.window.ui.editor["agent.builder"].clear(ask_user=False)
|
|
154
|
+
self.window.ui.dialogs.close('create')
|
|
155
|
+
self.edit_agent(uuid)
|
|
156
|
+
|
|
157
|
+
def rename_agent(self, agent_id: str, name: str = None):
|
|
158
|
+
"""
|
|
159
|
+
Rename agent
|
|
160
|
+
|
|
161
|
+
:param agent_id: agent ID
|
|
162
|
+
:param name: new name
|
|
163
|
+
"""
|
|
164
|
+
agent = self.window.core.agents.custom.get_agent(agent_id)
|
|
165
|
+
if not agent:
|
|
166
|
+
print("Agent not found:", agent_id)
|
|
167
|
+
return
|
|
168
|
+
current_name = agent.name
|
|
169
|
+
if name is None:
|
|
170
|
+
self.window.ui.dialog['rename'].id = 'agent.builder.agent'
|
|
171
|
+
self.window.ui.dialog['rename'].input.setText(current_name)
|
|
172
|
+
self.window.ui.dialog['rename'].current =agent_id
|
|
173
|
+
self.window.ui.dialog['rename'].show()
|
|
174
|
+
self.window.ui.dialog['rename'].input.setFocus()
|
|
175
|
+
return
|
|
176
|
+
if agent:
|
|
177
|
+
agent.name = name
|
|
178
|
+
self.window.core.agents.custom.save()
|
|
179
|
+
self.update_list()
|
|
180
|
+
self.window.ui.dialogs.close('rename')
|
|
181
|
+
|
|
182
|
+
def edit_agent(self, agent_id: str):
|
|
183
|
+
"""
|
|
184
|
+
Edit agent
|
|
185
|
+
|
|
186
|
+
:param agent_id: agent ID
|
|
187
|
+
"""
|
|
188
|
+
self.save()
|
|
189
|
+
self.current_agent = agent_id
|
|
190
|
+
agent = self.window.core.agents.custom.get_agent(agent_id)
|
|
191
|
+
if agent:
|
|
192
|
+
layout = agent.layout
|
|
193
|
+
if layout:
|
|
194
|
+
self.window.ui.editor["agent.builder"].load_layout(layout)
|
|
195
|
+
else:
|
|
196
|
+
self.window.ui.editor["agent.builder"].clear(ask_user=False)
|
|
197
|
+
self.select_on_list(agent_id)
|
|
198
|
+
|
|
199
|
+
def duplicate_agent(self, agent_id: str):
|
|
200
|
+
"""
|
|
201
|
+
Duplicate agent
|
|
202
|
+
|
|
203
|
+
:param agent_id: agent ID
|
|
204
|
+
"""
|
|
205
|
+
agent = self.window.core.agents.custom.get_agent(agent_id)
|
|
206
|
+
if agent:
|
|
207
|
+
new_name = f"{agent.name} (copy)"
|
|
208
|
+
self.window.core.agents.custom.duplicate_agent(agent_id, new_name)
|
|
209
|
+
self.update_list()
|
|
210
|
+
|
|
211
|
+
def delete_agent(self, agent_id: str, force: bool = False):
|
|
212
|
+
"""
|
|
213
|
+
Delete agent
|
|
214
|
+
|
|
215
|
+
:param agent_id: agent ID
|
|
216
|
+
:param force: force delete without confirmation
|
|
217
|
+
"""
|
|
218
|
+
if not force:
|
|
219
|
+
self.window.ui.dialogs.confirm(
|
|
220
|
+
type='agent.builder.agent.delete',
|
|
221
|
+
id=agent_id,
|
|
222
|
+
msg=trans('agent.builder.confirm.delete.msg'),
|
|
223
|
+
)
|
|
224
|
+
return
|
|
225
|
+
self.window.core.agents.custom.delete_agent(agent_id)
|
|
226
|
+
self.update_list()
|
|
227
|
+
if self.current_agent == agent_id:
|
|
228
|
+
self.current_agent = None
|
|
229
|
+
self.window.ui.editor["agent.builder"].clear(ask_user=False)
|
|
230
|
+
|
|
231
|
+
def update_list(self):
|
|
232
|
+
"""Update agents list"""
|
|
233
|
+
data = self.window.core.agents.custom.get_agents()
|
|
234
|
+
self.window.ui.nodes["agent.builder.list"].update_list(data)
|
|
235
|
+
|
|
236
|
+
def select_on_list(self, agent_id: str):
|
|
237
|
+
"""
|
|
238
|
+
Select agent on list
|
|
239
|
+
|
|
240
|
+
:param agent_id: agent ID
|
|
241
|
+
"""
|
|
242
|
+
nodes = self.window.ui.nodes
|
|
243
|
+
models = self.window.ui.models
|
|
244
|
+
|
|
245
|
+
agents_list = nodes["agent.builder.list"].list
|
|
246
|
+
model = models.get("agent.builder.list")
|
|
247
|
+
|
|
248
|
+
if model is None:
|
|
249
|
+
return
|
|
250
|
+
for row in range(model.rowCount()):
|
|
251
|
+
idx = model.index(row, 0)
|
|
252
|
+
if str(model.data(idx, QtCore.Qt.UserRole)) == str(agent_id):
|
|
253
|
+
agents_list.setCurrentIndex(idx)
|
|
254
|
+
sm = agents_list.selectionModel()
|
|
255
|
+
if sm:
|
|
256
|
+
sm.select(idx, QtCore.QItemSelectionModel.ClearAndSelect)
|
|
257
|
+
agents_list.scrollTo(idx)
|
|
258
|
+
break
|
|
259
|
+
|
|
260
|
+
def setup_menu(self) -> Dict[str, QAction]:
|
|
261
|
+
"""
|
|
262
|
+
Setup main menu
|
|
263
|
+
|
|
264
|
+
:return dict with menu actions
|
|
265
|
+
"""
|
|
266
|
+
actions = {}
|
|
267
|
+
actions["agent.builder"] = QAction(
|
|
268
|
+
QIcon(":/icons/robot.svg"),
|
|
269
|
+
trans("menu.tools.agent.builder"),
|
|
270
|
+
self.window,
|
|
271
|
+
checkable=False,
|
|
272
|
+
)
|
|
273
|
+
actions["agent.builder"].triggered.connect(
|
|
274
|
+
lambda: self.toggle()
|
|
275
|
+
)
|
|
276
|
+
return actions
|
|
277
|
+
|
|
278
|
+
def setup_dialogs(self):
|
|
279
|
+
"""Setup dialogs (static)"""
|
|
280
|
+
pass
|
|
281
|
+
|
|
282
|
+
def get_lang_mappings(self) -> Dict[str, Dict]:
|
|
283
|
+
"""
|
|
284
|
+
Get language mappings
|
|
285
|
+
|
|
286
|
+
:return: dict with language mappings
|
|
287
|
+
"""
|
|
288
|
+
return {
|
|
289
|
+
'menu.text': {
|
|
290
|
+
'tools.agent.builder': 'menu.tools.agent.builder',
|
|
291
|
+
}
|
|
292
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# ================================================== #
|
|
4
|
+
# This file is a part of PYGPT package #
|
|
5
|
+
# Website: https://pygpt.net #
|
|
6
|
+
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
|
+
# MIT License #
|
|
8
|
+
# Created By : Marcin Szczygliński #
|
|
9
|
+
# Updated Date: 2025.09.19 00:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
from PySide6.QtCore import Qt
|
|
13
|
+
from PySide6.QtGui import QAction, QIcon
|
|
14
|
+
from PySide6.QtWidgets import QVBoxLayout, QMenuBar, QSplitter
|
|
15
|
+
|
|
16
|
+
from pygpt_net.tools.agent_builder.ui.list import AgentsWidget
|
|
17
|
+
from pygpt_net.ui.widget.builder.editor import NodeEditor
|
|
18
|
+
from pygpt_net.ui.widget.dialog.base import BaseDialog
|
|
19
|
+
from pygpt_net.utils import trans
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Builder:
|
|
23
|
+
|
|
24
|
+
def __init__(self, window=None, tool=None):
|
|
25
|
+
"""
|
|
26
|
+
Builder dialog
|
|
27
|
+
|
|
28
|
+
:param window: Window instance
|
|
29
|
+
"""
|
|
30
|
+
self.window = window
|
|
31
|
+
self.tool = tool
|
|
32
|
+
self.menu_bar = None
|
|
33
|
+
self.file_menu = None
|
|
34
|
+
self.actions = {}
|
|
35
|
+
|
|
36
|
+
def setup_menu(self, parent=None) -> QMenuBar:
|
|
37
|
+
"""Setup audio agent_builder dialog menu"""
|
|
38
|
+
self.menu_bar = QMenuBar(parent)
|
|
39
|
+
self.file_menu = self.menu_bar.addMenu(trans("menu.file"))
|
|
40
|
+
t = self.tool
|
|
41
|
+
|
|
42
|
+
self.actions["open"] = QAction(QIcon(":/icons/folder.svg"), trans("action.open"), self.menu_bar)
|
|
43
|
+
self.actions["open"].triggered.connect(lambda checked=False, t=t: t.load())
|
|
44
|
+
|
|
45
|
+
self.actions["save"] = QAction(QIcon(":/icons/save.svg"), trans("action.save"), self.menu_bar)
|
|
46
|
+
self.actions["save"].triggered.connect(lambda checked=False, t=t: t.save())
|
|
47
|
+
|
|
48
|
+
self.actions["clear"] = QAction(QIcon(":/icons/clear.svg"), trans("action.clear"), self.menu_bar)
|
|
49
|
+
self.actions["clear"].triggered.connect(lambda checked=False, t=t: t.clear())
|
|
50
|
+
|
|
51
|
+
self.file_menu.addAction(self.actions["open"])
|
|
52
|
+
self.file_menu.addAction(self.actions["save"])
|
|
53
|
+
self.file_menu.addAction(self.actions["clear"])
|
|
54
|
+
return self.menu_bar
|
|
55
|
+
|
|
56
|
+
def setup(self):
|
|
57
|
+
"""Setup agent_builder dialog"""
|
|
58
|
+
id = 'agent.builder'
|
|
59
|
+
u = self.window.ui
|
|
60
|
+
|
|
61
|
+
u.dialog['agent.builder'] = BuilderDialog(self.window)
|
|
62
|
+
dlg = u.dialog['agent.builder']
|
|
63
|
+
|
|
64
|
+
editor = NodeEditor(dlg) # parent == dialog
|
|
65
|
+
editor.setStyleSheet("""
|
|
66
|
+
NodeEditor {
|
|
67
|
+
qproperty-gridBackColor: #242629;
|
|
68
|
+
qproperty-gridPenColor: #3b3f46;
|
|
69
|
+
|
|
70
|
+
qproperty-nodeBackgroundColor: #2d2f34;
|
|
71
|
+
qproperty-nodeBorderColor: #4b4f57;
|
|
72
|
+
qproperty-nodeSelectionColor: #ff9900;
|
|
73
|
+
qproperty-nodeTitleColor: #3a3d44;
|
|
74
|
+
|
|
75
|
+
qproperty-portInputColor: #66b2ff;
|
|
76
|
+
qproperty-portOutputColor: #70e070;
|
|
77
|
+
qproperty-portConnectedColor: #ffd166;
|
|
78
|
+
|
|
79
|
+
qproperty-edgeColor: #c0c0c0;
|
|
80
|
+
qproperty-edgeSelectedColor: #ff8a5c;
|
|
81
|
+
}
|
|
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))
|
|
88
|
+
editor.on_clear = self.tool.clear
|
|
89
|
+
|
|
90
|
+
u.editor[id] = editor
|
|
91
|
+
|
|
92
|
+
layout = QVBoxLayout()
|
|
93
|
+
layout.setMenuBar(self.setup_menu(dlg))
|
|
94
|
+
|
|
95
|
+
agents_list = AgentsWidget(self.window, tool=self.tool)
|
|
96
|
+
list_widget = agents_list.setup()
|
|
97
|
+
list_widget.setFixedWidth(250)
|
|
98
|
+
center_splitter = QSplitter(Qt.Horizontal)
|
|
99
|
+
center_splitter.addWidget(list_widget)
|
|
100
|
+
center_splitter.addWidget(u.editor[id])
|
|
101
|
+
center_splitter.setStretchFactor(0, 1)
|
|
102
|
+
center_splitter.setStretchFactor(1, 8)
|
|
103
|
+
layout.addWidget(center_splitter)
|
|
104
|
+
|
|
105
|
+
u.nodes["agent.builder.list"] = agents_list
|
|
106
|
+
|
|
107
|
+
dlg.setLayout(layout)
|
|
108
|
+
dlg.setWindowTitle(trans("agent.builder.title"))
|
|
109
|
+
|
|
110
|
+
def clear(self):
|
|
111
|
+
"""Clear transcribe dialog"""
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class BuilderDialog(BaseDialog):
|
|
116
|
+
def __init__(self, window=None, id=None):
|
|
117
|
+
"""
|
|
118
|
+
BuilderDialog
|
|
119
|
+
|
|
120
|
+
:param window: main window
|
|
121
|
+
:param id: configurator id
|
|
122
|
+
"""
|
|
123
|
+
super(BuilderDialog, self).__init__(window, id)
|
|
124
|
+
self.window = window
|
|
125
|
+
self.id = id # id
|
|
126
|
+
|
|
127
|
+
def closeEvent(self, event):
|
|
128
|
+
"""
|
|
129
|
+
Close event
|
|
130
|
+
|
|
131
|
+
:param event: close event
|
|
132
|
+
"""
|
|
133
|
+
self.cleanup()
|
|
134
|
+
super(BuilderDialog, self).closeEvent(event)
|
|
135
|
+
|
|
136
|
+
def keyPressEvent(self, event):
|
|
137
|
+
"""
|
|
138
|
+
Key press event
|
|
139
|
+
|
|
140
|
+
:param event: key press event
|
|
141
|
+
"""
|
|
142
|
+
if event.key() == Qt.Key_Escape:
|
|
143
|
+
self.cleanup()
|
|
144
|
+
self.close()
|
|
145
|
+
else:
|
|
146
|
+
super(BuilderDialog, self).keyPressEvent(event)
|
|
147
|
+
|
|
148
|
+
def cleanup(self):
|
|
149
|
+
"""
|
|
150
|
+
Cleanup on close
|
|
151
|
+
"""
|
|
152
|
+
self.window.tools.get("agent_builder").on_close()
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# ================================================== #
|
|
4
|
+
# This file is a part of PYGPT package #
|
|
5
|
+
# Website: https://pygpt.net #
|
|
6
|
+
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
|
+
# MIT License #
|
|
8
|
+
# Created By : Marcin Szczygliński #
|
|
9
|
+
# Updated Date: 2025.08.24 23:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
from PySide6 import QtCore
|
|
13
|
+
from PySide6.QtCore import QPoint
|
|
14
|
+
from PySide6.QtGui import QAction, QIcon, Qt, QStandardItemModel
|
|
15
|
+
from PySide6.QtWidgets import QMenu, QVBoxLayout, QPushButton, QWidget
|
|
16
|
+
|
|
17
|
+
from pygpt_net.ui.widget.lists.base import BaseList
|
|
18
|
+
from pygpt_net.utils import trans
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AgentsWidget:
|
|
22
|
+
def __init__(self, window=None, tool=None):
|
|
23
|
+
"""
|
|
24
|
+
Agents select widget
|
|
25
|
+
|
|
26
|
+
:param window: main window
|
|
27
|
+
:param tool: tool instance
|
|
28
|
+
"""
|
|
29
|
+
self.window = window
|
|
30
|
+
self.tool = tool
|
|
31
|
+
self.id = "agent.builder.list"
|
|
32
|
+
self.list = None
|
|
33
|
+
|
|
34
|
+
def setup(self):
|
|
35
|
+
"""
|
|
36
|
+
Setup agents list widget
|
|
37
|
+
|
|
38
|
+
:return: QWidget
|
|
39
|
+
"""
|
|
40
|
+
new_btn = QPushButton(QIcon(":/icons/add.svg"), "")
|
|
41
|
+
new_btn.clicked.connect(self.action_new)
|
|
42
|
+
|
|
43
|
+
self.list = AgentsList(self.window, tool=self.tool, id=self.id)
|
|
44
|
+
layout = QVBoxLayout()
|
|
45
|
+
layout.addWidget(new_btn)
|
|
46
|
+
layout.addWidget(self.list)
|
|
47
|
+
|
|
48
|
+
self.window.ui.models[self.id] = self.create_model(self.window)
|
|
49
|
+
self.list.setModel(self.window.ui.models[self.id])
|
|
50
|
+
|
|
51
|
+
widget = QWidget()
|
|
52
|
+
widget.setLayout(layout)
|
|
53
|
+
return widget
|
|
54
|
+
|
|
55
|
+
def action_new(self):
|
|
56
|
+
"""
|
|
57
|
+
New agent action
|
|
58
|
+
"""
|
|
59
|
+
self.tool.add_agent()
|
|
60
|
+
|
|
61
|
+
def create_model(self, parent) -> QStandardItemModel:
|
|
62
|
+
"""
|
|
63
|
+
Create list model
|
|
64
|
+
:param parent: parent widget
|
|
65
|
+
:return: QStandardItemModel
|
|
66
|
+
"""
|
|
67
|
+
return QStandardItemModel(0, 1, parent)
|
|
68
|
+
|
|
69
|
+
def update_list(self, data):
|
|
70
|
+
"""
|
|
71
|
+
Update presets list
|
|
72
|
+
|
|
73
|
+
:param data: Data to update
|
|
74
|
+
"""
|
|
75
|
+
nodes = self.window.ui.nodes
|
|
76
|
+
models = self.window.ui.models
|
|
77
|
+
|
|
78
|
+
view = nodes[self.id]
|
|
79
|
+
model = models.get(self.id)
|
|
80
|
+
|
|
81
|
+
if model is None:
|
|
82
|
+
model = self.create_model(self.window)
|
|
83
|
+
models[self.id] = model
|
|
84
|
+
view.setModel(model)
|
|
85
|
+
try:
|
|
86
|
+
if not data:
|
|
87
|
+
model.setRowCount(0)
|
|
88
|
+
else:
|
|
89
|
+
count = len(data)
|
|
90
|
+
model.setRowCount(count)
|
|
91
|
+
index_fn = model.index
|
|
92
|
+
set_item_data = model.setItemData
|
|
93
|
+
display_role = QtCore.Qt.DisplayRole
|
|
94
|
+
|
|
95
|
+
for i, (key, item) in enumerate(data.items()):
|
|
96
|
+
agent_id = item.id # <---------- from this
|
|
97
|
+
name = item.name
|
|
98
|
+
idx = index_fn(i, 0)
|
|
99
|
+
set_item_data(idx, {
|
|
100
|
+
display_role: name,
|
|
101
|
+
QtCore.Qt.UserRole: agent_id,
|
|
102
|
+
})
|
|
103
|
+
finally:
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
class AgentsList(BaseList):
|
|
107
|
+
_ICO_EDIT = QIcon(":/icons/edit.svg")
|
|
108
|
+
_ICO_COPY = QIcon(":/icons/copy.svg")
|
|
109
|
+
_ICO_UNDO = QIcon(":/icons/undo.svg")
|
|
110
|
+
_ICO_DELETE = QIcon(":/icons/delete.svg")
|
|
111
|
+
_ICO_CHECK = QIcon(":/icons/check.svg")
|
|
112
|
+
_ICO_CLOSE = QIcon(":/icons/close.svg")
|
|
113
|
+
|
|
114
|
+
def __init__(self, window=None, tool=None, id=None):
|
|
115
|
+
"""
|
|
116
|
+
Agents select menu
|
|
117
|
+
|
|
118
|
+
:param window: main window
|
|
119
|
+
:param id: input id
|
|
120
|
+
"""
|
|
121
|
+
super(AgentsList, self).__init__(window)
|
|
122
|
+
self.window = window
|
|
123
|
+
self.id = id
|
|
124
|
+
self.tool = tool
|
|
125
|
+
self.doubleClicked.connect(self.dblclick)
|
|
126
|
+
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
127
|
+
self.customContextMenuRequested.connect(self.show_context_menu)
|
|
128
|
+
|
|
129
|
+
def click(self, item):
|
|
130
|
+
idx = item.row()
|
|
131
|
+
if idx >= 0:
|
|
132
|
+
model = self.model()
|
|
133
|
+
agent_id = model.data(model.index(idx, 0), QtCore.Qt.UserRole)
|
|
134
|
+
if agent_id is not None:
|
|
135
|
+
self.tool.edit_agent(agent_id)
|
|
136
|
+
|
|
137
|
+
def dblclick(self, val):
|
|
138
|
+
"""
|
|
139
|
+
Double click event
|
|
140
|
+
|
|
141
|
+
:param val: double click event
|
|
142
|
+
"""
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
def show_context_menu(self, pos: QPoint):
|
|
146
|
+
"""
|
|
147
|
+
Context menu event
|
|
148
|
+
|
|
149
|
+
:param pos: QPoint
|
|
150
|
+
"""
|
|
151
|
+
global_pos = self.viewport().mapToGlobal(pos)
|
|
152
|
+
index = self.indexAt(pos)
|
|
153
|
+
if not index.isValid():
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
menu = QMenu(self)
|
|
157
|
+
|
|
158
|
+
edit_act = QAction(self._ICO_EDIT, trans('preset.action.edit'), menu)
|
|
159
|
+
edit_act.triggered.connect(lambda checked=False, it=index: self.action_edit(it))
|
|
160
|
+
menu.addAction(edit_act)
|
|
161
|
+
|
|
162
|
+
duplicate_act = QAction(self._ICO_COPY, trans('preset.action.duplicate'), menu)
|
|
163
|
+
duplicate_act.triggered.connect(lambda checked=False, it=index: self.action_duplicate(it))
|
|
164
|
+
menu.addAction(duplicate_act)
|
|
165
|
+
|
|
166
|
+
delete_act = QAction(self._ICO_DELETE, trans('preset.action.delete'), menu)
|
|
167
|
+
delete_act.triggered.connect(lambda checked=False, it=index: self.action_delete(it))
|
|
168
|
+
menu.addAction(delete_act)
|
|
169
|
+
|
|
170
|
+
menu.exec_(global_pos)
|
|
171
|
+
|
|
172
|
+
def action_edit(self, item):
|
|
173
|
+
"""
|
|
174
|
+
Edit action handler
|
|
175
|
+
|
|
176
|
+
:param item: list item
|
|
177
|
+
"""
|
|
178
|
+
idx = item.row()
|
|
179
|
+
if idx >= 0:
|
|
180
|
+
model = self.model()
|
|
181
|
+
agent_id = model.data(model.index(idx, 0), QtCore.Qt.UserRole)
|
|
182
|
+
if agent_id is not None:
|
|
183
|
+
self.tool.rename_agent(agent_id)
|
|
184
|
+
|
|
185
|
+
def action_duplicate(self, item):
|
|
186
|
+
"""
|
|
187
|
+
Duplicate action handler
|
|
188
|
+
|
|
189
|
+
:param item: list item
|
|
190
|
+
"""
|
|
191
|
+
idx = item.row()
|
|
192
|
+
if idx >= 0:
|
|
193
|
+
model = self.model()
|
|
194
|
+
agent_id = model.data(model.index(idx, 0), QtCore.Qt.UserRole)
|
|
195
|
+
if agent_id is not None:
|
|
196
|
+
self.tool.duplicate_agent(agent_id)
|
|
197
|
+
|
|
198
|
+
def action_delete(self, item):
|
|
199
|
+
"""
|
|
200
|
+
Delete action handler
|
|
201
|
+
|
|
202
|
+
:param item: list item
|
|
203
|
+
"""
|
|
204
|
+
idx = item.row()
|
|
205
|
+
if idx >= 0:
|
|
206
|
+
model = self.model()
|
|
207
|
+
agent_id = model.data(model.index(idx, 0), QtCore.Qt.UserRole)
|
|
208
|
+
if agent_id is not None:
|
|
209
|
+
self.tool.delete_agent(agent_id)
|
|
210
|
+
|
|
211
|
+
def mousePressEvent(self, event):
|
|
212
|
+
if event.button() == Qt.LeftButton:
|
|
213
|
+
index = self.indexAt(event.pos())
|
|
214
|
+
if not index.isValid():
|
|
215
|
+
return
|
|
216
|
+
super().mousePressEvent(event)
|
|
217
|
+
elif event.button() == Qt.RightButton:
|
|
218
|
+
event.accept()
|
|
219
|
+
else:
|
|
220
|
+
super().mousePressEvent(event)
|
|
221
|
+
|
|
222
|
+
def selectionCommand(self, index, event=None):
|
|
223
|
+
"""
|
|
224
|
+
Selection command
|
|
225
|
+
:param index: Index
|
|
226
|
+
:param event: Event
|
|
227
|
+
"""
|
|
228
|
+
return super().selectionCommand(index, event)
|
|
File without changes
|
|
File without changes
|