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,229 +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.09.
|
|
9
|
+
# Updated Date: 2025.09.25 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
from typing import Dict, List, Optional, Tuple, Any
|
|
14
|
-
from dataclasses import dataclass, field
|
|
15
|
-
from uuid import uuid4
|
|
16
|
-
from PySide6.QtCore import QObject, Signal
|
|
17
14
|
import re
|
|
18
15
|
|
|
16
|
+
from PySide6.QtCore import QObject, Signal
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# ------------------------ Data models (pure data) ------------------------
|
|
25
|
-
|
|
26
|
-
@dataclass
|
|
27
|
-
class PropertyModel:
|
|
28
|
-
uuid: str
|
|
29
|
-
id: str
|
|
30
|
-
type: str # "slot", "str", "int", "float", "bool", "combo", "text"
|
|
31
|
-
name: str
|
|
32
|
-
editable: bool = True
|
|
33
|
-
value: Any = None
|
|
34
|
-
allowed_inputs: int = 0 # 0 none, -1 unlimited, >0 limit
|
|
35
|
-
allowed_outputs: int = 0 # 0 none, -1 unlimited, >0 limit
|
|
36
|
-
options: Optional[List[str]] = None # for combo
|
|
37
|
-
|
|
38
|
-
def to_dict(self) -> dict:
|
|
39
|
-
return {
|
|
40
|
-
"uuid": self.uuid,
|
|
41
|
-
"id": self.id,
|
|
42
|
-
"type": self.type,
|
|
43
|
-
"name": self.name,
|
|
44
|
-
"editable": self.editable,
|
|
45
|
-
"value": self.value,
|
|
46
|
-
"allowed_inputs": self.allowed_inputs,
|
|
47
|
-
"allowed_outputs": self.allowed_outputs,
|
|
48
|
-
"options": self.options or [],
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
@staticmethod
|
|
52
|
-
def from_dict(d: dict) -> "PropertyModel":
|
|
53
|
-
return PropertyModel(
|
|
54
|
-
uuid=d.get("uuid", gen_uuid()),
|
|
55
|
-
id=d["id"],
|
|
56
|
-
type=d["type"],
|
|
57
|
-
name=d.get("name", d["id"]),
|
|
58
|
-
editable=d.get("editable", True),
|
|
59
|
-
value=d.get("value"),
|
|
60
|
-
allowed_inputs=d.get("allowed_inputs", 0),
|
|
61
|
-
allowed_outputs=d.get("allowed_outputs", 0),
|
|
62
|
-
options=d.get("options") or None,
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
@dataclass
|
|
67
|
-
class NodeModel:
|
|
68
|
-
uuid: str
|
|
69
|
-
id: str
|
|
70
|
-
name: str
|
|
71
|
-
type: str
|
|
72
|
-
properties: Dict[str, PropertyModel] = field(default_factory=dict)
|
|
73
|
-
|
|
74
|
-
def to_dict(self) -> dict:
|
|
75
|
-
return {
|
|
76
|
-
"uuid": self.uuid,
|
|
77
|
-
"id": self.id,
|
|
78
|
-
"name": self.name,
|
|
79
|
-
"type": self.type,
|
|
80
|
-
"properties": {pid: p.to_dict() for pid, p in self.properties.items()},
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
@staticmethod
|
|
84
|
-
def from_dict(d: dict) -> "NodeModel":
|
|
85
|
-
props = {pid: PropertyModel.from_dict(pd) for pid, pd in d.get("properties", {}).items()}
|
|
86
|
-
return NodeModel(
|
|
87
|
-
uuid=d.get("uuid", gen_uuid()),
|
|
88
|
-
id=d["id"],
|
|
89
|
-
name=d.get("name", d["id"]),
|
|
90
|
-
type=d["type"],
|
|
91
|
-
properties=props,
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@dataclass
|
|
96
|
-
class ConnectionModel:
|
|
97
|
-
uuid: str
|
|
98
|
-
src_node: str
|
|
99
|
-
src_prop: str
|
|
100
|
-
dst_node: str
|
|
101
|
-
dst_prop: str
|
|
102
|
-
|
|
103
|
-
def to_dict(self) -> dict:
|
|
104
|
-
return {
|
|
105
|
-
"uuid": self.uuid,
|
|
106
|
-
"src_node": self.src_node, "src_prop": self.src_prop,
|
|
107
|
-
"dst_node": self.dst_node, "dst_prop": self.dst_prop,
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
@staticmethod
|
|
111
|
-
def from_dict(d: dict) -> "ConnectionModel":
|
|
112
|
-
return ConnectionModel(
|
|
113
|
-
uuid=d.get("uuid", gen_uuid()),
|
|
114
|
-
src_node=d["src_node"], src_prop=d["src_prop"],
|
|
115
|
-
dst_node=d["dst_node"], dst_prop=d["dst_prop"],
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
# ------------------------ Types registry (templates) ------------------------
|
|
120
|
-
|
|
121
|
-
@dataclass
|
|
122
|
-
class PropertySpec:
|
|
123
|
-
id: str
|
|
124
|
-
type: str
|
|
125
|
-
name: Optional[str] = None
|
|
126
|
-
editable: bool = True
|
|
127
|
-
value: Any = None
|
|
128
|
-
allowed_inputs: int = 0
|
|
129
|
-
allowed_outputs: int = 0
|
|
130
|
-
options: Optional[List[str]] = None
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
@dataclass
|
|
134
|
-
class NodeTypeSpec:
|
|
135
|
-
type_name: str
|
|
136
|
-
title: Optional[str] = None
|
|
137
|
-
properties: List[PropertySpec] = field(default_factory=list)
|
|
138
|
-
# Below are optional extensions for agent-flow needs:
|
|
139
|
-
base_id: Optional[str] = None # base prefix for friendly ids, e.g. "agent"
|
|
140
|
-
export_kind: Optional[str] = None # short kind for export, e.g. "agent", "start"
|
|
141
|
-
bg_color: Optional[str] = None # optional per-type background color (CSS/hex)
|
|
142
|
-
|
|
143
|
-
class NodeTypeRegistry:
|
|
144
|
-
"""Registry for node type specifications. Extend/override in subclasses."""
|
|
145
|
-
def __init__(self):
|
|
146
|
-
self._types: Dict[str, NodeTypeSpec] = {}
|
|
147
|
-
self._install_default_types()
|
|
148
|
-
|
|
149
|
-
def register(self, spec: NodeTypeSpec):
|
|
150
|
-
self._types[spec.type_name] = spec
|
|
151
|
-
|
|
152
|
-
def types(self) -> List[str]:
|
|
153
|
-
return list(self._types.keys())
|
|
154
|
-
|
|
155
|
-
def get(self, type_name: str) -> Optional[NodeTypeSpec]:
|
|
156
|
-
return self._types.get(type_name)
|
|
157
|
-
|
|
158
|
-
def _install_default_types(self):
|
|
159
|
-
# Example/basic nodes kept intact
|
|
160
|
-
self.register(NodeTypeSpec(
|
|
161
|
-
type_name="Value/Float",
|
|
162
|
-
title="Float",
|
|
163
|
-
properties=[
|
|
164
|
-
PropertySpec(id="value", type="float", name="Value", editable=True, value=0.0,
|
|
165
|
-
allowed_inputs=0, allowed_outputs=1),
|
|
166
|
-
]
|
|
167
|
-
))
|
|
168
|
-
self.register(NodeTypeSpec(
|
|
169
|
-
type_name="Math/Add",
|
|
170
|
-
title="Add",
|
|
171
|
-
properties=[
|
|
172
|
-
PropertySpec(id="A", type="float", name="A", editable=True, value=0.0, allowed_inputs=1, allowed_outputs=0),
|
|
173
|
-
PropertySpec(id="B", type="float", name="B", editable=True, value=0.0, allowed_inputs=1, allowed_outputs=0),
|
|
174
|
-
PropertySpec(id="result", type="float", name="Result", editable=False, value=None, allowed_inputs=0, allowed_outputs=1),
|
|
175
|
-
]
|
|
176
|
-
))
|
|
177
|
-
# Tip: to allow multiple connections to an input or output, set allowed_inputs/allowed_outputs to -1.
|
|
178
|
-
|
|
179
|
-
# Agent-flow nodes
|
|
180
|
-
# Start
|
|
181
|
-
self.register(NodeTypeSpec(
|
|
182
|
-
type_name="Flow/Start",
|
|
183
|
-
title="Start",
|
|
184
|
-
base_id="start",
|
|
185
|
-
export_kind="start",
|
|
186
|
-
bg_color="#2D5A27",
|
|
187
|
-
properties=[
|
|
188
|
-
PropertySpec(id="output", type="flow", name="Output", editable=False, allowed_inputs=0, allowed_outputs=1),
|
|
189
|
-
# base_id will be auto-injected as read-only property at creation
|
|
190
|
-
],
|
|
191
|
-
))
|
|
192
|
-
# Agent
|
|
193
|
-
self.register(NodeTypeSpec(
|
|
194
|
-
type_name="Flow/Agent",
|
|
195
|
-
title="Agent",
|
|
196
|
-
base_id="agent",
|
|
197
|
-
export_kind="agent",
|
|
198
|
-
bg_color="#304A6E",
|
|
199
|
-
properties=[
|
|
200
|
-
PropertySpec(id="name", type="str", name="Name", editable=True, value=""),
|
|
201
|
-
PropertySpec(id="instruction", type="text", name="Instruction", editable=True, value=""),
|
|
202
|
-
PropertySpec(id="remote_tools", type="bool", name="Remote tools", editable=True, value=True),
|
|
203
|
-
PropertySpec(id="local_tools", type="bool", name="Local tools", editable=True, value=True),
|
|
204
|
-
PropertySpec(id="input", type="flow", name="Input", editable=False, allowed_inputs=-1, allowed_outputs=0),
|
|
205
|
-
PropertySpec(id="output", type="flow", name="Output", editable=False, allowed_inputs=0, allowed_outputs=1),
|
|
206
|
-
PropertySpec(id="memory", type="memory", name="Memory", editable=False, allowed_inputs=0, allowed_outputs=1),
|
|
207
|
-
],
|
|
208
|
-
))
|
|
209
|
-
# Memory
|
|
210
|
-
self.register(NodeTypeSpec(
|
|
211
|
-
type_name="Flow/Memory",
|
|
212
|
-
title="Memory",
|
|
213
|
-
base_id="mem",
|
|
214
|
-
export_kind="memory",
|
|
215
|
-
bg_color="#593E78",
|
|
216
|
-
properties=[
|
|
217
|
-
PropertySpec(id="name", type="str", name="Name", editable=True, value=""),
|
|
218
|
-
PropertySpec(id="input", type="memory", name="Input", editable=False, allowed_inputs=-1, allowed_outputs=0),
|
|
219
|
-
],
|
|
220
|
-
))
|
|
221
|
-
# End
|
|
222
|
-
self.register(NodeTypeSpec(
|
|
223
|
-
type_name="Flow/End",
|
|
224
|
-
title="End",
|
|
225
|
-
base_id="end",
|
|
226
|
-
export_kind="end",
|
|
227
|
-
bg_color="#6B2E2E",
|
|
228
|
-
properties=[
|
|
229
|
-
PropertySpec(id="input", type="flow", name="Input", editable=False, allowed_inputs=1, allowed_outputs=0),
|
|
230
|
-
],
|
|
231
|
-
))
|
|
18
|
+
from .models import NodeModel, ConnectionModel, PropertyModel
|
|
19
|
+
from .types import NodeTypeRegistry
|
|
20
|
+
from .utils import gen_uuid
|
|
232
21
|
|
|
233
22
|
|
|
234
23
|
# ------------------------ Graph (Qt QObject + signals) ------------------------
|
|
@@ -294,16 +83,24 @@ class NodeGraph(QObject):
|
|
|
294
83
|
props: Dict[str, PropertyModel] = {}
|
|
295
84
|
for ps in spec.properties:
|
|
296
85
|
props[ps.id] = PropertyModel(
|
|
297
|
-
uuid=gen_uuid(),
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
86
|
+
uuid=gen_uuid(),
|
|
87
|
+
id=ps.id,
|
|
88
|
+
type=ps.type,
|
|
89
|
+
name=ps.name or ps.id,
|
|
90
|
+
editable=ps.editable,
|
|
91
|
+
value=ps.value,
|
|
92
|
+
allowed_inputs=ps.allowed_inputs,
|
|
93
|
+
allowed_outputs=ps.allowed_outputs,
|
|
94
|
+
options=ps.options,
|
|
95
|
+
placeholder=getattr(ps, "placeholder", None),
|
|
96
|
+
description=getattr(ps, "description", None),
|
|
301
97
|
)
|
|
302
98
|
# Auto inject read-only 'base_id' property for visibility if base_id defined and not present
|
|
303
99
|
if spec.base_id and "base_id" not in props:
|
|
304
100
|
props["base_id"] = PropertyModel(
|
|
305
101
|
uuid=gen_uuid(), id="base_id", type="str", name="Base ID",
|
|
306
|
-
editable=False, value=base_id, allowed_inputs=0, allowed_outputs=0
|
|
102
|
+
editable=False, value=base_id, allowed_inputs=0, allowed_outputs=0,
|
|
103
|
+
placeholder=None, description="Internal base identifier (read-only)."
|
|
307
104
|
)
|
|
308
105
|
|
|
309
106
|
node = NodeModel(uuid=gen_uuid(), id=nid, name=name or spec.title or nid, type=type_name, properties=props)
|
|
@@ -406,14 +203,15 @@ class NodeGraph(QObject):
|
|
|
406
203
|
"type": n.type,
|
|
407
204
|
"id": n.id,
|
|
408
205
|
"name": n.name,
|
|
409
|
-
|
|
206
|
+
# UI-only fields like HelpLabel are skipped
|
|
207
|
+
"values": {pid: p.value for pid, p in n.properties.items() if p.type != "HelpLabel"},
|
|
410
208
|
}
|
|
411
209
|
conns_out = [{"src": [c.src_node, c.src_prop], "dst": [c.dst_node, c.dst_prop]}
|
|
412
210
|
for c in self.connections.values()]
|
|
413
211
|
return {"nodes": nodes_out, "connections": conns_out}
|
|
414
212
|
|
|
415
|
-
# --- Export to
|
|
416
|
-
def
|
|
213
|
+
# --- Export to list schema (list of nodes with slots/in-out) ---
|
|
214
|
+
def to_list_schema(self) -> List[dict]:
|
|
417
215
|
# Build helper maps
|
|
418
216
|
uuid_to_node: Dict[str, NodeModel] = dict(self.nodes)
|
|
419
217
|
uuid_to_id: Dict[str, str] = {u: n.id for u, n in uuid_to_node.items()}
|
|
@@ -437,8 +235,8 @@ class NodeGraph(QObject):
|
|
|
437
235
|
"out": list(outgoing.get((n.uuid, pid), [])),
|
|
438
236
|
}
|
|
439
237
|
else:
|
|
440
|
-
# Skip internal
|
|
441
|
-
if pid == "base_id":
|
|
238
|
+
# Skip internal/helper and UI-only fields
|
|
239
|
+
if pid == "base_id" or prop.type == "HelpLabel":
|
|
442
240
|
continue
|
|
443
241
|
slots[pid] = prop.value
|
|
444
242
|
result.append({
|
|
@@ -474,5 +272,9 @@ class NodeGraph(QObject):
|
|
|
474
272
|
def clear(self, silent: bool = False):
|
|
475
273
|
self.nodes.clear()
|
|
476
274
|
self.connections.clear()
|
|
275
|
+
# Reset counters to keep friendly IDs strictly per-layout.
|
|
276
|
+
# After a clear, numbering starts again from 1 for each base prefix.
|
|
277
|
+
self._node_counter = 1
|
|
278
|
+
self._id_counters.clear()
|
|
477
279
|
if not silent:
|
|
478
280
|
self.cleared.emit()
|
|
@@ -0,0 +1,118 @@
|
|
|
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.25 00:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
from typing import Dict, List, Optional, Any
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
|
|
16
|
+
from .utils import gen_uuid
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ------------------------ Data models (pure data) ------------------------
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class PropertyModel:
|
|
23
|
+
uuid: str
|
|
24
|
+
id: str
|
|
25
|
+
type: str # "slot", "str", "int", "float", "bool", "combo", "text", "HelpLabel"
|
|
26
|
+
name: str
|
|
27
|
+
editable: bool = True
|
|
28
|
+
value: Any = None
|
|
29
|
+
allowed_inputs: int = 0 # 0 none, -1 unlimited, >0 limit
|
|
30
|
+
allowed_outputs: int = 0 # 0 none, -1 unlimited, >0 limit
|
|
31
|
+
options: Optional[List[str]] = None # for combo
|
|
32
|
+
# UI helpers
|
|
33
|
+
placeholder: Optional[str] = None
|
|
34
|
+
description: Optional[str] = None
|
|
35
|
+
|
|
36
|
+
def to_dict(self) -> dict:
|
|
37
|
+
return {
|
|
38
|
+
"uuid": self.uuid,
|
|
39
|
+
"id": self.id,
|
|
40
|
+
"type": self.type,
|
|
41
|
+
"name": self.name,
|
|
42
|
+
"editable": self.editable,
|
|
43
|
+
"value": self.value,
|
|
44
|
+
"allowed_inputs": self.allowed_inputs,
|
|
45
|
+
"allowed_outputs": self.allowed_outputs,
|
|
46
|
+
"options": self.options or [],
|
|
47
|
+
"placeholder": self.placeholder,
|
|
48
|
+
"description": self.description,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def from_dict(d: dict) -> "PropertyModel":
|
|
53
|
+
return PropertyModel(
|
|
54
|
+
uuid=d.get("uuid", gen_uuid()),
|
|
55
|
+
id=d["id"],
|
|
56
|
+
type=d["type"],
|
|
57
|
+
name=d.get("name", d["id"]),
|
|
58
|
+
editable=d.get("editable", True),
|
|
59
|
+
value=d.get("value"),
|
|
60
|
+
allowed_inputs=d.get("allowed_inputs", 0),
|
|
61
|
+
allowed_outputs=d.get("allowed_outputs", 0),
|
|
62
|
+
options=d.get("options") or None,
|
|
63
|
+
placeholder=d.get("placeholder"),
|
|
64
|
+
description=d.get("description"),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class NodeModel:
|
|
70
|
+
uuid: str
|
|
71
|
+
id: str
|
|
72
|
+
name: str
|
|
73
|
+
type: str
|
|
74
|
+
properties: Dict[str, PropertyModel] = field(default_factory=dict)
|
|
75
|
+
|
|
76
|
+
def to_dict(self) -> dict:
|
|
77
|
+
return {
|
|
78
|
+
"uuid": self.uuid,
|
|
79
|
+
"id": self.id,
|
|
80
|
+
"name": self.name,
|
|
81
|
+
"type": self.type,
|
|
82
|
+
"properties": {pid: p.to_dict() for pid, p in self.properties.items()},
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def from_dict(d: dict) -> "NodeModel":
|
|
87
|
+
props = {pid: PropertyModel.from_dict(pd) for pid, pd in d.get("properties", {}).items()}
|
|
88
|
+
return NodeModel(
|
|
89
|
+
uuid=d.get("uuid", gen_uuid()),
|
|
90
|
+
id=d["id"],
|
|
91
|
+
name=d.get("name", d["id"]),
|
|
92
|
+
type=d["type"],
|
|
93
|
+
properties=props,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass
|
|
98
|
+
class ConnectionModel:
|
|
99
|
+
uuid: str
|
|
100
|
+
src_node: str
|
|
101
|
+
src_prop: str
|
|
102
|
+
dst_node: str
|
|
103
|
+
dst_prop: str
|
|
104
|
+
|
|
105
|
+
def to_dict(self) -> dict:
|
|
106
|
+
return {
|
|
107
|
+
"uuid": self.uuid,
|
|
108
|
+
"src_node": self.src_node, "src_prop": self.src_prop,
|
|
109
|
+
"dst_node": self.dst_node, "dst_prop": self.dst_prop,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def from_dict(d: dict) -> "ConnectionModel":
|
|
114
|
+
return ConnectionModel(
|
|
115
|
+
uuid=d.get("uuid", gen_uuid()),
|
|
116
|
+
src_node=d["src_node"], src_prop=d["src_prop"],
|
|
117
|
+
dst_node=d["dst_node"], dst_prop=d["dst_prop"],
|
|
118
|
+
)
|
|
@@ -0,0 +1,78 @@
|
|
|
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.25 15:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
from typing import Dict, List, Optional, Any
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# ------------------------ Types registry (templates) ------------------------
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class PropertySpec:
|
|
21
|
+
id: str
|
|
22
|
+
type: str
|
|
23
|
+
name: Optional[str] = None
|
|
24
|
+
editable: bool = True
|
|
25
|
+
value: Any = None
|
|
26
|
+
allowed_inputs: int = 0
|
|
27
|
+
allowed_outputs: int = 0
|
|
28
|
+
options: Optional[List[str]] = None
|
|
29
|
+
placeholder: Optional[str] = None # hint text for text editors
|
|
30
|
+
description: Optional[str] = None # tooltip/help text shown in UI
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class NodeTypeSpec:
|
|
35
|
+
type_name: str
|
|
36
|
+
title: Optional[str] = None
|
|
37
|
+
properties: List[PropertySpec] = field(default_factory=list)
|
|
38
|
+
# Below are optional extensions for agent-flow needs:
|
|
39
|
+
base_id: Optional[str] = None # base prefix for friendly ids, e.g. "agent"
|
|
40
|
+
export_kind: Optional[str] = None # short kind for export, e.g. "agent", "start"
|
|
41
|
+
bg_color: Optional[str] = None # optional per-type background color (CSS/hex)
|
|
42
|
+
|
|
43
|
+
class NodeTypeRegistry:
|
|
44
|
+
"""Registry for node type specifications. Extend/override in subclasses."""
|
|
45
|
+
def __init__(self, empty: bool = False):
|
|
46
|
+
self._types: Dict[str, NodeTypeSpec] = {}
|
|
47
|
+
if not empty:
|
|
48
|
+
self._install_default_types()
|
|
49
|
+
|
|
50
|
+
def register(self, spec: NodeTypeSpec):
|
|
51
|
+
self._types[spec.type_name] = spec
|
|
52
|
+
|
|
53
|
+
def types(self) -> List[str]:
|
|
54
|
+
return list(self._types.keys())
|
|
55
|
+
|
|
56
|
+
def get(self, type_name: str) -> Optional[NodeTypeSpec]:
|
|
57
|
+
return self._types.get(type_name)
|
|
58
|
+
|
|
59
|
+
def _install_default_types(self):
|
|
60
|
+
# Example/basic nodes kept intact
|
|
61
|
+
self.register(NodeTypeSpec(
|
|
62
|
+
type_name="Value/Float",
|
|
63
|
+
title="Float",
|
|
64
|
+
properties=[
|
|
65
|
+
PropertySpec(id="value", type="float", name="Value", editable=True, value=0.0,
|
|
66
|
+
allowed_inputs=0, allowed_outputs=1),
|
|
67
|
+
]
|
|
68
|
+
))
|
|
69
|
+
self.register(NodeTypeSpec(
|
|
70
|
+
type_name="Math/Add",
|
|
71
|
+
title="Add",
|
|
72
|
+
properties=[
|
|
73
|
+
PropertySpec(id="A", type="float", name="A", editable=True, value=0.0, allowed_inputs=1, allowed_outputs=0),
|
|
74
|
+
PropertySpec(id="B", type="float", name="B", editable=True, value=0.0, allowed_inputs=1, allowed_outputs=0),
|
|
75
|
+
PropertySpec(id="result", type="float", name="Result", editable=False, value=None, allowed_inputs=0, allowed_outputs=1),
|
|
76
|
+
]
|
|
77
|
+
))
|
|
78
|
+
# Tip: to allow multiple connections to an input or output, set allowed_inputs/allowed_outputs to -1.
|
|
@@ -0,0 +1,17 @@
|
|
|
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 00:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
from uuid import uuid4
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def gen_uuid() -> str:
|
|
17
|
+
return str(uuid4())
|