kailash 0.9.20__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/nodes/data/async_sql.py +76 -10
- {kailash-0.9.20.dist-info → kailash-0.9.21.dist-info}/METADATA +10 -3
- {kailash-0.9.20.dist-info → kailash-0.9.21.dist-info}/RECORD +9 -9
- {kailash-0.9.20.dist-info → kailash-0.9.21.dist-info}/WHEEL +0 -0
- {kailash-0.9.20.dist-info → kailash-0.9.21.dist-info}/entry_points.txt +0 -0
- {kailash-0.9.20.dist-info → kailash-0.9.21.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.9.20.dist-info → kailash-0.9.21.dist-info}/licenses/NOTICE +0 -0
- {kailash-0.9.20.dist-info → kailash-0.9.21.dist-info}/top_level.txt +0 -0
kailash/__init__.py
CHANGED
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!"""
|
@@ -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
|
@@ -122,11 +122,18 @@ Dynamic: requires-python
|
|
122
122
|
|
123
123
|
---
|
124
124
|
|
125
|
-
## 🔥 Latest Release: v0.9.
|
125
|
+
## 🔥 Latest Release: v0.9.21 (October 8, 2025)
|
126
126
|
|
127
|
-
**
|
127
|
+
**AsyncSQL Event Loop Isolation Fix**
|
128
128
|
|
129
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
|
135
|
+
|
136
|
+
### Previous Release: v0.9.20 (October 6, 2025)
|
130
137
|
- **Mock Provider Bypass**: Removed hardcoded `if provider == "mock"` logic from LLMAgentNode
|
131
138
|
- **Tool Execution Flow**: Unified provider response generation for all providers
|
132
139
|
- **Provider Registry**: All providers now use consistent registry path
|
@@ -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
|
@@ -219,7 +219,7 @@ kailash/nodes/compliance/data_retention.py,sha256=90bH_eGwlcDzUdklAJeXQM-RcuLUGQ
|
|
219
219
|
kailash/nodes/compliance/gdpr.py,sha256=ZMoHZjAo4QtGwtFCzGMrAUBFV3TbZOnJ5DZGZS87Bas,70548
|
220
220
|
kailash/nodes/data/__init__.py,sha256=f0h4ysvXxlyFcNJLvDyXrgJ0ixwDF1cS0pJ2QNPakhg,5213
|
221
221
|
kailash/nodes/data/async_connection.py,sha256=wfArHs9svU48bxGZIiixSV2YVn9cukNgEjagwTRu6J4,17250
|
222
|
-
kailash/nodes/data/async_sql.py,sha256=
|
222
|
+
kailash/nodes/data/async_sql.py,sha256=34jUqCiUaNMVynCSteTIT8mZUs2SanZoKdsyyyZf51E,191567
|
223
223
|
kailash/nodes/data/async_vector.py,sha256=HtwQLO25IXu8Vq80qzU8rMkUAKPQ2qM0x8YxjXHlygU,21005
|
224
224
|
kailash/nodes/data/bulk_operations.py,sha256=WVopmosVkIlweFxVt3boLdCPc93EqpYyQ1Ez9mCIt0c,34453
|
225
225
|
kailash/nodes/data/directory.py,sha256=fbfLqD_ijRubk-4xew3604QntPsyDxqaF4k6TpfyjDg,9923
|
@@ -423,10 +423,10 @@ kailash/workflow/templates.py,sha256=aZQzEPQD368nN0x0ICQlRKmAr2FqTxIOUa-7rb7EUWI
|
|
423
423
|
kailash/workflow/type_inference.py,sha256=i1F7Yd_Z3elTXrthsLpqGbOnQBIVVVEjhRpI0HrIjd0,24492
|
424
424
|
kailash/workflow/validation.py,sha256=LdbIPQSokCqSLfWTBhJR82pa_0va44pcVu9dpEM4rvY,45177
|
425
425
|
kailash/workflow/visualization.py,sha256=nHBW-Ai8QBMZtn2Nf3EE1_aiMGi9S6Ui_BfpA5KbJPU,23187
|
426
|
-
kailash-0.9.
|
427
|
-
kailash-0.9.
|
428
|
-
kailash-0.9.
|
429
|
-
kailash-0.9.
|
430
|
-
kailash-0.9.
|
431
|
-
kailash-0.9.
|
432
|
-
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,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|