kailash 0.3.0__py3-none-any.whl → 0.3.1__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 (114) hide show
  1. kailash/__init__.py +1 -1
  2. kailash/access_control.py +40 -39
  3. kailash/api/auth.py +26 -32
  4. kailash/api/custom_nodes.py +29 -29
  5. kailash/api/custom_nodes_secure.py +35 -35
  6. kailash/api/database.py +17 -17
  7. kailash/api/gateway.py +19 -19
  8. kailash/api/mcp_integration.py +24 -23
  9. kailash/api/studio.py +45 -45
  10. kailash/api/workflow_api.py +8 -8
  11. kailash/cli/commands.py +5 -8
  12. kailash/manifest.py +42 -42
  13. kailash/mcp/__init__.py +1 -1
  14. kailash/mcp/ai_registry_server.py +20 -20
  15. kailash/mcp/client.py +9 -11
  16. kailash/mcp/client_new.py +10 -10
  17. kailash/mcp/server.py +1 -2
  18. kailash/mcp/server_enhanced.py +449 -0
  19. kailash/mcp/servers/ai_registry.py +6 -6
  20. kailash/mcp/utils/__init__.py +31 -0
  21. kailash/mcp/utils/cache.py +267 -0
  22. kailash/mcp/utils/config.py +263 -0
  23. kailash/mcp/utils/formatters.py +293 -0
  24. kailash/mcp/utils/metrics.py +418 -0
  25. kailash/nodes/ai/agents.py +9 -9
  26. kailash/nodes/ai/ai_providers.py +33 -34
  27. kailash/nodes/ai/embedding_generator.py +31 -32
  28. kailash/nodes/ai/intelligent_agent_orchestrator.py +62 -66
  29. kailash/nodes/ai/iterative_llm_agent.py +48 -48
  30. kailash/nodes/ai/llm_agent.py +32 -33
  31. kailash/nodes/ai/models.py +13 -13
  32. kailash/nodes/ai/self_organizing.py +44 -44
  33. kailash/nodes/api/auth.py +11 -11
  34. kailash/nodes/api/graphql.py +13 -13
  35. kailash/nodes/api/http.py +19 -19
  36. kailash/nodes/api/monitoring.py +20 -20
  37. kailash/nodes/api/rate_limiting.py +9 -13
  38. kailash/nodes/api/rest.py +29 -29
  39. kailash/nodes/api/security.py +44 -47
  40. kailash/nodes/base.py +21 -23
  41. kailash/nodes/base_async.py +7 -7
  42. kailash/nodes/base_cycle_aware.py +12 -12
  43. kailash/nodes/base_with_acl.py +5 -5
  44. kailash/nodes/code/python.py +56 -55
  45. kailash/nodes/data/directory.py +6 -6
  46. kailash/nodes/data/event_generation.py +10 -10
  47. kailash/nodes/data/file_discovery.py +28 -31
  48. kailash/nodes/data/readers.py +8 -8
  49. kailash/nodes/data/retrieval.py +10 -10
  50. kailash/nodes/data/sharepoint_graph.py +17 -17
  51. kailash/nodes/data/sources.py +5 -5
  52. kailash/nodes/data/sql.py +13 -13
  53. kailash/nodes/data/streaming.py +25 -25
  54. kailash/nodes/data/vector_db.py +22 -22
  55. kailash/nodes/data/writers.py +7 -7
  56. kailash/nodes/logic/async_operations.py +17 -17
  57. kailash/nodes/logic/convergence.py +11 -11
  58. kailash/nodes/logic/loop.py +4 -4
  59. kailash/nodes/logic/operations.py +11 -11
  60. kailash/nodes/logic/workflow.py +8 -9
  61. kailash/nodes/mixins/mcp.py +17 -17
  62. kailash/nodes/mixins.py +8 -10
  63. kailash/nodes/transform/chunkers.py +3 -3
  64. kailash/nodes/transform/formatters.py +7 -7
  65. kailash/nodes/transform/processors.py +10 -10
  66. kailash/runtime/access_controlled.py +18 -18
  67. kailash/runtime/async_local.py +17 -19
  68. kailash/runtime/docker.py +20 -22
  69. kailash/runtime/local.py +16 -16
  70. kailash/runtime/parallel.py +23 -23
  71. kailash/runtime/parallel_cyclic.py +27 -27
  72. kailash/runtime/runner.py +6 -6
  73. kailash/runtime/testing.py +20 -20
  74. kailash/sdk_exceptions.py +0 -58
  75. kailash/security.py +14 -26
  76. kailash/tracking/manager.py +38 -38
  77. kailash/tracking/metrics_collector.py +15 -14
  78. kailash/tracking/models.py +53 -53
  79. kailash/tracking/storage/base.py +7 -17
  80. kailash/tracking/storage/database.py +22 -23
  81. kailash/tracking/storage/filesystem.py +38 -40
  82. kailash/utils/export.py +21 -21
  83. kailash/utils/templates.py +2 -3
  84. kailash/visualization/api.py +30 -34
  85. kailash/visualization/dashboard.py +17 -17
  86. kailash/visualization/performance.py +16 -16
  87. kailash/visualization/reports.py +25 -27
  88. kailash/workflow/builder.py +8 -8
  89. kailash/workflow/convergence.py +13 -12
  90. kailash/workflow/cycle_analyzer.py +30 -32
  91. kailash/workflow/cycle_builder.py +12 -12
  92. kailash/workflow/cycle_config.py +16 -15
  93. kailash/workflow/cycle_debugger.py +40 -40
  94. kailash/workflow/cycle_exceptions.py +29 -29
  95. kailash/workflow/cycle_profiler.py +21 -21
  96. kailash/workflow/cycle_state.py +20 -22
  97. kailash/workflow/cyclic_runner.py +44 -44
  98. kailash/workflow/graph.py +40 -40
  99. kailash/workflow/mermaid_visualizer.py +9 -11
  100. kailash/workflow/migration.py +22 -22
  101. kailash/workflow/mock_registry.py +6 -6
  102. kailash/workflow/runner.py +9 -9
  103. kailash/workflow/safety.py +12 -13
  104. kailash/workflow/state.py +8 -11
  105. kailash/workflow/templates.py +19 -19
  106. kailash/workflow/validation.py +14 -14
  107. kailash/workflow/visualization.py +22 -22
  108. {kailash-0.3.0.dist-info → kailash-0.3.1.dist-info}/METADATA +53 -5
  109. kailash-0.3.1.dist-info/RECORD +136 -0
  110. kailash-0.3.0.dist-info/RECORD +0 -130
  111. {kailash-0.3.0.dist-info → kailash-0.3.1.dist-info}/WHEEL +0 -0
  112. {kailash-0.3.0.dist-info → kailash-0.3.1.dist-info}/entry_points.txt +0 -0
  113. {kailash-0.3.0.dist-info → kailash-0.3.1.dist-info}/licenses/LICENSE +0 -0
  114. {kailash-0.3.0.dist-info → kailash-0.3.1.dist-info}/top_level.txt +0 -0
