kailash 0.4.1__py3-none-any.whl → 0.5.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 +3 -4
- kailash/middleware/__init__.py +4 -2
- kailash/middleware/auth/__init__.py +55 -12
- kailash/middleware/auth/exceptions.py +80 -0
- kailash/middleware/auth/jwt_auth.py +265 -123
- kailash/middleware/auth/models.py +137 -0
- kailash/middleware/auth/utils.py +257 -0
- kailash/middleware/communication/api_gateway.py +49 -7
- kailash/middleware/core/agent_ui.py +108 -1
- kailash/middleware/database/repositories.py +3 -1
- kailash/middleware/mcp/enhanced_server.py +2 -2
- kailash/nodes/admin/audit_log.py +364 -6
- kailash/nodes/admin/user_management.py +1006 -20
- kailash/nodes/api/http.py +95 -71
- kailash/nodes/base.py +281 -164
- kailash/nodes/base_async.py +30 -31
- kailash/nodes/code/python.py +18 -0
- kailash/nodes/data/async_sql.py +3 -22
- kailash/utils/resource_manager.py +420 -0
- kailash/workflow/builder.py +93 -10
- kailash/workflow/cyclic_runner.py +4 -25
- {kailash-0.4.1.dist-info → kailash-0.5.0.dist-info}/METADATA +6 -4
- {kailash-0.4.1.dist-info → kailash-0.5.0.dist-info}/RECORD +27 -24
- kailash/middleware/auth/kailash_jwt_auth.py +0 -616
- {kailash-0.4.1.dist-info → kailash-0.5.0.dist-info}/WHEEL +0 -0
- {kailash-0.4.1.dist-info → kailash-0.5.0.dist-info}/entry_points.txt +0 -0
- {kailash-0.4.1.dist-info → kailash-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.4.1.dist-info → kailash-0.5.0.dist-info}/top_level.txt +0 -0
kailash/nodes/api/http.py
CHANGED
@@ -17,6 +17,11 @@ from pydantic import BaseModel
|
|
17
17
|
from kailash.nodes.base import Node, NodeParameter, register_node
|
18
18
|
from kailash.nodes.base_async import AsyncNode
|
19
19
|
from kailash.sdk_exceptions import NodeExecutionError, NodeValidationError
|
20
|
+
from kailash.utils.resource_manager import (
|
21
|
+
AsyncResourcePool,
|
22
|
+
ResourcePool,
|
23
|
+
managed_resource,
|
24
|
+
)
|
20
25
|
|
21
26
|
|
22
27
|
class HTTPMethod(str, Enum):
|
@@ -55,6 +60,23 @@ class HTTPResponse(BaseModel):
|
|
55
60
|
url: str
|
56
61
|
|
57
62
|
|
63
|
+
# Global connection pool for HTTP sessions
|
64
|
+
_http_session_pool = ResourcePool(
|
65
|
+
factory=lambda: requests.Session(),
|
66
|
+
max_size=20,
|
67
|
+
timeout=30.0,
|
68
|
+
cleanup=lambda session: session.close(),
|
69
|
+
)
|
70
|
+
|
71
|
+
# Global async connection pool for aiohttp sessions
|
72
|
+
_async_http_session_pool = AsyncResourcePool(
|
73
|
+
factory=lambda: aiohttp.ClientSession(),
|
74
|
+
max_size=20,
|
75
|
+
timeout=30.0,
|
76
|
+
cleanup=lambda session: asyncio.create_task(session.close()),
|
77
|
+
)
|
78
|
+
|
79
|
+
|
58
80
|
@register_node()
|
59
81
|
class HTTPRequestNode(Node):
|
60
82
|
"""
|
@@ -520,8 +542,11 @@ class HTTPRequestNode(Node):
|
|
520
542
|
|
521
543
|
try:
|
522
544
|
start_time = time.time()
|
523
|
-
|
524
|
-
|
545
|
+
|
546
|
+
# Use connection pool for efficient resource management
|
547
|
+
with _http_session_pool.acquire() as session:
|
548
|
+
response = session.request(method=method.value, **request_kwargs)
|
549
|
+
response_time = (time.time() - start_time) * 1000 # Convert to ms
|
525
550
|
|
526
551
|
# Log response if enabled
|
527
552
|
if log_requests:
|
@@ -681,7 +706,6 @@ class AsyncHTTPRequestNode(AsyncNode):
|
|
681
706
|
Same as HTTPRequestNode
|
682
707
|
"""
|
683
708
|
super().__init__(**kwargs)
|
684
|
-
self._session = None # Will be created when needed
|
685
709
|
|
686
710
|
def get_parameters(self) -> dict[str, NodeParameter]:
|
687
711
|
"""Define the parameters this node accepts.
|
@@ -829,10 +853,6 @@ class AsyncHTTPRequestNode(AsyncNode):
|
|
829
853
|
if rate_limit_delay > 0:
|
830
854
|
await asyncio.sleep(rate_limit_delay)
|
831
855
|
|
832
|
-
# Create session if needed
|
833
|
-
if self._session is None:
|
834
|
-
self._session = aiohttp.ClientSession()
|
835
|
-
|
836
856
|
# Prepare request kwargs
|
837
857
|
request_kwargs = {
|
838
858
|
"url": url,
|
@@ -870,72 +890,76 @@ class AsyncHTTPRequestNode(AsyncNode):
|
|
870
890
|
try:
|
871
891
|
start_time = time.time()
|
872
892
|
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
893
|
+
# Use connection pool for efficient resource management
|
894
|
+
async with _async_http_session_pool.acquire() as session:
|
895
|
+
async with session.request(
|
896
|
+
method=method.value, **request_kwargs
|
897
|
+
) as response:
|
898
|
+
response_time = (
|
899
|
+
time.time() - start_time
|
900
|
+
) * 1000 # Convert to ms
|
901
|
+
|
902
|
+
# Get content type
|
903
|
+
content_type = response.headers.get("Content-Type", "")
|
904
|
+
|
905
|
+
# Log response if enabled
|
906
|
+
if log_requests:
|
907
|
+
self.logger.info(f"Response: {response.status}")
|
908
|
+
self.logger.info(f"Headers: {dict(response.headers)}")
|
909
|
+
text_preview = await response.text()
|
910
|
+
self.logger.info(f"Body: {text_preview[:500]}...")
|
911
|
+
|
912
|
+
# Determine response format
|
913
|
+
actual_format = response_format
|
914
|
+
if actual_format == ResponseFormat.AUTO:
|
915
|
+
if "application/json" in content_type:
|
916
|
+
actual_format = ResponseFormat.JSON
|
917
|
+
elif "text/" in content_type:
|
918
|
+
actual_format = ResponseFormat.TEXT
|
919
|
+
else:
|
920
|
+
actual_format = ResponseFormat.BINARY
|
921
|
+
|
922
|
+
# Parse response
|
923
|
+
try:
|
924
|
+
if actual_format == ResponseFormat.JSON:
|
925
|
+
content = await response.json()
|
926
|
+
elif actual_format == ResponseFormat.TEXT:
|
927
|
+
content = await response.text()
|
928
|
+
elif actual_format == ResponseFormat.BINARY:
|
929
|
+
content = await response.read()
|
930
|
+
else:
|
931
|
+
content = await response.text() # Fallback to text
|
932
|
+
except Exception as e:
|
933
|
+
self.logger.warning(
|
934
|
+
f"Failed to parse response as {actual_format}: {str(e)}"
|
935
|
+
)
|
907
936
|
content = await response.text() # Fallback to text
|
908
|
-
except Exception as e:
|
909
|
-
self.logger.warning(
|
910
|
-
f"Failed to parse response as {actual_format}: {str(e)}"
|
911
|
-
)
|
912
|
-
content = await response.text() # Fallback to text
|
913
|
-
|
914
|
-
# Create response object
|
915
|
-
http_response = HTTPResponse(
|
916
|
-
status_code=response.status,
|
917
|
-
headers=dict(response.headers),
|
918
|
-
content_type=content_type,
|
919
|
-
content=content,
|
920
|
-
response_time_ms=response_time,
|
921
|
-
url=str(response.url),
|
922
|
-
).model_dump()
|
923
|
-
|
924
|
-
# Return results
|
925
|
-
success = 200 <= response.status < 300
|
926
|
-
|
927
|
-
result = {
|
928
|
-
"response": http_response,
|
929
|
-
"status_code": response.status,
|
930
|
-
"success": success,
|
931
|
-
}
|
932
|
-
|
933
|
-
if not success:
|
934
|
-
result["recovery_suggestions"] = self._get_recovery_suggestions(
|
935
|
-
response.status
|
936
|
-
)
|
937
937
|
|
938
|
-
|
938
|
+
# Create response object
|
939
|
+
http_response = HTTPResponse(
|
940
|
+
status_code=response.status,
|
941
|
+
headers=dict(response.headers),
|
942
|
+
content_type=content_type,
|
943
|
+
content=content,
|
944
|
+
response_time_ms=response_time,
|
945
|
+
url=str(response.url),
|
946
|
+
).model_dump()
|
947
|
+
|
948
|
+
# Return results
|
949
|
+
success = 200 <= response.status < 300
|
950
|
+
|
951
|
+
result = {
|
952
|
+
"response": http_response,
|
953
|
+
"status_code": response.status,
|
954
|
+
"success": success,
|
955
|
+
}
|
956
|
+
|
957
|
+
if not success:
|
958
|
+
result["recovery_suggestions"] = (
|
959
|
+
self._get_recovery_suggestions(response.status)
|
960
|
+
)
|
961
|
+
|
962
|
+
return result
|
939
963
|
|
940
964
|
except (TimeoutError, aiohttp.ClientError) as e:
|
941
965
|
self.logger.warning(f"Async request failed: {str(e)}")
|