kailash 0.1.3__py3-none-any.whl → 0.1.4__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/api/__init__.py +11 -1
- kailash/api/gateway.py +394 -0
- kailash/api/mcp_integration.py +478 -0
- kailash/api/workflow_api.py +29 -13
- kailash/nodes/ai/__init__.py +4 -4
- kailash/nodes/ai/agents.py +4 -4
- kailash/nodes/ai/ai_providers.py +18 -22
- kailash/nodes/ai/embedding_generator.py +34 -38
- kailash/nodes/ai/llm_agent.py +351 -356
- kailash/nodes/base.py +60 -64
- kailash/nodes/code/python.py +61 -42
- kailash/nodes/data/__init__.py +10 -10
- kailash/nodes/data/readers.py +27 -29
- kailash/nodes/data/retrieval.py +1 -1
- kailash/nodes/data/sharepoint_graph.py +23 -25
- kailash/nodes/data/sql.py +24 -26
- kailash/nodes/data/writers.py +41 -44
- kailash/nodes/logic/__init__.py +9 -3
- kailash/nodes/logic/async_operations.py +14 -14
- kailash/nodes/logic/operations.py +18 -22
- kailash/nodes/mcp/client.py +29 -33
- kailash/nodes/transform/formatters.py +1 -1
- kailash/tracking/metrics_collector.py +6 -7
- kailash/utils/export.py +2 -2
- kailash/utils/templates.py +16 -16
- {kailash-0.1.3.dist-info → kailash-0.1.4.dist-info}/METADATA +103 -28
- {kailash-0.1.3.dist-info → kailash-0.1.4.dist-info}/RECORD +32 -30
- {kailash-0.1.3.dist-info → kailash-0.1.4.dist-info}/WHEEL +0 -0
- {kailash-0.1.3.dist-info → kailash-0.1.4.dist-info}/entry_points.txt +0 -0
- {kailash-0.1.3.dist-info → kailash-0.1.4.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.1.3.dist-info → kailash-0.1.4.dist-info}/top_level.txt +0 -0
kailash/nodes/base.py
CHANGED
@@ -214,24 +214,23 @@ class Node(ABC):
|
|
214
214
|
- During workflow creation: Used for connection validation
|
215
215
|
- During export: Included in workflow manifests
|
216
216
|
|
217
|
-
Example
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
}
|
217
|
+
Example:
|
218
|
+
>>> def get_parameters(self):
|
219
|
+
... return {
|
220
|
+
... 'input_file': NodeParameter(
|
221
|
+
... name='input_file',
|
222
|
+
... type=str,
|
223
|
+
... required=True,
|
224
|
+
... description='Path to input CSV file'
|
225
|
+
... ),
|
226
|
+
... 'delimiter': NodeParameter(
|
227
|
+
... name='delimiter',
|
228
|
+
... type=str,
|
229
|
+
... required=False,
|
230
|
+
... default=',',
|
231
|
+
... description='CSV delimiter character'
|
232
|
+
... )
|
233
|
+
... }
|
235
234
|
|
236
235
|
Returns:
|
237
236
|
Dictionary mapping parameter names to their definitions
|
@@ -265,29 +264,28 @@ class Node(ABC):
|
|
265
264
|
3. Workflow connection validation
|
266
265
|
4. Export manifest generation
|
267
266
|
|
268
|
-
Example
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
}
|
267
|
+
Example:
|
268
|
+
>>> def get_output_schema(self):
|
269
|
+
... return {
|
270
|
+
... 'dataframe': NodeParameter(
|
271
|
+
... name='dataframe',
|
272
|
+
... type=dict,
|
273
|
+
... required=True,
|
274
|
+
... description='Processed data as dictionary'
|
275
|
+
... ),
|
276
|
+
... 'row_count': NodeParameter(
|
277
|
+
... name='row_count',
|
278
|
+
... type=int,
|
279
|
+
... required=True,
|
280
|
+
... description='Number of rows processed'
|
281
|
+
... ),
|
282
|
+
... 'processing_time': NodeParameter(
|
283
|
+
... name='processing_time',
|
284
|
+
... type=float,
|
285
|
+
... required=False,
|
286
|
+
... description='Time taken to process in seconds'
|
287
|
+
... )
|
288
|
+
... }
|
291
289
|
|
292
290
|
Returns:
|
293
291
|
Dictionary mapping output names to their parameter definitions
|
@@ -325,15 +323,14 @@ class Node(ABC):
|
|
325
323
|
- Error wrapping and logging
|
326
324
|
- Execution timing and metrics
|
327
325
|
|
328
|
-
Example
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
}
|
326
|
+
Example:
|
327
|
+
>>> def run(self, input_file, delimiter=','):
|
328
|
+
... df = pd.read_csv(input_file, delimiter=delimiter)
|
329
|
+
... return {
|
330
|
+
... 'dataframe': df.to_dict(),
|
331
|
+
... 'row_count': len(df),
|
332
|
+
... 'columns': list(df.columns)
|
333
|
+
... }
|
337
334
|
|
338
335
|
Args:
|
339
336
|
**kwargs: Validated input parameters matching get_parameters()
|
@@ -1010,11 +1007,11 @@ class NodeRegistry:
|
|
1010
1007
|
- Logs the clearing action
|
1011
1008
|
- Existing node instances remain valid
|
1012
1009
|
|
1013
|
-
Warning
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1010
|
+
Warning:
|
1011
|
+
>>> # Warning: This affects all future operations
|
1012
|
+
>>> # - Subsequent get() calls will fail
|
1013
|
+
>>> # - Workflows may not deserialize
|
1014
|
+
>>> # - Should re-register needed nodes
|
1018
1015
|
"""
|
1019
1016
|
cls._nodes.clear()
|
1020
1017
|
logging.info("Cleared all registered nodes")
|
@@ -1057,15 +1054,14 @@ def register_node(alias: Optional[str] = None):
|
|
1057
1054
|
- Returns the unmodified class
|
1058
1055
|
- Handles registration errors
|
1059
1056
|
|
1060
|
-
Example
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
return pd.read_csv(file)
|
1057
|
+
Example:
|
1058
|
+
>>> @register_node(alias='CSV')
|
1059
|
+
... class CSVReaderNode(Node):
|
1060
|
+
... def get_parameters(self):
|
1061
|
+
... return {'file': NodeParameter(...)}
|
1062
|
+
...
|
1063
|
+
... def run(self, file):
|
1064
|
+
... return pd.read_csv(file)
|
1069
1065
|
"""
|
1070
1066
|
|
1071
1067
|
def decorator(node_class: Type[Node]):
|
kailash/nodes/code/python.py
CHANGED
@@ -144,6 +144,7 @@ class CodeExecutor:
|
|
144
144
|
"filter",
|
145
145
|
"float",
|
146
146
|
"int",
|
147
|
+
"isinstance", # Common type checking
|
147
148
|
"len",
|
148
149
|
"list",
|
149
150
|
"map",
|
@@ -546,48 +547,47 @@ class PythonCodeNode(Node):
|
|
546
547
|
- State management for class-based nodes
|
547
548
|
- AST-based security validation
|
548
549
|
|
549
|
-
Example
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
)
|
550
|
+
Example:
|
551
|
+
>>> # Function-based node
|
552
|
+
>>> def custom_filter(data: pd.DataFrame, threshold: float) -> pd.DataFrame:
|
553
|
+
... return data[data['value'] > threshold]
|
554
|
+
|
555
|
+
>>> node = PythonCodeNode.from_function(
|
556
|
+
... func=custom_filter,
|
557
|
+
... name="threshold_filter"
|
558
|
+
... )
|
559
|
+
|
560
|
+
>>> # Class-based stateful node
|
561
|
+
>>> class MovingAverage:
|
562
|
+
... def __init__(self, window_size: int = 3):
|
563
|
+
... self.window_size = window_size
|
564
|
+
... self.values = []
|
565
|
+
...
|
566
|
+
... def process(self, value: float) -> float:
|
567
|
+
... self.values.append(value)
|
568
|
+
... if len(self.values) > self.window_size:
|
569
|
+
... self.values.pop(0)
|
570
|
+
... return sum(self.values) / len(self.values)
|
571
|
+
|
572
|
+
>>> node = PythonCodeNode.from_class(
|
573
|
+
... cls=MovingAverage,
|
574
|
+
... name="moving_avg"
|
575
|
+
... )
|
576
|
+
|
577
|
+
>>> # Code string node
|
578
|
+
>>> code = '''
|
579
|
+
... result = []
|
580
|
+
... for item in data:
|
581
|
+
... if item > threshold:
|
582
|
+
... result.append(item * 2)
|
583
|
+
... '''
|
584
|
+
|
585
|
+
>>> node = PythonCodeNode(
|
586
|
+
... name="custom_processor",
|
587
|
+
... code=code,
|
588
|
+
... input_types={'data': list, 'threshold': float},
|
589
|
+
... output_type=list
|
590
|
+
... )
|
591
591
|
"""
|
592
592
|
|
593
593
|
def __init__(
|
@@ -727,6 +727,25 @@ class PythonCodeNode(Node):
|
|
727
727
|
|
728
728
|
return parameters
|
729
729
|
|
730
|
+
def validate_inputs(self, **kwargs) -> Dict[str, Any]:
|
731
|
+
"""Validate runtime inputs.
|
732
|
+
|
733
|
+
For code-based nodes, we accept any inputs since the code
|
734
|
+
can use whatever variables it needs.
|
735
|
+
|
736
|
+
Args:
|
737
|
+
**kwargs: Runtime inputs
|
738
|
+
|
739
|
+
Returns:
|
740
|
+
All inputs as-is for code nodes, validated inputs for function/class nodes
|
741
|
+
"""
|
742
|
+
# If using code string, pass through all inputs
|
743
|
+
if self.code:
|
744
|
+
return kwargs
|
745
|
+
|
746
|
+
# Otherwise use standard validation for function/class nodes
|
747
|
+
return super().validate_inputs(**kwargs)
|
748
|
+
|
730
749
|
def get_output_schema(self) -> Dict[str, "NodeParameter"]:
|
731
750
|
"""Define output parameters for this node.
|
732
751
|
|
kailash/nodes/data/__init__.py
CHANGED
@@ -57,9 +57,9 @@ All nodes provide detailed error messages for:
|
|
57
57
|
Example Workflows:
|
58
58
|
# Traditional ETL
|
59
59
|
workflow = Workflow()
|
60
|
-
workflow.add_node('read',
|
60
|
+
workflow.add_node('read', CSVReaderNode(file_path='input.csv'))
|
61
61
|
workflow.add_node('transform', DataTransform())
|
62
|
-
workflow.add_node('write',
|
62
|
+
workflow.add_node('write', JSONWriterNode(file_path='output.json'))
|
63
63
|
workflow.connect('read', 'transform')
|
64
64
|
workflow.connect('transform', 'write')
|
65
65
|
|
@@ -80,7 +80,7 @@ Example Workflows:
|
|
80
80
|
workflow.connect('process', 'publish')
|
81
81
|
"""
|
82
82
|
|
83
|
-
from kailash.nodes.data.readers import
|
83
|
+
from kailash.nodes.data.readers import CSVReaderNode, JSONReaderNode, TextReaderNode
|
84
84
|
from kailash.nodes.data.retrieval import RelevanceScorerNode
|
85
85
|
from kailash.nodes.data.sharepoint_graph import (
|
86
86
|
SharePointGraphReader,
|
@@ -99,18 +99,18 @@ from kailash.nodes.data.vector_db import (
|
|
99
99
|
TextSplitterNode,
|
100
100
|
VectorDatabaseNode,
|
101
101
|
)
|
102
|
-
from kailash.nodes.data.writers import
|
102
|
+
from kailash.nodes.data.writers import CSVWriterNode, JSONWriterNode, TextWriterNode
|
103
103
|
|
104
104
|
__all__ = [
|
105
105
|
# Readers
|
106
|
-
"
|
107
|
-
"
|
108
|
-
"
|
106
|
+
"CSVReaderNode",
|
107
|
+
"JSONReaderNode",
|
108
|
+
"TextReaderNode",
|
109
109
|
"SharePointGraphReader",
|
110
110
|
# Writers
|
111
|
-
"
|
112
|
-
"
|
113
|
-
"
|
111
|
+
"CSVWriterNode",
|
112
|
+
"JSONWriterNode",
|
113
|
+
"TextWriterNode",
|
114
114
|
"SharePointGraphWriter",
|
115
115
|
# Sources
|
116
116
|
"DocumentSourceNode",
|
kailash/nodes/data/readers.py
CHANGED
@@ -12,9 +12,9 @@ Design Philosophy:
|
|
12
12
|
5. Type-safe parameter validation
|
13
13
|
|
14
14
|
Node Categories:
|
15
|
-
-
|
16
|
-
-
|
17
|
-
-
|
15
|
+
- CSVReaderNode: Tabular data from CSV files
|
16
|
+
- JSONReaderNode: Structured data from JSON files
|
17
|
+
- TextReaderNode: Raw text from any text file
|
18
18
|
|
19
19
|
Upstream Components:
|
20
20
|
- FileSystem: Provides files to read
|
@@ -36,7 +36,7 @@ from kailash.nodes.base import Node, NodeParameter, register_node
|
|
36
36
|
|
37
37
|
|
38
38
|
@register_node()
|
39
|
-
class
|
39
|
+
class CSVReaderNode(Node):
|
40
40
|
"""Reads data from a CSV file.
|
41
41
|
|
42
42
|
This node provides robust CSV file reading capabilities with support for
|
@@ -78,19 +78,18 @@ class CSVReader(Node):
|
|
78
78
|
- UnicodeDecodeError: Encoding mismatch
|
79
79
|
- csv.Error: Malformed CSV data
|
80
80
|
|
81
|
-
Example
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
)
|
89
|
-
result =
|
90
|
-
#
|
91
|
-
# {'id': '
|
92
|
-
#
|
93
|
-
# ]
|
81
|
+
Example:
|
82
|
+
>>> # Read customer data with headers
|
83
|
+
>>> reader = CSVReaderNode(
|
84
|
+
... file_path='customers.csv',
|
85
|
+
... headers=True,
|
86
|
+
... delimiter=','
|
87
|
+
... )
|
88
|
+
>>> result = reader.execute()
|
89
|
+
>>> # result['data'] = [
|
90
|
+
>>> # {'id': '1', 'name': 'John', 'age': '30'},
|
91
|
+
>>> # {'id': '2', 'name': 'Jane', 'age': '25'}
|
92
|
+
>>> # ]
|
94
93
|
"""
|
95
94
|
|
96
95
|
def get_parameters(self) -> Dict[str, NodeParameter]:
|
@@ -235,7 +234,7 @@ class CSVReader(Node):
|
|
235
234
|
|
236
235
|
|
237
236
|
@register_node()
|
238
|
-
class
|
237
|
+
class JSONReaderNode(Node):
|
239
238
|
"""Reads data from a JSON file.
|
240
239
|
|
241
240
|
This node handles JSON file reading with support for complex nested
|
@@ -280,7 +279,7 @@ class JSONReader(Node):
|
|
280
279
|
|
281
280
|
Example:
|
282
281
|
# Read API response data
|
283
|
-
reader =
|
282
|
+
reader = JSONReaderNode(file_path='api_response.json')
|
284
283
|
result = reader.execute()
|
285
284
|
# result['data'] = {
|
286
285
|
# 'status': 'success',
|
@@ -359,7 +358,7 @@ class JSONReader(Node):
|
|
359
358
|
|
360
359
|
|
361
360
|
@register_node()
|
362
|
-
class
|
361
|
+
class TextReaderNode(Node):
|
363
362
|
"""Reads text from a file.
|
364
363
|
|
365
364
|
This node provides simple text file reading with encoding support.
|
@@ -403,15 +402,14 @@ class TextReader(Node):
|
|
403
402
|
- UnicodeDecodeError: Wrong encoding
|
404
403
|
- MemoryError: File too large
|
405
404
|
|
406
|
-
Example
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
)
|
413
|
-
result =
|
414
|
-
# result['text'] = "2024-01-01 INFO: Application started\\n..."
|
405
|
+
Example:
|
406
|
+
>>> # Read a log file
|
407
|
+
>>> reader = TextReaderNode(
|
408
|
+
... file_path='application.log',
|
409
|
+
... encoding='utf-8'
|
410
|
+
... )
|
411
|
+
>>> result = reader.execute()
|
412
|
+
>>> # result['text'] = "2024-01-01 INFO: Application started\\n..."
|
415
413
|
"""
|
416
414
|
|
417
415
|
def get_parameters(self) -> Dict[str, NodeParameter]:
|
kailash/nodes/data/retrieval.py
CHANGED
@@ -102,7 +102,7 @@ class RelevanceScorerNode(Node):
|
|
102
102
|
) -> List[Dict]:
|
103
103
|
"""Score chunks using cosine similarity."""
|
104
104
|
# Extract actual embedding vectors from the embedding objects
|
105
|
-
#
|
105
|
+
# EmbeddingGeneratorNode returns embeddings in format: {"embedding": [...], "text": "...", "dimensions": X}
|
106
106
|
|
107
107
|
# Handle query embedding - should be the first (and only) embedding in the list
|
108
108
|
query_embedding_obj = query_embeddings[0] if query_embeddings else {}
|
@@ -56,18 +56,17 @@ class SharePointGraphReader(Node):
|
|
56
56
|
3. Search for files by name
|
57
57
|
4. Navigate folder structures
|
58
58
|
|
59
|
-
Example
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
)
|
59
|
+
Example:
|
60
|
+
>>> reader = SharePointGraphReader()
|
61
|
+
>>> result = reader.execute(
|
62
|
+
... tenant_id="your-tenant-id",
|
63
|
+
... client_id="your-client-id",
|
64
|
+
... client_secret="your-secret",
|
65
|
+
... site_url="https://company.sharepoint.com/sites/project",
|
66
|
+
... operation="list_files",
|
67
|
+
... library_name="Documents",
|
68
|
+
... folder_path="Reports/2024"
|
69
|
+
... )
|
71
70
|
"""
|
72
71
|
|
73
72
|
def get_metadata(self) -> NodeMetadata:
|
@@ -471,19 +470,18 @@ class SharePointGraphWriter(Node):
|
|
471
470
|
This node handles file uploads to SharePoint document libraries,
|
472
471
|
supporting folder structures and metadata.
|
473
472
|
|
474
|
-
Example
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
)
|
473
|
+
Example:
|
474
|
+
>>> writer = SharePointGraphWriter()
|
475
|
+
>>> result = writer.execute(
|
476
|
+
... tenant_id="your-tenant-id",
|
477
|
+
... client_id="your-client-id",
|
478
|
+
... client_secret="your-secret",
|
479
|
+
... site_url="https://company.sharepoint.com/sites/project",
|
480
|
+
... local_path="report.pdf",
|
481
|
+
... library_name="Documents",
|
482
|
+
... folder_path="Reports/2024",
|
483
|
+
... sharepoint_name="Q4_Report_2024.pdf"
|
484
|
+
... )
|
487
485
|
"""
|
488
486
|
|
489
487
|
def get_metadata(self) -> NodeMetadata:
|
kailash/nodes/data/sql.py
CHANGED
@@ -63,20 +63,19 @@ class SQLDatabaseNode(Node):
|
|
63
63
|
- TimeoutError: Query execution timeout
|
64
64
|
- PermissionError: Access denied
|
65
65
|
|
66
|
-
Example
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
)
|
75
|
-
result =
|
76
|
-
#
|
77
|
-
# {'id':
|
78
|
-
#
|
79
|
-
# ]
|
66
|
+
Example:
|
67
|
+
>>> # Query customer data
|
68
|
+
>>> sql_node = SQLDatabaseNode(
|
69
|
+
... connection_string='postgresql://user:pass@host/db',
|
70
|
+
... query='SELECT * FROM customers WHERE active = ?',
|
71
|
+
... parameters=[True],
|
72
|
+
... result_format='dict'
|
73
|
+
... )
|
74
|
+
>>> result = sql_node.execute()
|
75
|
+
>>> # result['data'] = [
|
76
|
+
>>> # {'id': 1, 'name': 'John', 'active': True},
|
77
|
+
>>> # {'id': 2, 'name': 'Jane', 'active': True}
|
78
|
+
>>> # ]
|
80
79
|
"""
|
81
80
|
|
82
81
|
def get_parameters(self) -> Dict[str, NodeParameter]:
|
@@ -259,18 +258,17 @@ class SQLQueryBuilderNode(Node):
|
|
259
258
|
3. Multi-table joins
|
260
259
|
4. Aggregation queries
|
261
260
|
|
262
|
-
Example
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
)
|
271
|
-
result =
|
272
|
-
# result['
|
273
|
-
# result['parameters'] = [True, 'USA']
|
261
|
+
Example:
|
262
|
+
>>> builder = SQLQueryBuilderNode(
|
263
|
+
... table='customers',
|
264
|
+
... select=['name', 'email'],
|
265
|
+
... where={'active': True, 'country': 'USA'},
|
266
|
+
... order_by=['name'],
|
267
|
+
... limit=100
|
268
|
+
... )
|
269
|
+
>>> result = builder.execute()
|
270
|
+
>>> # result['query'] = 'SELECT name, email FROM customers WHERE active = ? AND country = ? ORDER BY name LIMIT 100'
|
271
|
+
>>> # result['parameters'] = [True, 'USA']
|
274
272
|
"""
|
275
273
|
|
276
274
|
def get_parameters(self) -> Dict[str, NodeParameter]:
|