@@ -44,7 +44,7 @@ Example:
44
44
  """
45
45
 
46
46
  import time
47
- from typing import Any, Dict, List
47
+ from typing import Any
48
48
 
49
49
  from kailash.nodes.base import Node, NodeMetadata, NodeParameter, register_node
50
50
  from kailash.sdk_exceptions import NodeConfigurationError, NodeExecutionError
@@ -148,7 +148,7 @@ class KafkaConsumerNode(Node):
148
148
  self._consumer = None
149
149
  self._topic = None
150
150
 
151
- def get_parameters(self) -> Dict[str, NodeParameter]:
151
+ def get_parameters(self) -> dict[str, NodeParameter]:
152
152
  """Define parameters for the Kafka consumer node."""
153
153
  return {
154
154
  "bootstrap_servers": NodeParameter(
@@ -206,7 +206,7 @@ class KafkaConsumerNode(Node):
206
206
  ),
207
207
  }
208
208
 
209
- def configure(self, config: Dict[str, Any]) -> None:
209
+ def configure(self, config: dict[str, Any]) -> None:
210
210
  """Configure the Kafka consumer.
211
211
 
212
212
  Creates and configures the Kafka consumer with the specified
@@ -241,7 +241,7 @@ class KafkaConsumerNode(Node):
241
241
  self._consumer = f"kafka_consumer_{self.config['group_id']}"
242
242
  self._topic = self.config["topic"]
243
243
 
244
- def run(self, **kwargs) -> Dict[str, Any]:
244
+ def run(self, **kwargs) -> dict[str, Any]:
245
245
  """Consume messages from Kafka.
246
246
 
247
247
  Implementation of the abstract run method from the base Node class.
@@ -254,7 +254,7 @@ class KafkaConsumerNode(Node):
254
254
  """
255
255
  return self.execute(kwargs)
