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,166 @@
|
|
|
1
|
+
-- Migration: 001_create_event_ledger.sql
|
|
2
|
+
-- Purpose: Create the event_ledger table for durable event capture and replay
|
|
3
|
+
-- Author: ONEX Infrastructure Team
|
|
4
|
+
-- Date: 2026-01-29
|
|
5
|
+
--
|
|
6
|
+
-- Design Decisions:
|
|
7
|
+
-- 1. TEXT vs VARCHAR(255) for topic: Kafka topic names can exceed 255 characters
|
|
8
|
+
-- with namespaced conventions (e.g., "dev.archon-intelligence.intelligence.code-analysis-requested.v1")
|
|
9
|
+
--
|
|
10
|
+
-- 2. BYTEA for event_key/event_value: Raw binary preservation without encoding assumptions.
|
|
11
|
+
-- Events may contain non-UTF8 data (Protobuf, Avro, MessagePack). TEXT would require
|
|
12
|
+
-- encoding validation and could corrupt binary payloads.
|
|
13
|
+
--
|
|
14
|
+
-- 3. All metadata fields NULLABLE: The audit ledger must NEVER drop events due to
|
|
15
|
+
-- malformed metadata. Missing envelope_id, correlation_id, event_type, or source
|
|
16
|
+
-- must not block event capture. Schema enforcement happens downstream.
|
|
17
|
+
--
|
|
18
|
+
-- 4. JSONB for onex_headers: Structured storage of ONEX-specific headers with indexing
|
|
19
|
+
-- capability. Avoids column explosion as header schema evolves.
|
|
20
|
+
--
|
|
21
|
+
-- 5. Dual timestamps: event_timestamp (from event) may be NULL if source doesn't provide it.
|
|
22
|
+
-- ledger_written_at provides guaranteed ordering for replay scenarios.
|
|
23
|
+
--
|
|
24
|
+
-- 6. Idempotency via (topic, partition, kafka_offset): Ensures exactly-once semantics
|
|
25
|
+
-- for event capture even with consumer restarts or rebalancing.
|
|
26
|
+
|
|
27
|
+
-- =============================================================================
|
|
28
|
+
-- TABLE: event_ledger
|
|
29
|
+
-- =============================================================================
|
|
30
|
+
-- The event_ledger provides durable, append-only storage of all events consumed
|
|
31
|
+
-- from Kafka. It serves as:
|
|
32
|
+
-- - Audit trail for compliance and debugging
|
|
33
|
+
-- - Source for event replay and reprocessing
|
|
34
|
+
-- - Idempotency guard against duplicate processing
|
|
35
|
+
-- =============================================================================
|
|
36
|
+
|
|
37
|
+
CREATE TABLE IF NOT EXISTS event_ledger (
|
|
38
|
+
-- Primary key: Auto-generated UUID for ledger entry identification
|
|
39
|
+
ledger_entry_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
40
|
+
|
|
41
|
+
-- =========================================================================
|
|
42
|
+
-- Kafka Position (Idempotency Key)
|
|
43
|
+
-- =========================================================================
|
|
44
|
+
-- These three fields together form the unique idempotency key.
|
|
45
|
+
-- Any consumer restart will attempt to re-insert, but the constraint prevents duplicates.
|
|
46
|
+
topic TEXT NOT NULL, -- Kafka topic name (TEXT for long namespaced topics)
|
|
47
|
+
partition INTEGER NOT NULL, -- Kafka partition number
|
|
48
|
+
kafka_offset BIGINT NOT NULL, -- Kafka offset within partition
|
|
49
|
+
|
|
50
|
+
-- =========================================================================
|
|
51
|
+
-- Raw Event Data
|
|
52
|
+
-- =========================================================================
|
|
53
|
+
-- Preserved exactly as received from Kafka, no transformation applied.
|
|
54
|
+
event_key BYTEA, -- Kafka message key (nullable - not all events have keys)
|
|
55
|
+
event_value BYTEA NOT NULL, -- Kafka message value (required - the actual event payload)
|
|
56
|
+
onex_headers JSONB NOT NULL DEFAULT '{}', -- ONEX-specific headers extracted from Kafka headers
|
|
57
|
+
|
|
58
|
+
-- =========================================================================
|
|
59
|
+
-- Extracted Metadata (ALL NULLABLE)
|
|
60
|
+
-- =========================================================================
|
|
61
|
+
-- These fields are extracted from the event for query optimization.
|
|
62
|
+
-- ALL are nullable because:
|
|
63
|
+
-- 1. Malformed events must still be captured (audit requirement)
|
|
64
|
+
-- 2. Legacy events may not have all metadata fields
|
|
65
|
+
-- 3. Schema evolution may introduce new optional fields
|
|
66
|
+
-- Missing metadata must NEVER block event capture.
|
|
67
|
+
envelope_id UUID, -- Event envelope identifier (if present)
|
|
68
|
+
correlation_id UUID, -- Request correlation ID for distributed tracing
|
|
69
|
+
event_type TEXT, -- Event type discriminator (e.g., "NodeRegistered")
|
|
70
|
+
source TEXT, -- Event source identifier (e.g., "node-registration-orchestrator")
|
|
71
|
+
|
|
72
|
+
-- =========================================================================
|
|
73
|
+
-- Timestamps
|
|
74
|
+
-- =========================================================================
|
|
75
|
+
event_timestamp TIMESTAMPTZ, -- Timestamp from event payload (nullable - not all events have timestamps)
|
|
76
|
+
ledger_written_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- When this ledger entry was written (guaranteed)
|
|
77
|
+
|
|
78
|
+
-- =========================================================================
|
|
79
|
+
-- Constraints
|
|
80
|
+
-- =========================================================================
|
|
81
|
+
-- Idempotency constraint: Ensures each Kafka message is recorded exactly once.
|
|
82
|
+
-- On consumer restart, duplicate inserts will fail gracefully (ON CONFLICT DO NOTHING).
|
|
83
|
+
CONSTRAINT uk_event_ledger_kafka_position UNIQUE (topic, partition, kafka_offset)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
-- =============================================================================
|
|
87
|
+
-- INDEXES
|
|
88
|
+
-- =============================================================================
|
|
89
|
+
-- Optimized for common query patterns: correlation lookups, event type filtering,
|
|
90
|
+
-- and time-range scans.
|
|
91
|
+
|
|
92
|
+
-- Index 1: Correlation ID lookups (partial - only indexed when NOT NULL)
|
|
93
|
+
-- Use case: Distributed tracing, finding all events for a request
|
|
94
|
+
-- Partial index reduces storage overhead for events without correlation_id
|
|
95
|
+
CREATE INDEX IF NOT EXISTS idx_event_ledger_correlation_id
|
|
96
|
+
ON event_ledger (correlation_id)
|
|
97
|
+
WHERE correlation_id IS NOT NULL;
|
|
98
|
+
|
|
99
|
+
-- Index 2: Event type filtering (partial - only indexed when NOT NULL)
|
|
100
|
+
-- Use case: Finding all events of a specific type for replay or analysis
|
|
101
|
+
-- Partial index excludes malformed events without event_type
|
|
102
|
+
CREATE INDEX IF NOT EXISTS idx_event_ledger_event_type
|
|
103
|
+
ON event_ledger (event_type)
|
|
104
|
+
WHERE event_type IS NOT NULL;
|
|
105
|
+
|
|
106
|
+
-- Index 3: Timestamp ordering with fallback
|
|
107
|
+
-- Use case: Time-range queries for replay, audit, and debugging
|
|
108
|
+
-- COALESCE ensures consistent ordering even when event_timestamp is NULL
|
|
109
|
+
-- Falls back to ledger_written_at which is always populated
|
|
110
|
+
CREATE INDEX IF NOT EXISTS idx_event_ledger_event_timestamp
|
|
111
|
+
ON event_ledger (COALESCE(event_timestamp, ledger_written_at));
|
|
112
|
+
|
|
113
|
+
-- Index 4: Topic + Timestamp composite (for topic-scoped time-range queries)
|
|
114
|
+
-- Use case: Replay events from a specific topic within a time window
|
|
115
|
+
-- Common pattern: "replay all registration events from the last hour"
|
|
116
|
+
CREATE INDEX IF NOT EXISTS idx_event_ledger_topic_timestamp
|
|
117
|
+
ON event_ledger (topic, COALESCE(event_timestamp, ledger_written_at));
|
|
118
|
+
|
|
119
|
+
-- =============================================================================
|
|
120
|
+
-- COMMENTS
|
|
121
|
+
-- =============================================================================
|
|
122
|
+
|
|
123
|
+
COMMENT ON TABLE event_ledger IS
|
|
124
|
+
'Durable, append-only ledger of all events consumed from Kafka. Provides audit trail, replay capability, and idempotency guarantees.';
|
|
125
|
+
|
|
126
|
+
COMMENT ON COLUMN event_ledger.ledger_entry_id IS
|
|
127
|
+
'Auto-generated UUID primary key for this ledger entry';
|
|
128
|
+
|
|
129
|
+
COMMENT ON COLUMN event_ledger.topic IS
|
|
130
|
+
'Kafka topic name (TEXT to support long namespaced topics)';
|
|
131
|
+
|
|
132
|
+
COMMENT ON COLUMN event_ledger.partition IS
|
|
133
|
+
'Kafka partition number';
|
|
134
|
+
|
|
135
|
+
COMMENT ON COLUMN event_ledger.kafka_offset IS
|
|
136
|
+
'Kafka offset within the partition (idempotency key component)';
|
|
137
|
+
|
|
138
|
+
COMMENT ON COLUMN event_ledger.event_key IS
|
|
139
|
+
'Raw Kafka message key as BYTEA (nullable - not all events have keys)';
|
|
140
|
+
|
|
141
|
+
COMMENT ON COLUMN event_ledger.event_value IS
|
|
142
|
+
'Raw Kafka message value as BYTEA (required - the actual event payload)';
|
|
143
|
+
|
|
144
|
+
COMMENT ON COLUMN event_ledger.onex_headers IS
|
|
145
|
+
'ONEX-specific headers extracted from Kafka headers as JSONB';
|
|
146
|
+
|
|
147
|
+
COMMENT ON COLUMN event_ledger.envelope_id IS
|
|
148
|
+
'Event envelope identifier extracted from payload (nullable for malformed events)';
|
|
149
|
+
|
|
150
|
+
COMMENT ON COLUMN event_ledger.correlation_id IS
|
|
151
|
+
'Request correlation ID for distributed tracing (nullable for legacy events)';
|
|
152
|
+
|
|
153
|
+
COMMENT ON COLUMN event_ledger.event_type IS
|
|
154
|
+
'Event type discriminator extracted from payload (nullable for malformed events)';
|
|
155
|
+
|
|
156
|
+
COMMENT ON COLUMN event_ledger.source IS
|
|
157
|
+
'Event source identifier (nullable for events without source metadata)';
|
|
158
|
+
|
|
159
|
+
COMMENT ON COLUMN event_ledger.event_timestamp IS
|
|
160
|
+
'Timestamp from event payload (nullable - falls back to ledger_written_at)';
|
|
161
|
+
|
|
162
|
+
COMMENT ON COLUMN event_ledger.ledger_written_at IS
|
|
163
|
+
'Timestamp when this entry was written to the ledger (guaranteed, used for ordering fallback)';
|
|
164
|
+
|
|
165
|
+
COMMENT ON CONSTRAINT uk_event_ledger_kafka_position ON event_ledger IS
|
|
166
|
+
'Idempotency constraint: ensures each Kafka message is recorded exactly once';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- Migration: 001_drop_event_ledger.sql
|
|
2
|
+
-- Purpose: Rollback for 001_create_event_ledger.sql
|
|
3
|
+
-- Author: ONEX Infrastructure Team
|
|
4
|
+
-- Date: 2026-01-29
|
|
5
|
+
--
|
|
6
|
+
-- WARNING: This migration is DESTRUCTIVE. All data in event_ledger will be lost.
|
|
7
|
+
-- Use only in development or when intentionally resetting the ledger.
|
|
8
|
+
--
|
|
9
|
+
-- This rollback drops:
|
|
10
|
+
-- - Table: event_ledger
|
|
11
|
+
-- - Constraint: uk_event_ledger_kafka_position (dropped with table)
|
|
12
|
+
-- - Index: idx_event_ledger_correlation_id (dropped with table)
|
|
13
|
+
-- - Index: idx_event_ledger_event_type (dropped with table)
|
|
14
|
+
-- - Index: idx_event_ledger_event_timestamp (dropped with table)
|
|
15
|
+
-- - Index: idx_event_ledger_topic_timestamp (dropped with table)
|
|
16
|
+
-- - All table and column comments (dropped with table)
|
|
17
|
+
|
|
18
|
+
DROP TABLE IF EXISTS event_ledger CASCADE;
|
|
@@ -199,6 +199,7 @@ import asyncio
|
|
|
199
199
|
import inspect
|
|
200
200
|
import json
|
|
201
201
|
import logging
|
|
202
|
+
import os
|
|
202
203
|
import time
|
|
203
204
|
from collections.abc import AsyncIterator, Awaitable, Callable
|
|
204
205
|
from contextlib import asynccontextmanager
|
|
@@ -210,6 +211,7 @@ from omnibase_core.enums import EnumNodeKind
|
|
|
210
211
|
from omnibase_core.models.events.model_event_envelope import ModelEventEnvelope
|
|
211
212
|
from omnibase_core.models.primitives.model_semver import ModelSemVer
|
|
212
213
|
from omnibase_infra.capabilities import ContractCapabilityExtractor
|
|
214
|
+
from omnibase_infra.constants_topic_patterns import TOPIC_NAME_PATTERN
|
|
213
215
|
from omnibase_infra.enums import EnumInfraTransportType, EnumIntrospectionReason
|
|
214
216
|
from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationError
|
|
215
217
|
from omnibase_infra.models.discovery import (
|
|
@@ -224,6 +226,10 @@ from omnibase_infra.models.registration import (
|
|
|
224
226
|
ModelNodeCapabilities,
|
|
225
227
|
ModelNodeHeartbeatEvent,
|
|
226
228
|
)
|
|
229
|
+
from omnibase_infra.models.registration.model_node_event_bus_config import (
|
|
230
|
+
ModelEventBusTopicEntry,
|
|
231
|
+
ModelNodeEventBusConfig,
|
|
232
|
+
)
|
|
227
233
|
from omnibase_infra.models.registration.model_node_introspection_event import (
|
|
228
234
|
ModelNodeIntrospectionEvent,
|
|
229
235
|
)
|
|
@@ -237,11 +243,6 @@ if TYPE_CHECKING:
|
|
|
237
243
|
|
|
238
244
|
logger = logging.getLogger(__name__)
|
|
239
245
|
|
|
240
|
-
# Event topic constants
|
|
241
|
-
INTROSPECTION_TOPIC = "node.introspection"
|
|
242
|
-
HEARTBEAT_TOPIC = "node.heartbeat"
|
|
243
|
-
REQUEST_INTROSPECTION_TOPIC = "node.request_introspection"
|
|
244
|
-
|
|
245
246
|
# Performance threshold constants (in milliseconds)
|
|
246
247
|
PERF_THRESHOLD_GET_CAPABILITIES_MS = 50.0
|
|
247
248
|
PERF_THRESHOLD_DISCOVER_CAPABILITIES_MS = 30.0
|
|
@@ -977,6 +978,109 @@ class MixinNodeIntrospection:
|
|
|
977
978
|
)
|
|
978
979
|
return None
|
|
979
980
|
|
|
981
|
+
def _extract_event_bus_config(
|
|
982
|
+
self,
|
|
983
|
+
env_prefix: str,
|
|
984
|
+
) -> ModelNodeEventBusConfig | None:
|
|
985
|
+
"""Extract and resolve event_bus config from contract.
|
|
986
|
+
|
|
987
|
+
Extracts topic suffixes from the contract's event_bus subcontract and
|
|
988
|
+
resolves them to full environment-qualified topic strings.
|
|
989
|
+
|
|
990
|
+
Topic Resolution:
|
|
991
|
+
Contract topics are suffixes (e.g., "onex.evt.intent-classified.v1").
|
|
992
|
+
This method prepends the environment prefix to create full topics
|
|
993
|
+
(e.g., "dev.onex.evt.intent-classified.v1").
|
|
994
|
+
|
|
995
|
+
Args:
|
|
996
|
+
env_prefix: Environment prefix (e.g., "dev", "prod", "staging").
|
|
997
|
+
Must be a valid identifier without dots or special characters.
|
|
998
|
+
|
|
999
|
+
Returns:
|
|
1000
|
+
Resolved event bus config with full topic strings, or None if:
|
|
1001
|
+
- No contract is configured (_introspection_contract is None)
|
|
1002
|
+
- Contract has no event_bus subcontract
|
|
1003
|
+
- event_bus subcontract has no publish_topics or subscribe_topics
|
|
1004
|
+
|
|
1005
|
+
Raises:
|
|
1006
|
+
ValueError: If topic resolution fails due to unresolved placeholders
|
|
1007
|
+
(e.g., "{env}" or "{namespace}" remaining in the resolved topic).
|
|
1008
|
+
This is a fail-fast mechanism to prevent misconfigured topics
|
|
1009
|
+
from being published to the registry.
|
|
1010
|
+
|
|
1011
|
+
Example:
|
|
1012
|
+
>>> config = self._extract_event_bus_config("dev")
|
|
1013
|
+
>>> config.publish_topic_strings
|
|
1014
|
+
['dev.onex.evt.node-registered.v1']
|
|
1015
|
+
|
|
1016
|
+
See Also:
|
|
1017
|
+
- ModelEventBusSubcontract: Contract model with topic suffixes
|
|
1018
|
+
- ModelNodeEventBusConfig: Registry storage model with full topics
|
|
1019
|
+
"""
|
|
1020
|
+
if self._introspection_contract is None:
|
|
1021
|
+
return None
|
|
1022
|
+
|
|
1023
|
+
# Get event_bus subcontract if present
|
|
1024
|
+
event_bus_sub = getattr(self._introspection_contract, "event_bus", None)
|
|
1025
|
+
if event_bus_sub is None:
|
|
1026
|
+
return None
|
|
1027
|
+
|
|
1028
|
+
# Get topic suffix lists from the subcontract
|
|
1029
|
+
publish_suffixes: list[str] = (
|
|
1030
|
+
getattr(event_bus_sub, "publish_topics", None) or []
|
|
1031
|
+
)
|
|
1032
|
+
subscribe_suffixes: list[str] = (
|
|
1033
|
+
getattr(event_bus_sub, "subscribe_topics", None) or []
|
|
1034
|
+
)
|
|
1035
|
+
|
|
1036
|
+
if not publish_suffixes and not subscribe_suffixes:
|
|
1037
|
+
return None
|
|
1038
|
+
|
|
1039
|
+
def resolve_topic(suffix: str) -> str:
|
|
1040
|
+
"""Resolve topic suffix to full topic with env prefix."""
|
|
1041
|
+
# Full topic format: {env}.{suffix}
|
|
1042
|
+
# Strip whitespace from suffix to handle YAML formatting artifacts
|
|
1043
|
+
suffix = suffix.strip()
|
|
1044
|
+
|
|
1045
|
+
# Fail-fast: check for unresolved placeholders BEFORE format validation
|
|
1046
|
+
# This provides more helpful error messages when placeholders aren't resolved
|
|
1047
|
+
if "{" in suffix or "}" in suffix:
|
|
1048
|
+
raise ValueError(
|
|
1049
|
+
f"Unresolved placeholder in topic: '{suffix}'. "
|
|
1050
|
+
"Ensure all placeholders like {env} or {namespace} are resolved "
|
|
1051
|
+
"before topic validation."
|
|
1052
|
+
)
|
|
1053
|
+
|
|
1054
|
+
# Validate suffix format (alphanumeric, dots, hyphens, underscores)
|
|
1055
|
+
if not TOPIC_NAME_PATTERN.match(suffix):
|
|
1056
|
+
context = ModelInfraErrorContext.with_correlation(
|
|
1057
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
1058
|
+
operation="_extract_event_bus_config",
|
|
1059
|
+
target_name=suffix,
|
|
1060
|
+
)
|
|
1061
|
+
raise ProtocolConfigurationError(
|
|
1062
|
+
f"Invalid topic suffix format: '{suffix}'. "
|
|
1063
|
+
"Topics must contain only alphanumeric characters, dots, hyphens, and underscores.",
|
|
1064
|
+
context=context,
|
|
1065
|
+
parameter="topic_suffix",
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
full_topic = f"{env_prefix}.{suffix}"
|
|
1069
|
+
|
|
1070
|
+
return full_topic
|
|
1071
|
+
|
|
1072
|
+
def build_entry(suffix: str) -> ModelEventBusTopicEntry:
|
|
1073
|
+
"""Build topic entry from suffix."""
|
|
1074
|
+
return ModelEventBusTopicEntry(
|
|
1075
|
+
topic=resolve_topic(suffix),
|
|
1076
|
+
# Metadata fields left as defaults (tooling-only)
|
|
1077
|
+
)
|
|
1078
|
+
|
|
1079
|
+
return ModelNodeEventBusConfig(
|
|
1080
|
+
publish_topics=[build_entry(s) for s in publish_suffixes],
|
|
1081
|
+
subscribe_topics=[build_entry(s) for s in subscribe_suffixes],
|
|
1082
|
+
)
|
|
1083
|
+
|
|
980
1084
|
async def get_capabilities(self) -> ModelDiscoveredCapabilities:
|
|
981
1085
|
"""Extract node capabilities via reflection.
|
|
982
1086
|
|
|
@@ -1355,6 +1459,28 @@ class MixinNodeIntrospection:
|
|
|
1355
1459
|
self._introspection_contract
|
|
1356
1460
|
)
|
|
1357
1461
|
|
|
1462
|
+
# Extract event_bus config from contract (OMN-1613)
|
|
1463
|
+
# Resolves topic suffixes to full environment-qualified topics
|
|
1464
|
+
# ValueError from _extract_event_bus_config (unresolved placeholders) is
|
|
1465
|
+
# wrapped in ProtocolConfigurationError for consistent error handling.
|
|
1466
|
+
# ProtocolConfigurationError (invalid format) propagates directly.
|
|
1467
|
+
event_bus_config: ModelNodeEventBusConfig | None = None
|
|
1468
|
+
env_prefix = os.getenv("ONEX_ENV", "dev")
|
|
1469
|
+
try:
|
|
1470
|
+
event_bus_config = self._extract_event_bus_config(env_prefix)
|
|
1471
|
+
except ValueError as e:
|
|
1472
|
+
# Wrap ValueError in ProtocolConfigurationError for fail-fast behavior
|
|
1473
|
+
context = ModelInfraErrorContext.with_correlation(
|
|
1474
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
1475
|
+
operation="get_introspection_data",
|
|
1476
|
+
target_name="event_bus",
|
|
1477
|
+
)
|
|
1478
|
+
raise ProtocolConfigurationError(
|
|
1479
|
+
f"Event bus extraction failed: {e}",
|
|
1480
|
+
context=context,
|
|
1481
|
+
parameter="event_bus",
|
|
1482
|
+
) from e
|
|
1483
|
+
|
|
1358
1484
|
# Create event with performance metrics (metrics is already Pydantic model)
|
|
1359
1485
|
event = ModelNodeIntrospectionEvent(
|
|
1360
1486
|
node_id=node_id_uuid,
|
|
@@ -1369,6 +1495,7 @@ class MixinNodeIntrospection:
|
|
|
1369
1495
|
correlation_id=uuid4(),
|
|
1370
1496
|
timestamp=datetime.now(UTC),
|
|
1371
1497
|
performance_metrics=metrics,
|
|
1498
|
+
event_bus=event_bus_config,
|
|
1372
1499
|
)
|
|
1373
1500
|
|
|
1374
1501
|
# Update cache - cast the model_dump output to our typed dict since we know
|
|
@@ -1549,6 +1676,7 @@ class MixinNodeIntrospection:
|
|
|
1549
1676
|
extra={
|
|
1550
1677
|
"node_id": self._introspection_node_id,
|
|
1551
1678
|
"reason": reason_enum.value,
|
|
1679
|
+
"correlation_id": str(final_correlation_id),
|
|
1552
1680
|
"error_type": type(e).__name__,
|
|
1553
1681
|
"error_message": str(e),
|
|
1554
1682
|
},
|
|
@@ -1580,6 +1708,9 @@ class MixinNodeIntrospection:
|
|
|
1580
1708
|
if self._introspection_event_bus is None:
|
|
1581
1709
|
return False
|
|
1582
1710
|
|
|
1711
|
+
# Generate correlation_id early for reliable exception logging
|
|
1712
|
+
heartbeat_correlation_id = uuid4()
|
|
1713
|
+
|
|
1583
1714
|
try:
|
|
1584
1715
|
# Calculate uptime
|
|
1585
1716
|
uptime_seconds = 0.0
|
|
@@ -1593,7 +1724,10 @@ class MixinNodeIntrospection:
|
|
|
1593
1724
|
logger.warning(
|
|
1594
1725
|
"Node ID not initialized, using nil UUID in heartbeat - "
|
|
1595
1726
|
"ensure initialize_introspection() was called correctly",
|
|
1596
|
-
extra={
|
|
1727
|
+
extra={
|
|
1728
|
+
"operation": "_publish_heartbeat",
|
|
1729
|
+
"correlation_id": str(heartbeat_correlation_id),
|
|
1730
|
+
},
|
|
1597
1731
|
)
|
|
1598
1732
|
# Use nil UUID (all zeros) as sentinel for uninitialized node
|
|
1599
1733
|
node_id = UUID("00000000-0000-0000-0000-000000000000")
|
|
@@ -1605,7 +1739,11 @@ class MixinNodeIntrospection:
|
|
|
1605
1739
|
logger.warning(
|
|
1606
1740
|
"Node type not initialized, using EFFECT in heartbeat - "
|
|
1607
1741
|
"ensure initialize_introspection() was called correctly",
|
|
1608
|
-
extra={
|
|
1742
|
+
extra={
|
|
1743
|
+
"node_id": str(node_id),
|
|
1744
|
+
"operation": "_publish_heartbeat",
|
|
1745
|
+
"correlation_id": str(heartbeat_correlation_id),
|
|
1746
|
+
},
|
|
1609
1747
|
)
|
|
1610
1748
|
node_type = EnumNodeKind.EFFECT
|
|
1611
1749
|
|
|
@@ -1620,7 +1758,7 @@ class MixinNodeIntrospection:
|
|
|
1620
1758
|
node_type=node_type,
|
|
1621
1759
|
uptime_seconds=uptime_seconds,
|
|
1622
1760
|
active_operations_count=active_ops_count,
|
|
1623
|
-
correlation_id=
|
|
1761
|
+
correlation_id=heartbeat_correlation_id,
|
|
1624
1762
|
timestamp=now, # Required: time injection pattern
|
|
1625
1763
|
)
|
|
1626
1764
|
|
|
@@ -1653,6 +1791,7 @@ class MixinNodeIntrospection:
|
|
|
1653
1791
|
f"Published heartbeat for {self._introspection_node_id}",
|
|
1654
1792
|
extra={
|
|
1655
1793
|
"node_id": self._introspection_node_id,
|
|
1794
|
+
"correlation_id": str(heartbeat_correlation_id),
|
|
1656
1795
|
"uptime_seconds": uptime_seconds,
|
|
1657
1796
|
"active_operations": active_ops_count,
|
|
1658
1797
|
"topic": topic,
|
|
@@ -1667,6 +1806,7 @@ class MixinNodeIntrospection:
|
|
|
1667
1806
|
f"Failed to publish heartbeat for {self._introspection_node_id}",
|
|
1668
1807
|
extra={
|
|
1669
1808
|
"node_id": self._introspection_node_id,
|
|
1809
|
+
"correlation_id": str(heartbeat_correlation_id),
|
|
1670
1810
|
"error_type": type(e).__name__,
|
|
1671
1811
|
"error_message": str(e),
|
|
1672
1812
|
},
|
|
@@ -1696,12 +1836,17 @@ class MixinNodeIntrospection:
|
|
|
1696
1836
|
)
|
|
1697
1837
|
|
|
1698
1838
|
while not self._introspection_stop_event.is_set():
|
|
1839
|
+
# Generate correlation_id for this loop iteration for traceability
|
|
1840
|
+
loop_correlation_id = uuid4()
|
|
1699
1841
|
try:
|
|
1700
1842
|
await self._publish_heartbeat()
|
|
1701
1843
|
except asyncio.CancelledError:
|
|
1702
1844
|
logger.debug(
|
|
1703
1845
|
f"Heartbeat loop cancelled for {self._introspection_node_id}",
|
|
1704
|
-
extra={
|
|
1846
|
+
extra={
|
|
1847
|
+
"node_id": self._introspection_node_id,
|
|
1848
|
+
"correlation_id": str(loop_correlation_id),
|
|
1849
|
+
},
|
|
1705
1850
|
)
|
|
1706
1851
|
break
|
|
1707
1852
|
except Exception as e:
|
|
@@ -1711,6 +1856,7 @@ class MixinNodeIntrospection:
|
|
|
1711
1856
|
f"Error in heartbeat loop for {self._introspection_node_id}",
|
|
1712
1857
|
extra={
|
|
1713
1858
|
"node_id": self._introspection_node_id,
|
|
1859
|
+
"correlation_id": str(loop_correlation_id),
|
|
1714
1860
|
"error_type": type(e).__name__,
|
|
1715
1861
|
"error_message": str(e),
|
|
1716
1862
|
},
|
|
@@ -1783,9 +1929,17 @@ class MixinNodeIntrospection:
|
|
|
1783
1929
|
"""
|
|
1784
1930
|
return consecutive_failures == 1 or consecutive_failures % threshold == 0
|
|
1785
1931
|
|
|
1786
|
-
async def _cleanup_registry_subscription(
|
|
1787
|
-
|
|
1932
|
+
async def _cleanup_registry_subscription(
|
|
1933
|
+
self, correlation_id: UUID | None = None
|
|
1934
|
+
) -> None:
|
|
1935
|
+
"""Clean up the current registry subscription.
|
|
1936
|
+
|
|
1937
|
+
Args:
|
|
1938
|
+
correlation_id: Optional correlation ID for traceability in logs.
|
|
1939
|
+
If not provided, a new one will be generated.
|
|
1940
|
+
"""
|
|
1788
1941
|
if self._registry_unsubscribe is not None:
|
|
1942
|
+
cleanup_correlation_id = correlation_id or uuid4()
|
|
1789
1943
|
try:
|
|
1790
1944
|
result = self._registry_unsubscribe()
|
|
1791
1945
|
if asyncio.iscoroutine(result):
|
|
@@ -1796,6 +1950,7 @@ class MixinNodeIntrospection:
|
|
|
1796
1950
|
f"{self._introspection_node_id}",
|
|
1797
1951
|
extra={
|
|
1798
1952
|
"node_id": self._introspection_node_id,
|
|
1953
|
+
"correlation_id": str(cleanup_correlation_id),
|
|
1799
1954
|
"error_type": type(cleanup_error).__name__,
|
|
1800
1955
|
"error_message": str(cleanup_error),
|
|
1801
1956
|
},
|
|
@@ -1814,12 +1969,14 @@ class MixinNodeIntrospection:
|
|
|
1814
1969
|
Args:
|
|
1815
1970
|
message: The incoming event message (implements ProtocolEventMessage protocol)
|
|
1816
1971
|
"""
|
|
1972
|
+
# Generate correlation_id for this request for traceability
|
|
1973
|
+
request_correlation_id = uuid4()
|
|
1817
1974
|
try:
|
|
1818
1975
|
await self._process_introspection_request(message)
|
|
1819
1976
|
# Reset failure counter on success
|
|
1820
1977
|
self._registry_callback_consecutive_failures = 0
|
|
1821
1978
|
except Exception as e:
|
|
1822
|
-
self._handle_request_error(e)
|
|
1979
|
+
self._handle_request_error(e, request_correlation_id)
|
|
1823
1980
|
|
|
1824
1981
|
async def _process_introspection_request(
|
|
1825
1982
|
self, message: ProtocolEventMessage
|
|
@@ -1859,13 +2016,14 @@ class MixinNodeIntrospection:
|
|
|
1859
2016
|
correlation_id=correlation_id,
|
|
1860
2017
|
)
|
|
1861
2018
|
|
|
1862
|
-
def _handle_request_error(self, error: Exception) -> None:
|
|
2019
|
+
def _handle_request_error(self, error: Exception, correlation_id: UUID) -> None:
|
|
1863
2020
|
"""Handle error during introspection request processing.
|
|
1864
2021
|
|
|
1865
2022
|
Tracks consecutive failures and rate-limits error logging.
|
|
1866
2023
|
|
|
1867
2024
|
Args:
|
|
1868
2025
|
error: The exception that occurred
|
|
2026
|
+
correlation_id: Correlation ID for traceability in logs
|
|
1869
2027
|
"""
|
|
1870
2028
|
# Track consecutive failures for rate-limited logging
|
|
1871
2029
|
self._registry_callback_consecutive_failures += 1
|
|
@@ -1880,6 +2038,7 @@ class MixinNodeIntrospection:
|
|
|
1880
2038
|
f"Error handling introspection request for {self._introspection_node_id}",
|
|
1881
2039
|
extra={
|
|
1882
2040
|
"node_id": self._introspection_node_id,
|
|
2041
|
+
"correlation_id": str(correlation_id),
|
|
1883
2042
|
"error_type": type(error).__name__,
|
|
1884
2043
|
"error_message": str(error),
|
|
1885
2044
|
"consecutive_failures": self._registry_callback_consecutive_failures,
|
|
@@ -1895,6 +2054,7 @@ class MixinNodeIntrospection:
|
|
|
1895
2054
|
f"(failure {self._registry_callback_consecutive_failures})",
|
|
1896
2055
|
extra={
|
|
1897
2056
|
"node_id": self._introspection_node_id,
|
|
2057
|
+
"correlation_id": str(correlation_id),
|
|
1898
2058
|
"error_type": type(error).__name__,
|
|
1899
2059
|
"consecutive_failures": self._registry_callback_consecutive_failures,
|
|
1900
2060
|
},
|
|
@@ -2028,6 +2188,8 @@ class MixinNodeIntrospection:
|
|
|
2028
2188
|
# Retry loop with exponential backoff for subscription failures
|
|
2029
2189
|
retry_count = 0
|
|
2030
2190
|
while not self._introspection_stop_event.is_set():
|
|
2191
|
+
# Generate correlation_id for this subscription attempt for traceability
|
|
2192
|
+
subscription_correlation_id = uuid4()
|
|
2031
2193
|
try:
|
|
2032
2194
|
if await self._attempt_subscription():
|
|
2033
2195
|
# Wait for stop signal
|
|
@@ -2038,13 +2200,20 @@ class MixinNodeIntrospection:
|
|
|
2038
2200
|
except asyncio.CancelledError:
|
|
2039
2201
|
logger.debug(
|
|
2040
2202
|
f"Registry listener cancelled for {self._introspection_node_id}",
|
|
2041
|
-
extra={
|
|
2203
|
+
extra={
|
|
2204
|
+
"node_id": self._introspection_node_id,
|
|
2205
|
+
"correlation_id": str(subscription_correlation_id),
|
|
2206
|
+
},
|
|
2042
2207
|
)
|
|
2043
2208
|
break
|
|
2044
2209
|
except Exception as e:
|
|
2045
2210
|
retry_count += 1
|
|
2046
2211
|
if not await self._handle_subscription_error(
|
|
2047
|
-
e,
|
|
2212
|
+
e,
|
|
2213
|
+
retry_count,
|
|
2214
|
+
max_retries,
|
|
2215
|
+
base_backoff_seconds,
|
|
2216
|
+
subscription_correlation_id,
|
|
2048
2217
|
):
|
|
2049
2218
|
break
|
|
2050
2219
|
|
|
@@ -2062,6 +2231,7 @@ class MixinNodeIntrospection:
|
|
|
2062
2231
|
retry_count: int,
|
|
2063
2232
|
max_retries: int,
|
|
2064
2233
|
base_backoff_seconds: float,
|
|
2234
|
+
correlation_id: UUID,
|
|
2065
2235
|
) -> bool:
|
|
2066
2236
|
"""Handle subscription error with retry logic.
|
|
2067
2237
|
|
|
@@ -2070,6 +2240,7 @@ class MixinNodeIntrospection:
|
|
|
2070
2240
|
retry_count: Current retry attempt number
|
|
2071
2241
|
max_retries: Maximum retry attempts
|
|
2072
2242
|
base_backoff_seconds: Base backoff time for exponential retry
|
|
2243
|
+
correlation_id: Correlation ID for traceability in logs
|
|
2073
2244
|
|
|
2074
2245
|
Returns:
|
|
2075
2246
|
True if should continue retrying, False if should stop
|
|
@@ -2078,6 +2249,7 @@ class MixinNodeIntrospection:
|
|
|
2078
2249
|
f"Error in registry listener for {self._introspection_node_id}",
|
|
2079
2250
|
extra={
|
|
2080
2251
|
"node_id": self._introspection_node_id,
|
|
2252
|
+
"correlation_id": str(correlation_id),
|
|
2081
2253
|
"error_type": type(error).__name__,
|
|
2082
2254
|
"error_message": str(error),
|
|
2083
2255
|
"retry_count": retry_count,
|
|
@@ -2087,7 +2259,7 @@ class MixinNodeIntrospection:
|
|
|
2087
2259
|
)
|
|
2088
2260
|
|
|
2089
2261
|
# Clean up any partial subscription before retry
|
|
2090
|
-
await self._cleanup_registry_subscription()
|
|
2262
|
+
await self._cleanup_registry_subscription(correlation_id)
|
|
2091
2263
|
|
|
2092
2264
|
# Check if we should retry
|
|
2093
2265
|
if retry_count >= max_retries:
|
|
@@ -2095,6 +2267,7 @@ class MixinNodeIntrospection:
|
|
|
2095
2267
|
"Registry listener exhausted retries",
|
|
2096
2268
|
extra={
|
|
2097
2269
|
"node_id": self._introspection_node_id,
|
|
2270
|
+
"correlation_id": str(correlation_id),
|
|
2098
2271
|
"retry_count": retry_count,
|
|
2099
2272
|
"max_retries": max_retries,
|
|
2100
2273
|
"error_type": type(error).__name__,
|
|
@@ -2485,13 +2658,10 @@ class MixinNodeIntrospection:
|
|
|
2485
2658
|
|
|
2486
2659
|
|
|
2487
2660
|
__all__ = [
|
|
2488
|
-
"HEARTBEAT_TOPIC",
|
|
2489
|
-
"INTROSPECTION_TOPIC",
|
|
2490
2661
|
"PERF_THRESHOLD_CACHE_HIT_MS",
|
|
2491
2662
|
"PERF_THRESHOLD_DISCOVER_CAPABILITIES_MS",
|
|
2492
2663
|
"PERF_THRESHOLD_GET_CAPABILITIES_MS",
|
|
2493
2664
|
"PERF_THRESHOLD_GET_INTROSPECTION_DATA_MS",
|
|
2494
|
-
"REQUEST_INTROSPECTION_TOPIC",
|
|
2495
2665
|
"DiscoveredCapabilitiesCacheDict", # TypedDict for cached discovered capabilities
|
|
2496
2666
|
"IntrospectionCacheDict",
|
|
2497
2667
|
"MixinNodeIntrospection",
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
This module exports all infrastructure-specific Pydantic models.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from omnibase_infra.models.bindings import (
|
|
9
|
+
ModelParsedBinding,
|
|
10
|
+
)
|
|
8
11
|
from omnibase_infra.models.dispatch import (
|
|
9
12
|
EnumDispatchStatus,
|
|
10
13
|
EnumTopicStandard,
|
|
@@ -22,6 +25,7 @@ from omnibase_infra.models.errors import ModelHandlerValidationError
|
|
|
22
25
|
from omnibase_infra.models.handlers import ModelHandlerIdentifier
|
|
23
26
|
from omnibase_infra.models.health import ModelHealthCheckResult
|
|
24
27
|
from omnibase_infra.models.logging import ModelLogContext
|
|
28
|
+
from omnibase_infra.models.model_node_identity import ModelNodeIdentity
|
|
25
29
|
from omnibase_infra.models.model_retry_error_classification import (
|
|
26
30
|
ModelRetryErrorClassification,
|
|
27
31
|
)
|
|
@@ -75,6 +79,8 @@ from omnibase_infra.models.validation import (
|
|
|
75
79
|
)
|
|
76
80
|
|
|
77
81
|
__all__: list[str] = [
|
|
82
|
+
# Binding models
|
|
83
|
+
"ModelParsedBinding",
|
|
78
84
|
# Dispatch models
|
|
79
85
|
"EnumDispatchStatus",
|
|
80
86
|
"EnumTopicStandard",
|
|
@@ -108,6 +114,8 @@ __all__: list[str] = [
|
|
|
108
114
|
"ModelLogContext",
|
|
109
115
|
"ModelNodeCapabilities",
|
|
110
116
|
"ModelNodeHeartbeatEvent",
|
|
117
|
+
# Node identity model
|
|
118
|
+
"ModelNodeIdentity",
|
|
111
119
|
"ModelNodeIntrospectionEvent",
|
|
112
120
|
"ModelNodeMetadata",
|
|
113
121
|
"ModelNodeRegistration",
|