omnibase_infra 0.2.1__py3-none-any.whl → 0.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omnibase_infra/__init__.py +1 -1
- omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
- omnibase_infra/capabilities/__init__.py +15 -0
- omnibase_infra/capabilities/capability_inference_rules.py +211 -0
- omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
- omnibase_infra/capabilities/intent_type_extractor.py +160 -0
- omnibase_infra/cli/commands.py +1 -1
- omnibase_infra/configs/widget_mapping.yaml +176 -0
- omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +5 -2
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +5 -2
- omnibase_infra/enums/__init__.py +6 -0
- omnibase_infra/enums/enum_handler_error_type.py +10 -0
- omnibase_infra/enums/enum_handler_source_mode.py +72 -0
- omnibase_infra/enums/enum_kafka_acks.py +99 -0
- omnibase_infra/errors/error_compute_registry.py +4 -1
- omnibase_infra/errors/error_event_bus_registry.py +4 -1
- omnibase_infra/errors/error_infra.py +3 -1
- omnibase_infra/errors/error_policy_registry.py +4 -1
- omnibase_infra/event_bus/event_bus_kafka.py +1 -1
- omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +59 -10
- omnibase_infra/handlers/__init__.py +8 -1
- omnibase_infra/handlers/handler_consul.py +7 -1
- omnibase_infra/handlers/handler_db.py +10 -3
- omnibase_infra/handlers/handler_graph.py +10 -5
- omnibase_infra/handlers/handler_http.py +8 -2
- omnibase_infra/handlers/handler_intent.py +387 -0
- omnibase_infra/handlers/handler_mcp.py +745 -63
- omnibase_infra/handlers/handler_vault.py +11 -5
- omnibase_infra/handlers/mixins/mixin_consul_kv.py +4 -3
- omnibase_infra/handlers/mixins/mixin_consul_service.py +2 -1
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +7 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +308 -4
- omnibase_infra/handlers/service_discovery/models/model_service_info.py +10 -0
- omnibase_infra/mixins/mixin_async_circuit_breaker.py +3 -2
- omnibase_infra/mixins/mixin_node_introspection.py +42 -7
- omnibase_infra/mixins/mixin_retry_execution.py +1 -1
- omnibase_infra/models/discovery/model_introspection_config.py +11 -0
- omnibase_infra/models/handlers/__init__.py +48 -5
- omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
- omnibase_infra/models/handlers/model_contract_discovery_result.py +6 -4
- omnibase_infra/models/handlers/model_handler_descriptor.py +15 -0
- omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
- omnibase_infra/models/mcp/__init__.py +15 -0
- omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
- omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
- omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
- omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
- omnibase_infra/models/registration/model_node_capabilities.py +11 -0
- omnibase_infra/models/registration/model_node_introspection_event.py +9 -0
- omnibase_infra/models/runtime/model_handler_contract.py +25 -9
- omnibase_infra/models/runtime/model_loaded_handler.py +9 -0
- omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +0 -5
- omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +17 -10
- omnibase_infra/nodes/effects/contract.yaml +0 -5
- omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +7 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +86 -1
- omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +3 -3
- omnibase_infra/nodes/node_registration_orchestrator/plugin.py +1 -1
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +9 -8
- omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +4 -3
- omnibase_infra/nodes/node_registration_orchestrator/wiring.py +14 -13
- omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +0 -5
- omnibase_infra/nodes/node_registration_storage_effect/node.py +4 -1
- omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +47 -26
- omnibase_infra/nodes/node_registry_effect/contract.yaml +0 -5
- omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +2 -1
- omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +28 -20
- omnibase_infra/plugins/examples/plugin_json_normalizer.py +2 -2
- omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +2 -2
- omnibase_infra/plugins/plugin_compute_base.py +16 -2
- omnibase_infra/protocols/__init__.py +2 -0
- omnibase_infra/protocols/protocol_container_aware.py +200 -0
- omnibase_infra/protocols/protocol_event_projector.py +1 -1
- omnibase_infra/runtime/__init__.py +90 -1
- omnibase_infra/runtime/binding_config_resolver.py +102 -37
- omnibase_infra/runtime/constants_notification.py +75 -0
- omnibase_infra/runtime/contract_handler_discovery.py +6 -1
- omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
- omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
- omnibase_infra/runtime/handler_contract_source.py +267 -186
- omnibase_infra/runtime/handler_identity.py +81 -0
- omnibase_infra/runtime/handler_plugin_loader.py +19 -2
- omnibase_infra/runtime/handler_registry.py +11 -3
- omnibase_infra/runtime/handler_source_resolver.py +326 -0
- omnibase_infra/runtime/mixin_semver_cache.py +25 -1
- omnibase_infra/runtime/mixins/__init__.py +7 -0
- omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
- omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +31 -10
- omnibase_infra/runtime/models/__init__.py +24 -0
- omnibase_infra/runtime/models/model_health_check_result.py +2 -1
- omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
- omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
- omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
- omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
- omnibase_infra/runtime/projector_plugin_loader.py +1 -1
- omnibase_infra/runtime/projector_shell.py +229 -1
- omnibase_infra/runtime/protocol_lifecycle_executor.py +6 -6
- omnibase_infra/runtime/protocols/__init__.py +10 -0
- omnibase_infra/runtime/registry/registry_protocol_binding.py +16 -15
- omnibase_infra/runtime/registry_contract_source.py +693 -0
- omnibase_infra/runtime/registry_policy.py +9 -326
- omnibase_infra/runtime/secret_resolver.py +4 -2
- omnibase_infra/runtime/service_kernel.py +11 -3
- omnibase_infra/runtime/service_message_dispatch_engine.py +4 -2
- omnibase_infra/runtime/service_runtime_host_process.py +589 -106
- omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
- omnibase_infra/runtime/transition_notification_publisher.py +764 -0
- omnibase_infra/runtime/util_container_wiring.py +6 -5
- omnibase_infra/runtime/util_wiring.py +17 -4
- omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
- omnibase_infra/services/__init__.py +21 -0
- omnibase_infra/services/corpus_capture.py +7 -1
- omnibase_infra/services/mcp/__init__.py +31 -0
- omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
- omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
- omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
- omnibase_infra/services/mcp/service_mcp_tool_sync.py +547 -0
- omnibase_infra/services/registry_api/__init__.py +40 -0
- omnibase_infra/services/registry_api/main.py +261 -0
- omnibase_infra/services/registry_api/models/__init__.py +66 -0
- omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
- omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
- omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
- omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
- omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
- omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
- omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
- omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
- omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
- omnibase_infra/services/registry_api/models/model_warning.py +49 -0
- omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
- omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
- omnibase_infra/services/registry_api/routes.py +371 -0
- omnibase_infra/services/registry_api/service.py +837 -0
- omnibase_infra/services/service_capability_query.py +4 -4
- omnibase_infra/services/service_health.py +3 -2
- omnibase_infra/services/service_timeout_emitter.py +20 -3
- omnibase_infra/services/service_timeout_scanner.py +7 -3
- omnibase_infra/services/session/__init__.py +56 -0
- omnibase_infra/services/session/config_consumer.py +120 -0
- omnibase_infra/services/session/config_store.py +139 -0
- omnibase_infra/services/session/consumer.py +1007 -0
- omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
- omnibase_infra/services/session/store.py +997 -0
- omnibase_infra/utils/__init__.py +19 -0
- omnibase_infra/utils/util_atomic_file.py +261 -0
- omnibase_infra/utils/util_db_transaction.py +239 -0
- omnibase_infra/utils/util_dsn_validation.py +1 -1
- omnibase_infra/utils/util_retry_optimistic.py +281 -0
- omnibase_infra/validation/__init__.py +3 -19
- omnibase_infra/validation/contracts/security.validation.yaml +114 -0
- omnibase_infra/validation/infra_validators.py +35 -24
- omnibase_infra/validation/validation_exemptions.yaml +140 -9
- omnibase_infra/validation/validator_chain_propagation.py +2 -2
- omnibase_infra/validation/validator_runtime_shape.py +1 -1
- omnibase_infra/validation/validator_security.py +473 -370
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/METADATA +3 -3
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/RECORD +161 -98
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/entry_points.txt +0 -0
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Handler Bootstrap Source for Hardcoded Handler Registration.
|
|
4
|
+
|
|
5
|
+
This module provides HandlerBootstrapSource, which centralizes all hardcoded handler
|
|
6
|
+
wiring that was previously scattered in util_wiring.py. This source implements
|
|
7
|
+
ProtocolContractSource and provides handler descriptors for the core infrastructure
|
|
8
|
+
handlers (Consul, Database, HTTP, Vault, MCP).
|
|
9
|
+
|
|
10
|
+
Part of OMN-1087: HandlerBootstrapSource for hardcoded handler registration.
|
|
11
|
+
|
|
12
|
+
The bootstrap source provides handler descriptors for effect handlers that interact
|
|
13
|
+
with external infrastructure services. These handlers use envelope-based routing
|
|
14
|
+
and are registered as the foundation of the ONEX runtime handler ecosystem.
|
|
15
|
+
|
|
16
|
+
Contract Loading:
|
|
17
|
+
Bootstrap handlers now load their contract YAML files during discovery.
|
|
18
|
+
This enables:
|
|
19
|
+
- Security metadata (trusted_namespace, audit_logging, requires_authentication)
|
|
20
|
+
- Tags for handler discovery and filtering
|
|
21
|
+
- Validation that contract files exist and are well-formed
|
|
22
|
+
|
|
23
|
+
Contract file locations (relative to repo root):
|
|
24
|
+
- contracts/handlers/consul/handler_contract.yaml (basic contract)
|
|
25
|
+
- contracts/handlers/db/handler_contract.yaml (basic contract)
|
|
26
|
+
- contracts/handlers/http/handler_contract.yaml (basic contract)
|
|
27
|
+
- contracts/handlers/vault/handler_contract.yaml (basic contract)
|
|
28
|
+
- src/omnibase_infra/contracts/handlers/mcp/handler_contract.yaml (rich contract with transport config)
|
|
29
|
+
|
|
30
|
+
Basic contracts provide: name, handler_class, handler_type, tags, security
|
|
31
|
+
Rich contracts (MCP only) additionally provide: descriptor, metadata.transport, metadata.security
|
|
32
|
+
|
|
33
|
+
If a contract file is missing or invalid, discovery fails fast with
|
|
34
|
+
ProtocolConfigurationError. This ensures bootstrap handlers always have
|
|
35
|
+
valid configuration.
|
|
36
|
+
|
|
37
|
+
Registered Handlers:
|
|
38
|
+
- consul: HandlerConsul for HashiCorp Consul service discovery
|
|
39
|
+
- db: HandlerDb for PostgreSQL database operations
|
|
40
|
+
- http: HandlerHttpRest for HTTP/REST protocol operations
|
|
41
|
+
- vault: HandlerVault for HashiCorp Vault secret management
|
|
42
|
+
- mcp: HandlerMCP for Model Context Protocol AI agent integration
|
|
43
|
+
|
|
44
|
+
All handlers are registered with handler_kind="effect" as they perform external I/O
|
|
45
|
+
operations with infrastructure services.
|
|
46
|
+
|
|
47
|
+
See Also:
|
|
48
|
+
- ProtocolContractSource: Protocol definition for handler sources
|
|
49
|
+
- HandlerContractSource: Filesystem-based contract discovery source
|
|
50
|
+
- handler_contract_config_loader: Loads and parses handler contract YAML files
|
|
51
|
+
- util_wiring: Module that previously contained hardcoded handler wiring
|
|
52
|
+
- ModelHandlerDescriptor: Descriptor model for discovered handlers
|
|
53
|
+
|
|
54
|
+
.. versionadded:: 0.6.4
|
|
55
|
+
Created as part of OMN-1087 bootstrap handler registration.
|
|
56
|
+
|
|
57
|
+
.. versionchanged:: 0.6.5
|
|
58
|
+
Added contract loading support. Bootstrap handlers now load and validate
|
|
59
|
+
their contract YAML files during discovery, populating contract_config
|
|
60
|
+
in the descriptor.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
from __future__ import annotations
|
|
64
|
+
|
|
65
|
+
import logging
|
|
66
|
+
import threading
|
|
67
|
+
import time
|
|
68
|
+
from typing import TypedDict, final
|
|
69
|
+
|
|
70
|
+
from omnibase_core.models.primitives import ModelSemVer
|
|
71
|
+
from omnibase_infra.errors import ProtocolConfigurationError
|
|
72
|
+
from omnibase_infra.models.handlers import (
|
|
73
|
+
LiteralHandlerKind,
|
|
74
|
+
ModelBootstrapHandlerDescriptor,
|
|
75
|
+
ModelContractDiscoveryResult,
|
|
76
|
+
ModelHandlerDescriptor,
|
|
77
|
+
)
|
|
78
|
+
from omnibase_infra.runtime.handler_contract_config_loader import (
|
|
79
|
+
extract_handler_config,
|
|
80
|
+
load_handler_contract_config,
|
|
81
|
+
)
|
|
82
|
+
from omnibase_infra.runtime.handler_identity import handler_identity
|
|
83
|
+
from omnibase_infra.runtime.protocol_contract_source import ProtocolContractSource
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class BootstrapEffectDefinition(TypedDict):
|
|
87
|
+
"""Type definition for bootstrap effect node configuration entries.
|
|
88
|
+
|
|
89
|
+
This TypedDict provides compile-time type safety for the hardcoded effect
|
|
90
|
+
definitions, ensuring kind values are correctly typed as LiteralHandlerKind
|
|
91
|
+
rather than generic str. This eliminates the need for type: ignore comments
|
|
92
|
+
when constructing ModelBootstrapHandlerDescriptor instances.
|
|
93
|
+
|
|
94
|
+
Note that handler_class is a required field here, matching the
|
|
95
|
+
ModelBootstrapHandlerDescriptor requirement that bootstrap handlers
|
|
96
|
+
must always specify their implementation class.
|
|
97
|
+
|
|
98
|
+
Attributes:
|
|
99
|
+
handler_id: Unique identifier with "proto." prefix (protocol identity namespace).
|
|
100
|
+
name: Human-readable display name.
|
|
101
|
+
description: Handler purpose description.
|
|
102
|
+
handler_kind: ONEX handler archetype (all are "effect" for I/O handlers).
|
|
103
|
+
handler_class: Fully qualified Python class path for dynamic import.
|
|
104
|
+
input_model: Fully qualified path to input type.
|
|
105
|
+
output_model: Fully qualified path to output type.
|
|
106
|
+
contract_path: Relative path to handler_contract.yaml from repo root.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
handler_id: str
|
|
110
|
+
name: str
|
|
111
|
+
description: str
|
|
112
|
+
handler_kind: LiteralHandlerKind
|
|
113
|
+
handler_class: str
|
|
114
|
+
input_model: str
|
|
115
|
+
output_model: str
|
|
116
|
+
contract_path: str
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# =============================================================================
|
|
120
|
+
# Thread-Safe Model Rebuild Pattern (Safety Net)
|
|
121
|
+
# =============================================================================
|
|
122
|
+
#
|
|
123
|
+
# ModelContractDiscoveryResult.model_rebuild() is now called CENTRALLY in
|
|
124
|
+
# omnibase_infra.models.handlers.__init__ to resolve forward references.
|
|
125
|
+
# This ensures the forward reference to ModelHandlerValidationError is resolved
|
|
126
|
+
# as soon as the handlers package is imported.
|
|
127
|
+
#
|
|
128
|
+
# This module retains a DEFERRED, thread-safe model_rebuild() call as a SAFETY NET:
|
|
129
|
+
# - model_rebuild() is idempotent - multiple calls are harmless
|
|
130
|
+
# - The flag-guarded pattern ensures at most one rebuild per process
|
|
131
|
+
# - This provides fallback protection if import order changes in the future
|
|
132
|
+
#
|
|
133
|
+
# WHY THREAD-SAFE (historical context):
|
|
134
|
+
# - discover_handlers() may be called concurrently from multiple threads
|
|
135
|
+
# - Unlike module-level code (which Python imports once, thread-safely),
|
|
136
|
+
# runtime-invoked code needs explicit synchronization
|
|
137
|
+
# - The double-checked locking pattern minimizes lock contention
|
|
138
|
+
#
|
|
139
|
+
# See Also:
|
|
140
|
+
# - omnibase_infra.models.handlers.__init__: Central model_rebuild() location
|
|
141
|
+
# - OMN-1087 for the ticket tracking this design decision
|
|
142
|
+
# =============================================================================
|
|
143
|
+
|
|
144
|
+
# Lock ensures only one thread performs the rebuild
|
|
145
|
+
_model_rebuild_lock = threading.Lock()
|
|
146
|
+
|
|
147
|
+
# Mutable container to track if model_rebuild() has been called
|
|
148
|
+
# Using a list avoids the need for global statement (PLW0603)
|
|
149
|
+
_model_rebuild_state: list[bool] = [False]
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _ensure_model_rebuilt() -> None:
|
|
153
|
+
"""Ensure ModelContractDiscoveryResult has resolved forward references.
|
|
154
|
+
|
|
155
|
+
This must be called before creating ModelContractDiscoveryResult instances.
|
|
156
|
+
It's deferred from module load time to avoid circular import issues when
|
|
157
|
+
this module is imported through the runtime.__init__.py chain.
|
|
158
|
+
|
|
159
|
+
The rebuild resolves the forward reference to ModelHandlerValidationError
|
|
160
|
+
in the validation_errors field of ModelContractDiscoveryResult.
|
|
161
|
+
|
|
162
|
+
Why Deferred (Not Module-Level):
|
|
163
|
+
Unlike HandlerContractSource which uses module-level model_rebuild(),
|
|
164
|
+
this module is imported early in the runtime bootstrap chain before all
|
|
165
|
+
model dependencies are resolved. Deferring the rebuild to first use
|
|
166
|
+
avoids circular import failures.
|
|
167
|
+
|
|
168
|
+
Thread Safety:
|
|
169
|
+
Uses double-checked locking pattern to ensure thread-safe initialization
|
|
170
|
+
while minimizing lock contention after the first successful rebuild.
|
|
171
|
+
This is necessary because discover_handlers() may be called from multiple
|
|
172
|
+
threads, unlike module-level code which Python imports once.
|
|
173
|
+
|
|
174
|
+
See Also:
|
|
175
|
+
handler_contract_source.py for the simpler immediate pattern used when
|
|
176
|
+
import order constraints don't apply.
|
|
177
|
+
"""
|
|
178
|
+
# Fast path - already rebuilt (no lock needed)
|
|
179
|
+
if _model_rebuild_state[0]:
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
# Thread-safe initialization with double-checked locking
|
|
183
|
+
with _model_rebuild_lock:
|
|
184
|
+
# Re-check after acquiring lock (another thread may have completed rebuild)
|
|
185
|
+
if _model_rebuild_state[0]:
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
# Import ModelHandlerValidationError here to avoid circular import at module load.
|
|
189
|
+
# This import MUST be in scope when model_rebuild() is called, as Pydantic uses
|
|
190
|
+
# the local namespace to resolve forward references in the validation_errors field.
|
|
191
|
+
from omnibase_infra.models.errors import ModelHandlerValidationError
|
|
192
|
+
|
|
193
|
+
# Rebuild the model to resolve forward references.
|
|
194
|
+
# If this fails, provide a clear error message rather than obscure Pydantic errors.
|
|
195
|
+
try:
|
|
196
|
+
ModelContractDiscoveryResult.model_rebuild()
|
|
197
|
+
except Exception as e:
|
|
198
|
+
raise RuntimeError(
|
|
199
|
+
f"Failed to rebuild ModelContractDiscoveryResult during bootstrap "
|
|
200
|
+
f"initialization. This typically indicates a circular import or missing "
|
|
201
|
+
f"type definition: {e}"
|
|
202
|
+
) from e
|
|
203
|
+
|
|
204
|
+
# Keep import reference in scope - required for Pydantic forward reference resolution
|
|
205
|
+
_ = ModelHandlerValidationError
|
|
206
|
+
_model_rebuild_state[0] = True
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
logger = logging.getLogger(__name__)
|
|
210
|
+
|
|
211
|
+
# Source type identifier for bootstrap handlers
|
|
212
|
+
SOURCE_TYPE_BOOTSTRAP = "BOOTSTRAP"
|
|
213
|
+
|
|
214
|
+
# Handler type constants (matching handler_registry.py)
|
|
215
|
+
_HANDLER_TYPE_CONSUL = "consul"
|
|
216
|
+
_HANDLER_TYPE_DATABASE = "db"
|
|
217
|
+
_HANDLER_TYPE_HTTP = "http"
|
|
218
|
+
_HANDLER_TYPE_MCP = "mcp"
|
|
219
|
+
_HANDLER_TYPE_VAULT = "vault"
|
|
220
|
+
|
|
221
|
+
# Bootstrap handler definitions.
|
|
222
|
+
#
|
|
223
|
+
# Each entry contains the metadata needed to create a ModelHandlerDescriptor:
|
|
224
|
+
# handler_id: Unique identifier with "proto." prefix (protocol identity namespace)
|
|
225
|
+
# name: Human-readable display name
|
|
226
|
+
# description: Handler purpose description
|
|
227
|
+
# handler_kind: ONEX handler archetype (all are "effect" for I/O handlers)
|
|
228
|
+
# handler_class: Fully qualified Python class path for dynamic import
|
|
229
|
+
# input_model: Fully qualified path to input type (envelope-based handlers use JsonDict)
|
|
230
|
+
# output_model: Fully qualified path to output type (all handlers return ModelHandlerOutput)
|
|
231
|
+
#
|
|
232
|
+
# Design Note (handler_class vs handler_module):
|
|
233
|
+
# ModelHandlerDescriptor uses a single handler_class field with the fully qualified
|
|
234
|
+
# path (e.g., "module.path.ClassName") rather than separate handler_module and
|
|
235
|
+
# handler_class fields. This follows the standard Python import convention and
|
|
236
|
+
# avoids redundancy. The runtime extracts module/class via rsplit(".", 1):
|
|
237
|
+
# module_path, class_name = handler_class.rsplit(".", 1)
|
|
238
|
+
# See: handler_plugin_loader.py::_import_handler_class() for implementation.
|
|
239
|
+
#
|
|
240
|
+
# These handlers are the core infrastructure handlers that support envelope-based
|
|
241
|
+
# routing patterns for external service integration.
|
|
242
|
+
#
|
|
243
|
+
# The BootstrapEffectDefinition TypedDict ensures handler_kind is typed as LiteralHandlerKind,
|
|
244
|
+
# providing compile-time type safety for the hardcoded values.
|
|
245
|
+
_BOOTSTRAP_HANDLER_DEFINITIONS: list[BootstrapEffectDefinition] = [
|
|
246
|
+
{
|
|
247
|
+
"handler_id": handler_identity(_HANDLER_TYPE_CONSUL),
|
|
248
|
+
"name": "Consul Handler",
|
|
249
|
+
"description": "HashiCorp Consul service discovery handler",
|
|
250
|
+
"handler_kind": "effect",
|
|
251
|
+
"handler_class": "omnibase_infra.handlers.handler_consul.HandlerConsul",
|
|
252
|
+
"input_model": "omnibase_infra.models.types.JsonDict",
|
|
253
|
+
"output_model": "omnibase_core.models.dispatch.ModelHandlerOutput",
|
|
254
|
+
"contract_path": "contracts/handlers/consul/handler_contract.yaml",
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"handler_id": handler_identity(_HANDLER_TYPE_DATABASE),
|
|
258
|
+
"name": "Database Handler",
|
|
259
|
+
"description": "PostgreSQL database handler",
|
|
260
|
+
"handler_kind": "effect",
|
|
261
|
+
"handler_class": "omnibase_infra.handlers.handler_db.HandlerDb",
|
|
262
|
+
"input_model": "omnibase_infra.models.types.JsonDict",
|
|
263
|
+
"output_model": "omnibase_core.models.dispatch.ModelHandlerOutput",
|
|
264
|
+
"contract_path": "contracts/handlers/db/handler_contract.yaml",
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"handler_id": handler_identity(_HANDLER_TYPE_HTTP),
|
|
268
|
+
"name": "HTTP Handler",
|
|
269
|
+
"description": "HTTP REST protocol handler",
|
|
270
|
+
"handler_kind": "effect",
|
|
271
|
+
"handler_class": "omnibase_infra.handlers.handler_http.HandlerHttpRest",
|
|
272
|
+
"input_model": "omnibase_infra.models.types.JsonDict",
|
|
273
|
+
"output_model": "omnibase_core.models.dispatch.ModelHandlerOutput",
|
|
274
|
+
"contract_path": "contracts/handlers/http/handler_contract.yaml",
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"handler_id": handler_identity(_HANDLER_TYPE_VAULT),
|
|
278
|
+
"name": "Vault Handler",
|
|
279
|
+
"description": "HashiCorp Vault secret management handler",
|
|
280
|
+
"handler_kind": "effect",
|
|
281
|
+
"handler_class": "omnibase_infra.handlers.handler_vault.HandlerVault",
|
|
282
|
+
"input_model": "omnibase_infra.models.types.JsonDict",
|
|
283
|
+
"output_model": "omnibase_core.models.dispatch.ModelHandlerOutput",
|
|
284
|
+
"contract_path": "contracts/handlers/vault/handler_contract.yaml",
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
"handler_id": handler_identity(_HANDLER_TYPE_MCP),
|
|
288
|
+
"name": "MCP Handler",
|
|
289
|
+
"description": "Model Context Protocol handler for AI agent integration",
|
|
290
|
+
"handler_kind": "effect",
|
|
291
|
+
"handler_class": "omnibase_infra.handlers.handler_mcp.HandlerMCP",
|
|
292
|
+
"input_model": "omnibase_infra.models.types.JsonDict",
|
|
293
|
+
"output_model": "omnibase_core.models.dispatch.ModelHandlerOutput",
|
|
294
|
+
"contract_path": "src/omnibase_infra/contracts/handlers/mcp/handler_contract.yaml",
|
|
295
|
+
},
|
|
296
|
+
]
|
|
297
|
+
|
|
298
|
+
# Version for all bootstrap handlers (hardcoded handlers use stable version)
|
|
299
|
+
_BOOTSTRAP_HANDLER_VERSION = ModelSemVer(major=1, minor=0, patch=0)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
@final
|
|
303
|
+
class HandlerBootstrapSource(
|
|
304
|
+
ProtocolContractSource
|
|
305
|
+
): # naming-ok: Handler prefix required by ProtocolHandlerSource convention
|
|
306
|
+
"""Handler source that provides hardcoded bootstrap handler descriptors.
|
|
307
|
+
|
|
308
|
+
This class implements ProtocolContractSource by returning predefined handler
|
|
309
|
+
descriptors for core infrastructure handlers. Unlike HandlerContractSource
|
|
310
|
+
which discovers handlers from filesystem contracts, this source provides
|
|
311
|
+
handlers that are essential for the ONEX runtime bootstrap process.
|
|
312
|
+
|
|
313
|
+
Protocol Compliance:
|
|
314
|
+
This class explicitly inherits from ProtocolContractSource and implements
|
|
315
|
+
all required protocol methods: discover_handlers() async method and
|
|
316
|
+
source_type property. Protocol compliance is verified at runtime through
|
|
317
|
+
Python's structural subtyping and enforced by type checkers.
|
|
318
|
+
|
|
319
|
+
Attributes:
|
|
320
|
+
source_type: Returns "BOOTSTRAP" as the source type identifier.
|
|
321
|
+
|
|
322
|
+
Example:
|
|
323
|
+
>>> source = HandlerBootstrapSource()
|
|
324
|
+
>>> result = await source.discover_handlers()
|
|
325
|
+
>>> print(f"Found {len(result.descriptors)} bootstrap handlers")
|
|
326
|
+
Found 5 bootstrap handlers
|
|
327
|
+
>>> for desc in result.descriptors:
|
|
328
|
+
... print(f" - {desc.handler_id}: {desc.description}")
|
|
329
|
+
- proto.consul: HashiCorp Consul service discovery handler
|
|
330
|
+
- proto.db: PostgreSQL database handler
|
|
331
|
+
- proto.http: HTTP REST protocol handler
|
|
332
|
+
- proto.mcp: Model Context Protocol handler for AI agent integration
|
|
333
|
+
- proto.vault: HashiCorp Vault secret management handler
|
|
334
|
+
|
|
335
|
+
Performance Characteristics:
|
|
336
|
+
- No filesystem or network I/O required
|
|
337
|
+
- Constant time O(1) discovery (hardcoded definitions)
|
|
338
|
+
- Typical performance: <1ms for all handlers (local dev)
|
|
339
|
+
- Test threshold: 100ms (generous for CI runner variance)
|
|
340
|
+
- Memory: ~500 bytes per handler descriptor
|
|
341
|
+
|
|
342
|
+
.. versionadded:: 0.6.4
|
|
343
|
+
Created as part of OMN-1087 bootstrap handler registration.
|
|
344
|
+
"""
|
|
345
|
+
|
|
346
|
+
@property
|
|
347
|
+
def source_type(self) -> str:
|
|
348
|
+
"""Return the source type identifier.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
"BOOTSTRAP" as the source type.
|
|
352
|
+
"""
|
|
353
|
+
return SOURCE_TYPE_BOOTSTRAP
|
|
354
|
+
|
|
355
|
+
async def discover_handlers(
|
|
356
|
+
self,
|
|
357
|
+
) -> ModelContractDiscoveryResult:
|
|
358
|
+
"""Discover bootstrap handler descriptors.
|
|
359
|
+
|
|
360
|
+
Returns predefined handler descriptors for core infrastructure handlers.
|
|
361
|
+
Unlike filesystem-based discovery, this method returns hardcoded
|
|
362
|
+
definitions that are essential for ONEX runtime bootstrap.
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
ModelContractDiscoveryResult containing bootstrap handler descriptors.
|
|
366
|
+
The validation_errors list will always be empty since bootstrap
|
|
367
|
+
handlers are hardcoded and validated at development time.
|
|
368
|
+
|
|
369
|
+
Note:
|
|
370
|
+
This method is idempotent and can be called multiple times safely.
|
|
371
|
+
Each call returns the same set of handler descriptors.
|
|
372
|
+
|
|
373
|
+
Implementation Note:
|
|
374
|
+
Uses ModelBootstrapHandlerDescriptor (which requires handler_class)
|
|
375
|
+
for construction validation, ensuring all bootstrap handlers have
|
|
376
|
+
the required handler_class field. The descriptors are instances
|
|
377
|
+
of ModelHandlerDescriptor due to inheritance.
|
|
378
|
+
"""
|
|
379
|
+
# Ensure forward references are resolved before creating result
|
|
380
|
+
_ensure_model_rebuilt()
|
|
381
|
+
|
|
382
|
+
start_time = time.perf_counter()
|
|
383
|
+
descriptors: list[ModelHandlerDescriptor] = []
|
|
384
|
+
|
|
385
|
+
logger.debug(
|
|
386
|
+
"Starting bootstrap handler discovery",
|
|
387
|
+
extra={
|
|
388
|
+
"source_type": SOURCE_TYPE_BOOTSTRAP,
|
|
389
|
+
"expected_handler_count": len(_BOOTSTRAP_HANDLER_DEFINITIONS),
|
|
390
|
+
},
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
# Create descriptors from hardcoded definitions
|
|
394
|
+
# Uses ModelBootstrapHandlerDescriptor to enforce handler_class requirement
|
|
395
|
+
for handler_def in _BOOTSTRAP_HANDLER_DEFINITIONS:
|
|
396
|
+
contract_path = handler_def["contract_path"]
|
|
397
|
+
contract_config = None
|
|
398
|
+
|
|
399
|
+
# Load contract configuration if path is specified
|
|
400
|
+
try:
|
|
401
|
+
contract = load_handler_contract_config(
|
|
402
|
+
contract_path,
|
|
403
|
+
handler_def["handler_id"],
|
|
404
|
+
)
|
|
405
|
+
handler_type = handler_def["handler_id"].split(".")[-1]
|
|
406
|
+
# Bootstrap handlers get handler_class from _BOOTSTRAP_HANDLER_DEFINITIONS,
|
|
407
|
+
# not from the contract file. Rich contracts (like MCP) and basic contracts
|
|
408
|
+
# don't include handler_class since it would be redundant with the definition.
|
|
409
|
+
contract_config = extract_handler_config(
|
|
410
|
+
contract, handler_type, require_basic_fields=False
|
|
411
|
+
)
|
|
412
|
+
logger.debug(
|
|
413
|
+
"Loaded contract config for bootstrap handler",
|
|
414
|
+
extra={
|
|
415
|
+
"handler_id": handler_def["handler_id"],
|
|
416
|
+
"contract_path": contract_path,
|
|
417
|
+
"config_keys": list(contract_config.keys()),
|
|
418
|
+
},
|
|
419
|
+
)
|
|
420
|
+
except ProtocolConfigurationError:
|
|
421
|
+
# Fail fast for bootstrap handlers - contracts must exist
|
|
422
|
+
logger.exception(
|
|
423
|
+
"Failed to load contract config for bootstrap handler",
|
|
424
|
+
extra={
|
|
425
|
+
"handler_id": handler_def["handler_id"],
|
|
426
|
+
"contract_path": contract_path,
|
|
427
|
+
},
|
|
428
|
+
)
|
|
429
|
+
raise
|
|
430
|
+
|
|
431
|
+
descriptor = ModelBootstrapHandlerDescriptor(
|
|
432
|
+
handler_id=handler_def["handler_id"],
|
|
433
|
+
name=handler_def["name"],
|
|
434
|
+
version=_BOOTSTRAP_HANDLER_VERSION,
|
|
435
|
+
handler_kind=handler_def["handler_kind"],
|
|
436
|
+
input_model=handler_def["input_model"],
|
|
437
|
+
output_model=handler_def["output_model"],
|
|
438
|
+
description=handler_def["description"],
|
|
439
|
+
handler_class=handler_def["handler_class"],
|
|
440
|
+
contract_path=contract_path,
|
|
441
|
+
contract_config=contract_config,
|
|
442
|
+
)
|
|
443
|
+
descriptors.append(descriptor)
|
|
444
|
+
|
|
445
|
+
logger.debug(
|
|
446
|
+
"Created bootstrap handler descriptor",
|
|
447
|
+
extra={
|
|
448
|
+
"handler_id": descriptor.handler_id,
|
|
449
|
+
"handler_name": descriptor.name,
|
|
450
|
+
"handler_kind": descriptor.handler_kind,
|
|
451
|
+
"contract_path": descriptor.contract_path,
|
|
452
|
+
"has_contract_config": descriptor.contract_config is not None,
|
|
453
|
+
"source_type": SOURCE_TYPE_BOOTSTRAP,
|
|
454
|
+
},
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
# Calculate duration and log results
|
|
458
|
+
duration_seconds = time.perf_counter() - start_time
|
|
459
|
+
self._log_discovery_results(len(descriptors), duration_seconds)
|
|
460
|
+
|
|
461
|
+
return ModelContractDiscoveryResult(
|
|
462
|
+
descriptors=descriptors,
|
|
463
|
+
validation_errors=[], # Bootstrap handlers have no validation errors
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
def _log_discovery_results(
|
|
467
|
+
self,
|
|
468
|
+
discovered_count: int,
|
|
469
|
+
duration_seconds: float,
|
|
470
|
+
) -> None:
|
|
471
|
+
"""Log the discovery results with structured counts and timing.
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
discovered_count: Number of successfully discovered handlers.
|
|
475
|
+
duration_seconds: Total discovery duration in seconds.
|
|
476
|
+
"""
|
|
477
|
+
# Cap handlers_per_sec at 1M to avoid float("inf") which can cause issues
|
|
478
|
+
# in downstream logging/monitoring systems expecting finite numbers.
|
|
479
|
+
# A value of 1M represents "effectively instant" discovery.
|
|
480
|
+
if duration_seconds > 0:
|
|
481
|
+
handlers_per_sec = discovered_count / duration_seconds
|
|
482
|
+
elif discovered_count > 0:
|
|
483
|
+
handlers_per_sec = 1_000_000.0 # Cap for instant discovery
|
|
484
|
+
else:
|
|
485
|
+
handlers_per_sec = 0.0
|
|
486
|
+
|
|
487
|
+
logger.info(
|
|
488
|
+
"Bootstrap handler discovery completed: "
|
|
489
|
+
"discovered_handler_count=%d, "
|
|
490
|
+
"duration_seconds=%.6f, handlers_per_second=%.1f",
|
|
491
|
+
discovered_count,
|
|
492
|
+
duration_seconds,
|
|
493
|
+
handlers_per_sec,
|
|
494
|
+
extra={
|
|
495
|
+
"discovered_handler_count": discovered_count,
|
|
496
|
+
"validation_failure_count": 0,
|
|
497
|
+
"source_type": SOURCE_TYPE_BOOTSTRAP,
|
|
498
|
+
"duration_seconds": duration_seconds,
|
|
499
|
+
"handlers_per_second": handlers_per_sec,
|
|
500
|
+
},
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
__all__ = [
|
|
505
|
+
"HandlerBootstrapSource",
|
|
506
|
+
"SOURCE_TYPE_BOOTSTRAP",
|
|
507
|
+
]
|