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,140 @@
|
|
|
1
|
+
"""Platform-reserved topic suffixes for ONEX infrastructure.
|
|
2
|
+
|
|
3
|
+
WARNING: These are platform-reserved suffixes. Domain services must NOT
|
|
4
|
+
import from this module. Domain topics should be defined in domain contracts.
|
|
5
|
+
|
|
6
|
+
Topic Suffix Format:
|
|
7
|
+
onex.<kind>.<producer>.<event-name>.v<version>
|
|
8
|
+
|
|
9
|
+
Structure:
|
|
10
|
+
- onex: Required prefix for all ONEX topics
|
|
11
|
+
- kind: Message category (evt, cmd, intent, snapshot, dlq)
|
|
12
|
+
- producer: Service/module that produces the message
|
|
13
|
+
- event-name: Descriptive name using kebab-case
|
|
14
|
+
- version: Semantic version (v1, v2, etc.)
|
|
15
|
+
|
|
16
|
+
Kinds:
|
|
17
|
+
evt - Event topics (state changes, notifications)
|
|
18
|
+
cmd - Command topics (requests for action)
|
|
19
|
+
intent - Intent topics (internal workflow coordination)
|
|
20
|
+
snapshot - Snapshot topics (periodic state snapshots)
|
|
21
|
+
dlq - Dead letter queue topics
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
onex.evt.platform.node-registration.v1
|
|
25
|
+
onex.cmd.platform.request-introspection.v1
|
|
26
|
+
onex.intent.platform.runtime-tick.v1
|
|
27
|
+
|
|
28
|
+
Usage:
|
|
29
|
+
from omnibase_infra.topics import SUFFIX_NODE_REGISTRATION
|
|
30
|
+
|
|
31
|
+
# Compose full topic with tenant/namespace prefix
|
|
32
|
+
full_topic = f"{tenant}.{namespace}.{SUFFIX_NODE_REGISTRATION}"
|
|
33
|
+
|
|
34
|
+
See Also:
|
|
35
|
+
omnibase_core.validation.validate_topic_suffix - Validation function
|
|
36
|
+
omnibase_core.validation.compose_full_topic - Topic composition utility
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from omnibase_core.errors import OnexError
|
|
40
|
+
from omnibase_core.validation import validate_topic_suffix
|
|
41
|
+
|
|
42
|
+
# =============================================================================
|
|
43
|
+
# PLATFORM-RESERVED TOPIC SUFFIXES
|
|
44
|
+
# =============================================================================
|
|
45
|
+
|
|
46
|
+
# Node lifecycle events
|
|
47
|
+
SUFFIX_NODE_REGISTRATION: str = "onex.evt.platform.node-registration.v1"
|
|
48
|
+
"""Topic suffix for node registration events.
|
|
49
|
+
|
|
50
|
+
Published when a node registers with the runtime. Contains node metadata,
|
|
51
|
+
capabilities, and health check configuration.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
SUFFIX_NODE_INTROSPECTION: str = "onex.evt.platform.node-introspection.v1"
|
|
55
|
+
"""Topic suffix for node introspection events.
|
|
56
|
+
|
|
57
|
+
Published when a node responds to an introspection request. Contains node
|
|
58
|
+
capabilities, supported operations, and current state.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
SUFFIX_NODE_HEARTBEAT: str = "onex.evt.platform.node-heartbeat.v1"
|
|
62
|
+
"""Topic suffix for node heartbeat events.
|
|
63
|
+
|
|
64
|
+
Published periodically by nodes to indicate liveness. Contains timestamp,
|
|
65
|
+
resource usage metrics, and health status.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
# Command topics
|
|
69
|
+
SUFFIX_REQUEST_INTROSPECTION: str = "onex.cmd.platform.request-introspection.v1"
|
|
70
|
+
"""Topic suffix for introspection request commands.
|
|
71
|
+
|
|
72
|
+
Published to request introspection from a specific node or all nodes.
|
|
73
|
+
Nodes respond on the SUFFIX_NODE_INTROSPECTION topic.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
# FSM and state management
|
|
77
|
+
SUFFIX_FSM_STATE_TRANSITIONS: str = "onex.evt.platform.fsm-state-transitions.v1"
|
|
78
|
+
"""Topic suffix for FSM state transition events.
|
|
79
|
+
|
|
80
|
+
Published when a node's finite state machine transitions between states.
|
|
81
|
+
Contains previous state, new state, trigger event, and transition metadata.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
# Runtime coordination
|
|
85
|
+
SUFFIX_RUNTIME_TICK: str = "onex.intent.platform.runtime-tick.v1"
|
|
86
|
+
"""Topic suffix for runtime tick intents.
|
|
87
|
+
|
|
88
|
+
Internal topic for runtime orchestration. Triggers periodic tasks like
|
|
89
|
+
heartbeat collection, health checks, and scheduled workflows.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
# Registration snapshots
|
|
93
|
+
SUFFIX_REGISTRATION_SNAPSHOTS: str = "onex.snapshot.platform.registration-snapshots.v1"
|
|
94
|
+
"""Topic suffix for registration snapshot events.
|
|
95
|
+
|
|
96
|
+
Published periodically with aggregated registration state. Used for
|
|
97
|
+
dashboard displays and monitoring systems.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
# =============================================================================
|
|
101
|
+
# AGGREGATE TUPLE
|
|
102
|
+
# =============================================================================
|
|
103
|
+
|
|
104
|
+
ALL_PLATFORM_SUFFIXES: tuple[str, ...] = (
|
|
105
|
+
SUFFIX_NODE_REGISTRATION,
|
|
106
|
+
SUFFIX_NODE_INTROSPECTION,
|
|
107
|
+
SUFFIX_NODE_HEARTBEAT,
|
|
108
|
+
SUFFIX_REQUEST_INTROSPECTION,
|
|
109
|
+
SUFFIX_FSM_STATE_TRANSITIONS,
|
|
110
|
+
SUFFIX_RUNTIME_TICK,
|
|
111
|
+
SUFFIX_REGISTRATION_SNAPSHOTS,
|
|
112
|
+
)
|
|
113
|
+
"""Complete tuple of all platform-reserved topic suffixes.
|
|
114
|
+
|
|
115
|
+
Use this tuple for:
|
|
116
|
+
- Validating that domain topics don't conflict with platform topics
|
|
117
|
+
- Iterating over all platform topics for subscription setup
|
|
118
|
+
- Documentation and discovery
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
# =============================================================================
|
|
122
|
+
# IMPORT-TIME VALIDATION
|
|
123
|
+
# =============================================================================
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _validate_all_suffixes() -> None:
|
|
127
|
+
"""Validate all suffixes at import time to fail fast on invalid format.
|
|
128
|
+
|
|
129
|
+
Raises:
|
|
130
|
+
OnexError: If any suffix fails validation with details about which
|
|
131
|
+
suffix failed and why.
|
|
132
|
+
"""
|
|
133
|
+
for suffix in ALL_PLATFORM_SUFFIXES:
|
|
134
|
+
result = validate_topic_suffix(suffix)
|
|
135
|
+
if not result.is_valid:
|
|
136
|
+
raise OnexError(f"Invalid platform topic suffix '{suffix}': {result.error}")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# Run validation at import time
|
|
140
|
+
_validate_all_suffixes()
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Topic composition utilities for ONEX infrastructure.
|
|
2
|
+
|
|
3
|
+
IMPORTANT: build_full_topic() is the ONLY supported way to compose
|
|
4
|
+
Kafka topics in omnibase_infra. Direct string concatenation is prohibited.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from omnibase_core.errors import OnexError
|
|
8
|
+
from omnibase_core.validation import validate_topic_suffix
|
|
9
|
+
from omnibase_core.validation.validator_topic_suffix import ENV_PREFIXES
|
|
10
|
+
|
|
11
|
+
MAX_NAMESPACE_LENGTH = 100
|
|
12
|
+
"""Maximum allowed namespace length.
|
|
13
|
+
|
|
14
|
+
Kafka topics have a 249 character limit. With env prefix (~10 chars),
|
|
15
|
+
separators (2 dots), and suffix (~50 chars), namespace should be limited
|
|
16
|
+
to ensure total topic length stays within bounds.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TopicCompositionError(OnexError):
|
|
21
|
+
"""Raised when topic composition fails due to invalid components.
|
|
22
|
+
|
|
23
|
+
Extends OnexError to follow ONEX error handling conventions.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def build_full_topic(env: str, namespace: str, suffix: str) -> str:
|
|
28
|
+
"""Build full topic from components with validation.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
env: Environment prefix (e.g., "dev", "staging", "prod", "test", "local")
|
|
32
|
+
namespace: Namespace/tenant identifier (e.g., "omnibase", "myapp", "tenant-123")
|
|
33
|
+
suffix: Validated topic suffix (e.g., "onex.evt.platform.node-introspection.v1")
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Full topic string: {env}.{namespace}.{suffix}
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
TopicCompositionError: If env is not a valid environment prefix
|
|
40
|
+
TopicCompositionError: If namespace is empty or contains invalid characters
|
|
41
|
+
TopicCompositionError: If suffix doesn't match ONEX topic format
|
|
42
|
+
|
|
43
|
+
Namespace Validation Rules:
|
|
44
|
+
- Must not be empty
|
|
45
|
+
- Allowed characters: alphanumeric (a-z, A-Z, 0-9), hyphens (-), underscores (_)
|
|
46
|
+
- Numeric-only namespaces ARE valid (e.g., "12345") to support numeric tenant IDs
|
|
47
|
+
- Invalid: spaces, dots, special characters
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
>>> build_full_topic("dev", "omnibase", "onex.evt.platform.node-introspection.v1")
|
|
51
|
+
'dev.omnibase.onex.evt.platform.node-introspection.v1'
|
|
52
|
+
|
|
53
|
+
>>> build_full_topic("prod", "myapp", "onex.cmd.platform.request-introspection.v1")
|
|
54
|
+
'prod.myapp.onex.cmd.platform.request-introspection.v1'
|
|
55
|
+
"""
|
|
56
|
+
# Validate environment prefix
|
|
57
|
+
if env not in ENV_PREFIXES:
|
|
58
|
+
raise TopicCompositionError(
|
|
59
|
+
f"Invalid environment prefix '{env}'. "
|
|
60
|
+
f"Must be one of: {', '.join(sorted(ENV_PREFIXES))}"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Validate namespace
|
|
64
|
+
# Allowed characters: alphanumeric (a-z, A-Z, 0-9), hyphens, underscores
|
|
65
|
+
# Note: Numeric-only namespaces (e.g., "12345") ARE valid because isalnum()
|
|
66
|
+
# returns True for digit-only strings. This is intentional to support
|
|
67
|
+
# numeric tenant/organization IDs as namespaces.
|
|
68
|
+
if not namespace:
|
|
69
|
+
raise TopicCompositionError("Namespace cannot be empty")
|
|
70
|
+
if len(namespace) > MAX_NAMESPACE_LENGTH:
|
|
71
|
+
raise TopicCompositionError(
|
|
72
|
+
f"Namespace exceeds maximum length of {MAX_NAMESPACE_LENGTH} characters. "
|
|
73
|
+
f"Got {len(namespace)} characters."
|
|
74
|
+
)
|
|
75
|
+
if not namespace.replace("-", "").replace("_", "").isalnum():
|
|
76
|
+
raise TopicCompositionError(
|
|
77
|
+
f"Invalid namespace '{namespace}'. "
|
|
78
|
+
"Must contain only alphanumeric characters, hyphens, and underscores"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Enforce lowercase to ensure composed topics are valid
|
|
82
|
+
# (ONEX topic suffixes are lowercase, so namespaces should match)
|
|
83
|
+
if namespace != namespace.lower():
|
|
84
|
+
raise TopicCompositionError(
|
|
85
|
+
f"Namespace must be lowercase: '{namespace}'. "
|
|
86
|
+
"Use lowercase to ensure consistent topic naming."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Validate suffix using omnibase_core validation
|
|
90
|
+
result = validate_topic_suffix(suffix)
|
|
91
|
+
if not result.is_valid:
|
|
92
|
+
raise TopicCompositionError(f"Invalid topic suffix '{suffix}': {result.error}")
|
|
93
|
+
|
|
94
|
+
# Compose full topic
|
|
95
|
+
return f"{env}.{namespace}.{suffix}"
|
|
@@ -7,10 +7,14 @@ forms of Pydantic models, enabling proper type checking for cache operations
|
|
|
7
7
|
and JSON serialization/deserialization without requiring type: ignore comments.
|
|
8
8
|
|
|
9
9
|
Available TypedDicts:
|
|
10
|
+
- TypedDictEnvelopeBuildParams: Parameters for building ModelEventEnvelope
|
|
10
11
|
- TypedDictIntrospectionCache: JSON-serialized ModelNodeIntrospectionEvent
|
|
11
12
|
- TypedDictPerformanceMetricsCache: JSON-serialized introspection performance metrics
|
|
12
13
|
"""
|
|
13
14
|
|
|
15
|
+
from omnibase_infra.types.typed_dict.typed_dict_envelope_build_params import (
|
|
16
|
+
TypedDictEnvelopeBuildParams,
|
|
17
|
+
)
|
|
14
18
|
from omnibase_infra.types.typed_dict.typed_dict_introspection_cache import (
|
|
15
19
|
TypedDictIntrospectionCache,
|
|
16
20
|
)
|
|
@@ -18,4 +22,8 @@ from omnibase_infra.types.typed_dict.typed_dict_performance_metrics_cache import
|
|
|
18
22
|
TypedDictPerformanceMetricsCache,
|
|
19
23
|
)
|
|
20
24
|
|
|
21
|
-
__all__ = [
|
|
25
|
+
__all__ = [
|
|
26
|
+
"TypedDictEnvelopeBuildParams",
|
|
27
|
+
"TypedDictIntrospectionCache",
|
|
28
|
+
"TypedDictPerformanceMetricsCache",
|
|
29
|
+
]
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""TypedDict definition for envelope build parameters.
|
|
4
|
+
|
|
5
|
+
This module provides a TypedDict that represents the parameters passed to the
|
|
6
|
+
_build_envelope method in AdapterProtocolEventPublisherInmemory, replacing the
|
|
7
|
+
loose dict[str, object] typing with proper type safety.
|
|
8
|
+
|
|
9
|
+
The TypedDictEnvelopeBuildParams ensures type-checked access to envelope
|
|
10
|
+
construction parameters without requiring type: ignore comments or unsafe casts.
|
|
11
|
+
|
|
12
|
+
Key Features:
|
|
13
|
+
- Full type annotations for all envelope build parameters
|
|
14
|
+
- Proper handling of nullable fields (correlation_id, causation_id, metadata)
|
|
15
|
+
- Integration with JsonType for payload typing
|
|
16
|
+
- Uses TYPE_CHECKING import for ContextValue to avoid circular imports
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
This TypedDict is primarily used for:
|
|
20
|
+
- Type-safe parameter passing to envelope builder methods
|
|
21
|
+
- Eliminating dict[str, object] loose typing
|
|
22
|
+
- Enabling mypy verification of parameter access
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
```python
|
|
26
|
+
from omnibase_infra.types.typed_dict import TypedDictEnvelopeBuildParams
|
|
27
|
+
|
|
28
|
+
def build_envelope(params: TypedDictEnvelopeBuildParams) -> ModelEventEnvelope:
|
|
29
|
+
# Type-safe access to all fields
|
|
30
|
+
event_type = params["event_type"]
|
|
31
|
+
payload = params["payload"]
|
|
32
|
+
correlation_id = params.get("correlation_id")
|
|
33
|
+
return ...
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
See Also:
|
|
37
|
+
- AdapterProtocolEventPublisherInmemory: Primary consumer of this TypedDict
|
|
38
|
+
- ModelEventEnvelope: Target envelope model constructed from these params
|
|
39
|
+
- ProtocolEventPublisher: SPI protocol defining publish interface
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
from typing import TYPE_CHECKING, NotRequired, TypedDict
|
|
45
|
+
|
|
46
|
+
from omnibase_core.types import JsonType
|
|
47
|
+
|
|
48
|
+
if TYPE_CHECKING:
|
|
49
|
+
from omnibase_spi.protocols.types.protocol_core_types import ContextValue
|
|
50
|
+
|
|
51
|
+
__all__ = ["TypedDictEnvelopeBuildParams"]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TypedDictEnvelopeBuildParams(TypedDict):
|
|
55
|
+
"""TypedDict representing parameters for building a ModelEventEnvelope.
|
|
56
|
+
|
|
57
|
+
This type provides a type-safe alternative to dict[str, object] for passing
|
|
58
|
+
envelope construction parameters. It enables proper type checking when
|
|
59
|
+
building event envelopes in publisher adapters.
|
|
60
|
+
|
|
61
|
+
Required fields (event_type, payload) are enforced by TypedDict's default
|
|
62
|
+
total=True behavior. Optional fields use NotRequired[] to allow omission
|
|
63
|
+
while maintaining type safety.
|
|
64
|
+
|
|
65
|
+
Attributes:
|
|
66
|
+
event_type: Fully-qualified event type identifier
|
|
67
|
+
(e.g., "omninode.user.event.created.v1"). Required field - must
|
|
68
|
+
always be provided when constructing the TypedDict.
|
|
69
|
+
payload: Event payload data as JSON-compatible types. Required field -
|
|
70
|
+
must always be provided. Can be dict, list, str, int, float, bool,
|
|
71
|
+
or None.
|
|
72
|
+
correlation_id: Optional correlation ID for request tracing.
|
|
73
|
+
Used to link related events across service boundaries.
|
|
74
|
+
May be omitted or set to None.
|
|
75
|
+
causation_id: Optional causation ID for event sourcing chains.
|
|
76
|
+
Links this event to the event that caused it.
|
|
77
|
+
May be omitted or set to None.
|
|
78
|
+
metadata: Optional additional context values for the envelope.
|
|
79
|
+
Keys are string identifiers, values are ContextValue protocol
|
|
80
|
+
implementations (e.g., ProtocolContextStringValue).
|
|
81
|
+
May be omitted or set to None.
|
|
82
|
+
|
|
83
|
+
Note:
|
|
84
|
+
The metadata field uses ContextValue from omnibase_spi which is imported
|
|
85
|
+
under TYPE_CHECKING to avoid runtime circular import issues. At runtime,
|
|
86
|
+
the dict values are duck-typed protocol implementations.
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
```python
|
|
90
|
+
# Minimal valid TypedDict (only required fields)
|
|
91
|
+
params: TypedDictEnvelopeBuildParams = {
|
|
92
|
+
"event_type": "user.created.v1",
|
|
93
|
+
"payload": {"user_id": "123", "email": "user@example.com"},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# Full TypedDict with all fields
|
|
97
|
+
params_full: TypedDictEnvelopeBuildParams = {
|
|
98
|
+
"event_type": "user.created.v1",
|
|
99
|
+
"payload": {"user_id": "123", "email": "user@example.com"},
|
|
100
|
+
"correlation_id": "corr-abc-123",
|
|
101
|
+
"causation_id": None,
|
|
102
|
+
"metadata": {"trace_id": trace_value},
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Safe field access
|
|
106
|
+
event_type: str = params["event_type"] # Always present
|
|
107
|
+
correlation_id: str | None = params.get("correlation_id") # May be absent
|
|
108
|
+
```
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
event_type: str
|
|
112
|
+
payload: JsonType
|
|
113
|
+
correlation_id: NotRequired[str | None]
|
|
114
|
+
causation_id: NotRequired[str | None]
|
|
115
|
+
metadata: NotRequired[dict[str, ContextValue] | None]
|
omnibase_infra/utils/__init__.py
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
This package provides common utilities used across the infrastructure:
|
|
6
6
|
- correlation: Correlation ID generation and propagation for distributed tracing
|
|
7
7
|
- util_atomic_file: Atomic file write primitives using temp-file-rename pattern
|
|
8
|
+
- util_consumer_group: Kafka consumer group ID generation with deterministic hashing
|
|
8
9
|
- util_datetime: Datetime validation and timezone normalization
|
|
9
10
|
- util_db_transaction: Database transaction context manager for asyncpg
|
|
10
11
|
- util_dsn_validation: PostgreSQL DSN validation and sanitization
|
|
@@ -26,6 +27,11 @@ from omnibase_infra.utils.util_atomic_file import (
|
|
|
26
27
|
write_atomic_bytes,
|
|
27
28
|
write_atomic_bytes_async,
|
|
28
29
|
)
|
|
30
|
+
from omnibase_infra.utils.util_consumer_group import (
|
|
31
|
+
KAFKA_CONSUMER_GROUP_MAX_LENGTH,
|
|
32
|
+
compute_consumer_group_id,
|
|
33
|
+
normalize_kafka_identifier,
|
|
34
|
+
)
|
|
29
35
|
from omnibase_infra.utils.util_datetime import (
|
|
30
36
|
ensure_timezone_aware,
|
|
31
37
|
is_timezone_aware,
|
|
@@ -72,15 +78,18 @@ from omnibase_infra.utils.util_semver import (
|
|
|
72
78
|
|
|
73
79
|
__all__: list[str] = [
|
|
74
80
|
"CorrelationContext",
|
|
81
|
+
"KAFKA_CONSUMER_GROUP_MAX_LENGTH",
|
|
75
82
|
"OptimisticConflictError",
|
|
76
83
|
"SAFE_ERROR_PATTERNS",
|
|
77
84
|
"SEMVER_PATTERN",
|
|
78
85
|
"SENSITIVE_PATTERNS",
|
|
79
86
|
"clear_correlation_id",
|
|
87
|
+
"compute_consumer_group_id",
|
|
80
88
|
"ensure_timezone_aware",
|
|
81
89
|
"generate_correlation_id",
|
|
82
90
|
"get_correlation_id",
|
|
83
91
|
"is_timezone_aware",
|
|
92
|
+
"normalize_kafka_identifier",
|
|
84
93
|
"parse_and_validate_dsn",
|
|
85
94
|
"parse_env_float",
|
|
86
95
|
"parse_env_int",
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Kafka consumer group ID utilities.
|
|
4
|
+
|
|
5
|
+
Provides utilities for normalizing and validating Kafka consumer group identifiers.
|
|
6
|
+
Kafka consumer group IDs have specific character and length constraints that this
|
|
7
|
+
module helps enforce consistently across the codebase.
|
|
8
|
+
|
|
9
|
+
Kafka Consumer Group ID Constraints:
|
|
10
|
+
- Maximum length: 255 characters
|
|
11
|
+
- Valid characters: alphanumeric, period (.), underscore (_), hyphen (-)
|
|
12
|
+
- Cannot be empty
|
|
13
|
+
|
|
14
|
+
This module provides:
|
|
15
|
+
- normalize_kafka_identifier: Normalize strings for use as Kafka consumer group IDs
|
|
16
|
+
- compute_consumer_group_id: Compute canonical consumer group ID from node identity
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import hashlib
|
|
22
|
+
import re
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
|
+
|
|
25
|
+
from omnibase_infra.enums import EnumConsumerGroupPurpose
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from omnibase_infra.models import ModelNodeIdentity
|
|
29
|
+
|
|
30
|
+
# Maximum length for Kafka consumer group IDs
|
|
31
|
+
KAFKA_CONSUMER_GROUP_MAX_LENGTH = 255
|
|
32
|
+
|
|
33
|
+
# Pattern for invalid characters (anything not alphanumeric, period, underscore, or hyphen)
|
|
34
|
+
_INVALID_CHAR_PATTERN = re.compile(r"[^a-z0-9._-]")
|
|
35
|
+
|
|
36
|
+
# Pattern for consecutive separators (period, underscore, or hyphen)
|
|
37
|
+
_CONSECUTIVE_SEPARATOR_PATTERN = re.compile(r"[._-]{2,}")
|
|
38
|
+
|
|
39
|
+
# Pattern for leading/trailing separators
|
|
40
|
+
_EDGE_SEPARATOR_PATTERN = re.compile(r"^[._-]+|[._-]+$")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def normalize_kafka_identifier(value: str) -> str:
|
|
44
|
+
"""Normalize a string for use as a Kafka consumer group ID.
|
|
45
|
+
|
|
46
|
+
Applies the following transformations in order:
|
|
47
|
+
1. Convert to lowercase
|
|
48
|
+
2. Replace invalid characters (non-alphanumeric, non-separator) with underscore
|
|
49
|
+
3. Collapse consecutive separators (., _, -) into a single separator
|
|
50
|
+
4. Strip leading and trailing separators
|
|
51
|
+
5. Truncate to max length (255) with hash suffix if necessary
|
|
52
|
+
|
|
53
|
+
The hash suffix ensures uniqueness when truncation is required. The suffix
|
|
54
|
+
format is `_<8-char-hash>` appended after truncating to fit within 255 chars.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
value: The input string to normalize.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
A normalized string safe for use as a Kafka consumer group ID.
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
ValueError: If the input is empty or results in an empty string after
|
|
64
|
+
normalization.
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
>>> normalize_kafka_identifier("My Service!!")
|
|
68
|
+
'my_service'
|
|
69
|
+
>>> normalize_kafka_identifier("foo..bar__baz")
|
|
70
|
+
'foo.bar_baz'
|
|
71
|
+
>>> normalize_kafka_identifier(" UPPER_Case-Test ")
|
|
72
|
+
'upper_case-test'
|
|
73
|
+
>>> normalize_kafka_identifier("valid.consumer-group_id")
|
|
74
|
+
'valid.consumer-group_id'
|
|
75
|
+
>>> normalize_kafka_identifier("@#$%^&*()") # Raises ValueError
|
|
76
|
+
Traceback (most recent call last):
|
|
77
|
+
...
|
|
78
|
+
ValueError: Input '@#$%^&*()' results in empty string after normalization
|
|
79
|
+
"""
|
|
80
|
+
if not value:
|
|
81
|
+
raise ValueError("Kafka consumer group ID cannot be empty")
|
|
82
|
+
|
|
83
|
+
# Step 1: Lowercase
|
|
84
|
+
result = value.lower()
|
|
85
|
+
|
|
86
|
+
# Step 2: Replace invalid characters with underscore
|
|
87
|
+
result = _INVALID_CHAR_PATTERN.sub("_", result)
|
|
88
|
+
|
|
89
|
+
# Step 3: Collapse consecutive separators, preserving the first separator type
|
|
90
|
+
result = _CONSECUTIVE_SEPARATOR_PATTERN.sub(lambda m: m.group(0)[0], result)
|
|
91
|
+
|
|
92
|
+
# Step 4: Strip leading and trailing separators
|
|
93
|
+
result = _EDGE_SEPARATOR_PATTERN.sub("", result)
|
|
94
|
+
|
|
95
|
+
# Check for empty result after normalization
|
|
96
|
+
if not result:
|
|
97
|
+
raise ValueError(f"Input {value!r} results in empty string after normalization")
|
|
98
|
+
|
|
99
|
+
# Step 5: Truncate with hash suffix if exceeds max length
|
|
100
|
+
if len(result) > KAFKA_CONSUMER_GROUP_MAX_LENGTH:
|
|
101
|
+
# Generate 8-character hash suffix from original value for determinism
|
|
102
|
+
hash_suffix = hashlib.sha256(value.encode()).hexdigest()[:8]
|
|
103
|
+
# Reserve space for underscore + hash suffix (9 chars total)
|
|
104
|
+
max_prefix_length = KAFKA_CONSUMER_GROUP_MAX_LENGTH - 9
|
|
105
|
+
result = f"{result[:max_prefix_length]}_{hash_suffix}"
|
|
106
|
+
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def compute_consumer_group_id(
|
|
111
|
+
identity: ModelNodeIdentity,
|
|
112
|
+
purpose: EnumConsumerGroupPurpose = EnumConsumerGroupPurpose.CONSUME,
|
|
113
|
+
) -> str:
|
|
114
|
+
"""Compute canonical Kafka consumer group ID from node identity.
|
|
115
|
+
|
|
116
|
+
Generates a deterministic, Kafka-compliant consumer group ID using the
|
|
117
|
+
canonical format: ``{env}.{service}.{node_name}.{purpose}.{version}``
|
|
118
|
+
|
|
119
|
+
Each component is normalized using ``normalize_kafka_identifier()`` to ensure
|
|
120
|
+
the result is safe for use as a Kafka consumer group ID. The final result
|
|
121
|
+
is validated against Kafka's 255 character limit.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
identity: Node identity containing env, service, node_name, and version.
|
|
125
|
+
purpose: Consumer group purpose classification. Defaults to CONSUME.
|
|
126
|
+
The purpose determines consumer behavior semantics (e.g., offset
|
|
127
|
+
reset policy) and is included in the group ID for disambiguation.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
A canonical consumer group ID in the format:
|
|
131
|
+
``{env}.{service}.{node_name}.{purpose}.{version}``
|
|
132
|
+
|
|
133
|
+
If the combined length exceeds Kafka's 255 character limit, the result
|
|
134
|
+
is truncated with an 8-character hash suffix to preserve uniqueness
|
|
135
|
+
while fitting within the constraint.
|
|
136
|
+
|
|
137
|
+
Example:
|
|
138
|
+
>>> from omnibase_infra.models import ModelNodeIdentity
|
|
139
|
+
>>> from omnibase_infra.enums import EnumConsumerGroupPurpose
|
|
140
|
+
>>> identity = ModelNodeIdentity(
|
|
141
|
+
... env="dev",
|
|
142
|
+
... service="omniintelligence",
|
|
143
|
+
... node_name="claude_hook_event_effect",
|
|
144
|
+
... version="v1",
|
|
145
|
+
... )
|
|
146
|
+
>>> compute_consumer_group_id(identity)
|
|
147
|
+
'dev.omniintelligence.claude_hook_event_effect.consume.v1'
|
|
148
|
+
|
|
149
|
+
With a different purpose:
|
|
150
|
+
|
|
151
|
+
>>> compute_consumer_group_id(identity, EnumConsumerGroupPurpose.INTROSPECTION)
|
|
152
|
+
'dev.omniintelligence.claude_hook_event_effect.introspection.v1'
|
|
153
|
+
|
|
154
|
+
Component normalization is applied automatically:
|
|
155
|
+
|
|
156
|
+
>>> identity_mixed = ModelNodeIdentity(
|
|
157
|
+
... env="DEV",
|
|
158
|
+
... service="Omni Intelligence",
|
|
159
|
+
... node_name="claude-hook-event-effect",
|
|
160
|
+
... version="V1.0.0",
|
|
161
|
+
... )
|
|
162
|
+
>>> compute_consumer_group_id(identity_mixed)
|
|
163
|
+
'dev.omni_intelligence.claude-hook-event-effect.consume.v1.0.0'
|
|
164
|
+
|
|
165
|
+
Long identities are automatically truncated with a hash suffix:
|
|
166
|
+
|
|
167
|
+
>>> long_identity = ModelNodeIdentity(
|
|
168
|
+
... env="development",
|
|
169
|
+
... service="a" * 100,
|
|
170
|
+
... node_name="b" * 100,
|
|
171
|
+
... version="v1",
|
|
172
|
+
... )
|
|
173
|
+
>>> result = compute_consumer_group_id(long_identity)
|
|
174
|
+
>>> len(result) <= 255
|
|
175
|
+
True
|
|
176
|
+
>>> result.endswith("_" + result[-8:]) # Has hash suffix
|
|
177
|
+
True
|
|
178
|
+
|
|
179
|
+
Note:
|
|
180
|
+
The canonical format uses period (.) as the separator between components.
|
|
181
|
+
This enables hierarchical grouping and filtering in Kafka tooling while
|
|
182
|
+
maintaining compatibility with Kafka's consumer group ID constraints.
|
|
183
|
+
|
|
184
|
+
See Also:
|
|
185
|
+
- :func:`normalize_kafka_identifier`: Component normalization rules
|
|
186
|
+
- :class:`~omnibase_infra.enums.EnumConsumerGroupPurpose`: Purpose values
|
|
187
|
+
- :class:`~omnibase_infra.models.ModelNodeIdentity`: Identity model
|
|
188
|
+
|
|
189
|
+
.. versionadded:: 0.2.6
|
|
190
|
+
Created as part of OMN-1602.
|
|
191
|
+
"""
|
|
192
|
+
# Normalize each component
|
|
193
|
+
normalized_env = normalize_kafka_identifier(identity.env)
|
|
194
|
+
normalized_service = normalize_kafka_identifier(identity.service)
|
|
195
|
+
normalized_node_name = normalize_kafka_identifier(identity.node_name)
|
|
196
|
+
normalized_purpose = normalize_kafka_identifier(purpose.value)
|
|
197
|
+
normalized_version = normalize_kafka_identifier(identity.version)
|
|
198
|
+
|
|
199
|
+
# Join with period separator
|
|
200
|
+
group_id = ".".join(
|
|
201
|
+
[
|
|
202
|
+
normalized_env,
|
|
203
|
+
normalized_service,
|
|
204
|
+
normalized_node_name,
|
|
205
|
+
normalized_purpose,
|
|
206
|
+
normalized_version,
|
|
207
|
+
]
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Handle length constraint with truncation + hash (same strategy as normalize_kafka_identifier)
|
|
211
|
+
# This can occur when multiple long components combine, even though each was individually
|
|
212
|
+
# truncated to 255 chars by normalize_kafka_identifier.
|
|
213
|
+
if len(group_id) > KAFKA_CONSUMER_GROUP_MAX_LENGTH:
|
|
214
|
+
# Generate deterministic hash from original (pre-normalized) identity components
|
|
215
|
+
# to ensure the same identity always produces the same truncated group ID
|
|
216
|
+
hash_input = (
|
|
217
|
+
f"{identity.env}|{identity.service}|{identity.node_name}|"
|
|
218
|
+
f"{purpose.value}|{identity.version}"
|
|
219
|
+
)
|
|
220
|
+
hash_suffix = hashlib.sha256(hash_input.encode()).hexdigest()[:8]
|
|
221
|
+
# Reserve space for underscore + hash suffix (9 chars total)
|
|
222
|
+
max_prefix_length = KAFKA_CONSUMER_GROUP_MAX_LENGTH - 9
|
|
223
|
+
group_id = f"{group_id[:max_prefix_length]}_{hash_suffix}"
|
|
224
|
+
|
|
225
|
+
return group_id
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
__all__: list[str] = [
|
|
229
|
+
"KAFKA_CONSUMER_GROUP_MAX_LENGTH",
|
|
230
|
+
"compute_consumer_group_id",
|
|
231
|
+
"normalize_kafka_identifier",
|
|
232
|
+
]
|
|
@@ -429,7 +429,24 @@ INFRA_NODES_PATH = "src/omnibase_infra/nodes/"
|
|
|
429
429
|
# ast.FunctionDef | ast.AsyncFunctionDef for AST method type checking
|
|
430
430
|
# - 105 (2026-01-21): Contract-driven handler config loading (+4 unions)
|
|
431
431
|
# ModelHandlerContract transport config fields and lifecycle types
|
|
432
|
-
|
|
432
|
+
# - 108 (2026-01-27): OMN-1518 declarative operation bindings (+3 unions)
|
|
433
|
+
# ModelEventEnvelope[object] | dict[str, object] for materialized
|
|
434
|
+
# envelopes in dispatch engine (3 occurrences in type aliases)
|
|
435
|
+
# - 105 (2026-01-27): OMN-1518 simplify to always-dict envelope format (-3 unions)
|
|
436
|
+
# Removed hybrid union types by always materializing to dict format
|
|
437
|
+
# Dispatchers now receive consistent dict[str, object] with __bindings
|
|
438
|
+
# - 112 (2026-01-27): OMN-1610 emit daemon for persistent Kafka connections (+7 unions)
|
|
439
|
+
# BoundedEventQueue, EmitClient, EventRegistry return types
|
|
440
|
+
# - 112 (2026-01-29): OMN-1610 emit daemon + refactor to strongly typed input model
|
|
441
|
+
# BoundedEventQueue, EmitClient, EventRegistry, socket_permissions
|
|
442
|
+
# Replaced dict unions with ModelEmitDaemonConfigInput
|
|
443
|
+
# - 113 (2026-01-29): OMN-1610 properly typed daemon protocol models (+1 union)
|
|
444
|
+
# Added ModelDaemonRequest, ModelDaemonResponse discriminated unions
|
|
445
|
+
# Replaced dict[str, object] soup with strongly typed Pydantic models
|
|
446
|
+
# - 115 (2026-01-29): OMN-1653 contract registry reducer (+2 unions)
|
|
447
|
+
# ContractRegistryEvent: 4-type union for event routing
|
|
448
|
+
# contract_yaml: dict | str for flexible YAML handling
|
|
449
|
+
INFRA_MAX_UNIONS = 115
|
|
433
450
|
|
|
434
451
|
# Maximum allowed architecture violations in infrastructure code.
|
|
435
452
|
# Set to 0 (strict enforcement) to ensure one-model-per-file principle is always followed.
|