kailash 0.1.4__py3-none-any.whl → 0.1.5__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/nodes/ai/__init__.py +36 -0
- kailash/nodes/ai/a2a.py +1143 -0
- kailash/nodes/ai/agents.py +116 -2
- kailash/nodes/ai/ai_providers.py +206 -8
- kailash/nodes/ai/intelligent_agent_orchestrator.py +2114 -0
- kailash/nodes/ai/self_organizing.py +1624 -0
- kailash/nodes/api/http.py +106 -25
- kailash/nodes/api/rest.py +116 -21
- kailash/nodes/data/readers.py +100 -47
- kailash/nodes/logic/async_operations.py +48 -9
- kailash/nodes/logic/operations.py +25 -0
- kailash/nodes/logic/workflow.py +26 -18
- kailash/nodes/transform/__init__.py +8 -1
- kailash/nodes/transform/processors.py +119 -4
- {kailash-0.1.4.dist-info → kailash-0.1.5.dist-info}/METADATA +191 -2
- {kailash-0.1.4.dist-info → kailash-0.1.5.dist-info}/RECORD +20 -17
- {kailash-0.1.4.dist-info → kailash-0.1.5.dist-info}/WHEEL +0 -0
- {kailash-0.1.4.dist-info → kailash-0.1.5.dist-info}/entry_points.txt +0 -0
- {kailash-0.1.4.dist-info → kailash-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.1.4.dist-info → kailash-0.1.5.dist-info}/top_level.txt +0 -0
kailash/nodes/api/http.py
CHANGED
@@ -57,33 +57,114 @@ class HTTPResponse(BaseModel):
|
|
57
57
|
|
58
58
|
@register_node()
|
59
59
|
class HTTPRequestNode(Node):
|
60
|
-
"""
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
* Specialized API nodes: May extend this node for specific APIs
|
60
|
+
"""
|
61
|
+
Enhanced node for making HTTP requests to external APIs.
|
62
|
+
|
63
|
+
This node provides a comprehensive HTTP client with enterprise-grade features for
|
64
|
+
integrating external APIs into Kailash workflows. It supports all common HTTP
|
65
|
+
operations with built-in authentication, error handling, and response parsing,
|
66
|
+
making it the foundation for API integration in the SDK.
|
67
|
+
|
68
|
+
Design Philosophy:
|
69
|
+
The HTTPRequestNode embodies the principle of "API integration made simple."
|
70
|
+
It abstracts the complexity of HTTP operations behind a clean interface while
|
71
|
+
providing advanced features when needed. The design prioritizes flexibility,
|
72
|
+
reliability, and ease of use, supporting everything from simple REST calls
|
73
|
+
to complex authentication flows and multipart uploads.
|
74
|
+
|
75
|
+
Upstream Dependencies:
|
76
|
+
- Workflow orchestrators configuring API endpoints
|
77
|
+
- Authentication nodes providing credentials
|
78
|
+
- Configuration systems supplying API settings
|
79
|
+
- Data transformation nodes preparing request payloads
|
80
|
+
- Rate limiting controllers managing API quotas
|
82
81
|
|
83
82
|
Downstream Consumers:
|
84
|
-
|
85
|
-
|
86
|
-
|
83
|
+
- Data processing nodes consuming API responses
|
84
|
+
- Decision nodes routing based on HTTP status
|
85
|
+
- Error handling nodes managing failures
|
86
|
+
- Caching nodes storing API results
|
87
|
+
- Analytics nodes tracking API usage
|
88
|
+
|
89
|
+
Configuration:
|
90
|
+
The node supports extensive configuration options:
|
91
|
+
- URL with template variable support
|
92
|
+
- All standard HTTP methods
|
93
|
+
- Custom headers and query parameters
|
94
|
+
- Multiple body formats (JSON, form, multipart)
|
95
|
+
- Authentication methods (Bearer, Basic, API Key, OAuth2)
|
96
|
+
- Timeout and retry settings
|
97
|
+
- Response format preferences
|
98
|
+
|
99
|
+
Implementation Details:
|
100
|
+
- Uses requests library for synchronous operations
|
101
|
+
- Automatic response format detection based on Content-Type
|
102
|
+
- Built-in JSON parsing with error handling
|
103
|
+
- Support for binary responses (files, images)
|
104
|
+
- Connection pooling for performance
|
105
|
+
- Comprehensive error messages with recovery hints
|
106
|
+
- Optional request/response logging
|
107
|
+
- Metrics collection for monitoring
|
108
|
+
|
109
|
+
Error Handling:
|
110
|
+
- Connection errors with retry suggestions
|
111
|
+
- Timeout handling with configurable limits
|
112
|
+
- HTTP error status codes with detailed messages
|
113
|
+
- JSON parsing errors with fallback to text
|
114
|
+
- Authentication failures with setup guidance
|
115
|
+
- Rate limit detection and backoff
|
116
|
+
|
117
|
+
Side Effects:
|
118
|
+
- Makes external HTTP requests
|
119
|
+
- May consume API rate limits
|
120
|
+
- Logs requests/responses when enabled
|
121
|
+
- Updates internal metrics
|
122
|
+
- May modify external resources (POST/PUT/DELETE)
|
123
|
+
|
124
|
+
Examples:
|
125
|
+
>>> # Simple GET request
|
126
|
+
>>> node = HTTPRequestNode()
|
127
|
+
>>> result = node.run(
|
128
|
+
... url="https://api.example.com/users",
|
129
|
+
... method="GET",
|
130
|
+
... headers={"Accept": "application/json"}
|
131
|
+
... )
|
132
|
+
>>> assert result["status_code"] == 200
|
133
|
+
>>> assert isinstance(result["content"], dict)
|
134
|
+
>>>
|
135
|
+
>>> # POST request with JSON body
|
136
|
+
>>> result = node.run(
|
137
|
+
... url="https://api.example.com/users",
|
138
|
+
... method="POST",
|
139
|
+
... json_data={"name": "John", "email": "john@example.com"},
|
140
|
+
... headers={"Authorization": "Bearer token123"}
|
141
|
+
... )
|
142
|
+
>>> assert result["status_code"] in [200, 201]
|
143
|
+
>>> assert result["headers"]["content-type"].startswith("application/json")
|
144
|
+
>>>
|
145
|
+
>>> # Form data submission
|
146
|
+
>>> result = node.run(
|
147
|
+
... url="https://api.example.com/form",
|
148
|
+
... method="POST",
|
149
|
+
... data={"field1": "value1", "field2": "value2"},
|
150
|
+
... headers={"Content-Type": "application/x-www-form-urlencoded"}
|
151
|
+
... )
|
152
|
+
>>>
|
153
|
+
>>> # File upload with multipart
|
154
|
+
>>> result = node.run(
|
155
|
+
... url="https://api.example.com/upload",
|
156
|
+
... method="POST",
|
157
|
+
... files={"file": ("data.csv", b"col1,col2\\n1,2", "text/csv")},
|
158
|
+
... data={"description": "Sample data"}
|
159
|
+
... )
|
160
|
+
>>>
|
161
|
+
>>> # Error handling example
|
162
|
+
>>> result = node.run(
|
163
|
+
... url="https://api.example.com/protected",
|
164
|
+
... method="GET"
|
165
|
+
... )
|
166
|
+
>>> if result["status_code"] == 401:
|
167
|
+
... print("Authentication required")
|
87
168
|
"""
|
88
169
|
|
89
170
|
def __init__(self, **kwargs):
|
kailash/nodes/api/rest.py
CHANGED
@@ -20,29 +20,124 @@ from kailash.sdk_exceptions import NodeExecutionError, NodeValidationError
|
|
20
20
|
|
21
21
|
@register_node()
|
22
22
|
class RESTClientNode(Node):
|
23
|
-
"""
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
23
|
+
"""
|
24
|
+
Node for interacting with REST APIs using resource-oriented patterns.
|
25
|
+
|
26
|
+
This node provides a higher-level abstraction over HTTP operations, specifically
|
27
|
+
designed for REST APIs. It understands REST conventions and provides convenient
|
28
|
+
methods for resource-based operations, making it easier to integrate RESTful
|
29
|
+
services into Kailash workflows.
|
30
|
+
|
31
|
+
Design Philosophy:
|
32
|
+
The RESTClientNode embraces REST principles and conventions, providing an
|
33
|
+
intuitive interface for resource manipulation. It abstracts common patterns
|
34
|
+
like path parameter substitution, pagination, and error handling while
|
35
|
+
maintaining flexibility for API-specific requirements. The design promotes
|
36
|
+
clean, maintainable API integration code.
|
37
|
+
|
38
|
+
Upstream Dependencies:
|
39
|
+
- Workflow orchestrators defining API endpoints
|
40
|
+
- Configuration nodes providing API credentials
|
41
|
+
- Data transformation nodes preparing resources
|
42
|
+
- Authentication nodes managing tokens
|
43
|
+
- Schema validation nodes defining expected formats
|
42
44
|
|
43
45
|
Downstream Consumers:
|
44
|
-
|
45
|
-
|
46
|
+
- Data processing nodes working with API responses
|
47
|
+
- Pagination handlers managing result sets
|
48
|
+
- Error recovery nodes handling failures
|
49
|
+
- Caching nodes storing resource data
|
50
|
+
- Analytics nodes tracking API usage patterns
|
51
|
+
|
52
|
+
Configuration:
|
53
|
+
The node supports REST-specific configuration:
|
54
|
+
- Base URL for API endpoints
|
55
|
+
- Resource paths with parameter placeholders
|
56
|
+
- Default headers and authentication
|
57
|
+
- API versioning strategies
|
58
|
+
- Pagination parameters
|
59
|
+
- Response format expectations
|
60
|
+
|
61
|
+
Implementation Details:
|
62
|
+
- Built on HTTPRequestNode for core functionality
|
63
|
+
- Automatic URL construction from base + resource
|
64
|
+
- Path parameter substitution (e.g., /users/{id})
|
65
|
+
- Query parameter handling with encoding
|
66
|
+
- Standard REST method mapping
|
67
|
+
- Response format negotiation
|
68
|
+
- Error response parsing for API-specific errors
|
69
|
+
- Link header parsing for pagination
|
70
|
+
|
71
|
+
Error Handling:
|
72
|
+
- 404 errors for missing resources
|
73
|
+
- 422 validation errors with field details
|
74
|
+
- 401/403 authentication/authorization errors
|
75
|
+
- Rate limiting (429) with retry headers
|
76
|
+
- 5xx server errors with backoff
|
77
|
+
- Network failures with retry logic
|
78
|
+
- Malformed response handling
|
79
|
+
|
80
|
+
Side Effects:
|
81
|
+
- Performs HTTP requests to external APIs
|
82
|
+
- May modify remote resources (POST/PUT/DELETE)
|
83
|
+
- Consumes API rate limits
|
84
|
+
- May trigger webhooks or notifications
|
85
|
+
- Updates internal request metrics
|
86
|
+
|
87
|
+
Examples:
|
88
|
+
>>> # Initialize REST client
|
89
|
+
>>> client = RESTClientNode()
|
90
|
+
>>>
|
91
|
+
>>> # Get a single resource
|
92
|
+
>>> result = client.run(
|
93
|
+
... base_url="https://api.example.com/v1",
|
94
|
+
... resource="users/{id}",
|
95
|
+
... method="GET",
|
96
|
+
... path_params={"id": 123},
|
97
|
+
... headers={"Authorization": "Bearer token"}
|
98
|
+
... )
|
99
|
+
>>> assert result["status_code"] == 200
|
100
|
+
>>> user = result["content"]
|
101
|
+
>>> assert user["id"] == 123
|
102
|
+
>>>
|
103
|
+
>>> # List resources with pagination
|
104
|
+
>>> result = client.run(
|
105
|
+
... base_url="https://api.example.com/v1",
|
106
|
+
... resource="products",
|
107
|
+
... method="GET",
|
108
|
+
... query_params={"page": 1, "per_page": 20, "category": "electronics"}
|
109
|
+
... )
|
110
|
+
>>> assert len(result["content"]) <= 20
|
111
|
+
>>>
|
112
|
+
>>> # Create a new resource
|
113
|
+
>>> result = client.run(
|
114
|
+
... base_url="https://api.example.com/v1",
|
115
|
+
... resource="posts",
|
116
|
+
... method="POST",
|
117
|
+
... data={"title": "New Post", "content": "Post content"},
|
118
|
+
... headers={"Content-Type": "application/json"}
|
119
|
+
... )
|
120
|
+
>>> assert result["status_code"] == 201
|
121
|
+
>>> assert "id" in result["content"]
|
122
|
+
>>>
|
123
|
+
>>> # Update a resource
|
124
|
+
>>> result = client.run(
|
125
|
+
... base_url="https://api.example.com/v1",
|
126
|
+
... resource="users/{id}",
|
127
|
+
... method="PATCH",
|
128
|
+
... path_params={"id": 123},
|
129
|
+
... data={"email": "newemail@example.com"}
|
130
|
+
... )
|
131
|
+
>>> assert result["status_code"] == 200
|
132
|
+
>>>
|
133
|
+
>>> # Delete a resource
|
134
|
+
>>> result = client.run(
|
135
|
+
... base_url="https://api.example.com/v1",
|
136
|
+
... resource="comments/{id}",
|
137
|
+
... method="DELETE",
|
138
|
+
... path_params={"id": 456}
|
139
|
+
... )
|
140
|
+
>>> assert result["status_code"] in [200, 204]
|
46
141
|
"""
|
47
142
|
|
48
143
|
def __init__(self, **kwargs):
|
kailash/nodes/data/readers.py
CHANGED
@@ -37,59 +37,112 @@ from kailash.nodes.base import Node, NodeParameter, register_node
|
|
37
37
|
|
38
38
|
@register_node()
|
39
39
|
class CSVReaderNode(Node):
|
40
|
-
"""
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
3. Loading configuration from CSV
|
62
|
-
4. Ingesting sensor data logs
|
63
|
-
|
64
|
-
Upstream Sources:
|
65
|
-
- File system paths from user input
|
66
|
-
- Output paths from previous nodes
|
67
|
-
- Configuration management systems
|
40
|
+
"""
|
41
|
+
Reads data from CSV files with automatic header detection and type inference.
|
42
|
+
|
43
|
+
This node provides comprehensive CSV file reading capabilities, handling various
|
44
|
+
formats, encodings, and edge cases. It automatically detects headers, infers data
|
45
|
+
types, and provides consistent structured output for downstream processing in
|
46
|
+
Kailash workflows.
|
47
|
+
|
48
|
+
Design Philosophy:
|
49
|
+
The CSVReaderNode embodies the principle of "data accessibility without
|
50
|
+
complexity." It abstracts the intricacies of CSV parsing while providing
|
51
|
+
flexibility for various formats. The design prioritizes memory efficiency,
|
52
|
+
automatic format detection, and consistent output structure, making it easy
|
53
|
+
to integrate diverse CSV data sources into workflows.
|
54
|
+
|
55
|
+
Upstream Dependencies:
|
56
|
+
- File system providing CSV files
|
57
|
+
- Workflow orchestrators specifying file paths
|
58
|
+
- Configuration systems providing parsing options
|
59
|
+
- Previous nodes generating CSV file paths
|
60
|
+
- User inputs defining data sources
|
68
61
|
|
69
62
|
Downstream Consumers:
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
63
|
+
- DataTransformNode: Processes tabular data
|
64
|
+
- FilterNode: Applies row/column filtering
|
65
|
+
- AggregatorNode: Summarizes data
|
66
|
+
- PythonCodeNode: Custom data processing
|
67
|
+
- WriterNodes: Exports to other formats
|
68
|
+
- Visualization nodes: Creates charts
|
69
|
+
- ML nodes: Uses as training data
|
70
|
+
|
71
|
+
Configuration:
|
72
|
+
The node supports extensive CSV parsing options:
|
73
|
+
- Delimiter detection (comma, tab, pipe, etc.)
|
74
|
+
- Header row identification
|
75
|
+
- Encoding specification (UTF-8, Latin-1, etc.)
|
76
|
+
- Quote character handling
|
77
|
+
- Skip rows/comments functionality
|
78
|
+
- Column type inference
|
79
|
+
- Missing value handling
|
80
|
+
|
81
|
+
Implementation Details:
|
82
|
+
- Uses Python's csv module for robust parsing
|
83
|
+
- Implements streaming for large files
|
84
|
+
- Automatic delimiter detection when not specified
|
85
|
+
- Header detection based on first row analysis
|
86
|
+
- Type inference for numeric/date columns
|
87
|
+
- Memory-efficient processing with generators
|
88
|
+
- Unicode normalization for consistent encoding
|
74
89
|
|
75
90
|
Error Handling:
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
91
|
+
- FileNotFoundError: Clear message with path
|
92
|
+
- PermissionError: Access rights guidance
|
93
|
+
- UnicodeDecodeError: Encoding detection hints
|
94
|
+
- csv.Error: Malformed data diagnostics
|
95
|
+
- EmptyFileError: Handles zero-byte files
|
96
|
+
- Partial read recovery for corrupted files
|
97
|
+
|
98
|
+
Side Effects:
|
99
|
+
- Reads from file system
|
100
|
+
- May consume significant memory for large files
|
101
|
+
- Creates file handles (properly closed)
|
102
|
+
- Updates internal read statistics
|
103
|
+
|
104
|
+
Examples:
|
105
|
+
>>> # Basic CSV reading with headers
|
106
|
+
>>> reader = CSVReaderNode()
|
107
|
+
>>> result = reader.run(
|
108
|
+
... file_path="customers.csv",
|
109
|
+
... headers=True
|
87
110
|
... )
|
88
|
-
>>> result
|
89
|
-
>>>
|
90
|
-
>>> #
|
91
|
-
>>> #
|
111
|
+
>>> assert isinstance(result["data"], list)
|
112
|
+
>>> assert all(isinstance(row, dict) for row in result["data"])
|
113
|
+
>>> # Example output:
|
114
|
+
>>> # result["data"] = [
|
115
|
+
>>> # {"id": "1", "name": "John Doe", "age": "30"},
|
116
|
+
>>> # {"id": "2", "name": "Jane Smith", "age": "25"}
|
92
117
|
>>> # ]
|
118
|
+
>>>
|
119
|
+
>>> # Reading with custom delimiter
|
120
|
+
>>> result = reader.run(
|
121
|
+
... file_path="data.tsv",
|
122
|
+
... delimiter="\\t",
|
123
|
+
... headers=True
|
124
|
+
... )
|
125
|
+
>>>
|
126
|
+
>>> # Reading without headers (returns list of lists)
|
127
|
+
>>> result = reader.run(
|
128
|
+
... file_path="data.csv",
|
129
|
+
... headers=False
|
130
|
+
... )
|
131
|
+
>>> assert all(isinstance(row, list) for row in result["data"])
|
132
|
+
>>>
|
133
|
+
>>> # Reading with specific encoding
|
134
|
+
>>> result = reader.run(
|
135
|
+
... file_path="european_data.csv",
|
136
|
+
... encoding="iso-8859-1",
|
137
|
+
... headers=True
|
138
|
+
... )
|
139
|
+
>>>
|
140
|
+
>>> # Handling quoted fields
|
141
|
+
>>> result = reader.run(
|
142
|
+
... file_path="complex.csv",
|
143
|
+
... headers=True,
|
144
|
+
... quotechar='"'
|
145
|
+
... )
|
93
146
|
"""
|
94
147
|
|
95
148
|
def get_parameters(self) -> Dict[str, NodeParameter]:
|
@@ -32,15 +32,25 @@ class AsyncMergeNode(AsyncNode):
|
|
32
32
|
concat (list concatenation), zip (parallel iteration), and merge_dict
|
33
33
|
(dictionary merging with optional key-based joining).
|
34
34
|
|
35
|
-
|
36
|
-
# Create an AsyncMergeNode
|
37
|
-
async_merge = AsyncMergeNode(merge_type="merge_dict", key="id")
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
workflow.
|
43
|
-
workflow
|
35
|
+
Example usage:
|
36
|
+
>>> # Create an AsyncMergeNode
|
37
|
+
>>> async_merge = AsyncMergeNode(merge_type="merge_dict", key="id")
|
38
|
+
>>> async_merge.metadata.name
|
39
|
+
'AsyncMergeNode'
|
40
|
+
|
41
|
+
>>> # Using in a workflow
|
42
|
+
>>> from kailash.workflow.graph import Workflow
|
43
|
+
>>> workflow = Workflow("wf-001", "async_example")
|
44
|
+
>>> workflow.add_node("data_combine", async_merge)
|
45
|
+
>>> "data_combine" in workflow.nodes
|
46
|
+
True
|
47
|
+
|
48
|
+
>>> # Async execution with concat
|
49
|
+
>>> import asyncio
|
50
|
+
>>> async_merge = AsyncMergeNode(merge_type="concat")
|
51
|
+
>>> result = asyncio.run(async_merge.execute_async(data1=[1, 2], data2=[3, 4]))
|
52
|
+
>>> result['merged_data']
|
53
|
+
[1, 2, 3, 4]
|
44
54
|
"""
|
45
55
|
|
46
56
|
def get_parameters(self) -> Dict[str, NodeParameter]:
|
@@ -364,6 +374,35 @@ class AsyncSwitchNode(AsyncNode):
|
|
364
374
|
|
365
375
|
The basic functionality is the same as the synchronous SwitchNode but optimized
|
366
376
|
for asynchronous execution.
|
377
|
+
|
378
|
+
Example usage:
|
379
|
+
>>> # Boolean condition routing
|
380
|
+
>>> import asyncio
|
381
|
+
>>> async_switch = AsyncSwitchNode(
|
382
|
+
... condition_field="status",
|
383
|
+
... operator="==",
|
384
|
+
... value="active"
|
385
|
+
... )
|
386
|
+
>>> result = asyncio.run(async_switch.execute_async(
|
387
|
+
... input_data={"status": "active", "data": "test"}
|
388
|
+
... ))
|
389
|
+
>>> result['true_output']
|
390
|
+
{'status': 'active', 'data': 'test'}
|
391
|
+
>>> result['false_output'] is None
|
392
|
+
True
|
393
|
+
|
394
|
+
>>> # Multi-case switching
|
395
|
+
>>> async_switch = AsyncSwitchNode(
|
396
|
+
... condition_field="priority",
|
397
|
+
... cases=["high", "medium", "low"]
|
398
|
+
... )
|
399
|
+
>>> result = asyncio.run(async_switch.execute_async(
|
400
|
+
... input_data={"priority": "high", "task": "urgent"}
|
401
|
+
... ))
|
402
|
+
>>> result['case_high']
|
403
|
+
{'priority': 'high', 'task': 'urgent'}
|
404
|
+
>>> result['default']
|
405
|
+
{'priority': 'high', 'task': 'urgent'}
|
367
406
|
"""
|
368
407
|
|
369
408
|
def get_parameters(self) -> Dict[str, NodeParameter]:
|
@@ -370,6 +370,31 @@ class MergeNode(Node):
|
|
370
370
|
The merge operation is determined by the merge_type parameter, which supports
|
371
371
|
concat (list concatenation), zip (parallel iteration), and merge_dict (dictionary
|
372
372
|
merging with optional key-based joining for lists of dictionaries).
|
373
|
+
|
374
|
+
Example usage:
|
375
|
+
>>> # Simple list concatenation
|
376
|
+
>>> merge_node = MergeNode(merge_type="concat")
|
377
|
+
>>> result = merge_node.execute(data1=[1, 2], data2=[3, 4])
|
378
|
+
>>> result['merged_data']
|
379
|
+
[1, 2, 3, 4]
|
380
|
+
|
381
|
+
>>> # Dictionary merging
|
382
|
+
>>> merge_node = MergeNode(merge_type="merge_dict")
|
383
|
+
>>> result = merge_node.execute(
|
384
|
+
... data1={"a": 1, "b": 2},
|
385
|
+
... data2={"b": 3, "c": 4}
|
386
|
+
... )
|
387
|
+
>>> result['merged_data']
|
388
|
+
{'a': 1, 'b': 3, 'c': 4}
|
389
|
+
|
390
|
+
>>> # List of dicts merging by key
|
391
|
+
>>> merge_node = MergeNode(merge_type="merge_dict", key="id")
|
392
|
+
>>> result = merge_node.execute(
|
393
|
+
... data1=[{"id": 1, "name": "Alice"}],
|
394
|
+
... data2=[{"id": 1, "age": 30}]
|
395
|
+
... )
|
396
|
+
>>> result['merged_data']
|
397
|
+
[{'id': 1, 'name': 'Alice', 'age': 30}]
|
373
398
|
"""
|
374
399
|
|
375
400
|
def get_parameters(self) -> Dict[str, NodeParameter]:
|
kailash/nodes/logic/workflow.py
CHANGED
@@ -52,24 +52,32 @@ class WorkflowNode(Node):
|
|
52
52
|
- Runtime executing the inner workflow
|
53
53
|
- Results passed to subsequent nodes
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
55
|
+
Example usage:
|
56
|
+
>>> # Direct workflow wrapping
|
57
|
+
>>> from kailash.workflow.graph import Workflow
|
58
|
+
>>> from kailash.nodes.data.readers import CSVReaderNode
|
59
|
+
>>> inner_workflow = Workflow("wf-001", "data_processing")
|
60
|
+
>>> inner_workflow.add_node("reader", CSVReaderNode(file_path="data.csv"))
|
61
|
+
>>> node = WorkflowNode(workflow=inner_workflow)
|
62
|
+
>>> node.metadata.name
|
63
|
+
'WorkflowNode'
|
64
|
+
|
65
|
+
>>> # Get parameters from wrapped workflow
|
66
|
+
>>> params = node.get_parameters()
|
67
|
+
>>> 'reader_file_path' in params
|
68
|
+
True
|
69
|
+
>>> 'inputs' in params
|
70
|
+
True
|
71
|
+
|
72
|
+
>>> # Loading from dictionary
|
73
|
+
>>> workflow_dict = {
|
74
|
+
... "name": "simple",
|
75
|
+
... "nodes": {"node1": {"type": "CSVReaderNode", "config": {"file_path": "test.csv"}}},
|
76
|
+
... "connections": []
|
77
|
+
... }
|
78
|
+
>>> node = WorkflowNode(workflow_dict=workflow_dict)
|
79
|
+
>>> node._workflow.name
|
80
|
+
'simple'
|
73
81
|
|
74
82
|
Implementation Details:
|
75
83
|
- Parameters derived from workflow entry nodes
|
@@ -6,10 +6,17 @@ from kailash.nodes.transform.formatters import (
|
|
6
6
|
ContextFormatterNode,
|
7
7
|
QueryTextWrapperNode,
|
8
8
|
)
|
9
|
-
from kailash.nodes.transform.processors import
|
9
|
+
from kailash.nodes.transform.processors import (
|
10
|
+
DataTransformer,
|
11
|
+
Filter,
|
12
|
+
FilterNode,
|
13
|
+
Map,
|
14
|
+
Sort,
|
15
|
+
)
|
10
16
|
|
11
17
|
__all__ = [
|
12
18
|
"Filter",
|
19
|
+
"FilterNode",
|
13
20
|
"Map",
|
14
21
|
"Sort",
|
15
22
|
"DataTransformer",
|