Workflow Patterns
This section demonstrates common workflow patterns and best practices.
Parallel Processing Pattern
from kailash.workflow import Workflow
from kailash.runtime.parallel import ParallelRuntime
# Create workflow with parallel branches
workflow = Workflow("parallel_pattern", name="Parallel Processing")
# Add nodes that can run in parallel
# ... (node setup)
# Execute with parallel runtime
runtime = ParallelRuntime(max_workers=4)
results, run_id = runtime.execute(workflow)
Error Handling Pattern
from kailash.workflow import Workflow
from kailash.nodes.logic import SwitchNode
# Create workflow with error handling
workflow = Workflow("error_handling", name="Error Handling Pattern")
# Add switch for error routing
error_router = SwitchNode()
workflow.add_node("error_handler", error_router)
# ... (error handling logic)
State Management Pattern
from kailash.workflow import Workflow
from kailash.workflow.state import WorkflowStateWrapper
# Create workflow with state management
workflow = Workflow("stateful_pattern", name="Stateful Workflow")
# Initialize state
state = {"counter": 0, "results": []}
state_wrapper = workflow.create_state_wrapper(state)
# ... (stateful operations)
Cyclic Workflow Patterns
Cyclic workflows enable iterative processing, optimization, and retry patterns.
Retry Pattern with Backoff
from kailash import Workflow
from kailash.nodes.base_cycle_aware import CycleAwareNode
import time
class RetryNode(CycleAwareNode):
def run(self, context, **kwargs):
iteration = self.get_iteration(context)
max_retries = kwargs.get("max_retries", 3)
try:
# Attempt operation
result = self.perform_operation(kwargs["data"])
return {"success": True, "result": result}
except Exception as e:
if iteration < max_retries:
# Exponential backoff
delay = 2 ** iteration
time.sleep(delay)
self.log_cycle_info(context, f"Retry {iteration + 1} after {delay}s")
return {"success": False, "retry": True}
else:
return {"success": False, "error": str(e)}
# Create workflow with retry cycle
workflow = Workflow("retry_pattern")
workflow.add_node("retry", RetryNode())
workflow.create_cycle("retry_cycle") \
.connect("retry", "retry") \
.max_iterations(5) \
.converge_when("success == True") \
.build()
Iterative Optimization Pattern
from kailash.nodes.base_cycle_aware import CycleAwareNode
class GradientDescentNode(CycleAwareNode):
def run(self, context, **kwargs):
iteration = self.get_iteration(context)
prev_state = self.get_previous_state(context)
# Get current parameters
params = prev_state.get("params", kwargs.get("initial_params"))
loss = self.calculate_loss(params)
# Track loss history
self.accumulate_values(context, "loss", loss)
trend = self.detect_convergence_trend(context, "loss")
# Update parameters
learning_rate = 0.01 * (0.95 ** iteration) # Decay
gradient = self.calculate_gradient(params)
new_params = params - learning_rate * gradient
# Save state
self.set_cycle_state({"params": new_params, "loss": loss})
# Check convergence
converged = (
trend["converging"] and trend["stability"] > 0.99 or
loss < 0.001 or
iteration >= 1000
)
return {
"params": new_params,
"loss": loss,
"converged": converged,
"iteration": iteration
}
# Create optimization workflow
workflow = Workflow("optimization")
workflow.add_node("optimizer", GradientDescentNode())
workflow.create_cycle("optimization_cycle") \
.connect("optimizer", "optimizer") \
.max_iterations(1000) \
.converge_when("converged == True") \
.build()
Data Quality Improvement Pattern
class DataQualityNode(CycleAwareNode):
def run(self, context, **kwargs):
data = kwargs["data"]
iteration = self.get_iteration(context)
# Calculate quality metrics
completeness = self.calculate_completeness(data)
accuracy = self.calculate_accuracy(data)
quality_score = (completeness + accuracy) / 2
# Track improvement
self.accumulate_values(context, "quality", quality_score)
trend = self.detect_convergence_trend(context, "quality")
# Apply improvements
if completeness < 0.95:
data = self.fill_missing_values(data)
if accuracy < 0.95:
data = self.fix_outliers(data)
# Log progress
self.log_cycle_info(context,
f"Iteration {iteration}: Quality={quality_score:.2%}")
# Check if we've plateaued
converged = (
quality_score > 0.95 or
trend["plateau_detected"] or
iteration >= 10
)
return {
"data": data,
"quality_score": quality_score,
"converged": converged
}
Stream Processing Pattern
class StreamProcessorNode(CycleAwareNode):
def run(self, context, **kwargs):
stream = kwargs["stream"]
batch_size = kwargs.get("batch_size", 100)
iteration = self.get_iteration(context)
# Calculate batch indices
start = iteration * batch_size
end = min(start + batch_size, len(stream))
# Process batch
batch = stream[start:end]
results = self.process_batch(batch)
# Accumulate results
all_results = self.get_previous_state(context).get("results", [])
all_results.extend(results)
self.set_cycle_state({"results": all_results})
# Check if more data exists
more_data = end < len(stream)
return {
"batch_results": results,
"total_processed": end,
"more_data": more_data,
"all_results": all_results if not more_data else None
}
# Create stream processing workflow
workflow = Workflow("stream_processor")
workflow.add_node("processor", StreamProcessorNode())
workflow.create_cycle("stream_cycle") \
.connect("processor", "processor") \
.converge_when("more_data == False") \
.build()
Multi-Node Cycle Pattern
# Create a cycle with multiple nodes
workflow = Workflow("multi_node_cycle")
# Add nodes for different stages
workflow.add_node("collector", DataCollectorNode())
workflow.add_node("processor", ProcessorNode())
workflow.add_node("validator", ValidatorNode())
workflow.add_node("decider", DecisionNode())
# Connect in a cycle: collector → processor → validator → decider → collector
workflow.connect("collector", "processor")
workflow.connect("processor", "validator")
workflow.connect("validator", "decider")
workflow.create_cycle("processing_cycle") \
.connect("decider", "collector") \
.max_iterations(50) \
.converge_when("should_continue == False") \
.build()
Best Practices for Cyclic Workflows
Always Set Convergence Conditions: Prevent infinite loops
Use State Management: Leverage CycleAwareNode helpers
Track Metrics: Monitor convergence with accumulate_values()
Handle Edge Cases: Consider first/last iteration behavior
Set Reasonable Limits: Use max_iterations as a safety net
Log Progress: Use log_cycle_info() for debugging
Optimize Performance: Cycles add ~0.03ms overhead per iteration
Alert and Notification Patterns
from kailash import Workflow
from kailash.nodes.alerts import DiscordAlertNode
from kailash.nodes.logic import SwitchNode
from kailash.nodes.code import PythonCodeNode
# Error Alert Pattern
def create_error_alert_workflow():
workflow = Workflow("error_alerts")
# Process data (might fail)
processor = PythonCodeNode.from_function(
func=process_data_with_errors,
name="processor"
)
workflow.add_node("process", processor)
# Route based on success/failure
router = SwitchNode(
condition_mapping={
"error": "status == 'error'",
"success": "status == 'success'"
}
)
workflow.add_node("router", router)
# Critical error alert
error_alert = DiscordAlertNode()
workflow.add_node("error_alert", error_alert)
# Success notification
success_alert = DiscordAlertNode()
workflow.add_node("success_alert", success_alert)
# Connect the workflow
workflow.connect("process", "router")
workflow.connect("router", "error_alert", output_key="error")
workflow.connect("router", "success_alert", output_key="success")
return workflow
# Health Dashboard Pattern
def create_health_dashboard():
workflow = Workflow("health_dashboard")
# System health check
health_checker = PythonCodeNode.from_function(
func=check_system_health,
name="health_checker"
)
workflow.add_node("health_check", health_checker)
# Dashboard alert
dashboard = DiscordAlertNode()
workflow.add_node("dashboard", dashboard)
workflow.connect("health_check", "dashboard")
return workflow
# Business KPI Alert Pattern
def create_kpi_alerts():
workflow = Workflow("kpi_alerts")
# Calculate KPIs
kpi_calculator = PythonCodeNode.from_function(
func=calculate_business_kpis,
name="kpi_calculator"
)
workflow.add_node("calculate", kpi_calculator)
# Check thresholds
threshold_checker = SwitchNode(
condition_mapping={
"critical": "revenue < target_revenue * 0.8",
"warning": "revenue < target_revenue * 0.9",
"normal": "default"
}
)
workflow.add_node("threshold_check", threshold_checker)
# Different alert types
critical_alert = DiscordAlertNode()
warning_alert = DiscordAlertNode()
normal_report = DiscordAlertNode()
workflow.add_node("critical_alert", critical_alert)
workflow.add_node("warning_alert", warning_alert)
workflow.add_node("normal_report", normal_report)
# Connect workflow
workflow.connect("calculate", "threshold_check")
workflow.connect("threshold_check", "critical_alert", output_key="critical")
workflow.connect("threshold_check", "warning_alert", output_key="warning")
workflow.connect("threshold_check", "normal_report", output_key="normal")
return workflow
# Execute with parameters
def execute_alert_workflows():
# Error alert execution
error_workflow = create_error_alert_workflow()
runtime.execute(error_workflow, parameters={
"error_alert": {
"webhook_url": "${DISCORD_WEBHOOK}",
"title": "🚨 Processing Error",
"alert_type": "critical",
"mentions": ["@here"]
},
"success_alert": {
"webhook_url": "${DISCORD_WEBHOOK}",
"title": "✅ Processing Complete",
"alert_type": "success"
}
})
# Health dashboard execution
health_workflow = create_health_dashboard()
runtime.execute(health_workflow, parameters={
"dashboard": {
"webhook_url": "${DISCORD_WEBHOOK}",
"title": "📊 System Health Dashboard",
"alert_type": "info",
"username": "Health Monitor",
"fields": [
{"name": "CPU", "value": "{cpu_usage:.1f}%", "inline": True},
{"name": "Memory", "value": "{memory_usage:.1f}%", "inline": True},
{"name": "Disk", "value": "{disk_usage:.1f}%", "inline": True}
]
}
})
Alert Pattern Benefits:
Real-time Notifications: Immediate alerts on failures or thresholds
Rich Formatting: Color-coded embeds with structured data
Escalation Support: Different alert types for different severity levels
Integration Ready: Works with existing monitoring and error handling
For more patterns, see the workflow examples.