256
256
 
257
- def execute(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
257
+ def execute(self, inputs: dict[str, Any]) -> dict[str, Any]:
258
258
  """Consume messages from Kafka.
259
259
 
260
260
  Polls for messages up to the specified limit or timeout.
@@ -286,7 +286,7 @@ class KafkaConsumerNode(Node):
286
286
  except Exception as e:
287
287
  raise NodeExecutionError(f"Failed to consume messages: {str(e)}")
288
288
 
289
- def _consume_messages(self, max_messages: int, timeout_ms: int) -> List[Dict]:
289
+ def _consume_messages(self, max_messages: int, timeout_ms: int) -> list[dict]:
290
290
  """Consume messages from Kafka.
291
291
 
292
292
  This is a placeholder for actual message consumption logic.
@@ -420,7 +420,7 @@ class StreamPublisherNode(Node):
420
420
  self._publisher = None
421
421
  self._protocol = None
422
422
 
423
- def get_parameters(self) -> Dict[str, NodeParameter]:
423
+ def get_parameters(self) -> dict[str, NodeParameter]:
424
424
  """Define parameters for the stream publisher node."""
425
425
  return {
426
426
  "protocol": NodeParameter(
@@ -465,7 +465,7 @@ class StreamPublisherNode(Node):
465
465
  ),
466
466
  }
467
467
 
468
- def configure(self, config: Dict[str, Any]) -> None:
468
+ def configure(self, config: dict[str, Any]) -> None:
469
469
  """Configure the stream publisher.
470
470
 
471
471
  Creates the appropriate publisher based on the protocol.
@@ -499,7 +499,7 @@ class StreamPublisherNode(Node):
499
499
  # Placeholder for actual publisher creation
500
500
  self._publisher = f"{self._protocol}_publisher"
501
501
 
502
- def run(self, **kwargs) -> Dict[str, Any]:
502
+ def run(self, **kwargs) -> dict[str, Any]:
503
503
  """Publish messages to the streaming platform.
504
504
 
505
505
  Implementation of the abstract run method from the base Node class.
@@ -512,7 +512,7 @@ class StreamPublisherNode(Node):
512
512
  """
513
513
  return self.execute(kwargs)
514
514
 
515
- def execute(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
515
+ def execute(self, inputs: dict[str, Any]) -> dict[str, Any]:
516
516
  """Publish messages to the streaming platform.
517
517
 
518
518
  Args:
@@ -541,7 +541,7 @@ class StreamPublisherNode(Node):
541
541
  except Exception as e:
542
542
  raise NodeExecutionError(f"Failed to publish messages: {str(e)}")
543
543
 
544
- def _publish_messages(self, messages: List[Dict], headers: Dict) -> Dict[str, Any]:
544
+ def _publish_messages(self, messages: list[dict], headers: dict) -> dict[str, Any]:
545
545
  """Publish messages to the stream.
546
546
 
547
547
  This is a placeholder for actual publishing logic.
@@ -683,7 +683,7 @@ class WebSocketNode(Node):
683
683
  self._connected = False
684
684
  self._message_queue = []
685
685
 
686
- def get_parameters(self) -> Dict[str, NodeParameter]:
686
+ def get_parameters(self) -> dict[str, NodeParameter]:
687
687
  """Get the parameters for this node.
688
688
 
689
689
  Returns:
@@ -750,7 +750,7 @@ class WebSocketNode(Node):
750
750
  ),
751
751
  }
752
752
 
753
- def configure(self, config: Dict[str, Any]) -> None:
753
+ def configure(self, config: dict[str, Any]) -> None:
754
754
  """Configure the WebSocket connection.
755
755
 
756
756
  Validates the URL and prepares connection parameters.
@@ -770,7 +770,7 @@ class WebSocketNode(Node):
770
770
  if not url.startswith(("ws://", "wss://")):
771
771
  raise NodeConfigurationError("URL must start with ws:// or wss://")
772
772
 
773
- def run(self, **kwargs) -> Dict[str, Any]:
773
+ def run(self, **kwargs) -> dict[str, Any]:
774
774
  """Run the WebSocket node.
775
775
 
776
776
  This method fulfills the abstract run method requirement from the base Node class.
@@ -786,7 +786,7 @@ class WebSocketNode(Node):
786
786
  """
787
787
  return self.execute(kwargs)
788
788
 
789
- def execute(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
789
+ def execute(self, inputs: dict[str, Any]) -> dict[str, Any]:
790
790
  """Execute WebSocket operations.
