kailash 0.1.4__py3-none-any.whl → 0.2.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 (83) 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 +38 -0
  21. kailash/nodes/ai/a2a.py +1790 -0
  22. kailash/nodes/ai/agents.py +116 -2
  23. kailash/nodes/ai/ai_providers.py +206 -8
  24. kailash/nodes/ai/intelligent_agent_orchestrator.py +2108 -0
  25. kailash/nodes/ai/iterative_llm_agent.py +1280 -0
  26. kailash/nodes/ai/llm_agent.py +324 -1
  27. kailash/nodes/ai/self_organizing.py +1623 -0
  28. kailash/nodes/api/http.py +106 -25
  29. kailash/nodes/api/rest.py +116 -21
  30. kailash/nodes/base.py +15 -2
  31. kailash/nodes/base_async.py +45 -0
  32. kailash/nodes/base_cycle_aware.py +374 -0
  33. kailash/nodes/base_with_acl.py +338 -0
  34. kailash/nodes/code/python.py +135 -27
  35. kailash/nodes/data/readers.py +116 -53
  36. kailash/nodes/data/writers.py +16 -6
  37. kailash/nodes/logic/__init__.py +8 -0
  38. kailash/nodes/logic/async_operations.py +48 -9
  39. kailash/nodes/logic/convergence.py +642 -0
  40. kailash/nodes/logic/loop.py +153 -0
  41. kailash/nodes/logic/operations.py +212 -27
  42. kailash/nodes/logic/workflow.py +26 -18
  43. kailash/nodes/mixins/__init__.py +11 -0
  44. kailash/nodes/mixins/mcp.py +228 -0
  45. kailash/nodes/mixins.py +387 -0
  46. kailash/nodes/transform/__init__.py +8 -1
  47. kailash/nodes/transform/processors.py +119 -4
  48. kailash/runtime/__init__.py +2 -1
  49. kailash/runtime/access_controlled.py +458 -0
  50. kailash/runtime/local.py +106 -33
  51. kailash/runtime/parallel_cyclic.py +529 -0
  52. kailash/sdk_exceptions.py +90 -5
  53. kailash/security.py +845 -0
  54. kailash/tracking/manager.py +38 -15
  55. kailash/tracking/models.py +1 -1
  56. kailash/tracking/storage/filesystem.py +30 -2
  57. kailash/utils/__init__.py +8 -0
  58. kailash/workflow/__init__.py +18 -0
  59. kailash/workflow/convergence.py +270 -0
  60. kailash/workflow/cycle_analyzer.py +768 -0
  61. kailash/workflow/cycle_builder.py +573 -0
  62. kailash/workflow/cycle_config.py +709 -0
  63. kailash/workflow/cycle_debugger.py +760 -0
  64. kailash/workflow/cycle_exceptions.py +601 -0
  65. kailash/workflow/cycle_profiler.py +671 -0
  66. kailash/workflow/cycle_state.py +338 -0
  67. kailash/workflow/cyclic_runner.py +985 -0
  68. kailash/workflow/graph.py +500 -39
  69. kailash/workflow/migration.py +768 -0
  70. kailash/workflow/safety.py +365 -0
  71. kailash/workflow/templates.py +744 -0
  72. kailash/workflow/validation.py +693 -0
  73. {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/METADATA +446 -13
  74. kailash-0.2.0.dist-info/RECORD +125 -0
  75. kailash/nodes/mcp/__init__.py +0 -11
  76. kailash/nodes/mcp/client.py +0 -554
  77. kailash/nodes/mcp/resource.py +0 -682
  78. kailash/nodes/mcp/server.py +0 -577
  79. kailash-0.1.4.dist-info/RECORD +0 -85
  80. {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/WHEEL +0 -0
  81. {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/entry_points.txt +0 -0
  82. {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/licenses/LICENSE +0 -0
  83. {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,709 @@
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 = ['import ', 'exec(', 'eval(', '__', 'open(', 'file(']
267
+ for pattern in unsafe_patterns:
268
+ if pattern in self.convergence_check:
269
+ errors.append(
270
+ f"Convergence condition contains unsafe operation: '{pattern}'. "
271
+ "Use simple comparison expressions only."
272
+ )
273
+ elif not callable(self.convergence_check):
274
+ errors.append(
275
+ f"convergence_check must be string or callable, got {type(self.convergence_check)}"
276
+ )
277
+
278
+ # Validate timeout
279
+ if self.timeout is not None:
280
+ if not isinstance(self.timeout, (int, float)):
281
+ errors.append(f"timeout must be numeric, got {type(self.timeout)}")
282
+ elif self.timeout <= 0:
283
+ errors.append(
284
+ f"timeout must be positive, got {self.timeout}. "
285
+ "Recommendation: Use 30-300 seconds for most cycles."
286
+ )
287
+ elif self.timeout > 3600:
288
+ warnings.append(
289
+ f"timeout={self.timeout} seconds is very long (>1 hour). "
290
+ "Consider breaking into smaller cycles."
291
+ )
292
+
293
+ # Validate memory_limit
294
+ if self.memory_limit is not None:
295
+ if not isinstance(self.memory_limit, int):
296
+ errors.append(f"memory_limit must be an integer, got {type(self.memory_limit)}")
297
+ elif self.memory_limit <= 0:
298
+ errors.append(
299
+ f"memory_limit must be positive, got {self.memory_limit}. "
300
+ "Recommendation: Use 100-1000 MB for most cycles."
301
+ )
302
+ elif self.memory_limit > 100000: # 100GB
303
+ warnings.append(
304
+ f"memory_limit={self.memory_limit} MB is very high. "
305
+ "Verify this is intentional for your use case."
306
+ )
307
+
308
+ # Validate iteration_safety_factor
309
+ if not isinstance(self.iteration_safety_factor, (int, float)):
310
+ errors.append(f"iteration_safety_factor must be numeric, got {type(self.iteration_safety_factor)}")
311
+ elif self.iteration_safety_factor < 1.0:
312
+ errors.append(
313
+ f"iteration_safety_factor must be >= 1.0, got {self.iteration_safety_factor}. "
314
+ "This factor provides safety buffer for max_iterations."
315
+ )
316
+ elif self.iteration_safety_factor > 10.0:
317
+ warnings.append(
318
+ f"iteration_safety_factor={self.iteration_safety_factor} is very high. "
319
+ "This may cause excessive iteration limits."
320
+ )
321
+
322
+ # Validate cycle_id
323
+ if self.cycle_id is not None:
324
+ if not isinstance(self.cycle_id, str) or not self.cycle_id.strip():
325
+ errors.append("cycle_id must be a non-empty string")
326
+ elif len(self.cycle_id) > 100:
327
+ warnings.append(f"cycle_id='{self.cycle_id}' is very long (>100 chars)")
328
+
329
+ # Validate parent_cycle
330
+ if self.parent_cycle is not None:
331
+ if not isinstance(self.parent_cycle, str) or not self.parent_cycle.strip():
332
+ errors.append("parent_cycle must be a non-empty string")
333
+ elif self.parent_cycle == self.cycle_id:
334
+ errors.append("parent_cycle cannot be the same as cycle_id")
335
+
336
+ # Validate condition
337
+ if self.condition is not None:
338
+ if not isinstance(self.condition, str) or not self.condition.strip():
339
+ errors.append(
340
+ "condition must be a non-empty string expression. "
341
+ "Examples: 'retry_count < 3', 'needs_improvement == True'"
342
+ )
343
+
344
+ # Validate priority
345
+ if not isinstance(self.priority, int):
346
+ errors.append(f"priority must be an integer, got {type(self.priority)}")
347
+ elif abs(self.priority) > 1000:
348
+ warnings.append(f"priority={self.priority} is very high/low")
349
+
350
+ # Log warnings
351
+ for warning in warnings:
352
+ logger.warning(f"CycleConfig validation warning: {warning}")
353
+
354
+ # Raise errors if any found
355
+ if errors:
356
+ error_message = "CycleConfig validation failed:\n" + "\n".join(f"• {error}" for error in errors)
357
+ raise CycleConfigurationError(
358
+ error_message,
359
+ error_code="CYCLE_CONFIG_001",
360
+ suggestions=[
361
+ "Ensure at least one termination condition (max_iterations, convergence_check, or timeout)",
362
+ "Use positive values for numeric parameters",
363
+ "Avoid unsafe operations in convergence expressions",
364
+ "Check the CycleConfig documentation for valid parameter ranges"
365
+ ]
366
+ )
367
+
368
+ def get_effective_max_iterations(self) -> Optional[int]:
369
+ """
370
+ Get the effective maximum iterations with safety factor applied.
371
+
372
+ Calculates the actual maximum iterations that will be used during
373
+ cycle execution, including the safety factor multiplier to prevent
374
+ runaway cycles even when convergence conditions fail.
375
+
376
+ Returns:
377
+ Optional[int]: Effective maximum iterations, or None if not configured
378
+
379
+ Side Effects:
380
+ None - this is a pure calculation method
381
+
382
+ Example:
383
+ >>> config = CycleConfig(max_iterations=100, iteration_safety_factor=1.5)
384
+ >>> config.get_effective_max_iterations()
385
+ 150
386
+ """
387
+ if self.max_iterations is None:
388
+ return None
389
+ return int(self.max_iterations * self.iteration_safety_factor)
390
+
391
+ def to_dict(self) -> Dict[str, Any]:
392
+ """
393
+ Convert configuration to dictionary format.
394
+
395
+ Serializes the configuration to a dictionary suitable for JSON/YAML
396
+ export, API transmission, or storage. Excludes None values and
397
+ callable convergence checks for clean serialization.
398
+
399
+ Returns:
400
+ Dict[str, Any]: Dictionary representation of configuration
401
+
402
+ Side Effects:
403
+ None - this method is pure
404
+
405
+ Example:
406
+ >>> config = CycleConfig(max_iterations=100)
407
+ >>> config.to_dict()
408
+ {'max_iterations': 100, 'iteration_safety_factor': 1.5, ...}
409
+ """
410
+ result = {}
411
+
412
+ for key, value in self.__dict__.items():
413
+ if value is not None:
414
+ # Skip callable convergence_check for serialization
415
+ if key == 'convergence_check' and callable(value):
416
+ result[key] = '<callable>'
417
+ else:
418
+ result[key] = value
419
+
420
+ return result
421
+
422
+ @classmethod
423
+ def from_dict(cls, data: Dict[str, Any]) -> "CycleConfig":
424
+ """
425
+ Create configuration from dictionary data.
426
+
427
+ Deserializes a configuration from dictionary format, typically
428
+ loaded from JSON/YAML files or API requests. Handles missing
429
+ fields gracefully with default values.
430
+
431
+ Args:
432
+ data (Dict[str, Any]): Dictionary containing configuration data
433
+
434
+ Returns:
435
+ CycleConfig: New configuration instance
436
+
437
+ Raises:
438
+ CycleConfigurationError: If data contains invalid values
439
+
440
+ Side Effects:
441
+ Validates the resulting configuration automatically
442
+
443
+ Example:
444
+ >>> data = {'max_iterations': 100, 'timeout': 60.0}
445
+ >>> config = CycleConfig.from_dict(data)
446
+ """
447
+ # Filter out unknown fields
448
+ known_fields = {f.name for f in cls.__dataclass_fields__.values()}
449
+ filtered_data = {k: v for k, v in data.items() if k in known_fields}
450
+
451
+ try:
452
+ return cls(**filtered_data)
453
+ except Exception as e:
454
+ raise CycleConfigurationError(f"Failed to create CycleConfig from data: {e}") from e
455
+
456
+ def merge(self, other: "CycleConfig") -> "CycleConfig":
457
+ """
458
+ Merge this configuration with another, with other taking precedence.
459
+
460
+ Creates a new configuration by merging two configurations, where
461
+ non-None values from the other configuration override values in
462
+ this configuration. Useful for applying templates and overlays.
463
+
464
+ Args:
465
+ other (CycleConfig): Configuration to merge with (takes precedence)
466
+
467
+ Returns:
468
+ CycleConfig: New merged configuration instance
469
+
470
+ Raises:
471
+ CycleConfigurationError: If merged configuration is invalid
472
+
473
+ Side Effects:
474
+ Validates the resulting merged configuration
475
+
476
+ Example:
477
+ >>> base = CycleConfig(max_iterations=100)
478
+ >>> override = CycleConfig(timeout=60.0, cycle_id="custom")
479
+ >>> merged = base.merge(override)
480
+ >>> # Result has max_iterations=100, timeout=60.0, cycle_id="custom"
481
+ """
482
+ merged_data = {}
483
+
484
+ # Start with this configuration
485
+ for key, value in self.__dict__.items():
486
+ if value is not None:
487
+ merged_data[key] = value
488
+
489
+ # Override with other configuration
490
+ for key, value in other.__dict__.items():
491
+ if value is not None:
492
+ merged_data[key] = value
493
+
494
+ return CycleConfig(**merged_data)
495
+
496
+ def create_template(self, template_name: str) -> Dict[str, Any]:
497
+ """
498
+ Create a reusable template from this configuration.
499
+
500
+ Exports the configuration as a named template that can be stored,
501
+ shared, and reused across multiple workflows. Templates include
502
+ metadata about their intended use case and recommended parameters.
503
+
504
+ Args:
505
+ template_name (str): Name for the template
506
+
507
+ Returns:
508
+ Dict[str, Any]: Template data including metadata
509
+
510
+ Side Effects:
511
+ None - this method is pure
512
+
513
+ Example:
514
+ >>> config = CycleConfig(max_iterations=50, convergence_check="quality > 0.9")
515
+ >>> template = config.create_template("quality_optimization")
516
+ """
517
+ template_data = {
518
+ "template_name": template_name,
519
+ "description": self.description or f"Cycle template: {template_name}",
520
+ "created_from": "CycleConfig.create_template()",
521
+ "configuration": self.to_dict(),
522
+ "usage_notes": {
523
+ "max_iterations": "Adjust based on expected convergence time",
524
+ "convergence_check": "Modify condition for your specific metrics",
525
+ "timeout": "Set based on acceptable execution time"
526
+ }
527
+ }
528
+
529
+ return template_data
530
+
531
+ def __repr__(self) -> str:
532
+ """
533
+ Return string representation of the configuration.
534
+
535
+ Returns:
536
+ str: Human-readable representation showing key configuration values
537
+
538
+ Example:
539
+ >>> config = CycleConfig(max_iterations=100, timeout=60.0)
540
+ >>> str(config)
541
+ 'CycleConfig(max_iterations=100, timeout=60.0, cycle_id=None)'
542
+ """
543
+ key_params = []
544
+
545
+ if self.max_iterations is not None:
546
+ key_params.append(f"max_iterations={self.max_iterations}")
547
+ if self.convergence_check is not None:
548
+ conv_str = self.convergence_check if isinstance(self.convergence_check, str) else "<callable>"
549
+ key_params.append(f"convergence_check='{conv_str}'")
550
+ if self.timeout is not None:
551
+ key_params.append(f"timeout={self.timeout}")
552
+ if self.cycle_id is not None:
553
+ key_params.append(f"cycle_id='{self.cycle_id}'")
554
+
555
+ return f"CycleConfig({', '.join(key_params)})"
556
+
557
+
558
+ # Pre-defined configuration templates for common use cases
559
+ class CycleTemplates:
560
+ """
561
+ Pre-defined cycle configuration templates for common patterns.
562
+
563
+ This class provides factory methods for creating CycleConfig instances
564
+ optimized for specific use cases, reducing boilerplate and ensuring
565
+ best practices are followed for common cycle patterns.
566
+
567
+ Design Philosophy:
568
+ Provides curated, tested configurations for common cycle patterns
569
+ to reduce setup time and ensure optimal performance. Templates
570
+ can be customized after creation for specific requirements.
571
+
572
+ Example:
573
+ >>> # Quick optimization cycle
574
+ >>> config = CycleTemplates.optimization_loop(max_iterations=50)
575
+
576
+ >>> # Retry logic with exponential backoff
577
+ >>> config = CycleTemplates.retry_cycle(max_retries=3)
578
+ """
579
+
580
+ @staticmethod
581
+ def optimization_loop(
582
+ max_iterations: int = 100,
583
+ convergence_threshold: float = 0.01,
584
+ timeout: Optional[float] = None
585
+ ) -> CycleConfig:
586
+ """
587
+ Create configuration for optimization cycles.
588
+
589
+ Optimized for iterative improvement algorithms like gradient descent,
590
+ quality improvement, or parameter tuning. Focuses on convergence
591
+ detection with reasonable iteration limits.
592
+
593
+ Args:
594
+ max_iterations (int): Maximum optimization iterations
595
+ convergence_threshold (float): Convergence threshold for stopping
596
+ timeout (Optional[float]): Optional timeout in seconds
597
+
598
+ Returns:
599
+ CycleConfig: Configured for optimization patterns
600
+
601
+ Example:
602
+ >>> config = CycleTemplates.optimization_loop(max_iterations=200)
603
+ >>> workflow.connect("optimizer", "evaluator", cycle_config=config)
604
+ """
605
+ return CycleConfig(
606
+ max_iterations=max_iterations,
607
+ convergence_check=f"improvement < {convergence_threshold}",
608
+ timeout=timeout,
609
+ cycle_id="optimization_loop",
610
+ description="Iterative optimization cycle with convergence detection",
611
+ iteration_safety_factor=2.0 # Higher safety for optimization
612
+ )
613
+
614
+ @staticmethod
615
+ def retry_cycle(
616
+ max_retries: int = 3,
617
+ timeout_per_retry: float = 30.0
618
+ ) -> CycleConfig:
619
+ """
620
+ Create configuration for retry logic patterns.
621
+
622
+ Optimized for error recovery, API retry logic, and fault-tolerant
623
+ operations. Includes reasonable timeout per attempt and limited
624
+ retry counts to prevent indefinite hanging.
625
+
626
+ Args:
627
+ max_retries (int): Maximum number of retry attempts
628
+ timeout_per_retry (float): Timeout per individual retry
629
+
630
+ Returns:
631
+ CycleConfig: Configured for retry patterns
632
+
633
+ Example:
634
+ >>> config = CycleTemplates.retry_cycle(max_retries=5)
635
+ >>> workflow.connect("api_call", "error_handler", cycle_config=config)
636
+ """
637
+ return CycleConfig(
638
+ max_iterations=max_retries,
639
+ timeout=timeout_per_retry * max_retries,
640
+ cycle_id="retry_cycle",
641
+ description="Retry cycle with exponential backoff support",
642
+ condition="error_occurred == True",
643
+ iteration_safety_factor=1.2 # Conservative safety for retries
644
+ )
645
+
646
+ @staticmethod
647
+ def data_quality_cycle(
648
+ quality_threshold: float = 0.95,
649
+ max_iterations: int = 10
650
+ ) -> CycleConfig:
651
+ """
652
+ Create configuration for data quality improvement cycles.
653
+
654
+ Optimized for iterative data cleaning, validation, and quality
655
+ enhancement workflows. Focuses on quality metrics and reasonable
656
+ iteration limits for data processing.
657
+
658
+ Args:
659
+ quality_threshold (float): Quality threshold for stopping (0.0-1.0)
660
+ max_iterations (int): Maximum cleaning iterations
661
+
662
+ Returns:
663
+ CycleConfig: Configured for data quality patterns
664
+
665
+ Example:
666
+ >>> config = CycleTemplates.data_quality_cycle(quality_threshold=0.98)
667
+ >>> workflow.connect("cleaner", "validator", cycle_config=config)
668
+ """
669
+ return CycleConfig(
670
+ max_iterations=max_iterations,
671
+ convergence_check=f"quality >= {quality_threshold}",
672
+ timeout=300.0, # 5 minutes for data processing
673
+ cycle_id="data_quality_cycle",
674
+ description="Data quality improvement cycle with quality metrics",
675
+ memory_limit=2048, # 2GB for data processing
676
+ )
677
+
678
+ @staticmethod
679
+ def training_loop(
680
+ max_epochs: int = 100,
681
+ early_stopping_patience: int = 10
682
+ ) -> CycleConfig:
683
+ """
684
+ Create configuration for machine learning training cycles.
685
+
686
+ Optimized for ML model training with early stopping, validation
687
+ monitoring, and resource management. Includes higher memory limits
688
+ and longer timeouts typical for training workflows.
689
+
690
+ Args:
691
+ max_epochs (int): Maximum training epochs
692
+ early_stopping_patience (int): Epochs to wait for improvement
693
+
694
+ Returns:
695
+ CycleConfig: Configured for ML training patterns
696
+
697
+ Example:
698
+ >>> config = CycleTemplates.training_loop(max_epochs=200)
699
+ >>> workflow.connect("trainer", "evaluator", cycle_config=config)
700
+ """
701
+ return CycleConfig(
702
+ max_iterations=max_epochs,
703
+ convergence_check=f"epochs_without_improvement >= {early_stopping_patience}",
704
+ timeout=3600.0, # 1 hour for training
705
+ cycle_id="training_loop",
706
+ description="ML training cycle with early stopping",
707
+ memory_limit=8192, # 8GB for ML training
708
+ iteration_safety_factor=1.1 # Conservative for long training
709
+ )