omnibase_infra 0.2.3__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/handlers/handler_graph.py +860 -4
- 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/validation/__init__.py +16 -0
- {omnibase_infra-0.2.3.dist-info → omnibase_infra-0.2.5.dist-info}/METADATA +3 -3
- {omnibase_infra-0.2.3.dist-info → omnibase_infra-0.2.5.dist-info}/RECORD +15 -7
- {omnibase_infra-0.2.3.dist-info → omnibase_infra-0.2.5.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.2.3.dist-info → omnibase_infra-0.2.5.dist-info}/entry_points.txt +0 -0
- {omnibase_infra-0.2.3.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"]
|