791
791
 
792
792
  Performs the requested action (connect, send, receive, disconnect).
@@ -816,7 +816,7 @@ class WebSocketNode(Node):
816
816
  except Exception as e:
817
817
  raise NodeExecutionError(f"WebSocket operation failed: {str(e)}")
818
818
 
819
- def _connect(self) -> Dict[str, Any]:
819
+ def _connect(self) -> dict[str, Any]:
820
820
  """Connect to the WebSocket server.
821
821
 
822
822
  Returns:
@@ -832,7 +832,7 @@ class WebSocketNode(Node):
832
832
  "metadata": {"timestamp": time.time()},
833
833
  }
834
834
 
835
- def _send_message(self, message: Any) -> Dict[str, Any]:
835
+ def _send_message(self, message: Any) -> dict[str, Any]:
836
836
  """Send a message through the WebSocket.
837
837
 
838
838
  Args:
@@ -851,7 +851,7 @@ class WebSocketNode(Node):
851
851
  "metadata": {"timestamp": time.time()},
852
852
  }
853
853
 
854
- def _receive_messages(self, timeout: float) -> Dict[str, Any]:
854
+ def _receive_messages(self, timeout: float) -> dict[str, Any]:
855
855
  """Receive messages from the WebSocket.
856
856
 
857
857
  Args:
@@ -877,7 +877,7 @@ class WebSocketNode(Node):
877
877
  "metadata": {"count": len(messages), "timeout": timeout},
878
878
  }
879
879
 
880
- def _disconnect(self) -> Dict[str, Any]:
880
+ def _disconnect(self) -> dict[str, Any]:
881
881
  """Disconnect from the WebSocket server.
882
882
 
883
883
  Returns:
@@ -994,7 +994,7 @@ class EventStreamNode(Node):
994
994
  self._connected = False
995
995
  self._last_event_id = None
996
996
 
997
- def get_parameters(self) -> Dict[str, NodeParameter]:
997
+ def get_parameters(self) -> dict[str, NodeParameter]:
998
998
  """Get the parameters for this node.
999
999
 
1000
1000
  Returns:
@@ -1048,7 +1048,7 @@ class EventStreamNode(Node):
1048
1048
  ),
1049
1049
  }
1050
1050
 
1051
- def run(self, **kwargs) -> Dict[str, Any]:
1051
+ def run(self, **kwargs) -> dict[str, Any]:
1052
1052
  """Run the EventStream node.
1053
1053
 
1054
1054
  This method fulfills the abstract run method requirement from the base Node class.
@@ -1064,7 +1064,7 @@ class EventStreamNode(Node):
1064
1064
  """
1065
1065
  return self.execute(kwargs)
1066
1066
 
1067
- def execute(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
1067
+ def execute(self, inputs: dict[str, Any]) -> dict[str, Any]:
1068
1068
  """Execute EventStream operations.
1069
1069
 
1070
1070
  Args:
@@ -1090,7 +1090,7 @@ class EventStreamNode(Node):
1090
1090
  except Exception as e:
1091
1091
  raise NodeExecutionError(f"EventStream operation failed: {str(e)}")
1092
1092
 
1093
- def _start_stream(self) -> Dict[str, Any]:
1093
+ def _start_stream(self) -> dict[str, Any]:
1094
1094
  """Start the event stream connection.
1095
1095
 
1096
1096
  Returns:
@@ -1106,7 +1106,7 @@ class EventStreamNode(Node):
1106
1106
  "metadata": {"timestamp": time.time()},
1107
1107
  }
1108
1108
 
1109
- def _stop_stream(self) -> Dict[str, Any]:
1109
+ def _stop_stream(self) -> dict[str, Any]:
1110
1110
  """Stop the event stream connection.
1111
1111
 
1112
1112
  Returns:
@@ -1124,7 +1124,7 @@ class EventStreamNode(Node):
1124
1124
  },
1125
1125
  }
1126
1126
 
1127
- def _receive_events(self, max_events: int) -> Dict[str, Any]:
1127
+ def _receive_events(self, max_events: int) -> dict[str, Any]:
1128
1128
  """Receive events from the stream.
1129
1129
 
1130
1130
  Args:
@@ -44,7 +44,7 @@ Example:
44
44
  ... })
45
45
  """
46
46
 
47
- from typing import Any, Dict, List
47
+ from typing import Any
48
48
 
49
49
  import numpy as np
50
50
 
