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
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Topic pattern constants for event bus topic validation.
|
|
4
|
+
|
|
5
|
+
This module provides shared regex patterns for validating event bus topic names.
|
|
6
|
+
These patterns are used across multiple modules (e.g., MixinConsulTopicIndex,
|
|
7
|
+
MixinNodeIntrospection) to ensure consistent topic validation.
|
|
8
|
+
|
|
9
|
+
Note:
|
|
10
|
+
This module is intentionally dependency-free (no imports from omnibase_infra)
|
|
11
|
+
to avoid circular import issues. Keep it that way.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import re
|
|
17
|
+
from typing import Final
|
|
18
|
+
|
|
19
|
+
# Topic name pattern: alphanumeric, underscores, hyphens, and periods only.
|
|
20
|
+
# This matches Kafka/Redpanda topic naming conventions and ensures safe
|
|
21
|
+
# interpolation into Consul KV paths (prevents path traversal via slashes).
|
|
22
|
+
# Pattern: ^[a-zA-Z0-9._-]+$
|
|
23
|
+
TOPIC_NAME_PATTERN: Final[re.Pattern[str]] = re.compile(r"^[a-zA-Z0-9._-]+$")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
__all__: list[str] = ["TOPIC_NAME_PATTERN"]
|
omnibase_infra/enums/__init__.py
CHANGED
|
@@ -16,6 +16,7 @@ Exports:
|
|
|
16
16
|
EnumChainViolationType: Chain violation types for correlation/causation validation
|
|
17
17
|
EnumCircuitState: Circuit breaker states (CLOSED, OPEN, HALF_OPEN)
|
|
18
18
|
EnumConfirmationEventType: Registration confirmation event types
|
|
19
|
+
EnumConsumerGroupPurpose: Consumer group purpose (CONSUME, INTROSPECTION, REPLAY, AUDIT, BACKFILL)
|
|
19
20
|
EnumContractType: Contract types for ONEX nodes (effect, compute, reducer, orchestrator)
|
|
20
21
|
EnumDispatchStatus: Dispatch operation status enumeration
|
|
21
22
|
EnumEnvironment: Deployment environment classification (DEVELOPMENT, STAGING, PRODUCTION, CI)
|
|
@@ -54,6 +55,7 @@ from omnibase_infra.enums.enum_capture_state import EnumCaptureState
|
|
|
54
55
|
from omnibase_infra.enums.enum_chain_violation_type import EnumChainViolationType
|
|
55
56
|
from omnibase_infra.enums.enum_circuit_state import EnumCircuitState
|
|
56
57
|
from omnibase_infra.enums.enum_confirmation_event_type import EnumConfirmationEventType
|
|
58
|
+
from omnibase_infra.enums.enum_consumer_group_purpose import EnumConsumerGroupPurpose
|
|
57
59
|
from omnibase_infra.enums.enum_contract_type import EnumContractType
|
|
58
60
|
from omnibase_infra.enums.enum_dedupe_strategy import EnumDedupeStrategy
|
|
59
61
|
from omnibase_infra.enums.enum_dispatch_status import EnumDispatchStatus
|
|
@@ -97,6 +99,7 @@ __all__: list[str] = [
|
|
|
97
99
|
"EnumChainViolationType",
|
|
98
100
|
"EnumCircuitState",
|
|
99
101
|
"EnumConfirmationEventType",
|
|
102
|
+
"EnumConsumerGroupPurpose",
|
|
100
103
|
"EnumContractType",
|
|
101
104
|
"EnumDedupeStrategy",
|
|
102
105
|
"EnumDispatchStatus",
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Consumer group purpose enumeration.
|
|
4
|
+
|
|
5
|
+
This module defines the purpose classification for Kafka consumer groups,
|
|
6
|
+
enabling semantic differentiation of consumer behavior and offset policies.
|
|
7
|
+
|
|
8
|
+
Consumer Group Purpose Categories:
|
|
9
|
+
- CONSUME: Standard event consumption (default behavior)
|
|
10
|
+
- INTROSPECTION: Node introspection and discovery operations
|
|
11
|
+
- REPLAY: Reprocess historical data from earliest offset
|
|
12
|
+
- AUDIT: Compliance and read-only consumption
|
|
13
|
+
- BACKFILL: One-shot bounded consumers for populating derived state
|
|
14
|
+
|
|
15
|
+
The purpose determines consumer group naming conventions and default
|
|
16
|
+
offset reset policies in the Kafka adapter layer.
|
|
17
|
+
|
|
18
|
+
Thread Safety:
|
|
19
|
+
All enums in this module are immutable and thread-safe.
|
|
20
|
+
Enum values can be safely shared across threads without synchronization.
|
|
21
|
+
|
|
22
|
+
See Also:
|
|
23
|
+
- ModelKafkaConsumerConfig: Consumer configuration using this enum
|
|
24
|
+
- docs/decisions/adr-consumer-group-naming.md: Naming conventions
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from enum import Enum
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class EnumConsumerGroupPurpose(str, Enum):
|
|
33
|
+
"""Consumer group purpose classification for Kafka consumers.
|
|
34
|
+
|
|
35
|
+
Defines the semantic purpose of a consumer group, which influences:
|
|
36
|
+
- Consumer group naming conventions (suffix added to group ID)
|
|
37
|
+
- Default offset reset policy (earliest vs latest)
|
|
38
|
+
- Expected consumption patterns (continuous vs bounded)
|
|
39
|
+
|
|
40
|
+
Values:
|
|
41
|
+
CONSUME: Standard event consumption for normal processing.
|
|
42
|
+
- Default offset reset: latest
|
|
43
|
+
- Naming: {base_group_id}-consume
|
|
44
|
+
- Pattern: Continuous consumption
|
|
45
|
+
|
|
46
|
+
INTROSPECTION: Node introspection and service discovery.
|
|
47
|
+
- Default offset reset: latest
|
|
48
|
+
- Naming: {base_group_id}-introspection
|
|
49
|
+
- Pattern: Targeted discovery queries
|
|
50
|
+
|
|
51
|
+
REPLAY: Reprocess historical data from the beginning.
|
|
52
|
+
- Default offset reset: earliest
|
|
53
|
+
- Naming: {base_group_id}-replay
|
|
54
|
+
- Pattern: Full topic reprocessing
|
|
55
|
+
|
|
56
|
+
AUDIT: Compliance and read-only consumption.
|
|
57
|
+
- Default offset reset: earliest
|
|
58
|
+
- Naming: {base_group_id}-audit
|
|
59
|
+
- Pattern: Full audit trail capture
|
|
60
|
+
|
|
61
|
+
BACKFILL: One-shot bounded consumers for derived state.
|
|
62
|
+
- Default offset reset: earliest
|
|
63
|
+
- Naming: {base_group_id}-backfill
|
|
64
|
+
- Pattern: Bounded consumption until caught up
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
>>> purpose = EnumConsumerGroupPurpose.REPLAY
|
|
68
|
+
>>> f"order-processor-{purpose.value}"
|
|
69
|
+
'order-processor-replay'
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
CONSUME = "consume"
|
|
73
|
+
"""Standard event consumption (default)."""
|
|
74
|
+
|
|
75
|
+
INTROSPECTION = "introspection"
|
|
76
|
+
"""Node introspection and discovery."""
|
|
77
|
+
|
|
78
|
+
REPLAY = "replay"
|
|
79
|
+
"""Reprocess historical data from earliest offset."""
|
|
80
|
+
|
|
81
|
+
AUDIT = "audit"
|
|
82
|
+
"""Compliance/read-only consumption."""
|
|
83
|
+
|
|
84
|
+
BACKFILL = "backfill"
|
|
85
|
+
"""One-shot bounded consumers for populating derived state."""
|
|
86
|
+
|
|
87
|
+
def __str__(self) -> str:
|
|
88
|
+
"""Return the string value for serialization."""
|
|
89
|
+
return self.value
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
__all__: list[str] = ["EnumConsumerGroupPurpose"]
|
|
@@ -6,7 +6,7 @@ Defines the canonical source modes for handler loading in the ONEX runtime.
|
|
|
6
6
|
Each mode determines where handlers are discovered and loaded from, enabling
|
|
7
7
|
flexible deployment configurations and migration strategies.
|
|
8
8
|
|
|
9
|
-
Handler loading can operate in
|
|
9
|
+
Handler loading can operate in four modes:
|
|
10
10
|
- BOOTSTRAP: Only use hardcoded bootstrap handlers.
|
|
11
11
|
Uses the legacy hardcoded handler registry. No YAML contract
|
|
12
12
|
discovery. Useful for minimal deployments or testing.
|
|
@@ -18,6 +18,11 @@ Handler loading can operate in three modes:
|
|
|
18
18
|
When a handler identity matches both bootstrap and contract,
|
|
19
19
|
the contract-defined handler wins. Bootstrap handlers serve
|
|
20
20
|
as fallback for handlers not defined in contracts.
|
|
21
|
+
- KAFKA_EVENTS: Cache-only Kafka-based contract discovery.
|
|
22
|
+
Subscribes to platform-reserved contract topics and
|
|
23
|
+
maintains an in-memory cache of discovered handlers.
|
|
24
|
+
Does NOT wire business subscriptions dynamically.
|
|
25
|
+
For beta: discover + next restart applies model.
|
|
21
26
|
|
|
22
27
|
The HYBRID mode supports gradual migration from hardcoded to contract-based
|
|
23
28
|
handlers by allowing both sources to coexist with deterministic resolution.
|
|
@@ -26,6 +31,7 @@ See Also:
|
|
|
26
31
|
- EnumHandlerSourceType: Defines validation error source types (different purpose)
|
|
27
32
|
- HandlerPluginLoader: Uses this enum to determine loading strategy
|
|
28
33
|
- ModelRuntimeConfig: Configuration model that holds the source mode setting
|
|
34
|
+
- KafkaContractSource: Implements KAFKA_EVENTS mode (OMN-1654)
|
|
29
35
|
"""
|
|
30
36
|
|
|
31
37
|
from enum import Enum
|
|
@@ -36,7 +42,7 @@ class EnumHandlerSourceMode(str, Enum):
|
|
|
36
42
|
|
|
37
43
|
These represent the different strategies for discovering and loading
|
|
38
44
|
handlers at runtime. The mode determines whether handlers come from
|
|
39
|
-
hardcoded registries, YAML contracts, or a combination
|
|
45
|
+
hardcoded registries, YAML contracts, Kafka events, or a combination.
|
|
40
46
|
|
|
41
47
|
Attributes:
|
|
42
48
|
BOOTSTRAP: Only use hardcoded bootstrap handlers.
|
|
@@ -62,11 +68,19 @@ class EnumHandlerSourceMode(str, Enum):
|
|
|
62
68
|
- Core handlers in bootstrap, extensions in contracts
|
|
63
69
|
- Development environments with mixed configurations
|
|
64
70
|
- A/B testing of handler implementations
|
|
71
|
+
KAFKA_EVENTS: Cache-only Kafka-based contract discovery.
|
|
72
|
+
Subscribes to platform-reserved contract topics (baseline-wired)
|
|
73
|
+
and maintains an in-memory cache of discovered handlers. Does NOT
|
|
74
|
+
wire business subscriptions dynamically. Use cases:
|
|
75
|
+
- Distributed contract discovery via event bus
|
|
76
|
+
- Dynamic contract registration/deregistration via Kafka
|
|
77
|
+
- Beta: discover + next restart applies model
|
|
65
78
|
"""
|
|
66
79
|
|
|
67
80
|
BOOTSTRAP = "bootstrap"
|
|
68
81
|
CONTRACT = "contract"
|
|
69
82
|
HYBRID = "hybrid"
|
|
83
|
+
KAFKA_EVENTS = "kafka_events"
|
|
70
84
|
|
|
71
85
|
|
|
72
86
|
__all__ = ["EnumHandlerSourceMode"]
|
|
@@ -22,6 +22,7 @@ Exports:
|
|
|
22
22
|
EventBusRegistryError: Event bus registry operation errors
|
|
23
23
|
ChainPropagationError: Correlation/causation chain validation errors
|
|
24
24
|
ArchitectureViolationError: Architecture validation errors (blocks startup)
|
|
25
|
+
BindingResolutionError: Binding resolution errors (declarative operation bindings)
|
|
25
26
|
|
|
26
27
|
Correlation ID Assignment:
|
|
27
28
|
All infrastructure errors support correlation_id for distributed tracing.
|
|
@@ -90,6 +91,7 @@ Error Sanitization Guidelines:
|
|
|
90
91
|
from omnibase_infra.errors.error_architecture_violation import (
|
|
91
92
|
ArchitectureViolationError,
|
|
92
93
|
)
|
|
94
|
+
from omnibase_infra.errors.error_binding_resolution import BindingResolutionError
|
|
93
95
|
from omnibase_infra.errors.error_chain_propagation import ChainPropagationError
|
|
94
96
|
from omnibase_infra.errors.error_compute_registry import ComputeRegistryError
|
|
95
97
|
from omnibase_infra.errors.error_consul import InfraConsulError
|
|
@@ -125,6 +127,8 @@ from omnibase_infra.models.errors.model_timeout_error_context import (
|
|
|
125
127
|
__all__: list[str] = [
|
|
126
128
|
# Architecture validation errors
|
|
127
129
|
"ArchitectureViolationError",
|
|
130
|
+
# Binding resolution errors
|
|
131
|
+
"BindingResolutionError",
|
|
128
132
|
"ChainPropagationError",
|
|
129
133
|
"ComputeRegistryError",
|
|
130
134
|
"ContainerValidationError",
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Binding Resolution Error for declarative operation bindings.
|
|
4
|
+
|
|
5
|
+
This module defines the error raised when binding resolution fails during
|
|
6
|
+
declarative operation processing. Bindings use ${source.path} expressions
|
|
7
|
+
to extract values from envelopes, payloads, or context.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from uuid import UUID
|
|
11
|
+
|
|
12
|
+
from omnibase_core.enums import EnumCoreErrorCode
|
|
13
|
+
from omnibase_infra.enums import EnumInfraTransportType
|
|
14
|
+
from omnibase_infra.errors.error_infra import RuntimeHostError
|
|
15
|
+
from omnibase_infra.models.errors.model_infra_error_context import (
|
|
16
|
+
ModelInfraErrorContext,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BindingResolutionError(RuntimeHostError):
|
|
21
|
+
"""Error when binding resolution fails.
|
|
22
|
+
|
|
23
|
+
Raised when a required binding cannot be resolved from the envelope,
|
|
24
|
+
payload, or context. Includes full diagnostic context for debugging.
|
|
25
|
+
|
|
26
|
+
This error is typically raised during declarative operation processing
|
|
27
|
+
when a ${source.path} expression cannot be resolved because:
|
|
28
|
+
- The source (payload, envelope, context) is missing
|
|
29
|
+
- A path segment does not exist in the source
|
|
30
|
+
- The value is required but None
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
operation_name: The operation being resolved (e.g., "db.query")
|
|
34
|
+
parameter_name: The parameter that failed (e.g., "sql")
|
|
35
|
+
expression: The ${source.path} expression that failed (e.g., "${payload.sql}")
|
|
36
|
+
missing_segment: Which path segment was not found (if applicable)
|
|
37
|
+
binding_correlation_id: Request correlation for tracing (stored separately from context)
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
>>> context = ModelInfraErrorContext.with_correlation(
|
|
41
|
+
... transport_type=EnumInfraTransportType.RUNTIME,
|
|
42
|
+
... operation="binding_resolution",
|
|
43
|
+
... target_name="db.query",
|
|
44
|
+
... )
|
|
45
|
+
>>> raise BindingResolutionError(
|
|
46
|
+
... "Required parameter 'sql' could not be resolved",
|
|
47
|
+
... operation_name="db.query",
|
|
48
|
+
... parameter_name="sql",
|
|
49
|
+
... expression="${payload.sql}",
|
|
50
|
+
... missing_segment="sql",
|
|
51
|
+
... context=context,
|
|
52
|
+
... )
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
message: str,
|
|
58
|
+
*,
|
|
59
|
+
operation_name: str,
|
|
60
|
+
parameter_name: str,
|
|
61
|
+
expression: str,
|
|
62
|
+
missing_segment: str | None = None,
|
|
63
|
+
correlation_id: UUID | None = None,
|
|
64
|
+
context: ModelInfraErrorContext | None = None,
|
|
65
|
+
) -> None:
|
|
66
|
+
"""Initialize binding resolution error.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
message: Human-readable error description
|
|
70
|
+
operation_name: Operation being resolved (e.g., "db.query")
|
|
71
|
+
parameter_name: Parameter that failed (e.g., "sql")
|
|
72
|
+
expression: Original expression (e.g., "${payload.sql}")
|
|
73
|
+
missing_segment: Path segment not found (e.g., "sql")
|
|
74
|
+
correlation_id: Request correlation ID (used if context not provided)
|
|
75
|
+
context: Infrastructure error context (preferred over correlation_id)
|
|
76
|
+
"""
|
|
77
|
+
self.operation_name = operation_name
|
|
78
|
+
self.parameter_name = parameter_name
|
|
79
|
+
self.expression = expression
|
|
80
|
+
self.missing_segment = missing_segment
|
|
81
|
+
# Store binding-specific correlation_id separately
|
|
82
|
+
self.binding_correlation_id = correlation_id
|
|
83
|
+
|
|
84
|
+
# Build context if not provided
|
|
85
|
+
if context is None:
|
|
86
|
+
context = ModelInfraErrorContext.with_correlation(
|
|
87
|
+
correlation_id=correlation_id,
|
|
88
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
89
|
+
operation="binding_resolution",
|
|
90
|
+
target_name=operation_name,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
super().__init__(
|
|
94
|
+
message=message,
|
|
95
|
+
error_code=EnumCoreErrorCode.INVALID_INPUT,
|
|
96
|
+
context=context,
|
|
97
|
+
operation_name=operation_name,
|
|
98
|
+
parameter_name=parameter_name,
|
|
99
|
+
expression=expression,
|
|
100
|
+
missing_segment=missing_segment,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def __str__(self) -> str:
|
|
104
|
+
"""Return detailed error message with diagnostic info.
|
|
105
|
+
|
|
106
|
+
Provides a structured diagnostic message that includes all relevant
|
|
107
|
+
context for debugging binding resolution failures.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Pipe-delimited string with operation, parameter, expression,
|
|
111
|
+
and optionally missing segment and correlation ID.
|
|
112
|
+
"""
|
|
113
|
+
parts = [
|
|
114
|
+
f"Binding resolution failed for operation '{self.operation_name}'",
|
|
115
|
+
f"Parameter: {self.parameter_name}",
|
|
116
|
+
f"Expression: {self.expression}",
|
|
117
|
+
]
|
|
118
|
+
if self.missing_segment:
|
|
119
|
+
parts.append(f"Missing segment: {self.missing_segment}")
|
|
120
|
+
# Include correlation_id from context (model attribute, always present after __init__)
|
|
121
|
+
if hasattr(self, "model") and self.model.correlation_id:
|
|
122
|
+
parts.append(f"Correlation ID: {self.model.correlation_id}")
|
|
123
|
+
elif self.binding_correlation_id:
|
|
124
|
+
parts.append(f"Correlation ID: {self.binding_correlation_id}")
|
|
125
|
+
return " | ".join(parts)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
__all__ = ["BindingResolutionError"]
|
|
@@ -36,8 +36,6 @@
|
|
|
36
36
|
bootstrap_servers: "${KAFKA_BOOTSTRAP_SERVERS:-localhost:9092}"
|
|
37
37
|
# Environment identifier for message routing (e.g., "local", "dev", "staging", "prod")
|
|
38
38
|
environment: "${KAFKA_ENVIRONMENT:-local}"
|
|
39
|
-
# Consumer group identifier for message routing
|
|
40
|
-
group: "${KAFKA_GROUP:-default}"
|
|
41
39
|
# =============================================================================
|
|
42
40
|
# Timeout and Retry Settings
|
|
43
41
|
# =============================================================================
|
|
@@ -18,14 +18,23 @@ Features:
|
|
|
18
18
|
Usage:
|
|
19
19
|
```python
|
|
20
20
|
from omnibase_infra.event_bus.event_bus_inmemory import EventBusInmemory
|
|
21
|
+
from omnibase_infra.models import ModelNodeIdentity
|
|
21
22
|
|
|
22
23
|
bus = EventBusInmemory(environment="dev", group="test")
|
|
23
24
|
await bus.start()
|
|
24
25
|
|
|
26
|
+
# Create node identity for consumer group derivation
|
|
27
|
+
identity = ModelNodeIdentity(
|
|
28
|
+
env="dev",
|
|
29
|
+
service="my-service",
|
|
30
|
+
node_name="event-processor",
|
|
31
|
+
version="v1",
|
|
32
|
+
)
|
|
33
|
+
|
|
25
34
|
# Subscribe to a topic
|
|
26
35
|
async def handler(msg):
|
|
27
36
|
print(f"Received: {msg.value}")
|
|
28
|
-
unsubscribe = await bus.subscribe("events",
|
|
37
|
+
unsubscribe = await bus.subscribe("events", identity, handler)
|
|
29
38
|
|
|
30
39
|
# Publish a message
|
|
31
40
|
await bus.publish("events", b"key", b"value")
|
|
@@ -50,13 +59,15 @@ from collections.abc import Awaitable, Callable
|
|
|
50
59
|
from datetime import UTC, datetime
|
|
51
60
|
from uuid import uuid4
|
|
52
61
|
|
|
53
|
-
from omnibase_infra.enums import EnumInfraTransportType
|
|
62
|
+
from omnibase_infra.enums import EnumConsumerGroupPurpose, EnumInfraTransportType
|
|
54
63
|
from omnibase_infra.errors import (
|
|
55
64
|
InfraUnavailableError,
|
|
56
65
|
ModelInfraErrorContext,
|
|
57
66
|
ProtocolConfigurationError,
|
|
58
67
|
)
|
|
59
68
|
from omnibase_infra.event_bus.models import ModelEventHeaders, ModelEventMessage
|
|
69
|
+
from omnibase_infra.models import ModelNodeIdentity
|
|
70
|
+
from omnibase_infra.utils import compute_consumer_group_id
|
|
60
71
|
|
|
61
72
|
logger = logging.getLogger(__name__)
|
|
62
73
|
|
|
@@ -97,13 +108,23 @@ class EventBusInmemory:
|
|
|
97
108
|
|
|
98
109
|
Example:
|
|
99
110
|
```python
|
|
111
|
+
from omnibase_infra.models import ModelNodeIdentity
|
|
112
|
+
|
|
100
113
|
bus = EventBusInmemory(environment="dev", group="test")
|
|
101
114
|
await bus.start()
|
|
102
115
|
|
|
116
|
+
# Create node identity for consumer group derivation
|
|
117
|
+
identity = ModelNodeIdentity(
|
|
118
|
+
env="dev",
|
|
119
|
+
service="my-service",
|
|
120
|
+
node_name="event-processor",
|
|
121
|
+
version="v1",
|
|
122
|
+
)
|
|
123
|
+
|
|
103
124
|
# Subscribe
|
|
104
125
|
async def handler(msg):
|
|
105
126
|
print(f"Received: {msg.value}")
|
|
106
|
-
unsubscribe = await bus.subscribe("events",
|
|
127
|
+
unsubscribe = await bus.subscribe("events", identity, handler)
|
|
107
128
|
|
|
108
129
|
# Publish
|
|
109
130
|
await bus.publish("events", b"key", b"value")
|
|
@@ -445,47 +466,80 @@ class EventBusInmemory:
|
|
|
445
466
|
async def subscribe(
|
|
446
467
|
self,
|
|
447
468
|
topic: str,
|
|
448
|
-
|
|
469
|
+
node_identity: ModelNodeIdentity,
|
|
449
470
|
on_message: Callable[[ModelEventMessage], Awaitable[None]],
|
|
471
|
+
*,
|
|
472
|
+
purpose: EnumConsumerGroupPurpose = EnumConsumerGroupPurpose.CONSUME,
|
|
450
473
|
) -> Callable[[], Awaitable[None]]:
|
|
451
474
|
"""Subscribe to topic with callback handler.
|
|
452
475
|
|
|
453
476
|
Registers a callback to be invoked for each message published to the topic.
|
|
454
477
|
Returns an unsubscribe function to remove the subscription.
|
|
455
478
|
|
|
479
|
+
The consumer group ID is derived from the node identity using the canonical
|
|
480
|
+
format: ``{env}.{service}.{node_name}.{purpose}.{version}``.
|
|
481
|
+
|
|
482
|
+
Note: For the in-memory implementation, the consumer group ID is used for
|
|
483
|
+
internal tracking and circuit breaker isolation, but does not affect actual
|
|
484
|
+
message delivery semantics (all subscribers receive all messages).
|
|
485
|
+
|
|
456
486
|
Args:
|
|
457
487
|
topic: Topic to subscribe to
|
|
458
|
-
|
|
488
|
+
node_identity: Node identity used to derive the consumer group ID.
|
|
489
|
+
Contains env, service, node_name, and version components.
|
|
459
490
|
on_message: Async callback invoked for each message
|
|
491
|
+
purpose: Consumer group purpose classification. Defaults to CONSUME.
|
|
492
|
+
Used in the consumer group ID derivation for disambiguation.
|
|
460
493
|
|
|
461
494
|
Returns:
|
|
462
495
|
Async unsubscribe function to remove this subscription
|
|
463
496
|
|
|
464
497
|
Example:
|
|
465
498
|
```python
|
|
499
|
+
from omnibase_infra.models import ModelNodeIdentity
|
|
500
|
+
from omnibase_infra.enums import EnumConsumerGroupPurpose
|
|
501
|
+
|
|
502
|
+
identity = ModelNodeIdentity(
|
|
503
|
+
env="dev",
|
|
504
|
+
service="my-service",
|
|
505
|
+
node_name="event-processor",
|
|
506
|
+
version="v1",
|
|
507
|
+
)
|
|
508
|
+
|
|
466
509
|
async def handler(msg):
|
|
467
510
|
print(f"Received: {msg.value}")
|
|
468
511
|
|
|
469
|
-
|
|
512
|
+
# Standard subscription (group_id: dev.my-service.event-processor.consume.v1)
|
|
513
|
+
unsubscribe = await bus.subscribe("events", identity, handler)
|
|
514
|
+
|
|
515
|
+
# With explicit purpose
|
|
516
|
+
unsubscribe = await bus.subscribe(
|
|
517
|
+
"events", identity, handler,
|
|
518
|
+
purpose=EnumConsumerGroupPurpose.INTROSPECTION,
|
|
519
|
+
)
|
|
520
|
+
|
|
470
521
|
# ... later ...
|
|
471
522
|
await unsubscribe()
|
|
472
523
|
```
|
|
473
524
|
"""
|
|
525
|
+
# Derive consumer group ID from node identity (no overrides allowed)
|
|
526
|
+
effective_group_id = compute_consumer_group_id(node_identity, purpose)
|
|
527
|
+
|
|
474
528
|
async with self._lock:
|
|
475
|
-
self._subscribers[topic].append((
|
|
529
|
+
self._subscribers[topic].append((effective_group_id, on_message))
|
|
476
530
|
logger.debug(
|
|
477
531
|
"Subscriber added",
|
|
478
|
-
extra={"topic": topic, "group_id":
|
|
532
|
+
extra={"topic": topic, "group_id": effective_group_id},
|
|
479
533
|
)
|
|
480
534
|
|
|
481
535
|
async def unsubscribe() -> None:
|
|
482
536
|
"""Remove this subscription from the topic."""
|
|
483
537
|
async with self._lock:
|
|
484
538
|
try:
|
|
485
|
-
self._subscribers[topic].remove((
|
|
539
|
+
self._subscribers[topic].remove((effective_group_id, on_message))
|
|
486
540
|
logger.debug(
|
|
487
541
|
"Subscriber removed",
|
|
488
|
-
extra={"topic": topic, "group_id":
|
|
542
|
+
extra={"topic": topic, "group_id": effective_group_id},
|
|
489
543
|
)
|
|
490
544
|
except ValueError:
|
|
491
545
|
# Already unsubscribed
|