kailash 0.1.0__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.
- kailash/__init__.py +1 -1
- kailash/nodes/__init__.py +2 -1
- kailash/nodes/ai/__init__.py +26 -0
- kailash/nodes/ai/ai_providers.py +1272 -0
- kailash/nodes/ai/embedding_generator.py +853 -0
- kailash/nodes/ai/llm_agent.py +1166 -0
- kailash/nodes/api/auth.py +3 -3
- kailash/nodes/api/graphql.py +2 -2
- kailash/nodes/api/http.py +391 -44
- kailash/nodes/api/rate_limiting.py +2 -2
- kailash/nodes/api/rest.py +464 -56
- kailash/nodes/base.py +71 -12
- kailash/nodes/code/python.py +2 -1
- kailash/nodes/data/__init__.py +7 -0
- kailash/nodes/data/readers.py +28 -26
- kailash/nodes/data/retrieval.py +178 -0
- kailash/nodes/data/sharepoint_graph.py +7 -7
- kailash/nodes/data/sources.py +65 -0
- kailash/nodes/data/sql.py +4 -2
- kailash/nodes/data/writers.py +6 -3
- kailash/nodes/logic/operations.py +2 -1
- kailash/nodes/mcp/__init__.py +11 -0
- kailash/nodes/mcp/client.py +558 -0
- kailash/nodes/mcp/resource.py +682 -0
- kailash/nodes/mcp/server.py +571 -0
- kailash/nodes/transform/__init__.py +16 -1
- kailash/nodes/transform/chunkers.py +78 -0
- kailash/nodes/transform/formatters.py +96 -0
- kailash/runtime/docker.py +6 -6
- kailash/sdk_exceptions.py +24 -10
- kailash/tracking/metrics_collector.py +2 -1
- kailash/utils/templates.py +6 -6
- {kailash-0.1.0.dist-info → kailash-0.1.2.dist-info}/METADATA +349 -49
- {kailash-0.1.0.dist-info → kailash-0.1.2.dist-info}/RECORD +38 -27
- {kailash-0.1.0.dist-info → kailash-0.1.2.dist-info}/WHEEL +0 -0
- {kailash-0.1.0.dist-info → kailash-0.1.2.dist-info}/entry_points.txt +0 -0
- {kailash-0.1.0.dist-info → kailash-0.1.2.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.1.0.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
|
-
|
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
|
kailash/utils/templates.py
CHANGED
@@ -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(),
|