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,1137 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Canonical Registration Reducer following ProtocolReducer pattern.
|
|
4
|
+
|
|
5
|
+
This reducer replaces the legacy NodeDualRegistrationReducer (887 lines)
|
|
6
|
+
with a pure function implementation (~80 lines) that follows the canonical
|
|
7
|
+
ONEX reducer pattern defined in DESIGN_TWO_WAY_REGISTRATION_ARCHITECTURE.md.
|
|
8
|
+
|
|
9
|
+
Performance Characteristics:
|
|
10
|
+
This reducer is designed for high-performance event processing with the
|
|
11
|
+
following targets:
|
|
12
|
+
|
|
13
|
+
- reduce() processing: <300ms per event (target)
|
|
14
|
+
- Intent building: <50ms per intent (includes Consul + PostgreSQL)
|
|
15
|
+
- Idempotency check: <1ms
|
|
16
|
+
|
|
17
|
+
Performance is logged when thresholds are exceeded. These thresholds are
|
|
18
|
+
configurable via module-level constants (PERF_THRESHOLD_*).
|
|
19
|
+
|
|
20
|
+
Typical performance on standard hardware:
|
|
21
|
+
- Simple introspection events: ~0.1-1ms
|
|
22
|
+
- Complex events with full metadata: ~1-5ms
|
|
23
|
+
- Hash-based event ID derivation: ~0.01ms
|
|
24
|
+
|
|
25
|
+
Circuit Breaker Considerations:
|
|
26
|
+
This reducer does NOT require a circuit breaker because:
|
|
27
|
+
|
|
28
|
+
1. **Pure Function Pattern**: Reducers are pure functions - they perform
|
|
29
|
+
NO I/O operations. All external interactions are delegated to the
|
|
30
|
+
Effect layer via emitted intents.
|
|
31
|
+
|
|
32
|
+
2. **No Transient Failures**: Without I/O, there are no transient failures
|
|
33
|
+
to recover from. Circuit breakers are designed for I/O resilience.
|
|
34
|
+
|
|
35
|
+
3. **Deterministic Behavior**: Given the same state and event, the reducer
|
|
36
|
+
always produces the same output. There's no "retry" semantic.
|
|
37
|
+
|
|
38
|
+
4. **Effect Layer Responsibility**: Circuit breakers should be implemented
|
|
39
|
+
in the Effect layer nodes (ConsulAdapter, PostgresAdapter) that actually
|
|
40
|
+
perform the external I/O operations.
|
|
41
|
+
|
|
42
|
+
See CLAUDE.md "Dispatcher Resilience Pattern" section for the general
|
|
43
|
+
principle: "Dispatchers own their own resilience."
|
|
44
|
+
|
|
45
|
+
Architecture:
|
|
46
|
+
- Pure function: reduce(state, event) -> ModelReducerOutput
|
|
47
|
+
- No internal state - state passed in and returned
|
|
48
|
+
- No I/O - emits intents for Effect layer
|
|
49
|
+
- Deterministic - same inputs produce same outputs
|
|
50
|
+
|
|
51
|
+
Key Differences from Legacy:
|
|
52
|
+
- No FSM machinery (FSM was for tracking async I/O, which pure reducers don't do)
|
|
53
|
+
- No initialize()/shutdown() lifecycle methods
|
|
54
|
+
- No internal metrics tracking (metrics are output-level concerns)
|
|
55
|
+
- State is external and immutable (ModelRegistrationState)
|
|
56
|
+
- Uses omnibase_core's ModelReducerOutput[T] for standard output format
|
|
57
|
+
|
|
58
|
+
State Persistence Strategy:
|
|
59
|
+
This reducer follows the ONEX pure reducer pattern where the reducer itself
|
|
60
|
+
performs NO I/O. State persistence is handled externally by the Runtime and
|
|
61
|
+
Projector components.
|
|
62
|
+
|
|
63
|
+
**HOW STATE IS STORED (PostgreSQL via Projector Layer):**
|
|
64
|
+
|
|
65
|
+
The reducer does NOT persist state directly. Instead:
|
|
66
|
+
|
|
67
|
+
1. Reducer returns ModelReducerOutput containing the new state
|
|
68
|
+
2. Runtime extracts the result (ModelRegistrationState) from the output
|
|
69
|
+
3. Runtime invokes Projector.persist() to write state to PostgreSQL
|
|
70
|
+
4. State is stored in the ``node_registrations`` table with fields matching
|
|
71
|
+
the ModelRegistrationState model plus tracking fields (last_event_offset)
|
|
72
|
+
|
|
73
|
+
PostgreSQL Schema (conceptual)::
|
|
74
|
+
|
|
75
|
+
node_registrations:
|
|
76
|
+
node_id UUID PRIMARY KEY
|
|
77
|
+
status VARCHAR(20) -- 'idle', 'pending', 'partial', 'complete', 'failed'
|
|
78
|
+
consul_confirmed BOOLEAN
|
|
79
|
+
postgres_confirmed BOOLEAN
|
|
80
|
+
last_processed_event_id UUID
|
|
81
|
+
failure_reason VARCHAR(50)
|
|
82
|
+
last_event_offset BIGINT -- For idempotent updates
|
|
83
|
+
updated_at TIMESTAMP
|
|
84
|
+
|
|
85
|
+
**HOW STATE IS RETRIEVED (Before reduce() is Called):**
|
|
86
|
+
|
|
87
|
+
Before calling reduce(), the orchestrator/runtime loads current state:
|
|
88
|
+
|
|
89
|
+
1. Orchestrator receives NodeIntrospectionEvent from Kafka
|
|
90
|
+
2. Orchestrator extracts entity_id (node_id) from event envelope
|
|
91
|
+
3. Orchestrator queries projection via ProtocolProjectionReader::
|
|
92
|
+
|
|
93
|
+
state = await projection_reader.get_projection(
|
|
94
|
+
entity_type="registration",
|
|
95
|
+
entity_id=node_id
|
|
96
|
+
)
|
|
97
|
+
if state is None:
|
|
98
|
+
state = ModelRegistrationState() # Initial idle state
|
|
99
|
+
|
|
100
|
+
4. Orchestrator invokes reducer: output = reducer.reduce(state, event)
|
|
101
|
+
5. Orchestrator passes output to Runtime for persistence and publishing
|
|
102
|
+
|
|
103
|
+
**STATE FLOW (Complete Round-Trip):**
|
|
104
|
+
|
|
105
|
+
::
|
|
106
|
+
|
|
107
|
+
+--------------+
|
|
108
|
+
| Kafka Event | NodeIntrospectionEvent
|
|
109
|
+
+------+-------+
|
|
110
|
+
|
|
|
111
|
+
v
|
|
112
|
+
+------------------+
|
|
113
|
+
| Orchestrator | 1. Receives event
|
|
114
|
+
| | 2. Loads state from PostgreSQL via ProtocolProjectionReader
|
|
115
|
+
+--------+---------+
|
|
116
|
+
| state + event
|
|
117
|
+
v
|
|
118
|
+
+------------------+
|
|
119
|
+
| Reducer | 3. reduce(state, event) -> ModelReducerOutput
|
|
120
|
+
| (THIS CLASS) | - Pure computation, no I/O
|
|
121
|
+
| | - Returns new state + intents
|
|
122
|
+
+--------+---------+
|
|
123
|
+
| ModelReducerOutput
|
|
124
|
+
v
|
|
125
|
+
+------------------+
|
|
126
|
+
| Runtime | 4. Extracts result (new state)
|
|
127
|
+
| | 5. Invokes Projector.persist() - SYNCHRONOUS
|
|
128
|
+
| | 6. Waits for persist acknowledgment
|
|
129
|
+
+--------+---------+
|
|
130
|
+
| persist()
|
|
131
|
+
v
|
|
132
|
+
+------------------+
|
|
133
|
+
| PostgreSQL | 7. State written to node_registrations table
|
|
134
|
+
| (Projection) | - Idempotent via last_event_offset check
|
|
135
|
+
+--------+---------+
|
|
136
|
+
| ack
|
|
137
|
+
v
|
|
138
|
+
+------------------+
|
|
139
|
+
| Runtime | 8. AFTER persist acks, publish intents to Kafka
|
|
140
|
+
| | - Ordering guarantee: persist BEFORE publish
|
|
141
|
+
+--------+---------+
|
|
142
|
+
| publish intents
|
|
143
|
+
v
|
|
144
|
+
+------------------+
|
|
145
|
+
| Kafka (intents) | 9. Intents available for Effect layer consumption
|
|
146
|
+
+------------------+
|
|
147
|
+
|
|
148
|
+
**ORDERING GUARANTEE (Critical for Consistency):**
|
|
149
|
+
|
|
150
|
+
Per ticket F0 (Projector Execution Model) in ONEX_RUNTIME_REGISTRATION_TICKET_PLAN.md:
|
|
151
|
+
|
|
152
|
+
- Projections are PERSISTED to PostgreSQL BEFORE intents are PUBLISHED to Kafka
|
|
153
|
+
- This ensures read models are consistent before downstream processing
|
|
154
|
+
- Effects can safely assume projection state is current when they execute
|
|
155
|
+
- No race conditions where effects execute before state is visible
|
|
156
|
+
|
|
157
|
+
**IDEMPOTENCY (Safe Replay via last_processed_event_id):**
|
|
158
|
+
|
|
159
|
+
The state model tracks ``last_processed_event_id`` to enable safe replay:
|
|
160
|
+
|
|
161
|
+
1. Each event has a unique event_id (correlation_id or generated UUID)
|
|
162
|
+
2. Before processing, reducer calls state.is_duplicate_event(event_id)
|
|
163
|
+
3. If duplicate, reducer returns current state unchanged with no intents
|
|
164
|
+
4. PostgreSQL projection also tracks last_event_offset for offset-based idempotency
|
|
165
|
+
|
|
166
|
+
This handles crash scenarios:
|
|
167
|
+
|
|
168
|
+
- If system crashes after persist but before Kafka ack, event is redelivered
|
|
169
|
+
- Reducer detects duplicate via last_processed_event_id match
|
|
170
|
+
- No duplicate intents are emitted
|
|
171
|
+
- System converges to correct state
|
|
172
|
+
|
|
173
|
+
See also: Ticket B3 (Idempotency Guard) for runtime-level idempotency.
|
|
174
|
+
|
|
175
|
+
Intent Emission:
|
|
176
|
+
The reducer emits ModelIntent objects (reducer-layer intents) that wrap
|
|
177
|
+
the typed infrastructure intents:
|
|
178
|
+
- consul.register: Consul service registration
|
|
179
|
+
- postgres.upsert_registration: PostgreSQL record upsert
|
|
180
|
+
|
|
181
|
+
The payload contains the serialized typed intent for Effect layer execution.
|
|
182
|
+
|
|
183
|
+
Confirmation Event Flow:
|
|
184
|
+
This section documents how confirmation events flow from Effect layer back to
|
|
185
|
+
this reducer, completing the registration workflow cycle.
|
|
186
|
+
|
|
187
|
+
1. INITIAL FLOW (Introspection -> Intents):
|
|
188
|
+
|
|
189
|
+
+----------------+ +-----------+ +------------------+
|
|
190
|
+
| Node emits | --> | Reducer | --> | Intents emitted |
|
|
191
|
+
| Introspection | | processes | | to Kafka |
|
|
192
|
+
| Event | | event | | (consul.register,|
|
|
193
|
+
+----------------+ +-----------+ | postgres.upsert) |
|
|
194
|
+
+------------------+
|
|
195
|
+
|
|
|
196
|
+
v
|
|
197
|
+
+------------------------+
|
|
198
|
+
| Runtime routes intents |
|
|
199
|
+
| to Effect layer nodes |
|
|
200
|
+
+------------------------+
|
|
201
|
+
|
|
202
|
+
2. EFFECT LAYER EXECUTION:
|
|
203
|
+
|
|
204
|
+
+-------------------+ +------------------+ +------------------+
|
|
205
|
+
| ConsulAdapter | --> | Execute intent | --> | Publish |
|
|
206
|
+
| (Effect Node) | | (register svc) | | confirmation |
|
|
207
|
+
+-------------------+ +------------------+ | event to Kafka |
|
|
208
|
+
+------------------+
|
|
209
|
+
|
|
|
210
|
+
+-------------------+ +------------------+ |
|
|
211
|
+
| PostgresAdapter | --> | Execute intent | ------------+
|
|
212
|
+
| (Effect Node) | | (upsert record) | |
|
|
213
|
+
+-------------------+ +------------------+ v
|
|
214
|
+
+------------------+
|
|
215
|
+
| Confirmation |
|
|
216
|
+
| events on Kafka |
|
|
217
|
+
+------------------+
|
|
218
|
+
|
|
219
|
+
3. CONFIRMATION EVENT FLOW (Back to Reducer):
|
|
220
|
+
|
|
221
|
+
+-------------------+ +------------------+ +-------------------+
|
|
222
|
+
| Kafka topic: | --> | Runtime routes | --> | Reducer processes |
|
|
223
|
+
| onex.registration.| | confirmation | | confirmation via |
|
|
224
|
+
| events | | to reducer | | reduce_confirm() |
|
|
225
|
+
+-------------------+ +------------------+ +-------------------+
|
|
226
|
+
|
|
|
227
|
+
v
|
|
228
|
+
+-------------------+
|
|
229
|
+
| State transitions:|
|
|
230
|
+
| pending -> partial|
|
|
231
|
+
| partial -> complete|
|
|
232
|
+
+-------------------+
|
|
233
|
+
|
|
234
|
+
4. CONFIRMATION EVENT TYPES:
|
|
235
|
+
|
|
236
|
+
- consul.registered: Confirmation from ConsulAdapter that service
|
|
237
|
+
was successfully registered in Consul. Published to:
|
|
238
|
+
onex.registration.events (or onex.<domain>.events)
|
|
239
|
+
|
|
240
|
+
Payload includes:
|
|
241
|
+
- correlation_id: Links back to original introspection event
|
|
242
|
+
- service_id: The registered Consul service ID
|
|
243
|
+
- success: bool indicating registration outcome
|
|
244
|
+
- error: Optional error message if failed
|
|
245
|
+
|
|
246
|
+
- postgres.registration_upserted: Confirmation from PostgresAdapter
|
|
247
|
+
that registration record was successfully upserted. Published to:
|
|
248
|
+
onex.registration.events
|
|
249
|
+
|
|
250
|
+
Payload includes:
|
|
251
|
+
- correlation_id: Links back to original introspection event
|
|
252
|
+
- node_id: The registered node ID
|
|
253
|
+
- success: bool indicating upsert outcome
|
|
254
|
+
- error: Optional error message if failed
|
|
255
|
+
|
|
256
|
+
5. STATE TRANSITION DIAGRAM:
|
|
257
|
+
|
|
258
|
+
+-------+ introspection +---------+
|
|
259
|
+
| idle | ----------------> | pending |
|
|
260
|
+
+-------+ +---------+
|
|
261
|
+
^ | |
|
|
262
|
+
| consul confirmed | | postgres confirmed
|
|
263
|
+
| (first) v v (first)
|
|
264
|
+
| +---------+
|
|
265
|
+
| | partial |
|
|
266
|
+
| +---------+
|
|
267
|
+
| | |
|
|
268
|
+
| remaining | | error received
|
|
269
|
+
| confirmed v v
|
|
270
|
+
| +---------+ +---------+
|
|
271
|
+
+---reset------| complete| | failed |---reset---+
|
|
272
|
+
+---------+ +---------+ |
|
|
273
|
+
v
|
|
274
|
+
+-------+
|
|
275
|
+
| idle |
|
|
276
|
+
+-------+
|
|
277
|
+
|
|
278
|
+
Transitions:
|
|
279
|
+
- idle -> pending: On introspection event (emits intents)
|
|
280
|
+
- pending -> partial: First confirmation received (consul OR postgres)
|
|
281
|
+
- pending -> failed: Error confirmation received
|
|
282
|
+
- partial -> complete: Second confirmation received (both confirmed)
|
|
283
|
+
- partial -> failed: Error confirmation for remaining backend
|
|
284
|
+
- any -> failed: Validation or backend error
|
|
285
|
+
- failed -> idle: Reset event (allows retry after failure)
|
|
286
|
+
- complete -> idle: Reset event (allows re-registration)
|
|
287
|
+
|
|
288
|
+
6. IMPLEMENTATION NOTE - reduce_confirmation():
|
|
289
|
+
|
|
290
|
+
The reduce_confirmation() method (to be implemented) will handle
|
|
291
|
+
confirmation events. It uses the same pure reducer pattern:
|
|
292
|
+
|
|
293
|
+
def reduce_confirmation(
|
|
294
|
+
self,
|
|
295
|
+
state: ModelRegistrationState,
|
|
296
|
+
confirmation: ModelRegistrationConfirmation,
|
|
297
|
+
) -> ModelReducerOutput[ModelRegistrationState]:
|
|
298
|
+
'''Process confirmation event from Effect layer.'''
|
|
299
|
+
# Validate confirmation matches current node_id
|
|
300
|
+
# Transition state based on confirmation type:
|
|
301
|
+
# - consul.registered -> with_consul_confirmed()
|
|
302
|
+
# - postgres.registration_upserted -> with_postgres_confirmed()
|
|
303
|
+
# - error -> with_failure()
|
|
304
|
+
# Return new state with no intents (confirmations don't emit new intents)
|
|
305
|
+
|
|
306
|
+
The confirmation event model should include:
|
|
307
|
+
- event_type: "consul.registered" | "postgres.registration_upserted"
|
|
308
|
+
- correlation_id: UUID linking to original introspection
|
|
309
|
+
- node_id: UUID of the registered node
|
|
310
|
+
- success: bool
|
|
311
|
+
- error_message: str | None
|
|
312
|
+
- timestamp: datetime
|
|
313
|
+
|
|
314
|
+
7. IDEMPOTENCY:
|
|
315
|
+
|
|
316
|
+
Confirmation events are also subject to idempotency:
|
|
317
|
+
- Duplicate confirmations (same event_id) are skipped
|
|
318
|
+
- Confirmations for wrong node_id are rejected
|
|
319
|
+
- Re-confirmations after complete/failed are no-ops
|
|
320
|
+
|
|
321
|
+
8. TIMEOUT HANDLING:
|
|
322
|
+
|
|
323
|
+
Per DESIGN_TWO_WAY_REGISTRATION_ARCHITECTURE.md, timeouts are owned
|
|
324
|
+
by the Orchestrator layer, not the Reducer:
|
|
325
|
+
|
|
326
|
+
- Orchestrator tracks pending registrations with deadlines
|
|
327
|
+
- Orchestrator consumes RuntimeTick events for timeout evaluation
|
|
328
|
+
- Orchestrator emits RegistrationTimedOut events when deadline passes
|
|
329
|
+
- Reducer folds RegistrationTimedOut as a failure confirmation
|
|
330
|
+
|
|
331
|
+
Related:
|
|
332
|
+
- NodeDualRegistrationReducer: Legacy 887-line implementation (deprecated)
|
|
333
|
+
- DESIGN_TWO_WAY_REGISTRATION_ARCHITECTURE.md: Architecture design
|
|
334
|
+
- MESSAGE_DISPATCH_ENGINE.md: How runtime routes events to reducers
|
|
335
|
+
- OMN-889: Infrastructure MVP - ModelNodeIntrospectionEvent
|
|
336
|
+
- OMN-912: ModelIntent typed payloads
|
|
337
|
+
"""
|
|
338
|
+
|
|
339
|
+
from __future__ import annotations
|
|
340
|
+
|
|
341
|
+
import hashlib
|
|
342
|
+
import logging
|
|
343
|
+
import os
|
|
344
|
+
import time
|
|
345
|
+
from datetime import UTC, datetime
|
|
346
|
+
from typing import Literal
|
|
347
|
+
from uuid import UUID, uuid4
|
|
348
|
+
|
|
349
|
+
from pydantic import BaseModel, ConfigDict, field_validator
|
|
350
|
+
|
|
351
|
+
from omnibase_core.enums import EnumNodeKind, EnumReductionType, EnumStreamingMode
|
|
352
|
+
from omnibase_core.models.reducer.model_intent import ModelIntent
|
|
353
|
+
from omnibase_core.nodes import ModelReducerOutput
|
|
354
|
+
from omnibase_infra.enums import EnumConfirmationEventType
|
|
355
|
+
from omnibase_infra.models.registration import (
|
|
356
|
+
ModelNodeIntrospectionEvent,
|
|
357
|
+
ModelNodeRegistrationRecord,
|
|
358
|
+
)
|
|
359
|
+
from omnibase_infra.nodes.reducers.models.model_payload_consul_register import (
|
|
360
|
+
ModelPayloadConsulRegister,
|
|
361
|
+
)
|
|
362
|
+
from omnibase_infra.nodes.reducers.models.model_payload_postgres_upsert_registration import (
|
|
363
|
+
ModelPayloadPostgresUpsertRegistration,
|
|
364
|
+
)
|
|
365
|
+
from omnibase_infra.nodes.reducers.models.model_registration_confirmation import (
|
|
366
|
+
ModelRegistrationConfirmation,
|
|
367
|
+
)
|
|
368
|
+
from omnibase_infra.nodes.reducers.models.model_registration_state import (
|
|
369
|
+
ModelRegistrationState,
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
# =============================================================================
|
|
373
|
+
# Performance Threshold Constants (in milliseconds)
|
|
374
|
+
#
|
|
375
|
+
# These constants define the performance targets for the RegistrationReducer.
|
|
376
|
+
# When processing time exceeds these thresholds, a warning is logged to help
|
|
377
|
+
# identify performance regressions or unusually complex events.
|
|
378
|
+
#
|
|
379
|
+
# These are intentionally generous for production use (300ms target) because:
|
|
380
|
+
# 1. Pure reducers should be fast (no I/O)
|
|
381
|
+
# 2. Most events complete in <5ms
|
|
382
|
+
# 3. Threshold alerts indicate something unusual (test environment, GC pause, etc.)
|
|
383
|
+
#
|
|
384
|
+
# Environment Variable Configuration:
|
|
385
|
+
# ONEX_PERF_THRESHOLD_REDUCE_MS - reduce() processing threshold (default: 300.0)
|
|
386
|
+
# ONEX_PERF_THRESHOLD_INTENT_BUILD_MS - intent building threshold (default: 50.0)
|
|
387
|
+
# ONEX_PERF_THRESHOLD_IDEMPOTENCY_CHECK_MS - idempotency check threshold (default: 1.0)
|
|
388
|
+
#
|
|
389
|
+
# Example usage:
|
|
390
|
+
# export ONEX_PERF_THRESHOLD_REDUCE_MS=100.0 # Stricter threshold for production
|
|
391
|
+
# export ONEX_PERF_THRESHOLD_REDUCE_MS=1000.0 # Relaxed threshold for dev/CI
|
|
392
|
+
# =============================================================================
|
|
393
|
+
|
|
394
|
+
# Target processing time for reduce() method (<300ms per event)
|
|
395
|
+
# This is the primary performance metric for the reducer.
|
|
396
|
+
PERF_THRESHOLD_REDUCE_MS: float = float(
|
|
397
|
+
os.getenv("ONEX_PERF_THRESHOLD_REDUCE_MS", "300.0")
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
# Target processing time for intent building (<50ms per intent)
|
|
401
|
+
# Consul and PostgreSQL intent construction should be fast.
|
|
402
|
+
PERF_THRESHOLD_INTENT_BUILD_MS: float = float(
|
|
403
|
+
os.getenv("ONEX_PERF_THRESHOLD_INTENT_BUILD_MS", "50.0")
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
# Target processing time for idempotency check (<1ms)
|
|
407
|
+
# Simple UUID comparison should be nearly instant.
|
|
408
|
+
PERF_THRESHOLD_IDEMPOTENCY_CHECK_MS: float = float(
|
|
409
|
+
os.getenv("ONEX_PERF_THRESHOLD_IDEMPOTENCY_CHECK_MS", "1.0")
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
# Logger for performance warnings and validation errors
|
|
413
|
+
_logger = logging.getLogger(__name__)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
# =============================================================================
|
|
417
|
+
# Validation Error Types
|
|
418
|
+
# =============================================================================
|
|
419
|
+
|
|
420
|
+
ValidationErrorCode = Literal[
|
|
421
|
+
"missing_node_id",
|
|
422
|
+
"missing_node_type",
|
|
423
|
+
"invalid_node_type",
|
|
424
|
+
]
|
|
425
|
+
|
|
426
|
+
# Sentinel value for "not set" state
|
|
427
|
+
_SENTINEL_STR: str = ""
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
class ModelValidationResult(BaseModel):
|
|
431
|
+
"""Result of event validation with detailed error information.
|
|
432
|
+
|
|
433
|
+
This Pydantic model replaces the previous dataclass implementation
|
|
434
|
+
to comply with ONEX requirements for Pydantic-based data structures.
|
|
435
|
+
|
|
436
|
+
This model uses sentinel values (empty string) instead of nullable unions
|
|
437
|
+
to minimize union count in the codebase (OMN-1004).
|
|
438
|
+
|
|
439
|
+
Sentinel Values:
|
|
440
|
+
- Empty string ("") for field_name and error_message means "not set"
|
|
441
|
+
- None for error_code (unavoidable for Literal type safety)
|
|
442
|
+
- Use ``has_field_name``, ``has_error_message`` to check
|
|
443
|
+
|
|
444
|
+
Constructor API:
|
|
445
|
+
Constructors accept ``None`` for string fields and convert to sentinel.
|
|
446
|
+
|
|
447
|
+
Attributes:
|
|
448
|
+
is_valid: Whether the event passed validation.
|
|
449
|
+
error_code: Distinct code identifying the validation failure (if any).
|
|
450
|
+
field_name: Name of the field that failed validation. Empty string if not set.
|
|
451
|
+
error_message: Human-readable error message for logging. Empty string if not set.
|
|
452
|
+
|
|
453
|
+
.. versionchanged:: 0.7.0
|
|
454
|
+
Refactored to use sentinel values for string fields (OMN-1004).
|
|
455
|
+
"""
|
|
456
|
+
|
|
457
|
+
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
|
|
458
|
+
|
|
459
|
+
is_valid: bool
|
|
460
|
+
error_code: ValidationErrorCode | None = None
|
|
461
|
+
field_name: str = _SENTINEL_STR
|
|
462
|
+
error_message: str = _SENTINEL_STR
|
|
463
|
+
|
|
464
|
+
# ---- Validators for None-to-Sentinel Conversion ----
|
|
465
|
+
@field_validator("field_name", "error_message", mode="before")
|
|
466
|
+
@classmethod
|
|
467
|
+
def _convert_none_to_str_sentinel(cls, v: object) -> str:
|
|
468
|
+
"""Convert None to empty string sentinel for API convenience."""
|
|
469
|
+
if v is None:
|
|
470
|
+
return _SENTINEL_STR
|
|
471
|
+
if isinstance(v, str):
|
|
472
|
+
return v
|
|
473
|
+
return str(v)
|
|
474
|
+
|
|
475
|
+
# ---- Sentinel Check Properties ----
|
|
476
|
+
@property
|
|
477
|
+
def has_field_name(self) -> bool:
|
|
478
|
+
"""Check if field_name is set (not empty string)."""
|
|
479
|
+
return self.field_name != _SENTINEL_STR
|
|
480
|
+
|
|
481
|
+
@property
|
|
482
|
+
def has_error_message(self) -> bool:
|
|
483
|
+
"""Check if error_message is set (not empty string)."""
|
|
484
|
+
return self.error_message != _SENTINEL_STR
|
|
485
|
+
|
|
486
|
+
@classmethod
|
|
487
|
+
def success(cls) -> ModelValidationResult:
|
|
488
|
+
"""Create a successful validation result."""
|
|
489
|
+
return cls(is_valid=True)
|
|
490
|
+
|
|
491
|
+
@classmethod
|
|
492
|
+
def failure(
|
|
493
|
+
cls,
|
|
494
|
+
error_code: ValidationErrorCode,
|
|
495
|
+
field_name: str,
|
|
496
|
+
error_message: str,
|
|
497
|
+
) -> ModelValidationResult:
|
|
498
|
+
"""Create a failed validation result with error details."""
|
|
499
|
+
return cls(
|
|
500
|
+
is_valid=False,
|
|
501
|
+
error_code=error_code,
|
|
502
|
+
field_name=field_name,
|
|
503
|
+
error_message=error_message,
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
# TODO(OMN-889): Complete pure reducer implementation - add reduce_confirmation() method
|
|
508
|
+
class RegistrationReducer:
|
|
509
|
+
"""Pure reducer for node registration workflow.
|
|
510
|
+
|
|
511
|
+
Follows ProtocolReducer pattern:
|
|
512
|
+
- reduce(state, event) -> ModelReducerOutput
|
|
513
|
+
- Pure function, no side effects
|
|
514
|
+
- Emits intents for Consul and PostgreSQL registration
|
|
515
|
+
|
|
516
|
+
This is a stateless class - all state is passed in and returned via
|
|
517
|
+
ModelRegistrationState. The class exists to group related pure functions.
|
|
518
|
+
|
|
519
|
+
Event Processing Methods:
|
|
520
|
+
This reducer handles two categories of events:
|
|
521
|
+
|
|
522
|
+
1. reduce(state, introspection_event) -> Processes initial node introspection,
|
|
523
|
+
emits registration intents for Effect layer execution.
|
|
524
|
+
|
|
525
|
+
2. reduce_confirmation(state, confirmation_event) -> Processes confirmation
|
|
526
|
+
events from Effect layer, updates state to partial/complete/failed.
|
|
527
|
+
(See module docstring section 6 for implementation details.)
|
|
528
|
+
|
|
529
|
+
Complete Event Cycle:
|
|
530
|
+
1. Node publishes introspection event to Kafka
|
|
531
|
+
2. Runtime routes introspection to this reducer via reduce()
|
|
532
|
+
3. Reducer emits intents (consul.register, postgres.upsert_registration)
|
|
533
|
+
4. Runtime publishes intents to Kafka intent topics
|
|
534
|
+
5. Effect layer nodes (ConsulAdapter, PostgresAdapter) consume intents
|
|
535
|
+
6. Effect nodes execute I/O and publish confirmation events to Kafka
|
|
536
|
+
7. Runtime routes confirmation events back to this reducer
|
|
537
|
+
8. Reducer updates state: pending -> partial -> complete
|
|
538
|
+
|
|
539
|
+
Topic Subscriptions:
|
|
540
|
+
The reducer node subscribes to:
|
|
541
|
+
- onex.registration.events (or onex.<domain>.events)
|
|
542
|
+
|
|
543
|
+
This includes both introspection events and confirmation events.
|
|
544
|
+
The reduce() method dispatches to the appropriate handler based on
|
|
545
|
+
event type.
|
|
546
|
+
|
|
547
|
+
Example:
|
|
548
|
+
>>> from uuid import uuid4
|
|
549
|
+
>>> from omnibase_infra.models.registration import ModelNodeIntrospectionEvent
|
|
550
|
+
>>> from omnibase_infra.nodes.reducers import RegistrationReducer
|
|
551
|
+
>>> from omnibase_infra.nodes.reducers.models import ModelRegistrationState
|
|
552
|
+
>>>
|
|
553
|
+
>>> reducer = RegistrationReducer()
|
|
554
|
+
>>> state = ModelRegistrationState() # Initial idle state
|
|
555
|
+
>>> event = ModelNodeIntrospectionEvent(
|
|
556
|
+
... node_id=uuid4(),
|
|
557
|
+
... node_type="effect",
|
|
558
|
+
... node_version="1.0.0",
|
|
559
|
+
... endpoints={"health": "http://localhost:8080/health"},
|
|
560
|
+
... )
|
|
561
|
+
>>> output = reducer.reduce(state, event)
|
|
562
|
+
>>> print(output.result.status) # "pending"
|
|
563
|
+
>>> print(len(output.intents)) # 2 (Consul + PostgreSQL)
|
|
564
|
+
"""
|
|
565
|
+
|
|
566
|
+
def reduce(
|
|
567
|
+
self,
|
|
568
|
+
state: ModelRegistrationState,
|
|
569
|
+
event: ModelNodeIntrospectionEvent,
|
|
570
|
+
) -> ModelReducerOutput[ModelRegistrationState]:
|
|
571
|
+
"""Pure reduce function: state + event -> new_state + intents.
|
|
572
|
+
|
|
573
|
+
Processes a node introspection event and emits registration intents
|
|
574
|
+
for both Consul and PostgreSQL backends. The returned output contains
|
|
575
|
+
the new state and any intents to be executed by the Effect layer.
|
|
576
|
+
|
|
577
|
+
This is PHASE 1 of the confirmation event flow:
|
|
578
|
+
1. Node publishes introspection event -> Runtime routes here
|
|
579
|
+
2. This method processes event -> Emits intents
|
|
580
|
+
3. Runtime publishes intents to Kafka -> Effect layer executes
|
|
581
|
+
4. Effect layer publishes confirmations -> reduce_confirmation() handles
|
|
582
|
+
|
|
583
|
+
Idempotency:
|
|
584
|
+
If the event has already been processed (based on event_id), the
|
|
585
|
+
reducer returns immediately with the current state and no intents.
|
|
586
|
+
|
|
587
|
+
Validation:
|
|
588
|
+
If the event fails validation (e.g., missing node_id), the reducer
|
|
589
|
+
transitions to failed state with no intents.
|
|
590
|
+
|
|
591
|
+
Args:
|
|
592
|
+
state: Current registration state (immutable).
|
|
593
|
+
event: Node introspection event to process.
|
|
594
|
+
|
|
595
|
+
Returns:
|
|
596
|
+
ModelReducerOutput containing new_state and intents tuple.
|
|
597
|
+
The result field contains the new ModelRegistrationState.
|
|
598
|
+
The intents field contains registration intents for Effect layer.
|
|
599
|
+
"""
|
|
600
|
+
start_time = time.perf_counter()
|
|
601
|
+
|
|
602
|
+
# =====================================================================
|
|
603
|
+
# CONFIRMATION FLOW STEP 1: Receive introspection event from Kafka
|
|
604
|
+
# This event was published by a node during startup/discovery.
|
|
605
|
+
# The Runtime (MessageDispatchEngine) routed it here based on:
|
|
606
|
+
# - Topic: onex.registration.events (or similar)
|
|
607
|
+
# - Message type: ModelNodeIntrospectionEvent
|
|
608
|
+
# =====================================================================
|
|
609
|
+
|
|
610
|
+
# Resolve event ID for idempotency.
|
|
611
|
+
# CRITICAL: We use a deterministic derivation when correlation_id is absent.
|
|
612
|
+
# Using uuid4() here would break idempotency because replayed events would
|
|
613
|
+
# get different IDs each time, making duplicate detection impossible.
|
|
614
|
+
# Instead, we derive a UUID from the event's content hash (node_id + node_type
|
|
615
|
+
# + timestamp), ensuring the same event always produces the same ID.
|
|
616
|
+
event_id = event.correlation_id or self._derive_deterministic_event_id(event)
|
|
617
|
+
|
|
618
|
+
# Idempotency guard - skip if we've already processed this event
|
|
619
|
+
if state.is_duplicate_event(event_id):
|
|
620
|
+
return self._build_output(
|
|
621
|
+
state=state,
|
|
622
|
+
intents=(),
|
|
623
|
+
processing_time_ms=0.0,
|
|
624
|
+
items_processed=0,
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
# Validate event - failures transition to failed state with no intents
|
|
628
|
+
# Note: Validation errors are logged with sanitized context (no PII/secrets)
|
|
629
|
+
# but the output intentionally uses a generic failure_reason to avoid
|
|
630
|
+
# exposing internal validation logic to external consumers.
|
|
631
|
+
validation_result = self._validate_event(event)
|
|
632
|
+
if not validation_result.is_valid:
|
|
633
|
+
# Log validation failure with sanitized context for diagnostics
|
|
634
|
+
# SECURITY: Only field presence booleans and error codes are logged
|
|
635
|
+
_logger.warning(
|
|
636
|
+
"Event validation failed",
|
|
637
|
+
extra={
|
|
638
|
+
"error_code": validation_result.error_code,
|
|
639
|
+
"field_name": validation_result.field_name,
|
|
640
|
+
"error_message": validation_result.error_message,
|
|
641
|
+
"correlation_id": str(event_id),
|
|
642
|
+
"has_node_id": event.node_id is not None,
|
|
643
|
+
"has_node_type": hasattr(event, "node_type")
|
|
644
|
+
and event.node_type is not None,
|
|
645
|
+
},
|
|
646
|
+
)
|
|
647
|
+
new_state = state.with_failure("validation_failed", event_id)
|
|
648
|
+
return self._build_output(
|
|
649
|
+
state=new_state,
|
|
650
|
+
intents=(),
|
|
651
|
+
processing_time_ms=(time.perf_counter() - start_time) * 1000,
|
|
652
|
+
items_processed=0,
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
# =====================================================================
|
|
656
|
+
# CONFIRMATION FLOW STEP 2: Build intents for Effect layer
|
|
657
|
+
# These intents describe the desired I/O operations:
|
|
658
|
+
# - consul.register: Register service in Consul
|
|
659
|
+
# - postgres.upsert_registration: Upsert record in PostgreSQL
|
|
660
|
+
#
|
|
661
|
+
# The correlation_id is propagated to enable confirmation tracking.
|
|
662
|
+
# When Effect nodes complete, they publish confirmation events with
|
|
663
|
+
# this correlation_id, allowing this reducer to match confirmations
|
|
664
|
+
# to the original introspection event.
|
|
665
|
+
# =====================================================================
|
|
666
|
+
|
|
667
|
+
correlation_id = event.correlation_id or event_id
|
|
668
|
+
consul_intent = self._build_consul_intent(event, correlation_id)
|
|
669
|
+
postgres_intent = self._build_postgres_intent(event, correlation_id)
|
|
670
|
+
|
|
671
|
+
# Collect non-None intents
|
|
672
|
+
intents: tuple[ModelIntent, ...] = tuple(
|
|
673
|
+
intent for intent in [consul_intent, postgres_intent] if intent is not None
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
# =====================================================================
|
|
677
|
+
# CONFIRMATION FLOW STEP 3: Transition to pending state
|
|
678
|
+
# State: idle -> pending
|
|
679
|
+
#
|
|
680
|
+
# After this method returns:
|
|
681
|
+
# - Runtime publishes intents to Kafka (onex.registration.intents)
|
|
682
|
+
# - Effect nodes (ConsulAdapter, PostgresAdapter) consume intents
|
|
683
|
+
# - Effect nodes execute I/O and publish confirmation events
|
|
684
|
+
# - Runtime routes confirmation events to reduce_confirmation()
|
|
685
|
+
# - reduce_confirmation() transitions: pending -> partial -> complete
|
|
686
|
+
# =====================================================================
|
|
687
|
+
|
|
688
|
+
new_state = state.with_pending_registration(event.node_id, event_id)
|
|
689
|
+
|
|
690
|
+
processing_time_ms = (time.perf_counter() - start_time) * 1000
|
|
691
|
+
|
|
692
|
+
# Performance logging: warn if processing exceeds threshold
|
|
693
|
+
if processing_time_ms > PERF_THRESHOLD_REDUCE_MS:
|
|
694
|
+
_logger.warning(
|
|
695
|
+
"Reducer processing time exceeded threshold",
|
|
696
|
+
extra={
|
|
697
|
+
"processing_time_ms": processing_time_ms,
|
|
698
|
+
"threshold_ms": PERF_THRESHOLD_REDUCE_MS,
|
|
699
|
+
"node_type": event.node_type.value,
|
|
700
|
+
"intent_count": len(intents),
|
|
701
|
+
"correlation_id": str(correlation_id),
|
|
702
|
+
},
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
return self._build_output(
|
|
706
|
+
state=new_state,
|
|
707
|
+
intents=intents,
|
|
708
|
+
processing_time_ms=processing_time_ms,
|
|
709
|
+
items_processed=1,
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
def _is_valid(self, event: ModelNodeIntrospectionEvent) -> bool:
|
|
713
|
+
"""Validate introspection event for processing.
|
|
714
|
+
|
|
715
|
+
Convenience wrapper around _validate_event() that returns a simple bool.
|
|
716
|
+
Use _validate_event() when detailed error information is needed.
|
|
717
|
+
|
|
718
|
+
Args:
|
|
719
|
+
event: Introspection event to validate.
|
|
720
|
+
|
|
721
|
+
Returns:
|
|
722
|
+
True if the event is valid for processing, False otherwise.
|
|
723
|
+
"""
|
|
724
|
+
return self._validate_event(event).is_valid
|
|
725
|
+
|
|
726
|
+
def _validate_event(
|
|
727
|
+
self, event: ModelNodeIntrospectionEvent
|
|
728
|
+
) -> ModelValidationResult:
|
|
729
|
+
"""Validate introspection event with detailed error information.
|
|
730
|
+
|
|
731
|
+
Validates that required fields are present for registration workflow.
|
|
732
|
+
Returns a ValidationResult with distinct error codes for each failure
|
|
733
|
+
scenario, enabling proper logging and diagnostics.
|
|
734
|
+
|
|
735
|
+
**Validation Rules:**
|
|
736
|
+
- node_id: Must be present (required for registration identity)
|
|
737
|
+
- node_type: Must be present and valid (required for service categorization)
|
|
738
|
+
|
|
739
|
+
**Error Codes:**
|
|
740
|
+
- missing_node_id: node_id is None
|
|
741
|
+
- missing_node_type: node_type attribute is missing or None
|
|
742
|
+
- invalid_node_type: node_type is not a valid ONEX node type
|
|
743
|
+
|
|
744
|
+
**Security Note:**
|
|
745
|
+
Error messages are logged server-side but the reducer's output uses
|
|
746
|
+
a generic "validation_failed" reason to avoid exposing internal
|
|
747
|
+
validation logic to external consumers.
|
|
748
|
+
|
|
749
|
+
Args:
|
|
750
|
+
event: Introspection event to validate.
|
|
751
|
+
|
|
752
|
+
Returns:
|
|
753
|
+
ValidationResult with is_valid=True if valid, or detailed error
|
|
754
|
+
information if validation failed.
|
|
755
|
+
"""
|
|
756
|
+
# Validate node_id: required for registration identity
|
|
757
|
+
if event.node_id is None:
|
|
758
|
+
return ModelValidationResult.failure(
|
|
759
|
+
error_code="missing_node_id",
|
|
760
|
+
field_name="node_id",
|
|
761
|
+
error_message="node_id is required for registration identity",
|
|
762
|
+
)
|
|
763
|
+
|
|
764
|
+
# Validate node_type: must be present
|
|
765
|
+
if not hasattr(event, "node_type") or event.node_type is None:
|
|
766
|
+
return ModelValidationResult.failure(
|
|
767
|
+
error_code="missing_node_type",
|
|
768
|
+
field_name="node_type",
|
|
769
|
+
error_message="node_type is required for service categorization",
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
# Validate node_type value is valid ONEX type
|
|
773
|
+
# Use EnumNodeKind values (excluding RUNTIME_HOST which is not a registration type)
|
|
774
|
+
valid_node_types = {
|
|
775
|
+
EnumNodeKind.EFFECT.value,
|
|
776
|
+
EnumNodeKind.COMPUTE.value,
|
|
777
|
+
EnumNodeKind.REDUCER.value,
|
|
778
|
+
EnumNodeKind.ORCHESTRATOR.value,
|
|
779
|
+
}
|
|
780
|
+
if event.node_type.value not in valid_node_types:
|
|
781
|
+
return ModelValidationResult.failure(
|
|
782
|
+
error_code="invalid_node_type",
|
|
783
|
+
field_name="node_type",
|
|
784
|
+
error_message=(
|
|
785
|
+
f"node_type must be one of: {', '.join(sorted(valid_node_types))}"
|
|
786
|
+
),
|
|
787
|
+
)
|
|
788
|
+
|
|
789
|
+
return ModelValidationResult.success()
|
|
790
|
+
|
|
791
|
+
def _derive_deterministic_event_id(
|
|
792
|
+
self, event: ModelNodeIntrospectionEvent
|
|
793
|
+
) -> UUID:
|
|
794
|
+
"""Derive a deterministic event ID from event content.
|
|
795
|
+
|
|
796
|
+
When an event lacks a correlation_id, we must derive a stable identifier
|
|
797
|
+
from its content to preserve idempotency guarantees. Using uuid4() would
|
|
798
|
+
break idempotency because replayed events would get different IDs.
|
|
799
|
+
|
|
800
|
+
The derived ID uses a SHA-256 hash of the event's identifying fields:
|
|
801
|
+
- node_id: Unique node identifier
|
|
802
|
+
- node_type: Node archetype (effect, compute, reducer, orchestrator)
|
|
803
|
+
- timestamp: Event creation timestamp (ISO format for stability)
|
|
804
|
+
|
|
805
|
+
This ensures:
|
|
806
|
+
1. Same event content always produces the same ID
|
|
807
|
+
2. Different events produce different IDs (collision-resistant)
|
|
808
|
+
3. ID format is compatible with existing UUID-based tracking
|
|
809
|
+
|
|
810
|
+
Args:
|
|
811
|
+
event: The introspection event to derive an ID from.
|
|
812
|
+
|
|
813
|
+
Returns:
|
|
814
|
+
A deterministic UUID derived from the event's content.
|
|
815
|
+
"""
|
|
816
|
+
# Build a canonical string from the event's identifying fields.
|
|
817
|
+
# Using ISO format for timestamp ensures string stability across serialization.
|
|
818
|
+
# The pipe delimiter prevents ambiguity between field values.
|
|
819
|
+
canonical_content = (
|
|
820
|
+
f"{event.node_id}|{event.node_type.value}|{event.timestamp.isoformat()}"
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
# Compute SHA-256 hash and convert to UUID format.
|
|
824
|
+
# SHA-256 provides strong collision resistance for content-derived IDs.
|
|
825
|
+
content_hash = hashlib.sha256(canonical_content.encode("utf-8")).hexdigest()
|
|
826
|
+
|
|
827
|
+
# Take first 32 hex chars (128 bits) and format as UUID.
|
|
828
|
+
# Insert hyphens in standard UUID format: 8-4-4-4-12
|
|
829
|
+
uuid_hex = content_hash[:32]
|
|
830
|
+
uuid_str = (
|
|
831
|
+
f"{uuid_hex[:8]}-{uuid_hex[8:12]}-{uuid_hex[12:16]}-"
|
|
832
|
+
f"{uuid_hex[16:20]}-{uuid_hex[20:32]}"
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
return UUID(uuid_str)
|
|
836
|
+
|
|
837
|
+
def _build_consul_intent(
|
|
838
|
+
self,
|
|
839
|
+
event: ModelNodeIntrospectionEvent,
|
|
840
|
+
correlation_id: UUID,
|
|
841
|
+
) -> ModelIntent | None:
|
|
842
|
+
"""Build Consul registration intent (pure, no I/O).
|
|
843
|
+
|
|
844
|
+
Creates a ModelIntent that describes the desired Consul service
|
|
845
|
+
registration. The Effect layer is responsible for executing this intent.
|
|
846
|
+
|
|
847
|
+
Args:
|
|
848
|
+
event: Introspection event containing node data.
|
|
849
|
+
correlation_id: Correlation ID for tracing.
|
|
850
|
+
|
|
851
|
+
Returns:
|
|
852
|
+
ModelIntent with intent_type="extension" (dual-layer routing).
|
|
853
|
+
The routing key "consul.register" is in payload.intent_type.
|
|
854
|
+
"""
|
|
855
|
+
service_id = f"onex-{event.node_type.value}-{event.node_id}"
|
|
856
|
+
service_name = f"onex-{event.node_type.value}"
|
|
857
|
+
tags = [
|
|
858
|
+
f"node_type:{event.node_type.value}",
|
|
859
|
+
f"node_version:{event.node_version}",
|
|
860
|
+
]
|
|
861
|
+
|
|
862
|
+
# Build health check configuration if health endpoint is provided
|
|
863
|
+
health_endpoint = event.endpoints.get("health") if event.endpoints else None
|
|
864
|
+
health_check: dict[str, str] | None = None
|
|
865
|
+
if health_endpoint:
|
|
866
|
+
health_check = {
|
|
867
|
+
"HTTP": health_endpoint,
|
|
868
|
+
"Interval": "10s",
|
|
869
|
+
"Timeout": "5s",
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
# Build typed Consul registration payload (implements ProtocolIntentPayload)
|
|
873
|
+
consul_payload = ModelPayloadConsulRegister(
|
|
874
|
+
correlation_id=correlation_id,
|
|
875
|
+
service_id=service_id,
|
|
876
|
+
service_name=service_name,
|
|
877
|
+
tags=tags,
|
|
878
|
+
health_check=health_check,
|
|
879
|
+
)
|
|
880
|
+
|
|
881
|
+
# ModelIntent.payload expects ProtocolIntentPayload, which our model implements
|
|
882
|
+
return ModelIntent(
|
|
883
|
+
intent_type="extension",
|
|
884
|
+
target=f"consul://service/{service_name}",
|
|
885
|
+
payload=consul_payload,
|
|
886
|
+
)
|
|
887
|
+
|
|
888
|
+
def _build_postgres_intent(
|
|
889
|
+
self,
|
|
890
|
+
event: ModelNodeIntrospectionEvent,
|
|
891
|
+
correlation_id: UUID,
|
|
892
|
+
) -> ModelIntent | None:
|
|
893
|
+
"""Build PostgreSQL upsert intent (pure, no I/O).
|
|
894
|
+
|
|
895
|
+
Creates a ModelIntent that describes the desired PostgreSQL record
|
|
896
|
+
upsert. The Effect layer is responsible for executing this intent.
|
|
897
|
+
|
|
898
|
+
Args:
|
|
899
|
+
event: Introspection event containing node data.
|
|
900
|
+
correlation_id: Correlation ID for tracing.
|
|
901
|
+
|
|
902
|
+
Returns:
|
|
903
|
+
ModelIntent with intent_type="extension" (dual-layer routing).
|
|
904
|
+
The routing key "postgres.upsert_registration" is in payload.intent_type.
|
|
905
|
+
"""
|
|
906
|
+
now = datetime.now(UTC)
|
|
907
|
+
|
|
908
|
+
# Build the registration record using strongly-typed models
|
|
909
|
+
# event.declared_capabilities and event.metadata are already typed as
|
|
910
|
+
# ModelNodeCapabilities and ModelNodeMetadata respectively
|
|
911
|
+
record = ModelNodeRegistrationRecord(
|
|
912
|
+
node_id=event.node_id,
|
|
913
|
+
node_type=event.node_type,
|
|
914
|
+
node_version=event.node_version,
|
|
915
|
+
capabilities=event.declared_capabilities,
|
|
916
|
+
endpoints=dict(event.endpoints) if event.endpoints else {},
|
|
917
|
+
metadata=event.metadata,
|
|
918
|
+
health_endpoint=(
|
|
919
|
+
event.endpoints.get("health") if event.endpoints else None
|
|
920
|
+
),
|
|
921
|
+
registered_at=now,
|
|
922
|
+
updated_at=now,
|
|
923
|
+
)
|
|
924
|
+
|
|
925
|
+
# Build typed PostgreSQL upsert payload (implements ProtocolIntentPayload)
|
|
926
|
+
postgres_payload = ModelPayloadPostgresUpsertRegistration(
|
|
927
|
+
correlation_id=correlation_id,
|
|
928
|
+
record=record,
|
|
929
|
+
)
|
|
930
|
+
|
|
931
|
+
# ModelIntent.payload expects ProtocolIntentPayload, which our model implements
|
|
932
|
+
return ModelIntent(
|
|
933
|
+
intent_type="extension",
|
|
934
|
+
target=f"postgres://node_registrations/{event.node_id}",
|
|
935
|
+
payload=postgres_payload,
|
|
936
|
+
)
|
|
937
|
+
|
|
938
|
+
# =========================================================================
|
|
939
|
+
# CONFIRMATION EVENT HANDLING (PHASE 2 of the event flow)
|
|
940
|
+
#
|
|
941
|
+
# The following method will handle confirmation events from Effect layer.
|
|
942
|
+
# It is documented here as a stub to show the complete event flow.
|
|
943
|
+
#
|
|
944
|
+
# Follow-up Ticket: OMN-996 (Implement Confirmation Event Handling)
|
|
945
|
+
# https://linear.app/omninode/issue/OMN-996
|
|
946
|
+
#
|
|
947
|
+
# Prerequisites:
|
|
948
|
+
# - [DONE] ModelRegistrationConfirmation model defined
|
|
949
|
+
# See: omnibase_infra.nodes.reducers.models.model_registration_confirmation
|
|
950
|
+
# - Effect layer confirmation event publishing implemented
|
|
951
|
+
# - Tests added for confirmation event handling
|
|
952
|
+
#
|
|
953
|
+
# See module docstring section 6 for detailed implementation notes.
|
|
954
|
+
# =========================================================================
|
|
955
|
+
|
|
956
|
+
def reduce_confirmation(
|
|
957
|
+
self,
|
|
958
|
+
state: ModelRegistrationState,
|
|
959
|
+
confirmation: ModelRegistrationConfirmation,
|
|
960
|
+
) -> ModelReducerOutput[ModelRegistrationState]:
|
|
961
|
+
"""Process confirmation event from Effect layer.
|
|
962
|
+
|
|
963
|
+
Not yet implemented. See OMN-996 for tracking.
|
|
964
|
+
|
|
965
|
+
Args:
|
|
966
|
+
state: Current registration state (immutable).
|
|
967
|
+
confirmation: Confirmation event from Effect layer.
|
|
968
|
+
|
|
969
|
+
Returns:
|
|
970
|
+
ModelReducerOutput with new state and no intents.
|
|
971
|
+
|
|
972
|
+
Raises:
|
|
973
|
+
NotImplementedError: Always raised until implementation is complete.
|
|
974
|
+
"""
|
|
975
|
+
raise NotImplementedError(
|
|
976
|
+
"reduce_confirmation() is not yet implemented. "
|
|
977
|
+
"See ticket OMN-996: https://linear.app/omninode/issue/OMN-996"
|
|
978
|
+
)
|
|
979
|
+
|
|
980
|
+
# TODO(OMN-996): Implement reduce_confirmation() using ModelRegistrationConfirmation
|
|
981
|
+
# Ticket: https://linear.app/omninode/issue/OMN-996
|
|
982
|
+
# Status: Backlog - Phase 2 of dual registration event flow
|
|
983
|
+
#
|
|
984
|
+
# Scope: Process confirmation events from Effect layer (Consul/PostgreSQL)
|
|
985
|
+
# to complete state transitions: pending -> partial -> complete
|
|
986
|
+
#
|
|
987
|
+
def reduce_reset(
|
|
988
|
+
self,
|
|
989
|
+
state: ModelRegistrationState,
|
|
990
|
+
reset_event_id: UUID,
|
|
991
|
+
) -> ModelReducerOutput[ModelRegistrationState]:
|
|
992
|
+
"""Process a reset event to recover from failed or complete states.
|
|
993
|
+
|
|
994
|
+
This method allows the FSM to recover from terminal states (failed, complete)
|
|
995
|
+
and return to idle, enabling retry workflows after failures.
|
|
996
|
+
|
|
997
|
+
State Validation:
|
|
998
|
+
Reset is ONLY allowed from terminal states (failed, complete). Attempting
|
|
999
|
+
to reset from in-flight states (pending, partial) or idle will result in
|
|
1000
|
+
a failed state with failure_reason="invalid_reset_state".
|
|
1001
|
+
|
|
1002
|
+
This validation prevents accidental loss of in-flight registration state.
|
|
1003
|
+
If a reset is attempted while registration is in progress (pending/partial),
|
|
1004
|
+
the Consul or PostgreSQL confirmations could be lost, leaving the system
|
|
1005
|
+
in an inconsistent state.
|
|
1006
|
+
|
|
1007
|
+
Use Cases:
|
|
1008
|
+
- Retry after registration failure (consul_failed, postgres_failed)
|
|
1009
|
+
- Re-register a node after deregistration
|
|
1010
|
+
- Manual recovery triggered by operator
|
|
1011
|
+
|
|
1012
|
+
State Transitions:
|
|
1013
|
+
- failed -> idle: Clears failure, enables retry
|
|
1014
|
+
- complete -> idle: Enables re-registration
|
|
1015
|
+
- idle -> failed: Invalid reset (already idle)
|
|
1016
|
+
- pending -> failed: Invalid reset (would lose in-flight state)
|
|
1017
|
+
- partial -> failed: Invalid reset (would lose in-flight state)
|
|
1018
|
+
|
|
1019
|
+
Idempotency:
|
|
1020
|
+
Reset events are subject to the same idempotency checks as other events.
|
|
1021
|
+
If the reset_event_id matches last_processed_event_id, no transition occurs.
|
|
1022
|
+
|
|
1023
|
+
Args:
|
|
1024
|
+
state: Current registration state (immutable).
|
|
1025
|
+
reset_event_id: UUID of the reset event triggering this transition.
|
|
1026
|
+
|
|
1027
|
+
Returns:
|
|
1028
|
+
ModelReducerOutput with new state and no intents.
|
|
1029
|
+
- If reset allowed: new state is idle
|
|
1030
|
+
- If reset not allowed: new state is failed with
|
|
1031
|
+
failure_reason="invalid_reset_state"
|
|
1032
|
+
|
|
1033
|
+
Example:
|
|
1034
|
+
>>> from uuid import uuid4
|
|
1035
|
+
>>> from omnibase_infra.nodes.reducers import RegistrationReducer
|
|
1036
|
+
>>> from omnibase_infra.nodes.reducers.models import ModelRegistrationState
|
|
1037
|
+
>>>
|
|
1038
|
+
>>> reducer = RegistrationReducer()
|
|
1039
|
+
>>> # Reset from failed state succeeds
|
|
1040
|
+
>>> failed_state = ModelRegistrationState(
|
|
1041
|
+
... status="failed",
|
|
1042
|
+
... failure_reason="consul_failed"
|
|
1043
|
+
... )
|
|
1044
|
+
>>> output = reducer.reduce_reset(failed_state, uuid4())
|
|
1045
|
+
>>> output.result.status
|
|
1046
|
+
'idle'
|
|
1047
|
+
>>>
|
|
1048
|
+
>>> # Reset from pending state fails (would lose in-flight state)
|
|
1049
|
+
>>> pending_state = ModelRegistrationState(status="pending")
|
|
1050
|
+
>>> output = reducer.reduce_reset(pending_state, uuid4())
|
|
1051
|
+
>>> output.result.status
|
|
1052
|
+
'failed'
|
|
1053
|
+
>>> output.result.failure_reason
|
|
1054
|
+
'invalid_reset_state'
|
|
1055
|
+
"""
|
|
1056
|
+
start_time = time.perf_counter()
|
|
1057
|
+
|
|
1058
|
+
# Idempotency guard
|
|
1059
|
+
if state.is_duplicate_event(reset_event_id):
|
|
1060
|
+
return self._build_output(
|
|
1061
|
+
state=state,
|
|
1062
|
+
intents=(),
|
|
1063
|
+
processing_time_ms=0.0,
|
|
1064
|
+
items_processed=0,
|
|
1065
|
+
)
|
|
1066
|
+
|
|
1067
|
+
# Validate state allows reset - only terminal states (failed, complete)
|
|
1068
|
+
# can be reset. Resetting from pending or partial would lose in-flight
|
|
1069
|
+
# registration state, potentially causing inconsistency between Consul
|
|
1070
|
+
# and PostgreSQL.
|
|
1071
|
+
if not state.can_reset():
|
|
1072
|
+
# Not in a resettable state - transition to failed with clear error
|
|
1073
|
+
# This prevents accidental loss of in-flight registration state.
|
|
1074
|
+
new_state = state.with_failure("invalid_reset_state", reset_event_id)
|
|
1075
|
+
return self._build_output(
|
|
1076
|
+
state=new_state,
|
|
1077
|
+
intents=(),
|
|
1078
|
+
processing_time_ms=(time.perf_counter() - start_time) * 1000,
|
|
1079
|
+
items_processed=1, # We processed the event (it caused a state change)
|
|
1080
|
+
)
|
|
1081
|
+
|
|
1082
|
+
# Perform the reset transition
|
|
1083
|
+
new_state = state.with_reset(reset_event_id)
|
|
1084
|
+
|
|
1085
|
+
processing_time_ms = (time.perf_counter() - start_time) * 1000
|
|
1086
|
+
|
|
1087
|
+
return self._build_output(
|
|
1088
|
+
state=new_state,
|
|
1089
|
+
intents=(), # Reset emits no intents
|
|
1090
|
+
processing_time_ms=processing_time_ms,
|
|
1091
|
+
items_processed=1,
|
|
1092
|
+
)
|
|
1093
|
+
|
|
1094
|
+
def _build_output(
|
|
1095
|
+
self,
|
|
1096
|
+
state: ModelRegistrationState,
|
|
1097
|
+
intents: tuple[ModelIntent, ...],
|
|
1098
|
+
processing_time_ms: float,
|
|
1099
|
+
items_processed: int,
|
|
1100
|
+
) -> ModelReducerOutput[ModelRegistrationState]:
|
|
1101
|
+
"""Build standardized ModelReducerOutput.
|
|
1102
|
+
|
|
1103
|
+
Creates the output model with all required fields for the
|
|
1104
|
+
omnibase_core reducer output contract.
|
|
1105
|
+
|
|
1106
|
+
Args:
|
|
1107
|
+
state: New registration state to return.
|
|
1108
|
+
intents: Tuple of ModelIntent objects to emit.
|
|
1109
|
+
processing_time_ms: Time taken to process the event.
|
|
1110
|
+
items_processed: Number of events processed (0 or 1).
|
|
1111
|
+
|
|
1112
|
+
Returns:
|
|
1113
|
+
ModelReducerOutput containing the state and intents.
|
|
1114
|
+
"""
|
|
1115
|
+
return ModelReducerOutput(
|
|
1116
|
+
result=state,
|
|
1117
|
+
operation_id=uuid4(),
|
|
1118
|
+
reduction_type=EnumReductionType.MERGE,
|
|
1119
|
+
processing_time_ms=processing_time_ms,
|
|
1120
|
+
items_processed=items_processed,
|
|
1121
|
+
conflicts_resolved=0,
|
|
1122
|
+
streaming_mode=EnumStreamingMode.BATCH,
|
|
1123
|
+
batches_processed=1,
|
|
1124
|
+
intents=intents,
|
|
1125
|
+
)
|
|
1126
|
+
|
|
1127
|
+
|
|
1128
|
+
__all__ = [
|
|
1129
|
+
"PERF_THRESHOLD_IDEMPOTENCY_CHECK_MS",
|
|
1130
|
+
"PERF_THRESHOLD_INTENT_BUILD_MS",
|
|
1131
|
+
# Performance threshold constants (for tests and monitoring)
|
|
1132
|
+
"PERF_THRESHOLD_REDUCE_MS",
|
|
1133
|
+
# Validation types (for tests and custom validators)
|
|
1134
|
+
"ModelValidationResult",
|
|
1135
|
+
"RegistrationReducer",
|
|
1136
|
+
"ValidationErrorCode",
|
|
1137
|
+
]
|