omnibase_infra 0.2.2__py3-none-any.whl → 0.2.4__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 +6 -1
- 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/contracts/handlers/filesystem/handler_contract.yaml +1 -1
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +1 -1
- 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/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 +8 -2
- omnibase_infra/handlers/handler_graph.py +860 -4
- omnibase_infra/handlers/handler_http.py +8 -2
- omnibase_infra/handlers/handler_intent.py +387 -0
- omnibase_infra/handlers/handler_mcp.py +10 -1
- omnibase_infra/handlers/handler_vault.py +11 -5
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +7 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +7 -0
- omnibase_infra/mixins/mixin_node_introspection.py +18 -0
- omnibase_infra/models/discovery/model_introspection_config.py +11 -0
- omnibase_infra/models/handlers/__init__.py +38 -5
- omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +4 -4
- omnibase_infra/models/handlers/model_contract_discovery_result.py +6 -4
- omnibase_infra/models/handlers/model_handler_source_config.py +220 -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/node_registration_orchestrator/plugin.py +1 -1
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +7 -7
- omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +4 -3
- omnibase_infra/nodes/node_registration_storage_effect/node.py +4 -1
- omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +1 -1
- omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +4 -1
- omnibase_infra/protocols/__init__.py +2 -0
- omnibase_infra/protocols/protocol_container_aware.py +200 -0
- omnibase_infra/runtime/__init__.py +39 -0
- omnibase_infra/runtime/handler_bootstrap_source.py +26 -33
- omnibase_infra/runtime/handler_contract_config_loader.py +1 -1
- omnibase_infra/runtime/handler_contract_source.py +10 -51
- omnibase_infra/runtime/handler_identity.py +81 -0
- omnibase_infra/runtime/handler_plugin_loader.py +15 -0
- omnibase_infra/runtime/handler_registry.py +11 -3
- omnibase_infra/runtime/handler_source_resolver.py +326 -0
- omnibase_infra/runtime/protocol_lifecycle_executor.py +6 -6
- omnibase_infra/runtime/registry/registry_protocol_binding.py +13 -13
- omnibase_infra/runtime/registry_contract_source.py +693 -0
- omnibase_infra/runtime/service_kernel.py +1 -1
- omnibase_infra/runtime/service_runtime_host_process.py +463 -190
- omnibase_infra/runtime/util_wiring.py +12 -3
- omnibase_infra/services/__init__.py +21 -0
- omnibase_infra/services/corpus_capture.py +7 -1
- omnibase_infra/services/mcp/mcp_server_lifecycle.py +9 -3
- omnibase_infra/services/registry_api/main.py +31 -13
- omnibase_infra/services/registry_api/service.py +10 -19
- omnibase_infra/services/service_timeout_emitter.py +7 -1
- 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_retry_optimistic.py +281 -0
- omnibase_infra/validation/__init__.py +16 -0
- omnibase_infra/validation/validation_exemptions.yaml +27 -0
- {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.4.dist-info}/METADATA +3 -3
- {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.4.dist-info}/RECORD +79 -58
- {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.4.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.4.dist-info}/entry_points.txt +0 -0
- {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Protocol extension for Infrastructure Handlers with Container DI.
|
|
4
|
+
|
|
5
|
+
This module defines the ProtocolContainerAware interface, which extends the base
|
|
6
|
+
ProtocolHandler from omnibase_spi to add container-based dependency injection
|
|
7
|
+
requirements. All infrastructure handlers in omnibase_infra must implement this
|
|
8
|
+
extended protocol.
|
|
9
|
+
|
|
10
|
+
Why This Protocol Exists:
|
|
11
|
+
The base ProtocolHandler in omnibase_spi is intentionally minimal and doesn't
|
|
12
|
+
mandate a specific constructor signature. This keeps the SPI layer decoupled
|
|
13
|
+
from implementation details.
|
|
14
|
+
|
|
15
|
+
However, omnibase_infra handlers require ModelONEXContainer for:
|
|
16
|
+
- Dependency injection of shared services (connection pools, clients)
|
|
17
|
+
- Configuration access without global state
|
|
18
|
+
- Testability through container mocking
|
|
19
|
+
|
|
20
|
+
This protocol adds the __init__ signature requirement while inheriting all
|
|
21
|
+
method requirements from the base protocol.
|
|
22
|
+
|
|
23
|
+
Protocol Hierarchy:
|
|
24
|
+
omnibase_spi.ProtocolHandler (base)
|
|
25
|
+
- handler_type property
|
|
26
|
+
- initialize()
|
|
27
|
+
- shutdown()
|
|
28
|
+
- execute()
|
|
29
|
+
- describe()
|
|
30
|
+
- health_check()
|
|
31
|
+
|
|
32
|
+
omnibase_infra.ProtocolContainerAware (extension)
|
|
33
|
+
- All methods from ProtocolHandler
|
|
34
|
+
- __init__(container: ModelONEXContainer) requirement
|
|
35
|
+
|
|
36
|
+
Usage:
|
|
37
|
+
Infrastructure handlers should implement this extended protocol:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from omnibase_core.container import ModelONEXContainer
|
|
41
|
+
from omnibase_infra.protocols import ProtocolContainerAware
|
|
42
|
+
|
|
43
|
+
class HandlerDatabase:
|
|
44
|
+
'''Database handler implementing the infra protocol.'''
|
|
45
|
+
|
|
46
|
+
def __init__(self, container: ModelONEXContainer) -> None:
|
|
47
|
+
self._container = container
|
|
48
|
+
# Access shared resources via container
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def handler_type(self) -> str:
|
|
52
|
+
return "db"
|
|
53
|
+
|
|
54
|
+
# ... implement remaining protocol methods ...
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The RuntimeHostProcess uses this protocol for type-safe handler instantiation:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
handler_cls: type[ProtocolContainerAware] = handler_registry.get(handler_type)
|
|
61
|
+
handler_instance = handler_cls(container=container) # Type-safe
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Thread Safety:
|
|
65
|
+
Handler implementations must be thread-safe. The container provides access
|
|
66
|
+
to shared resources that may be used concurrently.
|
|
67
|
+
|
|
68
|
+
See Also:
|
|
69
|
+
- omnibase_spi.protocols.handlers.protocol_handler.ProtocolHandler
|
|
70
|
+
- omnibase_core.container.ModelONEXContainer
|
|
71
|
+
- CLAUDE.md section "Container-Based Dependency Injection"
|
|
72
|
+
- PR #186: Container DI refactoring for handlers
|
|
73
|
+
|
|
74
|
+
.. versionadded:: 0.7.1
|
|
75
|
+
Created as part of OMN-1434 container DI standardization.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
from __future__ import annotations
|
|
79
|
+
|
|
80
|
+
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
81
|
+
|
|
82
|
+
from omnibase_spi.protocols.handlers.protocol_handler import (
|
|
83
|
+
ProtocolHandler as ProtocolHandlerBase,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if TYPE_CHECKING:
|
|
87
|
+
from omnibase_core.container import ModelONEXContainer
|
|
88
|
+
|
|
89
|
+
__all__ = [
|
|
90
|
+
"ProtocolContainerAware",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@runtime_checkable
|
|
95
|
+
class ProtocolContainerAware(ProtocolHandlerBase, Protocol):
|
|
96
|
+
"""Extended protocol for infrastructure handlers with container DI.
|
|
97
|
+
|
|
98
|
+
This protocol extends the base ProtocolHandler from omnibase_spi to require
|
|
99
|
+
a constructor that accepts ModelONEXContainer for dependency injection.
|
|
100
|
+
|
|
101
|
+
All infrastructure handlers in omnibase_infra must implement this protocol.
|
|
102
|
+
The container provides access to:
|
|
103
|
+
- Database connection pools
|
|
104
|
+
- HTTP clients
|
|
105
|
+
- Service discovery clients
|
|
106
|
+
- Configuration values
|
|
107
|
+
- Logging context
|
|
108
|
+
|
|
109
|
+
Methods inherited from ProtocolHandler:
|
|
110
|
+
handler_type: Property returning the handler type identifier.
|
|
111
|
+
initialize: Initialize clients and connection pools.
|
|
112
|
+
shutdown: Release resources and close connections.
|
|
113
|
+
execute: Execute protocol-specific operations.
|
|
114
|
+
describe: Return handler metadata and capabilities.
|
|
115
|
+
health_check: Check handler health and connectivity.
|
|
116
|
+
|
|
117
|
+
Constructor Requirement (added by this protocol):
|
|
118
|
+
__init__: Must accept container: ModelONEXContainer as first positional argument.
|
|
119
|
+
|
|
120
|
+
Example:
|
|
121
|
+
```python
|
|
122
|
+
from omnibase_core.container import ModelONEXContainer
|
|
123
|
+
|
|
124
|
+
class HandlerConsul:
|
|
125
|
+
def __init__(self, container: ModelONEXContainer) -> None:
|
|
126
|
+
self._container = container
|
|
127
|
+
self._consul_url = container.config.get("consul_url")
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def handler_type(self) -> str:
|
|
131
|
+
return "consul"
|
|
132
|
+
|
|
133
|
+
async def initialize(self, config):
|
|
134
|
+
# Use container for shared resources
|
|
135
|
+
...
|
|
136
|
+
|
|
137
|
+
# ... implement remaining methods ...
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Protocol Verification:
|
|
141
|
+
Per ONEX conventions, verify protocol compliance via duck typing:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
handler_cls = handler_registry.get("http")
|
|
145
|
+
|
|
146
|
+
# Verify constructor signature accepts container
|
|
147
|
+
import inspect
|
|
148
|
+
sig = inspect.signature(handler_cls.__init__)
|
|
149
|
+
params = list(sig.parameters.keys())
|
|
150
|
+
assert "container" in params or len(params) > 1 # self + container
|
|
151
|
+
|
|
152
|
+
# Verify protocol methods exist
|
|
153
|
+
assert hasattr(handler_cls, "handler_type")
|
|
154
|
+
assert hasattr(handler_cls, "initialize") and callable(getattr(handler_cls, "initialize"))
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Note:
|
|
158
|
+
Method bodies in this Protocol use ``...`` (Ellipsis) rather than
|
|
159
|
+
``raise NotImplementedError()``. This is the standard Python convention
|
|
160
|
+
for ``typing.Protocol`` classes per PEP 544.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
def __init__(self, container: ModelONEXContainer) -> None:
|
|
164
|
+
"""Initialize the handler with a dependency injection container.
|
|
165
|
+
|
|
166
|
+
All infrastructure handlers receive their dependencies through the
|
|
167
|
+
container rather than as individual constructor arguments. This enables:
|
|
168
|
+
|
|
169
|
+
- Consistent initialization across all handlers
|
|
170
|
+
- Easy testing through container mocking
|
|
171
|
+
- Runtime configuration without code changes
|
|
172
|
+
- Shared resource management (connection pools, clients)
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
container: ONEX dependency injection container providing access to:
|
|
176
|
+
- Configuration values (container.config)
|
|
177
|
+
- Shared services (database pools, HTTP clients)
|
|
178
|
+
- Logging context
|
|
179
|
+
- Runtime metadata
|
|
180
|
+
|
|
181
|
+
Note:
|
|
182
|
+
Handlers should store the container reference and access dependencies
|
|
183
|
+
lazily when needed, rather than extracting all values in __init__.
|
|
184
|
+
This improves startup time and allows for late-bound configuration.
|
|
185
|
+
|
|
186
|
+
Example:
|
|
187
|
+
```python
|
|
188
|
+
def __init__(self, container: ModelONEXContainer) -> None:
|
|
189
|
+
self._container = container
|
|
190
|
+
self._client: httpx.AsyncClient | None = None # Lazy init
|
|
191
|
+
|
|
192
|
+
async def initialize(self, config):
|
|
193
|
+
# Create client during initialize(), not __init__()
|
|
194
|
+
self._client = httpx.AsyncClient(
|
|
195
|
+
base_url=self._container.config.get("base_url"),
|
|
196
|
+
timeout=config.get("timeout", 30.0),
|
|
197
|
+
)
|
|
198
|
+
```
|
|
199
|
+
"""
|
|
200
|
+
...
|
|
@@ -160,6 +160,15 @@ from omnibase_infra.runtime.handler_bootstrap_source import (
|
|
|
160
160
|
SOURCE_TYPE_BOOTSTRAP,
|
|
161
161
|
)
|
|
162
162
|
|
|
163
|
+
# Handler identity helper (OMN-1095)
|
|
164
|
+
from omnibase_infra.runtime.handler_identity import (
|
|
165
|
+
HANDLER_IDENTITY_PREFIX,
|
|
166
|
+
handler_identity,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Handler source resolver (OMN-1095)
|
|
170
|
+
from omnibase_infra.runtime.handler_source_resolver import HandlerSourceResolver
|
|
171
|
+
|
|
163
172
|
# Handler contract config loader
|
|
164
173
|
from omnibase_infra.runtime.handler_contract_config_loader import (
|
|
165
174
|
MAX_CONTRACT_SIZE_BYTES,
|
|
@@ -212,6 +221,20 @@ from omnibase_infra.runtime.transition_notification_outbox import (
|
|
|
212
221
|
TransitionNotificationOutbox,
|
|
213
222
|
)
|
|
214
223
|
|
|
224
|
+
# Registry contract source (OMN-1100)
|
|
225
|
+
from omnibase_infra.runtime.registry_contract_source import (
|
|
226
|
+
DEFAULT_CONSUL_HOST,
|
|
227
|
+
DEFAULT_CONSUL_PORT,
|
|
228
|
+
DEFAULT_CONTRACT_PREFIX,
|
|
229
|
+
RegistryContractSource,
|
|
230
|
+
adelete_contract_from_consul,
|
|
231
|
+
alist_contracts_in_consul,
|
|
232
|
+
astore_contract_in_consul,
|
|
233
|
+
delete_contract_from_consul,
|
|
234
|
+
list_contracts_in_consul,
|
|
235
|
+
store_contract_in_consul,
|
|
236
|
+
)
|
|
237
|
+
|
|
215
238
|
# Chain-aware dispatch (OMN-951) - must be imported LAST to avoid circular import
|
|
216
239
|
from omnibase_infra.runtime.chain_aware_dispatch import (
|
|
217
240
|
ChainAwareDispatcher,
|
|
@@ -320,6 +343,11 @@ __all__: list[str] = [
|
|
|
320
343
|
# Handler bootstrap source (OMN-1087)
|
|
321
344
|
"HandlerBootstrapSource",
|
|
322
345
|
"SOURCE_TYPE_BOOTSTRAP",
|
|
346
|
+
# Handler identity helper (OMN-1095)
|
|
347
|
+
"HANDLER_IDENTITY_PREFIX",
|
|
348
|
+
"handler_identity",
|
|
349
|
+
# Handler source resolver (OMN-1095)
|
|
350
|
+
"HandlerSourceResolver",
|
|
323
351
|
# Handler contract config loader
|
|
324
352
|
"MAX_CONTRACT_SIZE_BYTES",
|
|
325
353
|
"extract_handler_config",
|
|
@@ -343,4 +371,15 @@ __all__: list[str] = [
|
|
|
343
371
|
# Transition notification publisher and outbox (OMN-1139)
|
|
344
372
|
"TransitionNotificationOutbox",
|
|
345
373
|
"TransitionNotificationPublisher",
|
|
374
|
+
# Registry contract source (OMN-1100)
|
|
375
|
+
"DEFAULT_CONSUL_HOST",
|
|
376
|
+
"DEFAULT_CONSUL_PORT",
|
|
377
|
+
"DEFAULT_CONTRACT_PREFIX",
|
|
378
|
+
"RegistryContractSource",
|
|
379
|
+
"adelete_contract_from_consul",
|
|
380
|
+
"alist_contracts_in_consul",
|
|
381
|
+
"astore_contract_in_consul",
|
|
382
|
+
"delete_contract_from_consul",
|
|
383
|
+
"list_contracts_in_consul",
|
|
384
|
+
"store_contract_in_consul",
|
|
346
385
|
]
|
|
@@ -79,6 +79,7 @@ from omnibase_infra.runtime.handler_contract_config_loader import (
|
|
|
79
79
|
extract_handler_config,
|
|
80
80
|
load_handler_contract_config,
|
|
81
81
|
)
|
|
82
|
+
from omnibase_infra.runtime.handler_identity import handler_identity
|
|
82
83
|
from omnibase_infra.runtime.protocol_contract_source import ProtocolContractSource
|
|
83
84
|
|
|
84
85
|
|
|
@@ -95,7 +96,7 @@ class BootstrapEffectDefinition(TypedDict):
|
|
|
95
96
|
must always specify their implementation class.
|
|
96
97
|
|
|
97
98
|
Attributes:
|
|
98
|
-
handler_id: Unique identifier with "
|
|
99
|
+
handler_id: Unique identifier with "proto." prefix (protocol identity namespace).
|
|
99
100
|
name: Human-readable display name.
|
|
100
101
|
description: Handler purpose description.
|
|
101
102
|
handler_kind: ONEX handler archetype (all are "effect" for I/O handlers).
|
|
@@ -116,36 +117,27 @@ class BootstrapEffectDefinition(TypedDict):
|
|
|
116
117
|
|
|
117
118
|
|
|
118
119
|
# =============================================================================
|
|
119
|
-
# Thread-Safe Model Rebuild Pattern (
|
|
120
|
+
# Thread-Safe Model Rebuild Pattern (Safety Net)
|
|
120
121
|
# =============================================================================
|
|
121
122
|
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
#
|
|
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.
|
|
125
127
|
#
|
|
126
|
-
#
|
|
127
|
-
# -
|
|
128
|
-
#
|
|
129
|
-
# -
|
|
130
|
-
# it would fail with circular import errors because ModelHandlerValidationError
|
|
131
|
-
# may not be fully defined yet in the import chain
|
|
132
|
-
# - HandlerContractSource can use immediate module-level model_rebuild() because
|
|
133
|
-
# by the time that module is imported (via explicit user code, not runtime init),
|
|
134
|
-
# all dependencies are already resolved
|
|
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
|
|
135
132
|
#
|
|
136
|
-
# WHY THREAD-SAFE:
|
|
133
|
+
# WHY THREAD-SAFE (historical context):
|
|
137
134
|
# - discover_handlers() may be called concurrently from multiple threads
|
|
138
135
|
# - Unlike module-level code (which Python imports once, thread-safely),
|
|
139
136
|
# runtime-invoked code needs explicit synchronization
|
|
140
|
-
# - The double-checked locking pattern minimizes lock contention
|
|
141
|
-
# first successful rebuild, subsequent calls hit only the fast path check
|
|
142
|
-
#
|
|
143
|
-
# PATTERN COMPARISON:
|
|
144
|
-
# - HandlerBootstrapSource: Deferred + thread-safe (this file)
|
|
145
|
-
# - HandlerContractSource: Immediate + module-level (see that file for rationale)
|
|
137
|
+
# - The double-checked locking pattern minimizes lock contention
|
|
146
138
|
#
|
|
147
139
|
# See Also:
|
|
148
|
-
# -
|
|
140
|
+
# - omnibase_infra.models.handlers.__init__: Central model_rebuild() location
|
|
149
141
|
# - OMN-1087 for the ticket tracking this design decision
|
|
150
142
|
# =============================================================================
|
|
151
143
|
|
|
@@ -229,7 +221,7 @@ _HANDLER_TYPE_VAULT = "vault"
|
|
|
229
221
|
# Bootstrap handler definitions.
|
|
230
222
|
#
|
|
231
223
|
# Each entry contains the metadata needed to create a ModelHandlerDescriptor:
|
|
232
|
-
# handler_id: Unique identifier with "
|
|
224
|
+
# handler_id: Unique identifier with "proto." prefix (protocol identity namespace)
|
|
233
225
|
# name: Human-readable display name
|
|
234
226
|
# description: Handler purpose description
|
|
235
227
|
# handler_kind: ONEX handler archetype (all are "effect" for I/O handlers)
|
|
@@ -252,7 +244,7 @@ _HANDLER_TYPE_VAULT = "vault"
|
|
|
252
244
|
# providing compile-time type safety for the hardcoded values.
|
|
253
245
|
_BOOTSTRAP_HANDLER_DEFINITIONS: list[BootstrapEffectDefinition] = [
|
|
254
246
|
{
|
|
255
|
-
"handler_id":
|
|
247
|
+
"handler_id": handler_identity(_HANDLER_TYPE_CONSUL),
|
|
256
248
|
"name": "Consul Handler",
|
|
257
249
|
"description": "HashiCorp Consul service discovery handler",
|
|
258
250
|
"handler_kind": "effect",
|
|
@@ -262,7 +254,7 @@ _BOOTSTRAP_HANDLER_DEFINITIONS: list[BootstrapEffectDefinition] = [
|
|
|
262
254
|
"contract_path": "contracts/handlers/consul/handler_contract.yaml",
|
|
263
255
|
},
|
|
264
256
|
{
|
|
265
|
-
"handler_id":
|
|
257
|
+
"handler_id": handler_identity(_HANDLER_TYPE_DATABASE),
|
|
266
258
|
"name": "Database Handler",
|
|
267
259
|
"description": "PostgreSQL database handler",
|
|
268
260
|
"handler_kind": "effect",
|
|
@@ -272,7 +264,7 @@ _BOOTSTRAP_HANDLER_DEFINITIONS: list[BootstrapEffectDefinition] = [
|
|
|
272
264
|
"contract_path": "contracts/handlers/db/handler_contract.yaml",
|
|
273
265
|
},
|
|
274
266
|
{
|
|
275
|
-
"handler_id":
|
|
267
|
+
"handler_id": handler_identity(_HANDLER_TYPE_HTTP),
|
|
276
268
|
"name": "HTTP Handler",
|
|
277
269
|
"description": "HTTP REST protocol handler",
|
|
278
270
|
"handler_kind": "effect",
|
|
@@ -282,7 +274,7 @@ _BOOTSTRAP_HANDLER_DEFINITIONS: list[BootstrapEffectDefinition] = [
|
|
|
282
274
|
"contract_path": "contracts/handlers/http/handler_contract.yaml",
|
|
283
275
|
},
|
|
284
276
|
{
|
|
285
|
-
"handler_id":
|
|
277
|
+
"handler_id": handler_identity(_HANDLER_TYPE_VAULT),
|
|
286
278
|
"name": "Vault Handler",
|
|
287
279
|
"description": "HashiCorp Vault secret management handler",
|
|
288
280
|
"handler_kind": "effect",
|
|
@@ -292,7 +284,7 @@ _BOOTSTRAP_HANDLER_DEFINITIONS: list[BootstrapEffectDefinition] = [
|
|
|
292
284
|
"contract_path": "contracts/handlers/vault/handler_contract.yaml",
|
|
293
285
|
},
|
|
294
286
|
{
|
|
295
|
-
"handler_id":
|
|
287
|
+
"handler_id": handler_identity(_HANDLER_TYPE_MCP),
|
|
296
288
|
"name": "MCP Handler",
|
|
297
289
|
"description": "Model Context Protocol handler for AI agent integration",
|
|
298
290
|
"handler_kind": "effect",
|
|
@@ -331,13 +323,14 @@ class HandlerBootstrapSource(
|
|
|
331
323
|
>>> source = HandlerBootstrapSource()
|
|
332
324
|
>>> result = await source.discover_handlers()
|
|
333
325
|
>>> print(f"Found {len(result.descriptors)} bootstrap handlers")
|
|
334
|
-
Found
|
|
326
|
+
Found 5 bootstrap handlers
|
|
335
327
|
>>> for desc in result.descriptors:
|
|
336
328
|
... print(f" - {desc.handler_id}: {desc.description}")
|
|
337
|
-
-
|
|
338
|
-
-
|
|
339
|
-
-
|
|
340
|
-
-
|
|
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
|
|
341
334
|
|
|
342
335
|
Performance Characteristics:
|
|
343
336
|
- No filesystem or network I/O required
|
|
@@ -31,6 +31,7 @@ from __future__ import annotations
|
|
|
31
31
|
import logging
|
|
32
32
|
import time
|
|
33
33
|
from pathlib import Path
|
|
34
|
+
from typing import cast
|
|
34
35
|
|
|
35
36
|
import yaml
|
|
36
37
|
from pydantic import ValidationError
|
|
@@ -41,62 +42,18 @@ from omnibase_core.models.primitives import ModelSemVer
|
|
|
41
42
|
from omnibase_infra.enums import EnumHandlerErrorType, EnumHandlerSourceType
|
|
42
43
|
from omnibase_infra.models.errors import ModelHandlerValidationError
|
|
43
44
|
from omnibase_infra.models.handlers import (
|
|
45
|
+
LiteralHandlerKind,
|
|
44
46
|
ModelContractDiscoveryResult,
|
|
45
47
|
ModelHandlerDescriptor,
|
|
46
48
|
ModelHandlerIdentifier,
|
|
47
49
|
)
|
|
48
50
|
from omnibase_infra.runtime.protocol_contract_source import ProtocolContractSource
|
|
49
51
|
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
# This
|
|
55
|
-
# HandlerBootstrapSource which uses a deferred, thread-safe pattern.
|
|
56
|
-
#
|
|
57
|
-
# WHY IMMEDIATE (module-load) IS SAFE HERE:
|
|
58
|
-
# - HandlerContractSource is NOT imported through runtime.__init__.py
|
|
59
|
-
# - This module is imported explicitly by user code AFTER the runtime is
|
|
60
|
-
# initialized and all model dependencies are resolved
|
|
61
|
-
# - Python's import mechanism is inherently thread-safe for module-level code:
|
|
62
|
-
# the import lock ensures module initialization runs exactly once, even if
|
|
63
|
-
# multiple threads import the same module simultaneously
|
|
64
|
-
# - Therefore, no explicit thread-safety mechanism (locks) is needed
|
|
65
|
-
#
|
|
66
|
-
# WHY HANDLERBOOTSTRAPSOURCE NEEDS DEFERRED PATTERN:
|
|
67
|
-
# - HandlerBootstrapSource is imported during runtime bootstrap (via __init__.py)
|
|
68
|
-
# - At that point, ModelHandlerValidationError may not be fully resolved
|
|
69
|
-
# - Additionally, discover_handlers() may be called from multiple threads,
|
|
70
|
-
# requiring explicit synchronization for the rebuild call
|
|
71
|
-
#
|
|
72
|
-
# PATTERN COMPARISON:
|
|
73
|
-
# - HandlerContractSource: Immediate + module-level (this file)
|
|
74
|
-
# - HandlerBootstrapSource: Deferred + thread-safe (see that file for rationale)
|
|
75
|
-
#
|
|
76
|
-
# See Also:
|
|
77
|
-
# - handler_bootstrap_source.py lines 68-100 for the deferred pattern rationale
|
|
78
|
-
# - OMN-1087 for the ticket tracking this design decision
|
|
79
|
-
# =============================================================================
|
|
80
|
-
#
|
|
81
|
-
# Rebuild ModelContractDiscoveryResult to resolve the forward reference
|
|
82
|
-
# to ModelHandlerValidationError. This must happen after ModelHandlerValidationError
|
|
83
|
-
# is imported to make the type available for Pydantic validation.
|
|
84
|
-
#
|
|
85
|
-
# Why forward reference resolution is needed:
|
|
86
|
-
# ModelContractDiscoveryResult has a field typed as list[ModelHandlerValidationError].
|
|
87
|
-
# ModelHandlerValidationError imports ModelHandlerIdentifier from models.handlers.
|
|
88
|
-
# If ModelContractDiscoveryResult directly imported ModelHandlerValidationError,
|
|
89
|
-
# it would cause a circular import because models.handlers.__init__.py imports
|
|
90
|
-
# ModelContractDiscoveryResult.
|
|
91
|
-
#
|
|
92
|
-
# The solution:
|
|
93
|
-
# 1. ModelContractDiscoveryResult uses TYPE_CHECKING to defer the import
|
|
94
|
-
# 2. With PEP 563 (from __future__ import annotations), the annotation becomes
|
|
95
|
-
# a string at runtime, avoiding the circular import
|
|
96
|
-
# 3. model_rebuild() resolves the string annotation to the actual type after
|
|
97
|
-
# both classes are defined
|
|
98
|
-
#
|
|
99
|
-
# This is tested in: tests/unit/runtime/test_handler_contract_source.py
|
|
52
|
+
# Forward Reference Resolution:
|
|
53
|
+
# ModelContractDiscoveryResult uses a forward reference to ModelHandlerValidationError.
|
|
54
|
+
# Since we import ModelHandlerValidationError above, we can call model_rebuild() here
|
|
55
|
+
# to resolve the forward reference. This call is idempotent - multiple calls are harmless.
|
|
56
|
+
# This ensures the model is fully defined before we create instances in discover_handlers().
|
|
100
57
|
ModelContractDiscoveryResult.model_rebuild()
|
|
101
58
|
|
|
102
59
|
logger = logging.getLogger(__name__)
|
|
@@ -735,7 +692,9 @@ class HandlerContractSource(ProtocolContractSource):
|
|
|
735
692
|
handler_id=contract.handler_id,
|
|
736
693
|
name=contract.name,
|
|
737
694
|
version=contract.contract_version,
|
|
738
|
-
handler_kind=
|
|
695
|
+
handler_kind=cast(
|
|
696
|
+
"LiteralHandlerKind", contract.descriptor.node_archetype.value
|
|
697
|
+
),
|
|
739
698
|
input_model=contract.input_model,
|
|
740
699
|
output_model=contract.output_model,
|
|
741
700
|
description=contract.description,
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Handler Identity Utilities for HYBRID Mode Resolution.
|
|
4
|
+
|
|
5
|
+
This module provides the `handler_identity()` function used by both bootstrap
|
|
6
|
+
and contract sources to generate consistent handler IDs. This enables per-handler
|
|
7
|
+
identity matching in HYBRID mode.
|
|
8
|
+
|
|
9
|
+
The Problem:
|
|
10
|
+
Prior to this module, contract-discovered handlers used a "bootstrap." prefix
|
|
11
|
+
for handler_id to enable HYBRID mode identity matching. This was semantically
|
|
12
|
+
confusing because "bootstrap" reads like "where it came from," not "what it is."
|
|
13
|
+
|
|
14
|
+
The Solution:
|
|
15
|
+
A neutral "proto." prefix that indicates this is a **protocol identity namespace**,
|
|
16
|
+
not a source indicator. Both HandlerBootstrapSource and PluginLoaderContractSource
|
|
17
|
+
use this shared helper to generate consistent IDs.
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
>>> from omnibase_infra.runtime.handler_identity import handler_identity
|
|
21
|
+
>>> handler_identity("consul")
|
|
22
|
+
'proto.consul'
|
|
23
|
+
>>> handler_identity("http")
|
|
24
|
+
'proto.http'
|
|
25
|
+
|
|
26
|
+
See Also:
|
|
27
|
+
- HandlerSourceResolver._resolve_hybrid(): Resolution logic that compares handler_id
|
|
28
|
+
- HandlerBootstrapSource: Uses this to generate bootstrap handler IDs
|
|
29
|
+
- PluginLoaderContractSource: Uses this for contract-discovered handlers
|
|
30
|
+
|
|
31
|
+
Part of OMN-1095: Handler Source Mode Feature Flag / Bootstrap Contract Hybrid.
|
|
32
|
+
|
|
33
|
+
.. versionadded:: 0.7.0
|
|
34
|
+
Introduced to fix handler ID namespace confusion.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
# Prefix used for handler identity in HYBRID mode resolution.
|
|
40
|
+
# This is a protocol namespace, NOT a source indicator.
|
|
41
|
+
# Both bootstrap and contract sources use this prefix.
|
|
42
|
+
HANDLER_IDENTITY_PREFIX = "proto"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def handler_identity(protocol_type: str) -> str:
|
|
46
|
+
"""Generate stable handler identity for HYBRID mode resolution.
|
|
47
|
+
|
|
48
|
+
Both bootstrap and contract sources use this to generate consistent IDs,
|
|
49
|
+
enabling per-handler identity matching in HYBRID mode. When both sources
|
|
50
|
+
provide a handler with the same identity, the resolver applies precedence
|
|
51
|
+
rules (contract wins by default, or bootstrap wins if allow_bootstrap_override=True).
|
|
52
|
+
|
|
53
|
+
The "proto." prefix indicates this is a **protocol identity namespace**, not
|
|
54
|
+
a source origin indicator. Contract-discovered handlers use this prefix
|
|
55
|
+
specifically so they can be compared against bootstrap-discovered handlers
|
|
56
|
+
with the same protocol_type.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
protocol_type: The protocol type (e.g., "consul", "http", "db", "vault", "mcp").
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Stable handler identity string (e.g., "proto.consul", "proto.http").
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
>>> handler_identity("consul")
|
|
66
|
+
'proto.consul'
|
|
67
|
+
>>> handler_identity("http")
|
|
68
|
+
'proto.http'
|
|
69
|
+
|
|
70
|
+
See Also:
|
|
71
|
+
HandlerSourceResolver._resolve_hybrid() for resolution logic that uses
|
|
72
|
+
these identities to determine which handler wins when both sources
|
|
73
|
+
provide handlers with the same identity.
|
|
74
|
+
"""
|
|
75
|
+
return f"{HANDLER_IDENTITY_PREFIX}.{protocol_type}"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
__all__ = [
|
|
79
|
+
"HANDLER_IDENTITY_PREFIX",
|
|
80
|
+
"handler_identity",
|
|
81
|
+
]
|
|
@@ -659,6 +659,20 @@ class HandlerPluginLoader(ProtocolHandlerPluginLoader):
|
|
|
659
659
|
},
|
|
660
660
|
)
|
|
661
661
|
|
|
662
|
+
# contract.handler_version is guaranteed non-None by model_validator
|
|
663
|
+
if contract.handler_version is None:
|
|
664
|
+
context = ModelInfraErrorContext.with_correlation(
|
|
665
|
+
correlation_id=correlation_id,
|
|
666
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
667
|
+
operation="load_from_contract",
|
|
668
|
+
)
|
|
669
|
+
raise ProtocolConfigurationError(
|
|
670
|
+
"handler_version should be set by model_validator",
|
|
671
|
+
context=context,
|
|
672
|
+
loader_error=EnumHandlerLoaderError.MISSING_REQUIRED_FIELDS.value,
|
|
673
|
+
contract_path=str(contract_path),
|
|
674
|
+
)
|
|
675
|
+
|
|
662
676
|
return ModelLoadedHandler(
|
|
663
677
|
handler_name=handler_name,
|
|
664
678
|
protocol_type=protocol_type,
|
|
@@ -667,6 +681,7 @@ class HandlerPluginLoader(ProtocolHandlerPluginLoader):
|
|
|
667
681
|
contract_path=resolved_contract_path,
|
|
668
682
|
capability_tags=capability_tags,
|
|
669
683
|
loaded_at=datetime.now(UTC),
|
|
684
|
+
handler_version=contract.handler_version,
|
|
670
685
|
)
|
|
671
686
|
|
|
672
687
|
def load_from_directory(
|
|
@@ -74,7 +74,7 @@ from omnibase_infra.runtime.registry.registry_protocol_binding import (
|
|
|
74
74
|
|
|
75
75
|
if TYPE_CHECKING:
|
|
76
76
|
from omnibase_core.protocol.protocol_event_bus import ProtocolEventBus
|
|
77
|
-
from
|
|
77
|
+
from omnibase_infra.protocols import ProtocolContainerAware
|
|
78
78
|
|
|
79
79
|
# =============================================================================
|
|
80
80
|
# Handler Type Constants
|
|
@@ -115,6 +115,12 @@ HANDLER_TYPE_MCP: str = "mcp"
|
|
|
115
115
|
The MCP handler exposes ONEX nodes as tools for AI agents via streamable HTTP.
|
|
116
116
|
Supports tools/list and tools/call operations per the MCP specification."""
|
|
117
117
|
|
|
118
|
+
HANDLER_TYPE_GRAPH: str = "graph"
|
|
119
|
+
"""Graph database (Memgraph/Neo4j) protocol handler type."""
|
|
120
|
+
|
|
121
|
+
HANDLER_TYPE_INTENT: str = "intent" # DEMO (OMN-1515)
|
|
122
|
+
"""Intent storage and query handler type for demo wiring."""
|
|
123
|
+
|
|
118
124
|
|
|
119
125
|
# =============================================================================
|
|
120
126
|
# Event Bus Kind Constants
|
|
@@ -192,7 +198,7 @@ def get_event_bus_registry() -> RegistryEventBusBinding:
|
|
|
192
198
|
# =============================================================================
|
|
193
199
|
|
|
194
200
|
|
|
195
|
-
def get_handler_class(handler_type: str) -> type[
|
|
201
|
+
def get_handler_class(handler_type: str) -> type[ProtocolContainerAware]:
|
|
196
202
|
"""Get handler class for the given type from the singleton registry.
|
|
197
203
|
|
|
198
204
|
Convenience function that wraps get_handler_registry().get().
|
|
@@ -275,7 +281,7 @@ def register_handlers_from_config(
|
|
|
275
281
|
|
|
276
282
|
TODO(OMN-41): Implement full handler resolution:
|
|
277
283
|
1. Use importlib to resolve protocol_class string to actual class
|
|
278
|
-
2. Validate class implements
|
|
284
|
+
2. Validate class implements ProtocolContainerAware protocol
|
|
279
285
|
3. Register handler with runtime via get_handler_registry()
|
|
280
286
|
4. Support handler instantiation options from config.options
|
|
281
287
|
"""
|
|
@@ -300,8 +306,10 @@ __all__: list[str] = [
|
|
|
300
306
|
"HANDLER_TYPE_CONSUL",
|
|
301
307
|
"HANDLER_TYPE_DATABASE",
|
|
302
308
|
"HANDLER_TYPE_GRPC",
|
|
309
|
+
"HANDLER_TYPE_GRAPH",
|
|
303
310
|
# Handler type constants
|
|
304
311
|
"HANDLER_TYPE_HTTP",
|
|
312
|
+
"HANDLER_TYPE_INTENT",
|
|
305
313
|
"HANDLER_TYPE_KAFKA",
|
|
306
314
|
"HANDLER_TYPE_MCP",
|
|
307
315
|
"HANDLER_TYPE_VALKEY",
|