@@ -142,7 +142,7 @@ class EmbeddingNode(Node):
142
142
  self._model = None
143
143
  self._model_info = {}
144
144
 
145
- def get_parameters(self) -> Dict[str, NodeParameter]:
145
+ def get_parameters(self) -> dict[str, NodeParameter]:
146
146
  """Define parameters for the embedding node."""
147
147
  return {
148
148
  "model": NodeParameter(
@@ -188,7 +188,7 @@ class EmbeddingNode(Node):
188
188
  ),
189
189
  }
190
190
 
191
- def configure(self, config: Dict[str, Any]) -> None:
191
+ def configure(self, config: dict[str, Any]) -> None:
192
192
  """Configure the embedding node with model settings.
193
193
 
194
194
  Validates configuration, initializes the embedding model, and
@@ -241,7 +241,7 @@ class EmbeddingNode(Node):
241
241
  "max_tokens": self.config.get("max_tokens", 8192),
242
242
  }
243
243
 
244
- def run(self, **kwargs) -> Dict[str, Any]:
244
+ def run(self, **kwargs) -> dict[str, Any]:
245
245
  """Generate embeddings for input texts.
246
246
 
247
247
  Implementation of the abstract run method from the base Node class.
@@ -254,7 +254,7 @@ class EmbeddingNode(Node):
254
254
  """
255
255
  return self.execute(kwargs)
256
256
 
257
- def execute(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
257
+ def execute(self, inputs: dict[str, Any]) -> dict[str, Any]:
258
258
  """Generate embeddings for input texts.
259
259
 
260
260
  Processes the input texts through the configured embedding model,
@@ -295,7 +295,7 @@ class EmbeddingNode(Node):
295
295
  except Exception as e:
296
296
  raise NodeExecutionError(f"Failed to generate embeddings: {str(e)}")
297
297
 
298
- def _generate_embeddings(self, texts: List[str]) -> List[List[float]]:
298
+ def _generate_embeddings(self, texts: list[str]) -> list[list[float]]:
299
299
  """Generate embeddings for a batch of texts.
300
300
 
301
301
  This is a placeholder for actual embedding generation logic.
@@ -310,7 +310,7 @@ class EmbeddingNode(Node):
310
310
  dim = self._model_info.get("dimensions", 768)
311
311
  return [np.random.randn(dim).tolist() for _ in texts]
312
312
 
313
- def _normalize_embeddings(self, embeddings: List[List[float]]) -> List[List[float]]:
313
+ def _normalize_embeddings(self, embeddings: list[list[float]]) -> list[list[float]]:
314
314
  """Normalize embedding vectors to unit length.
315
315
 
316
316
  Normalizes each embedding vector to have a magnitude of 1.0,
@@ -442,7 +442,7 @@ class VectorDatabaseNode(Node):
442
442
  self._client = None
443
443
  self._index = None
444
444
 
445
- def get_parameters(self) -> Dict[str, NodeParameter]:
445
+ def get_parameters(self) -> dict[str, NodeParameter]:
446
446
  """Define parameters for the vector database node."""
447
447
  return {
448
448
  "provider": NodeParameter(
@@ -484,7 +484,7 @@ class VectorDatabaseNode(Node):
484
484
  ),
485
485
  }
486
486
 
487
- def configure(self, config: Dict[str, Any]) -> None:
487
+ def configure(self, config: dict[str, Any]) -> None:
488
488
  """Configure the vector database connection.
489
489
 
490
490
  Establishes connection to the vector database, validates the index,
@@ -529,7 +529,7 @@ class VectorDatabaseNode(Node):
529
529
  self._client = f"{provider}_client"
530
530
  self._index = self.config.get("index_name")
531
531
 
532
- def run(self, **kwargs) -> Dict[str, Any]:
532
+ def run(self, **kwargs) -> dict[str, Any]:
533
533
  """Execute vector database operations.
534
534
 
535
535
  Implementation of the abstract run method from the base Node class.
@@ -542,7 +542,7 @@ class VectorDatabaseNode(Node):
542
542
  """
543
543
  return self.execute(kwargs)
544
544
 
545
- def execute(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
545
+ def execute(self, inputs: dict[str, Any]) -> dict[str, Any]:
546
546
  """Execute vector database operations.
547
547
 
548
548
  Performs the requested operation (upsert, query, delete, fetch)
@@ -573,7 +573,7 @@ class VectorDatabaseNode(Node):
573
573
  except Exception as e:
