kailash 0.1.5__py3-none-any.whl → 0.2.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 (77) hide show
  1. kailash/__init__.py +1 -1
  2. kailash/access_control.py +740 -0
  3. kailash/api/__main__.py +6 -0
  4. kailash/api/auth.py +668 -0
  5. kailash/api/custom_nodes.py +285 -0
  6. kailash/api/custom_nodes_secure.py +377 -0
  7. kailash/api/database.py +620 -0
  8. kailash/api/studio.py +915 -0
  9. kailash/api/studio_secure.py +893 -0
  10. kailash/mcp/__init__.py +53 -0
  11. kailash/mcp/__main__.py +13 -0
  12. kailash/mcp/ai_registry_server.py +712 -0
  13. kailash/mcp/client.py +447 -0
  14. kailash/mcp/client_new.py +334 -0
  15. kailash/mcp/server.py +293 -0
  16. kailash/mcp/server_new.py +336 -0
  17. kailash/mcp/servers/__init__.py +12 -0
  18. kailash/mcp/servers/ai_registry.py +289 -0
  19. kailash/nodes/__init__.py +4 -2
  20. kailash/nodes/ai/__init__.py +2 -0
  21. kailash/nodes/ai/a2a.py +714 -67
  22. kailash/nodes/ai/intelligent_agent_orchestrator.py +31 -37
  23. kailash/nodes/ai/iterative_llm_agent.py +1280 -0
  24. kailash/nodes/ai/llm_agent.py +324 -1
  25. kailash/nodes/ai/self_organizing.py +5 -6
  26. kailash/nodes/base.py +15 -2
  27. kailash/nodes/base_async.py +45 -0
  28. kailash/nodes/base_cycle_aware.py +374 -0
  29. kailash/nodes/base_with_acl.py +338 -0
  30. kailash/nodes/code/python.py +135 -27
  31. kailash/nodes/data/__init__.py +1 -2
  32. kailash/nodes/data/readers.py +16 -6
  33. kailash/nodes/data/sql.py +699 -256
  34. kailash/nodes/data/writers.py +16 -6
  35. kailash/nodes/logic/__init__.py +8 -0
  36. kailash/nodes/logic/convergence.py +642 -0
  37. kailash/nodes/logic/loop.py +153 -0
  38. kailash/nodes/logic/operations.py +187 -27
  39. kailash/nodes/mixins/__init__.py +11 -0
  40. kailash/nodes/mixins/mcp.py +228 -0
  41. kailash/nodes/mixins.py +387 -0
  42. kailash/runtime/__init__.py +2 -1
  43. kailash/runtime/access_controlled.py +458 -0
  44. kailash/runtime/local.py +106 -33
  45. kailash/runtime/parallel_cyclic.py +529 -0
  46. kailash/sdk_exceptions.py +90 -5
  47. kailash/security.py +845 -0
  48. kailash/tracking/manager.py +38 -15
  49. kailash/tracking/models.py +1 -1
  50. kailash/tracking/storage/filesystem.py +30 -2
  51. kailash/utils/__init__.py +8 -0
  52. kailash/workflow/__init__.py +18 -0
  53. kailash/workflow/convergence.py +270 -0
  54. kailash/workflow/cycle_analyzer.py +889 -0
  55. kailash/workflow/cycle_builder.py +579 -0
  56. kailash/workflow/cycle_config.py +725 -0
  57. kailash/workflow/cycle_debugger.py +860 -0
  58. kailash/workflow/cycle_exceptions.py +615 -0
  59. kailash/workflow/cycle_profiler.py +741 -0
  60. kailash/workflow/cycle_state.py +338 -0
  61. kailash/workflow/cyclic_runner.py +985 -0
  62. kailash/workflow/graph.py +500 -39
  63. kailash/workflow/migration.py +809 -0
  64. kailash/workflow/safety.py +365 -0
  65. kailash/workflow/templates.py +763 -0
  66. kailash/workflow/validation.py +751 -0
  67. {kailash-0.1.5.dist-info → kailash-0.2.1.dist-info}/METADATA +259 -12
  68. kailash-0.2.1.dist-info/RECORD +125 -0
  69. kailash/nodes/mcp/__init__.py +0 -11
  70. kailash/nodes/mcp/client.py +0 -554
  71. kailash/nodes/mcp/resource.py +0 -682
  72. kailash/nodes/mcp/server.py +0 -577
  73. kailash-0.1.5.dist-info/RECORD +0 -88
  74. {kailash-0.1.5.dist-info → kailash-0.2.1.dist-info}/WHEEL +0 -0
  75. {kailash-0.1.5.dist-info → kailash-0.2.1.dist-info}/entry_points.txt +0 -0
  76. {kailash-0.1.5.dist-info → kailash-0.2.1.dist-info}/licenses/LICENSE +0 -0
  77. {kailash-0.1.5.dist-info → kailash-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,725 @@
1
+ """
2
+ Type-Safe Configuration System for Cyclic Workflows.
3
+
4
+ This module provides a comprehensive, type-safe configuration system for cyclic
5
+ workflows using Python dataclasses with runtime validation. It enables
6
+ structured cycle configuration with full IDE support, compile-time type
7
+ checking, and extensive validation to prevent common configuration errors.
8
+
9
+ Design Philosophy:
10
+ Provides compile-time type safety and runtime validation for cycle
11
+ configurations, replacing loose parameter passing with validated
12
+ configuration objects. Enables configuration reuse, templating, and
13
+ standardization across workflows while maintaining maximum flexibility.
14
+
15
+ Key Features:
16
+ - Dataclass-based configuration with automatic validation
17
+ - Pre-built templates for common cycle patterns
18
+ - Configuration merging and composition
19
+ - Export/import capabilities for configuration persistence
20
+ - Comprehensive validation with actionable error messages
21
+
22
+ Configuration Categories:
23
+ 1. Termination Conditions: max_iterations, timeout, convergence_check
24
+ 2. Safety Limits: memory_limit, iteration_safety_factor
25
+ 3. Cycle Metadata: cycle_id, parent_cycle, description
26
+ 4. Execution Control: condition, priority, retry_policy
27
+
28
+ Validation Strategy:
29
+ Performs comprehensive validation at initialization and modification,
30
+ checking parameter types, ranges, safety constraints, and logical
31
+ consistency. Provides specific, actionable error messages.
32
+
33
+ Upstream Dependencies:
34
+ - Used by CycleBuilder.build() for configuration validation
35
+ - Can be used directly with Workflow.connect() for type safety
36
+ - Supports serialization for configuration persistence
37
+
38
+ Downstream Consumers:
39
+ - CyclicWorkflowExecutor for execution of configured cycles
40
+ - Cycle debugging and profiling tools for configuration analysis
41
+ - Configuration templates and presets for common patterns
42
+ - Workflow generation and automation tools
43
+
44
+ Template System:
45
+ CycleTemplates class provides factory methods for creating optimized
46
+ configurations for specific use cases, reducing boilerplate and ensuring
47
+ best practices are followed automatically.
48
+
49
+ Example Usage:
50
+ Basic configuration:
51
+
52
+ >>> from kailash.workflow.cycle_config import CycleConfig
53
+ >>> config = CycleConfig(
54
+ ... max_iterations=100,
55
+ ... convergence_check="error < 0.01",
56
+ ... timeout=300.0,
57
+ ... cycle_id="optimization_loop"
58
+ ... )
59
+ >>> # Use with workflow
60
+ >>> workflow.connect(
61
+ ... "processor", "evaluator",
62
+ ... cycle=True, cycle_config=config
63
+ ... )
64
+
65
+ Template usage:
66
+
67
+ >>> from kailash.workflow.cycle_config import CycleTemplates
68
+ >>> # Pre-optimized configuration
69
+ >>> config = CycleTemplates.optimization_loop(
70
+ ... max_iterations=200,
71
+ ... convergence_threshold=0.001
72
+ ... )
73
+ >>> # Customize template
74
+ >>> custom_config = config.merge(CycleConfig(
75
+ ... timeout=600.0,
76
+ ... memory_limit=2048
77
+ ... ))
78
+
79
+ Configuration management:
80
+
81
+ >>> # Export for reuse
82
+ >>> template_data = config.create_template("ml_training")
83
+ >>> # Import and modify
84
+ >>> loaded_config = CycleConfig.from_dict(template_data["configuration"])
85
+ >>> # Validation and safety
86
+ >>> config.validate() # Explicit validation
87
+ >>> effective_max = config.get_effective_max_iterations() # With safety factor
88
+
89
+ See Also:
90
+ - :mod:`kailash.workflow.cycle_builder` for fluent configuration API
91
+ - :mod:`kailash.workflow.templates` for pre-built cycle patterns
92
+ - :doc:`/guides/configuration` for configuration best practices
93
+ """
94
+
95
+ import logging
96
+ from dataclasses import dataclass, field
97
+ from typing import Any, Callable, Dict, Optional, Union
98
+
99
+ from kailash.workflow.cycle_exceptions import CycleConfigurationError
100
+
101
+ logger = logging.getLogger(__name__)
102
+
103
+
104
+ @dataclass
105
+ class CycleConfig:
106
+ """
107
+ Type-safe configuration for cyclic workflow connections.
108
+
109
+ This dataclass provides a structured, type-safe way to configure cycle
110
+ parameters with validation, default values, and comprehensive error
111
+ checking. It replaces loose parameter passing with a validated configuration
112
+ object that can be reused across multiple cycles.
113
+
114
+ Design Philosophy:
115
+ Provides compile-time type safety and runtime validation for cycle
116
+ configurations. Enables configuration reuse, templating, and
117
+ standardization across workflows while maintaining flexibility.
118
+
119
+ Upstream Dependencies:
120
+ - Used by CycleBuilder.build() for configuration validation
121
+ - Can be used directly with Workflow.connect() for type safety
122
+ - Supports serialization for configuration persistence
123
+
124
+ Downstream Consumers:
125
+ - CyclicWorkflowExecutor for execution of configured cycles
126
+ - Cycle debugging and profiling tools for configuration analysis
127
+ - Configuration templates and presets for common patterns
128
+
129
+ Configuration Categories:
130
+ 1. Termination Conditions: max_iterations, timeout, convergence_check
131
+ 2. Safety Limits: memory_limit, iteration_safety_factor
132
+ 3. Cycle Metadata: cycle_id, parent_cycle, description
133
+ 4. Execution Control: condition, priority, retry_policy
134
+
135
+ Example:
136
+ >>> # Basic configuration
137
+ >>> config = CycleConfig(max_iterations=100, convergence_check="error < 0.01")
138
+ >>> workflow.connect("a", "b", cycle_config=config)
139
+
140
+ >>> # Advanced configuration with all features
141
+ >>> config = CycleConfig(
142
+ ... max_iterations=50,
143
+ ... convergence_check="quality > 0.95",
144
+ ... timeout=300.0,
145
+ ... memory_limit=1024,
146
+ ... cycle_id="optimization_loop",
147
+ ... description="Quality optimization cycle",
148
+ ... condition="needs_optimization == True"
149
+ ... )
150
+ """
151
+
152
+ # Termination conditions (at least one required)
153
+ max_iterations: Optional[int] = None
154
+ convergence_check: Optional[Union[str, Callable]] = None
155
+ timeout: Optional[float] = None
156
+
157
+ # Safety and resource limits
158
+ memory_limit: Optional[int] = None
159
+ iteration_safety_factor: float = 1.5 # Multiplier for max_iterations safety
160
+
161
+ # Cycle metadata and identification
162
+ cycle_id: Optional[str] = None
163
+ parent_cycle: Optional[str] = None
164
+ description: str = ""
165
+
166
+ # Execution control and conditions
167
+ condition: Optional[str] = None # When to execute the cycle
168
+ priority: int = 0 # Execution priority for multiple cycles
169
+
170
+ # Advanced configuration
171
+ retry_policy: Dict[str, Any] = field(default_factory=dict)
172
+ metadata: Dict[str, Any] = field(default_factory=dict)
173
+
174
+ def __post_init__(self):
175
+ """
176
+ Validate configuration after initialization.
177
+
178
+ Performs comprehensive validation of all configuration parameters
179
+ to ensure they are valid, compatible, and safe for cycle execution.
180
+
181
+ Raises:
182
+ CycleConfigurationError: If configuration is invalid or unsafe
183
+
184
+ Side Effects:
185
+ Logs validation warnings for suboptimal configurations
186
+ Applies automatic fixes for minor configuration issues
187
+ """
188
+ self.validate()
189
+
190
+ def validate(self) -> None:
191
+ """
192
+ Validate the cycle configuration for correctness and safety.
193
+
194
+ Performs comprehensive validation of all configuration parameters,
195
+ checking for required fields, valid ranges, unsafe expressions,
196
+ and configuration conflicts. Provides actionable error messages
197
+ for any validation failures.
198
+
199
+ Raises:
200
+ CycleConfigurationError: If configuration is invalid
201
+
202
+ Side Effects:
203
+ Logs warnings for suboptimal but valid configurations
204
+ May modify configuration for automatic safety improvements
205
+
206
+ Example:
207
+ >>> config = CycleConfig(max_iterations=-5) # Will raise error
208
+ >>> config.validate() # CycleConfigurationError
209
+ """
210
+ errors = []
211
+ warnings = []
212
+
213
+ # Validate termination conditions (at least one required)
214
+ termination_conditions = [
215
+ self.max_iterations is not None,
216
+ self.convergence_check is not None,
217
+ self.timeout is not None,
218
+ ]
219
+
220
+ if not any(termination_conditions):
221
+ errors.append(
222
+ "At least one termination condition is required: "
223
+ "max_iterations, convergence_check, or timeout. "
224
+ "Recommendation: Always include max_iterations as a safety net."
225
+ )
226
+
227
+ # Validate max_iterations
228
+ if self.max_iterations is not None:
229
+ if not isinstance(self.max_iterations, int):
230
+ raise CycleConfigurationError(
231
+ f"max_iterations must be an integer, got {type(self.max_iterations)}",
232
+ error_code="CYCLE_CONFIG_002",
233
+ invalid_params={"max_iterations": self.max_iterations},
234
+ suggestions=[
235
+ "Use integer values for max_iterations",
236
+ "Convert float values to int if needed",
237
+ ],
238
+ )
239
+ elif self.max_iterations <= 0:
240
+ raise CycleConfigurationError(
241
+ f"max_iterations must be positive, got {self.max_iterations}",
242
+ error_code="CYCLE_CONFIG_002",
243
+ invalid_params={"max_iterations": self.max_iterations},
244
+ suggestions=[
245
+ "Use 10-100 iterations for quick convergence",
246
+ "Use 100-1000 iterations for complex optimization",
247
+ "Consider adding convergence_check for early termination",
248
+ ],
249
+ )
250
+ elif self.max_iterations > 10000:
251
+ warnings.append(
252
+ f"max_iterations={self.max_iterations} is very high. "
253
+ "Consider using convergence_check for efficiency."
254
+ )
255
+
256
+ # Validate convergence_check
257
+ if self.convergence_check is not None:
258
+ if isinstance(self.convergence_check, str):
259
+ if not self.convergence_check.strip():
260
+ errors.append(
261
+ "Convergence condition cannot be empty. "
262
+ "Examples: 'error < 0.01', 'quality > 0.9', 'count >= 10'"
263
+ )
264
+ else:
265
+ # Validate expression safety
266
+ unsafe_patterns = [
267
+ "import ",
268
+ "exec(",
269
+ "eval(",
270
+ "__",
271
+ "open(",
272
+ "file(",
273
+ ]
274
+ for pattern in unsafe_patterns:
275
+ if pattern in self.convergence_check:
276
+ errors.append(
277
+ f"Convergence condition contains unsafe operation: '{pattern}'. "
278
+ "Use simple comparison expressions only."
279
+ )
280
+ elif not callable(self.convergence_check):
281
+ errors.append(
282
+ f"convergence_check must be string or callable, got {type(self.convergence_check)}"
283
+ )
284
+
285
+ # Validate timeout
286
+ if self.timeout is not None:
287
+ if not isinstance(self.timeout, (int, float)):
288
+ errors.append(f"timeout must be numeric, got {type(self.timeout)}")
289
+ elif self.timeout <= 0:
290
+ errors.append(
291
+ f"timeout must be positive, got {self.timeout}. "
292
+ "Recommendation: Use 30-300 seconds for most cycles."
293
+ )
294
+ elif self.timeout > 3600:
295
+ warnings.append(
296
+ f"timeout={self.timeout} seconds is very long (>1 hour). "
297
+ "Consider breaking into smaller cycles."
298
+ )
299
+
300
+ # Validate memory_limit
301
+ if self.memory_limit is not None:
302
+ if not isinstance(self.memory_limit, int):
303
+ errors.append(
304
+ f"memory_limit must be an integer, got {type(self.memory_limit)}"
305
+ )
306
+ elif self.memory_limit <= 0:
307
+ errors.append(
308
+ f"memory_limit must be positive, got {self.memory_limit}. "
309
+ "Recommendation: Use 100-1000 MB for most cycles."
310
+ )
311
+ elif self.memory_limit > 100000: # 100GB
312
+ warnings.append(
313
+ f"memory_limit={self.memory_limit} MB is very high. "
314
+ "Verify this is intentional for your use case."
315
+ )
316
+
317
+ # Validate iteration_safety_factor
318
+ if not isinstance(self.iteration_safety_factor, (int, float)):
319
+ errors.append(
320
+ f"iteration_safety_factor must be numeric, got {type(self.iteration_safety_factor)}"
321
+ )
322
+ elif self.iteration_safety_factor < 1.0:
323
+ errors.append(
324
+ f"iteration_safety_factor must be >= 1.0, got {self.iteration_safety_factor}. "
325
+ "This factor provides safety buffer for max_iterations."
326
+ )
327
+ elif self.iteration_safety_factor > 10.0:
328
+ warnings.append(
329
+ f"iteration_safety_factor={self.iteration_safety_factor} is very high. "
330
+ "This may cause excessive iteration limits."
331
+ )
332
+
333
+ # Validate cycle_id
334
+ if self.cycle_id is not None:
335
+ if not isinstance(self.cycle_id, str) or not self.cycle_id.strip():
336
+ errors.append("cycle_id must be a non-empty string")
337
+ elif len(self.cycle_id) > 100:
338
+ warnings.append(f"cycle_id='{self.cycle_id}' is very long (>100 chars)")
339
+
340
+ # Validate parent_cycle
341
+ if self.parent_cycle is not None:
342
+ if not isinstance(self.parent_cycle, str) or not self.parent_cycle.strip():
343
+ errors.append("parent_cycle must be a non-empty string")
344
+ elif self.parent_cycle == self.cycle_id:
345
+ errors.append("parent_cycle cannot be the same as cycle_id")
346
+
347
+ # Validate condition
348
+ if self.condition is not None:
349
+ if not isinstance(self.condition, str) or not self.condition.strip():
350
+ errors.append(
351
+ "condition must be a non-empty string expression. "
352
+ "Examples: 'retry_count < 3', 'needs_improvement == True'"
353
+ )
354
+
355
+ # Validate priority
356
+ if not isinstance(self.priority, int):
357
+ errors.append(f"priority must be an integer, got {type(self.priority)}")
358
+ elif abs(self.priority) > 1000:
359
+ warnings.append(f"priority={self.priority} is very high/low")
360
+
361
+ # Log warnings
362
+ for warning in warnings:
363
+ logger.warning(f"CycleConfig validation warning: {warning}")
364
+
365
+ # Raise errors if any found
366
+ if errors:
367
+ error_message = "CycleConfig validation failed:\n" + "\n".join(
368
+ f"• {error}" for error in errors
369
+ )
370
+ raise CycleConfigurationError(
371
+ error_message,
372
+ error_code="CYCLE_CONFIG_001",
373
+ suggestions=[
374
+ "Ensure at least one termination condition (max_iterations, convergence_check, or timeout)",
375
+ "Use positive values for numeric parameters",
376
+ "Avoid unsafe operations in convergence expressions",
377
+ "Check the CycleConfig documentation for valid parameter ranges",
378
+ ],
379
+ )
380
+
381
+ def get_effective_max_iterations(self) -> Optional[int]:
382
+ """
383
+ Get the effective maximum iterations with safety factor applied.
384
+
385
+ Calculates the actual maximum iterations that will be used during
386
+ cycle execution, including the safety factor multiplier to prevent
387
+ runaway cycles even when convergence conditions fail.
388
+
389
+ Returns:
390
+ Optional[int]: Effective maximum iterations, or None if not configured
391
+
392
+ Side Effects:
393
+ None - this is a pure calculation method
394
+
395
+ Example:
396
+ >>> config = CycleConfig(max_iterations=100, iteration_safety_factor=1.5)
397
+ >>> config.get_effective_max_iterations()
398
+ 150
399
+ """
400
+ if self.max_iterations is None:
401
+ return None
402
+ return int(self.max_iterations * self.iteration_safety_factor)
403
+
404
+ def to_dict(self) -> Dict[str, Any]:
405
+ """
406
+ Convert configuration to dictionary format.
407
+
408
+ Serializes the configuration to a dictionary suitable for JSON/YAML
409
+ export, API transmission, or storage. Excludes None values and
410
+ callable convergence checks for clean serialization.
411
+
412
+ Returns:
413
+ Dict[str, Any]: Dictionary representation of configuration
414
+
415
+ Side Effects:
416
+ None - this method is pure
417
+
418
+ Example:
419
+ >>> config = CycleConfig(max_iterations=100)
420
+ >>> config.to_dict()
421
+ {'max_iterations': 100, 'iteration_safety_factor': 1.5, ...}
422
+ """
423
+ result = {}
424
+
425
+ for key, value in self.__dict__.items():
426
+ if value is not None:
427
+ # Skip callable convergence_check for serialization
428
+ if key == "convergence_check" and callable(value):
429
+ result[key] = "<callable>"
430
+ else:
431
+ result[key] = value
432
+
433
+ return result
434
+
435
+ @classmethod
436
+ def from_dict(cls, data: Dict[str, Any]) -> "CycleConfig":
437
+ """
438
+ Create configuration from dictionary data.
439
+
440
+ Deserializes a configuration from dictionary format, typically
441
+ loaded from JSON/YAML files or API requests. Handles missing
442
+ fields gracefully with default values.
443
+
444
+ Args:
445
+ data (Dict[str, Any]): Dictionary containing configuration data
446
+
447
+ Returns:
448
+ CycleConfig: New configuration instance
449
+
450
+ Raises:
451
+ CycleConfigurationError: If data contains invalid values
452
+
453
+ Side Effects:
454
+ Validates the resulting configuration automatically
455
+
456
+ Example:
457
+ >>> data = {'max_iterations': 100, 'timeout': 60.0}
458
+ >>> config = CycleConfig.from_dict(data)
459
+ """
460
+ # Filter out unknown fields
461
+ known_fields = {f.name for f in cls.__dataclass_fields__.values()}
462
+ filtered_data = {k: v for k, v in data.items() if k in known_fields}
463
+
464
+ try:
465
+ return cls(**filtered_data)
466
+ except Exception as e:
467
+ raise CycleConfigurationError(
468
+ f"Failed to create CycleConfig from data: {e}"
469
+ ) from e
470
+
471
+ def merge(self, other: "CycleConfig") -> "CycleConfig":
472
+ """
473
+ Merge this configuration with another, with other taking precedence.
474
+
475
+ Creates a new configuration by merging two configurations, where
476
+ non-None values from the other configuration override values in
477
+ this configuration. Useful for applying templates and overlays.
478
+
479
+ Args:
480
+ other (CycleConfig): Configuration to merge with (takes precedence)
481
+
482
+ Returns:
483
+ CycleConfig: New merged configuration instance
484
+
485
+ Raises:
486
+ CycleConfigurationError: If merged configuration is invalid
487
+
488
+ Side Effects:
489
+ Validates the resulting merged configuration
490
+
491
+ Example:
492
+ >>> base = CycleConfig(max_iterations=100)
493
+ >>> override = CycleConfig(timeout=60.0, cycle_id="custom")
494
+ >>> merged = base.merge(override)
495
+ >>> # Result has max_iterations=100, timeout=60.0, cycle_id="custom"
496
+ """
497
+ merged_data = {}
498
+
499
+ # Start with this configuration
500
+ for key, value in self.__dict__.items():
501
+ if value is not None:
502
+ merged_data[key] = value
503
+
504
+ # Override with other configuration
505
+ for key, value in other.__dict__.items():
506
+ if value is not None:
507
+ merged_data[key] = value
508
+
509
+ return CycleConfig(**merged_data)
510
+
511
+ def create_template(self, template_name: str) -> Dict[str, Any]:
512
+ """
513
+ Create a reusable template from this configuration.
514
+
515
+ Exports the configuration as a named template that can be stored,
516
+ shared, and reused across multiple workflows. Templates include
517
+ metadata about their intended use case and recommended parameters.
518
+
519
+ Args:
520
+ template_name (str): Name for the template
521
+
522
+ Returns:
523
+ Dict[str, Any]: Template data including metadata
524
+
525
+ Side Effects:
526
+ None - this method is pure
527
+
528
+ Example:
529
+ >>> config = CycleConfig(max_iterations=50, convergence_check="quality > 0.9")
530
+ >>> template = config.create_template("quality_optimization")
531
+ """
532
+ template_data = {
533
+ "template_name": template_name,
534
+ "description": self.description or f"Cycle template: {template_name}",
535
+ "created_from": "CycleConfig.create_template()",
536
+ "configuration": self.to_dict(),
537
+ "usage_notes": {
538
+ "max_iterations": "Adjust based on expected convergence time",
539
+ "convergence_check": "Modify condition for your specific metrics",
540
+ "timeout": "Set based on acceptable execution time",
541
+ },
542
+ }
543
+
544
+ return template_data
545
+
546
+ def __repr__(self) -> str:
547
+ """
548
+ Return string representation of the configuration.
549
+
550
+ Returns:
551
+ str: Human-readable representation showing key configuration values
552
+
553
+ Example:
554
+ >>> config = CycleConfig(max_iterations=100, timeout=60.0)
555
+ >>> str(config)
556
+ 'CycleConfig(max_iterations=100, timeout=60.0, cycle_id=None)'
557
+ """
558
+ key_params = []
559
+
560
+ if self.max_iterations is not None:
561
+ key_params.append(f"max_iterations={self.max_iterations}")
562
+ if self.convergence_check is not None:
563
+ conv_str = (
564
+ self.convergence_check
565
+ if isinstance(self.convergence_check, str)
566
+ else "<callable>"
567
+ )
568
+ key_params.append(f"convergence_check='{conv_str}'")
569
+ if self.timeout is not None:
570
+ key_params.append(f"timeout={self.timeout}")
571
+ if self.cycle_id is not None:
572
+ key_params.append(f"cycle_id='{self.cycle_id}'")
573
+
574
+ return f"CycleConfig({', '.join(key_params)})"
575
+
576
+
577
+ # Pre-defined configuration templates for common use cases
578
+ class CycleTemplates:
579
+ """
580
+ Pre-defined cycle configuration templates for common patterns.
581
+
582
+ This class provides factory methods for creating CycleConfig instances
583
+ optimized for specific use cases, reducing boilerplate and ensuring
584
+ best practices are followed for common cycle patterns.
585
+
586
+ Design Philosophy:
587
+ Provides curated, tested configurations for common cycle patterns
588
+ to reduce setup time and ensure optimal performance. Templates
589
+ can be customized after creation for specific requirements.
590
+
591
+ Example:
592
+ >>> # Quick optimization cycle
593
+ >>> config = CycleTemplates.optimization_loop(max_iterations=50)
594
+
595
+ >>> # Retry logic with exponential backoff
596
+ >>> config = CycleTemplates.retry_cycle(max_retries=3)
597
+ """
598
+
599
+ @staticmethod
600
+ def optimization_loop(
601
+ max_iterations: int = 100,
602
+ convergence_threshold: float = 0.01,
603
+ timeout: Optional[float] = None,
604
+ ) -> CycleConfig:
605
+ """
606
+ Create configuration for optimization cycles.
607
+
608
+ Optimized for iterative improvement algorithms like gradient descent,
609
+ quality improvement, or parameter tuning. Focuses on convergence
610
+ detection with reasonable iteration limits.
611
+
612
+ Args:
613
+ max_iterations (int): Maximum optimization iterations
614
+ convergence_threshold (float): Convergence threshold for stopping
615
+ timeout (Optional[float]): Optional timeout in seconds
616
+
617
+ Returns:
618
+ CycleConfig: Configured for optimization patterns
619
+
620
+ Example:
621
+ >>> config = CycleTemplates.optimization_loop(max_iterations=200)
622
+ >>> workflow.connect("optimizer", "evaluator", cycle_config=config)
623
+ """
624
+ return CycleConfig(
625
+ max_iterations=max_iterations,
626
+ convergence_check=f"improvement < {convergence_threshold}",
627
+ timeout=timeout,
628
+ cycle_id="optimization_loop",
629
+ description="Iterative optimization cycle with convergence detection",
630
+ iteration_safety_factor=2.0, # Higher safety for optimization
631
+ )
632
+
633
+ @staticmethod
634
+ def retry_cycle(
635
+ max_retries: int = 3, timeout_per_retry: float = 30.0
636
+ ) -> CycleConfig:
637
+ """
638
+ Create configuration for retry logic patterns.
639
+
640
+ Optimized for error recovery, API retry logic, and fault-tolerant
641
+ operations. Includes reasonable timeout per attempt and limited
642
+ retry counts to prevent indefinite hanging.
643
+
644
+ Args:
645
+ max_retries (int): Maximum number of retry attempts
646
+ timeout_per_retry (float): Timeout per individual retry
647
+
648
+ Returns:
649
+ CycleConfig: Configured for retry patterns
650
+
651
+ Example:
652
+ >>> config = CycleTemplates.retry_cycle(max_retries=5)
653
+ >>> workflow.connect("api_call", "error_handler", cycle_config=config)
654
+ """
655
+ return CycleConfig(
656
+ max_iterations=max_retries,
657
+ timeout=timeout_per_retry * max_retries,
658
+ cycle_id="retry_cycle",
659
+ description="Retry cycle with exponential backoff support",
660
+ condition="error_occurred == True",
661
+ iteration_safety_factor=1.2, # Conservative safety for retries
662
+ )
663
+
664
+ @staticmethod
665
+ def data_quality_cycle(
666
+ quality_threshold: float = 0.95, max_iterations: int = 10
667
+ ) -> CycleConfig:
668
+ """
669
+ Create configuration for data quality improvement cycles.
670
+
671
+ Optimized for iterative data cleaning, validation, and quality
672
+ enhancement workflows. Focuses on quality metrics and reasonable
673
+ iteration limits for data processing.
674
+
675
+ Args:
676
+ quality_threshold (float): Quality threshold for stopping (0.0-1.0)
677
+ max_iterations (int): Maximum cleaning iterations
678
+
679
+ Returns:
680
+ CycleConfig: Configured for data quality patterns
681
+
682
+ Example:
683
+ >>> config = CycleTemplates.data_quality_cycle(quality_threshold=0.98)
684
+ >>> workflow.connect("cleaner", "validator", cycle_config=config)
685
+ """
686
+ return CycleConfig(
687
+ max_iterations=max_iterations,
688
+ convergence_check=f"quality >= {quality_threshold}",
689
+ timeout=300.0, # 5 minutes for data processing
690
+ cycle_id="data_quality_cycle",
691
+ description="Data quality improvement cycle with quality metrics",
692
+ memory_limit=2048, # 2GB for data processing
693
+ )
694
+
695
+ @staticmethod
696
+ def training_loop(
697
+ max_epochs: int = 100, early_stopping_patience: int = 10
698
+ ) -> CycleConfig:
699
+ """
700
+ Create configuration for machine learning training cycles.
701
+
702
+ Optimized for ML model training with early stopping, validation
703
+ monitoring, and resource management. Includes higher memory limits
704
+ and longer timeouts typical for training workflows.
705
+
706
+ Args:
707
+ max_epochs (int): Maximum training epochs
708
+ early_stopping_patience (int): Epochs to wait for improvement
709
+
710
+ Returns:
711
+ CycleConfig: Configured for ML training patterns
712
+
713
+ Example:
714
+ >>> config = CycleTemplates.training_loop(max_epochs=200)
715
+ >>> workflow.connect("trainer", "evaluator", cycle_config=config)
716
+ """
717
+ return CycleConfig(
718
+ max_iterations=max_epochs,
719
+ convergence_check=f"epochs_without_improvement >= {early_stopping_patience}",
720
+ timeout=3600.0, # 1 hour for training
721
+ cycle_id="training_loop",
722
+ description="ML training cycle with early stopping",
723
+ memory_limit=8192, # 8GB for ML training
724
+ iteration_safety_factor=1.1, # Conservative for long training
725
+ )