kailash 0.1.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 (69) hide show
  1. kailash/__init__.py +31 -0
  2. kailash/__main__.py +11 -0
  3. kailash/cli/__init__.py +5 -0
  4. kailash/cli/commands.py +563 -0
  5. kailash/manifest.py +778 -0
  6. kailash/nodes/__init__.py +23 -0
  7. kailash/nodes/ai/__init__.py +26 -0
  8. kailash/nodes/ai/agents.py +417 -0
  9. kailash/nodes/ai/models.py +488 -0
  10. kailash/nodes/api/__init__.py +52 -0
  11. kailash/nodes/api/auth.py +567 -0
  12. kailash/nodes/api/graphql.py +480 -0
  13. kailash/nodes/api/http.py +598 -0
  14. kailash/nodes/api/rate_limiting.py +572 -0
  15. kailash/nodes/api/rest.py +665 -0
  16. kailash/nodes/base.py +1032 -0
  17. kailash/nodes/base_async.py +128 -0
  18. kailash/nodes/code/__init__.py +32 -0
  19. kailash/nodes/code/python.py +1021 -0
  20. kailash/nodes/data/__init__.py +125 -0
  21. kailash/nodes/data/readers.py +496 -0
  22. kailash/nodes/data/sharepoint_graph.py +623 -0
  23. kailash/nodes/data/sql.py +380 -0
  24. kailash/nodes/data/streaming.py +1168 -0
  25. kailash/nodes/data/vector_db.py +964 -0
  26. kailash/nodes/data/writers.py +529 -0
  27. kailash/nodes/logic/__init__.py +6 -0
  28. kailash/nodes/logic/async_operations.py +702 -0
  29. kailash/nodes/logic/operations.py +551 -0
  30. kailash/nodes/transform/__init__.py +5 -0
  31. kailash/nodes/transform/processors.py +379 -0
  32. kailash/runtime/__init__.py +6 -0
  33. kailash/runtime/async_local.py +356 -0
  34. kailash/runtime/docker.py +697 -0
  35. kailash/runtime/local.py +434 -0
  36. kailash/runtime/parallel.py +557 -0
  37. kailash/runtime/runner.py +110 -0
  38. kailash/runtime/testing.py +347 -0
  39. kailash/sdk_exceptions.py +307 -0
  40. kailash/tracking/__init__.py +7 -0
  41. kailash/tracking/manager.py +885 -0
  42. kailash/tracking/metrics_collector.py +342 -0
  43. kailash/tracking/models.py +535 -0
  44. kailash/tracking/storage/__init__.py +0 -0
  45. kailash/tracking/storage/base.py +113 -0
  46. kailash/tracking/storage/database.py +619 -0
  47. kailash/tracking/storage/filesystem.py +543 -0
  48. kailash/utils/__init__.py +0 -0
  49. kailash/utils/export.py +924 -0
  50. kailash/utils/templates.py +680 -0
  51. kailash/visualization/__init__.py +62 -0
  52. kailash/visualization/api.py +732 -0
  53. kailash/visualization/dashboard.py +951 -0
  54. kailash/visualization/performance.py +808 -0
  55. kailash/visualization/reports.py +1471 -0
  56. kailash/workflow/__init__.py +15 -0
  57. kailash/workflow/builder.py +245 -0
  58. kailash/workflow/graph.py +827 -0
  59. kailash/workflow/mermaid_visualizer.py +628 -0
  60. kailash/workflow/mock_registry.py +63 -0
  61. kailash/workflow/runner.py +302 -0
  62. kailash/workflow/state.py +238 -0
  63. kailash/workflow/visualization.py +588 -0
  64. kailash-0.1.0.dist-info/METADATA +710 -0
  65. kailash-0.1.0.dist-info/RECORD +69 -0
  66. kailash-0.1.0.dist-info/WHEEL +5 -0
  67. kailash-0.1.0.dist-info/entry_points.txt +2 -0
  68. kailash-0.1.0.dist-info/licenses/LICENSE +21 -0
  69. kailash-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,488 @@