574
574
  raise NodeExecutionError(f"Vector operation failed: {str(e)}")
575
575
 
576
- def _upsert_vectors(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
576
+ def _upsert_vectors(self, inputs: dict[str, Any]) -> dict[str, Any]:
577
577
  """Insert or update vectors in the database.
578
578
 
579
579
  Args:
@@ -600,7 +600,7 @@ class VectorDatabaseNode(Node):
600
600
  "index": self._index,
601
601
  }
602
602
 
603
- def _query_vectors(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
603
+ def _query_vectors(self, inputs: dict[str, Any]) -> dict[str, Any]:
604
604
  """Query similar vectors from the database.
605
605
 
606
606
  Args:
@@ -631,7 +631,7 @@ class VectorDatabaseNode(Node):
631
631
  "count": min(k, 5),
632
632
  }
633
633
 
634
- def _delete_vectors(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
634
+ def _delete_vectors(self, inputs: dict[str, Any]) -> dict[str, Any]:
635
635
  """Delete vectors from the database.
636
636
 
637
637
  Args:
@@ -648,7 +648,7 @@ class VectorDatabaseNode(Node):
648
648
  # Placeholder for actual deletion
649
649
  return {"operation": "delete", "status": "success", "count": len(ids)}
650
650
 
651
- def _fetch_vectors(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
651
+ def _fetch_vectors(self, inputs: dict[str, Any]) -> dict[str, Any]:
652
652
  """Fetch specific vectors by ID.
653
653
 
654
654
  Args:
@@ -748,7 +748,7 @@ class TextSplitterNode(Node):
748
748
  tags={"text", "processing", "nlp"},
749
749
  )
750
750
 
751
- def get_parameters(self) -> Dict[str, NodeParameter]:
751
+ def get_parameters(self) -> dict[str, NodeParameter]:
752
752
  """Define parameters for the text splitter node."""
753
753
  return {
754
754
  "strategy": NodeParameter(
@@ -788,7 +788,7 @@ class TextSplitterNode(Node):
788
788
  ),
789
789
  }
790
790
 
791
- def run(self, **kwargs) -> Dict[str, Any]:
791
+ def run(self, **kwargs) -> dict[str, Any]:
792
792
  """Split text into chunks using configured strategy.
793
793
 
794
794
  Implementation of the abstract run method from the base Node class.
@@ -801,7 +801,7 @@ class TextSplitterNode(Node):
801
801
  """
802
802
  return self.execute(kwargs)
803
803
 
804
- def execute(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
804
+ def execute(self, inputs: dict[str, Any]) -> dict[str, Any]:
805
805
  """Split text into chunks using configured strategy.
806
806
 
807
807
  Args:
@@ -854,7 +854,7 @@ class TextSplitterNode(Node):
854
854
  except Exception as e:
855
855
  raise NodeExecutionError(f"Text splitting failed: {str(e)}")
856
856
 
857
- def _recursive_split(self, text: str) -> List[str]:
857
+ def _recursive_split(self, text: str) -> list[str]:
858
858
  """Split text recursively using multiple separators.
859
859
 
860
860
  Args:
@@ -878,7 +878,7 @@ class TextSplitterNode(Node):
878
878
 
879
879
  return chunks
880
880
 
881
- def _character_split(self, text: str) -> List[str]:
881
+ def _character_split(self, text: str) -> list[str]:
882
882
  """Split text by character count.
883
883
 
884
884
  Args:
@@ -908,7 +908,7 @@ class TextSplitterNode(Node):
908
908
 
909
909
  return chunks
910
910
 
911
- def _sentence_split(self, text: str) -> List[str]:
911
+ def _sentence_split(self, text: str) -> list[str]:
912
912
  """Split text by sentences.
913
913
 
914
914
  Args:
@@ -936,7 +936,7 @@ class TextSplitterNode(Node):
936
936
 
937
937
  return chunks
938
938
 
939
- def _token_split(self, text: str) -> List[str]:
939
+ def _token_split(self, text: str) -> list[str]:
940
940
  """Split text by token count.
941
941
 
942
942
  Args:
@@ -31,7 +31,7 @@ Downstream Consumers:
31
31
 
32
32
  import csv
33
33
  import json
34
- from typing import Any, Dict
34
+ from typing import Any
35
35
 
36
36
  from kailash.nodes.base import Node, NodeParameter, register_node
37
37
  from kailash.security import safe_open, validate_file_path
@@ -96,7 +96,7 @@ class CSVWriterNode(Node):
96
96
  >>> # result = {'rows_written': 2, 'file_path': 'output.csv'}
97
97
  """
98
98
 
99
- def get_parameters(self) -> Dict[str, NodeParameter]:
99
+ def get_parameters(self) -> dict[str, NodeParameter]:
100
100
  """Define input parameters for CSV writing.
101
101
 
102
102
  Provides comprehensive parameters for flexible CSV output,
@@ -144,7 +144,7 @@ class CSVWriterNode(Node):
144
144
  ),
145
145
  }
146
146
 
147
- def run(self, **kwargs) -> Dict[str, Any]:
147
+ def run(self, **kwargs) -> dict[str, Any]:
148
148
  """Execute CSV writing operation.
149
149
 
150
150
  Intelligently handles different data structures, automatically
@@ -280,7 +280,7 @@ class JSONWriterNode(Node):
280
280
  >>> # result = {'file_path': 'response.json'}
281
281
  """
282
282
 
283
- def get_parameters(self) -> Dict[str, NodeParameter]:
283
+ def get_parameters(self) -> dict[str, NodeParameter]:
284
284
  """Define input parameters for JSON writing.
285
285
 
286
286
  Minimal parameters reflecting JSON's flexibility while
@@ -320,7 +320,7 @@ class JSONWriterNode(Node):
320
320
  ),
