omnibase_infra 0.2.1__py3-none-any.whl → 0.2.2__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 +446 -0
- omnibase_infra/cli/commands.py +1 -1
- omnibase_infra/configs/widget_mapping.yaml +176 -0
- omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +4 -1
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +4 -1
- 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/handlers/handler_db.py +2 -1
- omnibase_infra/handlers/handler_graph.py +10 -5
- omnibase_infra/handlers/handler_mcp.py +736 -63
- omnibase_infra/handlers/mixins/mixin_consul_kv.py +4 -3
- omnibase_infra/handlers/mixins/mixin_consul_service.py +2 -1
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +301 -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 +24 -7
- omnibase_infra/mixins/mixin_retry_execution.py +1 -1
- omnibase_infra/models/handlers/__init__.py +10 -0
- omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
- omnibase_infra/models/handlers/model_handler_descriptor.py +15 -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/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/registry/registry_infra_node_registration_orchestrator.py +9 -8
- 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/registry/registry_infra_registration_storage.py +46 -25
- 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 +24 -19
- 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/protocol_event_projector.py +1 -1
- omnibase_infra/runtime/__init__.py +51 -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 +514 -0
- omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
- omnibase_infra/runtime/handler_contract_source.py +289 -167
- omnibase_infra/runtime/handler_plugin_loader.py +4 -2
- 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/protocols/__init__.py +10 -0
- omnibase_infra/runtime/registry/registry_protocol_binding.py +3 -2
- omnibase_infra/runtime/registry_policy.py +9 -326
- omnibase_infra/runtime/secret_resolver.py +4 -2
- omnibase_infra/runtime/service_kernel.py +10 -2
- omnibase_infra/runtime/service_message_dispatch_engine.py +4 -2
- omnibase_infra/runtime/service_runtime_host_process.py +225 -15
- 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 +5 -1
- omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
- omnibase_infra/services/mcp/__init__.py +31 -0
- omnibase_infra/services/mcp/mcp_server_lifecycle.py +443 -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 +243 -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 +846 -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 +13 -2
- omnibase_infra/utils/util_dsn_validation.py +1 -1
- 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 +113 -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.2.dist-info}/METADATA +2 -2
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/RECORD +116 -74
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/entry_points.txt +0 -0
- {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -53,6 +53,7 @@ Related Tickets:
|
|
|
53
53
|
from __future__ import annotations
|
|
54
54
|
|
|
55
55
|
import logging
|
|
56
|
+
import re
|
|
56
57
|
import time
|
|
57
58
|
from datetime import datetime, timedelta
|
|
58
59
|
from typing import TYPE_CHECKING
|
|
@@ -237,6 +238,39 @@ class HandlerNodeIntrospected:
|
|
|
237
238
|
"""Check if HandlerConsul is configured for Consul registration."""
|
|
238
239
|
return self._consul_handler is not None
|
|
239
240
|
|
|
241
|
+
def _sanitize_tool_name(self, name: str) -> str:
|
|
242
|
+
"""Sanitize tool name for use in Consul tags.
|
|
243
|
+
|
|
244
|
+
Converts free-form text (like descriptions) into stable, Consul-safe
|
|
245
|
+
identifiers. This ensures consistent service discovery matching.
|
|
246
|
+
|
|
247
|
+
Transformation rules:
|
|
248
|
+
1. Convert to lowercase
|
|
249
|
+
2. Replace non-alphanumeric characters with dashes
|
|
250
|
+
3. Collapse multiple consecutive dashes into one
|
|
251
|
+
4. Remove leading/trailing dashes
|
|
252
|
+
5. Truncate to 63 characters (Consul tag limit)
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
name: Raw tool name or description text.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Sanitized string suitable for Consul tags (lowercase, alphanumeric
|
|
259
|
+
with dashes, max 63 chars).
|
|
260
|
+
|
|
261
|
+
Example:
|
|
262
|
+
>>> handler._sanitize_tool_name("My Cool Tool (v2.0)")
|
|
263
|
+
'my-cool-tool-v2-0'
|
|
264
|
+
>>> handler._sanitize_tool_name(" Spaces & Special!Chars ")
|
|
265
|
+
'spaces-special-chars'
|
|
266
|
+
"""
|
|
267
|
+
# Replace non-alphanumeric with dash, lowercase
|
|
268
|
+
sanitized = re.sub(r"[^a-zA-Z0-9]+", "-", name.lower())
|
|
269
|
+
# Remove leading/trailing dashes
|
|
270
|
+
sanitized = sanitized.strip("-")
|
|
271
|
+
# Truncate to Consul tag limit (63 chars is common limit for DNS labels)
|
|
272
|
+
return sanitized[:63]
|
|
273
|
+
|
|
240
274
|
async def handle(
|
|
241
275
|
self,
|
|
242
276
|
envelope: ModelEventEnvelope[ModelNodeIntrospectionEvent],
|
|
@@ -422,11 +456,19 @@ class HandlerNodeIntrospected:
|
|
|
422
456
|
|
|
423
457
|
# Register with Consul for service discovery (dual registration)
|
|
424
458
|
# This happens AFTER PostgreSQL persistence (source of truth)
|
|
459
|
+
# Pass MCP config from capabilities (if present) for MCP tag generation
|
|
460
|
+
mcp_config = (
|
|
461
|
+
event.declared_capabilities.mcp
|
|
462
|
+
if event.declared_capabilities is not None
|
|
463
|
+
else None
|
|
464
|
+
)
|
|
425
465
|
await self._register_with_consul(
|
|
426
466
|
node_id=node_id,
|
|
427
467
|
node_type=event.node_type.value,
|
|
428
468
|
endpoints=event.endpoints,
|
|
429
469
|
correlation_id=correlation_id,
|
|
470
|
+
mcp_config=mcp_config,
|
|
471
|
+
node_name=event.metadata.description if event.metadata else None,
|
|
430
472
|
)
|
|
431
473
|
|
|
432
474
|
logger.info(
|
|
@@ -458,6 +500,8 @@ class HandlerNodeIntrospected:
|
|
|
458
500
|
node_type: str,
|
|
459
501
|
endpoints: dict[str, str] | None,
|
|
460
502
|
correlation_id: UUID,
|
|
503
|
+
mcp_config: object | None = None,
|
|
504
|
+
node_name: str | None = None,
|
|
461
505
|
) -> None:
|
|
462
506
|
"""Register node with Consul for service discovery.
|
|
463
507
|
|
|
@@ -465,8 +509,16 @@ class HandlerNodeIntrospected:
|
|
|
465
509
|
- service_name: `onex-{node_type}` (ONEX convention for service discovery)
|
|
466
510
|
- service_id: `onex-{node_type}-{node_id}` (unique identifier)
|
|
467
511
|
- tags: [`onex`, `node-type:{node_type}`]
|
|
512
|
+
- MCP tags (orchestrators only): [`mcp-enabled`, `mcp-tool:{tool_name}`]
|
|
468
513
|
- address/port: Extracted from endpoints if available
|
|
469
514
|
|
|
515
|
+
MCP Tags:
|
|
516
|
+
MCP tags are added ONLY when:
|
|
517
|
+
1. node_type is "orchestrator"
|
|
518
|
+
2. mcp_config is provided with expose=True
|
|
519
|
+
|
|
520
|
+
This ensures only orchestrators can be exposed as MCP tools.
|
|
521
|
+
|
|
470
522
|
This method is idempotent - re-registering the same service_id updates it.
|
|
471
523
|
Errors are logged but not propagated (PostgreSQL is source of truth).
|
|
472
524
|
|
|
@@ -475,6 +527,8 @@ class HandlerNodeIntrospected:
|
|
|
475
527
|
node_type: ONEX node type (effect, compute, reducer, orchestrator).
|
|
476
528
|
endpoints: Optional dict of endpoint URLs from introspection event.
|
|
477
529
|
correlation_id: Correlation ID for tracing.
|
|
530
|
+
mcp_config: Optional MCP configuration from capabilities.
|
|
531
|
+
node_name: Optional node name for MCP tool naming.
|
|
478
532
|
"""
|
|
479
533
|
if self._consul_handler is None:
|
|
480
534
|
logger.debug(
|
|
@@ -554,11 +608,42 @@ class HandlerNodeIntrospected:
|
|
|
554
608
|
},
|
|
555
609
|
)
|
|
556
610
|
|
|
611
|
+
# Build base tags
|
|
612
|
+
tags: list[str] = ["onex", f"node-type:{node_type}"]
|
|
613
|
+
|
|
614
|
+
# Add MCP tags for orchestrators with MCP config enabled
|
|
615
|
+
# MCP tags are ONLY added when:
|
|
616
|
+
# 1. node_type is "orchestrator" (enforces orchestrator-only rule)
|
|
617
|
+
# 2. mcp_config exists with expose=True
|
|
618
|
+
if node_type == "orchestrator" and mcp_config is not None:
|
|
619
|
+
# Check if mcp_config has expose attribute and it's True
|
|
620
|
+
mcp_expose = getattr(mcp_config, "expose", False)
|
|
621
|
+
if mcp_expose:
|
|
622
|
+
# Get tool name from mcp_config or fall back to node_name
|
|
623
|
+
mcp_tool_name_raw = getattr(mcp_config, "tool_name", None)
|
|
624
|
+
if not mcp_tool_name_raw:
|
|
625
|
+
# Fall back to node_name (description), then service_name
|
|
626
|
+
mcp_tool_name_raw = node_name or service_name
|
|
627
|
+
# Sanitize tool name for Consul tag safety
|
|
628
|
+
# node_name comes from metadata.description which can be free-form text
|
|
629
|
+
mcp_tool_name = self._sanitize_tool_name(mcp_tool_name_raw)
|
|
630
|
+
tags.extend(["mcp-enabled", f"mcp-tool:{mcp_tool_name}"])
|
|
631
|
+
|
|
632
|
+
logger.info(
|
|
633
|
+
"Adding MCP tags to Consul registration",
|
|
634
|
+
extra={
|
|
635
|
+
"node_id": str(node_id),
|
|
636
|
+
"tool_name": mcp_tool_name,
|
|
637
|
+
"tool_name_raw": mcp_tool_name_raw,
|
|
638
|
+
"correlation_id": str(correlation_id),
|
|
639
|
+
},
|
|
640
|
+
)
|
|
641
|
+
|
|
557
642
|
# Build Consul registration payload
|
|
558
643
|
consul_payload: dict[str, object] = {
|
|
559
644
|
"name": service_name,
|
|
560
645
|
"service_id": service_id,
|
|
561
|
-
"tags":
|
|
646
|
+
"tags": tags,
|
|
562
647
|
}
|
|
563
648
|
if address:
|
|
564
649
|
consul_payload["address"] = address
|
|
@@ -392,7 +392,7 @@ class IntrospectionEventRouter:
|
|
|
392
392
|
correlation_id=raw_envelope.correlation_id or callback_correlation_id,
|
|
393
393
|
source_tool=raw_envelope.source_tool,
|
|
394
394
|
target_tool=raw_envelope.target_tool,
|
|
395
|
-
metadata=_normalize_metadata(raw_envelope.metadata),
|
|
395
|
+
metadata=_normalize_metadata(raw_envelope.metadata), # type: ignore[arg-type]
|
|
396
396
|
priority=raw_envelope.priority,
|
|
397
397
|
timeout_seconds=raw_envelope.timeout_seconds,
|
|
398
398
|
trace_id=raw_envelope.trace_id,
|
|
@@ -419,7 +419,7 @@ class IntrospectionEventRouter:
|
|
|
419
419
|
},
|
|
420
420
|
)
|
|
421
421
|
dispatcher_start_time = time.time()
|
|
422
|
-
result = await self._dispatcher.handle(event_envelope)
|
|
422
|
+
result = await self._dispatcher.handle(event_envelope) # type: ignore[arg-type]
|
|
423
423
|
dispatcher_duration = time.time() - dispatcher_start_time
|
|
424
424
|
|
|
425
425
|
if result.is_successful():
|
|
@@ -441,7 +441,7 @@ class IntrospectionEventRouter:
|
|
|
441
441
|
if result.output_events:
|
|
442
442
|
for output_event in result.output_events:
|
|
443
443
|
# Wrap output event in envelope
|
|
444
|
-
output_envelope = ModelEventEnvelope(
|
|
444
|
+
output_envelope = ModelEventEnvelope( # type: ignore[var-annotated]
|
|
445
445
|
payload=output_event,
|
|
446
446
|
correlation_id=event_envelope.correlation_id,
|
|
447
447
|
envelope_timestamp=datetime.now(UTC),
|
|
@@ -13,7 +13,7 @@ Handler Wiring (from contract.yaml):
|
|
|
13
13
|
- ModelNodeHeartbeatEvent -> HandlerNodeHeartbeat
|
|
14
14
|
|
|
15
15
|
Handler Implementation:
|
|
16
|
-
All handlers implement
|
|
16
|
+
All handlers implement ProtocolHandler directly with:
|
|
17
17
|
- handler_id, category, message_types, node_kind properties
|
|
18
18
|
- handle(envelope) -> ModelHandlerOutput signature
|
|
19
19
|
|
|
@@ -88,7 +88,7 @@ from __future__ import annotations
|
|
|
88
88
|
import importlib
|
|
89
89
|
import logging
|
|
90
90
|
from pathlib import Path
|
|
91
|
-
from typing import TYPE_CHECKING
|
|
91
|
+
from typing import TYPE_CHECKING, cast
|
|
92
92
|
|
|
93
93
|
from omnibase_core.services.service_handler_registry import ServiceHandlerRegistry
|
|
94
94
|
from omnibase_infra.enums import EnumInfraTransportType
|
|
@@ -96,6 +96,7 @@ from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationE
|
|
|
96
96
|
from omnibase_infra.runtime.contract_loaders import (
|
|
97
97
|
load_handler_class_info_from_contract,
|
|
98
98
|
)
|
|
99
|
+
from omnibase_spi.protocols.handlers import ProtocolHandler
|
|
99
100
|
|
|
100
101
|
logger = logging.getLogger(__name__)
|
|
101
102
|
|
|
@@ -110,14 +111,14 @@ ALLOWED_NAMESPACES: tuple[str, ...] = (
|
|
|
110
111
|
|
|
111
112
|
|
|
112
113
|
def _validate_handler_protocol(handler: object) -> tuple[bool, list[str]]:
|
|
113
|
-
"""Validate handler implements
|
|
114
|
+
"""Validate handler implements ProtocolHandler via duck typing.
|
|
114
115
|
|
|
115
116
|
Uses duck typing to verify the handler has the required properties and
|
|
116
|
-
methods for
|
|
117
|
+
methods for ProtocolHandler compliance. Per ONEX conventions,
|
|
117
118
|
protocol compliance is verified via structural typing rather than
|
|
118
119
|
isinstance checks.
|
|
119
120
|
|
|
120
|
-
Protocol Requirements (
|
|
121
|
+
Protocol Requirements (ProtocolHandler):
|
|
121
122
|
- handler_id (property): Unique identifier string
|
|
122
123
|
- category (property): EnumMessageCategory value
|
|
123
124
|
- message_types (property): set[str] of message type names
|
|
@@ -492,7 +493,7 @@ class RegistryInfraNodeRegistrationOrchestrator:
|
|
|
492
493
|
context=ctx,
|
|
493
494
|
) from e
|
|
494
495
|
|
|
495
|
-
# Validate handler implements
|
|
496
|
+
# Validate handler implements ProtocolHandler
|
|
496
497
|
is_valid, missing = _validate_handler_protocol(handler_instance)
|
|
497
498
|
if not is_valid:
|
|
498
499
|
ctx = ModelInfraErrorContext.with_correlation(
|
|
@@ -501,7 +502,7 @@ class RegistryInfraNodeRegistrationOrchestrator:
|
|
|
501
502
|
target_name=handler_class_name,
|
|
502
503
|
)
|
|
503
504
|
raise ProtocolConfigurationError(
|
|
504
|
-
f"Handler '{handler_class_name}' does not implement
|
|
505
|
+
f"Handler '{handler_class_name}' does not implement ProtocolHandler. "
|
|
505
506
|
f"Missing required members: {', '.join(missing)}. "
|
|
506
507
|
f"Handlers must have: handler_id, category, message_types, node_kind properties "
|
|
507
508
|
f"and handle(envelope) method. "
|
|
@@ -510,7 +511,7 @@ class RegistryInfraNodeRegistrationOrchestrator:
|
|
|
510
511
|
)
|
|
511
512
|
|
|
512
513
|
# Register handler
|
|
513
|
-
registry.register_handler(handler_instance)
|
|
514
|
+
registry.register_handler(handler_instance) # type: ignore[arg-type]
|
|
514
515
|
logger.debug(
|
|
515
516
|
"Registered handler from contract: %s",
|
|
516
517
|
handler_class_name,
|
|
@@ -44,6 +44,7 @@ import logging
|
|
|
44
44
|
from typing import TYPE_CHECKING, TypedDict, cast
|
|
45
45
|
from uuid import UUID
|
|
46
46
|
|
|
47
|
+
from omnibase_core.enums import EnumInjectionScope
|
|
47
48
|
from omnibase_infra.enums import EnumInfraTransportType
|
|
48
49
|
from omnibase_infra.errors import (
|
|
49
50
|
ContainerValidationError,
|
|
@@ -269,12 +270,14 @@ async def wire_registration_dispatchers(
|
|
|
269
270
|
# because MessageDispatchEngine.register_dispatcher() takes a callable
|
|
270
271
|
|
|
271
272
|
# 3a. Register DispatcherNodeIntrospected
|
|
273
|
+
# Note: node_kind is NOT passed to register_dispatcher because the dispatcher's
|
|
274
|
+
# handle() method doesn't accept ModelDispatchContext - it handles time injection
|
|
275
|
+
# internally. The node_kind property is informational only.
|
|
272
276
|
engine.register_dispatcher(
|
|
273
277
|
dispatcher_id=dispatcher_introspected.dispatcher_id,
|
|
274
278
|
dispatcher=dispatcher_introspected.handle,
|
|
275
279
|
category=dispatcher_introspected.category,
|
|
276
280
|
message_types=dispatcher_introspected.message_types,
|
|
277
|
-
node_kind=dispatcher_introspected.node_kind,
|
|
278
281
|
)
|
|
279
282
|
dispatchers_registered.append(dispatcher_introspected.dispatcher_id)
|
|
280
283
|
|
|
@@ -284,7 +287,6 @@ async def wire_registration_dispatchers(
|
|
|
284
287
|
dispatcher=dispatcher_runtime_tick.handle,
|
|
285
288
|
category=dispatcher_runtime_tick.category,
|
|
286
289
|
message_types=dispatcher_runtime_tick.message_types,
|
|
287
|
-
node_kind=dispatcher_runtime_tick.node_kind,
|
|
288
290
|
)
|
|
289
291
|
dispatchers_registered.append(dispatcher_runtime_tick.dispatcher_id)
|
|
290
292
|
|
|
@@ -294,7 +296,6 @@ async def wire_registration_dispatchers(
|
|
|
294
296
|
dispatcher=dispatcher_acked.handle,
|
|
295
297
|
category=dispatcher_acked.category,
|
|
296
298
|
message_types=dispatcher_acked.message_types,
|
|
297
|
-
node_kind=dispatcher_acked.node_kind,
|
|
298
299
|
)
|
|
299
300
|
dispatchers_registered.append(dispatcher_acked.dispatcher_id)
|
|
300
301
|
|
|
@@ -409,7 +410,7 @@ async def wire_registration_handlers(
|
|
|
409
410
|
ContainerWiringError: If service registration fails.
|
|
410
411
|
|
|
411
412
|
Note:
|
|
412
|
-
Services are registered with scope=
|
|
413
|
+
Services are registered with scope=EnumInjectionScope.GLOBAL and may conflict if multiple
|
|
413
414
|
plugins register the same interface type. This is acceptable for the
|
|
414
415
|
Registration domain as these handlers are singletons by design. If you
|
|
415
416
|
need to register multiple implementations of the same interface, use
|
|
@@ -440,7 +441,7 @@ async def wire_registration_handlers(
|
|
|
440
441
|
await container.service_registry.register_instance(
|
|
441
442
|
interface=ProjectionReaderRegistration,
|
|
442
443
|
instance=projection_reader,
|
|
443
|
-
scope=
|
|
444
|
+
scope=EnumInjectionScope.GLOBAL,
|
|
444
445
|
metadata={
|
|
445
446
|
"description": "Registration projection reader",
|
|
446
447
|
"version": str(semver_default),
|
|
@@ -453,7 +454,7 @@ async def wire_registration_handlers(
|
|
|
453
454
|
await container.service_registry.register_instance(
|
|
454
455
|
interface=ProjectorShell,
|
|
455
456
|
instance=projector,
|
|
456
|
-
scope=
|
|
457
|
+
scope=EnumInjectionScope.GLOBAL,
|
|
457
458
|
metadata={
|
|
458
459
|
"description": "Registration projector",
|
|
459
460
|
"version": str(semver_default),
|
|
@@ -470,7 +471,7 @@ async def wire_registration_handlers(
|
|
|
470
471
|
await container.service_registry.register_instance(
|
|
471
472
|
interface=HandlerNodeIntrospected,
|
|
472
473
|
instance=handler_introspected,
|
|
473
|
-
scope=
|
|
474
|
+
scope=EnumInjectionScope.GLOBAL,
|
|
474
475
|
metadata={
|
|
475
476
|
"description": "Handler for NodeIntrospectionEvent",
|
|
476
477
|
"version": str(semver_default),
|
|
@@ -485,7 +486,7 @@ async def wire_registration_handlers(
|
|
|
485
486
|
await container.service_registry.register_instance(
|
|
486
487
|
interface=HandlerRuntimeTick,
|
|
487
488
|
instance=handler_runtime_tick,
|
|
488
|
-
scope=
|
|
489
|
+
scope=EnumInjectionScope.GLOBAL,
|
|
489
490
|
metadata={
|
|
490
491
|
"description": "Handler for RuntimeTick",
|
|
491
492
|
"version": str(semver_default),
|
|
@@ -501,7 +502,7 @@ async def wire_registration_handlers(
|
|
|
501
502
|
await container.service_registry.register_instance(
|
|
502
503
|
interface=HandlerNodeRegistrationAcked,
|
|
503
504
|
instance=handler_acked,
|
|
504
|
-
scope=
|
|
505
|
+
scope=EnumInjectionScope.GLOBAL,
|
|
505
506
|
metadata={
|
|
506
507
|
"description": "Handler for NodeRegistrationAcked",
|
|
507
508
|
"version": str(semver_default),
|
|
@@ -579,7 +580,7 @@ async def get_projection_reader_from_container(
|
|
|
579
580
|
_validate_service_registry(container, "resolve ProjectionReaderRegistration")
|
|
580
581
|
try:
|
|
581
582
|
return cast(
|
|
582
|
-
ProjectionReaderRegistration,
|
|
583
|
+
"ProjectionReaderRegistration",
|
|
583
584
|
await container.service_registry.resolve_service(
|
|
584
585
|
ProjectionReaderRegistration
|
|
585
586
|
),
|
|
@@ -622,7 +623,7 @@ async def get_handler_node_introspected_from_container(
|
|
|
622
623
|
_validate_service_registry(container, "resolve HandlerNodeIntrospected")
|
|
623
624
|
try:
|
|
624
625
|
return cast(
|
|
625
|
-
HandlerNodeIntrospected,
|
|
626
|
+
"HandlerNodeIntrospected",
|
|
626
627
|
await container.service_registry.resolve_service(HandlerNodeIntrospected),
|
|
627
628
|
)
|
|
628
629
|
except Exception as e:
|
|
@@ -663,7 +664,7 @@ async def get_handler_runtime_tick_from_container(
|
|
|
663
664
|
_validate_service_registry(container, "resolve HandlerRuntimeTick")
|
|
664
665
|
try:
|
|
665
666
|
return cast(
|
|
666
|
-
HandlerRuntimeTick,
|
|
667
|
+
"HandlerRuntimeTick",
|
|
667
668
|
await container.service_registry.resolve_service(HandlerRuntimeTick),
|
|
668
669
|
)
|
|
669
670
|
except Exception as e:
|
|
@@ -704,7 +705,7 @@ async def get_handler_node_registration_acked_from_container(
|
|
|
704
705
|
_validate_service_registry(container, "resolve HandlerNodeRegistrationAcked")
|
|
705
706
|
try:
|
|
706
707
|
return cast(
|
|
707
|
-
HandlerNodeRegistrationAcked,
|
|
708
|
+
"HandlerNodeRegistrationAcked",
|
|
708
709
|
await container.service_registry.resolve_service(
|
|
709
710
|
HandlerNodeRegistrationAcked
|
|
710
711
|
),
|
|
@@ -15,11 +15,6 @@
|
|
|
15
15
|
name: "node_registration_storage_effect"
|
|
16
16
|
contract_name: "node_registration_storage_effect"
|
|
17
17
|
node_name: "node_registration_storage_effect"
|
|
18
|
-
# Version (semantic versioning)
|
|
19
|
-
version:
|
|
20
|
-
major: 1
|
|
21
|
-
minor: 0
|
|
22
|
-
patch: 0
|
|
23
18
|
contract_version:
|
|
24
19
|
major: 1
|
|
25
20
|
minor: 0
|
|
@@ -17,6 +17,12 @@ Related:
|
|
|
17
17
|
- NodeRegistrationStorageEffect: Effect node that uses these dependencies
|
|
18
18
|
- ProtocolRegistrationPersistence: Protocol for storage backends
|
|
19
19
|
- ModelONEXContainer: ONEX dependency injection container
|
|
20
|
+
|
|
21
|
+
Note:
|
|
22
|
+
This registry uses a module-level dict for handler storage because the
|
|
23
|
+
ServiceRegistry in omnibase_core v1.0 doesn't support dict-style access
|
|
24
|
+
or string-keyed multi-handler routing. The handlers are still validated
|
|
25
|
+
against the protocol but stored separately.
|
|
20
26
|
"""
|
|
21
27
|
|
|
22
28
|
from __future__ import annotations
|
|
@@ -31,6 +37,12 @@ if TYPE_CHECKING:
|
|
|
31
37
|
|
|
32
38
|
__all__ = ["RegistryInfraRegistrationStorage"]
|
|
33
39
|
|
|
40
|
+
# Module-level storage for handlers and metadata
|
|
41
|
+
# ServiceRegistry in v1.0 doesn't support dict-style access needed for
|
|
42
|
+
# multi-handler routing (e.g., "postgresql", "mock" handler types)
|
|
43
|
+
_HANDLER_STORAGE: dict[str, object] = {}
|
|
44
|
+
_PROTOCOL_METADATA: dict[str, dict[str, object]] = {}
|
|
45
|
+
|
|
34
46
|
|
|
35
47
|
class RegistryInfraRegistrationStorage:
|
|
36
48
|
"""Registry for registration storage node dependencies.
|
|
@@ -72,14 +84,18 @@ class RegistryInfraRegistrationStorage:
|
|
|
72
84
|
DEFAULT_HANDLER_TYPE = "postgresql"
|
|
73
85
|
|
|
74
86
|
@staticmethod
|
|
75
|
-
def register(
|
|
87
|
+
def register(_container: ModelONEXContainer) -> None:
|
|
76
88
|
"""Register registration storage dependencies with the container.
|
|
77
89
|
|
|
78
90
|
Registers the protocol key for later handler binding. This method
|
|
79
91
|
sets up the infrastructure but does not bind a specific handler.
|
|
80
92
|
|
|
81
93
|
Args:
|
|
82
|
-
|
|
94
|
+
_container: ONEX dependency injection container. Currently unused
|
|
95
|
+
because ServiceRegistry v1.0 doesn't support dict-style access
|
|
96
|
+
for multi-handler routing. The parameter is retained for API
|
|
97
|
+
consistency with other registry methods and future migration
|
|
98
|
+
when ServiceRegistry supports the required access patterns.
|
|
83
99
|
|
|
84
100
|
Example:
|
|
85
101
|
>>> from omnibase_core.models.container import ModelONEXContainer
|
|
@@ -88,20 +104,19 @@ class RegistryInfraRegistrationStorage:
|
|
|
88
104
|
"""
|
|
89
105
|
# Register protocol metadata for discovery
|
|
90
106
|
# Actual handler binding happens via register_handler()
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
107
|
+
# Note: Uses module-level storage since ServiceRegistry v1.0 doesn't
|
|
108
|
+
# support dict-style access for multi-handler routing
|
|
109
|
+
_PROTOCOL_METADATA[RegistryInfraRegistrationStorage.PROTOCOL_KEY] = {
|
|
110
|
+
"protocol": "ProtocolRegistrationPersistence",
|
|
111
|
+
"module": "omnibase_infra.nodes.node_registration_storage_effect.protocols",
|
|
112
|
+
"description": "Protocol for registration storage backends",
|
|
113
|
+
"pluggable": True,
|
|
114
|
+
"implementations": ["postgresql", "mock"],
|
|
115
|
+
}
|
|
101
116
|
|
|
102
117
|
@staticmethod
|
|
103
118
|
def register_handler(
|
|
104
|
-
|
|
119
|
+
_container: ModelONEXContainer,
|
|
105
120
|
handler: ProtocolRegistrationPersistence,
|
|
106
121
|
) -> None:
|
|
107
122
|
"""Register a specific storage handler with the container.
|
|
@@ -110,7 +125,11 @@ class RegistryInfraRegistrationStorage:
|
|
|
110
125
|
The handler must implement ProtocolRegistrationPersistence.
|
|
111
126
|
|
|
112
127
|
Args:
|
|
113
|
-
|
|
128
|
+
_container: ONEX dependency injection container. Currently unused
|
|
129
|
+
because ServiceRegistry v1.0 doesn't support dict-style access
|
|
130
|
+
for multi-handler routing. The parameter is retained for API
|
|
131
|
+
consistency and future migration when ServiceRegistry supports
|
|
132
|
+
the required access patterns.
|
|
114
133
|
handler: Handler implementation to register.
|
|
115
134
|
|
|
116
135
|
Raises:
|
|
@@ -143,32 +162,35 @@ class RegistryInfraRegistrationStorage:
|
|
|
143
162
|
f"got {type(handler).__name__}"
|
|
144
163
|
)
|
|
145
164
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
165
|
+
# Note: Uses module-level storage since ServiceRegistry v1.0 doesn't
|
|
166
|
+
# support dict-style access for multi-handler routing
|
|
149
167
|
handler_key = (
|
|
150
168
|
f"{RegistryInfraRegistrationStorage.PROTOCOL_KEY}.{handler.handler_type}"
|
|
151
169
|
)
|
|
152
|
-
|
|
170
|
+
_HANDLER_STORAGE[handler_key] = handler
|
|
153
171
|
|
|
154
172
|
# Also register as default if it matches the default type
|
|
155
173
|
if (
|
|
156
174
|
handler.handler_type
|
|
157
175
|
== RegistryInfraRegistrationStorage.DEFAULT_HANDLER_TYPE
|
|
158
176
|
):
|
|
159
|
-
|
|
177
|
+
_HANDLER_STORAGE[
|
|
160
178
|
RegistryInfraRegistrationStorage.PROTOCOL_KEY + ".default"
|
|
161
179
|
] = handler
|
|
162
180
|
|
|
163
181
|
@staticmethod
|
|
164
182
|
def get_handler(
|
|
165
|
-
|
|
183
|
+
_container: ModelONEXContainer,
|
|
166
184
|
handler_type: str | None = None,
|
|
167
185
|
) -> ProtocolRegistrationPersistence | None:
|
|
168
186
|
"""Retrieve a registered storage handler from the container.
|
|
169
187
|
|
|
170
188
|
Args:
|
|
171
|
-
|
|
189
|
+
_container: ONEX dependency injection container. Currently unused
|
|
190
|
+
because ServiceRegistry v1.0 doesn't support dict-style access
|
|
191
|
+
for multi-handler routing. The parameter is retained for API
|
|
192
|
+
consistency and future migration when ServiceRegistry supports
|
|
193
|
+
the required access patterns.
|
|
172
194
|
handler_type: Specific handler type to retrieve. If None, returns default.
|
|
173
195
|
|
|
174
196
|
Returns:
|
|
@@ -180,9 +202,8 @@ class RegistryInfraRegistrationStorage:
|
|
|
180
202
|
... handler_type="postgresql",
|
|
181
203
|
... )
|
|
182
204
|
"""
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
205
|
+
# Note: Uses module-level storage since ServiceRegistry v1.0 doesn't
|
|
206
|
+
# support dict-style access for multi-handler routing
|
|
186
207
|
if handler_type is not None:
|
|
187
208
|
handler_key = (
|
|
188
209
|
f"{RegistryInfraRegistrationStorage.PROTOCOL_KEY}.{handler_type}"
|
|
@@ -190,5 +211,5 @@ class RegistryInfraRegistrationStorage:
|
|
|
190
211
|
else:
|
|
191
212
|
handler_key = RegistryInfraRegistrationStorage.PROTOCOL_KEY + ".default"
|
|
192
213
|
|
|
193
|
-
result =
|
|
214
|
+
result = _HANDLER_STORAGE.get(handler_key)
|
|
194
215
|
return cast("ProtocolRegistrationPersistence | None", result)
|
|
@@ -41,6 +41,7 @@ import time
|
|
|
41
41
|
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
42
42
|
from uuid import UUID
|
|
43
43
|
|
|
44
|
+
from omnibase_core.models.primitives import ModelSemVer
|
|
44
45
|
from omnibase_infra.enums import EnumBackendType
|
|
45
46
|
from omnibase_infra.errors import (
|
|
46
47
|
InfraAuthenticationError,
|
|
@@ -80,7 +81,7 @@ class ProtocolPartialRetryRequest(Protocol):
|
|
|
80
81
|
|
|
81
82
|
node_id: UUID
|
|
82
83
|
node_type: EnumNodeKind
|
|
83
|
-
node_version:
|
|
84
|
+
node_version: ModelSemVer
|
|
84
85
|
target_backend: EnumBackendType
|
|
85
86
|
idempotency_key: str | None
|
|
86
87
|
service_name: str | None
|
omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py
CHANGED
|
@@ -40,6 +40,7 @@ Related:
|
|
|
40
40
|
|
|
41
41
|
from __future__ import annotations
|
|
42
42
|
|
|
43
|
+
import logging
|
|
43
44
|
from typing import TYPE_CHECKING
|
|
44
45
|
|
|
45
46
|
from omnibase_infra.errors import ProtocolConfigurationError
|
|
@@ -50,6 +51,8 @@ if TYPE_CHECKING:
|
|
|
50
51
|
ProtocolDiscoveryOperations,
|
|
51
52
|
)
|
|
52
53
|
|
|
54
|
+
logger = logging.getLogger(__name__)
|
|
55
|
+
|
|
53
56
|
|
|
54
57
|
class RegistryInfraServiceDiscovery:
|
|
55
58
|
"""Registry for service discovery node dependencies.
|
|
@@ -59,11 +62,11 @@ class RegistryInfraServiceDiscovery:
|
|
|
59
62
|
registration and explicit handler configuration.
|
|
60
63
|
|
|
61
64
|
API Pattern Note:
|
|
62
|
-
This registry uses ``container.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
This registry uses ``container.service_registry.register_instance()``
|
|
66
|
+
for protocol-based type-safe DI resolution. This differs from
|
|
67
|
+
RegistryInfraRegistrationStorage which uses a module-level dict for
|
|
68
|
+
multi-handler routing by type string (e.g., "postgresql", "mock"),
|
|
69
|
+
as ServiceRegistry does not support dict-style indexed access.
|
|
67
70
|
|
|
68
71
|
The different patterns serve different purposes:
|
|
69
72
|
- Protocol-based registration: Single handler per protocol type
|
|
@@ -113,21 +116,18 @@ class RegistryInfraServiceDiscovery:
|
|
|
113
116
|
if container.service_registry is None:
|
|
114
117
|
return
|
|
115
118
|
|
|
116
|
-
#
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
container.register_factory(
|
|
125
|
-
ProtocolDiscoveryOperations,
|
|
126
|
-
RegistryInfraServiceDiscovery._create_handler_from_config,
|
|
119
|
+
# NOTE: Factory registration (register_factory) is not implemented in
|
|
120
|
+
# omnibase_core v1.0. This method provides no-op registration for forward
|
|
121
|
+
# compatibility. Use register_with_handler() to explicitly provide a
|
|
122
|
+
# pre-configured handler instance.
|
|
123
|
+
logger.debug(
|
|
124
|
+
"Service discovery factory registration skipped - "
|
|
125
|
+
"factory registration not implemented in v1.0. "
|
|
126
|
+
"Use register_with_handler() to register an explicit handler instance."
|
|
127
127
|
)
|
|
128
128
|
|
|
129
129
|
@staticmethod
|
|
130
|
-
def register_with_handler(
|
|
130
|
+
async def register_with_handler(
|
|
131
131
|
container: ModelONEXContainer,
|
|
132
132
|
handler: ProtocolDiscoveryOperations,
|
|
133
133
|
) -> None:
|
|
@@ -146,12 +146,13 @@ class RegistryInfraServiceDiscovery:
|
|
|
146
146
|
Example:
|
|
147
147
|
>>> container = ModelONEXContainer()
|
|
148
148
|
>>> handler = HandlerServiceDiscoveryConsul(config)
|
|
149
|
-
>>> RegistryInfraServiceDiscovery.register_with_handler(
|
|
149
|
+
>>> await RegistryInfraServiceDiscovery.register_with_handler(
|
|
150
150
|
... container,
|
|
151
151
|
... handler=handler,
|
|
152
152
|
... )
|
|
153
153
|
"""
|
|
154
154
|
# Import at runtime for isinstance check (protocol is @runtime_checkable)
|
|
155
|
+
from omnibase_core.enums import EnumInjectionScope
|
|
155
156
|
from omnibase_infra.nodes.node_service_discovery_effect.protocols import (
|
|
156
157
|
ProtocolDiscoveryOperations,
|
|
157
158
|
)
|
|
@@ -174,7 +175,11 @@ class RegistryInfraServiceDiscovery:
|
|
|
174
175
|
if container.service_registry is None:
|
|
175
176
|
return
|
|
176
177
|
|
|
177
|
-
container.register_instance(
|
|
178
|
+
await container.service_registry.register_instance(
|
|
179
|
+
interface=ProtocolDiscoveryOperations, # type: ignore[type-abstract]
|
|
180
|
+
instance=handler,
|
|
181
|
+
scope=EnumInjectionScope.GLOBAL,
|
|
182
|
+
)
|
|
178
183
|
|
|
179
184
|
@staticmethod
|
|
180
185
|
def _create_handler_from_config(
|