omnibase_infra 0.2.1__py3-none-any.whl → 0.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omnibase_infra/__init__.py +1 -1
- omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
- omnibase_infra/capabilities/__init__.py +15 -0
- omnibase_infra/capabilities/capability_inference_rules.py +211 -0
- omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
- omnibase_infra/capabilities/intent_type_extractor.py +160 -0
- omnibase_infra/cli/commands.py +1 -1
- omnibase_infra/configs/widget_mapping.yaml +176 -0
- omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +5 -2
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +5 -2
- omnibase_infra/enums/__init__.py +6 -0
- omnibase_infra/enums/enum_handler_error_type.py +10 -0
- omnibase_infra/enums/enum_handler_source_mode.py +72 -0
- omnibase_infra/enums/enum_kafka_acks.py +99 -0
- omnibase_infra/errors/error_compute_registry.py +4 -1
- omnibase_infra/errors/error_event_bus_registry.py +4 -1
- omnibase_infra/errors/error_infra.py +3 -1
- omnibase_infra/errors/error_policy_registry.py +4 -1
- omnibase_infra/event_bus/event_bus_kafka.py +1 -1
- omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +59 -10
- omnibase_infra/handlers/__init__.py +8 -1
- omnibase_infra/handlers/handler_consul.py +7 -1
- omnibase_infra/handlers/handler_db.py +10 -3
- omnibase_infra/handlers/handler_graph.py +10 -5
- omnibase_infra/handlers/handler_http.py +8 -2
- omnibase_infra/handlers/handler_intent.py +387 -0
- omnibase_infra/handlers/handler_mcp.py +745 -63
- omnibase_infra/handlers/handler_vault.py +11 -5
- omnibase_infra/handlers/mixins/mixin_consul_kv.py +4 -3
- omnibase_infra/handlers/mixins/mixin_consul_service.py +2 -1
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +7 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +308 -4
- omnibase_infra/handlers/service_discovery/models/model_service_info.py +10 -0
- omnibase_infra/mixins/mixin_async_circuit_breaker.py +3 -2
- omnibase_infra/mixins/mixin_node_introspection.py +42 -7
- omnibase_infra/mixins/mixin_retry_execution.py +1 -1
- omnibase_infra/models/discovery/model_introspection_config.py +11 -0
- omnibase_infra/models/handlers/__init__.py +48 -5
- omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
- omnibase_infra/models/handlers/model_contract_discovery_result.py +6 -4
- omnibase_infra/models/handlers/model_handler_descriptor.py +15 -0
- omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
- omnibase_infra/models/mcp/__init__.py +15 -0
- omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
- omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
- omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
- omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
- omnibase_infra/models/registration/model_node_capabilities.py +11 -0
- omnibase_infra/models/registration/model_node_introspection_event.py +9 -0
- omnibase_infra/models/runtime/model_handler_contract.py +25 -9
- omnibase_infra/models/runtime/model_loaded_handler.py +9 -0
- omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +0 -5
- omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +17 -10
- omnibase_infra/nodes/effects/contract.yaml +0 -5
- omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +7 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +86 -1
- omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +3 -3
- omnibase_infra/nodes/node_registration_orchestrator/plugin.py +1 -1
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +9 -8
- omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +4 -3
- omnibase_infra/nodes/node_registration_orchestrator/wiring.py +14 -13
- omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +0 -5
- omnibase_infra/nodes/node_registration_storage_effect/node.py +4 -1
- omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +47 -26
- omnibase_infra/nodes/node_registry_effect/contract.yaml +0 -5
- omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +2 -1
- omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +28 -20
- omnibase_infra/plugins/examples/plugin_json_normalizer.py +2 -2
- omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +2 -2
- omnibase_infra/plugins/plugin_compute_base.py +16 -2
- omnibase_infra/protocols/__init__.py +2 -0
- omnibase_infra/protocols/protocol_container_aware.py +200 -0
- omnibase_infra/protocols/protocol_event_projector.py +1 -1
- omnibase_infra/runtime/__init__.py +90 -1
- omnibase_infra/runtime/binding_config_resolver.py +102 -37
- omnibase_infra/runtime/constants_notification.py +75 -0
- omnibase_infra/runtime/contract_handler_discovery.py +6 -1
- omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
- omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
- omnibase_infra/runtime/handler_contract_source.py +267 -186
- omnibase_infra/runtime/handler_identity.py +81 -0
- omnibase_infra/runtime/handler_plugin_loader.py +19 -2
- omnibase_infra/runtime/handler_registry.py +11 -3
- omnibase_infra/runtime/handler_source_resolver.py +326 -0
- omnibase_infra/runtime/mixin_semver_cache.py +25 -1
- omnibase_infra/runtime/mixins/__init__.py +7 -0
- omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
- omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +31 -10
- omnibase_infra/runtime/models/__init__.py +24 -0
- omnibase_infra/runtime/models/model_health_check_result.py +2 -1
- omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
- omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
- omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
- omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
- omnibase_infra/runtime/projector_plugin_loader.py +1 -1
- omnibase_infra/runtime/projector_shell.py +229 -1
- omnibase_infra/runtime/protocol_lifecycle_executor.py +6 -6
- omnibase_infra/runtime/protocols/__init__.py +10 -0
- omnibase_infra/runtime/registry/registry_protocol_binding.py +16 -15
- omnibase_infra/runtime/registry_contract_source.py +693 -0
- omnibase_infra/runtime/registry_policy.py +9 -326
- omnibase_infra/runtime/secret_resolver.py +4 -2
- omnibase_infra/runtime/service_kernel.py +11 -3
- omnibase_infra/runtime/service_message_dispatch_engine.py +4 -2
- omnibase_infra/runtime/service_runtime_host_process.py +589 -106
- omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
- omnibase_infra/runtime/transition_notification_publisher.py +764 -0
- omnibase_infra/runtime/util_container_wiring.py +6 -5
- omnibase_infra/runtime/util_wiring.py +17 -4
- omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
- omnibase_infra/services/__init__.py +21 -0
- omnibase_infra/services/corpus_capture.py +7 -1
- omnibase_infra/services/mcp/__init__.py +31 -0
- omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
- omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
- omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
- omnibase_infra/services/mcp/service_mcp_tool_sync.py +547 -0
- omnibase_infra/services/registry_api/__init__.py +40 -0
- omnibase_infra/services/registry_api/main.py +261 -0
- omnibase_infra/services/registry_api/models/__init__.py +66 -0
- omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
- omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
- omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
- omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
- omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
- omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
- omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
- omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
- omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
- omnibase_infra/services/registry_api/models/model_warning.py +49 -0
- omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
- omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
- omnibase_infra/services/registry_api/routes.py +371 -0
- omnibase_infra/services/registry_api/service.py +837 -0
- omnibase_infra/services/service_capability_query.py +4 -4
- omnibase_infra/services/service_health.py +3 -2
- omnibase_infra/services/service_timeout_emitter.py +20 -3
- omnibase_infra/services/service_timeout_scanner.py +7 -3
- omnibase_infra/services/session/__init__.py +56 -0
- omnibase_infra/services/session/config_consumer.py +120 -0
- omnibase_infra/services/session/config_store.py +139 -0
- omnibase_infra/services/session/consumer.py +1007 -0
- omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
- omnibase_infra/services/session/store.py +997 -0
- omnibase_infra/utils/__init__.py +19 -0
- omnibase_infra/utils/util_atomic_file.py +261 -0
- omnibase_infra/utils/util_db_transaction.py +239 -0
- omnibase_infra/utils/util_dsn_validation.py +1 -1
- omnibase_infra/utils/util_retry_optimistic.py +281 -0
- omnibase_infra/validation/__init__.py +3 -19
- omnibase_infra/validation/contracts/security.validation.yaml +114 -0
- omnibase_infra/validation/infra_validators.py +35 -24
- omnibase_infra/validation/validation_exemptions.yaml +140 -9
- omnibase_infra/validation/validator_chain_propagation.py +2 -2
- omnibase_infra/validation/validator_runtime_shape.py +1 -1
- omnibase_infra/validation/validator_security.py +473 -370
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/METADATA +3 -3
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/RECORD +161 -98
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/entry_points.txt +0 -0
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -54,6 +54,7 @@ from __future__ import annotations
|
|
|
54
54
|
import logging
|
|
55
55
|
from typing import TYPE_CHECKING
|
|
56
56
|
|
|
57
|
+
from omnibase_core.enums import EnumInjectionScope
|
|
57
58
|
from omnibase_core.models.primitives import ModelSemVer
|
|
58
59
|
from omnibase_infra.enums import EnumInfraTransportType
|
|
59
60
|
from omnibase_infra.errors import (
|
|
@@ -257,7 +258,7 @@ async def wire_infrastructure_services(
|
|
|
257
258
|
await container.service_registry.register_instance(
|
|
258
259
|
interface=RegistryPolicy,
|
|
259
260
|
instance=policy_registry,
|
|
260
|
-
scope=
|
|
261
|
+
scope=EnumInjectionScope.GLOBAL,
|
|
261
262
|
metadata={
|
|
262
263
|
"description": "ONEX policy plugin registry",
|
|
263
264
|
"version": str(SEMVER_DEFAULT),
|
|
@@ -274,7 +275,7 @@ async def wire_infrastructure_services(
|
|
|
274
275
|
await container.service_registry.register_instance(
|
|
275
276
|
interface=RegistryProtocolBinding,
|
|
276
277
|
instance=handler_registry,
|
|
277
|
-
scope=
|
|
278
|
+
scope=EnumInjectionScope.GLOBAL,
|
|
278
279
|
metadata={
|
|
279
280
|
"description": "ONEX protocol handler binding registry",
|
|
280
281
|
"version": str(SEMVER_DEFAULT),
|
|
@@ -291,7 +292,7 @@ async def wire_infrastructure_services(
|
|
|
291
292
|
await container.service_registry.register_instance(
|
|
292
293
|
interface=RegistryCompute,
|
|
293
294
|
instance=compute_registry,
|
|
294
|
-
scope=
|
|
295
|
+
scope=EnumInjectionScope.GLOBAL,
|
|
295
296
|
metadata={
|
|
296
297
|
"description": "ONEX compute plugin registry",
|
|
297
298
|
"version": str(SEMVER_DEFAULT),
|
|
@@ -530,7 +531,7 @@ async def get_or_create_policy_registry(
|
|
|
530
531
|
await container.service_registry.register_instance(
|
|
531
532
|
interface=RegistryPolicy,
|
|
532
533
|
instance=policy_registry,
|
|
533
|
-
scope=
|
|
534
|
+
scope=EnumInjectionScope.GLOBAL,
|
|
534
535
|
metadata={
|
|
535
536
|
"description": "ONEX policy plugin registry (auto-registered)",
|
|
536
537
|
"version": str(SEMVER_DEFAULT),
|
|
@@ -804,7 +805,7 @@ async def get_or_create_compute_registry(
|
|
|
804
805
|
await container.service_registry.register_instance(
|
|
805
806
|
interface=RegistryCompute,
|
|
806
807
|
instance=compute_registry,
|
|
807
|
-
scope=
|
|
808
|
+
scope=EnumInjectionScope.GLOBAL,
|
|
808
809
|
metadata={
|
|
809
810
|
"description": "ONEX compute plugin registry (auto-registered)",
|
|
810
811
|
"version": str(SEMVER_DEFAULT),
|
|
@@ -121,13 +121,19 @@ from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationE
|
|
|
121
121
|
from omnibase_infra.event_bus.event_bus_inmemory import EventBusInmemory
|
|
122
122
|
from omnibase_infra.handlers.handler_consul import HandlerConsul
|
|
123
123
|
from omnibase_infra.handlers.handler_db import HandlerDb
|
|
124
|
+
from omnibase_infra.handlers.handler_graph import HandlerGraph
|
|
124
125
|
from omnibase_infra.handlers.handler_http import HandlerHttpRest
|
|
126
|
+
from omnibase_infra.handlers.handler_intent import HandlerIntent
|
|
127
|
+
from omnibase_infra.handlers.handler_mcp import HandlerMCP
|
|
125
128
|
from omnibase_infra.handlers.handler_vault import HandlerVault
|
|
126
129
|
from omnibase_infra.runtime.handler_registry import (
|
|
127
130
|
EVENT_BUS_INMEMORY,
|
|
128
131
|
HANDLER_TYPE_CONSUL,
|
|
129
132
|
HANDLER_TYPE_DATABASE,
|
|
133
|
+
HANDLER_TYPE_GRAPH,
|
|
130
134
|
HANDLER_TYPE_HTTP,
|
|
135
|
+
HANDLER_TYPE_INTENT,
|
|
136
|
+
HANDLER_TYPE_MCP,
|
|
131
137
|
HANDLER_TYPE_VAULT,
|
|
132
138
|
RegistryEventBusBinding,
|
|
133
139
|
RegistryProtocolBinding,
|
|
@@ -137,7 +143,7 @@ from omnibase_infra.runtime.handler_registry import (
|
|
|
137
143
|
|
|
138
144
|
if TYPE_CHECKING:
|
|
139
145
|
from omnibase_core.protocol.protocol_event_bus import ProtocolEventBus
|
|
140
|
-
from
|
|
146
|
+
from omnibase_infra.protocols import ProtocolContainerAware
|
|
141
147
|
|
|
142
148
|
logger = logging.getLogger(__name__)
|
|
143
149
|
|
|
@@ -164,11 +170,15 @@ logger = logging.getLogger(__name__)
|
|
|
164
170
|
# NOTE: HandlerHttpRest and HandlerDb use legacy execute(envelope: dict) signature.
|
|
165
171
|
# They will be migrated to ProtocolHandler.execute(request, operation_config) in future.
|
|
166
172
|
# Type ignore comments suppress MyPy errors during MVP phase.
|
|
167
|
-
_KNOWN_HANDLERS: dict[str, tuple[type[
|
|
173
|
+
_KNOWN_HANDLERS: dict[str, tuple[type[ProtocolContainerAware], str]] = {
|
|
168
174
|
# NOTE: Handlers implement ProtocolHandler structurally but concrete types differ from protocol.
|
|
169
175
|
HANDLER_TYPE_CONSUL: (HandlerConsul, "HashiCorp Consul service discovery handler"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
170
176
|
HANDLER_TYPE_DATABASE: (HandlerDb, "PostgreSQL database handler"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
177
|
+
HANDLER_TYPE_GRAPH: (HandlerGraph, "Graph database (Memgraph/Neo4j) handler"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
171
178
|
HANDLER_TYPE_HTTP: (HandlerHttpRest, "HTTP REST protocol handler"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
179
|
+
# DEMO: Temporary registration - remove when contract-driven (OMN-1515)
|
|
180
|
+
HANDLER_TYPE_INTENT: (HandlerIntent, "Intent storage and query handler for demo"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
181
|
+
HANDLER_TYPE_MCP: (HandlerMCP, "Model Context Protocol handler for AI agents"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
172
182
|
HANDLER_TYPE_VAULT: (HandlerVault, "HashiCorp Vault secret management handler"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
173
183
|
}
|
|
174
184
|
|
|
@@ -194,7 +204,10 @@ def wire_default_handlers() -> dict[str, list[str]]:
|
|
|
194
204
|
Registered Handlers:
|
|
195
205
|
- CONSUL: HandlerConsul for HashiCorp Consul service discovery
|
|
196
206
|
- DB: HandlerDb for PostgreSQL database operations
|
|
207
|
+
- GRAPH: HandlerGraph for graph database (Memgraph/Neo4j) operations
|
|
197
208
|
- HTTP: HandlerHttpRest for HTTP/REST protocol operations
|
|
209
|
+
- INTENT: HandlerIntent for intent storage and query (demo)
|
|
210
|
+
- MCP: HandlerMCP for Model Context Protocol AI agent integration
|
|
198
211
|
- VAULT: HandlerVault for HashiCorp Vault secret management
|
|
199
212
|
|
|
200
213
|
Registered Event Buses:
|
|
@@ -217,7 +230,7 @@ def wire_default_handlers() -> dict[str, list[str]]:
|
|
|
217
230
|
Example:
|
|
218
231
|
>>> summary = wire_default_handlers()
|
|
219
232
|
>>> print(summary)
|
|
220
|
-
{'handlers': ['consul', 'db', 'http', 'vault'], 'event_buses': ['inmemory']}
|
|
233
|
+
{'handlers': ['consul', 'db', 'http', 'mcp', 'vault'], 'event_buses': ['inmemory']}
|
|
221
234
|
|
|
222
235
|
Note:
|
|
223
236
|
This function uses the singleton registries returned by
|
|
@@ -483,7 +496,7 @@ def get_known_event_bus_kinds() -> list[str]:
|
|
|
483
496
|
|
|
484
497
|
def wire_custom_handler(
|
|
485
498
|
handler_type: str,
|
|
486
|
-
handler_cls: type[
|
|
499
|
+
handler_cls: type[ProtocolContainerAware],
|
|
487
500
|
registry: RegistryProtocolBinding | None = None,
|
|
488
501
|
) -> None:
|
|
489
502
|
"""Register a custom handler class with the registry.
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
-- SPDX-License-Identifier: MIT
|
|
2
|
+
-- Copyright (c) 2025 OmniNode Team
|
|
3
|
+
--
|
|
4
|
+
-- Transition Notification Outbox Schema
|
|
5
|
+
-- Ticket: OMN-1139 (TransitionNotificationOutbox implementation)
|
|
6
|
+
-- Version: 1.0.0
|
|
7
|
+
--
|
|
8
|
+
-- Design Notes:
|
|
9
|
+
-- - Implements the outbox pattern for guaranteed notification delivery
|
|
10
|
+
-- - Stores notifications in same transaction as projections for atomicity
|
|
11
|
+
-- - Supports concurrent processing via SELECT FOR UPDATE SKIP LOCKED
|
|
12
|
+
-- - Includes retry tracking with error recording
|
|
13
|
+
-- - Indexes optimized for processor query patterns:
|
|
14
|
+
-- 1. Pending notification queries (processed_at IS NULL)
|
|
15
|
+
-- 2. Aggregate-specific queries (aggregate_type, aggregate_id)
|
|
16
|
+
-- 3. Retry processing (high retry counts for monitoring)
|
|
17
|
+
-- 4. Cleanup of old processed records
|
|
18
|
+
-- - All timestamps are timezone-aware (TIMESTAMPTZ)
|
|
19
|
+
-- - This schema is idempotent (IF NOT EXISTS used throughout)
|
|
20
|
+
--
|
|
21
|
+
-- Usage:
|
|
22
|
+
-- Execute this SQL file to create or update the outbox schema.
|
|
23
|
+
-- The schema is designed to be re-run safely (idempotent).
|
|
24
|
+
--
|
|
25
|
+
-- Related:
|
|
26
|
+
-- - TransitionNotificationOutbox: src/omnibase_infra/runtime/transition_notification_outbox.py
|
|
27
|
+
-- - ModelStateTransitionNotification: omnibase_core.models.notifications
|
|
28
|
+
-- - ProtocolTransitionNotificationPublisher: omnibase_core.protocols.notifications
|
|
29
|
+
|
|
30
|
+
-- =============================================================================
|
|
31
|
+
-- MAIN TABLE
|
|
32
|
+
-- =============================================================================
|
|
33
|
+
|
|
34
|
+
CREATE TABLE IF NOT EXISTS transition_notification_outbox (
|
|
35
|
+
-- Identity
|
|
36
|
+
id BIGSERIAL PRIMARY KEY,
|
|
37
|
+
|
|
38
|
+
-- Notification Payload (serialized ModelStateTransitionNotification)
|
|
39
|
+
notification_data JSONB NOT NULL,
|
|
40
|
+
|
|
41
|
+
-- Timestamps
|
|
42
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
43
|
+
-- processed_at encodes both "pending" and "processed" states in a single column:
|
|
44
|
+
-- NULL = pending (not yet processed, eligible for processing)
|
|
45
|
+
-- non-NULL = processed (successfully published, timestamp of completion)
|
|
46
|
+
-- Note: No separate "processed_implies_no_pending" constraint is needed because
|
|
47
|
+
-- the single-column semantic makes the states mutually exclusive by design -
|
|
48
|
+
-- a row cannot be both pending (NULL) and processed (non-NULL) simultaneously.
|
|
49
|
+
processed_at TIMESTAMPTZ,
|
|
50
|
+
|
|
51
|
+
-- Retry Tracking
|
|
52
|
+
retry_count INT NOT NULL DEFAULT 0,
|
|
53
|
+
last_error TEXT, -- Most recent error message (sanitized, max 1000 chars)
|
|
54
|
+
|
|
55
|
+
-- Aggregate Information (for queries and debugging)
|
|
56
|
+
aggregate_type TEXT NOT NULL,
|
|
57
|
+
aggregate_id UUID NOT NULL,
|
|
58
|
+
|
|
59
|
+
-- Constraints
|
|
60
|
+
CONSTRAINT valid_retry_count CHECK (retry_count >= 0)
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
-- =============================================================================
|
|
64
|
+
-- INDEXES FOR OUTBOX PROCESSING
|
|
65
|
+
-- =============================================================================
|
|
66
|
+
|
|
67
|
+
-- Index for efficient pending notification queries (PRIMARY query pattern)
|
|
68
|
+
-- The processor uses: SELECT ... WHERE processed_at IS NULL ORDER BY created_at
|
|
69
|
+
-- Partial index: only index rows with NULL processed_at (pending notifications)
|
|
70
|
+
-- Query pattern: SELECT id, notification_data FROM transition_notification_outbox
|
|
71
|
+
-- WHERE processed_at IS NULL ORDER BY created_at LIMIT :batch_size
|
|
72
|
+
-- FOR UPDATE SKIP LOCKED
|
|
73
|
+
CREATE INDEX IF NOT EXISTS idx_outbox_pending
|
|
74
|
+
ON transition_notification_outbox (created_at)
|
|
75
|
+
WHERE processed_at IS NULL;
|
|
76
|
+
|
|
77
|
+
-- Index for aggregate-specific queries (debugging and monitoring)
|
|
78
|
+
-- Enables queries like "show all notifications for entity X"
|
|
79
|
+
-- Query pattern: SELECT * FROM transition_notification_outbox
|
|
80
|
+
-- WHERE aggregate_type = :type AND aggregate_id = :id
|
|
81
|
+
CREATE INDEX IF NOT EXISTS idx_outbox_aggregate
|
|
82
|
+
ON transition_notification_outbox (aggregate_type, aggregate_id);
|
|
83
|
+
|
|
84
|
+
-- Index for retry monitoring (identify stuck/failing notifications)
|
|
85
|
+
-- Partial index: only pending notifications with retries
|
|
86
|
+
-- Query pattern: SELECT * FROM transition_notification_outbox
|
|
87
|
+
-- WHERE processed_at IS NULL AND retry_count > :threshold
|
|
88
|
+
CREATE INDEX IF NOT EXISTS idx_outbox_retry_pending
|
|
89
|
+
ON transition_notification_outbox (retry_count, created_at)
|
|
90
|
+
WHERE processed_at IS NULL AND retry_count > 0;
|
|
91
|
+
|
|
92
|
+
-- Index for cleanup queries (delete old processed records)
|
|
93
|
+
-- Enables efficient deletion of processed records older than a threshold
|
|
94
|
+
-- Query pattern: DELETE FROM transition_notification_outbox
|
|
95
|
+
-- WHERE processed_at IS NOT NULL AND processed_at < :cutoff_time
|
|
96
|
+
CREATE INDEX IF NOT EXISTS idx_outbox_cleanup
|
|
97
|
+
ON transition_notification_outbox (processed_at)
|
|
98
|
+
WHERE processed_at IS NOT NULL;
|
|
99
|
+
|
|
100
|
+
-- Index for aggregate type filtering with pending status
|
|
101
|
+
-- Useful for monitoring specific aggregate types
|
|
102
|
+
-- Query pattern: SELECT COUNT(*) FROM transition_notification_outbox
|
|
103
|
+
-- WHERE aggregate_type = :type AND processed_at IS NULL
|
|
104
|
+
CREATE INDEX IF NOT EXISTS idx_outbox_aggregate_type_pending
|
|
105
|
+
ON transition_notification_outbox (aggregate_type)
|
|
106
|
+
WHERE processed_at IS NULL;
|
|
107
|
+
|
|
108
|
+
-- =============================================================================
|
|
109
|
+
-- TABLE AND COLUMN COMMENTS
|
|
110
|
+
-- =============================================================================
|
|
111
|
+
|
|
112
|
+
COMMENT ON TABLE transition_notification_outbox IS
|
|
113
|
+
'Outbox pattern table for guaranteed state transition notification delivery (OMN-1139). '
|
|
114
|
+
'Stores notifications in same transaction as projections for atomicity. '
|
|
115
|
+
'Background processor publishes pending notifications asynchronously.';
|
|
116
|
+
|
|
117
|
+
COMMENT ON COLUMN transition_notification_outbox.id IS
|
|
118
|
+
'Auto-incrementing primary key. Used for efficient row locking with FOR UPDATE SKIP LOCKED.';
|
|
119
|
+
|
|
120
|
+
COMMENT ON COLUMN transition_notification_outbox.notification_data IS
|
|
121
|
+
'Serialized ModelStateTransitionNotification as JSONB. Contains all notification fields: '
|
|
122
|
+
'aggregate_type, aggregate_id, transition (from_state, to_state, event_type), '
|
|
123
|
+
'correlation_id, timestamp, and optional metadata.';
|
|
124
|
+
|
|
125
|
+
COMMENT ON COLUMN transition_notification_outbox.created_at IS
|
|
126
|
+
'Timestamp when notification was stored in outbox. Used for FIFO ordering of pending notifications.';
|
|
127
|
+
|
|
128
|
+
COMMENT ON COLUMN transition_notification_outbox.processed_at IS
|
|
129
|
+
'Timestamp when notification was successfully published. NULL indicates pending status.';
|
|
130
|
+
|
|
131
|
+
COMMENT ON COLUMN transition_notification_outbox.retry_count IS
|
|
132
|
+
'Number of failed publish attempts. Incremented on each failure, not reset on success. '
|
|
133
|
+
'Use for monitoring and alerting on stuck notifications.';
|
|
134
|
+
|
|
135
|
+
COMMENT ON COLUMN transition_notification_outbox.last_error IS
|
|
136
|
+
'Most recent error message from failed publish attempt (sanitized, max 1000 chars). '
|
|
137
|
+
'Updated on each failure for debugging. May contain value even when processed_at is set '
|
|
138
|
+
'if earlier retries failed before final success.';
|
|
139
|
+
|
|
140
|
+
COMMENT ON COLUMN transition_notification_outbox.aggregate_type IS
|
|
141
|
+
'Type of aggregate this notification is for (e.g., "registration", "workflow"). '
|
|
142
|
+
'Denormalized from notification_data for efficient indexing and filtering.';
|
|
143
|
+
|
|
144
|
+
COMMENT ON COLUMN transition_notification_outbox.aggregate_id IS
|
|
145
|
+
'UUID of the aggregate entity this notification is for. '
|
|
146
|
+
'Denormalized from notification_data for efficient indexing and aggregate-specific queries.';
|
|
147
|
+
|
|
148
|
+
-- =============================================================================
|
|
149
|
+
-- INDEX STRATEGY DOCUMENTATION
|
|
150
|
+
-- =============================================================================
|
|
151
|
+
--
|
|
152
|
+
-- This schema defines multiple indexes optimized for different query patterns
|
|
153
|
+
-- used by the TransitionNotificationOutbox processor and operators.
|
|
154
|
+
--
|
|
155
|
+
-- PENDING NOTIFICATION PROCESSING:
|
|
156
|
+
-- --------------------------------
|
|
157
|
+
-- 1. idx_outbox_pending:
|
|
158
|
+
-- - Single-column index on created_at
|
|
159
|
+
-- - WHERE: processed_at IS NULL
|
|
160
|
+
-- - Use case: Main processor query - fetch pending notifications in FIFO order
|
|
161
|
+
-- - Critical for: process_pending() batch retrieval
|
|
162
|
+
--
|
|
163
|
+
-- The partial WHERE clause keeps this index small and fast by excluding
|
|
164
|
+
-- all processed records.
|
|
165
|
+
--
|
|
166
|
+
-- AGGREGATE QUERIES (Debugging):
|
|
167
|
+
-- ------------------------------
|
|
168
|
+
-- 2. idx_outbox_aggregate:
|
|
169
|
+
-- - Composite index on (aggregate_type, aggregate_id)
|
|
170
|
+
-- - Use case: Query notifications for a specific entity
|
|
171
|
+
-- - Example: "Show all notifications for registration entity X"
|
|
172
|
+
--
|
|
173
|
+
-- RETRY MONITORING:
|
|
174
|
+
-- -----------------
|
|
175
|
+
-- 3. idx_outbox_retry_pending:
|
|
176
|
+
-- - Composite index on (retry_count, created_at)
|
|
177
|
+
-- - WHERE: processed_at IS NULL AND retry_count > 0
|
|
178
|
+
-- - Use case: Identify stuck or failing notifications
|
|
179
|
+
-- - Example: Alert when retry_count > 5 for pending notifications
|
|
180
|
+
--
|
|
181
|
+
-- CLEANUP OPERATIONS:
|
|
182
|
+
-- -------------------
|
|
183
|
+
-- 4. idx_outbox_cleanup:
|
|
184
|
+
-- - Single-column index on processed_at
|
|
185
|
+
-- - WHERE: processed_at IS NOT NULL
|
|
186
|
+
-- - Use case: Efficient deletion of old processed records
|
|
187
|
+
-- - Example: DELETE ... WHERE processed_at < NOW() - INTERVAL '7 days'
|
|
188
|
+
--
|
|
189
|
+
-- TYPE-SPECIFIC MONITORING:
|
|
190
|
+
-- -------------------------
|
|
191
|
+
-- 5. idx_outbox_aggregate_type_pending:
|
|
192
|
+
-- - Single-column index on aggregate_type
|
|
193
|
+
-- - WHERE: processed_at IS NULL
|
|
194
|
+
-- - Use case: Count pending notifications by type for monitoring dashboards
|
|
195
|
+
--
|
|
196
|
+
-- =============================================================================
|
|
197
|
+
-- CONCURRENT PROCESSING PATTERN
|
|
198
|
+
-- =============================================================================
|
|
199
|
+
--
|
|
200
|
+
-- The outbox processor uses SELECT FOR UPDATE SKIP LOCKED to enable safe
|
|
201
|
+
-- concurrent processing by multiple instances:
|
|
202
|
+
--
|
|
203
|
+
-- SELECT id, notification_data
|
|
204
|
+
-- FROM transition_notification_outbox
|
|
205
|
+
-- WHERE processed_at IS NULL
|
|
206
|
+
-- ORDER BY created_at
|
|
207
|
+
-- LIMIT :batch_size
|
|
208
|
+
-- FOR UPDATE SKIP LOCKED;
|
|
209
|
+
--
|
|
210
|
+
-- SKIP LOCKED ensures that if another processor has locked a row, this query
|
|
211
|
+
-- skips it rather than blocking. This prevents:
|
|
212
|
+
-- - Duplicate processing
|
|
213
|
+
-- - Deadlocks between processors
|
|
214
|
+
-- - Head-of-line blocking
|
|
215
|
+
--
|
|
216
|
+
-- The id column (BIGSERIAL PRIMARY KEY) provides an efficient row lock target.
|
|
217
|
+
--
|
|
218
|
+
-- =============================================================================
|
|
219
|
+
-- CLEANUP RECOMMENDATIONS
|
|
220
|
+
-- =============================================================================
|
|
221
|
+
--
|
|
222
|
+
-- Processed records should be periodically deleted to prevent table bloat.
|
|
223
|
+
-- Recommended cleanup query (run periodically via cron or pg_cron):
|
|
224
|
+
--
|
|
225
|
+
-- DELETE FROM transition_notification_outbox
|
|
226
|
+
-- WHERE processed_at IS NOT NULL
|
|
227
|
+
-- AND processed_at < NOW() - INTERVAL '7 days';
|
|
228
|
+
--
|
|
229
|
+
-- Alternatively, implement as a PostgreSQL function:
|
|
230
|
+
--
|
|
231
|
+
-- CREATE OR REPLACE FUNCTION cleanup_outbox(retention_interval INTERVAL)
|
|
232
|
+
-- RETURNS BIGINT AS $$
|
|
233
|
+
-- DECLARE
|
|
234
|
+
-- deleted_count BIGINT;
|
|
235
|
+
-- BEGIN
|
|
236
|
+
-- DELETE FROM transition_notification_outbox
|
|
237
|
+
-- WHERE processed_at IS NOT NULL
|
|
238
|
+
-- AND processed_at < NOW() - retention_interval;
|
|
239
|
+
-- GET DIAGNOSTICS deleted_count = ROW_COUNT;
|
|
240
|
+
-- RETURN deleted_count;
|
|
241
|
+
-- END;
|
|
242
|
+
-- $$ LANGUAGE plpgsql;
|
|
243
|
+
--
|
|
244
|
+
-- Usage: SELECT cleanup_outbox(INTERVAL '7 days');
|
|
245
|
+
--
|
|
@@ -39,6 +39,18 @@ from omnibase_infra.services.service_timeout_scanner import (
|
|
|
39
39
|
ModelTimeoutQueryResult,
|
|
40
40
|
ServiceTimeoutScanner,
|
|
41
41
|
)
|
|
42
|
+
|
|
43
|
+
# Session services (moved from omniclaude in OMN-1526)
|
|
44
|
+
from omnibase_infra.services.session import (
|
|
45
|
+
ConfigSessionConsumer,
|
|
46
|
+
ConfigSessionStorage,
|
|
47
|
+
ConsumerMetrics,
|
|
48
|
+
EnumCircuitState,
|
|
49
|
+
ProtocolSessionAggregator,
|
|
50
|
+
SessionEventConsumer,
|
|
51
|
+
SessionSnapshotStore,
|
|
52
|
+
SessionStoreNotInitializedError,
|
|
53
|
+
)
|
|
42
54
|
from omnibase_infra.services.snapshot import (
|
|
43
55
|
ServiceSnapshot,
|
|
44
56
|
StoreSnapshotInMemory,
|
|
@@ -65,4 +77,13 @@ __all__ = [
|
|
|
65
77
|
"StoreSnapshotPostgres",
|
|
66
78
|
"TimeoutEmitter",
|
|
67
79
|
"TimeoutScanner",
|
|
80
|
+
# Session services (OMN-1526)
|
|
81
|
+
"ConfigSessionConsumer",
|
|
82
|
+
"ConfigSessionStorage",
|
|
83
|
+
"ConsumerMetrics",
|
|
84
|
+
"EnumCircuitState",
|
|
85
|
+
"ProtocolSessionAggregator",
|
|
86
|
+
"SessionEventConsumer",
|
|
87
|
+
"SessionSnapshotStore",
|
|
88
|
+
"SessionStoreNotInitializedError",
|
|
68
89
|
]
|
|
@@ -22,6 +22,7 @@ from datetime import UTC, datetime
|
|
|
22
22
|
from typing import Protocol
|
|
23
23
|
from uuid import UUID
|
|
24
24
|
|
|
25
|
+
from omnibase_core.container import ModelONEXContainer
|
|
25
26
|
from omnibase_core.enums import EnumCoreErrorCode
|
|
26
27
|
from omnibase_core.errors import OnexError
|
|
27
28
|
from omnibase_core.models.manifest.model_execution_manifest import (
|
|
@@ -226,13 +227,15 @@ class CorpusCapture:
|
|
|
226
227
|
- Max executions enforcement with automatic state transitions
|
|
227
228
|
|
|
228
229
|
Example:
|
|
230
|
+
>>> from omnibase_core.container import ModelONEXContainer
|
|
231
|
+
>>> container = ModelONEXContainer(...) # Configure as needed
|
|
229
232
|
>>> config = ModelCaptureConfig(
|
|
230
233
|
... corpus_display_name="regression-suite-v1",
|
|
231
234
|
... max_executions=50,
|
|
232
235
|
... sample_rate=0.5,
|
|
233
236
|
... handler_filter=("compute-handler",),
|
|
234
237
|
... )
|
|
235
|
-
>>> service = CorpusCapture()
|
|
238
|
+
>>> service = CorpusCapture(container)
|
|
236
239
|
>>> service.create_corpus(config)
|
|
237
240
|
>>> service.start_capture()
|
|
238
241
|
>>>
|
|
@@ -249,17 +252,20 @@ class CorpusCapture:
|
|
|
249
252
|
|
|
250
253
|
def __init__(
|
|
251
254
|
self,
|
|
255
|
+
container: ModelONEXContainer,
|
|
252
256
|
persistence: ProtocolManifestPersistence | None = None,
|
|
253
257
|
) -> None:
|
|
254
258
|
"""
|
|
255
259
|
Initialize the corpus capture service.
|
|
256
260
|
|
|
257
261
|
Args:
|
|
262
|
+
container: ONEX container for dependency injection.
|
|
258
263
|
persistence: Optional persistence handler for flushing manifests.
|
|
259
264
|
If provided, manifests can be persisted via flush_to_persistence()
|
|
260
265
|
or by calling close_corpus_async(flush=True). The synchronous
|
|
261
266
|
close_corpus() does NOT automatically flush.
|
|
262
267
|
"""
|
|
268
|
+
self._container = container
|
|
263
269
|
self._state_machine = CaptureLifecycleFSM()
|
|
264
270
|
self._config: ModelCaptureConfig | None = None
|
|
265
271
|
self._corpus: ModelExecutionCorpus | None = None
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""MCP services for Model Context Protocol integration.
|
|
4
|
+
|
|
5
|
+
This package provides services for exposing ONEX orchestrator nodes
|
|
6
|
+
as MCP tools for AI agent integration.
|
|
7
|
+
|
|
8
|
+
Services:
|
|
9
|
+
ServiceMCPToolRegistry: Event-loop safe in-memory cache of tool definitions
|
|
10
|
+
ServiceMCPToolDiscovery: Consul scanner for MCP-enabled orchestrators
|
|
11
|
+
ServiceMCPToolSync: Kafka listener for hot reload with idempotency
|
|
12
|
+
MCPServerLifecycle: Server lifecycle management
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from omnibase_infra.services.mcp.mcp_server_lifecycle import (
|
|
16
|
+
MCPServerLifecycle,
|
|
17
|
+
ModelMCPServerConfig,
|
|
18
|
+
)
|
|
19
|
+
from omnibase_infra.services.mcp.service_mcp_tool_discovery import (
|
|
20
|
+
ServiceMCPToolDiscovery,
|
|
21
|
+
)
|
|
22
|
+
from omnibase_infra.services.mcp.service_mcp_tool_registry import ServiceMCPToolRegistry
|
|
23
|
+
from omnibase_infra.services.mcp.service_mcp_tool_sync import ServiceMCPToolSync
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"MCPServerLifecycle",
|
|
27
|
+
"ModelMCPServerConfig",
|
|
28
|
+
"ServiceMCPToolDiscovery",
|
|
29
|
+
"ServiceMCPToolRegistry",
|
|
30
|
+
"ServiceMCPToolSync",
|
|
31
|
+
]
|