kailash 0.1.4__py3-none-any.whl → 0.2.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 +740 -0
- kailash/api/__main__.py +6 -0
- kailash/api/auth.py +668 -0
- kailash/api/custom_nodes.py +285 -0
- kailash/api/custom_nodes_secure.py +377 -0
- kailash/api/database.py +620 -0
- kailash/api/studio.py +915 -0
- kailash/api/studio_secure.py +893 -0
- kailash/mcp/__init__.py +53 -0
- kailash/mcp/__main__.py +13 -0
- kailash/mcp/ai_registry_server.py +712 -0
- kailash/mcp/client.py +447 -0
- kailash/mcp/client_new.py +334 -0
- kailash/mcp/server.py +293 -0
- kailash/mcp/server_new.py +336 -0
- kailash/mcp/servers/__init__.py +12 -0
- kailash/mcp/servers/ai_registry.py +289 -0
- kailash/nodes/__init__.py +4 -2
- kailash/nodes/ai/__init__.py +38 -0
- kailash/nodes/ai/a2a.py +1790 -0
- kailash/nodes/ai/agents.py +116 -2
- kailash/nodes/ai/ai_providers.py +206 -8
- kailash/nodes/ai/intelligent_agent_orchestrator.py +2108 -0
- kailash/nodes/ai/iterative_llm_agent.py +1280 -0
- kailash/nodes/ai/llm_agent.py +324 -1
- kailash/nodes/ai/self_organizing.py +1623 -0
- kailash/nodes/api/http.py +106 -25
- kailash/nodes/api/rest.py +116 -21
- kailash/nodes/base.py +15 -2
- kailash/nodes/base_async.py +45 -0
- kailash/nodes/base_cycle_aware.py +374 -0
- kailash/nodes/base_with_acl.py +338 -0
- kailash/nodes/code/python.py +135 -27
- kailash/nodes/data/readers.py +116 -53
- kailash/nodes/data/writers.py +16 -6
- kailash/nodes/logic/__init__.py +8 -0
- kailash/nodes/logic/async_operations.py +48 -9
- kailash/nodes/logic/convergence.py +642 -0
- kailash/nodes/logic/loop.py +153 -0
- kailash/nodes/logic/operations.py +212 -27
- kailash/nodes/logic/workflow.py +26 -18
- kailash/nodes/mixins/__init__.py +11 -0
- kailash/nodes/mixins/mcp.py +228 -0
- kailash/nodes/mixins.py +387 -0
- kailash/nodes/transform/__init__.py +8 -1
- kailash/nodes/transform/processors.py +119 -4
- kailash/runtime/__init__.py +2 -1
- kailash/runtime/access_controlled.py +458 -0
- kailash/runtime/local.py +106 -33
- kailash/runtime/parallel_cyclic.py +529 -0
- kailash/sdk_exceptions.py +90 -5
- kailash/security.py +845 -0
- kailash/tracking/manager.py +38 -15
- kailash/tracking/models.py +1 -1
- kailash/tracking/storage/filesystem.py +30 -2
- kailash/utils/__init__.py +8 -0
- kailash/workflow/__init__.py +18 -0
- kailash/workflow/convergence.py +270 -0
- kailash/workflow/cycle_analyzer.py +768 -0
- kailash/workflow/cycle_builder.py +573 -0
- kailash/workflow/cycle_config.py +709 -0
- kailash/workflow/cycle_debugger.py +760 -0
- kailash/workflow/cycle_exceptions.py +601 -0
- kailash/workflow/cycle_profiler.py +671 -0
- kailash/workflow/cycle_state.py +338 -0
- kailash/workflow/cyclic_runner.py +985 -0
- kailash/workflow/graph.py +500 -39
- kailash/workflow/migration.py +768 -0
- kailash/workflow/safety.py +365 -0
- kailash/workflow/templates.py +744 -0
- kailash/workflow/validation.py +693 -0
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/METADATA +446 -13
- kailash-0.2.0.dist-info/RECORD +125 -0
- kailash/nodes/mcp/__init__.py +0 -11
- kailash/nodes/mcp/client.py +0 -554
- kailash/nodes/mcp/resource.py +0 -682
- kailash/nodes/mcp/server.py +0 -577
- kailash-0.1.4.dist-info/RECORD +0 -85
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/WHEEL +0 -0
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/entry_points.txt +0 -0
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.1.4.dist-info → kailash-0.2.0.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/base.py
CHANGED
@@ -60,7 +60,8 @@ class NodeMetadata(BaseModel):
|
|
60
60
|
version: str = Field("1.0.0", description="Node version")
|
61
61
|
author: str = Field("", description="Node author")
|
62
62
|
created_at: datetime = Field(
|
63
|
-
default_factory=datetime.
|
63
|
+
default_factory=lambda: datetime.now(timezone.utc),
|
64
|
+
description="Node creation date",
|
64
65
|
)
|
65
66
|
tags: Set[str] = Field(default_factory=set, description="Node tags")
|
66
67
|
|
@@ -185,7 +186,19 @@ class Node(ABC):
|
|
185
186
|
),
|
186
187
|
)
|
187
188
|
self.logger = logging.getLogger(f"kailash.nodes.{self.id}")
|
188
|
-
|
189
|
+
|
190
|
+
# Filter out internal fields from config
|
191
|
+
internal_fields = {
|
192
|
+
"id",
|
193
|
+
"name",
|
194
|
+
"description",
|
195
|
+
"version",
|
196
|
+
"author",
|
197
|
+
"tags",
|
198
|
+
"metadata",
|
199
|
+
}
|
200
|
+
self.config = {k: v for k, v in kwargs.items() if k not in internal_fields}
|
201
|
+
|
189
202
|
self._validate_config()
|
190
203
|
except ValidationError as e:
|
191
204
|
raise NodeConfigurationError(f"Invalid node metadata: {e}") from e
|
kailash/nodes/base_async.py
CHANGED
@@ -45,6 +45,51 @@ class AsyncNode(Node):
|
|
45
45
|
- TaskManager: Tracks node execution status
|
46
46
|
"""
|
47
47
|
|
48
|
+
def execute(self, **runtime_inputs) -> Dict[str, Any]:
|
49
|
+
"""Execute the node synchronously by running async code in a new event loop.
|
50
|
+
|
51
|
+
This override allows AsyncNode to work with synchronous runtimes like LocalRuntime.
|
52
|
+
It creates a new event loop to run the async code if needed.
|
53
|
+
|
54
|
+
Args:
|
55
|
+
**runtime_inputs: Runtime inputs for node execution
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
Dictionary of validated outputs
|
59
|
+
|
60
|
+
Raises:
|
61
|
+
NodeValidationError: If inputs or outputs are invalid
|
62
|
+
NodeExecutionError: If execution fails
|
63
|
+
"""
|
64
|
+
import asyncio
|
65
|
+
import sys
|
66
|
+
|
67
|
+
# Check if we're already in an event loop
|
68
|
+
try:
|
69
|
+
asyncio.get_running_loop()
|
70
|
+
# We're in an event loop - this is problematic for sync execution
|
71
|
+
# Try to use nest_asyncio if available
|
72
|
+
try:
|
73
|
+
import nest_asyncio
|
74
|
+
|
75
|
+
nest_asyncio.apply()
|
76
|
+
return asyncio.run(self.execute_async(**runtime_inputs))
|
77
|
+
except ImportError:
|
78
|
+
# Fall back to running in a thread pool
|
79
|
+
import concurrent.futures
|
80
|
+
|
81
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
82
|
+
future = executor.submit(
|
83
|
+
asyncio.run, self.execute_async(**runtime_inputs)
|
84
|
+
)
|
85
|
+
return future.result()
|
86
|
+
except RuntimeError:
|
87
|
+
# No event loop running, we can create one
|
88
|
+
if sys.platform == "win32":
|
89
|
+
# Windows requires special handling
|
90
|
+
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
|
91
|
+
return asyncio.run(self.execute_async(**runtime_inputs))
|
92
|
+
|
48
93
|
async def async_run(self, **kwargs) -> Dict[str, Any]:
|
49
94
|
"""Asynchronous execution method for the node.
|
50
95
|
|