321
321
  }
322
322
 
323
- def run(self, **kwargs) -> Dict[str, Any]:
323
+ def run(self, **kwargs) -> dict[str, Any]:
324
324
  """Execute JSON writing operation.
325
325
 
326
326
  Serializes data to JSON format with proper formatting
@@ -431,7 +431,7 @@ class TextWriterNode(Node):
431
431
  >>> # result = {'file_path': 'app.log', 'bytes_written': 25}
432
432
  """
433
433
 
434
- def get_parameters(self) -> Dict[str, NodeParameter]:
434
+ def get_parameters(self) -> dict[str, NodeParameter]:
435
435
  """Define input parameters for text writing.
436
436
 
437
437
  Comprehensive parameters supporting various text writing
@@ -478,7 +478,7 @@ class TextWriterNode(Node):
478
478
  ),
479
479
  }
480
480
 
481
- def run(self, **kwargs) -> Dict[str, Any]:
481
+ def run(self, **kwargs) -> dict[str, Any]:
482
482
  """Execute text writing operation.
483
483
 
484
484
  Writes text to file with specified encoding and mode.
@@ -6,7 +6,7 @@ data processing tasks in workflows.
6
6
  """
7
7
 
8
8
  import asyncio
9
- from typing import Any, Dict, List, Optional
9
+ from typing import Any
10
10
 
11
11
  from kailash.nodes.base import NodeParameter, register_node
12
12
  from kailash.nodes.base_async import AsyncNode
@@ -53,7 +53,7 @@ class AsyncMergeNode(AsyncNode):
53
53
  [1, 2, 3, 4]
54
54
  """
55
55
 
56
- def get_parameters(self) -> Dict[str, NodeParameter]:
56
+ def get_parameters(self) -> dict[str, NodeParameter]:
57
57
  """Define parameters for the AsyncMergeNode."""
58
58
  # Reuse parameters from SyncMerge
59
59
  return {
@@ -116,7 +116,7 @@ class AsyncMergeNode(AsyncNode):
116
116
  ),
117
117
  }
118
118
 
119
- def get_output_schema(self) -> Dict[str, NodeParameter]:
119
+ def get_output_schema(self) -> dict[str, NodeParameter]:
120
120
  """Define the output schema for AsyncMergeNode."""
121
121
  return {
122
122
  "merged_data": NodeParameter(
@@ -127,7 +127,7 @@ class AsyncMergeNode(AsyncNode):
127
127
  )
128
128
  }
129
129
 
130
- async def async_run(self, **kwargs) -> Dict[str, Any]:
130
+ async def async_run(self, **kwargs) -> dict[str, Any]:
131
131
  """Asynchronously execute the merge operation.
132
132
 
133
133
  This implementation provides efficient processing for large datasets by:
@@ -199,7 +199,7 @@ class AsyncMergeNode(AsyncNode):
199
199
 
200
200
  return {"merged_data": result}
201
201
 
202
- def run(self, **kwargs) -> Dict[str, Any]:
202
+ def run(self, **kwargs) -> dict[str, Any]:
203
203
  """Synchronous execution method that delegates to the async implementation.
