omnibase_infra 0.2.4__py3-none-any.whl → 0.2.5__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/nodes/node_intent_storage_effect/__init__.py +50 -0
- omnibase_infra/nodes/node_intent_storage_effect/contract.yaml +194 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/__init__.py +24 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_input.py +141 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_output.py +130 -0
- omnibase_infra/nodes/node_intent_storage_effect/node.py +94 -0
- omnibase_infra/nodes/node_intent_storage_effect/registry/__init__.py +35 -0
- omnibase_infra/nodes/node_intent_storage_effect/registry/registry_infra_intent_storage.py +294 -0
- {omnibase_infra-0.2.4.dist-info → omnibase_infra-0.2.5.dist-info}/METADATA +3 -3
- {omnibase_infra-0.2.4.dist-info → omnibase_infra-0.2.5.dist-info}/RECORD +13 -5
- {omnibase_infra-0.2.4.dist-info → omnibase_infra-0.2.5.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.2.4.dist-info → omnibase_infra-0.2.5.dist-info}/entry_points.txt +0 -0
- {omnibase_infra-0.2.4.dist-info → omnibase_infra-0.2.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 OmniNode Team
|
|
3
|
+
"""Node Intent Storage Effect - Capability-oriented intent storage node.
|
|
4
|
+
|
|
5
|
+
This package provides the NodeIntentStorageEffect, a capability-oriented
|
|
6
|
+
effect node for intent storage operations using Memgraph graph database.
|
|
7
|
+
|
|
8
|
+
Core Principle:
|
|
9
|
+
"I'm interested in what you do, not what you are"
|
|
10
|
+
|
|
11
|
+
Named by capability (intent.storage), not by specific backend implementation.
|
|
12
|
+
Uses Memgraph for graph-based intent persistence.
|
|
13
|
+
|
|
14
|
+
Capabilities:
|
|
15
|
+
- intent.storage: Store, query, and analyze intents
|
|
16
|
+
- intent.storage.store: Store classified intents as graph nodes
|
|
17
|
+
- intent.storage.query_session: Query intents by session identifier
|
|
18
|
+
- intent.storage.query_distribution: Get intent distribution statistics
|
|
19
|
+
|
|
20
|
+
Event Topics:
|
|
21
|
+
Consumed:
|
|
22
|
+
- {env}.{namespace}.onex.evt.intent-classified.v1
|
|
23
|
+
- {env}.{namespace}.onex.cmd.intent-query-session.v1
|
|
24
|
+
- {env}.{namespace}.onex.cmd.intent-query-distribution.v1
|
|
25
|
+
Published:
|
|
26
|
+
- {env}.{namespace}.onex.evt.intent-stored.v1
|
|
27
|
+
- {env}.{namespace}.onex.evt.intent-session-query-result.v1
|
|
28
|
+
- {env}.{namespace}.onex.evt.intent-distribution-result.v1
|
|
29
|
+
|
|
30
|
+
Available Exports:
|
|
31
|
+
- NodeIntentStorageEffect: The declarative effect node
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
>>> from omnibase_core.models.container import ModelONEXContainer
|
|
35
|
+
>>> from omnibase_infra.nodes.node_intent_storage_effect import (
|
|
36
|
+
... NodeIntentStorageEffect,
|
|
37
|
+
... )
|
|
38
|
+
>>>
|
|
39
|
+
>>> container = ModelONEXContainer()
|
|
40
|
+
>>> node = NodeIntentStorageEffect(container)
|
|
41
|
+
|
|
42
|
+
Related Modules:
|
|
43
|
+
- models: Pydantic models for intent storage operations
|
|
44
|
+
- registry: Dependency injection registration
|
|
45
|
+
- HandlerIntent: Handler for graph operations (omnibase_infra.handlers)
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from .node import NodeIntentStorageEffect
|
|
49
|
+
|
|
50
|
+
__all__ = ["NodeIntentStorageEffect"]
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 OmniNode Team
|
|
3
|
+
#
|
|
4
|
+
# ONEX Node Contract
|
|
5
|
+
# Node: NodeIntentStorageEffect
|
|
6
|
+
#
|
|
7
|
+
# Capability-oriented effect node for intent storage operations.
|
|
8
|
+
# Wraps HandlerIntent for graph-based intent persistence in Memgraph.
|
|
9
|
+
#
|
|
10
|
+
# This contract defines the interface for storing and querying intents
|
|
11
|
+
# as graph nodes, enabling session-based intent retrieval and distribution
|
|
12
|
+
# analysis.
|
|
13
|
+
#
|
|
14
|
+
# Related Tickets:
|
|
15
|
+
# - OMN-1509: Intent Processing System
|
|
16
|
+
# - WS-1: Intent Storage Effect Node
|
|
17
|
+
# Contract identifiers
|
|
18
|
+
name: "node_intent_storage_effect"
|
|
19
|
+
contract_name: "node_intent_storage_effect"
|
|
20
|
+
node_name: "node_intent_storage_effect"
|
|
21
|
+
contract_version:
|
|
22
|
+
major: 1
|
|
23
|
+
minor: 0
|
|
24
|
+
patch: 0
|
|
25
|
+
node_version:
|
|
26
|
+
major: 1
|
|
27
|
+
minor: 0
|
|
28
|
+
patch: 0
|
|
29
|
+
# Node type
|
|
30
|
+
node_type: "EFFECT_GENERIC"
|
|
31
|
+
# Description
|
|
32
|
+
description: >
|
|
33
|
+
Capability-oriented effect node for intent storage operations. Handles storing classified intents in Memgraph graph database and querying intents by session or distribution statistics.
|
|
34
|
+
|
|
35
|
+
# Strongly typed I/O models
|
|
36
|
+
input_model:
|
|
37
|
+
name: "ModelIntentStorageInput"
|
|
38
|
+
module: "omnibase_infra.nodes.node_intent_storage_effect.models"
|
|
39
|
+
description: "Input model for intent storage operations (classified intent event)."
|
|
40
|
+
output_model:
|
|
41
|
+
name: "ModelIntentStorageOutput"
|
|
42
|
+
module: "omnibase_infra.nodes.node_intent_storage_effect.models"
|
|
43
|
+
description: "Output model for intent storage results (storage confirmation)."
|
|
44
|
+
# Capability declaration (capability-oriented naming)
|
|
45
|
+
capabilities:
|
|
46
|
+
- name: "intent.storage"
|
|
47
|
+
description: "Store, query, and analyze intents in graph database"
|
|
48
|
+
version: "1.0.0"
|
|
49
|
+
- name: "intent.storage.store"
|
|
50
|
+
description: "Store classified intents as graph nodes"
|
|
51
|
+
- name: "intent.storage.query_session"
|
|
52
|
+
description: "Query intents by session identifier"
|
|
53
|
+
- name: "intent.storage.query_distribution"
|
|
54
|
+
description: "Get intent distribution statistics"
|
|
55
|
+
# Event-driven topic configuration
|
|
56
|
+
# Uses {env}.{namespace} placeholders for environment-aware topics
|
|
57
|
+
consumed_events:
|
|
58
|
+
- topic: "{env}.{namespace}.onex.evt.intent-classified.v1"
|
|
59
|
+
event_type: "IntentClassifiedEvent"
|
|
60
|
+
description: "Classified intent ready for storage"
|
|
61
|
+
- topic: "{env}.{namespace}.onex.cmd.intent-query-session.v1"
|
|
62
|
+
event_type: "IntentQuerySessionCommand"
|
|
63
|
+
message_category: "COMMAND"
|
|
64
|
+
description: "Command to query intents by session"
|
|
65
|
+
- topic: "{env}.{namespace}.onex.cmd.intent-query-distribution.v1"
|
|
66
|
+
event_type: "IntentQueryDistributionCommand"
|
|
67
|
+
message_category: "COMMAND"
|
|
68
|
+
description: "Command to get intent distribution statistics"
|
|
69
|
+
published_events:
|
|
70
|
+
- topic: "{env}.{namespace}.onex.evt.intent-stored.v1"
|
|
71
|
+
event_type: "IntentStoredEvent"
|
|
72
|
+
description: "Confirmation that intent was stored successfully"
|
|
73
|
+
- topic: "{env}.{namespace}.onex.evt.intent-session-query-result.v1"
|
|
74
|
+
event_type: "IntentSessionQueryResultEvent"
|
|
75
|
+
description: "Result of session-based intent query"
|
|
76
|
+
- topic: "{env}.{namespace}.onex.evt.intent-distribution-result.v1"
|
|
77
|
+
event_type: "IntentDistributionResultEvent"
|
|
78
|
+
description: "Result of intent distribution statistics query"
|
|
79
|
+
# IO operations (EFFECT node specific)
|
|
80
|
+
# Note: correlation_id is required (UUID) for all operations.
|
|
81
|
+
# Callers must provide a correlation_id for tracing.
|
|
82
|
+
io_operations:
|
|
83
|
+
- operation: "intent.store"
|
|
84
|
+
description: "Store a classified intent as a graph node"
|
|
85
|
+
input_fields:
|
|
86
|
+
- intent_type: "str"
|
|
87
|
+
- session_id: "str | None"
|
|
88
|
+
- payload: "dict[str, object]"
|
|
89
|
+
- correlation_id: "UUID"
|
|
90
|
+
output_fields:
|
|
91
|
+
- node_id: "str"
|
|
92
|
+
- element_id: "str"
|
|
93
|
+
- success: "bool"
|
|
94
|
+
# Not idempotent: Each call creates a new graph node with unique element_id.
|
|
95
|
+
# Duplicate calls with same input will create multiple nodes.
|
|
96
|
+
# Use correlation_id for deduplication at the caller level if needed.
|
|
97
|
+
idempotent: false
|
|
98
|
+
- operation: "intent.query_session"
|
|
99
|
+
description: "Query intents by session identifier"
|
|
100
|
+
input_fields:
|
|
101
|
+
- session_id: "str"
|
|
102
|
+
- correlation_id: "UUID"
|
|
103
|
+
output_fields:
|
|
104
|
+
- intents: "list[dict]"
|
|
105
|
+
- count: "int"
|
|
106
|
+
idempotent: true
|
|
107
|
+
- operation: "intent.query_distribution"
|
|
108
|
+
description: "Get intent count and distribution by intent_type"
|
|
109
|
+
input_fields:
|
|
110
|
+
- correlation_id: "UUID"
|
|
111
|
+
output_fields:
|
|
112
|
+
- total_count: "int"
|
|
113
|
+
- distribution: "dict[str, int]"
|
|
114
|
+
idempotent: true
|
|
115
|
+
# Handler routing (wraps HandlerIntent which wraps HandlerGraph)
|
|
116
|
+
handler_routing:
|
|
117
|
+
routing_strategy: "operation_match"
|
|
118
|
+
default_handler: "memgraph"
|
|
119
|
+
handlers:
|
|
120
|
+
- handler_type: "memgraph"
|
|
121
|
+
handler:
|
|
122
|
+
name: "HandlerIntent"
|
|
123
|
+
module: "omnibase_infra.handlers.handler_intent"
|
|
124
|
+
description: "Memgraph graph database handler for intent storage"
|
|
125
|
+
supported_operations:
|
|
126
|
+
- "intent.store"
|
|
127
|
+
- "intent.query_session"
|
|
128
|
+
- "intent.query_distribution"
|
|
129
|
+
# Dependencies (protocols this node requires)
|
|
130
|
+
dependencies:
|
|
131
|
+
- name: "handler_intent"
|
|
132
|
+
type: "handler"
|
|
133
|
+
class_name: "HandlerIntent"
|
|
134
|
+
module: "omnibase_infra.handlers.handler_intent"
|
|
135
|
+
description: "Intent handler for graph operations"
|
|
136
|
+
- name: "handler_graph"
|
|
137
|
+
type: "handler"
|
|
138
|
+
class_name: "HandlerGraph"
|
|
139
|
+
module: "omnibase_infra.handlers.handler_graph"
|
|
140
|
+
description: "Underlying graph handler (Memgraph)"
|
|
141
|
+
transitive: true
|
|
142
|
+
# Error handling configuration
|
|
143
|
+
error_handling:
|
|
144
|
+
retry_policy:
|
|
145
|
+
max_retries: 3
|
|
146
|
+
initial_delay_ms: 100
|
|
147
|
+
max_delay_ms: 5000
|
|
148
|
+
exponential_base: 2
|
|
149
|
+
retry_on:
|
|
150
|
+
- "InfraConnectionError"
|
|
151
|
+
- "InfraTimeoutError"
|
|
152
|
+
circuit_breaker:
|
|
153
|
+
enabled: true
|
|
154
|
+
failure_threshold: 5
|
|
155
|
+
reset_timeout_ms: 60000
|
|
156
|
+
error_types:
|
|
157
|
+
- name: "GraphConnectionError"
|
|
158
|
+
description: "Unable to connect to Memgraph"
|
|
159
|
+
recoverable: true
|
|
160
|
+
retry_strategy: "exponential_backoff"
|
|
161
|
+
- name: "GraphTimeoutError"
|
|
162
|
+
description: "Graph operation timed out"
|
|
163
|
+
recoverable: true
|
|
164
|
+
retry_strategy: "exponential_backoff"
|
|
165
|
+
- name: "IntentNotFoundError"
|
|
166
|
+
description: "Intent not found in graph"
|
|
167
|
+
recoverable: false
|
|
168
|
+
retry_strategy: "none"
|
|
169
|
+
- name: "ValidationError"
|
|
170
|
+
description: "Input validation failed"
|
|
171
|
+
recoverable: false
|
|
172
|
+
retry_strategy: "none"
|
|
173
|
+
# Health check configuration
|
|
174
|
+
health_check:
|
|
175
|
+
enabled: true
|
|
176
|
+
endpoint: "/health"
|
|
177
|
+
interval_seconds: 30
|
|
178
|
+
timeout_ms: 5000
|
|
179
|
+
checks:
|
|
180
|
+
- name: "graph_backend"
|
|
181
|
+
type: "connection"
|
|
182
|
+
critical: true
|
|
183
|
+
# Metadata
|
|
184
|
+
metadata:
|
|
185
|
+
author: "OmniNode Team"
|
|
186
|
+
license: "MIT"
|
|
187
|
+
created: "2026-01-26"
|
|
188
|
+
tags:
|
|
189
|
+
- effect
|
|
190
|
+
- intent
|
|
191
|
+
- storage
|
|
192
|
+
- graph
|
|
193
|
+
- memgraph
|
|
194
|
+
- capability-oriented
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 OmniNode Team
|
|
3
|
+
"""Models for Intent Storage Effect Node.
|
|
4
|
+
|
|
5
|
+
This module exports models used by the NodeIntentStorageEffect for
|
|
6
|
+
capability-oriented intent storage operations in Memgraph.
|
|
7
|
+
|
|
8
|
+
Available Models:
|
|
9
|
+
- ModelIntentStorageInput: Input model for intent storage (classified intent data)
|
|
10
|
+
- ModelIntentStorageOutput: Output model for storage results (node ID, success)
|
|
11
|
+
|
|
12
|
+
All models are:
|
|
13
|
+
- Frozen (immutable after creation)
|
|
14
|
+
- Extra="forbid" (no extra fields allowed)
|
|
15
|
+
- Strongly typed (no Any types)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from .model_intent_storage_input import ModelIntentStorageInput
|
|
19
|
+
from .model_intent_storage_output import ModelIntentStorageOutput
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"ModelIntentStorageInput",
|
|
23
|
+
"ModelIntentStorageOutput",
|
|
24
|
+
]
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 OmniNode Team
|
|
3
|
+
"""Intent Storage Input Model for Intent Storage Operations.
|
|
4
|
+
|
|
5
|
+
This module provides ModelIntentStorageInput, representing the input
|
|
6
|
+
for storing a classified intent in the graph database.
|
|
7
|
+
|
|
8
|
+
Architecture:
|
|
9
|
+
ModelIntentStorageInput is constructed from IntentClassifiedEvent payloads
|
|
10
|
+
and contains:
|
|
11
|
+
- intent_type: The classified intent type
|
|
12
|
+
- session_id: Optional session identifier for grouping
|
|
13
|
+
- payload: Intent-specific data to store as node properties
|
|
14
|
+
- correlation_id: Required correlation ID for tracing (fail-fast validation)
|
|
15
|
+
|
|
16
|
+
This model serves as the canonical input for the intent.store operation.
|
|
17
|
+
|
|
18
|
+
Related:
|
|
19
|
+
- NodeIntentStorageEffect: Effect node that processes this input
|
|
20
|
+
- ModelIntentStorageOutput: Output model containing storage result
|
|
21
|
+
- HandlerIntent: Handler that executes storage operations
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from uuid import UUID
|
|
27
|
+
|
|
28
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
29
|
+
|
|
30
|
+
from omnibase_core.enums import EnumCoreErrorCode
|
|
31
|
+
from omnibase_core.errors import OnexError
|
|
32
|
+
from omnibase_core.types import JsonType
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ModelIntentStorageInput(BaseModel):
|
|
36
|
+
"""Input model for intent storage operations.
|
|
37
|
+
|
|
38
|
+
Defines the required fields for storing a classified intent as a
|
|
39
|
+
graph node in Memgraph.
|
|
40
|
+
|
|
41
|
+
Immutability:
|
|
42
|
+
This model uses frozen=True to ensure inputs are immutable
|
|
43
|
+
once created, enabling safe reuse and caching.
|
|
44
|
+
|
|
45
|
+
Attributes:
|
|
46
|
+
intent_type: The classified intent type (e.g., "query", "action").
|
|
47
|
+
session_id: Optional session identifier for grouping related intents.
|
|
48
|
+
payload: Intent-specific data to store as node properties.
|
|
49
|
+
correlation_id: Required correlation ID for request tracing.
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
>>> from uuid import uuid4
|
|
53
|
+
>>> input_model = ModelIntentStorageInput(
|
|
54
|
+
... intent_type="query",
|
|
55
|
+
... session_id="sess-12345",
|
|
56
|
+
... payload={"query": "What is ONEX?", "confidence": 0.95},
|
|
57
|
+
... correlation_id=uuid4(),
|
|
58
|
+
... )
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
|
|
62
|
+
|
|
63
|
+
intent_type: str = Field(
|
|
64
|
+
...,
|
|
65
|
+
description="The classified intent type",
|
|
66
|
+
min_length=1,
|
|
67
|
+
max_length=256,
|
|
68
|
+
)
|
|
69
|
+
session_id: str | None = Field(
|
|
70
|
+
default=None,
|
|
71
|
+
description="Optional session identifier for grouping related intents",
|
|
72
|
+
max_length=256,
|
|
73
|
+
)
|
|
74
|
+
payload: dict[str, JsonType] = Field(
|
|
75
|
+
default_factory=dict,
|
|
76
|
+
description="Intent-specific data to store as node properties",
|
|
77
|
+
)
|
|
78
|
+
correlation_id: UUID = Field(
|
|
79
|
+
...,
|
|
80
|
+
description="Correlation ID for request tracing (required for observability)",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
@field_validator("payload")
|
|
84
|
+
@classmethod
|
|
85
|
+
def validate_no_reserved_keys(cls, v: dict[str, JsonType]) -> dict[str, JsonType]:
|
|
86
|
+
"""Validate payload does not contain reserved keys.
|
|
87
|
+
|
|
88
|
+
Reserved keys (intent_type, session_id, correlation_id) are used as
|
|
89
|
+
structured fields and cannot appear in the payload to prevent conflicts
|
|
90
|
+
during storage property merging.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
v: The payload dictionary to validate.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
The validated payload dictionary.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
OnexError: If payload contains any reserved keys.
|
|
100
|
+
"""
|
|
101
|
+
reserved_keys = {"intent_type", "session_id", "correlation_id"}
|
|
102
|
+
conflicting = reserved_keys & v.keys()
|
|
103
|
+
if conflicting:
|
|
104
|
+
raise OnexError(
|
|
105
|
+
message=f"Payload cannot contain reserved keys: {sorted(conflicting)}",
|
|
106
|
+
error_code=EnumCoreErrorCode.VALIDATION_ERROR,
|
|
107
|
+
)
|
|
108
|
+
return v
|
|
109
|
+
|
|
110
|
+
def has_session(self) -> bool:
|
|
111
|
+
"""Check if this input has a session identifier.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
True if session_id is provided.
|
|
115
|
+
"""
|
|
116
|
+
return self.session_id is not None
|
|
117
|
+
|
|
118
|
+
def to_storage_properties(self) -> dict[str, JsonType]:
|
|
119
|
+
"""Convert input to storage-friendly properties dict.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Dictionary containing all intent properties for graph storage,
|
|
123
|
+
including intent_type, correlation_id, and session_id merged with payload.
|
|
124
|
+
|
|
125
|
+
Note:
|
|
126
|
+
Reserved key validation (intent_type, session_id, correlation_id)
|
|
127
|
+
is performed at model construction time via field_validator,
|
|
128
|
+
so this method can safely merge payload with structured fields.
|
|
129
|
+
"""
|
|
130
|
+
properties: dict[str, JsonType] = {
|
|
131
|
+
"intent_type": self.intent_type,
|
|
132
|
+
"correlation_id": str(self.correlation_id),
|
|
133
|
+
}
|
|
134
|
+
if self.session_id:
|
|
135
|
+
properties["session_id"] = self.session_id
|
|
136
|
+
|
|
137
|
+
properties.update(self.payload)
|
|
138
|
+
return properties
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
__all__ = ["ModelIntentStorageInput"]
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 OmniNode Team
|
|
3
|
+
"""Intent Storage Output Model for Intent Storage Operations.
|
|
4
|
+
|
|
5
|
+
This module provides ModelIntentStorageOutput, representing the result
|
|
6
|
+
of storing a classified intent in the graph database.
|
|
7
|
+
|
|
8
|
+
Architecture:
|
|
9
|
+
ModelIntentStorageOutput is returned from intent.store operations
|
|
10
|
+
and contains:
|
|
11
|
+
- success: Whether the storage operation succeeded
|
|
12
|
+
- node_id: The graph node ID of the stored intent
|
|
13
|
+
- element_id: The graph element ID for the stored intent
|
|
14
|
+
- labels: Graph labels applied to the node
|
|
15
|
+
- properties: The stored properties (echoed back for verification)
|
|
16
|
+
- correlation_id: Correlation ID for request tracing
|
|
17
|
+
- error: Sanitized error message if operation failed
|
|
18
|
+
|
|
19
|
+
This model serves as the canonical output for the intent.store operation.
|
|
20
|
+
|
|
21
|
+
Related:
|
|
22
|
+
- NodeIntentStorageEffect: Effect node that produces this output
|
|
23
|
+
- ModelIntentStorageInput: Input model for storage operations
|
|
24
|
+
- HandlerIntent: Handler that executes storage operations
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from uuid import UUID
|
|
30
|
+
|
|
31
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
32
|
+
|
|
33
|
+
from omnibase_core.types import JsonType
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ModelIntentStorageOutput(BaseModel):
|
|
37
|
+
"""Output model for intent storage operations.
|
|
38
|
+
|
|
39
|
+
Contains the result of storing a classified intent as a graph node,
|
|
40
|
+
including the assigned node ID and stored properties.
|
|
41
|
+
|
|
42
|
+
Immutability:
|
|
43
|
+
This model uses frozen=True to ensure outputs are immutable
|
|
44
|
+
once created, enabling safe sharing and caching.
|
|
45
|
+
|
|
46
|
+
Attributes:
|
|
47
|
+
success: Whether the storage operation completed successfully.
|
|
48
|
+
node_id: The graph database node ID of the stored intent.
|
|
49
|
+
element_id: The graph element ID for the stored intent.
|
|
50
|
+
labels: Graph labels applied to the node (e.g., ["Intent"]).
|
|
51
|
+
properties: The stored properties (for verification).
|
|
52
|
+
correlation_id: Correlation ID for request tracing (required).
|
|
53
|
+
error: Sanitized error message if operation failed.
|
|
54
|
+
duration_ms: Time taken for the operation in milliseconds.
|
|
55
|
+
|
|
56
|
+
Example:
|
|
57
|
+
>>> output = ModelIntentStorageOutput(
|
|
58
|
+
... success=True,
|
|
59
|
+
... node_id="123",
|
|
60
|
+
... element_id="4:abc:123",
|
|
61
|
+
... labels=("Intent",),
|
|
62
|
+
... properties={"intent_type": "query", "session_id": "sess-12345"},
|
|
63
|
+
... correlation_id=correlation_id,
|
|
64
|
+
... duration_ms=15.2,
|
|
65
|
+
... )
|
|
66
|
+
>>> if output: # Uses custom __bool__
|
|
67
|
+
... print(f"Stored intent: {output.node_id}")
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
|
|
71
|
+
|
|
72
|
+
success: bool = Field(
|
|
73
|
+
...,
|
|
74
|
+
description="Whether the storage operation completed successfully",
|
|
75
|
+
)
|
|
76
|
+
node_id: str | None = Field(
|
|
77
|
+
default=None,
|
|
78
|
+
description="The graph database node ID of the stored intent",
|
|
79
|
+
)
|
|
80
|
+
element_id: str | None = Field(
|
|
81
|
+
default=None,
|
|
82
|
+
description="The graph element ID for the stored intent",
|
|
83
|
+
)
|
|
84
|
+
labels: tuple[str, ...] = Field(
|
|
85
|
+
default_factory=tuple,
|
|
86
|
+
description="Graph labels applied to the node",
|
|
87
|
+
)
|
|
88
|
+
properties: dict[str, JsonType] = Field(
|
|
89
|
+
default_factory=dict,
|
|
90
|
+
description="The stored properties (for verification)",
|
|
91
|
+
)
|
|
92
|
+
correlation_id: UUID = Field(
|
|
93
|
+
...,
|
|
94
|
+
description="Correlation ID for request tracing (required for traceability)",
|
|
95
|
+
)
|
|
96
|
+
error: str | None = Field(
|
|
97
|
+
default=None,
|
|
98
|
+
description="Sanitized error message if operation failed",
|
|
99
|
+
)
|
|
100
|
+
duration_ms: float = Field(
|
|
101
|
+
default=0.0,
|
|
102
|
+
description="Time taken for the operation in milliseconds",
|
|
103
|
+
ge=0.0,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def __bool__(self) -> bool:
|
|
107
|
+
"""Return True if the storage operation succeeded.
|
|
108
|
+
|
|
109
|
+
Warning:
|
|
110
|
+
This is non-standard Pydantic behavior. Normally bool(model)
|
|
111
|
+
returns True for any instance. This override enables idiomatic
|
|
112
|
+
`if result:` checks for success validation.
|
|
113
|
+
|
|
114
|
+
See: docs/decisions/adr-custom-bool-result-models.md
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
True if success is True, False otherwise.
|
|
118
|
+
"""
|
|
119
|
+
return self.success
|
|
120
|
+
|
|
121
|
+
def has_node_id(self) -> bool:
|
|
122
|
+
"""Check if a node ID was assigned.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
True if node_id is present.
|
|
126
|
+
"""
|
|
127
|
+
return self.node_id is not None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
__all__ = ["ModelIntentStorageOutput"]
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 OmniNode Team
|
|
3
|
+
"""Node Intent Storage Effect - Capability-oriented intent storage node.
|
|
4
|
+
|
|
5
|
+
This effect node provides intent storage capabilities using Memgraph graph database.
|
|
6
|
+
Named by capability ("intent.storage"), not by vendor (e.g., Memgraph).
|
|
7
|
+
|
|
8
|
+
Core Principle:
|
|
9
|
+
"I'm interested in what you do, not what you are"
|
|
10
|
+
|
|
11
|
+
This node follows the ONEX declarative pattern:
|
|
12
|
+
- DECLARATIVE effect driven by contract.yaml
|
|
13
|
+
- Zero custom storage logic - all behavior from handler (HandlerIntent)
|
|
14
|
+
- Lightweight shell that delegates to HandlerIntent -> HandlerGraph
|
|
15
|
+
- Pattern: "Contract-driven, handlers wired externally"
|
|
16
|
+
|
|
17
|
+
Extends NodeEffect from omnibase_core for external I/O operations.
|
|
18
|
+
All storage logic is 100% driven by handler implementations, not Python code.
|
|
19
|
+
|
|
20
|
+
Capabilities:
|
|
21
|
+
- intent.storage: Store, query, and analyze intents
|
|
22
|
+
- intent.storage.store: Store classified intents as graph nodes
|
|
23
|
+
- intent.storage.query_session: Query intents by session identifier
|
|
24
|
+
- intent.storage.query_distribution: Get intent distribution statistics
|
|
25
|
+
|
|
26
|
+
Event Topics:
|
|
27
|
+
Consumed:
|
|
28
|
+
- {env}.{namespace}.onex.evt.intent-classified.v1
|
|
29
|
+
- {env}.{namespace}.onex.cmd.intent-query-session.v1
|
|
30
|
+
- {env}.{namespace}.onex.cmd.intent-query-distribution.v1
|
|
31
|
+
Published:
|
|
32
|
+
- {env}.{namespace}.onex.evt.intent-stored.v1
|
|
33
|
+
- {env}.{namespace}.onex.evt.intent-session-query-result.v1
|
|
34
|
+
- {env}.{namespace}.onex.evt.intent-distribution-result.v1
|
|
35
|
+
|
|
36
|
+
Handler Stack:
|
|
37
|
+
NodeIntentStorageEffect -> HandlerIntent -> HandlerGraph -> Memgraph
|
|
38
|
+
|
|
39
|
+
Design Decisions:
|
|
40
|
+
- 100% Contract-Driven: All capabilities in YAML, not Python
|
|
41
|
+
- Zero Custom Methods: Base class handles everything
|
|
42
|
+
- Declarative Execution: Handler wired externally
|
|
43
|
+
- Capability-Oriented: Named by what it does, not what it uses
|
|
44
|
+
|
|
45
|
+
Related:
|
|
46
|
+
- contract.yaml: Capability definitions and IO operations
|
|
47
|
+
- HandlerIntent: Intent-specific graph operations handler
|
|
48
|
+
- HandlerGraph: Underlying Memgraph graph handler
|
|
49
|
+
- models/: Input, output models
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
from __future__ import annotations
|
|
53
|
+
|
|
54
|
+
from typing import TYPE_CHECKING
|
|
55
|
+
|
|
56
|
+
from omnibase_core.nodes.node_effect import NodeEffect
|
|
57
|
+
|
|
58
|
+
if TYPE_CHECKING:
|
|
59
|
+
from omnibase_core.models.container.model_onex_container import ModelONEXContainer
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class NodeIntentStorageEffect(NodeEffect):
|
|
63
|
+
"""Effect node for intent storage operations.
|
|
64
|
+
|
|
65
|
+
Capability: intent.storage
|
|
66
|
+
|
|
67
|
+
Provides a capability-oriented interface for intent storage operations.
|
|
68
|
+
Uses Memgraph graph database for storing intents as nodes with properties.
|
|
69
|
+
|
|
70
|
+
This node is declarative - all behavior is defined in contract.yaml and
|
|
71
|
+
implemented through the handler (HandlerIntent). No custom storage logic
|
|
72
|
+
exists in this class.
|
|
73
|
+
|
|
74
|
+
Attributes:
|
|
75
|
+
container: ONEX dependency injection container
|
|
76
|
+
|
|
77
|
+
Example:
|
|
78
|
+
>>> from omnibase_core.models.container import ModelONEXContainer
|
|
79
|
+
>>> container = ModelONEXContainer()
|
|
80
|
+
>>> node = NodeIntentStorageEffect(container)
|
|
81
|
+
>>> # Handler must be wired externally via registry
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init__(self, container: ModelONEXContainer) -> None:
|
|
85
|
+
"""Initialize the intent storage effect node.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
container: ONEX dependency injection container for resolving
|
|
89
|
+
dependencies defined in contract.yaml.
|
|
90
|
+
"""
|
|
91
|
+
super().__init__(container)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
__all__ = ["NodeIntentStorageEffect"]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 OmniNode Team
|
|
3
|
+
"""Registry for Intent Storage Effect Node.
|
|
4
|
+
|
|
5
|
+
This module exports the RegistryInfraIntentStorage for dependency registration
|
|
6
|
+
with the ONEX container. The registry wires up the NodeIntentStorageEffect
|
|
7
|
+
with its handler dependencies (HandlerIntent -> HandlerGraph -> Memgraph).
|
|
8
|
+
|
|
9
|
+
Architecture:
|
|
10
|
+
The registry follows the ONEX container-based dependency injection pattern:
|
|
11
|
+
- Registers NodeIntentStorageEffect with the container
|
|
12
|
+
- Wires handler dependencies defined in contract.yaml
|
|
13
|
+
- Enables capability-oriented resolution (intent.storage)
|
|
14
|
+
|
|
15
|
+
Registration Flow:
|
|
16
|
+
Container -> RegistryInfraIntentStorage.register() -> NodeIntentStorageEffect
|
|
17
|
+
-> HandlerIntent (resolved)
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
>>> from omnibase_core.models.container import ModelONEXContainer
|
|
21
|
+
>>> from omnibase_infra.nodes.node_intent_storage_effect.registry import (
|
|
22
|
+
... RegistryInfraIntentStorage,
|
|
23
|
+
... )
|
|
24
|
+
>>> container = ModelONEXContainer()
|
|
25
|
+
>>> RegistryInfraIntentStorage.register(container)
|
|
26
|
+
|
|
27
|
+
Related:
|
|
28
|
+
- NodeIntentStorageEffect: The effect node registered by this registry
|
|
29
|
+
- HandlerIntent: Handler wired for intent storage operations
|
|
30
|
+
- ModelONEXContainer: ONEX dependency injection container
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from .registry_infra_intent_storage import RegistryInfraIntentStorage
|
|
34
|
+
|
|
35
|
+
__all__ = ["RegistryInfraIntentStorage"]
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 OmniNode Team
|
|
3
|
+
"""Registry for Intent Storage Node Dependencies.
|
|
4
|
+
|
|
5
|
+
This module provides RegistryInfraIntentStorage, which registers
|
|
6
|
+
dependencies for the NodeIntentStorageEffect node.
|
|
7
|
+
|
|
8
|
+
Architecture:
|
|
9
|
+
The registry follows ONEX container-based dependency injection:
|
|
10
|
+
- Registers HandlerIntent with ModelONEXContainer
|
|
11
|
+
- Supports initialization with pre-configured HandlerGraph
|
|
12
|
+
- Enables runtime handler resolution via container
|
|
13
|
+
|
|
14
|
+
Registration is typically called during application bootstrap.
|
|
15
|
+
|
|
16
|
+
Related:
|
|
17
|
+
- NodeIntentStorageEffect: Effect node that uses these dependencies
|
|
18
|
+
- HandlerIntent: Intent handler for graph operations
|
|
19
|
+
- HandlerGraph: Underlying Memgraph graph handler
|
|
20
|
+
- ModelONEXContainer: ONEX dependency injection container
|
|
21
|
+
|
|
22
|
+
Testing:
|
|
23
|
+
This module uses module-level state (``_HANDLER_STORAGE``, ``_PROTOCOL_METADATA``)
|
|
24
|
+
for handler storage. Tests MUST call ``RegistryInfraIntentStorage.clear()`` in
|
|
25
|
+
setup and teardown fixtures to prevent test pollution between test cases.
|
|
26
|
+
|
|
27
|
+
Failure to clear state can cause:
|
|
28
|
+
- Tests passing in isolation but failing when run together
|
|
29
|
+
- Handlers from previous tests leaking into subsequent tests
|
|
30
|
+
- Flaky test behavior that is difficult to debug
|
|
31
|
+
|
|
32
|
+
Recommended fixture pattern:
|
|
33
|
+
|
|
34
|
+
.. code-block:: python
|
|
35
|
+
|
|
36
|
+
@pytest.fixture(autouse=True)
|
|
37
|
+
def clear_registry():
|
|
38
|
+
RegistryInfraIntentStorage.clear()
|
|
39
|
+
yield
|
|
40
|
+
RegistryInfraIntentStorage.clear()
|
|
41
|
+
|
|
42
|
+
Note:
|
|
43
|
+
This registry uses a module-level dict for handler storage because the
|
|
44
|
+
ServiceRegistry in omnibase_core v1.0 doesn't support dict-style access
|
|
45
|
+
or string-keyed multi-handler routing. The handlers are still validated
|
|
46
|
+
but stored separately.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
from __future__ import annotations
|
|
50
|
+
|
|
51
|
+
import logging
|
|
52
|
+
from typing import TYPE_CHECKING, cast
|
|
53
|
+
|
|
54
|
+
logger = logging.getLogger(__name__)
|
|
55
|
+
|
|
56
|
+
if TYPE_CHECKING:
|
|
57
|
+
from omnibase_core.models.container.model_onex_container import ModelONEXContainer
|
|
58
|
+
from omnibase_infra.handlers.handler_intent import HandlerIntent
|
|
59
|
+
|
|
60
|
+
__all__ = ["RegistryInfraIntentStorage"]
|
|
61
|
+
|
|
62
|
+
# TODO(ServiceRegistry-v2): Migrate to container.service_registry once v2.0
|
|
63
|
+
# supports dict-style access for multi-handler routing. Current module-level
|
|
64
|
+
# storage is a workaround for v1.0 limitations. See docstring Note section.
|
|
65
|
+
#
|
|
66
|
+
# Module-level storage for handlers and metadata
|
|
67
|
+
# ServiceRegistry in v1.0 doesn't support dict-style access needed for
|
|
68
|
+
# handler routing
|
|
69
|
+
_HANDLER_STORAGE: dict[str, object] = {}
|
|
70
|
+
_PROTOCOL_METADATA: dict[str, dict[str, object]] = {}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class RegistryInfraIntentStorage:
|
|
74
|
+
"""Registry for intent storage node dependencies.
|
|
75
|
+
|
|
76
|
+
Registers handler implementations with the ONEX container.
|
|
77
|
+
Supports the HandlerIntent which wraps HandlerGraph for Memgraph operations.
|
|
78
|
+
|
|
79
|
+
Usage:
|
|
80
|
+
.. code-block:: python
|
|
81
|
+
|
|
82
|
+
from omnibase_core.models.container import ModelONEXContainer
|
|
83
|
+
from omnibase_infra.nodes.node_intent_storage_effect.registry import (
|
|
84
|
+
RegistryInfraIntentStorage,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Create container
|
|
88
|
+
container = ModelONEXContainer()
|
|
89
|
+
|
|
90
|
+
# Register dependencies
|
|
91
|
+
RegistryInfraIntentStorage.register(container)
|
|
92
|
+
|
|
93
|
+
# Register the handler (must have graph_handler configured)
|
|
94
|
+
RegistryInfraIntentStorage.register_handler(
|
|
95
|
+
container,
|
|
96
|
+
handler=intent_handler,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
Note:
|
|
100
|
+
This registry does NOT instantiate handlers. Handlers must be
|
|
101
|
+
created externally with their specific dependencies (HandlerGraph)
|
|
102
|
+
and then registered via register_handler().
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
# Handler key for container registration
|
|
106
|
+
HANDLER_KEY = "handler_intent"
|
|
107
|
+
|
|
108
|
+
# Default handler type
|
|
109
|
+
DEFAULT_HANDLER_TYPE = "memgraph"
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def _is_registered(handler_key: str) -> bool:
|
|
113
|
+
"""Check if a handler is already registered for the given key.
|
|
114
|
+
|
|
115
|
+
This is a private helper method used to detect re-registration
|
|
116
|
+
attempts, which may indicate container lifecycle issues or
|
|
117
|
+
missing clear() calls in tests.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
handler_key: The handler key to check (e.g., "handler_intent.memgraph").
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
True if a handler is already registered for this key, False otherwise.
|
|
124
|
+
"""
|
|
125
|
+
return handler_key in _HANDLER_STORAGE
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def register(_container: ModelONEXContainer) -> None:
|
|
129
|
+
"""Register intent storage dependencies with the container.
|
|
130
|
+
|
|
131
|
+
Registers the handler key for later handler binding. This method
|
|
132
|
+
sets up the infrastructure but does not bind a specific handler.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
_container: ONEX dependency injection container. Currently unused
|
|
136
|
+
because ServiceRegistry v1.0 doesn't support dict-style access
|
|
137
|
+
for multi-handler routing. The parameter is retained for API
|
|
138
|
+
consistency with other registry methods and future migration.
|
|
139
|
+
|
|
140
|
+
Example:
|
|
141
|
+
>>> from omnibase_core.models.container import ModelONEXContainer
|
|
142
|
+
>>> container = ModelONEXContainer()
|
|
143
|
+
>>> RegistryInfraIntentStorage.register(container)
|
|
144
|
+
"""
|
|
145
|
+
# Register handler metadata for discovery
|
|
146
|
+
# Actual handler binding happens via register_handler()
|
|
147
|
+
_PROTOCOL_METADATA[RegistryInfraIntentStorage.HANDLER_KEY] = {
|
|
148
|
+
"handler": "HandlerIntent",
|
|
149
|
+
"module": "omnibase_infra.handlers.handler_intent",
|
|
150
|
+
"description": "Handler for intent storage operations in Memgraph",
|
|
151
|
+
"capabilities": [
|
|
152
|
+
"intent.storage",
|
|
153
|
+
"intent.storage.store",
|
|
154
|
+
"intent.storage.query_session",
|
|
155
|
+
"intent.storage.query_distribution",
|
|
156
|
+
],
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@staticmethod
|
|
160
|
+
def register_handler(
|
|
161
|
+
_container: ModelONEXContainer,
|
|
162
|
+
handler: HandlerIntent,
|
|
163
|
+
) -> None:
|
|
164
|
+
"""Register a specific intent handler with the container.
|
|
165
|
+
|
|
166
|
+
Binds a concrete HandlerIntent implementation to the handler key.
|
|
167
|
+
The handler must already be initialized with a HandlerGraph.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
_container: ONEX dependency injection container. Currently unused
|
|
171
|
+
because ServiceRegistry v1.0 doesn't support dict-style access.
|
|
172
|
+
The parameter is retained for API consistency.
|
|
173
|
+
handler: HandlerIntent instance to register (must be initialized).
|
|
174
|
+
|
|
175
|
+
Raises:
|
|
176
|
+
ProtocolConfigurationError: If handler does not implement the required
|
|
177
|
+
protocol methods (initialize, shutdown, execute).
|
|
178
|
+
|
|
179
|
+
Example:
|
|
180
|
+
>>> from omnibase_infra.handlers.handler_intent import HandlerIntent
|
|
181
|
+
>>> handler = HandlerIntent(container)
|
|
182
|
+
>>> await handler.initialize({"graph_handler": graph_handler})
|
|
183
|
+
>>> RegistryInfraIntentStorage.register_handler(container, handler)
|
|
184
|
+
"""
|
|
185
|
+
from omnibase_infra.enums import EnumInfraTransportType
|
|
186
|
+
from omnibase_infra.errors import (
|
|
187
|
+
ModelInfraErrorContext,
|
|
188
|
+
ProtocolConfigurationError,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# NOTE: Protocol-based duck typing is used here per ONEX conventions.
|
|
192
|
+
# We verify the handler implements required methods rather than checking
|
|
193
|
+
# concrete class inheritance. This enables:
|
|
194
|
+
# 1. Fail-fast validation: Immediately reject invalid handlers
|
|
195
|
+
# 2. Duck typing: Any object with the required methods is accepted
|
|
196
|
+
# 3. Testability: Mock handlers can be injected without subclassing
|
|
197
|
+
required_methods = ["initialize", "shutdown", "execute"]
|
|
198
|
+
missing = [
|
|
199
|
+
m for m in required_methods if not callable(getattr(handler, m, None))
|
|
200
|
+
]
|
|
201
|
+
if missing:
|
|
202
|
+
context = ModelInfraErrorContext.with_correlation(
|
|
203
|
+
transport_type=EnumInfraTransportType.GRAPH,
|
|
204
|
+
operation="register_handler",
|
|
205
|
+
target_name="intent_handler",
|
|
206
|
+
)
|
|
207
|
+
raise ProtocolConfigurationError(
|
|
208
|
+
f"Handler missing required protocol methods: {missing}. "
|
|
209
|
+
f"Got {type(handler).__name__}",
|
|
210
|
+
context=context,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Store handler in module-level storage
|
|
214
|
+
handler_key = (
|
|
215
|
+
f"{RegistryInfraIntentStorage.HANDLER_KEY}."
|
|
216
|
+
f"{RegistryInfraIntentStorage.DEFAULT_HANDLER_TYPE}"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Warn if re-registering over an existing handler
|
|
220
|
+
if RegistryInfraIntentStorage._is_registered(handler_key):
|
|
221
|
+
logger.warning(
|
|
222
|
+
"Re-registering handler '%s'. This may indicate container lifecycle "
|
|
223
|
+
"issues or missing clear() calls in tests.",
|
|
224
|
+
handler_key,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
_HANDLER_STORAGE[handler_key] = handler
|
|
228
|
+
|
|
229
|
+
# Also register as default
|
|
230
|
+
default_key = RegistryInfraIntentStorage.HANDLER_KEY + ".default"
|
|
231
|
+
if RegistryInfraIntentStorage._is_registered(default_key):
|
|
232
|
+
logger.warning(
|
|
233
|
+
"Re-registering handler '%s'. This may indicate container lifecycle "
|
|
234
|
+
"issues or missing clear() calls in tests.",
|
|
235
|
+
default_key,
|
|
236
|
+
)
|
|
237
|
+
_HANDLER_STORAGE[default_key] = handler
|
|
238
|
+
|
|
239
|
+
@staticmethod
|
|
240
|
+
def get_handler(
|
|
241
|
+
_container: ModelONEXContainer,
|
|
242
|
+
handler_type: str | None = None,
|
|
243
|
+
) -> HandlerIntent | None:
|
|
244
|
+
"""Retrieve a registered intent handler from the container.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
_container: ONEX dependency injection container. Currently unused
|
|
248
|
+
because ServiceRegistry v1.0 doesn't support dict-style access.
|
|
249
|
+
The parameter is retained for API consistency.
|
|
250
|
+
handler_type: Specific handler type to retrieve. If None, returns default.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
The registered HandlerIntent, or None if not found.
|
|
254
|
+
|
|
255
|
+
Example:
|
|
256
|
+
>>> handler = RegistryInfraIntentStorage.get_handler(container)
|
|
257
|
+
>>> if handler:
|
|
258
|
+
... result = await handler.execute(envelope)
|
|
259
|
+
"""
|
|
260
|
+
if handler_type is not None:
|
|
261
|
+
handler_key = f"{RegistryInfraIntentStorage.HANDLER_KEY}.{handler_type}"
|
|
262
|
+
else:
|
|
263
|
+
handler_key = RegistryInfraIntentStorage.HANDLER_KEY + ".default"
|
|
264
|
+
|
|
265
|
+
result = _HANDLER_STORAGE.get(handler_key)
|
|
266
|
+
return cast("HandlerIntent | None", result)
|
|
267
|
+
|
|
268
|
+
@staticmethod
|
|
269
|
+
def clear() -> None:
|
|
270
|
+
"""Clear all registered handlers and protocol metadata.
|
|
271
|
+
|
|
272
|
+
Resets all module-level state to empty dicts. This method is essential
|
|
273
|
+
for test isolation.
|
|
274
|
+
|
|
275
|
+
Warning:
|
|
276
|
+
This method MUST be called in test setup and teardown to prevent
|
|
277
|
+
test pollution. Module-level state persists across test cases within
|
|
278
|
+
the same Python process. Failing to call this method can cause:
|
|
279
|
+
|
|
280
|
+
- Handlers from previous tests affecting subsequent tests
|
|
281
|
+
- Tests passing individually but failing when run together
|
|
282
|
+
- Non-deterministic test failures that are difficult to reproduce
|
|
283
|
+
|
|
284
|
+
Example:
|
|
285
|
+
.. code-block:: python
|
|
286
|
+
|
|
287
|
+
@pytest.fixture(autouse=True)
|
|
288
|
+
def clear_registry():
|
|
289
|
+
RegistryInfraIntentStorage.clear()
|
|
290
|
+
yield
|
|
291
|
+
RegistryInfraIntentStorage.clear()
|
|
292
|
+
"""
|
|
293
|
+
_HANDLER_STORAGE.clear()
|
|
294
|
+
_PROTOCOL_METADATA.clear()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: omnibase_infra
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: ONEX Infrastructure - Service integration and database infrastructure tools
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -28,8 +28,8 @@ Requires-Dist: jinja2 (>=3.1.6,<4.0.0)
|
|
|
28
28
|
Requires-Dist: jsonschema (>=4.20.0,<5.0.0)
|
|
29
29
|
Requires-Dist: mcp (>=1.25.0,<2.0.0)
|
|
30
30
|
Requires-Dist: neo4j (>=5.15.0,<6.0.0)
|
|
31
|
-
Requires-Dist: omnibase-core (>=0.9.
|
|
32
|
-
Requires-Dist: omnibase-spi (>=0.6.
|
|
31
|
+
Requires-Dist: omnibase-core (>=0.9.6,<0.10.0)
|
|
32
|
+
Requires-Dist: omnibase-spi (>=0.6.1,<0.7.0)
|
|
33
33
|
Requires-Dist: opentelemetry-api (>=1.27.0,<2.0.0)
|
|
34
34
|
Requires-Dist: opentelemetry-exporter-otlp (>=1.27.0,<2.0.0)
|
|
35
35
|
Requires-Dist: opentelemetry-instrumentation (>=0.48b0,<0.49)
|
|
@@ -390,6 +390,14 @@ omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py,sha256=cGJmDLS
|
|
|
390
390
|
omnibase_infra/nodes/effects/protocol_postgres_adapter.py,sha256=foLR6ZRv1EqfrZVxo9DTM5uunhkdCncnAr-6gqSEw8M,3198
|
|
391
391
|
omnibase_infra/nodes/effects/registry_effect.py,sha256=dSoqw_9hF7GiYQ6a9KtF7bgEXCW2FqH5b3Ud3b5CQaM,20975
|
|
392
392
|
omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py,sha256=6WXK7ZcWqPdpGLVpXbFzu-uIuc6vxCAgEpWTNHOy2Is,15566
|
|
393
|
+
omnibase_infra/nodes/node_intent_storage_effect/__init__.py,sha256=CVxx3WtXt_BcSgEG5496N5n7ZCK8FZFmx4jt9sPwUjE,1864
|
|
394
|
+
omnibase_infra/nodes/node_intent_storage_effect/contract.yaml,sha256=GSECd-8WAxytIwUQQTnP7167S0bD-1roAGaI08e_K9Q,6642
|
|
395
|
+
omnibase_infra/nodes/node_intent_storage_effect/models/__init__.py,sha256=XZ6L3PLHjNvnxRmJnDtVVWCsmZVfKdxnrenQWdJ1_D8,778
|
|
396
|
+
omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_input.py,sha256=hnAb6IQJVmJYubdg_MnB2afBk9df0bLV0gv1vnSyFug,4889
|
|
397
|
+
omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_output.py,sha256=kRufInAdTi58pEtLM4sYWKrwvD7g-EGFKZxb6pTSRzo,4479
|
|
398
|
+
omnibase_infra/nodes/node_intent_storage_effect/node.py,sha256=zfThTRPijnyrGyQLaHETC_yxdwn56IwnnS1cyH6QZD0,3475
|
|
399
|
+
omnibase_infra/nodes/node_intent_storage_effect/registry/__init__.py,sha256=9sLDlHv4ySokV3Jhw6KTSgpWOfI3Lt1cg-jcO9-5Zjg,1443
|
|
400
|
+
omnibase_infra/nodes/node_intent_storage_effect/registry/registry_infra_intent_storage.py,sha256=C095vuMER24eNHPzTxFpVR9c_DxepFO0inUGfrUOvOw,11499
|
|
393
401
|
omnibase_infra/nodes/node_registration_orchestrator/README.md,sha256=DgmzwS7EkNi1ein4O1fG5FzIE_85fHCuN39J3N_3Wbk,18217
|
|
394
402
|
omnibase_infra/nodes/node_registration_orchestrator/__init__.py,sha256=TEmJpSvnjYR4rO0xm-V7roVSZwEQDf6X_NIryXPiHLc,4545
|
|
395
403
|
omnibase_infra/nodes/node_registration_orchestrator/contract.yaml,sha256=4amAy5T7LZgLx2-hODJ0UvKJpGNLttnGuAXWgO4pycs,26212
|
|
@@ -731,8 +739,8 @@ omnibase_infra/validation/validator_routing_coverage.py,sha256=51vYEi5NhU6wqohk2
|
|
|
731
739
|
omnibase_infra/validation/validator_runtime_shape.py,sha256=9Xl37_dNDinT7nr-BoJ9fG3O5cT1_j6N-C7bojwQi3g,39033
|
|
732
740
|
omnibase_infra/validation/validator_security.py,sha256=Yn_kwxBtPzEj1GzrW0OafL-bR-FgoqdeFzseQqF2_B8,20622
|
|
733
741
|
omnibase_infra/validation/validator_topic_category.py,sha256=1y6STpSNIJsE2AmX5f8PVzPuOfoQmedgocSbEqOdtrM,48798
|
|
734
|
-
omnibase_infra-0.2.
|
|
735
|
-
omnibase_infra-0.2.
|
|
736
|
-
omnibase_infra-0.2.
|
|
737
|
-
omnibase_infra-0.2.
|
|
738
|
-
omnibase_infra-0.2.
|
|
742
|
+
omnibase_infra-0.2.5.dist-info/METADATA,sha256=jfps4nOkHND0EapFOjf-osUGKwledT6kvy6DSfM8R_4,7553
|
|
743
|
+
omnibase_infra-0.2.5.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
744
|
+
omnibase_infra-0.2.5.dist-info/entry_points.txt,sha256=NlZEQIMp4I5S2f0l286hLyA48oQB92S0ISrn1rfFP30,110
|
|
745
|
+
omnibase_infra-0.2.5.dist-info/licenses/LICENSE,sha256=CwYWppeN0neFHYdbbUdHbR4_PiyTYnmk6ufFWuVsL7I,1070
|
|
746
|
+
omnibase_infra-0.2.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|