omnibase_infra 0.2.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (833) hide show
  1. omnibase_infra/__init__.py +101 -0
  2. omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
  3. omnibase_infra/capabilities/__init__.py +15 -0
  4. omnibase_infra/capabilities/capability_inference_rules.py +211 -0
  5. omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
  6. omnibase_infra/capabilities/intent_type_extractor.py +160 -0
  7. omnibase_infra/cli/__init__.py +1 -0
  8. omnibase_infra/cli/commands.py +216 -0
  9. omnibase_infra/clients/__init__.py +0 -0
  10. omnibase_infra/configs/widget_mapping.yaml +176 -0
  11. omnibase_infra/constants_topic_patterns.py +26 -0
  12. omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +264 -0
  13. omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +141 -0
  14. omnibase_infra/decorators/__init__.py +29 -0
  15. omnibase_infra/decorators/allow_any.py +109 -0
  16. omnibase_infra/dlq/__init__.py +90 -0
  17. omnibase_infra/dlq/constants_dlq.py +57 -0
  18. omnibase_infra/dlq/models/__init__.py +26 -0
  19. omnibase_infra/dlq/models/enum_replay_status.py +37 -0
  20. omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
  21. omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
  22. omnibase_infra/dlq/service_dlq_tracking.py +611 -0
  23. omnibase_infra/enums/__init__.py +132 -0
  24. omnibase_infra/enums/enum_any_type_violation.py +104 -0
  25. omnibase_infra/enums/enum_backend_type.py +27 -0
  26. omnibase_infra/enums/enum_capture_outcome.py +42 -0
  27. omnibase_infra/enums/enum_capture_state.py +88 -0
  28. omnibase_infra/enums/enum_chain_violation_type.py +119 -0
  29. omnibase_infra/enums/enum_circuit_state.py +51 -0
  30. omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
  31. omnibase_infra/enums/enum_consumer_group_purpose.py +92 -0
  32. omnibase_infra/enums/enum_contract_type.py +84 -0
  33. omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
  34. omnibase_infra/enums/enum_dispatch_status.py +191 -0
  35. omnibase_infra/enums/enum_environment.py +46 -0
  36. omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
  37. omnibase_infra/enums/enum_handler_error_type.py +111 -0
  38. omnibase_infra/enums/enum_handler_loader_error.py +178 -0
  39. omnibase_infra/enums/enum_handler_source_mode.py +86 -0
  40. omnibase_infra/enums/enum_handler_source_type.py +87 -0
  41. omnibase_infra/enums/enum_handler_type.py +77 -0
  42. omnibase_infra/enums/enum_handler_type_category.py +61 -0
  43. omnibase_infra/enums/enum_infra_transport_type.py +73 -0
  44. omnibase_infra/enums/enum_introspection_reason.py +154 -0
  45. omnibase_infra/enums/enum_kafka_acks.py +99 -0
  46. omnibase_infra/enums/enum_message_category.py +213 -0
  47. omnibase_infra/enums/enum_node_archetype.py +74 -0
  48. omnibase_infra/enums/enum_node_output_type.py +185 -0
  49. omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
  50. omnibase_infra/enums/enum_policy_type.py +32 -0
  51. omnibase_infra/enums/enum_registration_state.py +261 -0
  52. omnibase_infra/enums/enum_registration_status.py +33 -0
  53. omnibase_infra/enums/enum_registry_response_status.py +28 -0
  54. omnibase_infra/enums/enum_response_status.py +26 -0
  55. omnibase_infra/enums/enum_retry_error_category.py +98 -0
  56. omnibase_infra/enums/enum_security_rule_id.py +103 -0
  57. omnibase_infra/enums/enum_selection_strategy.py +91 -0
  58. omnibase_infra/enums/enum_topic_standard.py +42 -0
  59. omnibase_infra/enums/enum_validation_severity.py +78 -0
  60. omnibase_infra/errors/__init__.py +160 -0
  61. omnibase_infra/errors/error_architecture_violation.py +152 -0
  62. omnibase_infra/errors/error_binding_resolution.py +128 -0
  63. omnibase_infra/errors/error_chain_propagation.py +188 -0
  64. omnibase_infra/errors/error_compute_registry.py +95 -0
  65. omnibase_infra/errors/error_consul.py +132 -0
  66. omnibase_infra/errors/error_container_wiring.py +243 -0
  67. omnibase_infra/errors/error_event_bus_registry.py +105 -0
  68. omnibase_infra/errors/error_infra.py +610 -0
  69. omnibase_infra/errors/error_message_type_registry.py +101 -0
  70. omnibase_infra/errors/error_policy_registry.py +115 -0
  71. omnibase_infra/errors/error_vault.py +123 -0
  72. omnibase_infra/event_bus/__init__.py +72 -0
  73. omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +84 -0
  74. omnibase_infra/event_bus/event_bus_inmemory.py +797 -0
  75. omnibase_infra/event_bus/event_bus_kafka.py +1716 -0
  76. omnibase_infra/event_bus/mixin_kafka_broadcast.py +180 -0
  77. omnibase_infra/event_bus/mixin_kafka_dlq.py +771 -0
  78. omnibase_infra/event_bus/models/__init__.py +29 -0
  79. omnibase_infra/event_bus/models/config/__init__.py +20 -0
  80. omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +693 -0
  81. omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
  82. omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
  83. omnibase_infra/event_bus/models/model_event_headers.py +115 -0
  84. omnibase_infra/event_bus/models/model_event_message.py +60 -0
  85. omnibase_infra/event_bus/testing/__init__.py +26 -0
  86. omnibase_infra/event_bus/testing/adapter_protocol_event_publisher_inmemory.py +418 -0
  87. omnibase_infra/event_bus/testing/model_publisher_metrics.py +64 -0
  88. omnibase_infra/event_bus/topic_constants.py +376 -0
  89. omnibase_infra/handlers/__init__.py +82 -0
  90. omnibase_infra/handlers/filesystem/__init__.py +48 -0
  91. omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
  92. omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
  93. omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
  94. omnibase_infra/handlers/handler_consul.py +795 -0
  95. omnibase_infra/handlers/handler_db.py +1046 -0
  96. omnibase_infra/handlers/handler_filesystem.py +1478 -0
  97. omnibase_infra/handlers/handler_graph.py +2015 -0
  98. omnibase_infra/handlers/handler_http.py +926 -0
  99. omnibase_infra/handlers/handler_intent.py +387 -0
  100. omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
  101. omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
  102. omnibase_infra/handlers/handler_mcp.py +1430 -0
  103. omnibase_infra/handlers/handler_qdrant.py +1076 -0
  104. omnibase_infra/handlers/handler_vault.py +428 -0
  105. omnibase_infra/handlers/mcp/__init__.py +19 -0
  106. omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
  107. omnibase_infra/handlers/mcp/protocols.py +178 -0
  108. omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
  109. omnibase_infra/handlers/mixins/__init__.py +47 -0
  110. omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
  111. omnibase_infra/handlers/mixins/mixin_consul_kv.py +338 -0
  112. omnibase_infra/handlers/mixins/mixin_consul_service.py +542 -0
  113. omnibase_infra/handlers/mixins/mixin_consul_topic_index.py +585 -0
  114. omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
  115. omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
  116. omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
  117. omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
  118. omnibase_infra/handlers/models/__init__.py +286 -0
  119. omnibase_infra/handlers/models/consul/__init__.py +81 -0
  120. omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
  121. omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
  122. omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
  123. omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
  124. omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
  125. omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
  126. omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
  127. omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
  128. omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
  129. omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
  130. omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
  131. omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
  132. omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
  133. omnibase_infra/handlers/models/graph/__init__.py +35 -0
  134. omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
  135. omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
  136. omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
  137. omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
  138. omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
  139. omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
  140. omnibase_infra/handlers/models/http/__init__.py +50 -0
  141. omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
  142. omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
  143. omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
  144. omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
  145. omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
  146. omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
  147. omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
  148. omnibase_infra/handlers/models/mcp/__init__.py +23 -0
  149. omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
  150. omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
  151. omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
  152. omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
  153. omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
  154. omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
  155. omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
  156. omnibase_infra/handlers/models/model_db_query_response.py +60 -0
  157. omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
  158. omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
  159. omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
  160. omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
  161. omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
  162. omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
  163. omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
  164. omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
  165. omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
  166. omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
  167. omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
  168. omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
  169. omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
  170. omnibase_infra/handlers/models/model_handler_response.py +103 -0
  171. omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
  172. omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
  173. omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
  174. omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
  175. omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
  176. omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
  177. omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
  178. omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
  179. omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
  180. omnibase_infra/handlers/models/model_operation_context.py +187 -0
  181. omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
  182. omnibase_infra/handlers/models/model_retry_state.py +162 -0
  183. omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
  184. omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
  185. omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
  186. omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
  187. omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
  188. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
  189. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
  190. omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
  191. omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
  192. omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
  193. omnibase_infra/handlers/models/vault/__init__.py +69 -0
  194. omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
  195. omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
  196. omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
  197. omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
  198. omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
  199. omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
  200. omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
  201. omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
  202. omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
  203. omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
  204. omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
  205. omnibase_infra/handlers/registration_storage/__init__.py +43 -0
  206. omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
  207. omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +922 -0
  208. omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
  209. omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
  210. omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
  211. omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
  212. omnibase_infra/handlers/service_discovery/__init__.py +43 -0
  213. omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +1051 -0
  214. omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
  215. omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
  216. omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
  217. omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
  218. omnibase_infra/handlers/service_discovery/models/model_service_info.py +109 -0
  219. omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
  220. omnibase_infra/idempotency/__init__.py +94 -0
  221. omnibase_infra/idempotency/models/__init__.py +43 -0
  222. omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
  223. omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
  224. omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
  225. omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
  226. omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
  227. omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
  228. omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
  229. omnibase_infra/idempotency/store_inmemory.py +265 -0
  230. omnibase_infra/idempotency/store_postgres.py +923 -0
  231. omnibase_infra/infrastructure/__init__.py +0 -0
  232. omnibase_infra/migrations/001_create_event_ledger.sql +166 -0
  233. omnibase_infra/migrations/001_drop_event_ledger.sql +18 -0
  234. omnibase_infra/mixins/__init__.py +71 -0
  235. omnibase_infra/mixins/mixin_async_circuit_breaker.py +656 -0
  236. omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
  237. omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
  238. omnibase_infra/mixins/mixin_node_introspection.py +2670 -0
  239. omnibase_infra/mixins/mixin_retry_execution.py +386 -0
  240. omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
  241. omnibase_infra/models/__init__.py +144 -0
  242. omnibase_infra/models/bindings/__init__.py +59 -0
  243. omnibase_infra/models/bindings/constants.py +144 -0
  244. omnibase_infra/models/bindings/model_binding_resolution_result.py +103 -0
  245. omnibase_infra/models/bindings/model_operation_binding.py +44 -0
  246. omnibase_infra/models/bindings/model_operation_bindings_subcontract.py +152 -0
  247. omnibase_infra/models/bindings/model_parsed_binding.py +52 -0
  248. omnibase_infra/models/corpus/__init__.py +17 -0
  249. omnibase_infra/models/corpus/model_capture_config.py +133 -0
  250. omnibase_infra/models/corpus/model_capture_result.py +86 -0
  251. omnibase_infra/models/discovery/__init__.py +42 -0
  252. omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
  253. omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
  254. omnibase_infra/models/discovery/model_introspection_config.py +330 -0
  255. omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
  256. omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
  257. omnibase_infra/models/dispatch/__init__.py +155 -0
  258. omnibase_infra/models/dispatch/model_debug_trace_snapshot.py +114 -0
  259. omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
  260. omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
  261. omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
  262. omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
  263. omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
  264. omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
  265. omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
  266. omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
  267. omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
  268. omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
  269. omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
  270. omnibase_infra/models/dispatch/model_materialized_dispatch.py +141 -0
  271. omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
  272. omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
  273. omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
  274. omnibase_infra/models/errors/__init__.py +45 -0
  275. omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
  276. omnibase_infra/models/errors/model_infra_error_context.py +99 -0
  277. omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
  278. omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
  279. omnibase_infra/models/handlers/__init__.py +80 -0
  280. omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
  281. omnibase_infra/models/handlers/model_contract_discovery_result.py +82 -0
  282. omnibase_infra/models/handlers/model_handler_descriptor.py +200 -0
  283. omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
  284. omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
  285. omnibase_infra/models/health/__init__.py +9 -0
  286. omnibase_infra/models/health/model_health_check_result.py +40 -0
  287. omnibase_infra/models/lifecycle/__init__.py +39 -0
  288. omnibase_infra/models/logging/__init__.py +51 -0
  289. omnibase_infra/models/logging/model_log_context.py +756 -0
  290. omnibase_infra/models/mcp/__init__.py +15 -0
  291. omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
  292. omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
  293. omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
  294. omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
  295. omnibase_infra/models/model_node_identity.py +126 -0
  296. omnibase_infra/models/model_retry_error_classification.py +78 -0
  297. omnibase_infra/models/projection/__init__.py +43 -0
  298. omnibase_infra/models/projection/model_capability_fields.py +112 -0
  299. omnibase_infra/models/projection/model_registration_projection.py +434 -0
  300. omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
  301. omnibase_infra/models/projection/model_sequence_info.py +182 -0
  302. omnibase_infra/models/projection/model_snapshot_topic_config.py +591 -0
  303. omnibase_infra/models/projectors/__init__.py +41 -0
  304. omnibase_infra/models/projectors/model_projector_column.py +289 -0
  305. omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
  306. omnibase_infra/models/projectors/model_projector_index.py +270 -0
  307. omnibase_infra/models/projectors/model_projector_schema.py +415 -0
  308. omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
  309. omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
  310. omnibase_infra/models/registration/__init__.py +68 -0
  311. omnibase_infra/models/registration/commands/__init__.py +15 -0
  312. omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
  313. omnibase_infra/models/registration/events/__init__.py +56 -0
  314. omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
  315. omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
  316. omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
  317. omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
  318. omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
  319. omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
  320. omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
  321. omnibase_infra/models/registration/model_event_bus_topic_entry.py +59 -0
  322. omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
  323. omnibase_infra/models/registration/model_node_capabilities.py +190 -0
  324. omnibase_infra/models/registration/model_node_event_bus_config.py +99 -0
  325. omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
  326. omnibase_infra/models/registration/model_node_introspection_event.py +195 -0
  327. omnibase_infra/models/registration/model_node_metadata.py +79 -0
  328. omnibase_infra/models/registration/model_node_registration.py +162 -0
  329. omnibase_infra/models/registration/model_node_registration_record.py +162 -0
  330. omnibase_infra/models/registry/__init__.py +29 -0
  331. omnibase_infra/models/registry/model_domain_constraint.py +202 -0
  332. omnibase_infra/models/registry/model_message_type_entry.py +271 -0
  333. omnibase_infra/models/resilience/__init__.py +9 -0
  334. omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
  335. omnibase_infra/models/routing/__init__.py +25 -0
  336. omnibase_infra/models/routing/model_routing_entry.py +52 -0
  337. omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
  338. omnibase_infra/models/runtime/__init__.py +49 -0
  339. omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
  340. omnibase_infra/models/runtime/model_discovery_error.py +81 -0
  341. omnibase_infra/models/runtime/model_discovery_result.py +162 -0
  342. omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
  343. omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
  344. omnibase_infra/models/runtime/model_handler_contract.py +296 -0
  345. omnibase_infra/models/runtime/model_loaded_handler.py +129 -0
  346. omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
  347. omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
  348. omnibase_infra/models/security/__init__.py +50 -0
  349. omnibase_infra/models/security/classification_levels.py +99 -0
  350. omnibase_infra/models/security/model_environment_policy.py +145 -0
  351. omnibase_infra/models/security/model_handler_security_policy.py +107 -0
  352. omnibase_infra/models/security/model_security_error.py +81 -0
  353. omnibase_infra/models/security/model_security_validation_result.py +328 -0
  354. omnibase_infra/models/security/model_security_warning.py +67 -0
  355. omnibase_infra/models/snapshot/__init__.py +27 -0
  356. omnibase_infra/models/snapshot/model_field_change.py +65 -0
  357. omnibase_infra/models/snapshot/model_snapshot.py +270 -0
  358. omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
  359. omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
  360. omnibase_infra/models/types/__init__.py +71 -0
  361. omnibase_infra/models/validation/__init__.py +89 -0
  362. omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
  363. omnibase_infra/models/validation/model_any_type_violation.py +141 -0
  364. omnibase_infra/models/validation/model_category_match_result.py +345 -0
  365. omnibase_infra/models/validation/model_chain_violation.py +166 -0
  366. omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
  367. omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
  368. omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
  369. omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
  370. omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
  371. omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
  372. omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
  373. omnibase_infra/models/validation/model_output_validation_params.py +74 -0
  374. omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
  375. omnibase_infra/models/validation/model_validation_error_params.py +84 -0
  376. omnibase_infra/models/validation/model_validation_outcome.py +287 -0
  377. omnibase_infra/nodes/__init__.py +57 -0
  378. omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
  379. omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
  380. omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +203 -0
  381. omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
  382. omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
  383. omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
  384. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
  385. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
  386. omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
  387. omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
  388. omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
  389. omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
  390. omnibase_infra/nodes/architecture_validator/node.py +262 -0
  391. omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
  392. omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
  393. omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
  394. omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
  395. omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +106 -0
  396. omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
  397. omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
  398. omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
  399. omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
  400. omnibase_infra/nodes/contract_registry_reducer/__init__.py +29 -0
  401. omnibase_infra/nodes/contract_registry_reducer/contract.yaml +255 -0
  402. omnibase_infra/nodes/contract_registry_reducer/models/__init__.py +38 -0
  403. omnibase_infra/nodes/contract_registry_reducer/models/model_contract_registry_state.py +266 -0
  404. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_cleanup_topic_references.py +55 -0
  405. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_deactivate_contract.py +58 -0
  406. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_mark_stale.py +49 -0
  407. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_heartbeat.py +71 -0
  408. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_topic.py +66 -0
  409. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_upsert_contract.py +92 -0
  410. omnibase_infra/nodes/contract_registry_reducer/node.py +121 -0
  411. omnibase_infra/nodes/contract_registry_reducer/reducer.py +784 -0
  412. omnibase_infra/nodes/contract_registry_reducer/registry/__init__.py +9 -0
  413. omnibase_infra/nodes/contract_registry_reducer/registry/registry_infra_contract_registry_reducer.py +101 -0
  414. omnibase_infra/nodes/effects/README.md +358 -0
  415. omnibase_infra/nodes/effects/__init__.py +26 -0
  416. omnibase_infra/nodes/effects/contract.yaml +167 -0
  417. omnibase_infra/nodes/effects/models/__init__.py +32 -0
  418. omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
  419. omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
  420. omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
  421. omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
  422. omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
  423. omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
  424. omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
  425. omnibase_infra/nodes/effects/registry_effect.py +525 -0
  426. omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
  427. omnibase_infra/nodes/handlers/consul/contract.yaml +85 -0
  428. omnibase_infra/nodes/handlers/db/contract.yaml +72 -0
  429. omnibase_infra/nodes/handlers/graph/contract.yaml +127 -0
  430. omnibase_infra/nodes/handlers/http/contract.yaml +74 -0
  431. omnibase_infra/nodes/handlers/intent/contract.yaml +66 -0
  432. omnibase_infra/nodes/handlers/mcp/contract.yaml +69 -0
  433. omnibase_infra/nodes/handlers/vault/contract.yaml +91 -0
  434. omnibase_infra/nodes/node_intent_storage_effect/__init__.py +50 -0
  435. omnibase_infra/nodes/node_intent_storage_effect/contract.yaml +194 -0
  436. omnibase_infra/nodes/node_intent_storage_effect/models/__init__.py +24 -0
  437. omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_input.py +141 -0
  438. omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_output.py +130 -0
  439. omnibase_infra/nodes/node_intent_storage_effect/node.py +94 -0
  440. omnibase_infra/nodes/node_intent_storage_effect/registry/__init__.py +35 -0
  441. omnibase_infra/nodes/node_intent_storage_effect/registry/registry_infra_intent_storage.py +294 -0
  442. omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +50 -0
  443. omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +104 -0
  444. omnibase_infra/nodes/node_ledger_projection_compute/node.py +284 -0
  445. omnibase_infra/nodes/node_ledger_projection_compute/registry/__init__.py +29 -0
  446. omnibase_infra/nodes/node_ledger_projection_compute/registry/registry_infra_ledger_projection.py +118 -0
  447. omnibase_infra/nodes/node_ledger_write_effect/__init__.py +82 -0
  448. omnibase_infra/nodes/node_ledger_write_effect/contract.yaml +200 -0
  449. omnibase_infra/nodes/node_ledger_write_effect/handlers/__init__.py +22 -0
  450. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_append.py +372 -0
  451. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_query.py +597 -0
  452. omnibase_infra/nodes/node_ledger_write_effect/models/__init__.py +31 -0
  453. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_append_result.py +54 -0
  454. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_entry.py +92 -0
  455. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query.py +53 -0
  456. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query_result.py +41 -0
  457. omnibase_infra/nodes/node_ledger_write_effect/node.py +89 -0
  458. omnibase_infra/nodes/node_ledger_write_effect/protocols/__init__.py +13 -0
  459. omnibase_infra/nodes/node_ledger_write_effect/protocols/protocol_ledger_persistence.py +127 -0
  460. omnibase_infra/nodes/node_ledger_write_effect/registry/__init__.py +9 -0
  461. omnibase_infra/nodes/node_ledger_write_effect/registry/registry_infra_ledger_write.py +121 -0
  462. omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
  463. omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
  464. omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +482 -0
  465. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
  466. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
  467. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
  468. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
  469. omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
  470. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
  471. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +694 -0
  472. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
  473. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
  474. omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
  475. omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
  476. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
  477. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
  478. omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
  479. omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
  480. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
  481. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
  482. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
  483. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
  484. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
  485. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
  486. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
  487. omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
  488. omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
  489. omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
  490. omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
  491. omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
  492. omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
  493. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +528 -0
  494. omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +393 -0
  495. omnibase_infra/nodes/node_registration_orchestrator/wiring.py +743 -0
  496. omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
  497. omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
  498. omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
  499. omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
  500. omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
  501. omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
  502. omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
  503. omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
  504. omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +220 -0
  505. omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
  506. omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
  507. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
  508. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
  509. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
  510. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
  511. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
  512. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
  513. omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
  514. omnibase_infra/nodes/node_registration_storage_effect/node.py +112 -0
  515. omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
  516. omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
  517. omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
  518. omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +215 -0
  519. omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
  520. omnibase_infra/nodes/node_registry_effect/contract.yaml +677 -0
  521. omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
  522. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
  523. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
  524. omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +417 -0
  525. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
  526. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
  527. omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
  528. omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
  529. omnibase_infra/nodes/node_registry_effect/node.py +165 -0
  530. omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
  531. omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
  532. omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
  533. omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
  534. omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
  535. omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
  536. omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
  537. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
  538. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
  539. omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
  540. omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
  541. omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
  542. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
  543. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
  544. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
  545. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
  546. omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
  547. omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
  548. omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
  549. omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
  550. omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +222 -0
  551. omnibase_infra/nodes/reducers/__init__.py +30 -0
  552. omnibase_infra/nodes/reducers/models/__init__.py +37 -0
  553. omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +87 -0
  554. omnibase_infra/nodes/reducers/models/model_payload_ledger_append.py +133 -0
  555. omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
  556. omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
  557. omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
  558. omnibase_infra/nodes/reducers/registration_reducer.py +1138 -0
  559. omnibase_infra/observability/__init__.py +143 -0
  560. omnibase_infra/observability/constants_metrics.py +91 -0
  561. omnibase_infra/observability/factory_observability_sink.py +525 -0
  562. omnibase_infra/observability/handlers/__init__.py +118 -0
  563. omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
  564. omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
  565. omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
  566. omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
  567. omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
  568. omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
  569. omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
  570. omnibase_infra/observability/hooks/__init__.py +74 -0
  571. omnibase_infra/observability/hooks/hook_observability.py +1223 -0
  572. omnibase_infra/observability/models/__init__.py +30 -0
  573. omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
  574. omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
  575. omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
  576. omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
  577. omnibase_infra/observability/sinks/__init__.py +69 -0
  578. omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
  579. omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
  580. omnibase_infra/plugins/__init__.py +27 -0
  581. omnibase_infra/plugins/examples/__init__.py +28 -0
  582. omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
  583. omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
  584. omnibase_infra/plugins/models/__init__.py +21 -0
  585. omnibase_infra/plugins/models/model_plugin_context.py +76 -0
  586. omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
  587. omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
  588. omnibase_infra/plugins/plugin_compute_base.py +449 -0
  589. omnibase_infra/projectors/__init__.py +30 -0
  590. omnibase_infra/projectors/contracts/__init__.py +63 -0
  591. omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
  592. omnibase_infra/projectors/projection_reader_registration.py +1559 -0
  593. omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
  594. omnibase_infra/protocols/__init__.py +104 -0
  595. omnibase_infra/protocols/protocol_capability_projection.py +253 -0
  596. omnibase_infra/protocols/protocol_capability_query.py +251 -0
  597. omnibase_infra/protocols/protocol_container_aware.py +200 -0
  598. omnibase_infra/protocols/protocol_dispatch_engine.py +152 -0
  599. omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
  600. omnibase_infra/protocols/protocol_event_projector.py +96 -0
  601. omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
  602. omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
  603. omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
  604. omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
  605. omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
  606. omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
  607. omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
  608. omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
  609. omnibase_infra/runtime/__init__.py +445 -0
  610. omnibase_infra/runtime/binding_config_resolver.py +2771 -0
  611. omnibase_infra/runtime/binding_resolver.py +753 -0
  612. omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
  613. omnibase_infra/runtime/constants_notification.py +75 -0
  614. omnibase_infra/runtime/constants_security.py +70 -0
  615. omnibase_infra/runtime/contract_handler_discovery.py +587 -0
  616. omnibase_infra/runtime/contract_loaders/__init__.py +51 -0
  617. omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
  618. omnibase_infra/runtime/contract_loaders/operation_bindings_loader.py +789 -0
  619. omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
  620. omnibase_infra/runtime/emit_daemon/__init__.py +97 -0
  621. omnibase_infra/runtime/emit_daemon/cli.py +844 -0
  622. omnibase_infra/runtime/emit_daemon/client.py +811 -0
  623. omnibase_infra/runtime/emit_daemon/config.py +535 -0
  624. omnibase_infra/runtime/emit_daemon/daemon.py +812 -0
  625. omnibase_infra/runtime/emit_daemon/event_registry.py +477 -0
  626. omnibase_infra/runtime/emit_daemon/model_daemon_request.py +139 -0
  627. omnibase_infra/runtime/emit_daemon/model_daemon_response.py +191 -0
  628. omnibase_infra/runtime/emit_daemon/queue.py +618 -0
  629. omnibase_infra/runtime/enums/__init__.py +18 -0
  630. omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
  631. omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
  632. omnibase_infra/runtime/envelope_validator.py +179 -0
  633. omnibase_infra/runtime/event_bus_subcontract_wiring.py +466 -0
  634. omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
  635. omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
  636. omnibase_infra/runtime/handler_contract_source.py +750 -0
  637. omnibase_infra/runtime/handler_identity.py +81 -0
  638. omnibase_infra/runtime/handler_plugin_loader.py +2046 -0
  639. omnibase_infra/runtime/handler_registry.py +329 -0
  640. omnibase_infra/runtime/handler_source_resolver.py +367 -0
  641. omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
  642. omnibase_infra/runtime/kafka_contract_source.py +984 -0
  643. omnibase_infra/runtime/kernel.py +40 -0
  644. omnibase_infra/runtime/mixin_policy_validation.py +522 -0
  645. omnibase_infra/runtime/mixin_semver_cache.py +402 -0
  646. omnibase_infra/runtime/mixins/__init__.py +24 -0
  647. omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
  648. omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +778 -0
  649. omnibase_infra/runtime/models/__init__.py +229 -0
  650. omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
  651. omnibase_infra/runtime/models/model_binding_config.py +168 -0
  652. omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
  653. omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
  654. omnibase_infra/runtime/models/model_cached_secret.py +138 -0
  655. omnibase_infra/runtime/models/model_compute_key.py +138 -0
  656. omnibase_infra/runtime/models/model_compute_registration.py +97 -0
  657. omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
  658. omnibase_infra/runtime/models/model_config_ref.py +331 -0
  659. omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
  660. omnibase_infra/runtime/models/model_contract_load_result.py +224 -0
  661. omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
  662. omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
  663. omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
  664. omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
  665. omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
  666. omnibase_infra/runtime/models/model_failed_component.py +55 -0
  667. omnibase_infra/runtime/models/model_health_check_response.py +168 -0
  668. omnibase_infra/runtime/models/model_health_check_result.py +229 -0
  669. omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
  670. omnibase_infra/runtime/models/model_logging_config.py +42 -0
  671. omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
  672. omnibase_infra/runtime/models/model_optional_string.py +94 -0
  673. omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
  674. omnibase_infra/runtime/models/model_policy_context.py +100 -0
  675. omnibase_infra/runtime/models/model_policy_key.py +138 -0
  676. omnibase_infra/runtime/models/model_policy_registration.py +139 -0
  677. omnibase_infra/runtime/models/model_policy_result.py +103 -0
  678. omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
  679. omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
  680. omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
  681. omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
  682. omnibase_infra/runtime/models/model_retry_policy.py +105 -0
  683. omnibase_infra/runtime/models/model_runtime_config.py +150 -0
  684. omnibase_infra/runtime/models/model_runtime_contract_config.py +268 -0
  685. omnibase_infra/runtime/models/model_runtime_scheduler_config.py +625 -0
  686. omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
  687. omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
  688. omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
  689. omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
  690. omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
  691. omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
  692. omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
  693. omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
  694. omnibase_infra/runtime/models/model_security_config.py +109 -0
  695. omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
  696. omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
  697. omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
  698. omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
  699. omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
  700. omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
  701. omnibase_infra/runtime/projector_schema_manager.py +565 -0
  702. omnibase_infra/runtime/projector_shell.py +1330 -0
  703. omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
  704. omnibase_infra/runtime/protocol_contract_source.py +92 -0
  705. omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
  706. omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
  707. omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
  708. omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
  709. omnibase_infra/runtime/protocol_policy.py +366 -0
  710. omnibase_infra/runtime/protocols/__init__.py +37 -0
  711. omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
  712. omnibase_infra/runtime/publisher_topic_scoped.py +294 -0
  713. omnibase_infra/runtime/registry/__init__.py +93 -0
  714. omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
  715. omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
  716. omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
  717. omnibase_infra/runtime/registry/registry_message_type.py +542 -0
  718. omnibase_infra/runtime/registry/registry_protocol_binding.py +445 -0
  719. omnibase_infra/runtime/registry_compute.py +1143 -0
  720. omnibase_infra/runtime/registry_contract_source.py +693 -0
  721. omnibase_infra/runtime/registry_dispatcher.py +678 -0
  722. omnibase_infra/runtime/registry_policy.py +1185 -0
  723. omnibase_infra/runtime/runtime_contract_config_loader.py +406 -0
  724. omnibase_infra/runtime/runtime_scheduler.py +1070 -0
  725. omnibase_infra/runtime/secret_resolver.py +2112 -0
  726. omnibase_infra/runtime/security_metadata_validator.py +776 -0
  727. omnibase_infra/runtime/service_kernel.py +1651 -0
  728. omnibase_infra/runtime/service_message_dispatch_engine.py +2350 -0
  729. omnibase_infra/runtime/service_runtime_host_process.py +3493 -0
  730. omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
  731. omnibase_infra/runtime/transition_notification_publisher.py +765 -0
  732. omnibase_infra/runtime/util_container_wiring.py +1124 -0
  733. omnibase_infra/runtime/util_validation.py +314 -0
  734. omnibase_infra/runtime/util_version.py +98 -0
  735. omnibase_infra/runtime/util_wiring.py +723 -0
  736. omnibase_infra/schemas/schema_registration_projection.sql +320 -0
  737. omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
  738. omnibase_infra/services/__init__.py +89 -0
  739. omnibase_infra/services/corpus_capture.py +684 -0
  740. omnibase_infra/services/mcp/__init__.py +31 -0
  741. omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
  742. omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
  743. omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
  744. omnibase_infra/services/mcp/service_mcp_tool_sync.py +565 -0
  745. omnibase_infra/services/registry_api/__init__.py +40 -0
  746. omnibase_infra/services/registry_api/main.py +261 -0
  747. omnibase_infra/services/registry_api/models/__init__.py +66 -0
  748. omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
  749. omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
  750. omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
  751. omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
  752. omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
  753. omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
  754. omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
  755. omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
  756. omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
  757. omnibase_infra/services/registry_api/models/model_warning.py +49 -0
  758. omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
  759. omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
  760. omnibase_infra/services/registry_api/routes.py +371 -0
  761. omnibase_infra/services/registry_api/service.py +837 -0
  762. omnibase_infra/services/service_capability_query.py +945 -0
  763. omnibase_infra/services/service_health.py +898 -0
  764. omnibase_infra/services/service_node_selector.py +530 -0
  765. omnibase_infra/services/service_timeout_emitter.py +699 -0
  766. omnibase_infra/services/service_timeout_scanner.py +394 -0
  767. omnibase_infra/services/session/__init__.py +56 -0
  768. omnibase_infra/services/session/config_consumer.py +137 -0
  769. omnibase_infra/services/session/config_store.py +139 -0
  770. omnibase_infra/services/session/consumer.py +1007 -0
  771. omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
  772. omnibase_infra/services/session/store.py +997 -0
  773. omnibase_infra/services/snapshot/__init__.py +31 -0
  774. omnibase_infra/services/snapshot/service_snapshot.py +647 -0
  775. omnibase_infra/services/snapshot/store_inmemory.py +637 -0
  776. omnibase_infra/services/snapshot/store_postgres.py +1279 -0
  777. omnibase_infra/shared/__init__.py +8 -0
  778. omnibase_infra/testing/__init__.py +10 -0
  779. omnibase_infra/testing/utils.py +23 -0
  780. omnibase_infra/topics/__init__.py +45 -0
  781. omnibase_infra/topics/platform_topic_suffixes.py +140 -0
  782. omnibase_infra/topics/util_topic_composition.py +95 -0
  783. omnibase_infra/types/__init__.py +48 -0
  784. omnibase_infra/types/type_cache_info.py +49 -0
  785. omnibase_infra/types/type_dsn.py +173 -0
  786. omnibase_infra/types/type_infra_aliases.py +60 -0
  787. omnibase_infra/types/typed_dict/__init__.py +29 -0
  788. omnibase_infra/types/typed_dict/typed_dict_envelope_build_params.py +115 -0
  789. omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
  790. omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
  791. omnibase_infra/types/typed_dict_capabilities.py +64 -0
  792. omnibase_infra/utils/__init__.py +117 -0
  793. omnibase_infra/utils/correlation.py +208 -0
  794. omnibase_infra/utils/util_atomic_file.py +261 -0
  795. omnibase_infra/utils/util_consumer_group.py +232 -0
  796. omnibase_infra/utils/util_datetime.py +372 -0
  797. omnibase_infra/utils/util_db_transaction.py +239 -0
  798. omnibase_infra/utils/util_dsn_validation.py +333 -0
  799. omnibase_infra/utils/util_env_parsing.py +264 -0
  800. omnibase_infra/utils/util_error_sanitization.py +457 -0
  801. omnibase_infra/utils/util_pydantic_validators.py +477 -0
  802. omnibase_infra/utils/util_retry_optimistic.py +281 -0
  803. omnibase_infra/utils/util_semver.py +233 -0
  804. omnibase_infra/validation/__init__.py +307 -0
  805. omnibase_infra/validation/contracts/security.validation.yaml +114 -0
  806. omnibase_infra/validation/enums/__init__.py +11 -0
  807. omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
  808. omnibase_infra/validation/infra_validators.py +1514 -0
  809. omnibase_infra/validation/linter_contract.py +907 -0
  810. omnibase_infra/validation/mixin_any_type_classification.py +120 -0
  811. omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
  812. omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
  813. omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
  814. omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
  815. omnibase_infra/validation/models/__init__.py +15 -0
  816. omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
  817. omnibase_infra/validation/models/model_contract_violation.py +41 -0
  818. omnibase_infra/validation/service_validation_aggregator.py +395 -0
  819. omnibase_infra/validation/validation_exemptions.yaml +2033 -0
  820. omnibase_infra/validation/validator_any_type.py +715 -0
  821. omnibase_infra/validation/validator_chain_propagation.py +839 -0
  822. omnibase_infra/validation/validator_execution_shape.py +465 -0
  823. omnibase_infra/validation/validator_localhandler.py +261 -0
  824. omnibase_infra/validation/validator_registration_security.py +410 -0
  825. omnibase_infra/validation/validator_routing_coverage.py +1020 -0
  826. omnibase_infra/validation/validator_runtime_shape.py +915 -0
  827. omnibase_infra/validation/validator_security.py +513 -0
  828. omnibase_infra/validation/validator_topic_category.py +1152 -0
  829. omnibase_infra-0.2.6.dist-info/METADATA +197 -0
  830. omnibase_infra-0.2.6.dist-info/RECORD +833 -0
  831. omnibase_infra-0.2.6.dist-info/WHEEL +4 -0
  832. omnibase_infra-0.2.6.dist-info/entry_points.txt +5 -0
  833. omnibase_infra-0.2.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1051 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Consul Service Discovery Handler.
