omnibase_infra 0.2.5__py3-none-any.whl → 0.2.7__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/constants_topic_patterns.py +26 -0
- omnibase_infra/enums/__init__.py +3 -0
- omnibase_infra/enums/enum_consumer_group_purpose.py +92 -0
- omnibase_infra/enums/enum_handler_source_mode.py +16 -2
- omnibase_infra/errors/__init__.py +4 -0
- omnibase_infra/errors/error_binding_resolution.py +128 -0
- omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +0 -2
- omnibase_infra/event_bus/event_bus_inmemory.py +64 -10
- omnibase_infra/event_bus/event_bus_kafka.py +105 -47
- omnibase_infra/event_bus/mixin_kafka_broadcast.py +3 -7
- omnibase_infra/event_bus/mixin_kafka_dlq.py +12 -6
- omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +0 -81
- omnibase_infra/event_bus/testing/__init__.py +26 -0
- omnibase_infra/event_bus/testing/adapter_protocol_event_publisher_inmemory.py +418 -0
- omnibase_infra/event_bus/testing/model_publisher_metrics.py +64 -0
- omnibase_infra/handlers/handler_consul.py +2 -0
- omnibase_infra/handlers/mixins/__init__.py +5 -0
- omnibase_infra/handlers/mixins/mixin_consul_service.py +274 -10
- omnibase_infra/handlers/mixins/mixin_consul_topic_index.py +585 -0
- omnibase_infra/handlers/models/model_filesystem_config.py +4 -4
- omnibase_infra/migrations/001_create_event_ledger.sql +166 -0
- omnibase_infra/migrations/001_drop_event_ledger.sql +18 -0
- omnibase_infra/mixins/mixin_node_introspection.py +189 -19
- omnibase_infra/models/__init__.py +8 -0
- omnibase_infra/models/bindings/__init__.py +59 -0
- omnibase_infra/models/bindings/constants.py +144 -0
- omnibase_infra/models/bindings/model_binding_resolution_result.py +103 -0
- omnibase_infra/models/bindings/model_operation_binding.py +44 -0
- omnibase_infra/models/bindings/model_operation_bindings_subcontract.py +152 -0
- omnibase_infra/models/bindings/model_parsed_binding.py +52 -0
- omnibase_infra/models/discovery/model_introspection_config.py +25 -17
- omnibase_infra/models/dispatch/__init__.py +8 -0
- omnibase_infra/models/dispatch/model_debug_trace_snapshot.py +114 -0
- omnibase_infra/models/dispatch/model_materialized_dispatch.py +141 -0
- omnibase_infra/models/handlers/model_handler_source_config.py +1 -1
- omnibase_infra/models/model_node_identity.py +126 -0
- omnibase_infra/models/projection/model_snapshot_topic_config.py +3 -2
- omnibase_infra/models/registration/__init__.py +9 -0
- omnibase_infra/models/registration/model_event_bus_topic_entry.py +59 -0
- omnibase_infra/models/registration/model_node_event_bus_config.py +99 -0
- omnibase_infra/models/registration/model_node_introspection_event.py +11 -0
- omnibase_infra/models/runtime/__init__.py +9 -0
- omnibase_infra/models/validation/model_coverage_metrics.py +2 -2
- omnibase_infra/nodes/__init__.py +9 -0
- omnibase_infra/nodes/contract_registry_reducer/__init__.py +29 -0
- omnibase_infra/nodes/contract_registry_reducer/contract.yaml +255 -0
- omnibase_infra/nodes/contract_registry_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_contract_registry_state.py +266 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_cleanup_topic_references.py +55 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_deactivate_contract.py +58 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_mark_stale.py +49 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_heartbeat.py +71 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_topic.py +66 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_upsert_contract.py +92 -0
- omnibase_infra/nodes/contract_registry_reducer/node.py +121 -0
- omnibase_infra/nodes/contract_registry_reducer/reducer.py +784 -0
- omnibase_infra/nodes/contract_registry_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/contract_registry_reducer/registry/registry_infra_contract_registry_reducer.py +101 -0
- omnibase_infra/nodes/handlers/consul/contract.yaml +85 -0
- omnibase_infra/nodes/handlers/db/contract.yaml +72 -0
- omnibase_infra/nodes/handlers/graph/contract.yaml +127 -0
- omnibase_infra/nodes/handlers/http/contract.yaml +74 -0
- omnibase_infra/nodes/handlers/intent/contract.yaml +66 -0
- omnibase_infra/nodes/handlers/mcp/contract.yaml +69 -0
- omnibase_infra/nodes/handlers/vault/contract.yaml +91 -0
- omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +50 -0
- omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +104 -0
- omnibase_infra/nodes/node_ledger_projection_compute/node.py +284 -0
- omnibase_infra/nodes/node_ledger_projection_compute/registry/__init__.py +29 -0
- omnibase_infra/nodes/node_ledger_projection_compute/registry/registry_infra_ledger_projection.py +118 -0
- omnibase_infra/nodes/node_ledger_write_effect/__init__.py +82 -0
- omnibase_infra/nodes/node_ledger_write_effect/contract.yaml +200 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/__init__.py +22 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_append.py +372 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_query.py +597 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/__init__.py +31 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_append_result.py +54 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_entry.py +92 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query.py +53 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query_result.py +41 -0
- omnibase_infra/nodes/node_ledger_write_effect/node.py +89 -0
- omnibase_infra/nodes/node_ledger_write_effect/protocols/__init__.py +13 -0
- omnibase_infra/nodes/node_ledger_write_effect/protocols/protocol_ledger_persistence.py +127 -0
- omnibase_infra/nodes/node_ledger_write_effect/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_ledger_write_effect/registry/registry_infra_ledger_write.py +121 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +7 -5
- omnibase_infra/nodes/reducers/models/__init__.py +7 -2
- omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +11 -0
- omnibase_infra/nodes/reducers/models/model_payload_ledger_append.py +133 -0
- omnibase_infra/nodes/reducers/registration_reducer.py +1 -0
- omnibase_infra/protocols/__init__.py +3 -0
- omnibase_infra/protocols/protocol_dispatch_engine.py +152 -0
- omnibase_infra/runtime/__init__.py +60 -0
- omnibase_infra/runtime/binding_resolver.py +753 -0
- omnibase_infra/runtime/constants_security.py +70 -0
- omnibase_infra/runtime/contract_loaders/__init__.py +9 -0
- omnibase_infra/runtime/contract_loaders/operation_bindings_loader.py +789 -0
- omnibase_infra/runtime/emit_daemon/__init__.py +97 -0
- omnibase_infra/runtime/emit_daemon/cli.py +844 -0
- omnibase_infra/runtime/emit_daemon/client.py +811 -0
- omnibase_infra/runtime/emit_daemon/config.py +535 -0
- omnibase_infra/runtime/emit_daemon/daemon.py +812 -0
- omnibase_infra/runtime/emit_daemon/event_registry.py +477 -0
- omnibase_infra/runtime/emit_daemon/model_daemon_request.py +139 -0
- omnibase_infra/runtime/emit_daemon/model_daemon_response.py +191 -0
- omnibase_infra/runtime/emit_daemon/queue.py +618 -0
- omnibase_infra/runtime/event_bus_subcontract_wiring.py +466 -0
- omnibase_infra/runtime/handler_source_resolver.py +43 -2
- omnibase_infra/runtime/kafka_contract_source.py +984 -0
- omnibase_infra/runtime/models/__init__.py +13 -0
- omnibase_infra/runtime/models/model_contract_load_result.py +224 -0
- omnibase_infra/runtime/models/model_runtime_contract_config.py +268 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_config.py +4 -3
- omnibase_infra/runtime/models/model_security_config.py +109 -0
- omnibase_infra/runtime/publisher_topic_scoped.py +294 -0
- omnibase_infra/runtime/runtime_contract_config_loader.py +406 -0
- omnibase_infra/runtime/service_kernel.py +76 -6
- omnibase_infra/runtime/service_message_dispatch_engine.py +558 -15
- omnibase_infra/runtime/service_runtime_host_process.py +770 -20
- omnibase_infra/runtime/transition_notification_publisher.py +3 -2
- omnibase_infra/runtime/util_wiring.py +206 -62
- omnibase_infra/services/mcp/service_mcp_tool_sync.py +27 -9
- omnibase_infra/services/session/config_consumer.py +25 -8
- omnibase_infra/services/session/config_store.py +2 -2
- omnibase_infra/services/session/consumer.py +1 -1
- omnibase_infra/topics/__init__.py +45 -0
- omnibase_infra/topics/platform_topic_suffixes.py +140 -0
- omnibase_infra/topics/util_topic_composition.py +95 -0
- omnibase_infra/types/typed_dict/__init__.py +9 -1
- omnibase_infra/types/typed_dict/typed_dict_envelope_build_params.py +115 -0
- omnibase_infra/utils/__init__.py +9 -0
- omnibase_infra/utils/util_consumer_group.py +232 -0
- omnibase_infra/validation/infra_validators.py +18 -1
- omnibase_infra/validation/validation_exemptions.yaml +192 -0
- {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/METADATA +3 -3
- {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/RECORD +139 -52
- {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/entry_points.txt +1 -0
- {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/licenses/LICENSE +0 -0
|
@@ -56,6 +56,7 @@ Exports:
|
|
|
56
56
|
ModelBindingConfigCacheStats: Cache statistics for BindingConfigResolver
|
|
57
57
|
ModelBindingConfigResolverConfig: Configuration for BindingConfigResolver
|
|
58
58
|
ModelConfigCacheEntry: Internal cache entry for BindingConfigResolver
|
|
59
|
+
ModelSecurityConfig: Security configuration for handler namespace allowlisting
|
|
59
60
|
"""
|
|
60
61
|
|
|
61
62
|
# Re-export from omnibase_core for convenience
|
|
@@ -81,6 +82,9 @@ from omnibase_infra.runtime.models.model_config_ref import ModelConfigRef
|
|
|
81
82
|
from omnibase_infra.runtime.models.model_config_ref_parse_result import (
|
|
82
83
|
ModelConfigRefParseResult,
|
|
83
84
|
)
|
|
85
|
+
from omnibase_infra.runtime.models.model_contract_load_result import (
|
|
86
|
+
ModelContractLoadResult,
|
|
87
|
+
)
|
|
84
88
|
from omnibase_infra.runtime.models.model_domain_plugin_config import (
|
|
85
89
|
ModelDomainPluginConfig,
|
|
86
90
|
)
|
|
@@ -128,6 +132,9 @@ from omnibase_infra.runtime.models.model_protocol_registration_config import (
|
|
|
128
132
|
)
|
|
129
133
|
from omnibase_infra.runtime.models.model_retry_policy import ModelRetryPolicy
|
|
130
134
|
from omnibase_infra.runtime.models.model_runtime_config import ModelRuntimeConfig
|
|
135
|
+
from omnibase_infra.runtime.models.model_runtime_contract_config import (
|
|
136
|
+
ModelRuntimeContractConfig,
|
|
137
|
+
)
|
|
131
138
|
from omnibase_infra.runtime.models.model_runtime_scheduler_config import (
|
|
132
139
|
ModelRuntimeSchedulerConfig,
|
|
133
140
|
)
|
|
@@ -148,6 +155,7 @@ from omnibase_infra.runtime.models.model_secret_source_spec import (
|
|
|
148
155
|
ModelSecretSourceSpec,
|
|
149
156
|
SecretSourceType,
|
|
150
157
|
)
|
|
158
|
+
from omnibase_infra.runtime.models.model_security_config import ModelSecurityConfig
|
|
151
159
|
from omnibase_infra.runtime.models.model_shutdown_batch_result import (
|
|
152
160
|
ModelShutdownBatchResult,
|
|
153
161
|
)
|
|
@@ -213,4 +221,9 @@ __all__: list[str] = [
|
|
|
213
221
|
"ModelTransitionNotificationOutboxMetrics",
|
|
214
222
|
"ModelTransitionNotificationPublisherMetrics",
|
|
215
223
|
"SecretSourceType",
|
|
224
|
+
# Contract loading models (OMN-1519)
|
|
225
|
+
"ModelContractLoadResult",
|
|
226
|
+
"ModelRuntimeContractConfig",
|
|
227
|
+
# Security configuration (OMN-1519)
|
|
228
|
+
"ModelSecurityConfig",
|
|
216
229
|
]
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Contract load result model for tracking individual contract loading.
|
|
4
|
+
|
|
5
|
+
This module provides the ModelContractLoadResult class for representing
|
|
6
|
+
the outcome of loading a single contract.yaml file, including any subcontracts
|
|
7
|
+
(handler_routing, operation_bindings) that were loaded from it.
|
|
8
|
+
|
|
9
|
+
Part of OMN-1519: Runtime contract config loader.
|
|
10
|
+
|
|
11
|
+
Design Pattern:
|
|
12
|
+
ModelContractLoadResult captures both successful and failed contract
|
|
13
|
+
loading attempts, allowing the runtime to continue loading other
|
|
14
|
+
contracts even if one fails. Errors are collected rather than raised
|
|
15
|
+
immediately.
|
|
16
|
+
|
|
17
|
+
Thread Safety:
|
|
18
|
+
ModelContractLoadResult is immutable (frozen=True) after creation,
|
|
19
|
+
making it thread-safe for concurrent read access.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
>>> from pathlib import Path
|
|
23
|
+
>>> from omnibase_infra.runtime.models import ModelContractLoadResult
|
|
24
|
+
>>>
|
|
25
|
+
>>> # Successful load
|
|
26
|
+
>>> result = ModelContractLoadResult.succeeded(
|
|
27
|
+
... contract_path=Path("nodes/auth/contract.yaml"),
|
|
28
|
+
... handler_routing=routing_subcontract,
|
|
29
|
+
... operation_bindings=bindings_subcontract,
|
|
30
|
+
... )
|
|
31
|
+
>>> result.success
|
|
32
|
+
True
|
|
33
|
+
>>>
|
|
34
|
+
>>> # Failed load
|
|
35
|
+
>>> result = ModelContractLoadResult.failed(
|
|
36
|
+
... contract_path=Path("nodes/broken/contract.yaml"),
|
|
37
|
+
... error="Invalid YAML syntax",
|
|
38
|
+
... )
|
|
39
|
+
>>> result.success
|
|
40
|
+
False
|
|
41
|
+
|
|
42
|
+
.. versionadded:: 0.2.8
|
|
43
|
+
Created as part of OMN-1519.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
from __future__ import annotations
|
|
47
|
+
|
|
48
|
+
from pathlib import Path
|
|
49
|
+
from uuid import UUID
|
|
50
|
+
|
|
51
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
52
|
+
|
|
53
|
+
from omnibase_infra.models.bindings import ModelOperationBindingsSubcontract
|
|
54
|
+
from omnibase_infra.models.routing import ModelRoutingSubcontract
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ModelContractLoadResult(BaseModel):
|
|
58
|
+
"""Result of loading a single contract.yaml file.
|
|
59
|
+
|
|
60
|
+
Captures the outcome of loading a contract, including any subcontracts
|
|
61
|
+
that were successfully loaded or any errors that occurred.
|
|
62
|
+
|
|
63
|
+
Attributes:
|
|
64
|
+
contract_path: Path to the contract.yaml file that was loaded.
|
|
65
|
+
success: True if the contract was loaded successfully.
|
|
66
|
+
error: Error message if loading failed, empty string otherwise.
|
|
67
|
+
handler_routing: Loaded handler routing subcontract, or None if not present.
|
|
68
|
+
operation_bindings: Loaded operation bindings subcontract, or None if not present.
|
|
69
|
+
correlation_id: Correlation ID for tracing this load operation.
|
|
70
|
+
|
|
71
|
+
Example:
|
|
72
|
+
>>> result = ModelContractLoadResult.succeeded(
|
|
73
|
+
... contract_path=Path("nodes/auth/contract.yaml"),
|
|
74
|
+
... handler_routing=routing,
|
|
75
|
+
... operation_bindings=bindings,
|
|
76
|
+
... )
|
|
77
|
+
>>> result.success
|
|
78
|
+
True
|
|
79
|
+
>>> result.handler_routing is not None
|
|
80
|
+
True
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
model_config = ConfigDict(
|
|
84
|
+
frozen=True,
|
|
85
|
+
extra="forbid",
|
|
86
|
+
arbitrary_types_allowed=True,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
contract_path: Path = Field(
|
|
90
|
+
...,
|
|
91
|
+
description="Path to the contract.yaml file that was loaded.",
|
|
92
|
+
)
|
|
93
|
+
success: bool = Field(
|
|
94
|
+
...,
|
|
95
|
+
description="True if the contract was loaded successfully.",
|
|
96
|
+
)
|
|
97
|
+
error: str = Field(
|
|
98
|
+
default="",
|
|
99
|
+
description="Error message if loading failed, empty string otherwise.",
|
|
100
|
+
)
|
|
101
|
+
handler_routing: ModelRoutingSubcontract | None = Field(
|
|
102
|
+
default=None,
|
|
103
|
+
description="Loaded handler routing subcontract, or None if not present.",
|
|
104
|
+
)
|
|
105
|
+
operation_bindings: ModelOperationBindingsSubcontract | None = Field(
|
|
106
|
+
default=None,
|
|
107
|
+
description="Loaded operation bindings subcontract, or None if not present.",
|
|
108
|
+
)
|
|
109
|
+
correlation_id: UUID | None = Field(
|
|
110
|
+
default=None,
|
|
111
|
+
description="Correlation ID for tracing this load operation.",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def has_error(self) -> bool:
|
|
116
|
+
"""Check if an error message exists.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
True if error is non-empty, False otherwise.
|
|
120
|
+
"""
|
|
121
|
+
return bool(self.error)
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def has_handler_routing(self) -> bool:
|
|
125
|
+
"""Check if handler routing was loaded.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
True if handler_routing is not None.
|
|
129
|
+
"""
|
|
130
|
+
return self.handler_routing is not None
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def has_operation_bindings(self) -> bool:
|
|
134
|
+
"""Check if operation bindings were loaded.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
True if operation_bindings is not None.
|
|
138
|
+
"""
|
|
139
|
+
return self.operation_bindings is not None
|
|
140
|
+
|
|
141
|
+
@classmethod
|
|
142
|
+
def succeeded(
|
|
143
|
+
cls,
|
|
144
|
+
contract_path: Path,
|
|
145
|
+
handler_routing: ModelRoutingSubcontract | None = None,
|
|
146
|
+
operation_bindings: ModelOperationBindingsSubcontract | None = None,
|
|
147
|
+
correlation_id: UUID | None = None,
|
|
148
|
+
) -> ModelContractLoadResult:
|
|
149
|
+
"""Create a successful contract load result.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
contract_path: Path to the contract.yaml file.
|
|
153
|
+
handler_routing: Loaded handler routing subcontract, if present.
|
|
154
|
+
operation_bindings: Loaded operation bindings subcontract, if present.
|
|
155
|
+
correlation_id: Optional correlation ID for tracing.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
ModelContractLoadResult indicating success.
|
|
159
|
+
"""
|
|
160
|
+
return cls(
|
|
161
|
+
contract_path=contract_path,
|
|
162
|
+
success=True,
|
|
163
|
+
error="",
|
|
164
|
+
handler_routing=handler_routing,
|
|
165
|
+
operation_bindings=operation_bindings,
|
|
166
|
+
correlation_id=correlation_id,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
@classmethod
|
|
170
|
+
def failed(
|
|
171
|
+
cls,
|
|
172
|
+
contract_path: Path,
|
|
173
|
+
error: str,
|
|
174
|
+
correlation_id: UUID | None = None,
|
|
175
|
+
) -> ModelContractLoadResult:
|
|
176
|
+
"""Create a failed contract load result.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
contract_path: Path to the contract.yaml file.
|
|
180
|
+
error: Description of the error that occurred.
|
|
181
|
+
correlation_id: Optional correlation ID for tracing.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
ModelContractLoadResult indicating failure with error details.
|
|
185
|
+
"""
|
|
186
|
+
return cls(
|
|
187
|
+
contract_path=contract_path,
|
|
188
|
+
success=False,
|
|
189
|
+
error=error,
|
|
190
|
+
handler_routing=None,
|
|
191
|
+
operation_bindings=None,
|
|
192
|
+
correlation_id=correlation_id,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
def __bool__(self) -> bool:
|
|
196
|
+
"""Allow using result in boolean context.
|
|
197
|
+
|
|
198
|
+
Warning:
|
|
199
|
+
**Non-standard __bool__ behavior**: This model overrides ``__bool__`` to
|
|
200
|
+
return ``True`` only when ``success`` is True.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
True if contract was loaded successfully, False otherwise.
|
|
204
|
+
"""
|
|
205
|
+
return self.success
|
|
206
|
+
|
|
207
|
+
def __str__(self) -> str:
|
|
208
|
+
"""Return a human-readable string representation.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
String describing the load result.
|
|
212
|
+
"""
|
|
213
|
+
if self.success:
|
|
214
|
+
parts = []
|
|
215
|
+
if self.has_handler_routing:
|
|
216
|
+
parts.append("handler_routing")
|
|
217
|
+
if self.has_operation_bindings:
|
|
218
|
+
parts.append("operation_bindings")
|
|
219
|
+
loaded = ", ".join(parts) if parts else "no subcontracts"
|
|
220
|
+
return f"ModelContractLoadResult({self.contract_path}: {loaded})"
|
|
221
|
+
return f"ModelContractLoadResult({self.contract_path}: FAILED - {self.error})"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
__all__ = ["ModelContractLoadResult"]
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Runtime contract configuration model for consolidated contract loading.
|
|
4
|
+
|
|
5
|
+
This module provides the ModelRuntimeContractConfig class for representing
|
|
6
|
+
the consolidated result of loading all contract.yaml files at startup.
|
|
7
|
+
|
|
8
|
+
Part of OMN-1519: Runtime contract config loader.
|
|
9
|
+
|
|
10
|
+
Design Pattern:
|
|
11
|
+
ModelRuntimeContractConfig aggregates results from loading multiple
|
|
12
|
+
contract.yaml files, providing a single source of truth for all
|
|
13
|
+
handler routing and operation bindings configuration.
|
|
14
|
+
|
|
15
|
+
Thread Safety:
|
|
16
|
+
ModelRuntimeContractConfig is immutable (frozen=True) after creation,
|
|
17
|
+
making it thread-safe for concurrent read access.
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
>>> from omnibase_infra.runtime.models import ModelRuntimeContractConfig
|
|
21
|
+
>>>
|
|
22
|
+
>>> config = ModelRuntimeContractConfig(
|
|
23
|
+
... contract_results=[result1, result2],
|
|
24
|
+
... total_contracts_found=5,
|
|
25
|
+
... total_contracts_loaded=4,
|
|
26
|
+
... total_errors=1,
|
|
27
|
+
... )
|
|
28
|
+
>>> config.success_rate
|
|
29
|
+
0.8
|
|
30
|
+
|
|
31
|
+
.. versionadded:: 0.2.8
|
|
32
|
+
Created as part of OMN-1519.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
from uuid import UUID
|
|
39
|
+
|
|
40
|
+
from pydantic import BaseModel, ConfigDict, Field, computed_field
|
|
41
|
+
|
|
42
|
+
from omnibase_infra.models.bindings import ModelOperationBindingsSubcontract
|
|
43
|
+
from omnibase_infra.models.routing import ModelRoutingSubcontract
|
|
44
|
+
from omnibase_infra.runtime.models.model_contract_load_result import (
|
|
45
|
+
ModelContractLoadResult,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ModelRuntimeContractConfig(BaseModel):
|
|
50
|
+
"""Consolidated runtime contract configuration from all loaded contracts.
|
|
51
|
+
|
|
52
|
+
Aggregates the results of loading multiple contract.yaml files,
|
|
53
|
+
providing access to all handler routing and operation bindings
|
|
54
|
+
configuration loaded at startup.
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
contract_results: List of individual contract load results.
|
|
58
|
+
total_contracts_found: Number of contract.yaml files discovered.
|
|
59
|
+
total_contracts_loaded: Number of contracts successfully loaded.
|
|
60
|
+
total_errors: Number of contracts that failed to load.
|
|
61
|
+
correlation_id: Correlation ID for tracing this load session.
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
>>> config = ModelRuntimeContractConfig(
|
|
65
|
+
... contract_results=[result1, result2],
|
|
66
|
+
... total_contracts_found=2,
|
|
67
|
+
... total_contracts_loaded=2,
|
|
68
|
+
... total_errors=0,
|
|
69
|
+
... )
|
|
70
|
+
>>> config.all_successful
|
|
71
|
+
True
|
|
72
|
+
>>> len(config.handler_routing_configs)
|
|
73
|
+
2
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
model_config = ConfigDict(
|
|
77
|
+
frozen=True,
|
|
78
|
+
extra="forbid",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
contract_results: list[ModelContractLoadResult] = Field(
|
|
82
|
+
default_factory=list,
|
|
83
|
+
description="List of individual contract load results.",
|
|
84
|
+
)
|
|
85
|
+
total_contracts_found: int = Field(
|
|
86
|
+
default=0,
|
|
87
|
+
ge=0,
|
|
88
|
+
description="Number of contract.yaml files discovered.",
|
|
89
|
+
)
|
|
90
|
+
total_contracts_loaded: int = Field(
|
|
91
|
+
default=0,
|
|
92
|
+
ge=0,
|
|
93
|
+
description="Number of contracts successfully loaded.",
|
|
94
|
+
)
|
|
95
|
+
total_errors: int = Field(
|
|
96
|
+
default=0,
|
|
97
|
+
ge=0,
|
|
98
|
+
description="Number of contracts that failed to load.",
|
|
99
|
+
)
|
|
100
|
+
correlation_id: UUID | None = Field(
|
|
101
|
+
default=None,
|
|
102
|
+
description="Correlation ID for tracing this load session.",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
@computed_field # type: ignore[prop-decorator]
|
|
106
|
+
@property
|
|
107
|
+
def all_successful(self) -> bool:
|
|
108
|
+
"""Check if all contracts were loaded successfully.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
True if total_errors is 0 and at least one contract was found.
|
|
112
|
+
"""
|
|
113
|
+
return self.total_errors == 0 and self.total_contracts_found > 0
|
|
114
|
+
|
|
115
|
+
@computed_field # type: ignore[prop-decorator]
|
|
116
|
+
@property
|
|
117
|
+
def success_rate(self) -> float:
|
|
118
|
+
"""Calculate the success rate of contract loading.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Ratio of successfully loaded contracts to total found (0.0-1.0).
|
|
122
|
+
Returns 1.0 if no contracts were found.
|
|
123
|
+
"""
|
|
124
|
+
if self.total_contracts_found == 0:
|
|
125
|
+
return 1.0
|
|
126
|
+
return self.total_contracts_loaded / self.total_contracts_found
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def successful_results(self) -> list[ModelContractLoadResult]:
|
|
130
|
+
"""Get all successful contract load results.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
List of ModelContractLoadResult where success is True.
|
|
134
|
+
"""
|
|
135
|
+
return [r for r in self.contract_results if r.success]
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def failed_results(self) -> list[ModelContractLoadResult]:
|
|
139
|
+
"""Get all failed contract load results.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
List of ModelContractLoadResult where success is False.
|
|
143
|
+
"""
|
|
144
|
+
return [r for r in self.contract_results if not r.success]
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def handler_routing_configs(self) -> dict[Path, ModelRoutingSubcontract]:
|
|
148
|
+
"""Get all loaded handler routing configurations.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Mapping of contract path to handler routing subcontract.
|
|
152
|
+
"""
|
|
153
|
+
return {
|
|
154
|
+
r.contract_path: r.handler_routing
|
|
155
|
+
for r in self.contract_results
|
|
156
|
+
if r.handler_routing is not None
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def operation_bindings_configs(
|
|
161
|
+
self,
|
|
162
|
+
) -> dict[Path, ModelOperationBindingsSubcontract]:
|
|
163
|
+
"""Get all loaded operation bindings configurations.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Mapping of contract path to operation bindings subcontract.
|
|
167
|
+
"""
|
|
168
|
+
return {
|
|
169
|
+
r.contract_path: r.operation_bindings
|
|
170
|
+
for r in self.contract_results
|
|
171
|
+
if r.operation_bindings is not None
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def error_messages(self) -> dict[Path, str]:
|
|
176
|
+
"""Get all error messages from failed loads.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Mapping of contract path to error message.
|
|
180
|
+
"""
|
|
181
|
+
return {r.contract_path: r.error for r in self.contract_results if r.error}
|
|
182
|
+
|
|
183
|
+
def get_routing_for_contract(
|
|
184
|
+
self, contract_path: Path
|
|
185
|
+
) -> ModelRoutingSubcontract | None:
|
|
186
|
+
"""Get handler routing for a specific contract.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
contract_path: Path to the contract.yaml file.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
ModelRoutingSubcontract if found, None otherwise.
|
|
193
|
+
"""
|
|
194
|
+
for result in self.contract_results:
|
|
195
|
+
if result.contract_path == contract_path:
|
|
196
|
+
return result.handler_routing
|
|
197
|
+
return None
|
|
198
|
+
|
|
199
|
+
def get_bindings_for_contract(
|
|
200
|
+
self, contract_path: Path
|
|
201
|
+
) -> ModelOperationBindingsSubcontract | None:
|
|
202
|
+
"""Get operation bindings for a specific contract.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
contract_path: Path to the contract.yaml file.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
ModelOperationBindingsSubcontract if found, None otherwise.
|
|
209
|
+
"""
|
|
210
|
+
for result in self.contract_results:
|
|
211
|
+
if result.contract_path == contract_path:
|
|
212
|
+
return result.operation_bindings
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
def __bool__(self) -> bool:
|
|
216
|
+
"""Allow using config in boolean context.
|
|
217
|
+
|
|
218
|
+
Warning:
|
|
219
|
+
**Non-standard __bool__ behavior**: This model overrides ``__bool__`` to
|
|
220
|
+
return ``True`` only when all contracts were loaded successfully.
|
|
221
|
+
|
|
222
|
+
Note:
|
|
223
|
+
**Edge case - no contracts found**: Returns ``False`` when
|
|
224
|
+
``total_contracts_found == 0``, even if ``total_errors == 0``.
|
|
225
|
+
This is intentional: an empty result (no contracts discovered)
|
|
226
|
+
should trigger explicit handling by the caller rather than
|
|
227
|
+
silently proceeding as if everything succeeded.
|
|
228
|
+
|
|
229
|
+
Example:
|
|
230
|
+
>>> # Empty config with no errors still returns False
|
|
231
|
+
>>> config = ModelRuntimeContractConfig()
|
|
232
|
+
>>> bool(config)
|
|
233
|
+
False
|
|
234
|
+
>>> config.total_errors
|
|
235
|
+
0
|
|
236
|
+
>>> config.total_contracts_found
|
|
237
|
+
0
|
|
238
|
+
>>>
|
|
239
|
+
>>> # Only returns True when contracts are found AND no errors
|
|
240
|
+
>>> config_with_contracts = ModelRuntimeContractConfig(
|
|
241
|
+
... total_contracts_found=1,
|
|
242
|
+
... total_contracts_loaded=1,
|
|
243
|
+
... )
|
|
244
|
+
>>> bool(config_with_contracts)
|
|
245
|
+
True
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
True if all contracts loaded successfully (requires at least one
|
|
249
|
+
contract found and zero errors), False otherwise.
|
|
250
|
+
"""
|
|
251
|
+
return self.all_successful
|
|
252
|
+
|
|
253
|
+
def __str__(self) -> str:
|
|
254
|
+
"""Return a human-readable summary.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
String summarizing the load results.
|
|
258
|
+
"""
|
|
259
|
+
return (
|
|
260
|
+
f"ModelRuntimeContractConfig("
|
|
261
|
+
f"found={self.total_contracts_found}, "
|
|
262
|
+
f"loaded={self.total_contracts_loaded}, "
|
|
263
|
+
f"errors={self.total_errors}, "
|
|
264
|
+
f"success_rate={self.success_rate:.1%})"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
__all__ = ["ModelRuntimeContractConfig"]
|
|
@@ -28,7 +28,7 @@ Environment Variables:
|
|
|
28
28
|
Example: "runtime-scheduler-prod-1"
|
|
29
29
|
|
|
30
30
|
ONEX_RUNTIME_SCHEDULER_TICK_TOPIC: Kafka topic for publishing ticks
|
|
31
|
-
Default:
|
|
31
|
+
Default: SUFFIX_RUNTIME_TICK from omnibase_infra.topics
|
|
32
32
|
Example: "prod.runtime.tick.v1"
|
|
33
33
|
|
|
34
34
|
Restart-Safety Settings:
|
|
@@ -106,6 +106,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
|
106
106
|
|
|
107
107
|
from omnibase_infra.enums import EnumInfraTransportType
|
|
108
108
|
from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationError
|
|
109
|
+
from omnibase_infra.topics import SUFFIX_RUNTIME_TICK
|
|
109
110
|
|
|
110
111
|
logger = logging.getLogger(__name__)
|
|
111
112
|
|
|
@@ -167,7 +168,7 @@ class ModelRuntimeSchedulerConfig(BaseModel):
|
|
|
167
168
|
max_length=255,
|
|
168
169
|
)
|
|
169
170
|
tick_topic: str = Field(
|
|
170
|
-
default=
|
|
171
|
+
default=SUFFIX_RUNTIME_TICK,
|
|
171
172
|
description="Kafka topic for publishing tick events",
|
|
172
173
|
min_length=1,
|
|
173
174
|
max_length=255,
|
|
@@ -604,7 +605,7 @@ class ModelRuntimeSchedulerConfig(BaseModel):
|
|
|
604
605
|
base_config = cls(
|
|
605
606
|
tick_interval_ms=1000,
|
|
606
607
|
scheduler_id="runtime-scheduler-default",
|
|
607
|
-
tick_topic=
|
|
608
|
+
tick_topic=SUFFIX_RUNTIME_TICK,
|
|
608
609
|
persist_sequence_number=True,
|
|
609
610
|
sequence_number_key="runtime_scheduler_sequence",
|
|
610
611
|
max_tick_jitter_ms=100,
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Security Configuration Model for Runtime Handler Loading.
|
|
4
|
+
|
|
5
|
+
This module provides the Pydantic model for configuring trusted handler namespaces.
|
|
6
|
+
The model allows operators to extend the trusted namespace list via configuration
|
|
7
|
+
file while maintaining secure defaults.
|
|
8
|
+
|
|
9
|
+
Security Model:
|
|
10
|
+
- Default: Only omnibase_core. and omnibase_infra. are trusted
|
|
11
|
+
- Third-party: Requires allow_third_party_handlers=True AND
|
|
12
|
+
explicit listing in allowed_handler_namespaces
|
|
13
|
+
- Config file is auditable/reviewable (unlike env vars)
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> from omnibase_infra.runtime.models import ModelSecurityConfig
|
|
17
|
+
>>> config = ModelSecurityConfig() # Secure defaults
|
|
18
|
+
>>> config.get_effective_namespaces()
|
|
19
|
+
('omnibase_core.', 'omnibase_infra.')
|
|
20
|
+
|
|
21
|
+
>>> # Enable third-party handlers
|
|
22
|
+
>>> config = ModelSecurityConfig(
|
|
23
|
+
... allow_third_party_handlers=True,
|
|
24
|
+
... allowed_handler_namespaces=(
|
|
25
|
+
... "omnibase_core.",
|
|
26
|
+
... "omnibase_infra.",
|
|
27
|
+
... "mycompany.handlers.",
|
|
28
|
+
... ),
|
|
29
|
+
... )
|
|
30
|
+
>>> config.get_effective_namespaces()
|
|
31
|
+
('omnibase_core.', 'omnibase_infra.', 'mycompany.handlers.')
|
|
32
|
+
|
|
33
|
+
.. versionadded:: 0.2.8
|
|
34
|
+
Created as part of OMN-1519 security hardening.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
40
|
+
|
|
41
|
+
from omnibase_infra.runtime.constants_security import TRUSTED_HANDLER_NAMESPACE_PREFIXES
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ModelSecurityConfig(BaseModel):
|
|
45
|
+
"""Security configuration for runtime handler loading.
|
|
46
|
+
|
|
47
|
+
This model allows operators to extend the trusted namespace list
|
|
48
|
+
via configuration file. The defaults are secure - third-party
|
|
49
|
+
namespaces require explicit opt-in.
|
|
50
|
+
|
|
51
|
+
Security Model:
|
|
52
|
+
- Default: Only omnibase_core. and omnibase_infra. are trusted
|
|
53
|
+
- Third-party: Requires allow_third_party_handlers=True AND
|
|
54
|
+
explicit listing in allowed_handler_namespaces
|
|
55
|
+
- Config file is auditable/reviewable (unlike env vars)
|
|
56
|
+
|
|
57
|
+
Attributes:
|
|
58
|
+
allow_third_party_handlers: Enable loading handlers from third-party
|
|
59
|
+
namespaces. When False, only TRUSTED_HANDLER_NAMESPACE_PREFIXES
|
|
60
|
+
are allowed regardless of allowed_handler_namespaces setting.
|
|
61
|
+
allowed_handler_namespaces: Allowed namespace prefixes for handler
|
|
62
|
+
loading. Only effective when allow_third_party_handlers=True.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
model_config = ConfigDict(
|
|
66
|
+
strict=True,
|
|
67
|
+
frozen=True,
|
|
68
|
+
extra="forbid",
|
|
69
|
+
from_attributes=True,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
allow_third_party_handlers: bool = Field(
|
|
73
|
+
default=False,
|
|
74
|
+
description="Enable loading handlers from third-party namespaces. "
|
|
75
|
+
"When False, only TRUSTED_HANDLER_NAMESPACE_PREFIXES are allowed.",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
allowed_handler_namespaces: tuple[str, ...] = Field(
|
|
79
|
+
default=TRUSTED_HANDLER_NAMESPACE_PREFIXES,
|
|
80
|
+
description="Allowed namespace prefixes for handler loading. "
|
|
81
|
+
"Only effective when allow_third_party_handlers=True.",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def get_effective_namespaces(self) -> tuple[str, ...]:
|
|
85
|
+
"""Get the effective namespace allowlist based on configuration.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Tuple of allowed namespace prefixes. If third-party handlers
|
|
89
|
+
are disabled, returns only the trusted defaults regardless
|
|
90
|
+
of the allowed_handler_namespaces setting.
|
|
91
|
+
|
|
92
|
+
Example:
|
|
93
|
+
>>> config = ModelSecurityConfig()
|
|
94
|
+
>>> config.get_effective_namespaces()
|
|
95
|
+
('omnibase_core.', 'omnibase_infra.')
|
|
96
|
+
|
|
97
|
+
>>> config = ModelSecurityConfig(
|
|
98
|
+
... allow_third_party_handlers=True,
|
|
99
|
+
... allowed_handler_namespaces=("custom.namespace.",),
|
|
100
|
+
... )
|
|
101
|
+
>>> config.get_effective_namespaces()
|
|
102
|
+
('custom.namespace.',)
|
|
103
|
+
"""
|
|
104
|
+
if not self.allow_third_party_handlers:
|
|
105
|
+
return TRUSTED_HANDLER_NAMESPACE_PREFIXES
|
|
106
|
+
return self.allowed_handler_namespaces
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
__all__: list[str] = ["ModelSecurityConfig"]
|