kailash 0.9.19__py3-none-any.whl → 0.9.21__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/mcp_server/subscriptions.py +20 -20
- kailash/nodes/__init__.py +1 -0
- kailash/nodes/ai/ai_providers.py +237 -25
- kailash/nodes/ai/llm_agent.py +73 -15
- kailash/nodes/code/python.py +178 -15
- kailash/nodes/data/async_sql.py +76 -10
- kailash/workflow/builder.py +1 -1
- kailash/workflow/templates.py +2 -1
- {kailash-0.9.19.dist-info → kailash-0.9.21.dist-info}/METADATA +27 -19
- {kailash-0.9.19.dist-info → kailash-0.9.21.dist-info}/RECORD +16 -17
- kailash/nodes/ai/a2a_backup.py +0 -1807
- {kailash-0.9.19.dist-info → kailash-0.9.21.dist-info}/WHEEL +0 -0
- {kailash-0.9.19.dist-info → kailash-0.9.21.dist-info}/entry_points.txt +0 -0
- {kailash-0.9.19.dist-info → kailash-0.9.21.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.9.19.dist-info → kailash-0.9.21.dist-info}/licenses/NOTICE +0 -0
- {kailash-0.9.19.dist-info → kailash-0.9.21.dist-info}/top_level.txt +0 -0
kailash/nodes/code/python.py
CHANGED
@@ -49,11 +49,14 @@ Examples:
|
|
49
49
|
import ast
|
50
50
|
import importlib.util
|
51
51
|
import inspect
|
52
|
+
import json
|
52
53
|
import logging
|
53
54
|
import os
|
54
55
|
import resource
|
55
56
|
import traceback
|
56
57
|
from collections.abc import Callable
|
58
|
+
from datetime import date, datetime
|
59
|
+
from decimal import Decimal
|
57
60
|
from pathlib import Path
|
58
61
|
from typing import Any, get_type_hints
|
59
62
|
|
@@ -617,6 +620,37 @@ class CodeExecutor:
|
|
617
620
|
logger.error(error_msg)
|
618
621
|
raise NodeExecutionError(error_msg)
|
619
622
|
|
623
|
+
def _ensure_serializable(self, data: Any) -> Any:
|
624
|
+
"""Ensure data is JSON-serializable following AsyncSQL pattern."""
|
625
|
+
if data is None:
|
626
|
+
return None
|
627
|
+
elif isinstance(data, (str, int, float, bool)):
|
628
|
+
return data
|
629
|
+
elif isinstance(data, (datetime, date)):
|
630
|
+
return data.isoformat()
|
631
|
+
elif isinstance(data, Decimal):
|
632
|
+
return float(data)
|
633
|
+
elif isinstance(data, dict):
|
634
|
+
return {k: self._ensure_serializable(v) for k, v in data.items()}
|
635
|
+
elif isinstance(data, (list, tuple)):
|
636
|
+
return [self._ensure_serializable(item) for item in data]
|
637
|
+
else:
|
638
|
+
try:
|
639
|
+
json.dumps(data)
|
640
|
+
return data
|
641
|
+
except (TypeError, ValueError):
|
642
|
+
# Check if object has .to_dict() method for enhanced validation
|
643
|
+
if hasattr(data, "to_dict") and callable(getattr(data, "to_dict")):
|
644
|
+
try:
|
645
|
+
# Convert object to dict using its to_dict() method
|
646
|
+
dict_result = data.to_dict()
|
647
|
+
# Recursively ensure the dict result is also serializable
|
648
|
+
return self._ensure_json_serializable(dict_result)
|
649
|
+
except (TypeError, ValueError, AttributeError):
|
650
|
+
# If .to_dict() exists but fails, fall back to string
|
651
|
+
return str(data)
|
652
|
+
return str(data)
|
653
|
+
|
620
654
|
|
621
655
|
class FunctionWrapper:
|
622
656
|
"""Wrapper for converting Python functions to nodes.
|
@@ -651,6 +685,37 @@ class FunctionWrapper:
|
|
651
685
|
# Handle cases where type hints can't be resolved
|
652
686
|
self.type_hints = {}
|
653
687
|
|
688
|
+
def _ensure_serializable(self, data: Any) -> Any:
|
689
|
+
"""Ensure data is JSON-serializable."""
|
690
|
+
if data is None:
|
691
|
+
return None
|
692
|
+
elif isinstance(data, (str, int, float, bool)):
|
693
|
+
return data
|
694
|
+
elif isinstance(data, (datetime, date)):
|
695
|
+
return data.isoformat()
|
696
|
+
elif isinstance(data, Decimal):
|
697
|
+
return float(data)
|
698
|
+
elif isinstance(data, dict):
|
699
|
+
return {k: self._ensure_serializable(v) for k, v in data.items()}
|
700
|
+
elif isinstance(data, (list, tuple)):
|
701
|
+
return [self._ensure_serializable(item) for item in data]
|
702
|
+
else:
|
703
|
+
try:
|
704
|
+
json.dumps(data)
|
705
|
+
return data
|
706
|
+
except (TypeError, ValueError):
|
707
|
+
# Check if object has .to_dict() method for enhanced validation
|
708
|
+
if hasattr(data, "to_dict") and callable(getattr(data, "to_dict")):
|
709
|
+
try:
|
710
|
+
# Convert object to dict using its to_dict() method
|
711
|
+
dict_result = data.to_dict()
|
712
|
+
# Recursively ensure the dict result is also serializable
|
713
|
+
return self._ensure_json_serializable(dict_result)
|
714
|
+
except (TypeError, ValueError, AttributeError):
|
715
|
+
# If .to_dict() exists but fails, fall back to string
|
716
|
+
return str(data)
|
717
|
+
return str(data)
|
718
|
+
|
654
719
|
def get_input_types(self) -> dict[str, type]:
|
655
720
|
"""Extract input types from function signature.
|
656
721
|
|
@@ -710,18 +775,51 @@ class FunctionWrapper:
|
|
710
775
|
return self.type_hints.get("return", Any)
|
711
776
|
|
712
777
|
def execute(self, inputs: dict[str, Any]) -> dict[str, Any]:
|
713
|
-
"""Execute the wrapped function."""
|
778
|
+
"""Execute the wrapped function with proper serialization."""
|
714
779
|
result = self.executor.execute_function(self.func, inputs)
|
715
780
|
|
716
|
-
#
|
717
|
-
|
718
|
-
if not isinstance(result, dict):
|
719
|
-
result = {"result": result}
|
720
|
-
else:
|
721
|
-
# For dict results, wrap the entire dict in "result" key
|
722
|
-
result = {"result": result}
|
781
|
+
# Ensure JSON serializability inline
|
782
|
+
result = self._ensure_json_serializable(result)
|
723
783
|
|
724
|
-
|
784
|
+
# Smart wrapping: only wrap if result doesn't already have expected structure
|
785
|
+
# If function already returns {"result": value}, don't double-wrap
|
786
|
+
if isinstance(result, dict) and len(result) == 1 and "result" in result:
|
787
|
+
# Function already returned properly formatted result
|
788
|
+
return result
|
789
|
+
else:
|
790
|
+
# Wrap result for consistent schema validation
|
791
|
+
return {"result": result}
|
792
|
+
|
793
|
+
def _ensure_json_serializable(self, data: Any) -> Any:
|
794
|
+
"""Convert data to JSON-serializable format."""
|
795
|
+
if data is None:
|
796
|
+
return None
|
797
|
+
elif isinstance(data, (str, int, float, bool)):
|
798
|
+
return data
|
799
|
+
elif isinstance(data, (datetime, date)):
|
800
|
+
return data.isoformat()
|
801
|
+
elif isinstance(data, Decimal):
|
802
|
+
return float(data)
|
803
|
+
elif isinstance(data, dict):
|
804
|
+
return {k: self._ensure_json_serializable(v) for k, v in data.items()}
|
805
|
+
elif isinstance(data, (list, tuple)):
|
806
|
+
return [self._ensure_json_serializable(item) for item in data]
|
807
|
+
else:
|
808
|
+
try:
|
809
|
+
json.dumps(data)
|
810
|
+
return data
|
811
|
+
except (TypeError, ValueError):
|
812
|
+
# Check if object has .to_dict() method for enhanced validation
|
813
|
+
if hasattr(data, "to_dict") and callable(getattr(data, "to_dict")):
|
814
|
+
try:
|
815
|
+
# Convert object to dict using its to_dict() method
|
816
|
+
dict_result = data.to_dict()
|
817
|
+
# Recursively ensure the dict result is also serializable
|
818
|
+
return self._ensure_json_serializable(dict_result)
|
819
|
+
except (TypeError, ValueError, AttributeError):
|
820
|
+
# If .to_dict() exists but fails, fall back to string
|
821
|
+
return str(data)
|
822
|
+
return str(data)
|
725
823
|
|
726
824
|
def to_node(
|
727
825
|
self,
|
@@ -837,6 +935,37 @@ class ClassWrapper:
|
|
837
935
|
# Handle descriptor objects like properties
|
838
936
|
self.type_hints = {}
|
839
937
|
|
938
|
+
def _ensure_serializable(self, data: Any) -> Any:
|
939
|
+
"""Ensure data is JSON-serializable."""
|
940
|
+
if data is None:
|
941
|
+
return None
|
942
|
+
elif isinstance(data, (str, int, float, bool)):
|
943
|
+
return data
|
944
|
+
elif isinstance(data, (datetime, date)):
|
945
|
+
return data.isoformat()
|
946
|
+
elif isinstance(data, Decimal):
|
947
|
+
return float(data)
|
948
|
+
elif isinstance(data, dict):
|
949
|
+
return {k: self._ensure_serializable(v) for k, v in data.items()}
|
950
|
+
elif isinstance(data, (list, tuple)):
|
951
|
+
return [self._ensure_serializable(item) for item in data]
|
952
|
+
else:
|
953
|
+
try:
|
954
|
+
json.dumps(data)
|
955
|
+
return data
|
956
|
+
except (TypeError, ValueError):
|
957
|
+
# Check if object has .to_dict() method for enhanced validation
|
958
|
+
if hasattr(data, "to_dict") and callable(getattr(data, "to_dict")):
|
959
|
+
try:
|
960
|
+
# Convert object to dict using its to_dict() method
|
961
|
+
dict_result = data.to_dict()
|
962
|
+
# Recursively ensure the dict result is also serializable
|
963
|
+
return self._ensure_json_serializable(dict_result)
|
964
|
+
except (TypeError, ValueError, AttributeError):
|
965
|
+
# If .to_dict() exists but fails, fall back to string
|
966
|
+
return str(data)
|
967
|
+
return str(data)
|
968
|
+
|
840
969
|
def get_input_types(self) -> dict[str, type]:
|
841
970
|
"""Extract input types from method signature."""
|
842
971
|
input_types = {}
|
@@ -900,16 +1029,48 @@ class ClassWrapper:
|
|
900
1029
|
# Execute the method
|
901
1030
|
result = self.executor.execute_function(method, inputs)
|
902
1031
|
|
903
|
-
#
|
904
|
-
|
1032
|
+
# Ensure JSON serializability inline
|
1033
|
+
result = self._ensure_json_serializable(result)
|
1034
|
+
|
1035
|
+
# Smart wrapping: only wrap non-dict results in "result" key
|
1036
|
+
# Dict results are returned as-is to avoid double wrapping
|
905
1037
|
if not isinstance(result, dict):
|
906
1038
|
result = {"result": result}
|
907
|
-
|
908
|
-
# For dict results, wrap the entire dict in "result" key
|
909
|
-
result = {"result": result}
|
1039
|
+
# Dict results are already properly structured, no wrapping needed
|
910
1040
|
|
911
1041
|
return result
|
912
1042
|
|
1043
|
+
def _ensure_json_serializable(self, data: Any) -> Any:
|
1044
|
+
"""Convert data to JSON-serializable format."""
|
1045
|
+
if data is None:
|
1046
|
+
return None
|
1047
|
+
elif isinstance(data, (str, int, float, bool)):
|
1048
|
+
return data
|
1049
|
+
elif isinstance(data, (datetime, date)):
|
1050
|
+
return data.isoformat()
|
1051
|
+
elif isinstance(data, Decimal):
|
1052
|
+
return float(data)
|
1053
|
+
elif isinstance(data, dict):
|
1054
|
+
return {k: self._ensure_json_serializable(v) for k, v in data.items()}
|
1055
|
+
elif isinstance(data, (list, tuple)):
|
1056
|
+
return [self._ensure_json_serializable(item) for item in data]
|
1057
|
+
else:
|
1058
|
+
try:
|
1059
|
+
json.dumps(data)
|
1060
|
+
return data
|
1061
|
+
except (TypeError, ValueError):
|
1062
|
+
# Check if object has .to_dict() method for enhanced validation
|
1063
|
+
if hasattr(data, "to_dict") and callable(getattr(data, "to_dict")):
|
1064
|
+
try:
|
1065
|
+
# Convert object to dict using its to_dict() method
|
1066
|
+
dict_result = data.to_dict()
|
1067
|
+
# Recursively ensure the dict result is also serializable
|
1068
|
+
return self._ensure_json_serializable(dict_result)
|
1069
|
+
except (TypeError, ValueError, AttributeError):
|
1070
|
+
# If .to_dict() exists but fails, fall back to string
|
1071
|
+
return str(data)
|
1072
|
+
return str(data)
|
1073
|
+
|
913
1074
|
def to_node(
|
914
1075
|
self,
|
915
1076
|
name: str | None = None,
|
@@ -1272,7 +1433,9 @@ class PythonCodeNode(Node):
|
|
1272
1433
|
elif self.function:
|
1273
1434
|
# Execute function
|
1274
1435
|
wrapper = FunctionWrapper(self.function, self.executor)
|
1275
|
-
|
1436
|
+
result = wrapper.execute(kwargs)
|
1437
|
+
# FunctionWrapper.execute() already handles result wrapping
|
1438
|
+
return result
|
1276
1439
|
|
1277
1440
|
elif self.class_type:
|
1278
1441
|
# Execute class method
|
kailash/nodes/data/async_sql.py
CHANGED
@@ -3255,8 +3255,17 @@ class AsyncSQLDatabaseNode(AsyncNode):
|
|
3255
3255
|
|
3256
3256
|
def _generate_pool_key(self) -> str:
|
3257
3257
|
"""Generate a unique key for connection pool sharing."""
|
3258
|
-
#
|
3258
|
+
# Get event loop ID for isolation
|
3259
|
+
try:
|
3260
|
+
loop = asyncio.get_running_loop()
|
3261
|
+
loop_id = str(id(loop))
|
3262
|
+
except RuntimeError:
|
3263
|
+
# No running loop (initialization phase)
|
3264
|
+
loop_id = "no_loop"
|
3265
|
+
|
3266
|
+
# Create a unique key based on event loop and connection parameters
|
3259
3267
|
key_parts = [
|
3268
|
+
loop_id, # Event loop isolation
|
3260
3269
|
self.config.get("database_type", ""),
|
3261
3270
|
self.config.get("connection_string", "")
|
3262
3271
|
or (
|
@@ -3295,16 +3304,30 @@ class AsyncSQLDatabaseNode(AsyncNode):
|
|
3295
3304
|
):
|
3296
3305
|
|
3297
3306
|
if self._pool_key in self._shared_pools:
|
3298
|
-
#
|
3307
|
+
# Validate pool's event loop is still running before reuse
|
3299
3308
|
adapter, ref_count = self._shared_pools[self._pool_key]
|
3300
|
-
|
3301
|
-
|
3302
|
-
|
3303
|
-
|
3304
|
-
|
3305
|
-
|
3306
|
-
|
3307
|
-
|
3309
|
+
|
3310
|
+
try:
|
3311
|
+
# Check if we have a running event loop
|
3312
|
+
pool_loop = asyncio.get_running_loop()
|
3313
|
+
# If we got here, loop is running - safe to reuse
|
3314
|
+
self._shared_pools[self._pool_key] = (
|
3315
|
+
adapter,
|
3316
|
+
ref_count + 1,
|
3317
|
+
)
|
3318
|
+
self._adapter = adapter
|
3319
|
+
self._connected = True
|
3320
|
+
logger.debug(
|
3321
|
+
f"Using class-level shared pool for {self.id}"
|
3322
|
+
)
|
3323
|
+
return self._adapter
|
3324
|
+
except RuntimeError:
|
3325
|
+
# Loop is closed - remove stale pool
|
3326
|
+
logger.warning(
|
3327
|
+
f"Removing stale pool for {self._pool_key} - event loop closed"
|
3328
|
+
)
|
3329
|
+
del self._shared_pools[self._pool_key]
|
3330
|
+
# Fall through to create new pool
|
3308
3331
|
|
3309
3332
|
# Create new shared pool
|
3310
3333
|
self._adapter = await self._create_adapter()
|
@@ -4000,8 +4023,51 @@ class AsyncSQLDatabaseNode(AsyncNode):
|
|
4000
4023
|
|
4001
4024
|
metrics["pools"].append(pool_info)
|
4002
4025
|
|
4026
|
+
# Clean up stale pools from closed event loops
|
4027
|
+
cleaned_pools = cls._cleanup_closed_loop_pools()
|
4028
|
+
if cleaned_pools > 0:
|
4029
|
+
metrics["cleaned_stale_pools"] = cleaned_pools
|
4030
|
+
|
4003
4031
|
return metrics
|
4004
4032
|
|
4033
|
+
@classmethod
|
4034
|
+
def _cleanup_closed_loop_pools(cls) -> int:
|
4035
|
+
"""
|
4036
|
+
Clean up pools from closed event loops.
|
4037
|
+
|
4038
|
+
Returns:
|
4039
|
+
Number of pools removed
|
4040
|
+
"""
|
4041
|
+
removed_count = 0
|
4042
|
+
keys_to_remove = []
|
4043
|
+
|
4044
|
+
for pool_key in list(cls._shared_pools.keys()):
|
4045
|
+
# Extract loop ID from pool key (first part before "|")
|
4046
|
+
parts = pool_key.split("|")
|
4047
|
+
if len(parts) > 0:
|
4048
|
+
loop_id_str = parts[0]
|
4049
|
+
|
4050
|
+
# Check if this pool's event loop is still running
|
4051
|
+
try:
|
4052
|
+
current_loop = asyncio.get_running_loop()
|
4053
|
+
current_loop_id = str(id(current_loop))
|
4054
|
+
|
4055
|
+
# If loop IDs don't match and pool is stale, mark for removal
|
4056
|
+
if loop_id_str != current_loop_id and loop_id_str != "no_loop":
|
4057
|
+
keys_to_remove.append(pool_key)
|
4058
|
+
except RuntimeError:
|
4059
|
+
# No current loop - mark old pools for removal
|
4060
|
+
if loop_id_str != "no_loop":
|
4061
|
+
keys_to_remove.append(pool_key)
|
4062
|
+
|
4063
|
+
# Remove stale pools
|
4064
|
+
for key in keys_to_remove:
|
4065
|
+
if key in cls._shared_pools:
|
4066
|
+
del cls._shared_pools[key]
|
4067
|
+
removed_count += 1
|
4068
|
+
|
4069
|
+
return removed_count
|
4070
|
+
|
4005
4071
|
@classmethod
|
4006
4072
|
async def clear_shared_pools(cls) -> None:
|
4007
4073
|
"""Clear all shared connection pools. Use with caution!"""
|
kailash/workflow/builder.py
CHANGED
@@ -558,7 +558,7 @@ class WorkflowBuilder:
|
|
558
558
|
Raises:
|
559
559
|
WorkflowValidationError: If node_id is already used or instance is invalid
|
560
560
|
"""
|
561
|
-
return self.
|
561
|
+
return self._add_node_instance(node_instance, node_id)
|
562
562
|
|
563
563
|
def add_node_type(
|
564
564
|
self,
|
kailash/workflow/templates.py
CHANGED
@@ -171,7 +171,8 @@ class CycleTemplates:
|
|
171
171
|
... )
|
172
172
|
"""
|
173
173
|
if cycle_id is None:
|
174
|
-
|
174
|
+
# Use timestamp with milliseconds for ID generation to ensure uniqueness
|
175
|
+
cycle_id = f"optimization_cycle_{int(time.time() * 1000)}"
|
175
176
|
|
176
177
|
# Connect processor to evaluator
|
177
178
|
workflow.connect(processor_node, evaluator_node)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: kailash
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.21
|
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
|
@@ -53,7 +53,6 @@ Requires-Dist: pre-commit>=4.2.0
|
|
53
53
|
Requires-Dist: twine>=6.1.0
|
54
54
|
Requires-Dist: ollama>=0.5.1
|
55
55
|
Requires-Dist: sqlalchemy>=2.0.0
|
56
|
-
Requires-Dist: psycopg2-binary>=2.9.0
|
57
56
|
Requires-Dist: pymysql>=1.1.0
|
58
57
|
Requires-Dist: aiosqlite>=0.19.0
|
59
58
|
Requires-Dist: websockets>=12.0
|
@@ -70,7 +69,7 @@ Requires-Dist: qrcode>=8.2
|
|
70
69
|
Requires-Dist: aiofiles>=24.1.0
|
71
70
|
Requires-Dist: bcrypt>=4.3.0
|
72
71
|
Requires-Dist: plotly>=6.2.0
|
73
|
-
Requires-Dist: redis
|
72
|
+
Requires-Dist: redis>=6.2.0
|
74
73
|
Requires-Dist: faker>=37.4.0
|
75
74
|
Requires-Dist: structlog>=25.4.0
|
76
75
|
Requires-Dist: authlib>=1.6.0
|
@@ -86,6 +85,8 @@ Requires-Dist: seaborn>=0.13.2
|
|
86
85
|
Requires-Dist: sqlparse>=0.5.3
|
87
86
|
Requires-Dist: jsonschema>=4.24.0
|
88
87
|
Requires-Dist: openai>=1.97.1
|
88
|
+
Requires-Dist: pymongo>=4.15.2
|
89
|
+
Requires-Dist: psycopg>=3.2.10
|
89
90
|
Provides-Extra: dev
|
90
91
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
91
92
|
Requires-Dist: pytest-cov>=3.0; extra == "dev"
|
@@ -121,28 +122,35 @@ Dynamic: requires-python
|
|
121
122
|
|
122
123
|
---
|
123
124
|
|
124
|
-
## 🔥 Latest Release: v0.9.
|
125
|
+
## 🔥 Latest Release: v0.9.21 (October 8, 2025)
|
125
126
|
|
126
|
-
**
|
127
|
+
**AsyncSQL Event Loop Isolation Fix**
|
127
128
|
|
128
|
-
###
|
129
|
-
- **
|
130
|
-
-
|
131
|
-
-
|
132
|
-
-
|
129
|
+
### 🐛 Critical Bug Fixes
|
130
|
+
- **Event Loop Isolation**: Fixed "Event loop is closed" errors in AsyncSQLDatabaseNode
|
131
|
+
- Automatic connection pool isolation per event loop
|
132
|
+
- Prevents pool sharing across different event loops (FastAPI, sequential workflows)
|
133
|
+
- Backward compatible - no code changes required
|
134
|
+
- <5% performance overhead
|
133
135
|
|
134
|
-
###
|
135
|
-
- **
|
136
|
-
- **
|
137
|
-
- **
|
138
|
-
|
136
|
+
### Previous Release: v0.9.20 (October 6, 2025)
|
137
|
+
- **Mock Provider Bypass**: Removed hardcoded `if provider == "mock"` logic from LLMAgentNode
|
138
|
+
- **Tool Execution Flow**: Unified provider response generation for all providers
|
139
|
+
- **Provider Registry**: All providers now use consistent registry path
|
140
|
+
|
141
|
+
### ✨ Enhancements
|
142
|
+
- **Custom Mock Providers**: Enables signature-aware mock providers (e.g., KaizenMockProvider)
|
143
|
+
- **Multi-Modal Foundation**: Foundation for vision/audio processing in Kaizen AI framework
|
144
|
+
- **Tool Call Generation**: MockProvider generates mock tool_calls for action-oriented messages
|
145
|
+
- **Enhanced Testing**: 510+ tests passing with custom mock providers
|
139
146
|
|
140
147
|
### 📦 Package Updates
|
141
|
-
- **kailash**: v0.9.
|
142
|
-
- **kailash-
|
143
|
-
- **kailash-
|
148
|
+
- **kailash**: v0.9.20 - Provider registry fix & multi-modal support
|
149
|
+
- **kailash-kaizen**: v0.1.1 - AI agent framework (NEW!)
|
150
|
+
- **kailash-nexus**: v1.0.6 - Multi-channel platform
|
151
|
+
- **kailash-dataflow**: v0.5.0 - Database framework
|
144
152
|
|
145
|
-
[Full Changelog](sdk-users/6-reference/changelogs/releases/v0.9.
|
153
|
+
[Full Changelog](sdk-users/6-reference/changelogs/releases/v0.9.20-provider-registry-fix.md) | [Core SDK 0.9.20](https://pypi.org/project/kailash/0.9.20/) | [Kaizen 0.1.1](https://pypi.org/project/kailash-kaizen/0.1.1/) | [Nexus 1.0.6](https://pypi.org/project/kailash-nexus/1.0.6/) | [DataFlow 0.5.0](https://pypi.org/project/kailash-dataflow/0.5.0/)
|
146
154
|
|
147
155
|
## 🎯 What Makes Kailash Different
|
148
156
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
kailash/__init__.py,sha256=
|
1
|
+
kailash/__init__.py,sha256=C2Wda8HouzENpwGg80j0axK14KHtCoteXfA8a298b5U,2928
|
2
2
|
kailash/__main__.py,sha256=vr7TVE5o16V6LsTmRFKG6RDKUXHpIWYdZ6Dok2HkHnI,198
|
3
3
|
kailash/access_control.py,sha256=MjKtkoQ2sg1Mgfe7ovGxVwhAbpJKvaepPWr8dxOueMA,26058
|
4
4
|
kailash/access_control_abac.py,sha256=FPfa_8PuDP3AxTjdWfiH3ntwWO8NodA0py9W8SE5dno,30263
|
@@ -93,7 +93,7 @@ kailash/mcp_server/oauth.py,sha256=GFC2O2ueiTTI6V-91Huevhc3K8CxrHe22knuHfuCTqY,5
|
|
93
93
|
kailash/mcp_server/protocol.py,sha256=NIdEwJT21JT9ItajXniPNvCbZtTbpqyOC_ZezqsguGE,35694
|
94
94
|
kailash/mcp_server/registry_integration.py,sha256=B8CSLq_O1ea3cXrbVjC3bB_OFgHIP-KS9dk77mNM02I,19791
|
95
95
|
kailash/mcp_server/server.py,sha256=yFp1F4QQl6gkTY_9JJWmiMiwfT-zACLJLubz-NR5sCw,108675
|
96
|
-
kailash/mcp_server/subscriptions.py,sha256
|
96
|
+
kailash/mcp_server/subscriptions.py,sha256=-SWy1RnKUrBQ1y8ooeTiA2ODhxBbzfpsrtb8HtkhTYg,58591
|
97
97
|
kailash/mcp_server/transports.py,sha256=fBa7CTVYTDb0ZbBQTsZ2d8rKvcVuqBIteczq8eqarr4,49919
|
98
98
|
kailash/mcp_server/servers/ai_registry.py,sha256=IdF_keUuJlMsvjLjSAykxxbm46K4qA7eCj7T-lYSrzk,10007
|
99
99
|
kailash/mcp_server/utils/__init__.py,sha256=R20N-iiKXUPxc9MOh6vPO1vIfkPmwhEQ5KNFgGd4xSs,771
|
@@ -155,7 +155,7 @@ kailash/monitoring/__init__.py,sha256=w7We20bpBdcYR3PTfN9lkep8fPEc3T2eenUkNwjdw_
|
|
155
155
|
kailash/monitoring/alerts.py,sha256=Hk3Xs0EEkOIBH2ZhlejJBOsLYaPlvRejAAEGqNQISc0,21400
|
156
156
|
kailash/monitoring/asyncsql_metrics.py,sha256=jj9M8D5qHoS3zEFfZYsUCWsy5kb-J5-iYVacmNUaGjE,9577
|
157
157
|
kailash/monitoring/metrics.py,sha256=SiAnL3o6K0QaJHgfAuWBa-0pTkW5zymhuPEsj4bgOgM,22022
|
158
|
-
kailash/nodes/__init__.py,sha256=
|
158
|
+
kailash/nodes/__init__.py,sha256=dBnEwrop0cPblHxSOtVWAKCDzhRtcyQVv9j_YGWxczQ,6410
|
159
159
|
kailash/nodes/__init___original.py,sha256=p2KSo0dyUBCLClU123qpQ0tyv5S_36PTxosNyW58nyY,1031
|
160
160
|
kailash/nodes/base.py,sha256=GR2E1fWf8j1yMvJic7m2NAih7kjY1NtoDi47hHwoZ40,85437
|
161
161
|
kailash/nodes/base_async.py,sha256=whxepCiVplrltfzEQuabmnGCpEV5WgfqwgxbLdCyiDk,8864
|
@@ -177,14 +177,13 @@ kailash/nodes/admin/transaction_utils.py,sha256=IAAdIiQ5Q4kTT5ul4hv1iSfoXG3CeFAk
|
|
177
177
|
kailash/nodes/admin/user_management.py,sha256=Al9mKgTx3ASDSefZaIcPxL4zSjASI3cGdZIs33uwpvg,54707
|
178
178
|
kailash/nodes/ai/__init__.py,sha256=1mH94Ap5Zo0sUFNNZbMQf3h_KmyS2fquCTFMPvdJLoc,2725
|
179
179
|
kailash/nodes/ai/a2a.py,sha256=g3QIeCefWpcmQ3RpuoTAnCj9Q8145ieV3NyEfGsyOno,138552
|
180
|
-
kailash/nodes/ai/a2a_backup.py,sha256=fDSnihMFQ6MuhKSzL9ueWGAQLrKZQRkq9HqArdotf_w,70048
|
181
180
|
kailash/nodes/ai/agents.py,sha256=CRA3cdapQjpuvOniXUh6ZVWAlRxUIepVw1BROW6QzdY,20373
|
182
|
-
kailash/nodes/ai/ai_providers.py,sha256=
|
181
|
+
kailash/nodes/ai/ai_providers.py,sha256=XdkZlG7TJcQWoPEYLBy65jjtkOTxOpjl7MFO5MfZc6U,76627
|
183
182
|
kailash/nodes/ai/embedding_generator.py,sha256=akGCzz7zLRSziqEQCiPwL2qWhRWxuM_1RQh-YtVEddw,31879
|
184
183
|
kailash/nodes/ai/hybrid_search.py,sha256=k26uDDP_bwrIpv7Yl7PBCPvWSyQEmTlBjI1IpbgDsO4,35446
|
185
184
|
kailash/nodes/ai/intelligent_agent_orchestrator.py,sha256=LvBqMKc64zSxFWVCjbLKKel2QwEzoTeJAEgna7rZw00,83097
|
186
185
|
kailash/nodes/ai/iterative_llm_agent.py,sha256=h8iP1KFhB_eCDs7UvmY_9y0OUBuprYMj2MLM6dR0W2c,100287
|
187
|
-
kailash/nodes/ai/llm_agent.py,sha256
|
186
|
+
kailash/nodes/ai/llm_agent.py,sha256=-3K5BuMsPk_-0gIc-_pg95-cgh8yXZ5CaQvE91Wu3nw,99307
|
188
187
|
kailash/nodes/ai/models.py,sha256=wsEeUTuegy87mnLtKgSTg7ggCXvC1n3MsL-iZ4qujHs,16393
|
189
188
|
kailash/nodes/ai/self_organizing.py,sha256=B7NwKaBW8OHQBf5b0F9bSs8Wm-5BDJ9IjIkxS9h00mg,62885
|
190
189
|
kailash/nodes/ai/semantic_memory.py,sha256=ZTXIgxwMheux712cN__cNrQ3VgHaKcDyfQv_Gto7MRM,18644
|
@@ -214,13 +213,13 @@ kailash/nodes/cache/cache_invalidation.py,sha256=IUvxrRj3K5EF29Z2EaKl7t6Uze_cssn
|
|
214
213
|
kailash/nodes/cache/redis_pool_manager.py,sha256=GR82GCWxo_gAzRE-091OB6AhKre8CTwM3OoePLb2gvE,21574
|
215
214
|
kailash/nodes/code/__init__.py,sha256=yhEwuMjUEPFfe6hMGMd4E4gZdLUuf2JEQ7knYapiM4o,1283
|
216
215
|
kailash/nodes/code/async_python.py,sha256=Ai-iMpmz-sAori73JBk0wZtqmwtmF2GNPDxqB04I2Ck,37058
|
217
|
-
kailash/nodes/code/python.py,sha256=
|
216
|
+
kailash/nodes/code/python.py,sha256=rHBynVhV16G9AOQMZtLnpwTkp-wxsGe69BAjVZ4X6hg,70224
|
218
217
|
kailash/nodes/compliance/__init__.py,sha256=6a_FL4ofc8MAVuZ-ARW5uYenZLS4mBFVM9AI2QsnoF8,214
|
219
218
|
kailash/nodes/compliance/data_retention.py,sha256=90bH_eGwlcDzUdklAJeXQM-RcuLUGQFQ5fgHOK8a4qk,69443
|
220
219
|
kailash/nodes/compliance/gdpr.py,sha256=ZMoHZjAo4QtGwtFCzGMrAUBFV3TbZOnJ5DZGZS87Bas,70548
|
221
220
|
kailash/nodes/data/__init__.py,sha256=f0h4ysvXxlyFcNJLvDyXrgJ0ixwDF1cS0pJ2QNPakhg,5213
|
222
221
|
kailash/nodes/data/async_connection.py,sha256=wfArHs9svU48bxGZIiixSV2YVn9cukNgEjagwTRu6J4,17250
|
223
|
-
kailash/nodes/data/async_sql.py,sha256=
|
222
|
+
kailash/nodes/data/async_sql.py,sha256=34jUqCiUaNMVynCSteTIT8mZUs2SanZoKdsyyyZf51E,191567
|
224
223
|
kailash/nodes/data/async_vector.py,sha256=HtwQLO25IXu8Vq80qzU8rMkUAKPQ2qM0x8YxjXHlygU,21005
|
225
224
|
kailash/nodes/data/bulk_operations.py,sha256=WVopmosVkIlweFxVt3boLdCPc93EqpYyQ1Ez9mCIt0c,34453
|
226
225
|
kailash/nodes/data/directory.py,sha256=fbfLqD_ijRubk-4xew3604QntPsyDxqaF4k6TpfyjDg,9923
|
@@ -399,7 +398,7 @@ kailash/visualization/reports.py,sha256=D7kJ0flHr16d-qSEq8vnw20N8u_dgTrXtKVSXVm8
|
|
399
398
|
kailash/workflow/__init__.py,sha256=DDQDE9K6RmbX6479guNLLgjiVVV-gQERRvCEJWSVlsM,1836
|
400
399
|
kailash/workflow/async_builder.py,sha256=iv8bDJHdWAUZ77SyMo6sucd92dTdtXesdxycrSE7mM4,20613
|
401
400
|
kailash/workflow/async_patterns.py,sha256=X0ZDXwr6UAu0WC1xnCB7-0V1-tRbKs9UI4JqaBCB6tE,22824
|
402
|
-
kailash/workflow/builder.py,sha256=
|
401
|
+
kailash/workflow/builder.py,sha256=mHWfoHsq4W5DvJmv3b_vMDeVXqqpNHXO6U3Kuvk54tI,51079
|
403
402
|
kailash/workflow/contracts.py,sha256=Uch-s2SC-NYrg0n2zgljgkyFHf4bufY4OydFuIfAk7E,13442
|
404
403
|
kailash/workflow/convergence.py,sha256=vfIDR-uNaQE-LVUEzrRtfgKPgX9gL0nLNH-nTg5ra-c,10031
|
405
404
|
kailash/workflow/cycle_analyzer.py,sha256=BGBpgdB-g0-KRI65sVAvHV4lxfoCzMt4uKOHbw8GXT4,32596
|
@@ -420,14 +419,14 @@ kailash/workflow/resilience.py,sha256=Ecef4gBg-QWP369a_xfzQnVWhHryvEcO2RSFVSriLJ
|
|
420
419
|
kailash/workflow/runner.py,sha256=l6jb-H7DwbRlvQ3H3SuTs70rut-u7H3Gi8nybKCEjZU,10795
|
421
420
|
kailash/workflow/safety.py,sha256=pS5GKu7UdkzFZcb16Dn-0jBxjULDU-59_M0CbUVMVyw,11298
|
422
421
|
kailash/workflow/state.py,sha256=UTZxs5-Ona6uvBhx1__i6-RX8gB4qazkBIWE7uyRmWQ,7600
|
423
|
-
kailash/workflow/templates.py,sha256=
|
422
|
+
kailash/workflow/templates.py,sha256=aZQzEPQD368nN0x0ICQlRKmAr2FqTxIOUa-7rb7EUWI,48578
|
424
423
|
kailash/workflow/type_inference.py,sha256=i1F7Yd_Z3elTXrthsLpqGbOnQBIVVVEjhRpI0HrIjd0,24492
|
425
424
|
kailash/workflow/validation.py,sha256=LdbIPQSokCqSLfWTBhJR82pa_0va44pcVu9dpEM4rvY,45177
|
426
425
|
kailash/workflow/visualization.py,sha256=nHBW-Ai8QBMZtn2Nf3EE1_aiMGi9S6Ui_BfpA5KbJPU,23187
|
427
|
-
kailash-0.9.
|
428
|
-
kailash-0.9.
|
429
|
-
kailash-0.9.
|
430
|
-
kailash-0.9.
|
431
|
-
kailash-0.9.
|
432
|
-
kailash-0.9.
|
433
|
-
kailash-0.9.
|
426
|
+
kailash-0.9.21.dist-info/licenses/LICENSE,sha256=9GYZHXVUmx6FdFRNzOeE_w7a_aEGeYbqTVmFtJlrbGk,13438
|
427
|
+
kailash-0.9.21.dist-info/licenses/NOTICE,sha256=9ssIK4LcHSTFqriXGdteMpBPTS1rSLlYtjppZ_bsjZ0,723
|
428
|
+
kailash-0.9.21.dist-info/METADATA,sha256=LfBFe4bjx6PGjbDuUUawKQhpMJ57QlbRDWEY7oUkuIk,24027
|
429
|
+
kailash-0.9.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
430
|
+
kailash-0.9.21.dist-info/entry_points.txt,sha256=M_q3b8PG5W4XbhSgESzIJjh3_4OBKtZFYFsOdkr2vO4,45
|
431
|
+
kailash-0.9.21.dist-info/top_level.txt,sha256=z7GzH2mxl66498pVf5HKwo5wwfPtt9Aq95uZUpH6JV0,8
|
432
|
+
kailash-0.9.21.dist-info/RECORD,,
|