1
+ """AI/ML model nodes for the Kailash SDK."""
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 TextClassifier(Node):
10
+ """Generic text classification node."""
11
+
12
+ def get_parameters(self) -> Dict[str, NodeParameter]:
13
+ return {
14
+ "texts": NodeParameter(
15
+ name="texts",
16
+ type=list,
17
+ required=True,
18
+ description="List of texts to classify",
19
+ ),
20
+ "model_name": NodeParameter(
21
+ name="model_name",
22
+ type=str,
23
+ required=False,
24
+ default="simple",
25
+ description="Model to use for classification",
26
+ ),
27
+ "categories": NodeParameter(
28
+ name="categories",
29
+ type=list,
30
+ required=False,
31
+ default=["positive", "negative", "neutral"],
32
+ description="Categories for classification",
33
+ ),
34
+ "confidence_threshold": NodeParameter(
35
+ name="confidence_threshold",
36
+ type=float,
37
+ required=False,
38
+ default=0.5,
39
+ description="Minimum confidence threshold",
40
+ ),
41
+ }
42
+
43
+ def run(self, **kwargs) -> Dict[str, Any]:
44
+ texts = kwargs["texts"]
45
+ model_name = kwargs.get("model_name", "simple")
46
+ categories = kwargs.get("categories", ["positive", "negative", "neutral"])
47
+ threshold = kwargs.get("confidence_threshold", 0.5)
48
+
49
+ # Simple mock classification
50
+ classifications = []
51
+ for text in texts:
52
+ # Mock classification logic
53
+ if isinstance(text, str):
54
+ if "good" in text.lower() or "excellent" in text.lower():
55
+ category = "positive"
56
+ confidence = 0.8
57
+ elif "bad" in text.lower() or "terrible" in text.lower():
58
+ category = "negative"
59
+ confidence = 0.9
60
+ else:
61
+ category = "neutral"
62
+ confidence = 0.6
63
+
64
+ classifications.append(
65
+ {
66
+ "text": text,
67
+ "category": category,
68
+ "confidence": confidence,
69
+ "passed_threshold": confidence >= threshold,
70
+ }
71
+ )
72
+
73
+ return {
74
+ "classifications": classifications,
75
+ "model_used": model_name,
76
+ "categories": categories,
77
+ "threshold": threshold,
78
+ }
79
+
80
+
81
+ @register_node()
82
+ class TextEmbedder(Node):
83
+ """Generate text embeddings."""
84
+
85
+ def get_parameters(self) -> Dict[str, NodeParameter]:
86
+ return {
87
+ "texts": NodeParameter(
88
+ name="texts",
89
+ type=list,
90
+ required=True,
91
+ description="List of texts to embed",
92
+ ),
93
+ "model_name": NodeParameter(
94
+ name="model_name",
95
+ type=str,
96
+ required=False,
97
+ default="simple",
98
+ description="Embedding model to use",
99
+ ),
100
+ "dimensions": NodeParameter(
101
+ name="dimensions",
102
+ type=int,
103
+ required=False,
104
+ default=384,
105
+ description="Embedding dimensions",
106
+ ),
107
+ }
108
+
109
+ def run(self, **kwargs) -> Dict[str, Any]:
110
+ texts = kwargs["texts"]
111
+ model_name = kwargs.get("model_name", "simple")
112
+ dimensions = kwargs.get("dimensions", 384)
113
+
114
+ # Mock embeddings
115
+ embeddings = []
116
+ for text in texts:
117
+ if isinstance(text, str):
118
+ # Generate mock embedding based on text hash
119
+ import hashlib
120
+
121
+ hash_val = int(hashlib.md5(text.encode()).hexdigest(), 16)
122
+
123
+ # Create consistent mock embedding
124
+ embedding = []
125
+ for i in range(dimensions):
126
+ val = ((hash_val >> i) & 1) * 2 - 1 # -1 or 1
127
+ val = val * (0.5 + 0.5 * ((hash_val >> (i + 8)) & 1))
128
+ embedding.append(val)
129
+
130
+ embeddings.append({"text": text, "embedding": embedding[:dimensions]})
131
+
132
+ return {
133
+ "embeddings": embeddings,
134
+ "model_used": model_name,
135
+ "dimensions": dimensions,
136
+ }
137
+
138
+
139
+ @register_node()
140
+ class SentimentAnalyzer(Node):
141
+ """Analyze sentiment of text."""
142
+
143
+ def get_parameters(self) -> Dict[str, NodeParameter]:
144
+ return {
145
+ "texts": NodeParameter(
146
+ name="texts",
147
+ type=list,
148
+ required=True,
149
+ description="List of texts to analyze",
150
+ ),
151
+ "language": NodeParameter(
152
+ name="language",
153
+ type=str,
154
+ required=False,
155
+ default="en",
156
+ description="Language of the texts",
157
+ ),
158
+ "granularity": NodeParameter(
159
+ name="granularity",
160
+ type=str,
161
+ required=False,
162
+ default="document",
163
+ description="Analysis granularity (document, sentence)",
164
+ ),
165
+ }
166
+
167
+ def run(self, **kwargs) -> Dict[str, Any]:
168
+ texts = kwargs["texts"]
169
+ language = kwargs.get("language", "en")
170
+ granularity = kwargs.get("granularity", "document")
171
+
172
+ # Mock sentiment analysis
173
+ sentiments = []
174
+ for text in texts:
175
+ if isinstance(text, str):
176
+ # Simple keyword-based sentiment
177
+ positive_words = [
178
+ "good",
179
+ "great",
180
+ "excellent",
181
+ "amazing",
182
+ "wonderful",
183
+ "love",
184
+ ]
185
+ negative_words = [
186
+ "bad",
187
+ "terrible",
188
+ "awful",
189
+ "hate",
190
+ "horrible",
191
+ "worst",
192
+ ]
193
+
194
+ text_lower = text.lower()
195
+ positive_count = sum(1 for word in positive_words if word in text_lower)
196
+ negative_count = sum(1 for word in negative_words if word in text_lower)
197
+
198
+ if positive_count > negative_count:
199
+ sentiment = "positive"
200
+ score = min(0.5 + positive_count * 0.1, 1.0)
201
+ elif negative_count > positive_count:
202
+ sentiment = "negative"
203
+ score = max(0.5 - negative_count * 0.1, 0.0)
204
+ else:
205
+ sentiment = "neutral"
206
+ score = 0.5
207
+
208
+ sentiments.append(
209
+ {
210
+ "text": text,
211
+ "sentiment": sentiment,
212
+ "score": score,
213
+ "language": language,
214
+ }
215
+ )
216
+
217
+ return {
218
+ "sentiments": sentiments,
219
+ "granularity": granularity,
220
+ "language": language,
221
+ }
222
+
223
+
224
+ @register_node()
225
+ class NamedEntityRecognizer(Node):
226
+ """Extract named entities from text."""
227
+
228
+ def get_parameters(self) -> Dict[str, NodeParameter]:
229
+ return {
230
+ "texts": NodeParameter(
231
+ name="texts",
232
+ type=list,
233
+ required=True,
234
+ description="List of texts to process",
235
+ ),
236
+ "entity_types": NodeParameter(
237
+ name="entity_types",
238
+ type=list,
239
+ required=False,
240
+ default=["PERSON", "ORGANIZATION", "LOCATION"],
241
+ description="Types of entities to extract",
242
+ ),
243
+ "language": NodeParameter(
244
+ name="language",
245
+ type=str,
246
+ required=False,
247
+ default="en",
248
+ description="Language of the texts",
249
+ ),
250
+ }
251
+
252
+ def run(self, **kwargs) -> Dict[str, Any]:
253
+ texts = kwargs["texts"]
254
+ entity_types = kwargs.get(
255
+ "entity_types", ["PERSON", "ORGANIZATION", "LOCATION"]
256
+ )
257
+ language = kwargs.get("language", "en")
258
+
259
+ # Mock NER
260
+ entities = []
261
+
262
+ # Mock entity patterns
263
+ person_names = ["John", "Jane", "Bob", "Alice", "Smith", "Johnson"]
264
+ org_names = ["Microsoft", "Google", "Apple", "IBM", "Amazon"]
265
+ locations = ["New York", "London", "Paris", "Tokyo", "Berlin"]
266
+
267
+ for text in texts:
268
+ if isinstance(text, str):
269
+ text_entities = []
270
+
271
+ # Simple pattern matching
272
+ for name in person_names:
273
+ if name in text:
274
+ text_entities.append(
275
+ {
276
+ "text": name,
277
+ "type": "PERSON",
278
+ "start": text.find(name),
279
+ "end": text.find(name) + len(name),
280
+ }
281
+ )
282
+
283
+ for org in org_names:
284
+ if org in text:
285
+ text_entities.append(
286
+ {
287
+ "text": org,
288
+ "type": "ORGANIZATION",
289
+ "start": text.find(org),
290
+ "end": text.find(org) + len(org),
291
+ }
292
+ )
293
+
294
+ for loc in locations:
295
+ if loc in text:
296
+ text_entities.append(
297
+ {
298
+ "text": loc,
299
+ "type": "LOCATION",
300
+ "start": text.find(loc),
301
+ "end": text.find(loc) + len(loc),
302
+ }
303
+ )
304
+
305
+ # Filter by requested entity types
306
+ text_entities = [e for e in text_entities if e["type"] in entity_types]
307
+
308
+ entities.append({"text": text, "entities": text_entities})
309
+
310
+ return {
311
+ "entities": entities,
312
+ "entity_types": entity_types,
313
+ "language": language,
314
+ }
315
+
316
+
317
+ @register_node()
318
+ class ModelPredictor(Node):
319
+ """Generic model prediction node."""
320
+
321
+ def get_parameters(self) -> Dict[str, NodeParameter]:
322
+ return {
323
+ "data": NodeParameter(
324
+ name="data",
325
+ type=list,
326
+ required=True,
327
+ description="Input data for prediction",
328
+ ),
329
+ "model_path": NodeParameter(
330
+ name="model_path",
331
+ type=str,
332
+ required=False,
333
+ default="default_model",
334
+ description="Path to the model",
335
+ ),
336
+ "prediction_type": NodeParameter(
337
+ name="prediction_type",
338
+ type=str,
339
+ required=False,
340
+ default="classification",
341
+ description="Type of prediction (classification, regression)",
342
+ ),
343
+ "batch_size": NodeParameter(
344
+ name="batch_size",
345
+ type=int,
346
+ required=False,
347
+ default=32,
348
+ description="Batch size for prediction",
349
+ ),
350
+ }
351
+
352
+ def run(self, **kwargs) -> Dict[str, Any]:
353
+ data = kwargs["data"]
354
+ model_path = kwargs.get("model_path", "default_model")
355
+ prediction_type = kwargs.get("prediction_type", "classification")
356
+ batch_size = kwargs.get("batch_size", 32)
357
+
358
+ # Mock predictions
359
+ predictions = []
360
+
361
+ for i, item in enumerate(data):
362
+ if prediction_type == "classification":
363
+ # Mock classification
364
+ classes = ["class_a", "class_b", "class_c"]
365
+ predicted_class = classes[i % len(classes)]
366
+ confidence = 0.7 + (i % 3) * 0.1
367
+
368
+ predictions.append(
369
+ {
370
+ "input": item,
371
+ "prediction": predicted_class,
372
+ "confidence": confidence,
373
+ "probabilities": {
374
+ c: (
375
+ confidence
376
+ if c == predicted_class
377
+ else (1 - confidence) / (len(classes) - 1)
378
+ )
379
+ for c in classes
380
+ },
381
+ }
382
+ )
383
+ else:
384
+ # Mock regression
385
+ value = 100 + (i * 10) + (hash(str(item)) % 50)
386
+
387
+ predictions.append(
388
+ {"input": item, "prediction": value, "confidence": 0.85}
389
+ )
390
+
391
+ return {
392
+ "predictions": predictions,
393
+ "model_path": model_path,
394
+ "prediction_type": prediction_type,
395
+ "batch_size": batch_size,
396
+ "total_processed": len(predictions),
397
+ }
398
+
399
+
400
+ @register_node()
401
+ class TextSummarizer(Node):
402
+ """Summarize text content."""
403
+
404
+ def get_parameters(self) -> Dict[str, NodeParameter]:
405
+ return {
406
+ "texts": NodeParameter(
407
+ name="texts",
408
+ type=list,
409
+ required=True,
410
+ description="List of texts to summarize",
411
+ ),
412
+ "max_length": NodeParameter(
413
+ name="max_length",
414
+ type=int,
415
+ required=False,
416
+ default=150,
417
+ description="Maximum summary length",
418
+ ),
419
+ "min_length": NodeParameter(
420
+ name="min_length",
421
+ type=int,
422
+ required=False,
423
+ default=50,
424
+ description="Minimum summary length",
425
+ ),
426
+ "style": NodeParameter(
427
+ name="style",
428
+ type=str,
429
+ required=False,
430
+ default="extractive",
431
+ description="Summarization style (extractive, abstractive)",
432
+ ),
433
+ }
434
+
435
+ def run(self, **kwargs) -> Dict[str, Any]:
436
+ texts = kwargs["texts"]
437
+ max_length = kwargs.get("max_length", 150)
438
+ min_length = kwargs.get("min_length", 50)
439
+ style = kwargs.get("style", "extractive")
440
+
441
+ summaries = []
442
+
443
+ for text in texts:
444
+ if isinstance(text, str):
445
+ # Simple extractive summarization (first sentences)
446
+ sentences = text.split(". ")
447
+
448
+ if style == "extractive":
449
+ # Take first few sentences
450
+ summary_sentences = []
451
+ current_length = 0
452
+
453
+ for sentence in sentences:
454
+ if current_length < min_length:
455
+ summary_sentences.append(sentence)
456
+ current_length += len(sentence)
457
+ elif current_length < max_length:
458
+ if len(sentence) + current_length <= max_length:
459
+ summary_sentences.append(sentence)
460
+ current_length += len(sentence)
461
+ else:
462
+ break
463
+ else:
464
+ break
465
+
466
+ summary = ". ".join(summary_sentences)
467
+ if summary and not summary.endswith("."):
468
+ summary += "."
469
+ else:
470
+ # Mock abstractive summary
471
+ words = text.split()[: max_length // 5] # Rough word count
472
+ summary = " ".join(words) + "..."
473
+
474
+ summaries.append(
475
+ {
476
+ "original": text,
477
+ "summary": summary,
478
+ "compression_ratio": len(summary) / len(text) if text else 0,
479
+ "style": style,
480
+ }
481
+ )
482
+
483
+ return {
484
+ "summaries": summaries,
485
+ "max_length": max_length,
486
+ "min_length": min_length,
487
+ "style": style,
488
+ }
@@ -0,0 +1,52 @@
1
+ """
2
+ API integration and HTTP client nodes for the Kailash SDK.
3
+
4
+ This module provides nodes for interacting with external HTTP APIs, with support for
5
+ various authentication methods, request/response formats, and both synchronous and
6
+ asynchronous operation.
7
+
8
+ The module includes:
9
+ - Base HTTP client nodes
10
+ - Specialized API client nodes (REST, GraphQL)
11
+ - Authentication helpers
12
+ - Request/response formatters
13
+
14
+ Design philosophy:
15
+ - Support both simple one-off API calls and complex client integrations
16
+ - Maintain consistent interface patterns with other node types
17
+ - Provide sensible defaults while allowing full customization
18
+ - Enable both synchronous and asynchronous operation
19
+ """
20
+
21
+ from .auth import APIKeyNode, BasicAuthNode, OAuth2Node
22
+ from .graphql import AsyncGraphQLClientNode, GraphQLClientNode
23
+ from .http import AsyncHTTPRequestNode, HTTPRequestNode
24
+ from .rate_limiting import (
25
+ AsyncRateLimitedAPINode,
26
+ RateLimitConfig,
27
+ RateLimitedAPINode,
28
+ RateLimiter,
29
+ SlidingWindowRateLimiter,
30
+ TokenBucketRateLimiter,
31
+ create_rate_limiter,
32
+ )
33
+ from .rest import AsyncRESTClientNode, RESTClientNode
34
+
35
+ __all__ = [
36
+ "HTTPRequestNode",
37
+ "AsyncHTTPRequestNode",
38
+ "RESTClientNode",
39
+ "AsyncRESTClientNode",
40
+ "GraphQLClientNode",
41
+ "AsyncGraphQLClientNode",
42
+ "BasicAuthNode",
43
+ "OAuth2Node",
44
+ "APIKeyNode",
45
+ "RateLimitConfig",
46
+ "RateLimiter",
47
+ "TokenBucketRateLimiter",
48
+ "SlidingWindowRateLimiter",
49
+ "RateLimitedAPINode",
50
+ "AsyncRateLimitedAPINode",
51
+ "create_rate_limiter",
52
+ ]