kailash 0.4.2__py3-none-any.whl → 0.6.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.
Files changed (64) hide show
  1. kailash/__init__.py +1 -1
  2. kailash/client/__init__.py +12 -0
  3. kailash/client/enhanced_client.py +306 -0
  4. kailash/core/actors/__init__.py +16 -0
  5. kailash/core/actors/connection_actor.py +566 -0
  6. kailash/core/actors/supervisor.py +364 -0
  7. kailash/edge/__init__.py +16 -0
  8. kailash/edge/compliance.py +834 -0
  9. kailash/edge/discovery.py +659 -0
  10. kailash/edge/location.py +582 -0
  11. kailash/gateway/__init__.py +33 -0
  12. kailash/gateway/api.py +289 -0
  13. kailash/gateway/enhanced_gateway.py +357 -0
  14. kailash/gateway/resource_resolver.py +217 -0
  15. kailash/gateway/security.py +227 -0
  16. kailash/middleware/auth/models.py +2 -2
  17. kailash/middleware/database/base_models.py +1 -7
  18. kailash/middleware/database/repositories.py +3 -1
  19. kailash/middleware/gateway/__init__.py +22 -0
  20. kailash/middleware/gateway/checkpoint_manager.py +398 -0
  21. kailash/middleware/gateway/deduplicator.py +382 -0
  22. kailash/middleware/gateway/durable_gateway.py +417 -0
  23. kailash/middleware/gateway/durable_request.py +498 -0
  24. kailash/middleware/gateway/event_store.py +459 -0
  25. kailash/nodes/admin/audit_log.py +364 -6
  26. kailash/nodes/admin/permission_check.py +817 -33
  27. kailash/nodes/admin/role_management.py +1242 -108
  28. kailash/nodes/admin/schema_manager.py +438 -0
  29. kailash/nodes/admin/user_management.py +1209 -681
  30. kailash/nodes/api/http.py +95 -71
  31. kailash/nodes/base.py +281 -164
  32. kailash/nodes/base_async.py +30 -31
  33. kailash/nodes/code/__init__.py +8 -1
  34. kailash/nodes/code/async_python.py +1035 -0
  35. kailash/nodes/code/python.py +1 -0
  36. kailash/nodes/data/async_sql.py +12 -25
  37. kailash/nodes/data/sql.py +20 -11
  38. kailash/nodes/data/workflow_connection_pool.py +643 -0
  39. kailash/nodes/rag/__init__.py +1 -4
  40. kailash/resources/__init__.py +40 -0
  41. kailash/resources/factory.py +533 -0
  42. kailash/resources/health.py +319 -0
  43. kailash/resources/reference.py +288 -0
  44. kailash/resources/registry.py +392 -0
  45. kailash/runtime/async_local.py +711 -302
  46. kailash/testing/__init__.py +34 -0
  47. kailash/testing/async_test_case.py +353 -0
  48. kailash/testing/async_utils.py +345 -0
  49. kailash/testing/fixtures.py +458 -0
  50. kailash/testing/mock_registry.py +495 -0
  51. kailash/utils/resource_manager.py +420 -0
  52. kailash/workflow/__init__.py +8 -0
  53. kailash/workflow/async_builder.py +621 -0
  54. kailash/workflow/async_patterns.py +766 -0
  55. kailash/workflow/builder.py +93 -10
  56. kailash/workflow/cyclic_runner.py +111 -41
  57. kailash/workflow/graph.py +7 -2
  58. kailash/workflow/resilience.py +11 -1
  59. {kailash-0.4.2.dist-info → kailash-0.6.0.dist-info}/METADATA +12 -7
  60. {kailash-0.4.2.dist-info → kailash-0.6.0.dist-info}/RECORD +64 -28
  61. {kailash-0.4.2.dist-info → kailash-0.6.0.dist-info}/WHEEL +0 -0
  62. {kailash-0.4.2.dist-info → kailash-0.6.0.dist-info}/entry_points.txt +0 -0
  63. {kailash-0.4.2.dist-info → kailash-0.6.0.dist-info}/licenses/LICENSE +0 -0
  64. {kailash-0.4.2.dist-info → kailash-0.6.0.dist-info}/top_level.txt +0 -0
@@ -100,6 +100,7 @@ ALLOWED_MODULES = {
100
100
  "pathlib", # For modern path operations
101
101
  "glob", # For file pattern matching
102
102
  "xml", # For XML processing
103
+ "uuid", # For generating unique identifiers (safe, no I/O)
103
104
  }
104
105
 
105
106
 
@@ -34,7 +34,8 @@ from decimal import Decimal
34
34
  from enum import Enum
35
35
  from typing import Any, AsyncIterator, Optional, Union
36
36
 
37
- from kailash.nodes.base import Node, NodeParameter, register_node
37
+ from kailash.nodes.base import NodeParameter, register_node
38
+ from kailash.nodes.base_async import AsyncNode
38
39
  from kailash.sdk_exceptions import NodeExecutionError, NodeValidationError
39
40
 
40
41
 
@@ -204,16 +205,22 @@ class PostgreSQLAdapter(DatabaseAdapter):
204
205
  query_params.append(value)
205
206
  params = query_params
206
207
 
