omnibase_infra 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.
- omnibase_infra/__init__.py +101 -0
- omnibase_infra/cli/__init__.py +1 -0
- omnibase_infra/cli/commands.py +216 -0
- omnibase_infra/clients/__init__.py +0 -0
- omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +261 -0
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +138 -0
- omnibase_infra/decorators/__init__.py +29 -0
- omnibase_infra/decorators/allow_any.py +109 -0
- omnibase_infra/dlq/__init__.py +90 -0
- omnibase_infra/dlq/constants_dlq.py +57 -0
- omnibase_infra/dlq/models/__init__.py +26 -0
- omnibase_infra/dlq/models/enum_replay_status.py +37 -0
- omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
- omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
- omnibase_infra/dlq/service_dlq_tracking.py +611 -0
- omnibase_infra/enums/__init__.py +123 -0
- omnibase_infra/enums/enum_any_type_violation.py +104 -0
- omnibase_infra/enums/enum_backend_type.py +27 -0
- omnibase_infra/enums/enum_capture_outcome.py +42 -0
- omnibase_infra/enums/enum_capture_state.py +88 -0
- omnibase_infra/enums/enum_chain_violation_type.py +119 -0
- omnibase_infra/enums/enum_circuit_state.py +51 -0
- omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
- omnibase_infra/enums/enum_contract_type.py +84 -0
- omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
- omnibase_infra/enums/enum_dispatch_status.py +191 -0
- omnibase_infra/enums/enum_environment.py +46 -0
- omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
- omnibase_infra/enums/enum_handler_error_type.py +101 -0
- omnibase_infra/enums/enum_handler_loader_error.py +178 -0
- omnibase_infra/enums/enum_handler_source_type.py +87 -0
- omnibase_infra/enums/enum_handler_type.py +77 -0
- omnibase_infra/enums/enum_handler_type_category.py +61 -0
- omnibase_infra/enums/enum_infra_transport_type.py +73 -0
- omnibase_infra/enums/enum_introspection_reason.py +154 -0
- omnibase_infra/enums/enum_message_category.py +213 -0
- omnibase_infra/enums/enum_node_archetype.py +74 -0
- omnibase_infra/enums/enum_node_output_type.py +185 -0
- omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
- omnibase_infra/enums/enum_policy_type.py +32 -0
- omnibase_infra/enums/enum_registration_state.py +261 -0
- omnibase_infra/enums/enum_registration_status.py +33 -0
- omnibase_infra/enums/enum_registry_response_status.py +28 -0
- omnibase_infra/enums/enum_response_status.py +26 -0
- omnibase_infra/enums/enum_retry_error_category.py +98 -0
- omnibase_infra/enums/enum_security_rule_id.py +103 -0
- omnibase_infra/enums/enum_selection_strategy.py +91 -0
- omnibase_infra/enums/enum_topic_standard.py +42 -0
- omnibase_infra/enums/enum_validation_severity.py +78 -0
- omnibase_infra/errors/__init__.py +156 -0
- omnibase_infra/errors/error_architecture_violation.py +152 -0
- omnibase_infra/errors/error_chain_propagation.py +188 -0
- omnibase_infra/errors/error_compute_registry.py +92 -0
- omnibase_infra/errors/error_consul.py +132 -0
- omnibase_infra/errors/error_container_wiring.py +243 -0
- omnibase_infra/errors/error_event_bus_registry.py +102 -0
- omnibase_infra/errors/error_infra.py +608 -0
- omnibase_infra/errors/error_message_type_registry.py +101 -0
- omnibase_infra/errors/error_policy_registry.py +112 -0
- omnibase_infra/errors/error_vault.py +123 -0
- omnibase_infra/event_bus/__init__.py +72 -0
- omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +86 -0
- omnibase_infra/event_bus/event_bus_inmemory.py +743 -0
- omnibase_infra/event_bus/event_bus_kafka.py +1658 -0
- omnibase_infra/event_bus/mixin_kafka_broadcast.py +184 -0
- omnibase_infra/event_bus/mixin_kafka_dlq.py +765 -0
- omnibase_infra/event_bus/models/__init__.py +29 -0
- omnibase_infra/event_bus/models/config/__init__.py +20 -0
- omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +725 -0
- omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
- omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
- omnibase_infra/event_bus/models/model_event_headers.py +115 -0
- omnibase_infra/event_bus/models/model_event_message.py +60 -0
- omnibase_infra/event_bus/topic_constants.py +376 -0
- omnibase_infra/handlers/__init__.py +75 -0
- omnibase_infra/handlers/filesystem/__init__.py +48 -0
- omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
- omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
- omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
- omnibase_infra/handlers/handler_consul.py +787 -0
- omnibase_infra/handlers/handler_db.py +1039 -0
- omnibase_infra/handlers/handler_filesystem.py +1478 -0
- omnibase_infra/handlers/handler_graph.py +1154 -0
- omnibase_infra/handlers/handler_http.py +920 -0
- omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
- omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
- omnibase_infra/handlers/handler_mcp.py +748 -0
- omnibase_infra/handlers/handler_qdrant.py +1076 -0
- omnibase_infra/handlers/handler_vault.py +422 -0
- omnibase_infra/handlers/mcp/__init__.py +19 -0
- omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
- omnibase_infra/handlers/mcp/protocols.py +178 -0
- omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
- omnibase_infra/handlers/mixins/__init__.py +42 -0
- omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
- omnibase_infra/handlers/mixins/mixin_consul_kv.py +337 -0
- omnibase_infra/handlers/mixins/mixin_consul_service.py +277 -0
- omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
- omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
- omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
- omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
- omnibase_infra/handlers/models/__init__.py +286 -0
- omnibase_infra/handlers/models/consul/__init__.py +81 -0
- omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
- omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
- omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
- omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
- omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
- omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
- omnibase_infra/handlers/models/graph/__init__.py +35 -0
- omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
- omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
- omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
- omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
- omnibase_infra/handlers/models/http/__init__.py +50 -0
- omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
- omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
- omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
- omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
- omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
- omnibase_infra/handlers/models/mcp/__init__.py +23 -0
- omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
- omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
- omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
- omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
- omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
- omnibase_infra/handlers/models/model_db_query_response.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
- omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
- omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
- omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
- omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
- omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
- omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
- omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_handler_response.py +103 -0
- omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
- omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
- omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
- omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
- omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
- omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
- omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
- omnibase_infra/handlers/models/model_operation_context.py +187 -0
- omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_retry_state.py +162 -0
- omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
- omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
- omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
- omnibase_infra/handlers/models/vault/__init__.py +69 -0
- omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
- omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
- omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
- omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
- omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
- omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
- omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
- omnibase_infra/handlers/registration_storage/__init__.py +43 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +915 -0
- omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
- omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
- omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
- omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
- omnibase_infra/handlers/service_discovery/__init__.py +43 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +747 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
- omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
- omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
- omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
- omnibase_infra/handlers/service_discovery/models/model_service_info.py +99 -0
- omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
- omnibase_infra/idempotency/__init__.py +94 -0
- omnibase_infra/idempotency/models/__init__.py +43 -0
- omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
- omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
- omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
- omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
- omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
- omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
- omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
- omnibase_infra/idempotency/store_inmemory.py +265 -0
- omnibase_infra/idempotency/store_postgres.py +923 -0
- omnibase_infra/infrastructure/__init__.py +0 -0
- omnibase_infra/mixins/__init__.py +71 -0
- omnibase_infra/mixins/mixin_async_circuit_breaker.py +655 -0
- omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
- omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
- omnibase_infra/mixins/mixin_node_introspection.py +2465 -0
- omnibase_infra/mixins/mixin_retry_execution.py +386 -0
- omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
- omnibase_infra/models/__init__.py +136 -0
- omnibase_infra/models/corpus/__init__.py +17 -0
- omnibase_infra/models/corpus/model_capture_config.py +133 -0
- omnibase_infra/models/corpus/model_capture_result.py +86 -0
- omnibase_infra/models/discovery/__init__.py +42 -0
- omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
- omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
- omnibase_infra/models/discovery/model_introspection_config.py +311 -0
- omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
- omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
- omnibase_infra/models/dispatch/__init__.py +147 -0
- omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
- omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
- omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
- omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
- omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
- omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
- omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
- omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
- omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
- omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
- omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
- omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
- omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
- omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
- omnibase_infra/models/errors/__init__.py +45 -0
- omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
- omnibase_infra/models/errors/model_infra_error_context.py +99 -0
- omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
- omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
- omnibase_infra/models/handlers/__init__.py +37 -0
- omnibase_infra/models/handlers/model_contract_discovery_result.py +80 -0
- omnibase_infra/models/handlers/model_handler_descriptor.py +185 -0
- omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
- omnibase_infra/models/health/__init__.py +9 -0
- omnibase_infra/models/health/model_health_check_result.py +40 -0
- omnibase_infra/models/lifecycle/__init__.py +39 -0
- omnibase_infra/models/logging/__init__.py +51 -0
- omnibase_infra/models/logging/model_log_context.py +756 -0
- omnibase_infra/models/model_retry_error_classification.py +78 -0
- omnibase_infra/models/projection/__init__.py +43 -0
- omnibase_infra/models/projection/model_capability_fields.py +112 -0
- omnibase_infra/models/projection/model_registration_projection.py +434 -0
- omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
- omnibase_infra/models/projection/model_sequence_info.py +182 -0
- omnibase_infra/models/projection/model_snapshot_topic_config.py +590 -0
- omnibase_infra/models/projectors/__init__.py +41 -0
- omnibase_infra/models/projectors/model_projector_column.py +289 -0
- omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
- omnibase_infra/models/projectors/model_projector_index.py +270 -0
- omnibase_infra/models/projectors/model_projector_schema.py +415 -0
- omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
- omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
- omnibase_infra/models/registration/__init__.py +59 -0
- omnibase_infra/models/registration/commands/__init__.py +15 -0
- omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
- omnibase_infra/models/registration/events/__init__.py +56 -0
- omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
- omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
- omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
- omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
- omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
- omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
- omnibase_infra/models/registration/model_node_capabilities.py +179 -0
- omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
- omnibase_infra/models/registration/model_node_introspection_event.py +175 -0
- omnibase_infra/models/registration/model_node_metadata.py +79 -0
- omnibase_infra/models/registration/model_node_registration.py +162 -0
- omnibase_infra/models/registration/model_node_registration_record.py +162 -0
- omnibase_infra/models/registry/__init__.py +29 -0
- omnibase_infra/models/registry/model_domain_constraint.py +202 -0
- omnibase_infra/models/registry/model_message_type_entry.py +271 -0
- omnibase_infra/models/resilience/__init__.py +9 -0
- omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
- omnibase_infra/models/routing/__init__.py +25 -0
- omnibase_infra/models/routing/model_routing_entry.py +52 -0
- omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
- omnibase_infra/models/runtime/__init__.py +40 -0
- omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
- omnibase_infra/models/runtime/model_discovery_error.py +81 -0
- omnibase_infra/models/runtime/model_discovery_result.py +162 -0
- omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
- omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
- omnibase_infra/models/runtime/model_handler_contract.py +280 -0
- omnibase_infra/models/runtime/model_loaded_handler.py +120 -0
- omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
- omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
- omnibase_infra/models/security/__init__.py +50 -0
- omnibase_infra/models/security/classification_levels.py +99 -0
- omnibase_infra/models/security/model_environment_policy.py +145 -0
- omnibase_infra/models/security/model_handler_security_policy.py +107 -0
- omnibase_infra/models/security/model_security_error.py +81 -0
- omnibase_infra/models/security/model_security_validation_result.py +328 -0
- omnibase_infra/models/security/model_security_warning.py +67 -0
- omnibase_infra/models/snapshot/__init__.py +27 -0
- omnibase_infra/models/snapshot/model_field_change.py +65 -0
- omnibase_infra/models/snapshot/model_snapshot.py +270 -0
- omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
- omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
- omnibase_infra/models/types/__init__.py +71 -0
- omnibase_infra/models/validation/__init__.py +89 -0
- omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
- omnibase_infra/models/validation/model_any_type_violation.py +141 -0
- omnibase_infra/models/validation/model_category_match_result.py +345 -0
- omnibase_infra/models/validation/model_chain_violation.py +166 -0
- omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
- omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
- omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
- omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
- omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
- omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
- omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
- omnibase_infra/models/validation/model_output_validation_params.py +74 -0
- omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
- omnibase_infra/models/validation/model_validation_error_params.py +84 -0
- omnibase_infra/models/validation/model_validation_outcome.py +287 -0
- omnibase_infra/nodes/__init__.py +48 -0
- omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
- omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
- omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +208 -0
- omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
- omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
- omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
- omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
- omnibase_infra/nodes/architecture_validator/node.py +262 -0
- omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
- omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
- omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
- omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
- omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +99 -0
- omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
- omnibase_infra/nodes/effects/README.md +358 -0
- omnibase_infra/nodes/effects/__init__.py +26 -0
- omnibase_infra/nodes/effects/contract.yaml +172 -0
- omnibase_infra/nodes/effects/models/__init__.py +32 -0
- omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
- omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
- omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
- omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
- omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
- omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
- omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
- omnibase_infra/nodes/effects/registry_effect.py +525 -0
- omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
- omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
- omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
- omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +475 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +609 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
- omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
- omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
- omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
- omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +525 -0
- omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +392 -0
- omnibase_infra/nodes/node_registration_orchestrator/wiring.py +742 -0
- omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
- omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
- omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
- omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
- omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
- omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +225 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
- omnibase_infra/nodes/node_registration_storage_effect/node.py +109 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +194 -0
- omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
- omnibase_infra/nodes/node_registry_effect/contract.yaml +682 -0
- omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +416 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
- omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
- omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
- omnibase_infra/nodes/node_registry_effect/node.py +165 -0
- omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
- omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
- omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +214 -0
- omnibase_infra/nodes/reducers/__init__.py +30 -0
- omnibase_infra/nodes/reducers/models/__init__.py +32 -0
- omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +76 -0
- omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
- omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
- omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
- omnibase_infra/nodes/reducers/registration_reducer.py +1137 -0
- omnibase_infra/observability/__init__.py +143 -0
- omnibase_infra/observability/constants_metrics.py +91 -0
- omnibase_infra/observability/factory_observability_sink.py +525 -0
- omnibase_infra/observability/handlers/__init__.py +118 -0
- omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
- omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
- omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
- omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
- omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
- omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
- omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
- omnibase_infra/observability/hooks/__init__.py +74 -0
- omnibase_infra/observability/hooks/hook_observability.py +1223 -0
- omnibase_infra/observability/models/__init__.py +30 -0
- omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
- omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
- omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
- omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
- omnibase_infra/observability/sinks/__init__.py +69 -0
- omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
- omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
- omnibase_infra/plugins/__init__.py +27 -0
- omnibase_infra/plugins/examples/__init__.py +28 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
- omnibase_infra/plugins/models/__init__.py +21 -0
- omnibase_infra/plugins/models/model_plugin_context.py +76 -0
- omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
- omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
- omnibase_infra/plugins/plugin_compute_base.py +435 -0
- omnibase_infra/projectors/__init__.py +30 -0
- omnibase_infra/projectors/contracts/__init__.py +63 -0
- omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
- omnibase_infra/projectors/projection_reader_registration.py +1559 -0
- omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
- omnibase_infra/protocols/__init__.py +99 -0
- omnibase_infra/protocols/protocol_capability_projection.py +253 -0
- omnibase_infra/protocols/protocol_capability_query.py +251 -0
- omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
- omnibase_infra/protocols/protocol_event_projector.py +96 -0
- omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
- omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
- omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
- omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
- omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
- omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
- omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
- omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
- omnibase_infra/runtime/__init__.py +296 -0
- omnibase_infra/runtime/binding_config_resolver.py +2706 -0
- omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
- omnibase_infra/runtime/contract_handler_discovery.py +582 -0
- omnibase_infra/runtime/contract_loaders/__init__.py +42 -0
- omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
- omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
- omnibase_infra/runtime/enums/__init__.py +18 -0
- omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
- omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
- omnibase_infra/runtime/envelope_validator.py +179 -0
- omnibase_infra/runtime/handler_contract_source.py +669 -0
- omnibase_infra/runtime/handler_plugin_loader.py +2029 -0
- omnibase_infra/runtime/handler_registry.py +321 -0
- omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
- omnibase_infra/runtime/kernel.py +40 -0
- omnibase_infra/runtime/mixin_policy_validation.py +522 -0
- omnibase_infra/runtime/mixin_semver_cache.py +378 -0
- omnibase_infra/runtime/mixins/__init__.py +17 -0
- omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +757 -0
- omnibase_infra/runtime/models/__init__.py +192 -0
- omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
- omnibase_infra/runtime/models/model_binding_config.py +168 -0
- omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
- omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
- omnibase_infra/runtime/models/model_cached_secret.py +138 -0
- omnibase_infra/runtime/models/model_compute_key.py +138 -0
- omnibase_infra/runtime/models/model_compute_registration.py +97 -0
- omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
- omnibase_infra/runtime/models/model_config_ref.py +331 -0
- omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
- omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
- omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
- omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
- omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
- omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
- omnibase_infra/runtime/models/model_failed_component.py +55 -0
- omnibase_infra/runtime/models/model_health_check_response.py +168 -0
- omnibase_infra/runtime/models/model_health_check_result.py +228 -0
- omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
- omnibase_infra/runtime/models/model_logging_config.py +42 -0
- omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
- omnibase_infra/runtime/models/model_optional_string.py +94 -0
- omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
- omnibase_infra/runtime/models/model_policy_context.py +100 -0
- omnibase_infra/runtime/models/model_policy_key.py +138 -0
- omnibase_infra/runtime/models/model_policy_registration.py +139 -0
- omnibase_infra/runtime/models/model_policy_result.py +103 -0
- omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
- omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
- omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
- omnibase_infra/runtime/models/model_retry_policy.py +105 -0
- omnibase_infra/runtime/models/model_runtime_config.py +150 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_config.py +624 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
- omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
- omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
- omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
- omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
- omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
- omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
- omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
- omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
- omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
- omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
- omnibase_infra/runtime/projector_schema_manager.py +565 -0
- omnibase_infra/runtime/projector_shell.py +1102 -0
- omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
- omnibase_infra/runtime/protocol_contract_source.py +92 -0
- omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
- omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
- omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
- omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
- omnibase_infra/runtime/protocol_policy.py +366 -0
- omnibase_infra/runtime/protocols/__init__.py +27 -0
- omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
- omnibase_infra/runtime/registry/__init__.py +93 -0
- omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
- omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
- omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
- omnibase_infra/runtime/registry/registry_message_type.py +542 -0
- omnibase_infra/runtime/registry/registry_protocol_binding.py +444 -0
- omnibase_infra/runtime/registry_compute.py +1143 -0
- omnibase_infra/runtime/registry_dispatcher.py +678 -0
- omnibase_infra/runtime/registry_policy.py +1502 -0
- omnibase_infra/runtime/runtime_scheduler.py +1070 -0
- omnibase_infra/runtime/secret_resolver.py +2110 -0
- omnibase_infra/runtime/security_metadata_validator.py +776 -0
- omnibase_infra/runtime/service_kernel.py +1573 -0
- omnibase_infra/runtime/service_message_dispatch_engine.py +1805 -0
- omnibase_infra/runtime/service_runtime_host_process.py +2260 -0
- omnibase_infra/runtime/util_container_wiring.py +1123 -0
- omnibase_infra/runtime/util_validation.py +314 -0
- omnibase_infra/runtime/util_version.py +98 -0
- omnibase_infra/runtime/util_wiring.py +566 -0
- omnibase_infra/schemas/schema_registration_projection.sql +320 -0
- omnibase_infra/services/__init__.py +68 -0
- omnibase_infra/services/corpus_capture.py +678 -0
- omnibase_infra/services/service_capability_query.py +945 -0
- omnibase_infra/services/service_health.py +897 -0
- omnibase_infra/services/service_node_selector.py +530 -0
- omnibase_infra/services/service_timeout_emitter.py +682 -0
- omnibase_infra/services/service_timeout_scanner.py +390 -0
- omnibase_infra/services/snapshot/__init__.py +31 -0
- omnibase_infra/services/snapshot/service_snapshot.py +647 -0
- omnibase_infra/services/snapshot/store_inmemory.py +637 -0
- omnibase_infra/services/snapshot/store_postgres.py +1279 -0
- omnibase_infra/shared/__init__.py +8 -0
- omnibase_infra/testing/__init__.py +10 -0
- omnibase_infra/testing/utils.py +23 -0
- omnibase_infra/types/__init__.py +48 -0
- omnibase_infra/types/type_cache_info.py +49 -0
- omnibase_infra/types/type_dsn.py +173 -0
- omnibase_infra/types/type_infra_aliases.py +60 -0
- omnibase_infra/types/typed_dict/__init__.py +21 -0
- omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
- omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
- omnibase_infra/types/typed_dict_capabilities.py +64 -0
- omnibase_infra/utils/__init__.py +89 -0
- omnibase_infra/utils/correlation.py +208 -0
- omnibase_infra/utils/util_datetime.py +372 -0
- omnibase_infra/utils/util_dsn_validation.py +333 -0
- omnibase_infra/utils/util_env_parsing.py +264 -0
- omnibase_infra/utils/util_error_sanitization.py +457 -0
- omnibase_infra/utils/util_pydantic_validators.py +477 -0
- omnibase_infra/utils/util_semver.py +233 -0
- omnibase_infra/validation/__init__.py +307 -0
- omnibase_infra/validation/enums/__init__.py +11 -0
- omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
- omnibase_infra/validation/infra_validators.py +1486 -0
- omnibase_infra/validation/linter_contract.py +907 -0
- omnibase_infra/validation/mixin_any_type_classification.py +120 -0
- omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
- omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
- omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
- omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
- omnibase_infra/validation/models/__init__.py +15 -0
- omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
- omnibase_infra/validation/models/model_contract_violation.py +41 -0
- omnibase_infra/validation/service_validation_aggregator.py +395 -0
- omnibase_infra/validation/validation_exemptions.yaml +1710 -0
- omnibase_infra/validation/validator_any_type.py +715 -0
- omnibase_infra/validation/validator_chain_propagation.py +839 -0
- omnibase_infra/validation/validator_execution_shape.py +465 -0
- omnibase_infra/validation/validator_localhandler.py +261 -0
- omnibase_infra/validation/validator_registration_security.py +410 -0
- omnibase_infra/validation/validator_routing_coverage.py +1020 -0
- omnibase_infra/validation/validator_runtime_shape.py +915 -0
- omnibase_infra/validation/validator_security.py +410 -0
- omnibase_infra/validation/validator_topic_category.py +1152 -0
- omnibase_infra-0.2.1.dist-info/METADATA +197 -0
- omnibase_infra-0.2.1.dist-info/RECORD +675 -0
- omnibase_infra-0.2.1.dist-info/WHEEL +4 -0
- omnibase_infra-0.2.1.dist-info/entry_points.txt +4 -0
- omnibase_infra-0.2.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Dead Letter Queue mixin for Kafka event bus.
|
|
4
|
+
|
|
5
|
+
This module provides DLQ functionality that can be mixed into EventBusKafka
|
|
6
|
+
to handle failed message processing. It supports metrics tracking, callback
|
|
7
|
+
hooks for custom alerting, and proper error sanitization.
|
|
8
|
+
|
|
9
|
+
Features:
|
|
10
|
+
- DLQ message publishing with comprehensive failure metadata
|
|
11
|
+
- Callback registration for custom alerting integration
|
|
12
|
+
- Metrics tracking for monitoring DLQ operations
|
|
13
|
+
- Error sanitization to prevent credential leakage
|
|
14
|
+
- Support for both processed and raw (deserialization failure) messages
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
```python
|
|
18
|
+
class EventBusKafka(MixinKafkaDlq, MixinAsyncCircuitBreaker):
|
|
19
|
+
def __init__(self, config):
|
|
20
|
+
# Initialize DLQ mixin
|
|
21
|
+
self._init_dlq()
|
|
22
|
+
# ... rest of init
|
|
23
|
+
|
|
24
|
+
# DLQ methods are now available:
|
|
25
|
+
# - register_dlq_callback()
|
|
26
|
+
# - dlq_metrics property
|
|
27
|
+
# - _publish_to_dlq()
|
|
28
|
+
# - _publish_raw_to_dlq()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Design Note:
|
|
32
|
+
This mixin assumes the parent class has:
|
|
33
|
+
- self._config: ModelKafkaEventBusConfig with dead_letter_topic
|
|
34
|
+
- self._environment: str for environment context
|
|
35
|
+
- self._group: str for consumer group context
|
|
36
|
+
- self._producer: AIOKafkaProducer | None
|
|
37
|
+
- self._producer_lock: asyncio.Lock for producer access
|
|
38
|
+
- self._timeout_seconds: int for publish timeout
|
|
39
|
+
- self._model_headers_to_kafka(): Method to convert headers
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
import asyncio
|
|
45
|
+
import json
|
|
46
|
+
import logging
|
|
47
|
+
from collections.abc import Awaitable, Callable
|
|
48
|
+
from datetime import UTC, datetime
|
|
49
|
+
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
50
|
+
from uuid import UUID
|
|
51
|
+
|
|
52
|
+
from omnibase_infra.event_bus.models import (
|
|
53
|
+
ModelDlqEvent,
|
|
54
|
+
ModelDlqMetrics,
|
|
55
|
+
ModelEventHeaders,
|
|
56
|
+
ModelEventMessage,
|
|
57
|
+
)
|
|
58
|
+
from omnibase_infra.utils import sanitize_error_message
|
|
59
|
+
|
|
60
|
+
if TYPE_CHECKING:
|
|
61
|
+
from aiokafka import AIOKafkaProducer
|
|
62
|
+
|
|
63
|
+
from omnibase_infra.event_bus.models.config import ModelKafkaEventBusConfig
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@runtime_checkable
|
|
67
|
+
class ProtocolKafkaDlqHost(Protocol):
|
|
68
|
+
"""Protocol defining methods required by MixinKafkaDlq from its host class.
|
|
69
|
+
|
|
70
|
+
This protocol exists to satisfy mypy type checking for mixin classes that
|
|
71
|
+
call methods defined on the parent class (EventBusKafka). It also includes
|
|
72
|
+
attributes defined by the mixin itself that are accessed via self.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
# Attributes from parent class (EventBusKafka)
|
|
76
|
+
_config: ModelKafkaEventBusConfig
|
|
77
|
+
_environment: str
|
|
78
|
+
_group: str
|
|
79
|
+
_producer: AIOKafkaProducer | None
|
|
80
|
+
_producer_lock: asyncio.Lock
|
|
81
|
+
_timeout_seconds: int
|
|
82
|
+
|
|
83
|
+
# Attributes defined by the mixin itself
|
|
84
|
+
_dlq_metrics: ModelDlqMetrics
|
|
85
|
+
_dlq_metrics_lock: asyncio.Lock
|
|
86
|
+
|
|
87
|
+
def _model_headers_to_kafka(
|
|
88
|
+
self, headers: ModelEventHeaders
|
|
89
|
+
) -> list[tuple[str, bytes]]:
|
|
90
|
+
"""Convert ModelEventHeaders to Kafka header format."""
|
|
91
|
+
...
|
|
92
|
+
|
|
93
|
+
async def _invoke_dlq_callbacks(self, event: ModelDlqEvent) -> None:
|
|
94
|
+
"""Invoke registered DLQ callbacks."""
|
|
95
|
+
...
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# Type alias for DLQ callback functions
|
|
99
|
+
DlqCallbackType = Callable[[ModelDlqEvent], Awaitable[None]]
|
|
100
|
+
|
|
101
|
+
logger = logging.getLogger(__name__)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class MixinKafkaDlq:
|
|
105
|
+
"""Mixin providing Dead Letter Queue functionality for Kafka event bus.
|
|
106
|
+
|
|
107
|
+
This mixin adds DLQ message publishing, callback registration, and metrics
|
|
108
|
+
tracking capabilities to a Kafka event bus implementation.
|
|
109
|
+
|
|
110
|
+
Attributes provided by mixin:
|
|
111
|
+
dlq_metrics: Property returning current DLQ metrics (copy-on-write)
|
|
112
|
+
|
|
113
|
+
Methods provided by mixin:
|
|
114
|
+
register_dlq_callback: Register callback for DLQ events
|
|
115
|
+
_publish_to_dlq: Publish failed message to DLQ
|
|
116
|
+
_publish_raw_to_dlq: Publish raw message (deserialization failure) to DLQ
|
|
117
|
+
_invoke_dlq_callbacks: Invoke registered callbacks with error isolation
|
|
118
|
+
|
|
119
|
+
Required attributes from parent class:
|
|
120
|
+
_config: ModelKafkaEventBusConfig with dead_letter_topic
|
|
121
|
+
_environment: str for environment context
|
|
122
|
+
_group: str for consumer group context
|
|
123
|
+
_producer: AIOKafkaProducer | None
|
|
124
|
+
_producer_lock: asyncio.Lock for producer access
|
|
125
|
+
_timeout_seconds: int for publish timeout
|
|
126
|
+
_model_headers_to_kafka: Method to convert headers to Kafka format
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
# Type hints for attributes expected from parent class
|
|
130
|
+
_config: ModelKafkaEventBusConfig
|
|
131
|
+
_environment: str
|
|
132
|
+
_group: str
|
|
133
|
+
_producer: AIOKafkaProducer | None
|
|
134
|
+
_producer_lock: asyncio.Lock
|
|
135
|
+
_timeout_seconds: int
|
|
136
|
+
|
|
137
|
+
def _init_dlq(self) -> None:
|
|
138
|
+
"""Initialize DLQ mixin state.
|
|
139
|
+
|
|
140
|
+
Must be called during __init__ of the parent class.
|
|
141
|
+
"""
|
|
142
|
+
# DLQ metrics tracking (copy-on-write pattern)
|
|
143
|
+
self._dlq_metrics = ModelDlqMetrics.create_empty()
|
|
144
|
+
self._dlq_metrics_lock = asyncio.Lock()
|
|
145
|
+
|
|
146
|
+
# DLQ callback hooks for custom alerting integration
|
|
147
|
+
self._dlq_callbacks: list[DlqCallbackType] = []
|
|
148
|
+
self._dlq_callbacks_lock = asyncio.Lock()
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def dlq_metrics(self) -> ModelDlqMetrics:
|
|
152
|
+
"""Get a deep copy of the current DLQ metrics.
|
|
153
|
+
|
|
154
|
+
Returns a deep copy of the metrics to prevent unintended mutation
|
|
155
|
+
from external code. Deep copy ensures nested dicts (topic_counts,
|
|
156
|
+
error_type_counts) are also copied. Thread-safe access to metrics data.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Deep copy of the current DLQ metrics
|
|
160
|
+
"""
|
|
161
|
+
return self._dlq_metrics.model_copy(deep=True)
|
|
162
|
+
|
|
163
|
+
async def register_dlq_callback(
|
|
164
|
+
self,
|
|
165
|
+
callback: DlqCallbackType,
|
|
166
|
+
) -> Callable[[], Awaitable[None]]:
|
|
167
|
+
"""Register a callback to be invoked when messages are sent to DLQ.
|
|
168
|
+
|
|
169
|
+
Callbacks receive a ModelDlqEvent containing comprehensive context
|
|
170
|
+
about the DLQ operation, including success/failure status, original
|
|
171
|
+
topic, error information, and correlation ID for tracing.
|
|
172
|
+
|
|
173
|
+
Callbacks are invoked asynchronously after DLQ publish completes.
|
|
174
|
+
If a callback raises an exception, it is logged but does not affect
|
|
175
|
+
other callbacks or the DLQ publish operation.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
callback: Async function that receives ModelDlqEvent
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Async function to unregister the callback
|
|
182
|
+
|
|
183
|
+
Example:
|
|
184
|
+
```python
|
|
185
|
+
async def alert_on_dlq(event: ModelDlqEvent) -> None:
|
|
186
|
+
if event.is_critical:
|
|
187
|
+
await pagerduty.trigger(
|
|
188
|
+
summary=f"DLQ publish failed: {event.dlq_error_type}",
|
|
189
|
+
severity="critical",
|
|
190
|
+
)
|
|
191
|
+
else:
|
|
192
|
+
logger.warning("Message sent to DLQ", extra=event.to_log_context())
|
|
193
|
+
|
|
194
|
+
unregister = await bus.register_dlq_callback(alert_on_dlq)
|
|
195
|
+
# Later, to unregister:
|
|
196
|
+
await unregister()
|
|
197
|
+
```
|
|
198
|
+
"""
|
|
199
|
+
async with self._dlq_callbacks_lock:
|
|
200
|
+
self._dlq_callbacks.append(callback)
|
|
201
|
+
|
|
202
|
+
async def unregister() -> None:
|
|
203
|
+
async with self._dlq_callbacks_lock:
|
|
204
|
+
if callback in self._dlq_callbacks:
|
|
205
|
+
self._dlq_callbacks.remove(callback)
|
|
206
|
+
|
|
207
|
+
return unregister
|
|
208
|
+
|
|
209
|
+
async def _publish_to_dlq(
|
|
210
|
+
self: ProtocolKafkaDlqHost,
|
|
211
|
+
original_topic: str,
|
|
212
|
+
failed_message: ModelEventMessage,
|
|
213
|
+
error: Exception,
|
|
214
|
+
correlation_id: UUID,
|
|
215
|
+
) -> None:
|
|
216
|
+
"""Publish failed message to dead letter queue with metrics and alerting.
|
|
217
|
+
|
|
218
|
+
This method publishes messages that failed processing to the configured
|
|
219
|
+
dead letter queue topic with comprehensive failure metadata for later
|
|
220
|
+
analysis and retry. If DLQ publishing fails, the error is logged but
|
|
221
|
+
does not crash the consumer.
|
|
222
|
+
|
|
223
|
+
Features:
|
|
224
|
+
- Structured logging with appropriate log levels (WARNING/ERROR)
|
|
225
|
+
- Metrics tracking (dlq.messages.published, dlq.messages.failed)
|
|
226
|
+
- Callback hooks for custom alerting integration
|
|
227
|
+
- Correlation ID included in all log entries for tracing
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
original_topic: Original topic where message was consumed from.
|
|
231
|
+
Must be a non-empty, non-whitespace string.
|
|
232
|
+
failed_message: The message that failed processing
|
|
233
|
+
error: The exception that caused the failure
|
|
234
|
+
correlation_id: Correlation ID for tracking
|
|
235
|
+
|
|
236
|
+
Note:
|
|
237
|
+
This method logs errors if DLQ publishing fails but does not raise
|
|
238
|
+
exceptions to prevent cascading failures in the consumer loop.
|
|
239
|
+
"""
|
|
240
|
+
# Validate original_topic - reject whitespace-only values
|
|
241
|
+
if not original_topic or not original_topic.strip():
|
|
242
|
+
logger.error(
|
|
243
|
+
"DLQ publish rejected: original_topic is empty or whitespace-only",
|
|
244
|
+
extra={
|
|
245
|
+
"correlation_id": str(correlation_id),
|
|
246
|
+
"error_type": type(error).__name__,
|
|
247
|
+
},
|
|
248
|
+
)
|
|
249
|
+
return
|
|
250
|
+
|
|
251
|
+
# Track timing for metrics
|
|
252
|
+
start_time = datetime.now(UTC)
|
|
253
|
+
error_type = type(error).__name__
|
|
254
|
+
|
|
255
|
+
# Get DLQ topic using convention-based resolution
|
|
256
|
+
# This supports both explicit dead_letter_topic config and automatic
|
|
257
|
+
# topic generation following ONEX conventions: <env>.dlq.<category>.v1
|
|
258
|
+
dlq_topic = self._config.get_dlq_topic()
|
|
259
|
+
|
|
260
|
+
# Sanitize error message to prevent credential leakage in DLQ
|
|
261
|
+
sanitized_failure_reason = sanitize_error_message(error)
|
|
262
|
+
|
|
263
|
+
# Defensive decode for key and value to handle edge cases
|
|
264
|
+
# This matches the pattern in _publish_raw_to_dlq for consistency
|
|
265
|
+
try:
|
|
266
|
+
key_str = (
|
|
267
|
+
failed_message.key.decode("utf-8", errors="replace")
|
|
268
|
+
if failed_message.key
|
|
269
|
+
else None
|
|
270
|
+
)
|
|
271
|
+
except Exception:
|
|
272
|
+
key_str = "<decode_failed>"
|
|
273
|
+
|
|
274
|
+
try:
|
|
275
|
+
value_str = (
|
|
276
|
+
failed_message.value.decode("utf-8", errors="replace")
|
|
277
|
+
if failed_message.value
|
|
278
|
+
else "<no_value>"
|
|
279
|
+
)
|
|
280
|
+
except Exception:
|
|
281
|
+
value_str = "<decode_failed>"
|
|
282
|
+
|
|
283
|
+
# Build DLQ message with failure metadata
|
|
284
|
+
dlq_payload = {
|
|
285
|
+
"original_topic": original_topic,
|
|
286
|
+
"original_message": {
|
|
287
|
+
"key": key_str,
|
|
288
|
+
"value": value_str,
|
|
289
|
+
"offset": failed_message.offset,
|
|
290
|
+
"partition": failed_message.partition,
|
|
291
|
+
},
|
|
292
|
+
"failure_reason": sanitized_failure_reason,
|
|
293
|
+
"failure_timestamp": start_time.isoformat(),
|
|
294
|
+
"correlation_id": str(correlation_id),
|
|
295
|
+
"retry_count": failed_message.headers.retry_count,
|
|
296
|
+
"error_type": error_type,
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
# Create DLQ headers with failure metadata
|
|
300
|
+
dlq_headers = ModelEventHeaders(
|
|
301
|
+
source=f"{self._environment}.{self._group}",
|
|
302
|
+
event_type="dlq_message",
|
|
303
|
+
content_type="application/json",
|
|
304
|
+
correlation_id=correlation_id,
|
|
305
|
+
timestamp=datetime.now(UTC),
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
# Convert DLQ payload to JSON bytes with explicit error handling
|
|
309
|
+
# for non-serializable content
|
|
310
|
+
try:
|
|
311
|
+
dlq_value = json.dumps(dlq_payload).encode("utf-8")
|
|
312
|
+
except (TypeError, ValueError) as json_error:
|
|
313
|
+
# Handle non-serializable envelope content by falling back to repr
|
|
314
|
+
logger.warning(
|
|
315
|
+
"DLQ payload contains non-serializable data, using repr fallback",
|
|
316
|
+
extra={
|
|
317
|
+
"correlation_id": str(correlation_id),
|
|
318
|
+
"original_topic": original_topic,
|
|
319
|
+
"json_error": str(json_error),
|
|
320
|
+
},
|
|
321
|
+
)
|
|
322
|
+
# Create a safe fallback payload with repr of original values
|
|
323
|
+
fallback_payload = {
|
|
324
|
+
"original_topic": original_topic,
|
|
325
|
+
"original_message": {
|
|
326
|
+
"key": repr(key_str),
|
|
327
|
+
"value": "<non-serializable>",
|
|
328
|
+
"offset": failed_message.offset,
|
|
329
|
+
"partition": failed_message.partition,
|
|
330
|
+
},
|
|
331
|
+
"failure_reason": sanitized_failure_reason,
|
|
332
|
+
"failure_timestamp": start_time.isoformat(),
|
|
333
|
+
"correlation_id": str(correlation_id),
|
|
334
|
+
"retry_count": failed_message.headers.retry_count,
|
|
335
|
+
"error_type": error_type,
|
|
336
|
+
"serialization_fallback": True,
|
|
337
|
+
}
|
|
338
|
+
dlq_value = json.dumps(fallback_payload).encode("utf-8")
|
|
339
|
+
|
|
340
|
+
# Variables for event creation
|
|
341
|
+
success = False
|
|
342
|
+
dlq_error_type: str | None = None
|
|
343
|
+
dlq_error_message: str | None = None
|
|
344
|
+
|
|
345
|
+
# Publish to DLQ (without retry - best effort)
|
|
346
|
+
# Capture producer reference and headers under lock, then send outside lock
|
|
347
|
+
producer = None
|
|
348
|
+
kafka_headers: list[tuple[str, bytes]] | None = None
|
|
349
|
+
try:
|
|
350
|
+
async with self._producer_lock:
|
|
351
|
+
if self._producer is None:
|
|
352
|
+
dlq_error_type = "ProducerUnavailable"
|
|
353
|
+
dlq_error_message = "Producer not initialized or closed"
|
|
354
|
+
logger.error(
|
|
355
|
+
"DLQ publish failed: producer not available",
|
|
356
|
+
extra={
|
|
357
|
+
"original_topic": original_topic,
|
|
358
|
+
"dlq_topic": dlq_topic,
|
|
359
|
+
"correlation_id": str(correlation_id),
|
|
360
|
+
"error_type": error_type,
|
|
361
|
+
"retry_count": failed_message.headers.retry_count,
|
|
362
|
+
},
|
|
363
|
+
)
|
|
364
|
+
else:
|
|
365
|
+
kafka_headers = self._model_headers_to_kafka(dlq_headers)
|
|
366
|
+
kafka_headers.extend(
|
|
367
|
+
[
|
|
368
|
+
("original_topic", original_topic.encode("utf-8")),
|
|
369
|
+
(
|
|
370
|
+
"failure_reason",
|
|
371
|
+
sanitized_failure_reason.encode("utf-8"),
|
|
372
|
+
),
|
|
373
|
+
(
|
|
374
|
+
"failure_timestamp",
|
|
375
|
+
start_time.isoformat().encode("utf-8"),
|
|
376
|
+
),
|
|
377
|
+
]
|
|
378
|
+
)
|
|
379
|
+
producer = self._producer
|
|
380
|
+
|
|
381
|
+
# Send and wait for completion with timeout (outside producer lock)
|
|
382
|
+
# Using send_and_wait() wrapped in wait_for() for cleaner timeout handling
|
|
383
|
+
if producer is not None and kafka_headers is not None:
|
|
384
|
+
await asyncio.wait_for(
|
|
385
|
+
producer.send_and_wait(
|
|
386
|
+
dlq_topic,
|
|
387
|
+
value=dlq_value,
|
|
388
|
+
key=failed_message.key,
|
|
389
|
+
headers=kafka_headers,
|
|
390
|
+
),
|
|
391
|
+
timeout=self._timeout_seconds,
|
|
392
|
+
)
|
|
393
|
+
success = True
|
|
394
|
+
|
|
395
|
+
except Exception as dlq_error:
|
|
396
|
+
dlq_error_type = type(dlq_error).__name__
|
|
397
|
+
dlq_error_message = sanitize_error_message(dlq_error)
|
|
398
|
+
logger.exception(
|
|
399
|
+
"DLQ publish failed: message may be lost",
|
|
400
|
+
extra={
|
|
401
|
+
"original_topic": original_topic,
|
|
402
|
+
"dlq_topic": dlq_topic,
|
|
403
|
+
"correlation_id": str(correlation_id),
|
|
404
|
+
"error_type": error_type,
|
|
405
|
+
"dlq_error_type": dlq_error_type,
|
|
406
|
+
"dlq_error_message": dlq_error_message,
|
|
407
|
+
"retry_count": failed_message.headers.retry_count,
|
|
408
|
+
"message_offset": failed_message.offset,
|
|
409
|
+
"message_partition": failed_message.partition,
|
|
410
|
+
},
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
# Calculate duration for metrics
|
|
414
|
+
end_time = datetime.now(UTC)
|
|
415
|
+
duration_ms = (end_time - start_time).total_seconds() * 1000
|
|
416
|
+
|
|
417
|
+
# Log successful DLQ publish at WARNING level
|
|
418
|
+
if success:
|
|
419
|
+
logger.warning(
|
|
420
|
+
"Message published to DLQ due to processing failure",
|
|
421
|
+
extra={
|
|
422
|
+
"original_topic": original_topic,
|
|
423
|
+
"dlq_topic": dlq_topic,
|
|
424
|
+
"correlation_id": str(correlation_id),
|
|
425
|
+
"error_type": error_type,
|
|
426
|
+
"error_message": sanitized_failure_reason,
|
|
427
|
+
"retry_count": failed_message.headers.retry_count,
|
|
428
|
+
"message_offset": failed_message.offset,
|
|
429
|
+
"message_partition": failed_message.partition,
|
|
430
|
+
"duration_ms": round(duration_ms, 2),
|
|
431
|
+
},
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
# Create DLQ event for metrics and callbacks
|
|
435
|
+
# Convert message_offset to string for type consistency with ModelDlqEvent
|
|
436
|
+
# (which expects str | None) and consistency with _publish_raw_to_dlq
|
|
437
|
+
message_offset_str = (
|
|
438
|
+
str(failed_message.offset) if failed_message.offset is not None else None
|
|
439
|
+
)
|
|
440
|
+
dlq_event = ModelDlqEvent(
|
|
441
|
+
original_topic=original_topic,
|
|
442
|
+
dlq_topic=dlq_topic,
|
|
443
|
+
correlation_id=correlation_id,
|
|
444
|
+
error_type=error_type,
|
|
445
|
+
error_message=sanitized_failure_reason,
|
|
446
|
+
retry_count=failed_message.headers.retry_count,
|
|
447
|
+
message_offset=message_offset_str,
|
|
448
|
+
message_partition=failed_message.partition,
|
|
449
|
+
success=success,
|
|
450
|
+
dlq_error_type=dlq_error_type,
|
|
451
|
+
dlq_error_message=dlq_error_message,
|
|
452
|
+
timestamp=end_time,
|
|
453
|
+
environment=self._environment,
|
|
454
|
+
consumer_group=self._group,
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
# Update DLQ metrics (copy-on-write pattern)
|
|
458
|
+
async with self._dlq_metrics_lock:
|
|
459
|
+
self._dlq_metrics = self._dlq_metrics.record_dlq_publish(
|
|
460
|
+
original_topic=original_topic,
|
|
461
|
+
error_type=error_type,
|
|
462
|
+
success=success,
|
|
463
|
+
duration_ms=duration_ms,
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
# Invoke DLQ callbacks for custom alerting
|
|
467
|
+
await self._invoke_dlq_callbacks(dlq_event)
|
|
468
|
+
|
|
469
|
+
async def _invoke_dlq_callbacks(self, event: ModelDlqEvent) -> None:
|
|
470
|
+
"""Invoke registered DLQ callbacks with error isolation.
|
|
471
|
+
|
|
472
|
+
Callbacks are invoked sequentially. If a callback raises an exception,
|
|
473
|
+
it is logged but does not prevent other callbacks from executing.
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
event: The DLQ event to pass to callbacks
|
|
477
|
+
"""
|
|
478
|
+
# Get a copy of callbacks under lock
|
|
479
|
+
async with self._dlq_callbacks_lock:
|
|
480
|
+
callbacks = list(self._dlq_callbacks)
|
|
481
|
+
|
|
482
|
+
for callback in callbacks:
|
|
483
|
+
try:
|
|
484
|
+
await callback(event)
|
|
485
|
+
except Exception as callback_error:
|
|
486
|
+
logger.warning(
|
|
487
|
+
"DLQ callback raised exception",
|
|
488
|
+
extra={
|
|
489
|
+
"callback": getattr(callback, "__name__", str(callback)),
|
|
490
|
+
"correlation_id": str(event.correlation_id),
|
|
491
|
+
"original_topic": event.original_topic,
|
|
492
|
+
"callback_error_type": type(callback_error).__name__,
|
|
493
|
+
"callback_error_message": sanitize_error_message(
|
|
494
|
+
callback_error
|
|
495
|
+
),
|
|
496
|
+
},
|
|
497
|
+
exc_info=True,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
async def _publish_raw_to_dlq(
|
|
501
|
+
self: ProtocolKafkaDlqHost,
|
|
502
|
+
original_topic: str,
|
|
503
|
+
raw_msg: object,
|
|
504
|
+
error: Exception,
|
|
505
|
+
correlation_id: UUID,
|
|
506
|
+
failure_type: str,
|
|
507
|
+
) -> None:
|
|
508
|
+
"""Publish raw Kafka message to DLQ when deserialization fails.
|
|
509
|
+
|
|
510
|
+
This method handles cases where message conversion fails before we have
|
|
511
|
+
a ModelEventMessage. It extracts raw data directly from the Kafka
|
|
512
|
+
ConsumerRecord for DLQ payload construction.
|
|
513
|
+
|
|
514
|
+
Args:
|
|
515
|
+
original_topic: Original topic where message was consumed from.
|
|
516
|
+
Must be a non-empty, non-whitespace string.
|
|
517
|
+
raw_msg: Raw Kafka ConsumerRecord that failed conversion
|
|
518
|
+
error: The exception that caused the failure
|
|
519
|
+
correlation_id: Correlation ID for tracking
|
|
520
|
+
failure_type: Type of failure (e.g., "deserialization_error")
|
|
521
|
+
|
|
522
|
+
Note:
|
|
523
|
+
This method logs errors if DLQ publishing fails but does not raise
|
|
524
|
+
exceptions to prevent cascading failures in the consumer loop.
|
|
525
|
+
"""
|
|
526
|
+
# Validate original_topic - reject whitespace-only values
|
|
527
|
+
if not original_topic or not original_topic.strip():
|
|
528
|
+
logger.error(
|
|
529
|
+
"DLQ publish rejected: original_topic is empty or whitespace-only",
|
|
530
|
+
extra={
|
|
531
|
+
"correlation_id": str(correlation_id),
|
|
532
|
+
"error_type": type(error).__name__,
|
|
533
|
+
"failure_type": failure_type,
|
|
534
|
+
},
|
|
535
|
+
)
|
|
536
|
+
return
|
|
537
|
+
|
|
538
|
+
# Track timing for metrics
|
|
539
|
+
start_time = datetime.now(UTC)
|
|
540
|
+
error_type = type(error).__name__
|
|
541
|
+
|
|
542
|
+
# Get DLQ topic using convention-based resolution
|
|
543
|
+
# This supports both explicit dead_letter_topic config and automatic
|
|
544
|
+
# topic generation following ONEX conventions: <env>.dlq.<category>.v1
|
|
545
|
+
dlq_topic = self._config.get_dlq_topic()
|
|
546
|
+
|
|
547
|
+
# Sanitize error message
|
|
548
|
+
sanitized_failure_reason = sanitize_error_message(error)
|
|
549
|
+
|
|
550
|
+
# Extract raw data from Kafka message
|
|
551
|
+
raw_key = getattr(raw_msg, "key", None)
|
|
552
|
+
raw_value = getattr(raw_msg, "value", b"")
|
|
553
|
+
raw_offset = getattr(raw_msg, "offset", None)
|
|
554
|
+
raw_partition = getattr(raw_msg, "partition", None)
|
|
555
|
+
|
|
556
|
+
# Safe decode with error replacement
|
|
557
|
+
try:
|
|
558
|
+
key_str = (
|
|
559
|
+
raw_key.decode("utf-8", errors="replace")
|
|
560
|
+
if isinstance(raw_key, bytes)
|
|
561
|
+
else str(raw_key)
|
|
562
|
+
if raw_key is not None
|
|
563
|
+
else None
|
|
564
|
+
)
|
|
565
|
+
except Exception:
|
|
566
|
+
key_str = "<decode_failed>"
|
|
567
|
+
|
|
568
|
+
try:
|
|
569
|
+
value_str = (
|
|
570
|
+
raw_value.decode("utf-8", errors="replace")
|
|
571
|
+
if isinstance(raw_value, bytes)
|
|
572
|
+
else str(raw_value)
|
|
573
|
+
)
|
|
574
|
+
except Exception:
|
|
575
|
+
value_str = "<decode_failed>"
|
|
576
|
+
|
|
577
|
+
# Build DLQ message with failure metadata
|
|
578
|
+
dlq_payload = {
|
|
579
|
+
"original_topic": original_topic,
|
|
580
|
+
"original_message": {
|
|
581
|
+
"key": key_str,
|
|
582
|
+
"value": value_str,
|
|
583
|
+
"offset": raw_offset,
|
|
584
|
+
"partition": raw_partition,
|
|
585
|
+
},
|
|
586
|
+
"failure_reason": sanitized_failure_reason,
|
|
587
|
+
"failure_type": failure_type,
|
|
588
|
+
"failure_timestamp": start_time.isoformat(),
|
|
589
|
+
"correlation_id": str(correlation_id),
|
|
590
|
+
"retry_count": 0,
|
|
591
|
+
"error_type": error_type,
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
# Create DLQ headers
|
|
595
|
+
dlq_headers = ModelEventHeaders(
|
|
596
|
+
source=f"{self._environment}.{self._group}",
|
|
597
|
+
event_type="dlq_raw_message",
|
|
598
|
+
content_type="application/json",
|
|
599
|
+
correlation_id=correlation_id,
|
|
600
|
+
timestamp=start_time,
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
# Convert DLQ payload to JSON bytes with explicit error handling
|
|
604
|
+
# for non-serializable content
|
|
605
|
+
try:
|
|
606
|
+
dlq_value = json.dumps(dlq_payload).encode("utf-8")
|
|
607
|
+
except (TypeError, ValueError) as json_error:
|
|
608
|
+
# Handle non-serializable envelope content by falling back to repr
|
|
609
|
+
logger.warning(
|
|
610
|
+
"DLQ raw payload contains non-serializable data, using repr fallback",
|
|
611
|
+
extra={
|
|
612
|
+
"correlation_id": str(correlation_id),
|
|
613
|
+
"original_topic": original_topic,
|
|
614
|
+
"failure_type": failure_type,
|
|
615
|
+
"json_error": str(json_error),
|
|
616
|
+
},
|
|
617
|
+
)
|
|
618
|
+
# Create a safe fallback payload with repr of original values
|
|
619
|
+
fallback_payload = {
|
|
620
|
+
"original_topic": original_topic,
|
|
621
|
+
"original_message": {
|
|
622
|
+
"key": repr(key_str),
|
|
623
|
+
"value": "<non-serializable>",
|
|
624
|
+
"offset": raw_offset,
|
|
625
|
+
"partition": raw_partition,
|
|
626
|
+
},
|
|
627
|
+
"failure_reason": sanitized_failure_reason,
|
|
628
|
+
"failure_type": failure_type,
|
|
629
|
+
"failure_timestamp": start_time.isoformat(),
|
|
630
|
+
"correlation_id": str(correlation_id),
|
|
631
|
+
"retry_count": 0,
|
|
632
|
+
"error_type": error_type,
|
|
633
|
+
"serialization_fallback": True,
|
|
634
|
+
}
|
|
635
|
+
dlq_value = json.dumps(fallback_payload).encode("utf-8")
|
|
636
|
+
dlq_key = raw_key if isinstance(raw_key, bytes) else None
|
|
637
|
+
|
|
638
|
+
# Variables for event creation
|
|
639
|
+
success = False
|
|
640
|
+
dlq_error_type: str | None = None
|
|
641
|
+
dlq_error_message: str | None = None
|
|
642
|
+
|
|
643
|
+
# Publish to DLQ
|
|
644
|
+
# Capture producer reference and headers under lock, then send outside lock
|
|
645
|
+
producer = None
|
|
646
|
+
kafka_headers: list[tuple[str, bytes]] | None = None
|
|
647
|
+
try:
|
|
648
|
+
async with self._producer_lock:
|
|
649
|
+
if self._producer is None:
|
|
650
|
+
dlq_error_type = "ProducerUnavailable"
|
|
651
|
+
dlq_error_message = "Producer not initialized or closed"
|
|
652
|
+
logger.error(
|
|
653
|
+
"DLQ publish failed: producer not available for raw message",
|
|
654
|
+
extra={
|
|
655
|
+
"original_topic": original_topic,
|
|
656
|
+
"dlq_topic": dlq_topic,
|
|
657
|
+
"correlation_id": str(correlation_id),
|
|
658
|
+
"error_type": error_type,
|
|
659
|
+
"failure_type": failure_type,
|
|
660
|
+
},
|
|
661
|
+
)
|
|
662
|
+
else:
|
|
663
|
+
kafka_headers = self._model_headers_to_kafka(dlq_headers)
|
|
664
|
+
kafka_headers.extend(
|
|
665
|
+
[
|
|
666
|
+
("original_topic", original_topic.encode("utf-8")),
|
|
667
|
+
("failure_type", failure_type.encode("utf-8")),
|
|
668
|
+
(
|
|
669
|
+
"failure_reason",
|
|
670
|
+
sanitized_failure_reason.encode("utf-8"),
|
|
671
|
+
),
|
|
672
|
+
(
|
|
673
|
+
"failure_timestamp",
|
|
674
|
+
start_time.isoformat().encode("utf-8"),
|
|
675
|
+
),
|
|
676
|
+
]
|
|
677
|
+
)
|
|
678
|
+
producer = self._producer
|
|
679
|
+
|
|
680
|
+
# Send and wait for completion with timeout (outside producer lock)
|
|
681
|
+
# Using send_and_wait() wrapped in wait_for() for cleaner timeout handling
|
|
682
|
+
if producer is not None and kafka_headers is not None:
|
|
683
|
+
await asyncio.wait_for(
|
|
684
|
+
producer.send_and_wait(
|
|
685
|
+
dlq_topic,
|
|
686
|
+
value=dlq_value,
|
|
687
|
+
key=dlq_key,
|
|
688
|
+
headers=kafka_headers,
|
|
689
|
+
),
|
|
690
|
+
timeout=self._timeout_seconds,
|
|
691
|
+
)
|
|
692
|
+
success = True
|
|
693
|
+
|
|
694
|
+
except Exception as dlq_error:
|
|
695
|
+
dlq_error_type = type(dlq_error).__name__
|
|
696
|
+
dlq_error_message = sanitize_error_message(dlq_error)
|
|
697
|
+
logger.exception(
|
|
698
|
+
"DLQ publish failed for raw message: message may be lost",
|
|
699
|
+
extra={
|
|
700
|
+
"original_topic": original_topic,
|
|
701
|
+
"dlq_topic": dlq_topic,
|
|
702
|
+
"correlation_id": str(correlation_id),
|
|
703
|
+
"error_type": error_type,
|
|
704
|
+
"failure_type": failure_type,
|
|
705
|
+
"dlq_error_type": dlq_error_type,
|
|
706
|
+
"dlq_error_message": dlq_error_message,
|
|
707
|
+
"message_offset": raw_offset,
|
|
708
|
+
"message_partition": raw_partition,
|
|
709
|
+
},
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
# Calculate duration for metrics
|
|
713
|
+
end_time = datetime.now(UTC)
|
|
714
|
+
duration_ms = (end_time - start_time).total_seconds() * 1000
|
|
715
|
+
|
|
716
|
+
# Log successful DLQ publish
|
|
717
|
+
if success:
|
|
718
|
+
logger.warning(
|
|
719
|
+
"Raw message published to DLQ due to deserialization/validation failure",
|
|
720
|
+
extra={
|
|
721
|
+
"original_topic": original_topic,
|
|
722
|
+
"dlq_topic": dlq_topic,
|
|
723
|
+
"correlation_id": str(correlation_id),
|
|
724
|
+
"error_type": error_type,
|
|
725
|
+
"error_message": sanitized_failure_reason,
|
|
726
|
+
"failure_type": failure_type,
|
|
727
|
+
"message_offset": raw_offset,
|
|
728
|
+
"message_partition": raw_partition,
|
|
729
|
+
"duration_ms": round(duration_ms, 2),
|
|
730
|
+
},
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
# Create DLQ event for metrics and callbacks
|
|
734
|
+
message_offset_str = str(raw_offset) if raw_offset is not None else None
|
|
735
|
+
dlq_event = ModelDlqEvent(
|
|
736
|
+
original_topic=original_topic,
|
|
737
|
+
dlq_topic=dlq_topic,
|
|
738
|
+
correlation_id=correlation_id,
|
|
739
|
+
error_type=error_type,
|
|
740
|
+
error_message=sanitized_failure_reason,
|
|
741
|
+
retry_count=0,
|
|
742
|
+
message_offset=message_offset_str,
|
|
743
|
+
message_partition=raw_partition,
|
|
744
|
+
success=success,
|
|
745
|
+
dlq_error_type=dlq_error_type,
|
|
746
|
+
dlq_error_message=dlq_error_message,
|
|
747
|
+
timestamp=end_time,
|
|
748
|
+
environment=self._environment,
|
|
749
|
+
consumer_group=self._group,
|
|
750
|
+
)
|
|
751
|
+
|
|
752
|
+
# Update DLQ metrics
|
|
753
|
+
async with self._dlq_metrics_lock:
|
|
754
|
+
self._dlq_metrics = self._dlq_metrics.record_dlq_publish(
|
|
755
|
+
original_topic=original_topic,
|
|
756
|
+
error_type=error_type,
|
|
757
|
+
success=success,
|
|
758
|
+
duration_ms=duration_ms,
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
# Invoke DLQ callbacks
|
|
762
|
+
await self._invoke_dlq_callbacks(dlq_event)
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
__all__: list[str] = ["MixinKafkaDlq", "DlqCallbackType"]
|