kailash 0.7.0__py3-none-any.whl → 0.8.0__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.
- kailash/__init__.py +1 -1
- kailash/access_control.py +64 -46
- kailash/api/workflow_api.py +34 -3
- kailash/mcp_server/discovery.py +56 -17
- kailash/middleware/communication/api_gateway.py +23 -3
- kailash/middleware/communication/realtime.py +83 -0
- kailash/middleware/core/agent_ui.py +1 -1
- kailash/middleware/gateway/storage_backends.py +393 -0
- kailash/nexus/cli/__init__.py +5 -0
- kailash/nexus/cli/__main__.py +6 -0
- kailash/nexus/cli/main.py +176 -0
- kailash/nodes/__init__.py +6 -5
- kailash/nodes/base.py +29 -5
- kailash/nodes/code/python.py +50 -6
- kailash/nodes/data/async_sql.py +90 -0
- kailash/nodes/security/behavior_analysis.py +414 -0
- kailash/runtime/access_controlled.py +9 -7
- kailash/runtime/runner.py +6 -4
- kailash/runtime/testing.py +1 -1
- kailash/security.py +6 -2
- kailash/servers/enterprise_workflow_server.py +58 -2
- kailash/servers/workflow_server.py +3 -0
- kailash/workflow/builder.py +102 -14
- kailash/workflow/cyclic_runner.py +102 -10
- kailash/workflow/visualization.py +99 -27
- {kailash-0.7.0.dist-info → kailash-0.8.0.dist-info}/METADATA +3 -2
- {kailash-0.7.0.dist-info → kailash-0.8.0.dist-info}/RECORD +31 -28
- kailash/workflow/builder_improvements.py +0 -207
- {kailash-0.7.0.dist-info → kailash-0.8.0.dist-info}/WHEEL +0 -0
- {kailash-0.7.0.dist-info → kailash-0.8.0.dist-info}/entry_points.txt +0 -0
- {kailash-0.7.0.dist-info → kailash-0.8.0.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.7.0.dist-info → kailash-0.8.0.dist-info}/top_level.txt +0 -0
@@ -18,7 +18,7 @@ class WorkflowVisualizer:
|
|
18
18
|
|
19
19
|
def __init__(
|
20
20
|
self,
|
21
|
-
workflow: Workflow,
|
21
|
+
workflow: Workflow | None = None,
|
22
22
|
node_colors: dict[str, str] | None = None,
|
23
23
|
edge_colors: dict[str, str] | None = None,
|
24
24
|
layout: str = "hierarchical",
|
@@ -26,7 +26,7 @@ class WorkflowVisualizer:
|
|
26
26
|
"""Initialize visualizer.
|
27
27
|
|
28
28
|
Args:
|
29
|
-
workflow: Workflow to visualize
|
29
|
+
workflow: Workflow to visualize (can be set later)
|
30
30
|
node_colors: Custom node color map
|
31
31
|
edge_colors: Custom edge color map
|
32
32
|
layout: Layout algorithm to use
|
@@ -70,9 +70,12 @@ class WorkflowVisualizer:
|
|
70
70
|
"""Get colors for all nodes in workflow."""
|
71
71
|
colors = []
|
72
72
|
for node_id in self.workflow.graph.nodes():
|
73
|
-
node_instance =
|
74
|
-
|
75
|
-
|
73
|
+
node_instance = target_workflow.nodes.get(node_id)
|
74
|
+
if node_instance:
|
75
|
+
node_type = node_instance.node_type
|
76
|
+
colors.append(self._get_node_color(node_type))
|
77
|
+
else:
|
78
|
+
colors.append(self.node_colors["default"])
|
76
79
|
return colors
|
77
80
|
|
78
81
|
def _get_node_labels(self) -> dict[str, str]:
|
@@ -119,11 +122,15 @@ class WorkflowVisualizer:
|
|
119
122
|
|
120
123
|
return edge_labels
|
121
124
|
|
122
|
-
def _calculate_layout(self) -> dict[str, tuple[float, float]]:
|
125
|
+
def _calculate_layout(self, workflow: 'Workflow' = None) -> dict[str, tuple[float, float]]:
|
123
126
|
"""Calculate node positions for visualization."""
|
127
|
+
target_workflow = workflow or self.workflow
|
128
|
+
if not target_workflow:
|
129
|
+
return {}
|
130
|
+
|
124
131
|
# Try to use stored positions first
|
125
132
|
pos = {}
|
126
|
-
for node_id, node_instance in
|
133
|
+
for node_id, node_instance in target_workflow.nodes.items():
|
127
134
|
if node_instance.position != (0, 0):
|
128
135
|
pos[node_id] = node_instance.position
|
129
136
|
|
@@ -133,32 +140,69 @@ class WorkflowVisualizer:
|
|
133
140
|
# Use hierarchical layout for DAGs
|
134
141
|
try:
|
135
142
|
# Create layers based on topological order
|
136
|
-
layers = self._create_layers()
|
143
|
+
layers = self._create_layers(target_workflow)
|
137
144
|
pos = self._hierarchical_layout(layers)
|
138
145
|
except Exception:
|
139
146
|
# Fallback to spring layout
|
140
|
-
pos = nx.spring_layout(
|
147
|
+
pos = nx.spring_layout(target_workflow.graph, k=3, iterations=50)
|
141
148
|
elif self.layout == "circular":
|
142
|
-
pos = nx.circular_layout(
|
149
|
+
pos = nx.circular_layout(target_workflow.graph)
|
143
150
|
elif self.layout == "spring":
|
144
|
-
pos = nx.spring_layout(
|
151
|
+
pos = nx.spring_layout(target_workflow.graph, k=2, iterations=100)
|
145
152
|
else:
|
146
153
|
# Default to spring layout
|
147
|
-
pos = nx.spring_layout(
|
154
|
+
pos = nx.spring_layout(target_workflow.graph)
|
148
155
|
|
149
156
|
return pos
|
157
|
+
|
158
|
+
def _get_layout_positions(self, workflow: Workflow) -> dict[str, tuple[float, float]]:
|
159
|
+
"""Get layout positions for workflow nodes."""
|
160
|
+
# Temporarily store workflow and calculate layout
|
161
|
+
original_workflow = self.workflow
|
162
|
+
self.workflow = workflow
|
163
|
+
try:
|
164
|
+
return self._calculate_layout()
|
165
|
+
finally:
|
166
|
+
self.workflow = original_workflow
|
167
|
+
|
168
|
+
def _get_node_colors(self, workflow: Workflow) -> list[str]:
|
169
|
+
"""Get node colors for workflow."""
|
170
|
+
colors = []
|
171
|
+
for node_id in workflow.graph.nodes():
|
172
|
+
node_instance = workflow.get_node(node_id)
|
173
|
+
if node_instance:
|
174
|
+
node_type = node_instance.__class__.__name__.lower()
|
175
|
+
# Map node types to color categories
|
176
|
+
if "data" in node_type or "csv" in node_type or "json" in node_type:
|
177
|
+
color_key = "data"
|
178
|
+
elif "transform" in node_type or "python" in node_type:
|
179
|
+
color_key = "transform"
|
180
|
+
elif "switch" in node_type or "merge" in node_type:
|
181
|
+
color_key = "logic"
|
182
|
+
elif "llm" in node_type or "ai" in node_type:
|
183
|
+
color_key = "ai"
|
184
|
+
else:
|
185
|
+
color_key = "default"
|
186
|
+
colors.append(self.node_colors.get(color_key, self.node_colors["default"]))
|
187
|
+
else:
|
188
|
+
colors.append(self.node_colors["default"])
|
189
|
+
return colors
|
150
190
|
|
151
|
-
def _create_layers(self) -> dict[int, list]:
|
191
|
+
def _create_layers(self, workflow: 'Workflow' = None) -> dict[int, list]:
|
152
192
|
"""Create layers of nodes for hierarchical layout."""
|
193
|
+
target_workflow = workflow or self.workflow
|
194
|
+
if not target_workflow:
|
195
|
+
return {}
|
196
|
+
|
153
197
|
layers = {}
|
154
|
-
remaining = set(
|
198
|
+
remaining = set(target_workflow.graph.nodes())
|
155
199
|
layer = 0
|
156
200
|
|
157
201
|
while remaining:
|
158
202
|
# Find nodes with no dependencies in remaining set
|
159
203
|
current_layer = []
|
160
204
|
for node in remaining:
|
161
|
-
predecessors = set(
|
205
|
+
predecessors = set(target_workflow.graph.predecessors(node))
|
162
206
|
if not predecessors.intersection(remaining):
|
163
207
|
current_layer.append(node)
|
164
208
|
|
@@ -196,20 +240,34 @@ class WorkflowVisualizer:
|
|
196
240
|
|
197
241
|
def _draw_graph(
|
198
242
|
self,
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
243
|
+
workflow: Workflow | None = None,
|
244
|
+
pos: dict[str, tuple[float, float]] | None = None,
|
245
|
+
node_colors: list[str] | None = None,
|
246
|
+
show_labels: bool = True,
|
247
|
+
show_connections: bool = True,
|
203
248
|
) -> None:
|
204
249
|
"""Draw the graph with given positions and options."""
|
250
|
+
# Use provided workflow or fall back to instance workflow
|
251
|
+
target_workflow = workflow or self.workflow
|
252
|
+
if not target_workflow:
|
253
|
+
raise ValueError("No workflow provided to draw")
|
254
|
+
|
255
|
+
# Use default position if not provided
|
256
|
+
if pos is None:
|
257
|
+
pos = self._get_layout_positions(target_workflow)
|
258
|
+
|
259
|
+
# Use default colors if not provided
|
260
|
+
if node_colors is None:
|
261
|
+
node_colors = self._get_node_colors(target_workflow)
|
262
|
+
|
205
263
|
# Draw nodes
|
206
264
|
nx.draw_networkx_nodes(
|
207
|
-
|
265
|
+
target_workflow.graph, pos, node_color=node_colors, node_size=3000, alpha=0.9
|
208
266
|
)
|
209
267
|
|
210
268
|
# Draw edges
|
211
269
|
nx.draw_networkx_edges(
|
212
|
-
|
270
|
+
target_workflow.graph,
|
213
271
|
pos,
|
214
272
|
edge_color=self.edge_colors["default"],
|
215
273
|
width=2,
|
@@ -221,16 +279,26 @@ class WorkflowVisualizer:
|
|
221
279
|
|
222
280
|
# Draw labels
|
223
281
|
if show_labels:
|
282
|
+
# Temporarily set workflow for label generation
|
283
|
+
old_workflow = self.workflow
|
284
|
+
self.workflow = target_workflow
|
224
285
|
labels = self._get_node_labels()
|
286
|
+
self.workflow = old_workflow
|
287
|
+
|
225
288
|
nx.draw_networkx_labels(
|
226
|
-
|
289
|
+
target_workflow.graph, pos, labels, font_size=10, font_weight="bold"
|
227
290
|
)
|
228
291
|
|
229
292
|
# Draw connection labels
|
230
293
|
if show_connections:
|
294
|
+
# Temporarily set workflow for edge label generation
|
295
|
+
old_workflow = self.workflow
|
296
|
+
self.workflow = target_workflow
|
231
297
|
edge_labels = self._get_edge_labels()
|
298
|
+
self.workflow = old_workflow
|
299
|
+
|
232
300
|
nx.draw_networkx_edge_labels(
|
233
|
-
|
301
|
+
target_workflow.graph, pos, edge_labels, font_size=8
|
234
302
|
)
|
235
303
|
|
236
304
|
def visualize(
|
@@ -255,10 +323,14 @@ class WorkflowVisualizer:
|
|
255
323
|
**kwargs: Additional options passed to plt.savefig
|
256
324
|
"""
|
257
325
|
try:
|
326
|
+
# Check if workflow is available
|
327
|
+
if not self.workflow:
|
328
|
+
raise ValueError("No workflow to visualize. Set workflow property or create visualizer with workflow.")
|
329
|
+
|
258
330
|
plt.figure(figsize=figsize)
|
259
331
|
|
260
332
|
# Calculate node positions
|
261
|
-
pos = self._calculate_layout()
|
333
|
+
pos = self._calculate_layout(self.workflow)
|
262
334
|
|
263
335
|
# Handle empty workflow case
|
264
336
|
if not self.workflow.graph.nodes():
|
@@ -266,11 +338,11 @@ class WorkflowVisualizer:
|
|
266
338
|
node_colors = []
|
267
339
|
else:
|
268
340
|
# Draw the graph with colors
|
269
|
-
node_colors = self._get_node_colors()
|
341
|
+
node_colors = self._get_node_colors(self.workflow)
|
270
342
|
|
271
343
|
# Draw the graph components
|
272
|
-
if pos
|
273
|
-
self._draw_graph(pos, node_colors, show_labels, show_connections)
|
344
|
+
if pos:
|
345
|
+
self._draw_graph(workflow=self.workflow, pos=pos, node_colors=node_colors, show_labels=show_labels, show_connections=show_connections)
|
274
346
|
|
275
347
|
# Set title
|
276
348
|
if title is None:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: kailash
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.8.0
|
4
4
|
Summary: Python SDK for the Kailash container-node architecture
|
5
5
|
Home-page: https://github.com/integrum/kailash-python-sdk
|
6
6
|
Author: Integrum
|
@@ -21,7 +21,7 @@ Requires-Dist: matplotlib>=3.5
|
|
21
21
|
Requires-Dist: pyyaml>=6.0
|
22
22
|
Requires-Dist: click>=8.0
|
23
23
|
Requires-Dist: pytest>=8.3.5
|
24
|
-
Requires-Dist: mcp[cli]
|
24
|
+
Requires-Dist: mcp[cli]==1.11.0
|
25
25
|
Requires-Dist: pandas>=2.2.3
|
26
26
|
Requires-Dist: numpy>=2.2.5
|
27
27
|
Requires-Dist: scipy>=1.15.3
|
@@ -78,6 +78,7 @@ Requires-Dist: passlib>=1.7.4
|
|
78
78
|
Requires-Dist: pyotp>=2.9.0
|
79
79
|
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.55b1
|
80
80
|
Requires-Dist: seaborn>=0.13.2
|
81
|
+
Requires-Dist: sqlparse>=0.5.3
|
81
82
|
Provides-Extra: dev
|
82
83
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
83
84
|
Requires-Dist: pytest-cov>=3.0; extra == "dev"
|
@@ -1,10 +1,10 @@
|
|
1
|
-
kailash/__init__.py,sha256=
|
1
|
+
kailash/__init__.py,sha256=yCfV0dNXfj0xBIsqAbdMl31CxaSma9AtltRW6rI2AhU,2878
|
2
2
|
kailash/__main__.py,sha256=vr7TVE5o16V6LsTmRFKG6RDKUXHpIWYdZ6Dok2HkHnI,198
|
3
|
-
kailash/access_control.py,sha256=
|
3
|
+
kailash/access_control.py,sha256=MjKtkoQ2sg1Mgfe7ovGxVwhAbpJKvaepPWr8dxOueMA,26058
|
4
4
|
kailash/access_control_abac.py,sha256=FPfa_8PuDP3AxTjdWfiH3ntwWO8NodA0py9W8SE5dno,30263
|
5
5
|
kailash/manifest.py,sha256=qzOmeMGWz20Sp4IJitSH9gTVbGng7hlimc96VTW4KI8,24814
|
6
6
|
kailash/sdk_exceptions.py,sha256=dueSUxUYqKXmHS5mwl6QtEzP6a0rMGXcBFGCh5sT0tg,10179
|
7
|
-
kailash/security.py,sha256=
|
7
|
+
kailash/security.py,sha256=GBU93tUXcTZGEjl0f9XXRlF3W-sSulhiatMZzv1dNqc,27228
|
8
8
|
kailash/access_control/__init__.py,sha256=ykR_zGJXQoCU_4gGNFbYzhVdUemxeAAoDQLhKIPVBGE,4018
|
9
9
|
kailash/access_control/managers.py,sha256=Vg2inaZqR2GBXsySvPZcEqQtFHgF94z7A_wUHMtA3qA,14431
|
10
10
|
kailash/access_control/rule_evaluators.py,sha256=niguhjThBjA0jIXvdKdGAXzdSM_bAd0ebphGgRrDFKU,15337
|
@@ -16,7 +16,7 @@ kailash/api/custom_nodes_secure.py,sha256=cMh1FbEtUAEoLmTb5ew_CeX5kjikBzmBsat1GO
|
|
16
16
|
kailash/api/gateway.py,sha256=BVEKyC53JRnBkOyg3YXAPGDzLm3zon0vbO1xiM6yNxU,12540
|
17
17
|
kailash/api/mcp_integration.py,sha256=xY5VjYXh4bFuNyyWBXEuTm4jjwUn8_9QxZpa4hh6a0Q,14521
|
18
18
|
kailash/api/studio.py,sha256=8Gj3R3p_-EpJerkWYLyrqVzJprKOQmTmK-1EgiLoXMc,34892
|
19
|
-
kailash/api/workflow_api.py,sha256=
|
19
|
+
kailash/api/workflow_api.py,sha256=S1O_PRtTo2SSk47iUh4-uTh0WY1Tjbl1Gt54QIOLDmE,14330
|
20
20
|
kailash/channels/__init__.py,sha256=DP3PxbGDRCLrJkr6gGf1BBJ0MaHsfp_zUSbkyc04ieE,610
|
21
21
|
kailash/channels/api_channel.py,sha256=stuJxNJRUobzj1CavwsHWVZWr2kYqyVLjv_ynt6Wy6A,14541
|
22
22
|
kailash/channels/base.py,sha256=8-cUYAVMlO67BMZCZscShy0FPYAK31s9bxk0JXA9wIM,7825
|
@@ -60,7 +60,7 @@ kailash/mcp_server/ai_registry_server.py,sha256=vMNMvWLegKBVp7YAHVKgltWa2vTXKNvV
|
|
60
60
|
kailash/mcp_server/auth.py,sha256=Y2BztTL2IILCLSHzZIDSXsIKyUTR8gUXwum5rKffUxs,25251
|
61
61
|
kailash/mcp_server/client.py,sha256=uJXOP7Z4q3CFnX_RvRL5NM6PBcEfR7fUkPQIxfa8c8I,25710
|
62
62
|
kailash/mcp_server/client_new.py,sha256=YU671JvAM0uvuX0uhGZCIKI8co3fqz0cs6HqLZ59Xyo,10285
|
63
|
-
kailash/mcp_server/discovery.py,sha256=
|
63
|
+
kailash/mcp_server/discovery.py,sha256=D8vcwVkbgQCNp0_BlkGeU_dnqgIXN2g0s3_GpndlQu0,57828
|
64
64
|
kailash/mcp_server/errors.py,sha256=_lycwudWP_AJ_KwLO5N3VCKbG1ikfaTyzA2PBF8UAYU,21181
|
65
65
|
kailash/mcp_server/oauth.py,sha256=GFC2O2ueiTTI6V-91Huevhc3K8CxrHe22knuHfuCTqY,56493
|
66
66
|
kailash/mcp_server/protocol.py,sha256=gXeJ-GvSf39WymfS6433SqLKBA40PdeDoBQqu7DUbJE,35129
|
@@ -83,11 +83,11 @@ kailash/middleware/auth/models.py,sha256=sMQ1JnEDKPId1kLNCi2fDfhgLZX-YguHPnSMfS_
|
|
83
83
|
kailash/middleware/auth/utils.py,sha256=AW4fYJ2opQvOr_1YgSQi_MsC_RoFPq0XVOEgbxoXWtw,6555
|
84
84
|
kailash/middleware/communication/__init__.py,sha256=keQ2db4WI2-oZ_nJ5sLE1Tum_RkUt7M2VLTmqOlt0zA,778
|
85
85
|
kailash/middleware/communication/ai_chat.py,sha256=2XmnJB2Nz2xm2darsF2DYnSLGLAYdyYooHok5GrHKb8,35994
|
86
|
-
kailash/middleware/communication/api_gateway.py,sha256
|
86
|
+
kailash/middleware/communication/api_gateway.py,sha256=hjs3Ued5YhFR1LcGJEB2d-F96imIuS7W7bxctQG3blA,32272
|
87
87
|
kailash/middleware/communication/events.py,sha256=MEjgcibNyjA4tSFK8CeXDn1oCE75My7K_saxdCBz2HY,15367
|
88
|
-
kailash/middleware/communication/realtime.py,sha256=
|
88
|
+
kailash/middleware/communication/realtime.py,sha256=xm0i-TUs82XUtnFfHpdeZcx4uMt_V-fmS_uTuvXqDV0,28647
|
89
89
|
kailash/middleware/core/__init__.py,sha256=4yQkOWC4b88zSogs1YVqtKG1PugbncqNCwNNWxTdIZA,545
|
90
|
-
kailash/middleware/core/agent_ui.py,sha256=
|
90
|
+
kailash/middleware/core/agent_ui.py,sha256=Ki5QpsYNM3o3h9KGor4YEXs9Xg8ehXe3w_M8SJnYhxI,36358
|
91
91
|
kailash/middleware/core/schema.py,sha256=uVF-5ZJlLYHOQdsKrG46FnTO1bq_QtDjhUSkIIDL9dY,23584
|
92
92
|
kailash/middleware/core/workflows.py,sha256=kjwwP69-T6eCY7kWIMLUBwVy2CapoPR34cqCETquq0s,12480
|
93
93
|
kailash/middleware/database/__init__.py,sha256=UMws94L-vja94AjfzPWIgn0h4_5BGQzI3YaSdqtzeLk,1682
|
@@ -104,14 +104,18 @@ kailash/middleware/gateway/deduplicator.py,sha256=CblV3fwc7s4wg6KIvypwyNMYL5AuQi
|
|
104
104
|
kailash/middleware/gateway/durable_gateway.py,sha256=EsIgMNxS_no2W40AXDyE7FmVdnGNU26kBRC5rfnSzoQ,14626
|
105
105
|
kailash/middleware/gateway/durable_request.py,sha256=SCnp-bF0tQX9oahr9reqcZjJ_YhyJkeYYl-un9rJ6lo,15437
|
106
106
|
kailash/middleware/gateway/event_store.py,sha256=A3Kh2MhVVPbXWvjeo550SqEGPiJYyspAfu6Gv7UZzo4,16131
|
107
|
+
kailash/middleware/gateway/storage_backends.py,sha256=HJTi6zU6oBI3R3ffcm_U-Xp9qqb9yOHS7JbRawP8AV8,12974
|
107
108
|
kailash/middleware/mcp/__init__.py,sha256=EdZB8zOMSBEEmudRzs8ksz9QZJYWQMEx7Tm1MOwIWnI,922
|
108
109
|
kailash/middleware/mcp/client_integration.py,sha256=dY1RmX-g5E6JzUFuWxk7viuOYIh8bMwoUSvHQMVEsYk,18265
|
109
110
|
kailash/middleware/mcp/enhanced_server.py,sha256=RUVS7jWHn0ma4F3F23UvuFwUdu7OkSsIRNQmGtkG9I8,18547
|
110
111
|
kailash/nexus/__init__.py,sha256=zaOXOHmmxsDDuyVDA7P-EWkXHLsLHcnrZPSlNQ74M1k,463
|
111
112
|
kailash/nexus/factory.py,sha256=oqtPJoVvwQUdafs_h92MDpjt6xSO1vNLZHizZ1-XLks,12296
|
112
113
|
kailash/nexus/gateway.py,sha256=QzvJVFVmgNI_Z4VHIRpUQ9F8_HNrhINjAiJQ1kTpqJU,19105
|
113
|
-
kailash/
|
114
|
-
kailash/
|
114
|
+
kailash/nexus/cli/__init__.py,sha256=XbGcZSytNg5lJCOZ981prJpzwTXYJcR6_d6Fw9cXUTo,106
|
115
|
+
kailash/nexus/cli/__main__.py,sha256=GCIzeLFbdtBoRwfB1r0gxME078qd1e3IIFrGHHZQfAc,117
|
116
|
+
kailash/nexus/cli/main.py,sha256=2GqJP5fvP8DgPaeWzOjAG9yS0CbDWhzEl_tI38jO3VQ,5550
|
117
|
+
kailash/nodes/__init__.py,sha256=0uT1nGAIZNzk_tkQo1gmEEkroE8qiVWSjDv1hpoKqQA,1009
|
118
|
+
kailash/nodes/base.py,sha256=LqxnXG1hl33GY4caZiOCshagq-FfHe_nDHVAww1Dkmg,57125
|
115
119
|
kailash/nodes/base_async.py,sha256=whxepCiVplrltfzEQuabmnGCpEV5WgfqwgxbLdCyiDk,8864
|
116
120
|
kailash/nodes/base_cycle_aware.py,sha256=Xpze9xZzLepWeLpi9Y3tMn1dm2LVv-omr5TSQuGTtWo,13377
|
117
121
|
kailash/nodes/base_with_acl.py,sha256=ZfrkLPgrEBcNbG0LKvtq6glDxyOYOMRw3VXX4vWX6bI,11852
|
@@ -162,13 +166,13 @@ kailash/nodes/cache/cache_invalidation.py,sha256=IUvxrRj3K5EF29Z2EaKl7t6Uze_cssn
|
|
162
166
|
kailash/nodes/cache/redis_pool_manager.py,sha256=GR82GCWxo_gAzRE-091OB6AhKre8CTwM3OoePLb2gvE,21574
|
163
167
|
kailash/nodes/code/__init__.py,sha256=yhEwuMjUEPFfe6hMGMd4E4gZdLUuf2JEQ7knYapiM4o,1283
|
164
168
|
kailash/nodes/code/async_python.py,sha256=Ai-iMpmz-sAori73JBk0wZtqmwtmF2GNPDxqB04I2Ck,37058
|
165
|
-
kailash/nodes/code/python.py,sha256=
|
169
|
+
kailash/nodes/code/python.py,sha256=u-5lSoxc5ul7zGGSanrcVwXLxlGcDlirdCXT6mLSm40,60331
|
166
170
|
kailash/nodes/compliance/__init__.py,sha256=6a_FL4ofc8MAVuZ-ARW5uYenZLS4mBFVM9AI2QsnoF8,214
|
167
171
|
kailash/nodes/compliance/data_retention.py,sha256=90bH_eGwlcDzUdklAJeXQM-RcuLUGQFQ5fgHOK8a4qk,69443
|
168
172
|
kailash/nodes/compliance/gdpr.py,sha256=ZMoHZjAo4QtGwtFCzGMrAUBFV3TbZOnJ5DZGZS87Bas,70548
|
169
173
|
kailash/nodes/data/__init__.py,sha256=f0h4ysvXxlyFcNJLvDyXrgJ0ixwDF1cS0pJ2QNPakhg,5213
|
170
174
|
kailash/nodes/data/async_connection.py,sha256=wfArHs9svU48bxGZIiixSV2YVn9cukNgEjagwTRu6J4,17250
|
171
|
-
kailash/nodes/data/async_sql.py,sha256=
|
175
|
+
kailash/nodes/data/async_sql.py,sha256=k4zgeHxgZLB06bl8Aos4WdFrHkYvhtAHX3en9wJv1Hw,103160
|
172
176
|
kailash/nodes/data/async_vector.py,sha256=HtwQLO25IXu8Vq80qzU8rMkUAKPQ2qM0x8YxjXHlygU,21005
|
173
177
|
kailash/nodes/data/bulk_operations.py,sha256=WVopmosVkIlweFxVt3boLdCPc93EqpYyQ1Ez9mCIt0c,34453
|
174
178
|
kailash/nodes/data/directory.py,sha256=fbfLqD_ijRubk-4xew3604QntPsyDxqaF4k6TpfyjDg,9923
|
@@ -237,7 +241,7 @@ kailash/nodes/rag/workflows.py,sha256=PL9d3Lk15drwo2QVdNR9B61EpZ8Kc1crjnqFXc10O1
|
|
237
241
|
kailash/nodes/security/__init__.py,sha256=oaoZdbKhjEZ8vtUHGRPZPDixInzFDmhUZg4ou_Lz6mI,611
|
238
242
|
kailash/nodes/security/abac_evaluator.py,sha256=MAVJrIIQGHdOI-OfzGJFImVaHSq4vO8avEB0bSTprWk,50313
|
239
243
|
kailash/nodes/security/audit_log.py,sha256=zjwLoWHJLhkJiD5rRmE8H2mSEE7-6EHh_FR0FT91uLU,3130
|
240
|
-
kailash/nodes/security/behavior_analysis.py,sha256=
|
244
|
+
kailash/nodes/security/behavior_analysis.py,sha256=zFR5Yny0C85JFnR4I4nfWgHX5PI1bmpB1i-cLZjSvq4,90007
|
241
245
|
kailash/nodes/security/credential_manager.py,sha256=kqetmEBUnOOtSJyCpf0o_IRlwzpCv5FeInB4enBfk5E,14968
|
242
246
|
kailash/nodes/security/rotating_credentials.py,sha256=yEDSraBi_M3DKAaodmkkVPXHreUvrLU8xuTky_Z-tT4,28928
|
243
247
|
kailash/nodes/security/security_event.py,sha256=yVP8foaki-LDD16mlRapaPsbaWMwsum5J_V0n1bnqvw,4150
|
@@ -266,7 +270,7 @@ kailash/resources/health.py,sha256=i6XS0HdL6pUYq8SBdRjvchf-oj0sy3JoRLszNylfQJc,9
|
|
266
270
|
kailash/resources/reference.py,sha256=RKfXzJFIdid0cLOwsXSmlXq4NhSEci4GO-q3M3qcjA8,7526
|
267
271
|
kailash/resources/registry.py,sha256=5978e9VcUq0XBi9LRN89swptBSOAOAyNmwe2pxFHMxM,13789
|
268
272
|
kailash/runtime/__init__.py,sha256=CvU-qBMESYYISqFOlYlLsYJrXJu0Gqr4x6yr4Ob_Rng,278
|
269
|
-
kailash/runtime/access_controlled.py,sha256=
|
273
|
+
kailash/runtime/access_controlled.py,sha256=HtNJZylaB-2FuPsfEOfQ-4ny4HzwJfHaHNMu2xS1Nzs,17324
|
270
274
|
kailash/runtime/async_local.py,sha256=sYNggSU0R-oo8cCvU5ayodDBqASzUhxu994ZvZxDSC0,34010
|
271
275
|
kailash/runtime/docker.py,sha256=sZknVl1PCGfAZeyc0-exTuKlllSyjYlFIgJoiB3CRNs,23500
|
272
276
|
kailash/runtime/local.py,sha256=-xqgydrSpg0eiC3MIVfWK3S6H4dUNUojrXFKttN_YGc,46422
|
@@ -274,13 +278,13 @@ kailash/runtime/parallel.py,sha256=mz_wPD13-YVc3Q_8HkOs4nPQPdTjnjCcnRL7ZRM70lo,2
|
|
274
278
|
kailash/runtime/parallel_cyclic.py,sha256=yANZHnePjhCPuCFbq3lFQA1K6jbCv5Of5-vIKbCsmZk,19863
|
275
279
|
kailash/runtime/parameter_injection.py,sha256=kG4GhmarsRr5t3VDFbc2G1HSbsZJg6UmienHCE2Ru7o,14852
|
276
280
|
kailash/runtime/parameter_injector.py,sha256=4l-OOKEBzM_cQ17Zz5o24QSV9a1Zqfu3D2qyq6Uy7hE,30810
|
277
|
-
kailash/runtime/runner.py,sha256=
|
278
|
-
kailash/runtime/testing.py,sha256=
|
281
|
+
kailash/runtime/runner.py,sha256=t_SEumtVn9uXlSzJicb50MpUu6xdqjW0uZ5b2phjVPY,3318
|
282
|
+
kailash/runtime/testing.py,sha256=hTrgGnqxwSBYoztVqnZpxzFwm0DwUnaJdChRHikgoio,19089
|
279
283
|
kailash/servers/__init__.py,sha256=-2nkB5pIkBjc-klGJcKCuzWRAl_G7hlepvyV_Es64DI,976
|
280
284
|
kailash/servers/durable_workflow_server.py,sha256=lMnf-rTUKrjiS9NhNo3qx2lWYC0NR7EEF-4WBUPZ9zI,16588
|
281
|
-
kailash/servers/enterprise_workflow_server.py,sha256=
|
285
|
+
kailash/servers/enterprise_workflow_server.py,sha256=HxArcgu5fQwV2KxKkf1n0Yu75zXy_tS8oxd4-1DM5UY,19785
|
282
286
|
kailash/servers/gateway.py,sha256=faZTHsHL-3GwX-um3iLT4MaJmub2QIMNPnlkLAJe7Oc,6552
|
283
|
-
kailash/servers/workflow_server.py,sha256=
|
287
|
+
kailash/servers/workflow_server.py,sha256=nkx8qpZ4xJD767L3M2dTj68SXv8uLUNht6tZ0v2eB5c,9538
|
284
288
|
kailash/testing/__init__.py,sha256=4cqLSbcFz9bDY09vuK5Vpg3UtoAAa93R5tc9zIUvGAE,796
|
285
289
|
kailash/testing/async_test_case.py,sha256=3Pdi13zT-_LduJ5Tfe7yuhz8Qz7KQpcER6G2nMe-t5A,12815
|
286
290
|
kailash/testing/async_utils.py,sha256=gSHcArDJf98mDi--NPFpCoGmAgKHDZ0__tyKAjDjpm0,10745
|
@@ -312,8 +316,7 @@ kailash/visualization/reports.py,sha256=D7kJ0flHr16d-qSEq8vnw20N8u_dgTrXtKVSXVm8
|
|
312
316
|
kailash/workflow/__init__.py,sha256=DDQDE9K6RmbX6479guNLLgjiVVV-gQERRvCEJWSVlsM,1836
|
313
317
|
kailash/workflow/async_builder.py,sha256=iv8bDJHdWAUZ77SyMo6sucd92dTdtXesdxycrSE7mM4,20613
|
314
318
|
kailash/workflow/async_patterns.py,sha256=X0ZDXwr6UAu0WC1xnCB7-0V1-tRbKs9UI4JqaBCB6tE,22824
|
315
|
-
kailash/workflow/builder.py,sha256=
|
316
|
-
kailash/workflow/builder_improvements.py,sha256=MYAf-CJkrXTP8Q6leapnfVZATQxCJJ1cAymNOqXCh2w,7374
|
319
|
+
kailash/workflow/builder.py,sha256=3l5wVRtMB1Om_QmeoQ6jsrc_Lm8Hna_pH47UjnfMb9I,31184
|
317
320
|
kailash/workflow/convergence.py,sha256=vfIDR-uNaQE-LVUEzrRtfgKPgX9gL0nLNH-nTg5ra-c,10031
|
318
321
|
kailash/workflow/cycle_analyzer.py,sha256=BGBpgdB-g0-KRI65sVAvHV4lxfoCzMt4uKOHbw8GXT4,32596
|
319
322
|
kailash/workflow/cycle_builder.py,sha256=uWAx8K4ZKMtFC3mWylK4gHT03xeu0xChJlhw50hVqEE,20883
|
@@ -322,7 +325,7 @@ kailash/workflow/cycle_debugger.py,sha256=eG-Q_kakqyhr1Ts-q4pRnO0EI7mVO9ao1s9WOx
|
|
322
325
|
kailash/workflow/cycle_exceptions.py,sha256=4_OLnbEXqIiXKzOc3uh8DzFik4wEHwl8bRZhY9Xhf2A,21838
|
323
326
|
kailash/workflow/cycle_profiler.py,sha256=aEWSCm0Xy15SjgLTpPooVJMzpFhtJWt4livR-3Me4N8,28547
|
324
327
|
kailash/workflow/cycle_state.py,sha256=hzRUvciRreWfS56Cf7ZLQPit_mlPTQDoNTawh8yi-2s,10747
|
325
|
-
kailash/workflow/cyclic_runner.py,sha256=
|
328
|
+
kailash/workflow/cyclic_runner.py,sha256=0IlmghTrwYb3ivqP975DprP3aj45-8_Sn5Wq9tEutG0,43888
|
326
329
|
kailash/workflow/graph.py,sha256=zRpGLXeuwtuxFBvE7_16c_bB9yqZirM_uwtfD1_MY4g,59272
|
327
330
|
kailash/workflow/input_handling.py,sha256=HrW--AmelYC8F18nkfmYlF_wXycA24RuNbDRjvM8rqk,6561
|
328
331
|
kailash/workflow/mermaid_visualizer.py,sha256=UsQDvxgIAhy-Th7ZzFnbuziaTx1Cd5yUh6S_25DffoQ,22294
|
@@ -334,10 +337,10 @@ kailash/workflow/safety.py,sha256=pS5GKu7UdkzFZcb16Dn-0jBxjULDU-59_M0CbUVMVyw,11
|
|
334
337
|
kailash/workflow/state.py,sha256=UTZxs5-Ona6uvBhx1__i6-RX8gB4qazkBIWE7uyRmWQ,7600
|
335
338
|
kailash/workflow/templates.py,sha256=98EN5H4fO9b4xeczk20Hu5L8hNcAuRQNGayT6vnZYCw,48540
|
336
339
|
kailash/workflow/validation.py,sha256=rWZNJYA_XAfk_og6Cxz8p1gZ3j5CylPvMTDXg2SfjgM,38574
|
337
|
-
kailash/workflow/visualization.py,sha256=
|
338
|
-
kailash-0.
|
339
|
-
kailash-0.
|
340
|
-
kailash-0.
|
341
|
-
kailash-0.
|
342
|
-
kailash-0.
|
343
|
-
kailash-0.
|
340
|
+
kailash/workflow/visualization.py,sha256=_oWiLrd6PjoAnNvCnQtFjxx2SVwhDeuYkuPz_ptIQvQ,23013
|
341
|
+
kailash-0.8.0.dist-info/licenses/LICENSE,sha256=Axe6g7bTrJkToK9h9j2SpRUKKNaDZDCo2lQ2zPxCE6s,1065
|
342
|
+
kailash-0.8.0.dist-info/METADATA,sha256=xsd4eOqs4Lt3pigjT2SoOKnoQ0-P6YjSXf9zYu66LVY,26441
|
343
|
+
kailash-0.8.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
344
|
+
kailash-0.8.0.dist-info/entry_points.txt,sha256=M_q3b8PG5W4XbhSgESzIJjh3_4OBKtZFYFsOdkr2vO4,45
|
345
|
+
kailash-0.8.0.dist-info/top_level.txt,sha256=z7GzH2mxl66498pVf5HKwo5wwfPtt9Aq95uZUpH6JV0,8
|
346
|
+
kailash-0.8.0.dist-info/RECORD,,
|
@@ -1,207 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
WorkflowBuilder Improvements for Parameter Passing
|
3
|
-
|
4
|
-
This module contains improvements to the WorkflowBuilder to handle
|
5
|
-
parameter passing to nodes without incoming connections.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from typing import Any, Dict, List, Optional
|
9
|
-
|
10
|
-
from kailash.workflow.builder import WorkflowBuilder
|
11
|
-
from kailash.workflow.graph import Workflow
|
12
|
-
|
13
|
-
|
14
|
-
class ImprovedWorkflowBuilder(WorkflowBuilder):
|
15
|
-
"""Enhanced WorkflowBuilder that automatically handles parameter passing"""
|
16
|
-
|
17
|
-
def __init__(self):
|
18
|
-
super().__init__()
|
19
|
-
self.workflow_parameters: Dict[str, Any] = {}
|
20
|
-
self.parameter_mappings: Dict[str, Dict[str, str]] = {}
|
21
|
-
|
22
|
-
def set_workflow_parameters(self, **parameters) -> "ImprovedWorkflowBuilder":
|
23
|
-
"""
|
24
|
-
Set default parameters that will be passed to all nodes.
|
25
|
-
|
26
|
-
Args:
|
27
|
-
**parameters: Key-value pairs of workflow-level parameters
|
28
|
-
|
29
|
-
Returns:
|
30
|
-
Self for chaining
|
31
|
-
"""
|
32
|
-
self.workflow_parameters.update(parameters)
|
33
|
-
return self
|
34
|
-
|
35
|
-
def add_parameter_mapping(
|
36
|
-
self, node_id: str, mappings: Dict[str, str]
|
37
|
-
) -> "ImprovedWorkflowBuilder":
|
38
|
-
"""
|
39
|
-
Add parameter mappings for a specific node.
|
40
|
-
|
41
|
-
Args:
|
42
|
-
node_id: Node to configure
|
43
|
-
mappings: Dict mapping workflow param names to node param names
|
44
|
-
|
45
|
-
Returns:
|
46
|
-
Self for chaining
|
47
|
-
"""
|
48
|
-
if node_id not in self.parameter_mappings:
|
49
|
-
self.parameter_mappings[node_id] = {}
|
50
|
-
self.parameter_mappings[node_id].update(mappings)
|
51
|
-
return self
|
52
|
-
|
53
|
-
def add_input_connection(
|
54
|
-
self, to_node: str, to_input: str, from_workflow_param: str
|
55
|
-
) -> "ImprovedWorkflowBuilder":
|
56
|
-
"""
|
57
|
-
Connect a workflow parameter directly to a node input.
|
58
|
-
|
59
|
-
Args:
|
60
|
-
to_node: Target node ID
|
61
|
-
to_input: Input parameter name on the node
|
62
|
-
from_workflow_param: Workflow parameter name
|
63
|
-
|
64
|
-
Returns:
|
65
|
-
Self for chaining
|
66
|
-
"""
|
67
|
-
# Add a special connection type for workflow inputs
|
68
|
-
connection = {
|
69
|
-
"from_node": "__workflow_input__",
|
70
|
-
"from_output": from_workflow_param,
|
71
|
-
"to_node": to_node,
|
72
|
-
"to_input": to_input,
|
73
|
-
"is_workflow_input": True,
|
74
|
-
}
|
75
|
-
self.connections.append(connection)
|
76
|
-
return self
|
77
|
-
|
78
|
-
def build(self, workflow_id: str | None = None, **kwargs) -> Workflow:
|
79
|
-
"""
|
80
|
-
Build the workflow with automatic parameter injection.
|
81
|
-
|
82
|
-
Returns:
|
83
|
-
Enhanced Workflow instance
|
84
|
-
"""
|
85
|
-
# First, build the base workflow
|
86
|
-
workflow = super().build(workflow_id, **kwargs)
|
87
|
-
|
88
|
-
# Find nodes without incoming connections
|
89
|
-
nodes_with_inputs = set()
|
90
|
-
for conn in self.connections:
|
91
|
-
if not conn.get("is_workflow_input"):
|
92
|
-
nodes_with_inputs.add(conn["to_node"])
|
93
|
-
|
94
|
-
nodes_without_inputs = set(self.nodes.keys()) - nodes_with_inputs
|
95
|
-
|
96
|
-
# For each node without inputs, check if it needs workflow parameters
|
97
|
-
for node_id in nodes_without_inputs:
|
98
|
-
node = self.nodes[node_id]
|
99
|
-
node_instance = workflow.get_node(node_id)
|
100
|
-
|
101
|
-
if hasattr(node_instance, "get_parameters"):
|
102
|
-
params = node_instance.get_parameters()
|
103
|
-
|
104
|
-
# Check which required parameters are missing from config
|
105
|
-
for param_name, param_def in params.items():
|
106
|
-
if param_def.required and param_name not in node["config"]:
|
107
|
-
# Check if this parameter should come from workflow parameters
|
108
|
-
if param_name in self.workflow_parameters:
|
109
|
-
# Add to node config
|
110
|
-
node["config"][param_name] = self.workflow_parameters[
|
111
|
-
param_name
|
112
|
-
]
|
113
|
-
elif node_id in self.parameter_mappings:
|
114
|
-
# Check parameter mappings
|
115
|
-
mapping = self.parameter_mappings[node_id]
|
116
|
-
if param_name in mapping:
|
117
|
-
workflow_param = mapping[param_name]
|
118
|
-
if workflow_param in self.workflow_parameters:
|
119
|
-
node["config"][param_name] = (
|
120
|
-
self.workflow_parameters[workflow_param]
|
121
|
-
)
|
122
|
-
|
123
|
-
# Store workflow parameters in metadata for runtime reference
|
124
|
-
workflow._metadata["workflow_parameters"] = self.workflow_parameters
|
125
|
-
workflow._metadata["parameter_mappings"] = self.parameter_mappings
|
126
|
-
|
127
|
-
return workflow
|
128
|
-
|
129
|
-
|
130
|
-
def create_user_login_workflow_improved(config: Dict[str, Any]) -> Workflow:
|
131
|
-
"""
|
132
|
-
Example of creating a login workflow with proper parameter handling.
|
133
|
-
"""
|
134
|
-
workflow = ImprovedWorkflowBuilder()
|
135
|
-
|
136
|
-
# Set workflow-level parameters that will be shared
|
137
|
-
workflow.set_workflow_parameters(
|
138
|
-
tenant_id="default", database_config=config["database_config"]
|
139
|
-
)
|
140
|
-
|
141
|
-
# Add user fetcher node
|
142
|
-
workflow.add_node(
|
143
|
-
"UserManagementNode",
|
144
|
-
"user_fetcher",
|
145
|
-
{
|
146
|
-
"operation": "get_user",
|
147
|
-
"identifier": "$.email",
|
148
|
-
"identifier_type": "email",
|
149
|
-
# tenant_id and database_config will be auto-injected
|
150
|
-
},
|
151
|
-
)
|
152
|
-
|
153
|
-
# Map workflow inputs to the first node
|
154
|
-
workflow.add_input_connection("user_fetcher", "email", "email")
|
155
|
-
workflow.add_input_connection("user_fetcher", "password", "password")
|
156
|
-
|
157
|
-
# Add other nodes...
|
158
|
-
workflow.add_node(
|
159
|
-
"PythonCodeNode",
|
160
|
-
"password_verifier",
|
161
|
-
{"code": "# Password verification code here"},
|
162
|
-
)
|
163
|
-
|
164
|
-
# Connect nodes
|
165
|
-
workflow.add_connection("user_fetcher", "result", "password_verifier", "input")
|
166
|
-
|
167
|
-
return workflow.build(name="user_login_improved")
|
168
|
-
|
169
|
-
|
170
|
-
# Alternative approach: Fix in the existing WorkflowBuilder
|
171
|
-
def patch_workflow_builder():
|
172
|
-
"""
|
173
|
-
Monkey patch the existing WorkflowBuilder to handle parameters better.
|
174
|
-
"""
|
175
|
-
original_build = WorkflowBuilder.build
|
176
|
-
|
177
|
-
def enhanced_build(self, workflow_id: str | None = None, **kwargs) -> Workflow:
|
178
|
-
# Build the workflow normally
|
179
|
-
workflow = original_build(self, workflow_id, **kwargs)
|
180
|
-
|
181
|
-
# Enhanced parameter handling
|
182
|
-
# Find nodes without incoming connections and inject common parameters
|
183
|
-
nodes_with_inputs = set()
|
184
|
-
for edge in workflow._graph.edges():
|
185
|
-
nodes_with_inputs.add(edge[1]) # target node
|
186
|
-
|
187
|
-
# Get all nodes
|
188
|
-
all_nodes = set(workflow._graph.nodes())
|
189
|
-
nodes_without_inputs = all_nodes - nodes_with_inputs
|
190
|
-
|
191
|
-
# Common parameters that should be injected
|
192
|
-
common_params = {
|
193
|
-
"tenant_id": "default",
|
194
|
-
"database_config": kwargs.get("database_config", {}),
|
195
|
-
}
|
196
|
-
|
197
|
-
for node_id in nodes_without_inputs:
|
198
|
-
if node_id in workflow._nodes:
|
199
|
-
node_instance = workflow._nodes[node_id]
|
200
|
-
# Update node config with common parameters if not already set
|
201
|
-
for param, value in common_params.items():
|
202
|
-
if param not in node_instance.config:
|
203
|
-
node_instance.config[param] = value
|
204
|
-
|
205
|
-
return workflow
|
206
|
-
|
207
|
-
WorkflowBuilder.build = enhanced_build
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|