208
+ # Ensure params is a list/tuple for asyncpg
209
+ if params is None:
210
+ params = []
211
+ elif not isinstance(params, (list, tuple)):
212
+ params = [params]
213
+
207
214
  if fetch_mode == FetchMode.ONE:
208
- row = await conn.fetchrow(query, *(params or []))
215
+ row = await conn.fetchrow(query, *params)
209
216
  return self._convert_row(dict(row)) if row else None
210
217
  elif fetch_mode == FetchMode.ALL:
211
- rows = await conn.fetch(query, *(params or []))
218
+ rows = await conn.fetch(query, *params)
212
219
  return [self._convert_row(dict(row)) for row in rows]
213
220
  elif fetch_mode == FetchMode.MANY:
214
221
  if not fetch_size:
215
222
  raise ValueError("fetch_size required for MANY mode")
216
- rows = await conn.fetch(query, *(params or []))
223
+ rows = await conn.fetch(query, *params)
217
224
  return [self._convert_row(dict(row)) for row in rows[:fetch_size]]
218
225
  elif fetch_mode == FetchMode.ITERATOR:
219
226
  raise NotImplementedError("Iterator mode not yet implemented")
@@ -425,7 +432,7 @@ class SQLiteAdapter(DatabaseAdapter):
425
432
 
426
433
 
427
434
  @register_node()
428
- class AsyncSQLDatabaseNode(Node):
435
+ class AsyncSQLDatabaseNode(AsyncNode):
429
436
  """Asynchronous SQL database node for high-concurrency database operations.
430
437
 
431
438
  This node provides non-blocking database operations with connection pooling,
@@ -713,26 +720,6 @@ class AsyncSQLDatabaseNode(Node):
713
720
  except Exception as e:
714
721
  raise NodeExecutionError(f"Database query failed: {str(e)}")
715
722
 
716
- def run(self, **inputs) -> dict[str, Any]:
717
- """Synchronous run method - delegates to async_run."""
718
- import asyncio
719
-
720
- import nest_asyncio
721
-
722
- try:
723
- # Check if we're already in an event loop
724
- loop = asyncio.get_running_loop()
725
-
726
- # Apply nest_asyncio to allow nested event loops
727
- nest_asyncio.apply()
728
-
729
- # Now we can safely run even in an existing event loop
730
- return asyncio.run(self.async_run(**inputs))
731
-
732
- except RuntimeError:
733
- # No event loop running, we can use asyncio.run() directly
734
- return asyncio.run(self.async_run(**inputs))
735
-
736
723
  async def process(self, inputs: dict[str, Any]) -> dict[str, Any]:
737
724
  """Async process method for middleware compatibility."""
738
725
  return await self.async_run(**inputs)
kailash/nodes/data/sql.py CHANGED
@@ -288,10 +288,10 @@ class SQLDatabaseNode(Node):
288
288
  ),
289
289
  "parameters": NodeParameter(
290
290
  name="parameters",
291
- type=list,
291
+ type=Any, # Allow both list and dict for parameters
292
292
  required=False,
293
- default=[],
294
- description="Query parameters for parameterized queries",
293
+ default=None,
294
+ description="Query parameters for parameterized queries (list for positional, dict for named)",
295
295
  ),
296
296
  "result_format": NodeParameter(
297
297
  name="result_format",
@@ -367,7 +367,7 @@ class SQLDatabaseNode(Node):
367
367
  """
368
368
  # Extract validated inputs
369
369
  query = kwargs.get("query")
370
- parameters = kwargs.get("parameters", [])
370
+ parameters = kwargs.get("parameters")
371
371
  result_format = kwargs.get("result_format", "dict")
372
372
  user_context = kwargs.get("user_context")
373
373
 
@@ -757,19 +757,28 @@ class SQLDatabaseNode(Node):
757
757
 
758
758
  import re
759
759
 
760
- # Remove potential passwords from error messages
760
+ # Remove potential passwords from error messages, but be more selective
761
761
  patterns_to_mask = [
762
762
  # Connection string passwords
763
763
  (r"://[^:]+:[^@]+@", "://***:***@"),
764
- # SQL query content (in some error messages)
765
- (r"'[^']*'", "'***'"),
766
- # Quoted strings that might contain sensitive data
767
- (r'"[^"]*"', '"***"'),
764
+ # Password fields in SQL (case insensitive)
765
+ (r"password\s*=\s*['\"][^'\"]*['\"]", "password='***'", re.IGNORECASE),
766
+ # API keys and tokens in SQL
767
+ (
768
+ r"(api_key|token|secret)\s*=\s*['\"][^'\"]*['\"]",
769
+ r"\1='***'",
770
+ re.IGNORECASE,
771
+ ),
768
772
  ]
769
773
 
770
774
  sanitized = error_message
771
- for pattern, replacement in patterns_to_mask:
772
- sanitized = re.sub(pattern, replacement, sanitized)
775
+ for pattern_info in patterns_to_mask:
776
+ if len(pattern_info) == 3:
777
+ pattern, replacement, flags = pattern_info
778
+ sanitized = re.sub(pattern, replacement, sanitized, flags=flags)
779
+ else:
780
+ pattern, replacement = pattern_info
781
+ sanitized = re.sub(pattern, replacement, sanitized)
773
782
 
774
783
  return sanitized
775
784