4
+
5
+ This module provides a Consul-backed implementation of the service discovery
6
+ handler protocol, wrapping existing Consul functionality with circuit breaker
7
+ resilience.
8
+
9
+ Thread Pool Management:
10
+ - All synchronous consul operations run in a dedicated thread pool
11
+ - Configurable max workers (default: 10)
12
+ - Thread pool is lazily initialized on first use
13
+ - Thread pool gracefully shutdown on handler shutdown
14
+
15
+ Circuit Breaker:
16
+ - Uses MixinAsyncCircuitBreaker for consistent resilience
17
+ - Three states: CLOSED (normal), OPEN (blocking), HALF_OPEN (testing)
18
+ - Configurable via ModelCircuitBreakerConfig model
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import asyncio
24
+ import logging
25
+ import time
26
+ from concurrent.futures import ThreadPoolExecutor
27
+ from datetime import UTC, datetime
28
+ from typing import TYPE_CHECKING, cast
29
+ from uuid import NAMESPACE_DNS, UUID, uuid4, uuid5
30
+
31
+ import consul
32
+
33
+ from omnibase_core.container import ModelONEXContainer
34
+ from omnibase_infra.enums import EnumInfraTransportType
35
+ from omnibase_infra.errors import (
36
+ InfraConnectionError,
37
+ InfraTimeoutError,
38
+ ModelInfraErrorContext,
39
+ ModelTimeoutErrorContext,
40
+ )
41
+ from omnibase_infra.handlers.service_discovery.models import (
42
+ ModelDiscoveryResult,
43
+ ModelHandlerRegistrationResult,
44
+ ModelServiceInfo,
45
+ )
46
+ from omnibase_infra.mixins import MixinAsyncCircuitBreaker
47
+ from omnibase_infra.models.resilience import ModelCircuitBreakerConfig
48
+ from omnibase_infra.nodes.node_service_discovery_effect.models import (
49
+ ModelDiscoveryQuery,
50
+ ModelServiceDiscoveryHealthCheckDetails,
51
+ ModelServiceDiscoveryHealthCheckResult,
52
+ )
53
+ from omnibase_infra.nodes.node_service_discovery_effect.models.enum_health_status import (
54
+ EnumHealthStatus,
55
+ )
56
+ from omnibase_infra.nodes.node_service_discovery_effect.models.enum_service_discovery_operation import (
57
+ EnumServiceDiscoveryOperation,
58
+ )
59
+
60
+ if TYPE_CHECKING:
61
+ from omnibase_infra.nodes.effects.protocol_consul_client import ProtocolConsulClient
62
+
63
+ logger = logging.getLogger(__name__)
64
+
65
+ # Default configuration values
66
+ DEFAULT_MAX_WORKERS = 10
67
+ DEFAULT_TIMEOUT_SECONDS = 30.0
68
+
69
+ # Custom namespace for ONEX service IDs (deterministic but distinct from DNS namespace)
70
+ # This ensures UUID5 generation for non-UUID service IDs is specific to ONEX
71
+ # and won't collide with DNS-based UUID5 values from other systems.
72
+ NAMESPACE_ONEX_SERVICE = uuid5(NAMESPACE_DNS, "omninode.service.discovery")
73
+
74
+
75
+ class HandlerServiceDiscoveryConsul(MixinAsyncCircuitBreaker):
76
+ """Consul implementation of ProtocolServiceDiscoveryHandler.
77
+
78
+ Wraps existing Consul client functionality with circuit breaker resilience
79
+ and proper error handling.
80
+
81
+ Thread Safety:
82
+ This handler is coroutine-safe. All Consul operations are executed
83
+ in a dedicated thread pool, and circuit breaker state is protected
84
+ by asyncio.Lock.
85
+
86
+ Thread Pool Initialization:
87
+ The thread pool executor is lazily initialized on first use via
88
+ ``_ensure_executor()``. This avoids resource allocation until the
89
+ handler is actually used.
90
+
91
+ Attributes:
92
+ handler_type: Returns "consul" identifier.
93
+
94
+ Example:
95
+ >>> from unittest.mock import MagicMock
96
+ >>> container = MagicMock(spec=ModelONEXContainer)
97
+ >>> handler = HandlerServiceDiscoveryConsul(
98
+ ... container=container,
99
+ ... consul_client=consul_client,
100
+ ... circuit_breaker_config=ModelCircuitBreakerConfig(threshold=5),
101
+ ... )
102
+ >>> result = await handler.register_service(service_info)
103
+ """
104
+
105
+ def __init__(
106
+ self,
107
+ container: ModelONEXContainer,
108
+ consul_client: ProtocolConsulClient | None = None,
109
+ consul_host: str = "localhost",
110
+ consul_port: int = 8500,
111
+ consul_scheme: str = "http",
112
+ consul_token: str | None = None,
113
+ circuit_breaker_config: ModelCircuitBreakerConfig
114
+ | dict[str, object]
115
+ | None = None,
116
+ max_workers: int = DEFAULT_MAX_WORKERS,
117
+ timeout_seconds: float = DEFAULT_TIMEOUT_SECONDS,
118
+ ) -> None:
119
+ """Initialize HandlerServiceDiscoveryConsul.
120
+
121
+ Args:
122
+ container: ONEX container for dependency injection and service resolution.
123
+ consul_client: Optional existing Consul client (ProtocolConsulClient).
124
+ If not provided, a new python-consul client will be created.
125
+ consul_host: Consul server hostname (default: "localhost").
126
+ consul_port: Consul server port (default: 8500).
127
+ consul_scheme: HTTP scheme "http" or "https" (default: "http").
128
+ consul_token: Optional Consul ACL token.
129
+ circuit_breaker_config: Optional circuit breaker configuration.
130
+ Can be a ModelCircuitBreakerConfig instance or a dict with keys:
131
+ - threshold: Max failures before opening (default: 5)
132
+ - reset_timeout_seconds: Seconds before reset (default: 60.0)
133
+ - service_name: Service identifier (default: "consul.discovery")
134
+ If not provided, uses ModelCircuitBreakerConfig defaults.
135
+ max_workers: Thread pool max workers (default: 10).
136
+ timeout_seconds: Operation timeout in seconds (default: 30.0).
137
+ """
138
+ self._container = container
139
+ # Parse circuit breaker configuration using ModelCircuitBreakerConfig
140
+ if isinstance(circuit_breaker_config, ModelCircuitBreakerConfig):
141
+ cb_config = circuit_breaker_config
142
+ elif circuit_breaker_config is not None:
143
+ # Handle legacy dict format with key mapping
144
+ config_dict = dict(circuit_breaker_config)
145
+ # Map legacy 'reset_timeout' key to 'reset_timeout_seconds'
146
+ if (
147
+ "reset_timeout" in config_dict
148
+ and "reset_timeout_seconds" not in config_dict
149
+ ):
150
+ config_dict["reset_timeout_seconds"] = config_dict.pop("reset_timeout")
151
+ # Set defaults for service_name and transport_type if not provided
152
+ config_dict.setdefault("service_name", "consul.discovery")
153
+ config_dict.setdefault("transport_type", EnumInfraTransportType.CONSUL)
154
+ cb_config = ModelCircuitBreakerConfig(**config_dict)
155
+ else:
156
+ cb_config = ModelCircuitBreakerConfig(
157
+ service_name="consul.discovery",
158
+ transport_type=EnumInfraTransportType.CONSUL,
159
+ )
160
+
161
+ self._init_circuit_breaker(
162
+ threshold=cb_config.threshold,
163
+ reset_timeout=cb_config.reset_timeout_seconds,
164
+ service_name=cb_config.service_name,
165
+ transport_type=cb_config.transport_type,
166
+ )
167
+
168
+ # Store configuration
169
+ self._consul_host = consul_host
170
+ self._consul_port = consul_port
171
+ self._consul_scheme = consul_scheme
172
+ self._consul_token = consul_token
173
+ self._timeout_seconds = timeout_seconds
174
+
175
+ # Initialize Consul client
176
+ # Note: We use consul.Consul type since that's what we create internally.
177
+ # External clients are expected to duck-type as consul.Consul.
178
+ self._consul_client: consul.Consul | None
179
+ if consul_client is not None:
180
+ # Use provided client (duck-typed ProtocolConsulClient)
181
+ self._consul_client = consul_client
182
+ self._owns_client = False
183
+ else:
184
+ # Create python-consul client
185
+ self._consul_client = consul.Consul(
186
+ host=consul_host,
187
+ port=consul_port,
188
+ scheme=consul_scheme,
189
+ token=consul_token,
190
+ )
191
+ self._owns_client = True
192
+
193
+ # Lazy thread pool initialization
194
+ self._executor: ThreadPoolExecutor | None = None
195
+ self._executor_lock = asyncio.Lock()
196
+ self._max_workers = max_workers
197
+
198
+ logger.info(
199
+ "HandlerServiceDiscoveryConsul initialized",
200
+ extra={
201
+ "consul_host": consul_host,
202
+ "consul_port": consul_port,
203
+ "max_workers": max_workers,
204
+ },
205
+ )
206
+
207
+ @property
208
+ def handler_type(self) -> str:
209
+ """Return the handler type identifier.
210
+
211
+ Returns:
212
+ "consul" identifier string.
213
+ """
214
+ return "consul"
215
+
216
+ async def _ensure_executor(self) -> ThreadPoolExecutor:
217
+ """Ensure thread pool executor is initialized.
218
+
219
+ Uses double-checked locking for thread-safe lazy initialization.
220
+ The executor is created on first use rather than at handler
221
+ construction time to avoid allocating resources for handlers
222
+ that may never be used.
223
+
224
+ Returns:
225
+ The ThreadPoolExecutor instance.
226
+ """
227
+ if self._executor is not None:
228
+ return self._executor
229
+
230
+ async with self._executor_lock:
231
+ if self._executor is None:
232
+ self._executor = ThreadPoolExecutor(max_workers=self._max_workers)
233
+ logger.debug(
234
+ "Thread pool executor initialized",
235
+ extra={"max_workers": self._max_workers},
236
+ )
237
+ return self._executor
238
+
239
+ def _check_not_shutdown(
240
+ self,
241
+ operation: str,
242
+ correlation_id: UUID,
243
+ ) -> None:
244
+ """Check that handler has not been shut down.
245
+
246
+ Args:
247
+ operation: Name of the operation being attempted.
248
+ correlation_id: Correlation ID for tracing.
249
+
250
+ Raises:
251
+ InfraConnectionError: If handler has been shut down.
252
+ """
253
+ if self._consul_client is None:
254
+ context = ModelInfraErrorContext(
255
+ transport_type=EnumInfraTransportType.CONSUL,
256
+ operation=operation,
257
+ target_name="consul.discovery",
258
+ correlation_id=correlation_id,
259
+ )
260
+ raise InfraConnectionError(
261
+ "Handler has been shut down, cannot perform operation",
262
+ context=context,
263
+ )
264
+
265
+ async def register_service(
266
+ self,
267
+ service_info: ModelServiceInfo,
268
+ correlation_id: UUID | None = None,
269
+ ) -> ModelHandlerRegistrationResult:
270
+ """Register a service with Consul.
271
+
272
+ Args:
273
+ service_info: Service information to register.
274
+ correlation_id: Optional correlation ID for tracing.
275
+
276
+ Returns:
277
+ ModelHandlerRegistrationResult with registration outcome.
278
+
279
+ Raises:
280
+ InfraConnectionError: If connection to Consul fails.
281
+ InfraTimeoutError: If operation times out.
282
+ InfraUnavailableError: If circuit breaker is open.
283
+ """
284
+ correlation_id = correlation_id or uuid4()
285
+ start_time = time.monotonic()
286
+
287
+ # Guard against use-after-shutdown
288
+ self._check_not_shutdown("register_service", correlation_id)
289
+
290
+ # Check circuit breaker
291
+ async with self._circuit_breaker_lock:
292
+ await self._check_circuit_breaker(
293
+ operation="register_service",
294
+ correlation_id=correlation_id,
295
+ )
296
+
297
+ try:
298
+ # Build health check config if URL provided
299
+ check_config: dict[str, str] | None = None
300
+ if service_info.health_check_url:
301
+ check_config = {
302
+ "http": service_info.health_check_url,
303
+ "interval": "10s",
304
+ "timeout": "5s",
305
+ }
306
+
307
+ # Execute registration in thread pool
308
+ # Client is typed as consul.Consul (duck-typed for injected clients)
309
+ client = self._consul_client
310
+ executor = await self._ensure_executor()
311
+ loop = asyncio.get_running_loop()
312
+ # Convert UUID to string for Consul API compatibility
313
+ service_id_str = str(service_info.service_id)
314
+ await asyncio.wait_for(
315
+ loop.run_in_executor(
316
+ executor,
317
+ # NOTE: client is duck-typed ProtocolConsulClient; mypy cannot verify
318
+ # agent.service.register exists on Optional[Consul] union type.
319
+ lambda: client.agent.service.register( # type: ignore[union-attr] # NOTE: duck-typed client
320
+ name=service_info.service_name,
321
+ service_id=service_id_str,
322
+ address=service_info.address,
323
+ port=service_info.port,
324
+ tags=list(service_info.tags),
325
+ meta=service_info.metadata,
326
+ check=check_config,
327
+ ),
328
+ ),
329
+ timeout=self._timeout_seconds,
330
+ )
331
+
332
+ # Reset circuit breaker on success
333
+ async with self._circuit_breaker_lock:
334
+ await self._reset_circuit_breaker()
335
+
336
+ duration_ms = (time.monotonic() - start_time) * 1000
337
+
338
+ logger.info(
339
+ "Service registered with Consul",
340
+ extra={
341
+ "service_id": service_id_str,
342
+ "service_name": service_info.service_name,
343
+ "duration_ms": duration_ms,
344
+ "correlation_id": str(correlation_id),
345
+ },
346
+ )
347
+
348
+ return ModelHandlerRegistrationResult(
349
+ success=True,
350
+ service_id=service_info.service_id,
351
+ operation=EnumServiceDiscoveryOperation.REGISTER,
352
+ duration_ms=duration_ms,
353
+ backend_type=self.handler_type,
354
+ correlation_id=correlation_id,
355
+ )
356
+
357
+ except TimeoutError as e:
358
+ async with self._circuit_breaker_lock:
359
+ await self._record_circuit_failure(
360
+ operation="register_service",
361
+ correlation_id=correlation_id,
362
+ )
363
+ duration_ms = (time.monotonic() - start_time) * 1000
364
+ raise InfraTimeoutError(
365
+ f"Consul registration timed out after {self._timeout_seconds}s",
366
+ context=ModelTimeoutErrorContext(
367
+ transport_type=EnumInfraTransportType.CONSUL,
368
+ operation="register_service",
369
+ target_name="consul.discovery",
370
+ correlation_id=correlation_id,
371
+ timeout_seconds=self._timeout_seconds,
372
+ ),
373
+ ) from e
374
+
375
+ except consul.ConsulException as e:
376
+ async with self._circuit_breaker_lock:
377
+ await self._record_circuit_failure(
378
+ operation="register_service",
379
+ correlation_id=correlation_id,
380
+ )
381
+ duration_ms = (time.monotonic() - start_time) * 1000
382
+ context = ModelInfraErrorContext(
383
+ transport_type=EnumInfraTransportType.CONSUL,
384
+ operation="register_service",
385
+ target_name="consul.discovery",
386
+ correlation_id=correlation_id,
387
+ )
388
+ raise InfraConnectionError(
389
+ "Consul registration failed",
390
+ context=context,
391
+ ) from e
392
+
393
+ except Exception as e:
394
+ async with self._circuit_breaker_lock:
395
+ await self._record_circuit_failure(
396
+ operation="register_service",
397
+ correlation_id=correlation_id,
398
+ )
399
+ duration_ms = (time.monotonic() - start_time) * 1000
400
+ context = ModelInfraErrorContext(
401
+ transport_type=EnumInfraTransportType.CONSUL,
402
+ operation="register_service",
403
+ target_name="consul.discovery",
404
+ correlation_id=correlation_id,
405
+ )
406
+ raise InfraConnectionError(
407
+ f"Consul registration failed: {type(e).__name__}",
408
+ context=context,
409
+ ) from e
410
+
411
+ async def deregister_service(
412
+ self,
413
+ service_id: UUID,
414
+ correlation_id: UUID | None = None,
415
+ ) -> None:
416
+ """Deregister a service from Consul.
417
+
418
+ Args:
419
+ service_id: UUID of the service to deregister.
420
+ correlation_id: Optional correlation ID for tracing.
421
+
422
+ Raises:
423
+ InfraConnectionError: If connection to Consul fails.
424
+ InfraTimeoutError: If operation times out.
425
+ InfraUnavailableError: If circuit breaker is open.
426
+ """
427
+ correlation_id = correlation_id or uuid4()
428
+ # Convert UUID to string for Consul API
429
+ service_id_str = str(service_id)
430
+
431
+ # Guard against use-after-shutdown
432
+ self._check_not_shutdown("deregister_service", correlation_id)
433
+
434
+ # Check circuit breaker
435
+ async with self._circuit_breaker_lock:
436
+ await self._check_circuit_breaker(
437
+ operation="deregister_service",
438
+ correlation_id=correlation_id,
439
+ )
440
+
441
+ try:
442
+ # Client is typed as consul.Consul (duck-typed for injected clients)
443
+ client = self._consul_client
444
+ executor = await self._ensure_executor()
445
+ loop = asyncio.get_running_loop()
446
+ await asyncio.wait_for(
447
+ loop.run_in_executor(
448
+ executor,
449
+ # NOTE: client is duck-typed ProtocolConsulClient; mypy cannot verify
450
+ # agent.service.deregister exists on Optional[Consul] union type.
451
+ lambda: client.agent.service.deregister(service_id_str), # type: ignore[union-attr] # NOTE: duck-typed client
452
+ ),
453
+ timeout=self._timeout_seconds,
454
+ )
455
+
456
+ # Reset circuit breaker on success
457
+ async with self._circuit_breaker_lock:
458
+ await self._reset_circuit_breaker()
459
+
460
+ logger.info(
461
+ "Service deregistered from Consul",
462
+ extra={
463
+ "service_id": service_id_str,
464
+ "correlation_id": str(correlation_id),
465
+ },
466
+ )
467
+
468
+ except TimeoutError as e:
469
+ async with self._circuit_breaker_lock:
470
+ await self._record_circuit_failure(
471
+ operation="deregister_service",
472
+ correlation_id=correlation_id,
473
+ )
474
+ raise InfraTimeoutError(
475
+ f"Consul deregistration timed out after {self._timeout_seconds}s",
476
+ context=ModelTimeoutErrorContext(
477
+ transport_type=EnumInfraTransportType.CONSUL,
478
+ operation="deregister_service",
479
+ target_name="consul.discovery",
480
+ correlation_id=correlation_id,
481
+ timeout_seconds=self._timeout_seconds,
482
+ ),
483
+ ) from e
484
+
485
+ except consul.ConsulException as e:
486
+ async with self._circuit_breaker_lock:
487
+ await self._record_circuit_failure(
488
+ operation="deregister_service",
489
+ correlation_id=correlation_id,
490
+ )
491
+ context = ModelInfraErrorContext(
492
+ transport_type=EnumInfraTransportType.CONSUL,
493
+ operation="deregister_service",
494
+ target_name="consul.discovery",
495
+ correlation_id=correlation_id,
496
+ )
497
+ raise InfraConnectionError(
498
+ "Consul deregistration failed",
499
+ context=context,
500
+ ) from e
501
+
502
+ async def discover_services(
503
+ self,
504
+ query: ModelDiscoveryQuery,
505
+ correlation_id: UUID | None = None,
506
+ ) -> ModelDiscoveryResult:
507
+ """Discover services matching the query criteria.
508
+
509
+ Args:
510
+ query: Query parameters including service_name, tags,
511
+ and health_filter for filtering services.
512
+ correlation_id: Optional correlation ID for tracing.
513
+ If not provided, uses query.correlation_id, then generates new.
514
+
515
+ Returns:
516
+ ModelDiscoveryResult with list of matching services.
517
+
518
+ Raises:
519
+ InfraConnectionError: If connection to Consul fails.
520
+ InfraTimeoutError: If operation times out.
521
+ InfraUnavailableError: If circuit breaker is open.
522
+
523
+ Note:
524
+ Service IDs from Consul are converted to UUIDs. If the Consul service
525
+ ID is not a valid UUID string, a deterministic UUID5 is generated
526
+ using the ONEX service namespace. This ensures consistent IDs across
527
+ discovery calls while maintaining UUID format compatibility.
528
+ """
529
+ # Standardized correlation ID handling: explicit > query > generate
530
+ correlation_id = correlation_id or query.correlation_id or uuid4()
531
+ service_name = query.service_name or ""
532
+ tags = query.tags
533
+ start_time = time.monotonic()
534
+
535
+ # Guard against use-after-shutdown
536
+ self._check_not_shutdown("discover_services", correlation_id)
537
+
538
+ # Check circuit breaker
539
+ async with self._circuit_breaker_lock:
540
+ await self._check_circuit_breaker(
541
+ operation="discover_services",
542
+ correlation_id=correlation_id,
543
+ )
544
+
545
+ try:
546
+ # Query Consul catalog
547
+ # Client is typed as consul.Consul (duck-typed for injected clients)
548
+ client = self._consul_client
549
+ executor = await self._ensure_executor()
550
+ loop = asyncio.get_running_loop()
551
+
552
+ def _query_services() -> tuple[int, list[dict[str, object]]]:
553
+ # Use health endpoint for service discovery (includes health status)
554
+ tag = tags[0] if tags else None
555
+ # NOTE: client is duck-typed ProtocolConsulClient; mypy cannot verify
556
+ # health.service exists on Optional[Consul] union type.
557
+ result: tuple[int, list[dict[str, object]]] = client.health.service( # type: ignore[union-attr] # NOTE: duck-typed client
558
+ service_name,
559
+ tag=tag,
560
+ passing=True, # Only healthy services
561
+ )
562
+ return result
563
+
564
+ _, services = await asyncio.wait_for(
565
+ loop.run_in_executor(executor, _query_services),
566
+ timeout=self._timeout_seconds,
567
+ )
568
+
569
+ # Reset circuit breaker on success
570
+ async with self._circuit_breaker_lock:
571
+ await self._reset_circuit_breaker()
572
+
573
+ # Convert to ModelServiceInfo list
574
+ service_infos: list[ModelServiceInfo] = []
575
+ for svc in services:
576
+ # Cast nested dicts for type safety
577
+ svc_data = cast("dict[str, object]", svc.get("Service", {}))
578
+ node_data = cast("dict[str, object]", svc.get("Node", {}))
579
+ svc_id_str = str(svc_data.get("ID", ""))
580
+ svc_name = svc_data.get("Service", "")
581
+ address = svc_data.get("Address", "") or node_data.get("Address", "")
582
+ port_raw = svc_data.get("Port", 0)
583
+ port = int(port_raw) if isinstance(port_raw, int | float | str) else 0
584
+ svc_tags = cast("list[str]", svc_data.get("Tags", []))
585
+ svc_meta = cast("dict[str, str]", svc_data.get("Meta", {}))
586
+
587
+ if svc_id_str and svc_name and address and port:
588
+ # Convert Consul service ID string to UUID
589
+ # Try to parse as UUID, otherwise generate deterministic UUID from string
590
+ try:
591
+ svc_id = UUID(svc_id_str)
592
+ except ValueError:
593
+ # Non-UUID service ID from Consul - generate deterministic UUID5
594
+ # using ONEX-specific namespace to avoid collisions
595
+ logger.warning(
596
+ "Non-UUID service ID from Consul, using deterministic UUID5 conversion",
597
+ extra={
598
+ "original_id": svc_id_str,
599
+ "correlation_id": str(correlation_id),
600
+ },
601
+ )
602
+ svc_id = uuid5(NAMESPACE_ONEX_SERVICE, svc_id_str)
603
+
604
+ service_infos.append(
605
+ ModelServiceInfo(
606
+ service_id=svc_id,
607
+ service_name=str(svc_name),
608
+ address=str(address),
609
+ port=port,
610
+ tags=tuple(svc_tags or []),
611
+ health_status=EnumHealthStatus.HEALTHY,
612
+ metadata=svc_meta or {},
613
+ registered_at=datetime.now(UTC),
614
+ correlation_id=correlation_id,
615
+ )
616
+ )
617
+
618
+ duration_ms = (time.monotonic() - start_time) * 1000
619
+
620
+ logger.info(
621
+ "Service discovery completed",
622
+ extra={
623
+ "service_name": service_name,
624
+ "found_count": len(service_infos),
625
+ "duration_ms": duration_ms,
626
+ "correlation_id": str(correlation_id),
627
+ },
628
+ )
629
+
630
+ return ModelDiscoveryResult(
631
+ success=True,
632
+ services=tuple(service_infos),
633
+ duration_ms=duration_ms,
634
+ backend_type=self.handler_type,
635
+ correlation_id=correlation_id,
636
+ )
637
+
638
+ except TimeoutError as e:
639
+ async with self._circuit_breaker_lock:
640
+ await self._record_circuit_failure(
641
+ operation="discover_services",
642
+ correlation_id=correlation_id,
643
+ )
644
+ duration_ms = (time.monotonic() - start_time) * 1000
645
+ raise InfraTimeoutError(
646
+ f"Consul discovery timed out after {self._timeout_seconds}s",
647
+ context=ModelTimeoutErrorContext(
648
+ transport_type=EnumInfraTransportType.CONSUL,
649
+ operation="discover_services",
650
+ target_name="consul.discovery",
651
+ correlation_id=correlation_id,
652
+ timeout_seconds=self._timeout_seconds,
653
+ ),
654
+ ) from e
655
+
656
+ except consul.ConsulException as e:
657
+ async with self._circuit_breaker_lock:
658
+ await self._record_circuit_failure(
659
+ operation="discover_services",
660
+ correlation_id=correlation_id,
661
+ )
662
+ duration_ms = (time.monotonic() - start_time) * 1000
663
+ context = ModelInfraErrorContext(
664
+ transport_type=EnumInfraTransportType.CONSUL,
665
+ operation="discover_services",
666
+ target_name="consul.discovery",
667
+ correlation_id=correlation_id,
668
+ )
669
+ raise InfraConnectionError(
670
+ "Consul discovery failed",
671
+ context=context,
672
+ ) from e
673
+
674
+ async def list_all_services(
675
+ self,
676
+ tag_filter: str | None = None,
677
+ correlation_id: UUID | None = None,
678
+ ) -> dict[str, list[str]]:
679
+ """List all registered service names with their tags.
680
+
681
+ Uses Consul catalog API (/v1/catalog/services) to retrieve all
682
+ service names registered in the Consul catalog, along with their tags.
683
+
684
+ Args:
685
+ tag_filter: Optional tag to filter services by. If provided,
686
+ only services with this tag will be returned.
687
+ correlation_id: Optional correlation ID for tracing.
688
+
689
+ Returns:
690
+ Dictionary mapping service names to their list of tags.
691
+ Example: {"my-service": ["web", "api"], "other-service": ["db"]}
692
+
693
+ Raises:
694
+ InfraConnectionError: If connection to Consul fails.
695
+ InfraTimeoutError: If operation times out.
696
+ InfraUnavailableError: If circuit breaker is open.
697
+ """
698
+ correlation_id = correlation_id or uuid4()
699
+ start_time = time.monotonic()
700
+
701
+ # Guard against use-after-shutdown
702
+ self._check_not_shutdown("list_all_services", correlation_id)
703
+
704
+ # Check circuit breaker
705
+ async with self._circuit_breaker_lock:
706
+ await self._check_circuit_breaker(
707
+ operation="list_all_services",
708
+ correlation_id=correlation_id,
709
+ )
710
+
711
+ try:
712
+ # Query Consul catalog for all services
713
+ client = self._consul_client
714
+ executor = await self._ensure_executor()
715
+ loop = asyncio.get_running_loop()
716
+
717
+ def _query_catalog() -> tuple[int, dict[str, list[str]]]:
718
+ # NOTE: client is duck-typed ProtocolConsulClient; mypy cannot verify
719
+ # catalog.services exists on Optional[Consul] union type.
720
+ result: tuple[int, dict[str, list[str]]] = client.catalog.services() # type: ignore[union-attr] # NOTE: duck-typed client
721
+ return result
722
+
723
+ _, services = await asyncio.wait_for(
724
+ loop.run_in_executor(executor, _query_catalog),
725
+ timeout=self._timeout_seconds,
726
+ )
727
+
728
+ # Reset circuit breaker on success
729
+ async with self._circuit_breaker_lock:
730
+ await self._reset_circuit_breaker()
731
+
732
+ # Apply tag filter if provided
733
+ if tag_filter is not None:
734
+ services = {
735
+ name: tags for name, tags in services.items() if tag_filter in tags
736
+ }
737
+
738
+ duration_ms = (time.monotonic() - start_time) * 1000
739
+
740
+ logger.info(
741
+ "Listed all services from Consul catalog",
742
+ extra={
743
+ "service_count": len(services),
744
+ "tag_filter": tag_filter,
745
+ "duration_ms": duration_ms,
746
+ "correlation_id": str(correlation_id),
747
+ },
748
+ )
749
+
750
+ return services
751
+
752
+ except TimeoutError as e:
753
+ async with self._circuit_breaker_lock:
754
+ await self._record_circuit_failure(
755
+ operation="list_all_services",
756
+ correlation_id=correlation_id,
757
+ )
758
+ raise InfraTimeoutError(
759
+ f"Consul catalog query timed out after {self._timeout_seconds}s",
760
+ context=ModelTimeoutErrorContext(
761
+ transport_type=EnumInfraTransportType.CONSUL,
762
+ operation="list_all_services",
763
+ target_name="consul.discovery",
764
+ correlation_id=correlation_id,
765
+ timeout_seconds=self._timeout_seconds,
766
+ ),
767
+ ) from e
768
+
769
+ except consul.ConsulException as e:
770
+ async with self._circuit_breaker_lock:
771
+ await self._record_circuit_failure(
772
+ operation="list_all_services",
773
+ correlation_id=correlation_id,
774
+ )
775
+ context = ModelInfraErrorContext(
776
+ transport_type=EnumInfraTransportType.CONSUL,
777
+ operation="list_all_services",
778
+ target_name="consul.discovery",
779
+ correlation_id=correlation_id,
780
+ )
781
+ raise InfraConnectionError(
782
+ "Consul catalog query failed",
783
+ context=context,
784
+ ) from e
785
+
786
+ async def get_all_service_instances(
787
+ self,
788
+ service_name: str,
789
+ include_unhealthy: bool = True,
790
+ correlation_id: UUID | None = None,
791
+ ) -> list[ModelServiceInfo]:
792
+ """Get all instances of a service, optionally including unhealthy ones.
793
+
794
+ Uses Consul health API (/v1/health/service/<name>) to retrieve all
795
+ instances of a service with their health status information.
796
+
797
+ Args:
798
+ service_name: Name of the service to query.
799
+ include_unhealthy: If True, return all instances regardless of health.
800
+ If False, only return healthy (passing) instances.
801
+ correlation_id: Optional correlation ID for tracing.
802
+
803
+ Returns:
804
+ List of ModelServiceInfo objects representing service instances,
805
+ including health status, health output, and last check timestamp.
806
+
807
+ Raises:
808
+ InfraConnectionError: If connection to Consul fails.
809
+ InfraTimeoutError: If operation times out.
810
+ InfraUnavailableError: If circuit breaker is open.
811
+ """
812
+ correlation_id = correlation_id or uuid4()
813
+ start_time = time.monotonic()
814
+
815
+ # Guard against use-after-shutdown
816
+ self._check_not_shutdown("get_all_service_instances", correlation_id)
817
+
818
+ # Check circuit breaker
819
+ async with self._circuit_breaker_lock:
820
+ await self._check_circuit_breaker(
821
+ operation="get_all_service_instances",
822
+ correlation_id=correlation_id,
823
+ )
824
+
825
+ try:
826
+ # Query Consul health API for service instances
827
+ client = self._consul_client
828
+ executor = await self._ensure_executor()
829
+ loop = asyncio.get_running_loop()
830
+
831
+ def _query_health() -> tuple[int, list[dict[str, object]]]:
832
+ # NOTE: client is duck-typed ProtocolConsulClient; mypy cannot verify
833
+ # health.service exists on Optional[Consul] union type.
834
+ # When passing=False, we get ALL instances
835
+ # When passing=True, we only get healthy instances
836
+ result: tuple[int, list[dict[str, object]]] = client.health.service( # type: ignore[union-attr] # NOTE: duck-typed client
837
+ service_name,
838
+ passing=not include_unhealthy,
839
+ )
840
+ return result
841
+
842
+ _, services = await asyncio.wait_for(
843
+ loop.run_in_executor(executor, _query_health),
844
+ timeout=self._timeout_seconds,
845
+ )
846
+
847
+ # Reset circuit breaker on success
848
+ async with self._circuit_breaker_lock:
849
+ await self._reset_circuit_breaker()
850
+
851
+ # Convert to ModelServiceInfo list with full health details
852
+ service_infos: list[ModelServiceInfo] = []
853
+ for svc in services:
854
+ # Cast nested dicts for type safety
855
+ svc_data = cast("dict[str, object]", svc.get("Service", {}))
856
+ node_data = cast("dict[str, object]", svc.get("Node", {}))
857
+ checks = cast("list[dict[str, object]]", svc.get("Checks", []))
858
+
859
+ svc_id_str = str(svc_data.get("ID", ""))
860
+ svc_name = svc_data.get("Service", "")
861
+ address = svc_data.get("Address", "") or node_data.get("Address", "")
862
+ port_raw = svc_data.get("Port", 0)
863
+ port = int(port_raw) if isinstance(port_raw, int | float | str) else 0
864
+ svc_tags = cast("list[str]", svc_data.get("Tags", []))
865
+ svc_meta = cast("dict[str, str]", svc_data.get("Meta", {}))
866
+
867
+ if svc_id_str and svc_name and address and port:
868
+ # Convert Consul service ID string to UUID
869
+ try:
870
+ svc_id = UUID(svc_id_str)
871
+ except ValueError:
872
+ # Non-UUID service ID from Consul - generate deterministic UUID5
873
+ logger.warning(
874
+ "Non-UUID service ID from Consul, using deterministic UUID5 conversion",
875
+ extra={
876
+ "original_id": svc_id_str,
877
+ "correlation_id": str(correlation_id),
878
+ },
879
+ )
880
+ svc_id = uuid5(NAMESPACE_ONEX_SERVICE, svc_id_str)
881
+
882
+ # Determine health status from checks
883
+ health_status = EnumHealthStatus.UNKNOWN
884
+ health_output: str | None = None
885
+ last_check_at: datetime | None = None
886
+
887
+ # Find the service-specific health check
888
+ for check in checks:
889
+ check_service_id = check.get("ServiceID", "")
890
+ if check_service_id == svc_id_str or not check_service_id:
891
+ status_str = str(check.get("Status", "")).lower()
892
+ if status_str == "passing":
893
+ health_status = EnumHealthStatus.HEALTHY
894
+ elif status_str in ("critical", "warning"):
895
+ health_status = EnumHealthStatus.UNHEALTHY
896
+ else:
897
+ health_status = EnumHealthStatus.UNKNOWN
898
+
899
+ health_output = str(check.get("Output", "")) or None
900
+
901
+ # Parse CreateIndex as a proxy for last check time
902
+ # (Consul doesn't directly expose last check time in this API)
903
+ # In production, you might use the check's CreateIndex or ModifyIndex
904
+ break
905
+
906
+ service_infos.append(
907
+ ModelServiceInfo(
908
+ service_id=svc_id,
909
+ service_name=str(svc_name),
910
+ address=str(address),
911
+ port=port,
912
+ tags=tuple(svc_tags or []),
913
+ health_status=health_status,
914
+ health_output=health_output,
915
+ last_check_at=last_check_at,
916
+ metadata=svc_meta or {},
917
+ registered_at=datetime.now(UTC),
918
+ correlation_id=correlation_id,
919
+ )
920
+ )
921
+
922
+ duration_ms = (time.monotonic() - start_time) * 1000
923
+
924
+ logger.info(
925
+ "Retrieved service instances from Consul",
926
+ extra={
927
+ "service_name": service_name,
928
+ "instance_count": len(service_infos),
929
+ "include_unhealthy": include_unhealthy,
930
+ "duration_ms": duration_ms,
931
+ "correlation_id": str(correlation_id),
932
+ },
933
+ )
934
+
935
+ return service_infos
936
+
937
+ except TimeoutError as e:
938
+ async with self._circuit_breaker_lock:
939
+ await self._record_circuit_failure(
940
+ operation="get_all_service_instances",
941
+ correlation_id=correlation_id,
942
+ )
943
+ raise InfraTimeoutError(
944
+ f"Consul health query timed out after {self._timeout_seconds}s",
945
+ context=ModelTimeoutErrorContext(
946
+ transport_type=EnumInfraTransportType.CONSUL,
947
+ operation="get_all_service_instances",
948
+ target_name="consul.discovery",
949
+ correlation_id=correlation_id,
950
+ timeout_seconds=self._timeout_seconds,
951
+ ),
952
+ ) from e
953
+
954
+ except consul.ConsulException as e:
955
+ async with self._circuit_breaker_lock:
956
+ await self._record_circuit_failure(
957
+ operation="get_all_service_instances",
958
+ correlation_id=correlation_id,
959
+ )
960
+ context = ModelInfraErrorContext(
961
+ transport_type=EnumInfraTransportType.CONSUL,
962
+ operation="get_all_service_instances",
963
+ target_name="consul.discovery",
964
+ correlation_id=correlation_id,
965
+ )
966
+ raise InfraConnectionError(
967
+ "Consul health query failed",
968
+ context=context,
969
+ ) from e
970
+
971
+ async def health_check(
972
+ self,
973
+ correlation_id: UUID | None = None,
974
+ ) -> ModelServiceDiscoveryHealthCheckResult:
975
+ """Perform a health check on the Consul connection.
976
+
977
+ Args:
978
+ correlation_id: Optional correlation ID for tracing.
979
+
980
+ Returns:
981
+ ModelServiceDiscoveryHealthCheckResult with health status information.
982
+ """
983
+ correlation_id = correlation_id or uuid4()
984
+ start_time = time.monotonic()
985
+
986
+ # Guard against use-after-shutdown
987
+ self._check_not_shutdown("health_check", correlation_id)
988
+
989
+ try:
990
+ # Client is typed as consul.Consul (duck-typed for injected clients)
991
+ client = self._consul_client
992
+ executor = await self._ensure_executor()
993
+ loop = asyncio.get_running_loop()
994
+ leader = await asyncio.wait_for(
995
+ loop.run_in_executor(
996
+ executor,
997
+ # NOTE: client is duck-typed ProtocolConsulClient; mypy cannot verify
998
+ # status.leader exists on Optional[Consul] union type.
999
+ lambda: client.status.leader(), # type: ignore[union-attr] # NOTE: duck-typed client
1000
+ ),
1001
+ timeout=5.0, # Short timeout for health check
1002
+ )
1003
+
1004
+ duration_ms = (time.monotonic() - start_time) * 1000
1005
+
1006
+ return ModelServiceDiscoveryHealthCheckResult(
1007
+ healthy=True,
1008
+ backend_type=self.handler_type,
1009
+ latency_ms=duration_ms,
1010
+ reason="ok",
1011
+ details=ModelServiceDiscoveryHealthCheckDetails(
1012
+ agent_address=f"{self._consul_host}:{self._consul_port}",
1013
+ leader=str(leader) if leader else None,
1014
+ ),
1015
+ correlation_id=correlation_id,
1016
+ )
1017
+
1018
+ except Exception as e:
1019
+ duration_ms = (time.monotonic() - start_time) * 1000
1020
+ return ModelServiceDiscoveryHealthCheckResult(
1021
+ healthy=False,
1022
+ backend_type=self.handler_type,
1023
+ latency_ms=duration_ms,
1024
+ reason=f"Health check failed: {type(e).__name__}",
1025
+ error_type=type(e).__name__,
1026
+ details=ModelServiceDiscoveryHealthCheckDetails(
1027
+ agent_address=f"{self._consul_host}:{self._consul_port}",
1028
+ ),
1029
+ correlation_id=correlation_id,
1030
+ )
1031
+
1032
+ async def shutdown(self) -> None:
1033
+ """Shutdown the handler and release resources.
1034
+
1035
+ Cleans up:
1036
+ - Thread pool executor (always)
1037
+ - Consul client (only if handler owns it)
1038
+ """
1039
+ if self._executor is not None:
1040
+ self._executor.shutdown(wait=True)
1041
+ self._executor = None
1042
+
1043
+ # Clean up owned Consul client
1044
+ if self._owns_client:
1045
+ self._consul_client = None
1046
+ self._owns_client = False
1047
+
1048
+ logger.info("HandlerServiceDiscoveryConsul shutdown complete")
1049
+
1050
+
1051
+ __all__ = ["HandlerServiceDiscoveryConsul"]