kailash 0.1.1__py3-none-any.whl → 0.1.2__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 (37) hide show
  1. kailash/nodes/__init__.py +2 -1
  2. kailash/nodes/ai/__init__.py +26 -0
  3. kailash/nodes/ai/ai_providers.py +1272 -0
  4. kailash/nodes/ai/embedding_generator.py +853 -0
  5. kailash/nodes/ai/llm_agent.py +1166 -0
  6. kailash/nodes/api/auth.py +3 -3
  7. kailash/nodes/api/graphql.py +2 -2
  8. kailash/nodes/api/http.py +391 -44
  9. kailash/nodes/api/rate_limiting.py +2 -2
  10. kailash/nodes/api/rest.py +464 -56
  11. kailash/nodes/base.py +71 -12
  12. kailash/nodes/code/python.py +2 -1
  13. kailash/nodes/data/__init__.py +7 -0
  14. kailash/nodes/data/readers.py +28 -26
  15. kailash/nodes/data/retrieval.py +178 -0
  16. kailash/nodes/data/sharepoint_graph.py +7 -7
  17. kailash/nodes/data/sources.py +65 -0
  18. kailash/nodes/data/sql.py +4 -2
  19. kailash/nodes/data/writers.py +6 -3
  20. kailash/nodes/logic/operations.py +2 -1
  21. kailash/nodes/mcp/__init__.py +11 -0
  22. kailash/nodes/mcp/client.py +558 -0
  23. kailash/nodes/mcp/resource.py +682 -0
  24. kailash/nodes/mcp/server.py +571 -0
  25. kailash/nodes/transform/__init__.py +16 -1
  26. kailash/nodes/transform/chunkers.py +78 -0
  27. kailash/nodes/transform/formatters.py +96 -0
  28. kailash/runtime/docker.py +6 -6
  29. kailash/sdk_exceptions.py +24 -10
  30. kailash/tracking/metrics_collector.py +2 -1
  31. kailash/utils/templates.py +6 -6
  32. {kailash-0.1.1.dist-info → kailash-0.1.2.dist-info}/METADATA +344 -46
  33. {kailash-0.1.1.dist-info → kailash-0.1.2.dist-info}/RECORD +37 -26
  34. {kailash-0.1.1.dist-info → kailash-0.1.2.dist-info}/WHEEL +0 -0
  35. {kailash-0.1.1.dist-info → kailash-0.1.2.dist-info}/entry_points.txt +0 -0
  36. {kailash-0.1.1.dist-info → kailash-0.1.2.dist-info}/licenses/LICENSE +0 -0
  37. {kailash-0.1.1.dist-info → kailash-0.1.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,96 @@
1
+ """Text formatting nodes for transforming and preparing text data."""
2
+
3
+ from typing import Any, Dict
4
+
5
+ from kailash.nodes.base import Node, NodeParameter, register_node
6
+
7
+
8
+ @register_node()
9
+ class ChunkTextExtractorNode(Node):
10
+ """Extracts text content from chunks for embedding generation."""
11
+
12
+ def get_parameters(self) -> Dict[str, NodeParameter]:
13
+ return {
14
+ "chunks": NodeParameter(
15
+ name="chunks",
16
+ type=list,
17
+ required=False,
18
+ description="List of chunks to extract text from",
19
+ )
20
+ }
21
+
22
+ def run(self, **kwargs) -> Dict[str, Any]:
23
+ chunks = kwargs.get("chunks", [])
24
+ # Extract just the content text from chunks
25
+ texts = [chunk["content"] for chunk in chunks]
26
+ return {"input_texts": texts}
27
+
28
+
29
+ @register_node()
30
+ class QueryTextWrapperNode(Node):
31
+ """Wraps query string in list for embedding generation."""
32
+
33
+ def get_parameters(self) -> Dict[str, NodeParameter]:
34
+ return {
35
+ "query": NodeParameter(
36
+ name="query",
37
+ type=str,
38
+ required=False,
39
+ description="Query string to wrap",
40
+ )
41
+ }
42
+
43
+ def run(self, **kwargs) -> Dict[str, Any]:
44
+ query = kwargs.get("query", "")
45
+ print(f"Debug QueryTextWrapper: received query='{query}'")
46
+ # Use input_texts for batch embedding (single item list)
47
+ result = {"input_texts": [query]}
48
+ print(f"Debug QueryTextWrapper: returning {result}")
49
+ return result
50
+
51
+
52
+ @register_node()
53
+ class ContextFormatterNode(Node):
54
+ """Formats relevant chunks into context for LLM."""
55
+
56
+ def get_parameters(self) -> Dict[str, NodeParameter]:
57
+ return {
58
+ "relevant_chunks": NodeParameter(
59
+ name="relevant_chunks",
60
+ type=list,
61
+ required=False,
62
+ description="List of relevant chunks with scores",
63
+ ),
64
+ "query": NodeParameter(
65
+ name="query",
66
+ type=str,
67
+ required=False,
68
+ description="Original query string",
69
+ ),
70
+ }
71
+
72
+ def run(self, **kwargs) -> Dict[str, Any]:
73
+ relevant_chunks = kwargs.get("relevant_chunks", [])
74
+ query = kwargs.get("query", "")
75
+ # Format context from relevant chunks
76
+ context_parts = []
77
+ for chunk in relevant_chunks:
78
+ context_parts.append(
79
+ f"From '{chunk['document_title']}' (Score: {chunk['relevance_score']:.3f}):\n"
80
+ f"{chunk['content']}\n"
81
+ )
82
+
83
+ context = "\n".join(context_parts)
84
+
85
+ # Create prompt for LLM
86
+ prompt = f"""Based on the following context, please answer the question: "{query}"
87
+
88
+ Context:
89
+ {context}
90
+
91
+ Please provide a comprehensive answer based on the information provided above."""
92
+
93
+ # Create messages list for LLMAgent
94
+ messages = [{"role": "user", "content": prompt}]
95
+
96
+ return {"formatted_prompt": prompt, "messages": messages, "context": context}
kailash/runtime/docker.py CHANGED
@@ -174,7 +174,7 @@ def main():
174
174
  logger.info(f"Loaded configuration for {node_data['class']} node")
175
175
 
176
176
  # Load runtime inputs if available
177
- input_path = Path("/data/input/inputs.json")
177
+ input_path = Path("/examples/data/input/inputs.json")
178
178
  runtime_inputs = {}
179
179
  if input_path.exists():
180
180
  logger.info(f"Loading inputs from {input_path}")
@@ -206,7 +206,7 @@ def main():
206
206
  except Exception as e:
207
207
  logger.error(f"Node execution failed: {e}")
208
208
  # Save error information
209
- with open("/data/output/error.json", 'w') as f:
209
+ with open("/examples/data/output/error.json", 'w') as f:
210
210
  json.dump({
211
211
  "error": str(e),
212
212
  "type": e.__class__.__name__
@@ -216,7 +216,7 @@ def main():
216
216
  # Save results
217
217
  logger.info("Saving execution results")
218
218
  try:
219
- result_path = Path("/data/output/result.json")
219
+ result_path = Path("/examples/data/output/result.json")
220
220
  with open(result_path, 'w') as f:
221
221
  # Handle non-serializable objects with basic conversion
222
222
  try:
@@ -266,7 +266,7 @@ COPY node.json /app/node.json
266
266
  COPY entrypoint.py /app/entrypoint.py
267
267
 
268
268
  # Create data directories
269
- RUN mkdir -p /data/input /data/output
269
+ RUN mkdir -p /examples/data/input /examples/data/output
270
270
 
271
271
  # Set entrypoint
272
272
  ENTRYPOINT ["/app/entrypoint.py"]
@@ -391,9 +391,9 @@ ENTRYPOINT ["/app/entrypoint.py"]
391
391
  cmd.extend(
392
392
  [
393
393
  "-v",
394
- f"{self.input_dir.absolute()}:/data/input",
394
+ f"{self.input_dir.absolute()}:/examples/data/input",
395
395
  "-v",
396
- f"{self.output_dir.absolute()}:/data/output",
396
+ f"{self.output_dir.absolute()}:/examples/data/output",
397
397
  ]
398
398
  )
399
399
 
kailash/sdk_exceptions.py CHANGED
@@ -278,16 +278,7 @@ class TemplateError(KailashException):
278
278
 
279
279
 
280
280
  # Code execution exceptions
281
- class SafetyViolationError(NodeException):
282
- """Raised when code execution violates safety rules.
283
-
284
- This typically occurs when:
285
- - Potentially dangerous operations are attempted
286
- - Resource limits are exceeded
287
- - Security policies are violated
288
- """
289
-
290
- pass
281
+ # (SafetyViolationError already defined above - removing duplicate)
291
282
 
292
283
 
293
284
  class CodeExecutionError(NodeException):
@@ -302,6 +293,29 @@ class CodeExecutionError(NodeException):
302
293
  pass
303
294
 
304
295
 
296
+ # Resource exceptions
297
+ class KailashNotFoundException(KailashException):
298
+ """Raised when a requested resource cannot be found.
299
+
300
+ This typically occurs when:
301
+ - A template ID doesn't exist in the registry
302
+ - A node type is not registered
303
+ - A file or resource is missing
304
+ """
305
+
306
+ pass
307
+
308
+
309
+ # Workflow-specific exceptions
310
+ class KailashWorkflowException(WorkflowException):
311
+ """Raised for general workflow-related errors.
312
+
313
+ This is an alias for WorkflowException for consistency.
314
+ """
315
+
316
+ pass
317
+
318
+
305
319
  # Legacy exception name compatibility for tests and backwards compatibility
306
320
  KailashRuntimeError = RuntimeExecutionError
307
321
  KailashValidationError = NodeValidationError
@@ -88,7 +88,8 @@ class MetricsCollector:
88
88
  metrics during node execution, with support for both process-level and
89
89
  system-level monitoring.
90
90
 
91
- Usage:
91
+ Usage::
92
+
92
93
  collector = MetricsCollector()
93
94
  with collector.collect() as metrics:
94
95
  # Execute node code here
@@ -327,7 +327,7 @@ A Kailash workflow project.
327
327
 
328
328
  - `workflows/`: Workflow definitions
329
329
  - `nodes/`: Custom node implementations
330
- - `data/`: Input data files
330
+ - `examples/data/`: Input data files
331
331
  - `outputs/`: Output files
332
332
 
333
333
  ## Usage
@@ -363,7 +363,7 @@ workflow = Workflow(
363
363
  )
364
364
 
365
365
  # Add nodes
366
- workflow.add_node("reader", CSVReader(), file_path="data/input.csv")
366
+ workflow.add_node("reader", CSVReader(), file_path="examples/examples/data/input.csv")
367
367
  workflow.add_node("filter", Filter(), field="value", operator=">", value=100)
368
368
  workflow.add_node("sort", Sort(), field="value", reverse=True)
369
369
  workflow.add_node("aggregate", Aggregator(), group_by="category", operation="sum")
@@ -482,8 +482,8 @@ workflow = Workflow(
482
482
  )
483
483
 
484
484
  # Data ingestion
485
- workflow.add_node("csv_reader", CSVReader(), file_path="data/sales_data.csv")
486
- workflow.add_node("json_reader", JSONReader(), file_path="data/product_data.json")
485
+ workflow.add_node("csv_reader", CSVReader(), file_path="examples/examples/data/sales_data.csv")
486
+ workflow.add_node("json_reader", JSONReader(), file_path="examples/examples/data/product_data.json")
487
487
 
488
488
  # Transform data
489
489
  workflow.add_node("filter_sales", Filter(), field="amount", operator=">", value=1000)
@@ -553,7 +553,7 @@ workflow = Workflow(
553
553
  )
554
554
 
555
555
  # Data ingestion
556
- workflow.add_node("read_data", CSVReader(), file_path="data/text_data.csv")
556
+ workflow.add_node("read_data", CSVReader(), file_path="examples/examples/data/text_data.csv")
557
557
 
558
558
  # Preprocessing
559
559
  workflow.add_node("extract_text", Map(), field="content")
@@ -616,7 +616,7 @@ workflow = Workflow(
616
616
  )
617
617
 
618
618
  # Read configuration
619
- workflow.add_node("read_config", JSONReader(), file_path="data/api_config.json")
619
+ workflow.add_node("read_config", JSONReader(), file_path="examples/examples/data/api_config.json")
620
620
 
621
621
  # Process with AI agent
622
622
  workflow.add_node("chat_agent", ChatAgent(),