204
204
 
205
205
  This method is required by the Node abstract base class but shouldn't
@@ -220,7 +220,7 @@ class AsyncMergeNode(AsyncNode):
220
220
  "AsyncMergeNode.run() was called directly. Use execute() or execute_async() instead."
221
221
  )
222
222
 
223
- async def _async_concat(self, data_inputs: List[Any], chunk_size: int) -> Any:
223
+ async def _async_concat(self, data_inputs: list[Any], chunk_size: int) -> Any:
224
224
  """Asynchronously concatenate data.
225
225
 
226
226
  Args:
@@ -254,7 +254,7 @@ class AsyncMergeNode(AsyncNode):
254
254
 
255
255
  return result
256
256
 
257
- async def _async_zip(self, data_inputs: List[Any]) -> List[tuple]:
257
+ async def _async_zip(self, data_inputs: list[Any]) -> list[tuple]:
258
258
  """Asynchronously zip data.
259
259
 
260
260
  Args:
@@ -275,10 +275,10 @@ class AsyncMergeNode(AsyncNode):
275
275
  await asyncio.sleep(0.005)
276
276
 
277
277
  # Zip the lists together
278
- return list(zip(*normalized_inputs))
278
+ return list(zip(*normalized_inputs, strict=False))
279
279
 
280
280
  async def _async_merge_dict(
281
- self, data_inputs: List[Any], key: Optional[str], chunk_size: int
281
+ self, data_inputs: list[Any], key: str | None, chunk_size: int
282
282
  ) -> Any:
283
283
  """Asynchronously merge dictionaries.
284
284
 
@@ -326,7 +326,7 @@ class AsyncMergeNode(AsyncNode):
326
326
  )
327
327
 
328
328
  async def _merge_dict_chunk(
329
- self, result: List[dict], data: List[dict], key: str
329
+ self, result: list[dict], data: list[dict], key: str
330
330
  ) -> None:
331
331
  """Merge a chunk of dictionaries into the result list.
332
332
 
@@ -405,7 +405,7 @@ class AsyncSwitchNode(AsyncNode):
405
405
  {'priority': 'high', 'task': 'urgent'}
406
406
  """
407
407
 
408
- def get_parameters(self) -> Dict[str, NodeParameter]:
408
+ def get_parameters(self) -> dict[str, NodeParameter]:
409
409
  """Define parameters for the AsyncSwitchNode."""
410
410
  return {
411
411
  "input_data": NodeParameter(
@@ -469,7 +469,7 @@ class AsyncSwitchNode(AsyncNode):
469
469
  ),
470
470
  }
471
471
 
472
- def get_output_schema(self) -> Dict[str, NodeParameter]:
472
+ def get_output_schema(self) -> dict[str, NodeParameter]:
473
473
  """Dynamic schema with standard outputs."""
474
474
  return {
475
475
  "true_output": NodeParameter(
@@ -499,7 +499,7 @@ class AsyncSwitchNode(AsyncNode):
499
499
  # Note: case_X outputs are dynamic and not listed here
500
500
  }
501
501
 
502
- async def async_run(self, **kwargs) -> Dict[str, Any]:
502
+ async def async_run(self, **kwargs) -> dict[str, Any]:
503
503
  """Asynchronously execute the switch operation.
504
504
 
505
505
  Args:
@@ -624,7 +624,7 @@ class AsyncSwitchNode(AsyncNode):
624
624
  self.logger.debug(f"AsyncSwitch node result keys: {list(result.keys())}")
625
625
  return result
626
626
 
627
- def run(self, **kwargs) -> Dict[str, Any]:
627
+ def run(self, **kwargs) -> dict[str, Any]:
628
628
  """Synchronous execution method that delegates to the async implementation.
629
629
 
630
630
  This method is required by the Node abstract base class but shouldn't
@@ -693,12 +693,12 @@ class AsyncSwitchNode(AsyncNode):
693
693
 
694
694
  async def _handle_list_grouping(
695
695
  self,
696
- groups: Dict[Any, List],
697
- cases: List[Any],
696
+ groups: dict[Any, list],
697
+ cases: list[Any],
698
698
  case_prefix: str,
699
699
  default_field: str,
700
700
  pass_condition_result: bool,
701
- ) -> Dict[str, Any]:
701
+ ) -> dict[str, Any]:
702
702
  """Asynchronously handle routing when input is a list of dictionaries.
703
703
 
704
704
  This method creates outputs for each case with the filtered data.