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
|
@@ -0,0 +1,96 @@
|
|
|
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.24 23:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
from typing import Any, Dict, Tuple, Optional, List
|
|
14
|
+
|
|
15
|
+
from pygpt_net.core.types import AGENT_TYPE_OPENAI, AGENT_MODE_OPENAI
|
|
16
|
+
from pygpt_net.item.ctx import CtxItem
|
|
17
|
+
from pygpt_net.item.model import ModelItem
|
|
18
|
+
from pygpt_net.item.preset import PresetItem
|
|
19
|
+
|
|
20
|
+
from pygpt_net.core.agents.bridge import ConnectionContext
|
|
21
|
+
from pygpt_net.core.bridge import BridgeContext
|
|
22
|
+
|
|
23
|
+
from agents import TResponseInputItem
|
|
24
|
+
|
|
25
|
+
from ..base import BaseAgent
|
|
26
|
+
|
|
27
|
+
from pygpt_net.core.agents.custom.logging import NullLogger, StdLogger
|
|
28
|
+
from pygpt_net.core.agents.custom.runner import FlowOrchestrator
|
|
29
|
+
from pygpt_net.core.agents.custom.utils import make_option_getter
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Agent(BaseAgent):
|
|
33
|
+
def __init__(self, *args, **kwargs):
|
|
34
|
+
super(Agent, self).__init__(*args, **kwargs)
|
|
35
|
+
self.id = "openai_custom"
|
|
36
|
+
self.type = AGENT_TYPE_OPENAI
|
|
37
|
+
self.mode = AGENT_MODE_OPENAI
|
|
38
|
+
self.name = "Custom"
|
|
39
|
+
|
|
40
|
+
async def run(
|
|
41
|
+
self,
|
|
42
|
+
window: Any = None,
|
|
43
|
+
agent_kwargs: Dict[str, Any] = None,
|
|
44
|
+
previous_response_id: str = None,
|
|
45
|
+
messages: List[TResponseInputItem] = None,
|
|
46
|
+
ctx: CtxItem = None,
|
|
47
|
+
stream: bool = False,
|
|
48
|
+
bridge: ConnectionContext = None,
|
|
49
|
+
use_partial_ctx: Optional[bool] = False,
|
|
50
|
+
schema: Optional[list] = None,
|
|
51
|
+
) -> Tuple[CtxItem, str, str]:
|
|
52
|
+
agent_kwargs = agent_kwargs or {}
|
|
53
|
+
messages = messages or []
|
|
54
|
+
|
|
55
|
+
context: BridgeContext = agent_kwargs.get("context", BridgeContext())
|
|
56
|
+
preset: Optional[PresetItem] = context.preset
|
|
57
|
+
model: ModelItem = agent_kwargs.get("model", ModelItem())
|
|
58
|
+
function_tools: list = agent_kwargs.get("function_tools", [])
|
|
59
|
+
|
|
60
|
+
base_prompt = self.get_option(preset, "base", "prompt")
|
|
61
|
+
allow_local_tools_default = bool(self.get_option(preset, "base", "allow_local_tools"))
|
|
62
|
+
allow_remote_tools_default = bool(self.get_option(preset, "base", "allow_remote_tools"))
|
|
63
|
+
max_iterations = int(self.get_option(preset, "base", "max_iterations") or agent_kwargs.get("max_iterations", 20))
|
|
64
|
+
trace_id = self.get_option(preset, "debug", "trace_id") or agent_kwargs.get("trace_id", None)
|
|
65
|
+
router_stream_mode = self.get_option(preset, "router", "stream_mode") or agent_kwargs.get("router_stream_mode", "realtime")
|
|
66
|
+
verbose = bool(agent_kwargs.get("verbose", False))
|
|
67
|
+
logger = StdLogger(prefix="[flow]") if verbose else NullLogger()
|
|
68
|
+
option_get = make_option_getter(self, preset)
|
|
69
|
+
|
|
70
|
+
orchestrator = FlowOrchestrator(window=window, logger=logger)
|
|
71
|
+
logger.debug(f"[schema] {schema}")
|
|
72
|
+
|
|
73
|
+
result = await orchestrator.run_flow(
|
|
74
|
+
schema=schema or [],
|
|
75
|
+
messages=messages,
|
|
76
|
+
ctx=ctx,
|
|
77
|
+
bridge=bridge,
|
|
78
|
+
agent_kwargs=agent_kwargs,
|
|
79
|
+
preset=preset,
|
|
80
|
+
model=model,
|
|
81
|
+
stream=stream,
|
|
82
|
+
use_partial_ctx=use_partial_ctx or False,
|
|
83
|
+
base_prompt=base_prompt,
|
|
84
|
+
allow_local_tools_default=allow_local_tools_default,
|
|
85
|
+
allow_remote_tools_default=allow_remote_tools_default,
|
|
86
|
+
function_tools=function_tools,
|
|
87
|
+
trace_id=trace_id,
|
|
88
|
+
max_iterations=max_iterations,
|
|
89
|
+
router_stream_mode=str(router_stream_mode).lower(),
|
|
90
|
+
option_get=option_get,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
final_output = result.final_output or ""
|
|
94
|
+
last_response_id = result.last_response_id or ""
|
|
95
|
+
|
|
96
|
+
return result.ctx, final_output, last_response_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: 2025.09.
|
|
9
|
+
# Updated Date: 2025.09.24 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import json
|
|
@@ -60,7 +60,10 @@ class JsonFileProvider(BaseProvider):
|
|
|
60
60
|
try:
|
|
61
61
|
if os.path.exists(path):
|
|
62
62
|
with open(path, 'r', encoding="utf-8") as file:
|
|
63
|
-
|
|
63
|
+
try:
|
|
64
|
+
data = json.load(file)
|
|
65
|
+
except json.JSONDecodeError:
|
|
66
|
+
data = {}
|
|
64
67
|
if data == "" or data is None:
|
|
65
68
|
return {}
|
|
66
69
|
if "layout" in data:
|
|
@@ -143,6 +146,7 @@ class JsonFileProvider(BaseProvider):
|
|
|
143
146
|
'id': item.id,
|
|
144
147
|
'name': item.name,
|
|
145
148
|
'layout': item.layout,
|
|
149
|
+
'schema': item.schema,
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
@staticmethod
|
|
@@ -159,6 +163,8 @@ class JsonFileProvider(BaseProvider):
|
|
|
159
163
|
item.name = data['name']
|
|
160
164
|
if "layout" in data:
|
|
161
165
|
item.layout = data['layout']
|
|
166
|
+
if "schema" in data:
|
|
167
|
+
item.schema = data['schema']
|
|
162
168
|
|
|
163
169
|
@staticmethod
|
|
164
170
|
def serialize_layout(item: BuilderLayoutItem) -> Dict[str, Any]:
|
|
@@ -169,9 +175,9 @@ class JsonFileProvider(BaseProvider):
|
|
|
169
175
|
:return: serialized item
|
|
170
176
|
"""
|
|
171
177
|
return {
|
|
172
|
-
'id': item.id,
|
|
173
|
-
'name': item.name,
|
|
174
|
-
'data': item.data,
|
|
178
|
+
'id': item.id if item else "",
|
|
179
|
+
'name': item.name if item else "",
|
|
180
|
+
'data': item.data if item else {},
|
|
175
181
|
}
|
|
176
182
|
|
|
177
183
|
@staticmethod
|
|
@@ -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.09.
|
|
9
|
+
# Updated Date: 2025.09.26 03:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import copy
|
|
@@ -151,6 +151,15 @@ class Patch:
|
|
|
151
151
|
data["ctx.urls.internal"] = False
|
|
152
152
|
updated = True
|
|
153
153
|
|
|
154
|
+
# < 2.6.61
|
|
155
|
+
if old < parse_version("2.6.61"):
|
|
156
|
+
print("Migrating config from < 2.6.61...")
|
|
157
|
+
if "presets.drag_and_drop.enabled" not in data:
|
|
158
|
+
data["presets.drag_and_drop.enabled"] = True
|
|
159
|
+
if "presets_order" not in data:
|
|
160
|
+
data["presets_order"] = {}
|
|
161
|
+
updated = True
|
|
162
|
+
|
|
154
163
|
# update file
|
|
155
164
|
migrated = False
|
|
156
165
|
if updated:
|
|
@@ -367,8 +367,6 @@ class Patch:
|
|
|
367
367
|
# < 2.0.72
|
|
368
368
|
if old < parse_version("2.0.72"):
|
|
369
369
|
print("Migrating config from < 2.0.72...")
|
|
370
|
-
if 'theme.markdown' not in data:
|
|
371
|
-
data['theme.markdown'] = True
|
|
372
370
|
prompt = 'IMAGE GENERATION: Whenever I provide a basic idea or concept for an image, such as \'a picture of ' \
|
|
373
371
|
'mountains\', I want you to ALWAYS translate it into English and expand and elaborate on this idea. ' \
|
|
374
372
|
'Use your knowledge and creativity to add details that would make the image more vivid and ' \
|
|
@@ -1301,8 +1299,6 @@ class Patch:
|
|
|
1301
1299
|
data["render.code_syntax"] = "github-dark"
|
|
1302
1300
|
if 'zoom' not in data:
|
|
1303
1301
|
data["zoom"] = 1.0
|
|
1304
|
-
if 'ctx.convert_lists' not in data:
|
|
1305
|
-
data["ctx.convert_lists"] = False
|
|
1306
1302
|
if 'render.engine' not in data:
|
|
1307
1303
|
data["render.engine"] = "web"
|
|
1308
1304
|
if 'render.open_gl' not in data:
|
|
@@ -1668,8 +1664,6 @@ class Patch:
|
|
|
1668
1664
|
# < 2.4.19
|
|
1669
1665
|
if old < parse_version("2.4.19"):
|
|
1670
1666
|
print("Migrating config from < 2.4.19...")
|
|
1671
|
-
if 'layout.animation.disable' not in data:
|
|
1672
|
-
data["layout.animation.disable"] = cfg_get_base('layout.animation.disable')
|
|
1673
1667
|
if 'cmd_code_interpreter' in data['plugins'] \
|
|
1674
1668
|
and 'cmd.ipython_execute' in data['plugins']['cmd_code_interpreter']:
|
|
1675
1669
|
# remove
|
|
@@ -6,9 +6,10 @@
|
|
|
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.25 14:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
|
+
import copy
|
|
12
13
|
from datetime import datetime
|
|
13
14
|
from typing import Dict
|
|
14
15
|
|
|
@@ -18,21 +19,26 @@ from PySide6.QtGui import QAction, QIcon
|
|
|
18
19
|
from pygpt_net.tools.base import BaseTool
|
|
19
20
|
from pygpt_net.utils import trans
|
|
20
21
|
|
|
22
|
+
from pygpt_net.core.node_editor.types import (
|
|
23
|
+
NodeTypeRegistry,
|
|
24
|
+
PropertySpec,
|
|
25
|
+
NodeTypeSpec
|
|
26
|
+
)
|
|
27
|
+
|
|
21
28
|
from .ui.dialogs import Builder
|
|
22
29
|
|
|
30
|
+
|
|
23
31
|
class AgentBuilder(BaseTool):
|
|
24
32
|
def __init__(self, *args, **kwargs):
|
|
25
|
-
"""
|
|
26
|
-
Agent builder (nodes editor)
|
|
27
|
-
|
|
28
|
-
:param window: Window instance
|
|
29
|
-
"""
|
|
30
33
|
super(AgentBuilder, self).__init__(*args, **kwargs)
|
|
31
34
|
self.id = "agent_builder"
|
|
32
35
|
self.opened = False
|
|
33
36
|
self.dialog = None # dialog
|
|
34
37
|
self.initialized = False
|
|
35
38
|
self.current_agent = None # current agent ID
|
|
39
|
+
# Reentrancy/teardown guards
|
|
40
|
+
self._restoring = False # True while restore/load is in progress
|
|
41
|
+
self._closing = False # True while dialog is being closed
|
|
36
42
|
|
|
37
43
|
def setup(self):
|
|
38
44
|
"""Setup controller"""
|
|
@@ -42,17 +48,41 @@ class AgentBuilder(BaseTool):
|
|
|
42
48
|
"""Update"""
|
|
43
49
|
pass
|
|
44
50
|
|
|
45
|
-
def save(self):
|
|
51
|
+
def save(self, force: bool = False):
|
|
46
52
|
"""Save layout to file"""
|
|
53
|
+
# Never save during restore or teardown
|
|
54
|
+
if not self.opened or self._closing or self._restoring:
|
|
55
|
+
if not force:
|
|
56
|
+
return
|
|
47
57
|
if "agent.builder" not in self.window.ui.editor:
|
|
48
58
|
return
|
|
59
|
+
|
|
60
|
+
builder = self.window.ui.editor["agent.builder"]
|
|
61
|
+
# Ensure the editor is alive (not closing)
|
|
62
|
+
if not getattr(builder, "_alive", True) or getattr(builder, "_closing", False):
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
data = builder.save_layout()
|
|
66
|
+
if not isinstance(data, dict):
|
|
67
|
+
return # skip invalid snapshots
|
|
68
|
+
|
|
69
|
+
schema_list = builder.export_schema(as_list=True)
|
|
70
|
+
try:
|
|
71
|
+
# Deep copy for safety (JSON-only types expected)
|
|
72
|
+
layout = copy.deepcopy(data)
|
|
73
|
+
schema = copy.deepcopy(schema_list)
|
|
74
|
+
except Exception:
|
|
75
|
+
# As a fallback, store raw references (still pure dict/list)
|
|
76
|
+
layout = data
|
|
77
|
+
schema = schema_list
|
|
78
|
+
|
|
49
79
|
custom = self.window.core.agents.custom
|
|
50
|
-
layout = self.window.ui.editor["agent.builder"].save_layout()
|
|
51
80
|
custom.update_layout(layout)
|
|
52
81
|
if self.current_agent:
|
|
53
|
-
custom.
|
|
82
|
+
custom.update_agent(self.current_agent, layout, schema)
|
|
54
83
|
custom.save()
|
|
55
84
|
self.window.update_status(f"Saved at: {datetime.now().strftime('%H:%M:%S')}")
|
|
85
|
+
self.update_presets()
|
|
56
86
|
|
|
57
87
|
def load(self):
|
|
58
88
|
"""Load layout from file"""
|
|
@@ -60,11 +90,10 @@ class AgentBuilder(BaseTool):
|
|
|
60
90
|
|
|
61
91
|
def open(self):
|
|
62
92
|
"""Open dialog"""
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
self.initialized = True
|
|
93
|
+
self.dialog = Builder(self.window, tool=self)
|
|
94
|
+
self.dialog.setup()
|
|
95
|
+
# Defer restore to allow previous deleteLater() to be processed
|
|
96
|
+
QtCore.QTimer.singleShot(0, self.restore)
|
|
68
97
|
self.window.ui.dialogs.open('agent.builder', width=900, height=600)
|
|
69
98
|
self.opened = True
|
|
70
99
|
self.update()
|
|
@@ -81,26 +110,33 @@ class AgentBuilder(BaseTool):
|
|
|
81
110
|
else:
|
|
82
111
|
self.open()
|
|
83
112
|
|
|
84
|
-
def store_current(self):
|
|
113
|
+
def store_current(self, force: bool = False):
|
|
85
114
|
"""Store current to file"""
|
|
86
|
-
self.
|
|
115
|
+
if self.opened and not self._restoring and (not self._closing or force):
|
|
116
|
+
self.save(force=force)
|
|
87
117
|
|
|
88
118
|
def restore(self):
|
|
89
119
|
"""Restore layout and agents from file"""
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
120
|
+
if self._closing:
|
|
121
|
+
return
|
|
122
|
+
self._restoring = True
|
|
123
|
+
try:
|
|
124
|
+
layout = self.window.core.agents.custom.get_layout()
|
|
125
|
+
if layout and "agent.builder" in self.window.ui.editor:
|
|
126
|
+
data = layout.data
|
|
127
|
+
if data:
|
|
128
|
+
self.window.ui.editor["agent.builder"].load_layout(data)
|
|
129
|
+
self.window.update_status(f"Loaded layout at: {datetime.now().strftime('%H:%M:%S')}")
|
|
96
130
|
|
|
97
|
-
|
|
131
|
+
self.update_list()
|
|
98
132
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
133
|
+
# select first agent on list
|
|
134
|
+
agents = self.window.core.agents.custom.get_agents()
|
|
135
|
+
if agents and len(agents) > 0:
|
|
136
|
+
first_agent_id = list(agents.keys())[0]
|
|
137
|
+
self.edit_agent(first_agent_id)
|
|
138
|
+
finally:
|
|
139
|
+
self._restoring = False
|
|
104
140
|
|
|
105
141
|
def show_hide(self, show: bool = True):
|
|
106
142
|
"""
|
|
@@ -114,13 +150,18 @@ class AgentBuilder(BaseTool):
|
|
|
114
150
|
self.close()
|
|
115
151
|
|
|
116
152
|
def on_close(self):
|
|
117
|
-
"""On window close"""
|
|
118
|
-
self.
|
|
119
|
-
|
|
153
|
+
"""On dialog window close (before destroy)"""
|
|
154
|
+
self._closing = True
|
|
155
|
+
try:
|
|
156
|
+
self.store_current(force=True)
|
|
157
|
+
finally:
|
|
158
|
+
self.opened = False
|
|
159
|
+
self._closing = False
|
|
120
160
|
|
|
121
161
|
def on_exit(self):
|
|
122
|
-
"""On exit"""
|
|
123
|
-
self.
|
|
162
|
+
"""On app exit"""
|
|
163
|
+
if self.opened:
|
|
164
|
+
self.on_close()
|
|
124
165
|
|
|
125
166
|
def clear(self, force: bool = False):
|
|
126
167
|
"""
|
|
@@ -135,24 +176,33 @@ class AgentBuilder(BaseTool):
|
|
|
135
176
|
msg=trans('agent.builder.confirm.clear.msg'),
|
|
136
177
|
)
|
|
137
178
|
return
|
|
138
|
-
if self.window.ui.editor
|
|
139
|
-
self.
|
|
179
|
+
if "agent.builder" in self.window.ui.editor:
|
|
180
|
+
if self.window.ui.editor["agent.builder"].clear(ask_user=False):
|
|
181
|
+
self.save()
|
|
140
182
|
|
|
141
183
|
def add_agent(self, name: str = None):
|
|
142
184
|
"""Add new agent"""
|
|
143
185
|
if name is None:
|
|
144
|
-
self.window.ui.dialog['create']
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
186
|
+
dialog = self.window.ui.dialog['create']
|
|
187
|
+
dialog.id = 'agent.builder.agent'
|
|
188
|
+
dialog.input.setText("")
|
|
189
|
+
dialog.current = ""
|
|
190
|
+
dialog.show()
|
|
191
|
+
dialog.input.setFocus()
|
|
149
192
|
return
|
|
193
|
+
|
|
194
|
+
if self.current_agent:
|
|
195
|
+
self.save() # save current before creating new
|
|
196
|
+
|
|
197
|
+
self.current_agent = None # prevent overwriting current agent
|
|
150
198
|
uuid = self.window.core.agents.custom.new_agent(name)
|
|
151
199
|
if uuid:
|
|
152
200
|
self.update_list()
|
|
153
|
-
self.window.ui.editor
|
|
201
|
+
if "agent.builder" in self.window.ui.editor:
|
|
202
|
+
self.window.ui.editor["agent.builder"].clear(ask_user=False)
|
|
154
203
|
self.window.ui.dialogs.close('create')
|
|
155
204
|
self.edit_agent(uuid)
|
|
205
|
+
self.update_presets()
|
|
156
206
|
|
|
157
207
|
def rename_agent(self, agent_id: str, name: str = None):
|
|
158
208
|
"""
|
|
@@ -167,17 +217,19 @@ class AgentBuilder(BaseTool):
|
|
|
167
217
|
return
|
|
168
218
|
current_name = agent.name
|
|
169
219
|
if name is None:
|
|
170
|
-
self.window.ui.dialog['rename']
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
220
|
+
dialog = self.window.ui.dialog['rename']
|
|
221
|
+
dialog.id = 'agent.builder.agent'
|
|
222
|
+
dialog.input.setText(current_name)
|
|
223
|
+
dialog.current =agent_id
|
|
224
|
+
dialog.show()
|
|
225
|
+
dialog.input.setFocus()
|
|
175
226
|
return
|
|
176
227
|
if agent:
|
|
177
228
|
agent.name = name
|
|
178
229
|
self.window.core.agents.custom.save()
|
|
179
230
|
self.update_list()
|
|
180
231
|
self.window.ui.dialogs.close('rename')
|
|
232
|
+
self.update_presets()
|
|
181
233
|
|
|
182
234
|
def edit_agent(self, agent_id: str):
|
|
183
235
|
"""
|
|
@@ -185,16 +237,36 @@ class AgentBuilder(BaseTool):
|
|
|
185
237
|
|
|
186
238
|
:param agent_id: agent ID
|
|
187
239
|
"""
|
|
188
|
-
|
|
240
|
+
# Save current agent only when switching and UI is stable
|
|
241
|
+
if (
|
|
242
|
+
self.current_agent
|
|
243
|
+
and self.current_agent != agent_id
|
|
244
|
+
and self.opened
|
|
245
|
+
and not self._restoring
|
|
246
|
+
and not self._closing
|
|
247
|
+
and "agent.builder" in self.window.ui.editor
|
|
248
|
+
):
|
|
249
|
+
builder = self.window.ui.editor["agent.builder"]
|
|
250
|
+
if getattr(builder, "_alive", True) and not getattr(builder, "_closing", False):
|
|
251
|
+
self.save()
|
|
252
|
+
|
|
189
253
|
self.current_agent = agent_id
|
|
254
|
+
|
|
255
|
+
if "agent.builder" not in self.window.ui.editor:
|
|
256
|
+
return
|
|
257
|
+
|
|
190
258
|
agent = self.window.core.agents.custom.get_agent(agent_id)
|
|
191
259
|
if agent:
|
|
260
|
+
editor = self.window.ui.editor["agent.builder"]
|
|
192
261
|
layout = agent.layout
|
|
193
262
|
if layout:
|
|
194
|
-
|
|
263
|
+
editor.load_layout(layout)
|
|
195
264
|
else:
|
|
196
|
-
|
|
197
|
-
|
|
265
|
+
editor.clear(ask_user=False)
|
|
266
|
+
|
|
267
|
+
# Select on list only when the list exists (dialog open)
|
|
268
|
+
if "agent.builder.list" in self.window.ui.nodes:
|
|
269
|
+
self.select_on_list(agent_id)
|
|
198
270
|
|
|
199
271
|
def duplicate_agent(self, agent_id: str):
|
|
200
272
|
"""
|
|
@@ -224,12 +296,39 @@ class AgentBuilder(BaseTool):
|
|
|
224
296
|
return
|
|
225
297
|
self.window.core.agents.custom.delete_agent(agent_id)
|
|
226
298
|
self.update_list()
|
|
299
|
+
|
|
300
|
+
# if deleted current, clear editor
|
|
227
301
|
if self.current_agent == agent_id:
|
|
228
302
|
self.current_agent = None
|
|
229
|
-
self.window.ui.editor
|
|
303
|
+
if "agent.builder" in self.window.ui.editor:
|
|
304
|
+
self.window.ui.editor["agent.builder"].clear(ask_user=False)
|
|
305
|
+
|
|
306
|
+
# select current selected on list if any after deletion (only if UI is open)
|
|
307
|
+
if self.opened and "agent.builder.list" in self.window.ui.nodes:
|
|
308
|
+
agents = self.window.core.agents.custom.get_agents()
|
|
309
|
+
if agents and len(agents) > 0:
|
|
310
|
+
idx = self.window.ui.nodes["agent.builder.list"].list.currentIndex()
|
|
311
|
+
if idx.isValid():
|
|
312
|
+
next_id = idx.data(QtCore.Qt.UserRole)
|
|
313
|
+
self.edit_agent(next_id)
|
|
314
|
+
|
|
315
|
+
def editing_allowed(self) -> bool:
|
|
316
|
+
"""
|
|
317
|
+
Check if editing is enabled (dialog is open and not closing/restoring)
|
|
318
|
+
|
|
319
|
+
:return: True if editing is enabled
|
|
320
|
+
"""
|
|
321
|
+
return self.opened and not self._closing and not self._restoring and self.current_agent is not None
|
|
322
|
+
|
|
323
|
+
def update_presets(self):
|
|
324
|
+
"""Update presets in the tools"""
|
|
325
|
+
self.window.controller.presets.editor.reload_all(all=True)
|
|
230
326
|
|
|
231
327
|
def update_list(self):
|
|
232
328
|
"""Update agents list"""
|
|
329
|
+
# Guard: dialog may not be open; do nothing if widget is not present
|
|
330
|
+
if "agent.builder.list" not in self.window.ui.nodes:
|
|
331
|
+
return
|
|
233
332
|
data = self.window.core.agents.custom.get_agents()
|
|
234
333
|
self.window.ui.nodes["agent.builder.list"].update_list(data)
|
|
235
334
|
|
|
@@ -239,13 +338,16 @@ class AgentBuilder(BaseTool):
|
|
|
239
338
|
|
|
240
339
|
:param agent_id: agent ID
|
|
241
340
|
"""
|
|
341
|
+
if "agent.builder.list" not in self.window.ui.nodes:
|
|
342
|
+
return
|
|
343
|
+
|
|
242
344
|
nodes = self.window.ui.nodes
|
|
243
345
|
models = self.window.ui.models
|
|
244
346
|
|
|
245
347
|
agents_list = nodes["agent.builder.list"].list
|
|
246
348
|
model = models.get("agent.builder.list")
|
|
247
349
|
|
|
248
|
-
if model is None:
|
|
350
|
+
if model is None or agents_list is None:
|
|
249
351
|
return
|
|
250
352
|
for row in range(model.rowCount()):
|
|
251
353
|
idx = model.index(row, 0)
|
|
@@ -279,6 +381,85 @@ class AgentBuilder(BaseTool):
|
|
|
279
381
|
"""Setup dialogs (static)"""
|
|
280
382
|
pass
|
|
281
383
|
|
|
384
|
+
def get_registry(self) -> NodeTypeRegistry:
|
|
385
|
+
"""
|
|
386
|
+
Get node type registry
|
|
387
|
+
|
|
388
|
+
:return: NodeTypeRegistry
|
|
389
|
+
"""
|
|
390
|
+
registry = NodeTypeRegistry(empty=True)
|
|
391
|
+
|
|
392
|
+
# Start
|
|
393
|
+
registry.register(NodeTypeSpec(
|
|
394
|
+
type_name="Flow/Start",
|
|
395
|
+
title=trans("node.editor.spec.start.title"),
|
|
396
|
+
base_id="start",
|
|
397
|
+
export_kind="start",
|
|
398
|
+
bg_color="#2D5A27",
|
|
399
|
+
properties=[
|
|
400
|
+
PropertySpec(id="output", type="flow", name=trans("node.editor.property.output.name"), editable=False,
|
|
401
|
+
allowed_inputs=0, allowed_outputs=1),
|
|
402
|
+
PropertySpec(id="memory", type="memory", name=trans("node.editor.property.memory.name"), editable=False,
|
|
403
|
+
allowed_inputs=0, allowed_outputs=-1),
|
|
404
|
+
],
|
|
405
|
+
))
|
|
406
|
+
# Agent
|
|
407
|
+
registry.register(NodeTypeSpec(
|
|
408
|
+
type_name="Flow/Agent",
|
|
409
|
+
title=trans("node.editor.spec.agent.title"),
|
|
410
|
+
base_id="agent",
|
|
411
|
+
export_kind="agent",
|
|
412
|
+
bg_color="#304A6E",
|
|
413
|
+
properties=[
|
|
414
|
+
PropertySpec(id="name", type="str", name=trans("node.editor.property.name.name"), editable=True,
|
|
415
|
+
value="",
|
|
416
|
+
placeholder=trans("node.editor.property.name.placeholder")),
|
|
417
|
+
PropertySpec(id="role", type="str", name=trans("node.editor.property.role.name"), editable=True,
|
|
418
|
+
value="",
|
|
419
|
+
placeholder=trans("node.editor.property.role.placeholder")),
|
|
420
|
+
PropertySpec(id="instruction", type="text", name=trans("node.editor.property.instruction.name"),
|
|
421
|
+
editable=True, value="",
|
|
422
|
+
placeholder=trans("node.editor.property.instruction.placeholder")),
|
|
423
|
+
PropertySpec(id="remote_tools", type="bool", name=trans("node.editor.property.remote_tools.name"),
|
|
424
|
+
editable=True, value=True),
|
|
425
|
+
PropertySpec(id="local_tools", type="bool", name=trans("node.editor.property.local_tools.name"),
|
|
426
|
+
editable=True, value=True),
|
|
427
|
+
PropertySpec(id="input", type="flow", name=trans("node.editor.property.input.name"), editable=False,
|
|
428
|
+
allowed_inputs=-1, allowed_outputs=0),
|
|
429
|
+
PropertySpec(id="output", type="flow", name=trans("node.editor.property.output.name"), editable=False,
|
|
430
|
+
allowed_inputs=0, allowed_outputs=-1),
|
|
431
|
+
PropertySpec(id="memory", type="memory", name=trans("node.editor.property.memory.name"), editable=False,
|
|
432
|
+
allowed_inputs=0, allowed_outputs=1),
|
|
433
|
+
],
|
|
434
|
+
))
|
|
435
|
+
# Memory
|
|
436
|
+
registry.register(NodeTypeSpec(
|
|
437
|
+
type_name="Flow/Memory",
|
|
438
|
+
title=trans("node.editor.spec.memory.title"),
|
|
439
|
+
base_id="mem",
|
|
440
|
+
export_kind="memory",
|
|
441
|
+
bg_color="#593E78",
|
|
442
|
+
properties=[
|
|
443
|
+
PropertySpec(id="name", type="str", name=trans("node.editor.property.name.name"), editable=True,
|
|
444
|
+
value=""),
|
|
445
|
+
PropertySpec(id="input", type="memory", name=trans("node.editor.property.agent.name"), editable=False,
|
|
446
|
+
allowed_inputs=-1, allowed_outputs=0),
|
|
447
|
+
],
|
|
448
|
+
))
|
|
449
|
+
# End
|
|
450
|
+
registry.register(NodeTypeSpec(
|
|
451
|
+
type_name="Flow/End",
|
|
452
|
+
title=trans("node.editor.spec.end.title"),
|
|
453
|
+
base_id="end",
|
|
454
|
+
export_kind="end",
|
|
455
|
+
bg_color="#6B2E2E",
|
|
456
|
+
properties=[
|
|
457
|
+
PropertySpec(id="input", type="flow", name=trans("node.editor.property.input.name"), editable=False,
|
|
458
|
+
allowed_inputs=-1, allowed_outputs=0),
|
|
459
|
+
],
|
|
460
|
+
))
|
|
461
|
+
return registry
|
|
462
|
+
|
|
282
463
|
def get_lang_mappings(self) -> Dict[str, Dict]:
|
|
283
464
|
"""
|
|
284
465
|
Get language mappings
|