2025-08-23 10:19:58 +02:00
|
|
|
# shared/config/rabbitmq_config.py
|
|
|
|
|
"""
|
2025-11-27 15:52:40 +01:00
|
|
|
RabbitMQ configuration for the event system
|
|
|
|
|
|
|
|
|
|
Supports three event classes through a unified topic exchange:
|
|
|
|
|
- ALERT: Actionable events requiring user decision
|
|
|
|
|
- NOTIFICATION: Informational state changes
|
|
|
|
|
- RECOMMENDATION: AI-generated suggestions
|
|
|
|
|
|
|
|
|
|
Routing key pattern: {event_class}.{event_domain}.{severity}
|
|
|
|
|
Examples:
|
|
|
|
|
- alert.inventory.urgent
|
|
|
|
|
- notification.production.info
|
|
|
|
|
- recommendation.demand.medium
|
2025-08-23 10:19:58 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
RABBITMQ_CONFIG = {
|
|
|
|
|
"exchanges": {
|
2025-11-27 15:52:40 +01:00
|
|
|
"events": {
|
|
|
|
|
"name": "events.exchange",
|
|
|
|
|
"type": "topic",
|
|
|
|
|
"durable": True,
|
|
|
|
|
"auto_delete": False
|
|
|
|
|
},
|
|
|
|
|
# Legacy exchange for backward compatibility during migration
|
2025-08-23 10:19:58 +02:00
|
|
|
"alerts": {
|
|
|
|
|
"name": "alerts.exchange",
|
|
|
|
|
"type": "topic",
|
|
|
|
|
"durable": True,
|
|
|
|
|
"auto_delete": False
|
|
|
|
|
},
|
|
|
|
|
"dead_letter": {
|
|
|
|
|
"name": "dlx.exchange",
|
|
|
|
|
"type": "direct",
|
|
|
|
|
"durable": True,
|
|
|
|
|
"auto_delete": False
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"queues": {
|
2025-11-27 15:52:40 +01:00
|
|
|
"event_processing": {
|
|
|
|
|
"name": "event.processing.queue",
|
2025-08-23 10:19:58 +02:00
|
|
|
"durable": True,
|
|
|
|
|
"arguments": {
|
|
|
|
|
"x-message-ttl": 3600000, # 1 hour TTL
|
|
|
|
|
"x-max-length": 10000, # Max 10k messages
|
2025-11-27 15:52:40 +01:00
|
|
|
"x-overflow": "reject-publish",
|
|
|
|
|
"x-dead-letter-exchange": "dlx.exchange",
|
|
|
|
|
"x-dead-letter-routing-key": "failed.events"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
# Legacy queue for backward compatibility
|
|
|
|
|
"alert_processing": {
|
|
|
|
|
"name": "alert.processing.queue",
|
|
|
|
|
"durable": True,
|
|
|
|
|
"arguments": {
|
|
|
|
|
"x-message-ttl": 3600000,
|
|
|
|
|
"x-max-length": 10000,
|
|
|
|
|
"x-overflow": "reject-publish",
|
2025-08-23 10:19:58 +02:00
|
|
|
"x-dead-letter-exchange": "dlx.exchange",
|
|
|
|
|
"x-dead-letter-routing-key": "failed.items"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"dead_letter": {
|
2025-11-27 15:52:40 +01:00
|
|
|
"name": "event.dead_letter.queue",
|
2025-08-23 10:19:58 +02:00
|
|
|
"durable": True,
|
|
|
|
|
"arguments": {
|
|
|
|
|
"x-message-ttl": 86400000 # 24 hours for dead letters
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"bindings": [
|
2025-11-27 15:52:40 +01:00
|
|
|
# New event architecture bindings
|
|
|
|
|
{
|
|
|
|
|
"queue": "event.processing.queue",
|
|
|
|
|
"exchange": "events.exchange",
|
|
|
|
|
"routing_key": "*.*.*" # event_class.event_domain.severity
|
|
|
|
|
},
|
|
|
|
|
# Legacy bindings for backward compatibility
|
2025-08-23 10:19:58 +02:00
|
|
|
{
|
|
|
|
|
"queue": "alert.processing.queue",
|
|
|
|
|
"exchange": "alerts.exchange",
|
|
|
|
|
"routing_key": "*.*.*" # alert/recommendation.severity.service
|
|
|
|
|
},
|
|
|
|
|
{
|
2025-11-27 15:52:40 +01:00
|
|
|
"queue": "event.dead_letter.queue",
|
2025-08-23 10:19:58 +02:00
|
|
|
"exchange": "dlx.exchange",
|
2025-11-27 15:52:40 +01:00
|
|
|
"routing_key": "failed.events"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"queue": "event.dead_letter.queue",
|
|
|
|
|
"exchange": "dlx.exchange",
|
|
|
|
|
"routing_key": "failed.items" # Legacy
|
2025-08-23 10:19:58 +02:00
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"routing_patterns": {
|
2025-11-27 15:52:40 +01:00
|
|
|
# New event architecture patterns
|
|
|
|
|
# event_class.event_domain.severity
|
|
|
|
|
"alert_inventory": "alert.inventory.*",
|
|
|
|
|
"alert_production": "alert.production.*",
|
|
|
|
|
"alert_supply_chain": "alert.supply_chain.*",
|
|
|
|
|
"notification_inventory": "notification.inventory.*",
|
|
|
|
|
"notification_production": "notification.production.*",
|
|
|
|
|
"notification_operations": "notification.operations.*",
|
|
|
|
|
"recommendation_all": "recommendation.*.*",
|
|
|
|
|
|
|
|
|
|
# By severity
|
|
|
|
|
"all_urgent": "*.*.urgent",
|
|
|
|
|
"all_high": "*.*.high",
|
|
|
|
|
"all_medium": "*.*.medium",
|
|
|
|
|
"all_low": "*.*.low",
|
|
|
|
|
|
|
|
|
|
# By event class
|
2025-08-23 10:19:58 +02:00
|
|
|
"all_alerts": "alert.*.*",
|
2025-11-27 15:52:40 +01:00
|
|
|
"all_notifications": "notification.*.*",
|
2025-08-23 10:19:58 +02:00
|
|
|
"all_recommendations": "recommendation.*.*",
|
2025-11-27 15:52:40 +01:00
|
|
|
|
|
|
|
|
# By domain
|
|
|
|
|
"inventory_all": "*.inventory.*",
|
|
|
|
|
"production_all": "*.production.*",
|
|
|
|
|
"supply_chain_all": "*.supply_chain.*",
|
|
|
|
|
"demand_all": "*.demand.*",
|
|
|
|
|
"operations_all": "*.operations.*",
|
|
|
|
|
|
|
|
|
|
# Legacy patterns (for backward compatibility)
|
|
|
|
|
"legacy_alert": "alert.{severity}.{service}",
|
|
|
|
|
"legacy_recommendation": "recommendation.{severity}.{service}",
|
2025-08-23 10:19:58 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-27 15:52:40 +01:00
|
|
|
|
|
|
|
|
def get_routing_key(event_class: str, event_domain: str, severity: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Generate routing key for event publishing.
|
|
|
|
|
|
|
|
|
|
New pattern: {event_class}.{event_domain}.{severity}
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
event_class: 'alert', 'notification', or 'recommendation'
|
|
|
|
|
event_domain: 'inventory', 'production', 'supply_chain', 'demand', 'operations'
|
|
|
|
|
severity: 'urgent', 'high', 'medium', 'low'
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Routing key string
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
>>> get_routing_key('alert', 'inventory', 'urgent')
|
|
|
|
|
'alert.inventory.urgent'
|
|
|
|
|
>>> get_routing_key('notification', 'production', 'info')
|
|
|
|
|
'notification.production.info'
|
|
|
|
|
"""
|
|
|
|
|
return f"{event_class}.{event_domain}.{severity}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_legacy_routing_key(item_type: str, severity: str, service: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Generate legacy routing key for backward compatibility.
|
|
|
|
|
|
|
|
|
|
Legacy pattern: {item_type}.{severity}.{service}
|
|
|
|
|
|
|
|
|
|
TODO: Remove after migration is complete.
|
|
|
|
|
"""
|
2025-08-23 10:19:58 +02:00
|
|
|
return f"{item_type}.{severity}.{service}"
|
|
|
|
|
|
2025-11-27 15:52:40 +01:00
|
|
|
|
|
|
|
|
def get_binding_patterns(
|
|
|
|
|
event_classes: list = None,
|
|
|
|
|
event_domains: list = None,
|
|
|
|
|
severities: list = None
|
|
|
|
|
) -> list:
|
|
|
|
|
"""
|
|
|
|
|
Generate binding patterns for selective consumption.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
event_classes: List of event classes to bind (default: all)
|
|
|
|
|
event_domains: List of event domains to bind (default: all)
|
|
|
|
|
severities: List of severities to bind (default: all)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
List of routing key patterns
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
>>> get_binding_patterns(['alert'], ['inventory'], ['urgent', 'high'])
|
|
|
|
|
['alert.inventory.urgent', 'alert.inventory.high']
|
|
|
|
|
"""
|
2025-08-23 10:19:58 +02:00
|
|
|
patterns = []
|
2025-11-27 15:52:40 +01:00
|
|
|
|
|
|
|
|
event_classes = event_classes or ["alert", "notification", "recommendation"]
|
|
|
|
|
event_domains = event_domains or ["inventory", "production", "supply_chain", "demand", "operations"]
|
2025-08-23 10:19:58 +02:00
|
|
|
severities = severities or ["urgent", "high", "medium", "low"]
|
2025-11-27 15:52:40 +01:00
|
|
|
|
|
|
|
|
for event_class in event_classes:
|
|
|
|
|
for event_domain in event_domains:
|
|
|
|
|
for severity in severities:
|
|
|
|
|
patterns.append(f"{event_class}.{event_domain}.{severity}")
|
|
|
|
|
|
|
|
|
|
return patterns
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def priority_score_to_severity(priority_score: int) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Convert priority score (0-100) to severity level.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
priority_score: Priority score (0-100)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Severity level: 'urgent', 'high', 'medium', or 'low'
|
|
|
|
|
"""
|
|
|
|
|
if priority_score >= 90:
|
|
|
|
|
return "urgent"
|
|
|
|
|
elif priority_score >= 70:
|
|
|
|
|
return "high"
|
|
|
|
|
elif priority_score >= 50:
|
|
|
|
|
return "medium"
|
|
|
|
|
else:
|
|
|
|
|
return "low"
|