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
|
@@ -16,12 +16,21 @@ and error reporting in ONEX handlers.
|
|
|
16
16
|
Added ModelBootstrapHandlerDescriptor for OMN-1087 bootstrap handler
|
|
17
17
|
validation with required handler_class field.
|
|
18
18
|
|
|
19
|
+
.. versionchanged:: 0.7.0
|
|
20
|
+
Added ModelHandlerSourceConfig for OMN-1095 handler source mode
|
|
21
|
+
configuration with production hardening features.
|
|
22
|
+
|
|
19
23
|
Note:
|
|
20
|
-
ModelContractDiscoveryResult uses a forward reference to
|
|
21
|
-
|
|
22
|
-
reference is resolved via model_rebuild() in
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
ModelContractDiscoveryResult uses a forward reference to ModelHandlerValidationError
|
|
25
|
+
to avoid circular imports between models.handlers and models.errors packages.
|
|
26
|
+
The forward reference is resolved via model_rebuild() calls in runtime modules
|
|
27
|
+
that import ModelHandlerValidationError (e.g., handler_contract_source.py,
|
|
28
|
+
handler_bootstrap_source.py, registry_contract_source.py). Each module calls
|
|
29
|
+
model_rebuild() after importing both the model and the forward-referenced type.
|
|
30
|
+
This pattern is required because:
|
|
31
|
+
1. models.errors imports ModelHandlerIdentifier from models.handlers
|
|
32
|
+
2. models.handlers cannot import from models.errors at module level (circular)
|
|
33
|
+
3. model_rebuild() is idempotent, so multiple calls are harmless
|
|
25
34
|
"""
|
|
26
35
|
|
|
27
36
|
from omnibase_infra.models.handlers.model_bootstrap_handler_descriptor import (
|
|
@@ -37,6 +46,9 @@ from omnibase_infra.models.handlers.model_handler_descriptor import (
|
|
|
37
46
|
from omnibase_infra.models.handlers.model_handler_identifier import (
|
|
38
47
|
ModelHandlerIdentifier,
|
|
39
48
|
)
|
|
49
|
+
from omnibase_infra.models.handlers.model_handler_source_config import (
|
|
50
|
+
ModelHandlerSourceConfig,
|
|
51
|
+
)
|
|
40
52
|
|
|
41
53
|
__all__ = [
|
|
42
54
|
"LiteralHandlerKind",
|
|
@@ -44,4 +56,25 @@ __all__ = [
|
|
|
44
56
|
"ModelContractDiscoveryResult",
|
|
45
57
|
"ModelHandlerDescriptor",
|
|
46
58
|
"ModelHandlerIdentifier",
|
|
59
|
+
"ModelHandlerSourceConfig",
|
|
47
60
|
]
|
|
61
|
+
|
|
62
|
+
# =============================================================================
|
|
63
|
+
# Forward Reference Resolution
|
|
64
|
+
# =============================================================================
|
|
65
|
+
# ModelContractDiscoveryResult uses TYPE_CHECKING to defer import of
|
|
66
|
+
# ModelHandlerValidationError to avoid circular imports:
|
|
67
|
+
# - models.errors imports ModelHandlerIdentifier from models.handlers
|
|
68
|
+
# - models.handlers cannot import ModelHandlerValidationError at module level
|
|
69
|
+
#
|
|
70
|
+
# The forward reference is resolved via model_rebuild() in runtime modules that
|
|
71
|
+
# import ModelHandlerValidationError (e.g., handler_contract_source.py,
|
|
72
|
+
# handler_bootstrap_source.py, registry_contract_source.py, handler_source_resolver.py).
|
|
73
|
+
# Each module calls model_rebuild() at module level after importing both the model
|
|
74
|
+
# and the forward-referenced type. This is safe because model_rebuild() is idempotent.
|
|
75
|
+
#
|
|
76
|
+
# Why NOT here at module level:
|
|
77
|
+
# - Circular import: models.handlers.__init__ -> models.errors.__init__
|
|
78
|
+
# -> model_handler_validation_error.py -> models.handlers (for identifier)
|
|
79
|
+
# - Runtime modules load after model packages, avoiding this cycle
|
|
80
|
+
# =============================================================================
|
|
@@ -44,7 +44,7 @@ class ModelBootstrapHandlerDescriptor(ModelHandlerDescriptor):
|
|
|
44
44
|
All other fields maintain the same constraints as the parent class.
|
|
45
45
|
|
|
46
46
|
Attributes:
|
|
47
|
-
handler_id: Unique identifier for the handler (e.g., "
|
|
47
|
+
handler_id: Unique identifier for the handler (e.g., "proto.consul").
|
|
48
48
|
name: Human-readable name for the handler.
|
|
49
49
|
version: Semantic version (ModelSemVer). Accepts string, dict, or ModelSemVer.
|
|
50
50
|
handler_kind: Handler kind (compute, effect, reducer, orchestrator).
|
|
@@ -58,7 +58,7 @@ class ModelBootstrapHandlerDescriptor(ModelHandlerDescriptor):
|
|
|
58
58
|
Create a bootstrap handler descriptor:
|
|
59
59
|
|
|
60
60
|
>>> descriptor = ModelBootstrapHandlerDescriptor(
|
|
61
|
-
... handler_id="
|
|
61
|
+
... handler_id="proto.consul",
|
|
62
62
|
... name="Consul Handler",
|
|
63
63
|
... version="1.0.0",
|
|
64
64
|
... handler_kind="effect",
|
|
@@ -74,7 +74,7 @@ class ModelBootstrapHandlerDescriptor(ModelHandlerDescriptor):
|
|
|
74
74
|
>>> from pydantic import ValidationError
|
|
75
75
|
>>> try:
|
|
76
76
|
... ModelBootstrapHandlerDescriptor(
|
|
77
|
-
... handler_id="
|
|
77
|
+
... handler_id="proto.consul",
|
|
78
78
|
... name="Consul Handler",
|
|
79
79
|
... version="1.0.0",
|
|
80
80
|
... handler_kind="effect",
|
|
@@ -144,7 +144,7 @@ class ModelBootstrapHandlerDescriptor(ModelHandlerDescriptor):
|
|
|
144
144
|
|
|
145
145
|
Example:
|
|
146
146
|
>>> bootstrap_desc = ModelBootstrapHandlerDescriptor(
|
|
147
|
-
... handler_id="
|
|
147
|
+
... handler_id="proto.consul",
|
|
148
148
|
... name="Consul Handler",
|
|
149
149
|
... version="1.0.0",
|
|
150
150
|
... handler_kind="effect",
|
|
@@ -72,9 +72,11 @@ class ModelContractDiscoveryResult(BaseModel):
|
|
|
72
72
|
)
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
#
|
|
75
|
+
# Forward Reference Resolution:
|
|
76
|
+
# This model uses TYPE_CHECKING to defer import of ModelHandlerValidationError.
|
|
77
|
+
# model_rebuild() is called in runtime modules that import ModelHandlerValidationError
|
|
78
|
+
# (e.g., handler_contract_source.py, handler_bootstrap_source.py, registry_contract_source.py).
|
|
79
|
+
# Each module calls model_rebuild() at module level after importing both the model
|
|
80
|
+
# and the forward-referenced type. This is safe because model_rebuild() is idempotent.
|
|
79
81
|
|
|
80
82
|
__all__ = ["ModelContractDiscoveryResult"]
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Handler Source Configuration Model.
|
|
4
|
+
|
|
5
|
+
This module provides ModelHandlerSourceConfig, a Pydantic model for
|
|
6
|
+
configuring handler source mode selection at runtime.
|
|
7
|
+
|
|
8
|
+
The configuration controls how handlers are discovered and loaded:
|
|
9
|
+
- BOOTSTRAP: Hardcoded handlers from _KNOWN_HANDLERS dict (MVP mode)
|
|
10
|
+
- CONTRACT: YAML contracts from handler_contract.yaml files (production)
|
|
11
|
+
- HYBRID: Contract-first with bootstrap fallback per-handler identity
|
|
12
|
+
|
|
13
|
+
Production hardening features:
|
|
14
|
+
- Bootstrap expiry enforcement: If bootstrap_expires_at is set and now > expires_at,
|
|
15
|
+
the runtime will refuse to start in BOOTSTRAP mode (or force CONTRACT mode)
|
|
16
|
+
- Structured logging of expiry status at startup
|
|
17
|
+
- Override control for hybrid mode handler resolution
|
|
18
|
+
|
|
19
|
+
.. versionadded:: 0.7.0
|
|
20
|
+
Created as part of OMN-1095 handler source mode configuration.
|
|
21
|
+
|
|
22
|
+
See Also:
|
|
23
|
+
- HANDLER_PROTOCOL_DRIVEN_ARCHITECTURE.md: Full architecture documentation
|
|
24
|
+
- EnumHandlerSourceMode: Enum defining valid source modes
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from datetime import UTC, datetime, timezone
|
|
30
|
+
|
|
31
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
32
|
+
|
|
33
|
+
from omnibase_infra.enums.enum_handler_source_mode import EnumHandlerSourceMode
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ModelHandlerSourceConfig(BaseModel):
|
|
37
|
+
"""Configuration for handler source mode selection.
|
|
38
|
+
|
|
39
|
+
Controls how handlers are discovered and loaded at runtime. This model
|
|
40
|
+
is used by RuntimeHostProcess and related components to determine the
|
|
41
|
+
handler loading strategy.
|
|
42
|
+
|
|
43
|
+
Configuration Options:
|
|
44
|
+
- handler_source_mode: Selects the loading strategy (BOOTSTRAP, CONTRACT, HYBRID)
|
|
45
|
+
- allow_bootstrap_override: Controls handler resolution in HYBRID mode
|
|
46
|
+
- bootstrap_expires_at: Production safety - forces CONTRACT after expiry
|
|
47
|
+
|
|
48
|
+
Production Hardening:
|
|
49
|
+
When bootstrap_expires_at is set and the current time exceeds it:
|
|
50
|
+
- BOOTSTRAP mode: Runtime refuses to start (safety mechanism)
|
|
51
|
+
- HYBRID mode: Bootstrap fallback disabled, contract-only resolution
|
|
52
|
+
- CONTRACT mode: No effect (already contract-only)
|
|
53
|
+
|
|
54
|
+
This prevents accidental deployment with hardcoded handlers in production.
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
handler_source_mode: Handler loading source mode.
|
|
58
|
+
- BOOTSTRAP: Load from hardcoded _KNOWN_HANDLERS dict (MVP)
|
|
59
|
+
- CONTRACT: Load from handler_contract.yaml files (production)
|
|
60
|
+
- HYBRID: Contract-first with bootstrap fallback per-handler identity
|
|
61
|
+
Defaults to HYBRID as recommended for gradual migration.
|
|
62
|
+
|
|
63
|
+
allow_bootstrap_override: If True, bootstrap handlers can override
|
|
64
|
+
contract handlers in HYBRID mode. Default is False, meaning
|
|
65
|
+
contract handlers take precedence (inverse of naive HYBRID).
|
|
66
|
+
Has no effect in BOOTSTRAP or CONTRACT modes.
|
|
67
|
+
When parsing from environment or config, string values "true",
|
|
68
|
+
"yes", "1", "on" (case-insensitive) are accepted as truthy.
|
|
69
|
+
|
|
70
|
+
bootstrap_expires_at: If set and expired, refuse BOOTSTRAP mode and
|
|
71
|
+
force CONTRACT. This is a production safety mechanism to ensure
|
|
72
|
+
hardcoded handlers are not accidentally deployed to production
|
|
73
|
+
after a migration deadline. Set to None to disable expiry checking.
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
>>> from datetime import datetime, timezone
|
|
77
|
+
>>> from omnibase_infra.models.handlers import ModelHandlerSourceConfig
|
|
78
|
+
>>> from omnibase_infra.enums import EnumHandlerSourceMode
|
|
79
|
+
>>>
|
|
80
|
+
>>> # Production configuration (recommended)
|
|
81
|
+
>>> config = ModelHandlerSourceConfig(
|
|
82
|
+
... handler_source_mode=EnumHandlerSourceMode.CONTRACT,
|
|
83
|
+
... )
|
|
84
|
+
>>>
|
|
85
|
+
>>> # Migration configuration with safety expiry (must be timezone-aware)
|
|
86
|
+
>>> config = ModelHandlerSourceConfig(
|
|
87
|
+
... handler_source_mode=EnumHandlerSourceMode.HYBRID,
|
|
88
|
+
... bootstrap_expires_at=datetime(2025, 3, 1, 0, 0, 0, tzinfo=timezone.utc),
|
|
89
|
+
... )
|
|
90
|
+
>>>
|
|
91
|
+
>>> # Check if bootstrap is expired
|
|
92
|
+
>>> if config.is_bootstrap_expired:
|
|
93
|
+
... print("Bootstrap mode has expired - must use CONTRACT")
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
model_config = ConfigDict(
|
|
97
|
+
strict=True,
|
|
98
|
+
frozen=True,
|
|
99
|
+
extra="forbid",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
handler_source_mode: EnumHandlerSourceMode = Field(
|
|
103
|
+
default=EnumHandlerSourceMode.HYBRID,
|
|
104
|
+
description="Handler loading source mode: BOOTSTRAP, CONTRACT, or HYBRID. "
|
|
105
|
+
"Defaults to HYBRID (contract-first with bootstrap fallback).",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
allow_bootstrap_override: bool = Field(
|
|
109
|
+
default=False,
|
|
110
|
+
description=(
|
|
111
|
+
"If True, bootstrap handlers can override contract handlers in HYBRID mode. "
|
|
112
|
+
"Default is False (contract handlers take precedence). "
|
|
113
|
+
"When parsing from config, string values 'true', 'yes', '1', 'on' "
|
|
114
|
+
"(case-insensitive) are accepted as truthy."
|
|
115
|
+
),
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
bootstrap_expires_at: datetime | None = Field(
|
|
119
|
+
default=None,
|
|
120
|
+
description=(
|
|
121
|
+
"If set and expired, refuse BOOTSTRAP mode and force CONTRACT. "
|
|
122
|
+
"Production safety mechanism for migration deadlines. "
|
|
123
|
+
"Must be timezone-aware (UTC recommended); naive datetimes are rejected."
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
@field_validator("allow_bootstrap_override", mode="before")
|
|
128
|
+
@classmethod
|
|
129
|
+
def _coerce_allow_bootstrap_override(cls, value: object) -> bool:
|
|
130
|
+
"""Coerce string and numeric values to boolean for config file compatibility.
|
|
131
|
+
|
|
132
|
+
Environment variables and YAML/JSON config files often represent booleans
|
|
133
|
+
as strings. This validator handles common truthy string representations
|
|
134
|
+
before Pydantic's strict type validation.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
value: The raw value to coerce (may be str, bool, int, float, None, or other).
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
True if value is a truthy string ("true", "yes", "1", "on") or
|
|
141
|
+
a truthy boolean, False otherwise.
|
|
142
|
+
|
|
143
|
+
Type Handling:
|
|
144
|
+
- bool: Passed through unchanged.
|
|
145
|
+
- str: Case-insensitive check for "true", "yes", "1", "on".
|
|
146
|
+
- int/float: 0 and 0.0 are False, all other numbers are True.
|
|
147
|
+
- None: Returns False.
|
|
148
|
+
- Unknown types: Default to False for safety.
|
|
149
|
+
"""
|
|
150
|
+
if isinstance(value, bool):
|
|
151
|
+
return value
|
|
152
|
+
if isinstance(value, str):
|
|
153
|
+
return value.lower() in ("true", "yes", "1", "on")
|
|
154
|
+
if isinstance(value, (int, float)):
|
|
155
|
+
# Explicit: 0/0.0 = False, any other number = True
|
|
156
|
+
return bool(value)
|
|
157
|
+
# Unknown types default to False for safety
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
@field_validator("bootstrap_expires_at")
|
|
161
|
+
@classmethod
|
|
162
|
+
def _validate_expires_at_timezone(cls, value: datetime | None) -> datetime | None:
|
|
163
|
+
"""Validate and normalize bootstrap_expires_at to UTC.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
value: The datetime value to validate.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
None if value is None, otherwise the datetime normalized to UTC.
|
|
170
|
+
|
|
171
|
+
Raises:
|
|
172
|
+
ValueError: If the datetime is naive (no timezone info).
|
|
173
|
+
"""
|
|
174
|
+
if value is None:
|
|
175
|
+
return None
|
|
176
|
+
if value.tzinfo is None:
|
|
177
|
+
raise ValueError(
|
|
178
|
+
"bootstrap_expires_at must be timezone-aware (UTC recommended). "
|
|
179
|
+
"Use datetime.now(timezone.utc) or datetime(..., tzinfo=timezone.utc)."
|
|
180
|
+
)
|
|
181
|
+
return value.astimezone(UTC)
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def is_bootstrap_expired(self) -> bool:
|
|
185
|
+
"""Check if bootstrap mode has expired.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
True if bootstrap_expires_at is set and current time exceeds it,
|
|
189
|
+
False otherwise.
|
|
190
|
+
|
|
191
|
+
Note:
|
|
192
|
+
Uses UTC-aware comparison. The bootstrap_expires_at field is
|
|
193
|
+
validated and normalized to UTC at construction time.
|
|
194
|
+
"""
|
|
195
|
+
if self.bootstrap_expires_at is None:
|
|
196
|
+
return False
|
|
197
|
+
return datetime.now(UTC) > self.bootstrap_expires_at
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def effective_mode(self) -> EnumHandlerSourceMode:
|
|
201
|
+
"""Get the effective handler source mode after expiry check.
|
|
202
|
+
|
|
203
|
+
If bootstrap_expires_at is set and expired, returns CONTRACT
|
|
204
|
+
regardless of the configured handler_source_mode. Otherwise
|
|
205
|
+
returns the configured mode.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
The effective handler source mode to use at runtime.
|
|
209
|
+
|
|
210
|
+
Note:
|
|
211
|
+
This property should be used by runtime components instead of
|
|
212
|
+
directly accessing handler_source_mode to ensure expiry
|
|
213
|
+
enforcement is applied.
|
|
214
|
+
"""
|
|
215
|
+
if self.is_bootstrap_expired:
|
|
216
|
+
return EnumHandlerSourceMode.CONTRACT
|
|
217
|
+
return self.handler_source_mode
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
__all__ = ["ModelHandlerSourceConfig"]
|
|
@@ -14,6 +14,7 @@ from uuid import UUID
|
|
|
14
14
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
15
15
|
|
|
16
16
|
from omnibase_core.enums import EnumNodeKind
|
|
17
|
+
from omnibase_core.models.capabilities import ModelContractCapabilities
|
|
17
18
|
from omnibase_core.models.primitives.model_semver import ModelSemVer
|
|
18
19
|
from omnibase_infra.enums import EnumIntrospectionReason
|
|
19
20
|
from omnibase_infra.models.discovery.model_discovered_capabilities import (
|
|
@@ -49,6 +50,8 @@ class ModelNodeIntrospectionEvent(BaseModel):
|
|
|
49
50
|
node_version: Semantic version of the node.
|
|
50
51
|
declared_capabilities: Contract-declared capabilities (feature flags).
|
|
51
52
|
discovered_capabilities: Runtime-discovered capabilities (reflection).
|
|
53
|
+
contract_capabilities: Contract-derived capabilities (design-time truth).
|
|
54
|
+
Populated from the node's contract when available, None otherwise.
|
|
52
55
|
endpoints: Dictionary of exposed endpoints (name -> URL).
|
|
53
56
|
current_state: Current FSM state if the node has state management.
|
|
54
57
|
reason: Why this introspection event was emitted.
|
|
@@ -98,6 +101,12 @@ class ModelNodeIntrospectionEvent(BaseModel):
|
|
|
98
101
|
default_factory=ModelDiscoveredCapabilities,
|
|
99
102
|
description="Capabilities discovered via runtime reflection",
|
|
100
103
|
)
|
|
104
|
+
contract_capabilities: ModelContractCapabilities | None = Field(
|
|
105
|
+
default=None,
|
|
106
|
+
description="Contract-derived capabilities (design-time truth, deterministic). "
|
|
107
|
+
"Populated by ContractCapabilityExtractor from the node's contract. "
|
|
108
|
+
"None when contract is not available or extraction fails.",
|
|
109
|
+
)
|
|
101
110
|
|
|
102
111
|
# Endpoints and state
|
|
103
112
|
endpoints: dict[str, str] = Field(
|
|
@@ -27,6 +27,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_valida
|
|
|
27
27
|
|
|
28
28
|
logger = logging.getLogger(__name__)
|
|
29
29
|
|
|
30
|
+
from omnibase_core.models.primitives import ModelSemVer
|
|
30
31
|
from omnibase_infra.enums.enum_handler_type_category import EnumHandlerTypeCategory
|
|
31
32
|
from omnibase_infra.errors import ProtocolConfigurationError
|
|
32
33
|
from omnibase_infra.models.runtime.model_contract_security_config import (
|
|
@@ -131,6 +132,10 @@ class ModelHandlerContract(BaseModel):
|
|
|
131
132
|
default=None,
|
|
132
133
|
description="Optional security configuration for the handler",
|
|
133
134
|
)
|
|
135
|
+
handler_version: ModelSemVer | None = Field(
|
|
136
|
+
default=None,
|
|
137
|
+
description="Handler version in semantic versioning format. If not provided, defaults to 1.0.0",
|
|
138
|
+
)
|
|
134
139
|
|
|
135
140
|
@field_validator("handler_type", mode="before")
|
|
136
141
|
@classmethod
|
|
@@ -207,18 +212,22 @@ class ModelHandlerContract(BaseModel):
|
|
|
207
212
|
return []
|
|
208
213
|
|
|
209
214
|
@model_validator(mode="after")
|
|
210
|
-
def
|
|
211
|
-
"""Set
|
|
215
|
+
def set_defaults(self) -> ModelHandlerContract:
|
|
216
|
+
"""Set default values for protocol_type and handler_version.
|
|
212
217
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
218
|
+
Protocol Type:
|
|
219
|
+
If protocol_type is None, derives it from handler_name by stripping
|
|
220
|
+
the 'handler-' prefix. If handler_name doesn't have that prefix,
|
|
221
|
+
uses the full handler_name as protocol_type.
|
|
216
222
|
|
|
217
|
-
|
|
218
|
-
|
|
223
|
+
Guards against empty derived protocol_type which would produce
|
|
224
|
+
invalid registry keys.
|
|
225
|
+
|
|
226
|
+
Handler Version:
|
|
227
|
+
If handler_version is None, sets it to the default version 1.0.0.
|
|
219
228
|
|
|
220
229
|
Returns:
|
|
221
|
-
Self with protocol_type populated.
|
|
230
|
+
Self with protocol_type and handler_version populated.
|
|
222
231
|
|
|
223
232
|
Raises:
|
|
224
233
|
ValueError: If derived protocol_type would be empty (e.g., handler_name
|
|
@@ -232,6 +241,8 @@ class ModelHandlerContract(BaseModel):
|
|
|
232
241
|
... )
|
|
233
242
|
>>> contract.protocol_type
|
|
234
243
|
'db'
|
|
244
|
+
>>> contract.handler_version
|
|
245
|
+
ModelSemVer(major=1, minor=0, patch=0)
|
|
235
246
|
|
|
236
247
|
>>> contract = ModelHandlerContract(
|
|
237
248
|
... handler_name="custom-handler",
|
|
@@ -241,6 +252,10 @@ class ModelHandlerContract(BaseModel):
|
|
|
241
252
|
>>> contract.protocol_type
|
|
242
253
|
'custom-handler'
|
|
243
254
|
"""
|
|
255
|
+
# Set default handler_version if not provided
|
|
256
|
+
if self.handler_version is None:
|
|
257
|
+
self.handler_version = ModelSemVer(major=1, minor=0, patch=0)
|
|
258
|
+
|
|
244
259
|
if self.protocol_type is None:
|
|
245
260
|
prefix = "handler-"
|
|
246
261
|
if self.handler_name.startswith(prefix):
|
|
@@ -263,7 +278,7 @@ class ModelHandlerContract(BaseModel):
|
|
|
263
278
|
f"Provide a non-empty protocol_type or handler_name."
|
|
264
279
|
)
|
|
265
280
|
|
|
266
|
-
# Log successful
|
|
281
|
+
# Log successful contract validation for debugging
|
|
267
282
|
logger.debug(
|
|
268
283
|
"Handler contract validated successfully",
|
|
269
284
|
extra={
|
|
@@ -271,6 +286,7 @@ class ModelHandlerContract(BaseModel):
|
|
|
271
286
|
"handler_class": self.handler_class,
|
|
272
287
|
"protocol_type": self.protocol_type,
|
|
273
288
|
"handler_type": self.handler_type.value,
|
|
289
|
+
"handler_version": str(self.handler_version),
|
|
274
290
|
},
|
|
275
291
|
)
|
|
276
292
|
|
|
@@ -24,6 +24,7 @@ from pathlib import Path
|
|
|
24
24
|
|
|
25
25
|
from pydantic import BaseModel, ConfigDict, Field
|
|
26
26
|
|
|
27
|
+
from omnibase_core.models.primitives import ModelSemVer
|
|
27
28
|
from omnibase_infra.enums.enum_handler_type_category import EnumHandlerTypeCategory
|
|
28
29
|
|
|
29
30
|
|
|
@@ -53,10 +54,13 @@ class ModelLoadedHandler(BaseModel):
|
|
|
53
54
|
Examples: ['auth', 'validation', 'http-client'].
|
|
54
55
|
loaded_at: Timestamp when the handler was successfully loaded.
|
|
55
56
|
Used for diagnostics and cache invalidation.
|
|
57
|
+
handler_version: Semantic version of the handler from the contract.
|
|
58
|
+
Used for version tracking and compatibility checks.
|
|
56
59
|
|
|
57
60
|
Example:
|
|
58
61
|
>>> from datetime import datetime, UTC
|
|
59
62
|
>>> from pathlib import Path
|
|
63
|
+
>>> from omnibase_core.models.primitives import ModelSemVer
|
|
60
64
|
>>> from omnibase_infra.enums import EnumHandlerTypeCategory
|
|
61
65
|
>>> handler = ModelLoadedHandler(
|
|
62
66
|
... handler_name="auth.validate_token",
|
|
@@ -65,6 +69,7 @@ class ModelLoadedHandler(BaseModel):
|
|
|
65
69
|
... contract_path=Path("/app/handlers/auth/handler_contract.yaml"),
|
|
66
70
|
... capability_tags=["auth", "validation", "jwt"],
|
|
67
71
|
... loaded_at=datetime.now(UTC),
|
|
72
|
+
... handler_version=ModelSemVer(major=1, minor=0, patch=0),
|
|
68
73
|
... )
|
|
69
74
|
>>> handler.handler_name
|
|
70
75
|
'auth.validate_token'
|
|
@@ -115,6 +120,10 @@ class ModelLoadedHandler(BaseModel):
|
|
|
115
120
|
...,
|
|
116
121
|
description="Timestamp when the handler was successfully loaded",
|
|
117
122
|
)
|
|
123
|
+
handler_version: ModelSemVer = Field(
|
|
124
|
+
...,
|
|
125
|
+
description="Handler semantic version from contract",
|
|
126
|
+
)
|
|
118
127
|
|
|
119
128
|
|
|
120
129
|
__all__ = ["ModelLoadedHandler"]
|
|
@@ -515,7 +515,7 @@ class PluginRegistration:
|
|
|
515
515
|
# Deferred import: Only load HandlerConsul when Consul is configured
|
|
516
516
|
from omnibase_infra.handlers import HandlerConsul
|
|
517
517
|
|
|
518
|
-
self._consul_handler = HandlerConsul()
|
|
518
|
+
self._consul_handler = HandlerConsul(config.container)
|
|
519
519
|
await self._consul_handler.initialize(
|
|
520
520
|
{"host": consul_host, "port": consul_port}
|
|
521
521
|
)
|
|
@@ -13,7 +13,7 @@ Handler Wiring (from contract.yaml):
|
|
|
13
13
|
- ModelNodeHeartbeatEvent -> HandlerNodeHeartbeat
|
|
14
14
|
|
|
15
15
|
Handler Implementation:
|
|
16
|
-
All handlers implement
|
|
16
|
+
All handlers implement ProtocolContainerAware directly with:
|
|
17
17
|
- handler_id, category, message_types, node_kind properties
|
|
18
18
|
- handle(envelope) -> ModelHandlerOutput signature
|
|
19
19
|
|
|
@@ -93,10 +93,10 @@ from typing import TYPE_CHECKING, cast
|
|
|
93
93
|
from omnibase_core.services.service_handler_registry import ServiceHandlerRegistry
|
|
94
94
|
from omnibase_infra.enums import EnumInfraTransportType
|
|
95
95
|
from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationError
|
|
96
|
+
from omnibase_infra.protocols import ProtocolContainerAware
|
|
96
97
|
from omnibase_infra.runtime.contract_loaders import (
|
|
97
98
|
load_handler_class_info_from_contract,
|
|
98
99
|
)
|
|
99
|
-
from omnibase_spi.protocols.handlers import ProtocolHandler
|
|
100
100
|
|
|
101
101
|
logger = logging.getLogger(__name__)
|
|
102
102
|
|
|
@@ -111,14 +111,14 @@ ALLOWED_NAMESPACES: tuple[str, ...] = (
|
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
def _validate_handler_protocol(handler: object) -> tuple[bool, list[str]]:
|
|
114
|
-
"""Validate handler implements
|
|
114
|
+
"""Validate handler implements ProtocolContainerAware via duck typing.
|
|
115
115
|
|
|
116
116
|
Uses duck typing to verify the handler has the required properties and
|
|
117
|
-
methods for
|
|
117
|
+
methods for ProtocolContainerAware compliance. Per ONEX conventions,
|
|
118
118
|
protocol compliance is verified via structural typing rather than
|
|
119
119
|
isinstance checks.
|
|
120
120
|
|
|
121
|
-
Protocol Requirements (
|
|
121
|
+
Protocol Requirements (ProtocolContainerAware):
|
|
122
122
|
- handler_id (property): Unique identifier string
|
|
123
123
|
- category (property): EnumMessageCategory value
|
|
124
124
|
- message_types (property): set[str] of message type names
|
|
@@ -493,7 +493,7 @@ class RegistryInfraNodeRegistrationOrchestrator:
|
|
|
493
493
|
context=ctx,
|
|
494
494
|
) from e
|
|
495
495
|
|
|
496
|
-
# Validate handler implements
|
|
496
|
+
# Validate handler implements ProtocolContainerAware
|
|
497
497
|
is_valid, missing = _validate_handler_protocol(handler_instance)
|
|
498
498
|
if not is_valid:
|
|
499
499
|
ctx = ModelInfraErrorContext.with_correlation(
|
|
@@ -502,7 +502,7 @@ class RegistryInfraNodeRegistrationOrchestrator:
|
|
|
502
502
|
target_name=handler_class_name,
|
|
503
503
|
)
|
|
504
504
|
raise ProtocolConfigurationError(
|
|
505
|
-
f"Handler '{handler_class_name}' does not implement
|
|
505
|
+
f"Handler '{handler_class_name}' does not implement ProtocolContainerAware. "
|
|
506
506
|
f"Missing required members: {', '.join(missing)}. "
|
|
507
507
|
f"Handlers must have: handler_id, category, message_types, node_kind properties "
|
|
508
508
|
f"and handle(envelope) method. "
|
|
@@ -203,8 +203,9 @@ class TimeoutCoordinator:
|
|
|
203
203
|
|
|
204
204
|
Usage in orchestrator:
|
|
205
205
|
>>> # Wire dependencies
|
|
206
|
-
>>> timeout_query = ServiceTimeoutScanner(projection_reader)
|
|
206
|
+
>>> timeout_query = ServiceTimeoutScanner(container, projection_reader)
|
|
207
207
|
>>> timeout_emission = ServiceTimeoutEmitter(
|
|
208
|
+
... container=container,
|
|
208
209
|
... timeout_query=timeout_query,
|
|
209
210
|
... event_bus=event_bus,
|
|
210
211
|
... projector=projector,
|
|
@@ -235,8 +236,8 @@ class TimeoutCoordinator:
|
|
|
235
236
|
|
|
236
237
|
Example:
|
|
237
238
|
>>> reader = ProjectionReaderRegistration(pool)
|
|
238
|
-
>>> query = ServiceTimeoutScanner(reader)
|
|
239
|
-
>>> emission = ServiceTimeoutEmitter(query, event_bus, projector)
|
|
239
|
+
>>> query = ServiceTimeoutScanner(container, reader)
|
|
240
|
+
>>> emission = ServiceTimeoutEmitter(container, query, event_bus, projector)
|
|
240
241
|
>>> coordinator = TimeoutCoordinator(query, emission)
|
|
241
242
|
"""
|
|
242
243
|
self._timeout_query = timeout_query
|
|
@@ -44,7 +44,10 @@ Pluggable Backends:
|
|
|
44
44
|
|
|
45
45
|
# Create container and register handler
|
|
46
46
|
container = ModelONEXContainer()
|
|
47
|
-
handler = HandlerRegistrationStoragePostgres(
|
|
47
|
+
handler = HandlerRegistrationStoragePostgres(
|
|
48
|
+
container=container,
|
|
49
|
+
dsn="postgresql://...",
|
|
50
|
+
)
|
|
48
51
|
RegistryInfraRegistrationStorage.register(container)
|
|
49
52
|
RegistryInfraRegistrationStorage.register_handler(container, handler)
|
|
50
53
|
|
|
@@ -139,7 +139,7 @@ class RegistryInfraRegistrationStorage:
|
|
|
139
139
|
>>> from omnibase_infra.handlers.registration_storage import (
|
|
140
140
|
... HandlerRegistrationStoragePostgres,
|
|
141
141
|
... )
|
|
142
|
-
>>> handler = HandlerRegistrationStoragePostgres(
|
|
142
|
+
>>> handler = HandlerRegistrationStoragePostgres(container, dsn="postgresql://...")
|
|
143
143
|
>>> RegistryInfraRegistrationStorage.register_handler(container, handler)
|
|
144
144
|
"""
|
|
145
145
|
# Import at runtime for isinstance check (protocol is @runtime_checkable)
|
omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py
CHANGED
|
@@ -86,7 +86,10 @@ class RegistryInfraServiceDiscovery:
|
|
|
86
86
|
RegistryInfraServiceDiscovery.register(container)
|
|
87
87
|
|
|
88
88
|
# Explicit handler registration
|
|
89
|
-
consul_handler = HandlerServiceDiscoveryConsul(
|
|
89
|
+
consul_handler = HandlerServiceDiscoveryConsul(
|
|
90
|
+
container=container,
|
|
91
|
+
consul_host="localhost",
|
|
92
|
+
)
|
|
90
93
|
RegistryInfraServiceDiscovery.register_with_handler(
|
|
91
94
|
container,
|
|
92
95
|
handler=consul_handler,
|
|
@@ -62,6 +62,7 @@ from omnibase_infra.protocols.protocol_capability_projection import (
|
|
|
62
62
|
ProtocolCapabilityProjection,
|
|
63
63
|
)
|
|
64
64
|
from omnibase_infra.protocols.protocol_capability_query import ProtocolCapabilityQuery
|
|
65
|
+
from omnibase_infra.protocols.protocol_container_aware import ProtocolContainerAware
|
|
65
66
|
from omnibase_infra.protocols.protocol_event_bus_like import ProtocolEventBusLike
|
|
66
67
|
from omnibase_infra.protocols.protocol_event_projector import ProtocolEventProjector
|
|
67
68
|
from omnibase_infra.protocols.protocol_idempotency_store import (
|
|
@@ -88,6 +89,7 @@ __all__: list[str] = [
|
|
|
88
89
|
"ProtocolCapabilityQuery",
|
|
89
90
|
"ProtocolEventBusLike",
|
|
90
91
|
"ProtocolEventProjector",
|
|
92
|
+
"ProtocolContainerAware",
|
|
91
93
|
"ProtocolIdempotencyStore",
|
|
92
94
|
"ProtocolMessageDispatcher",
|
|
93
95
|
"ProtocolMessageTypeRegistry",
|