omnibase_infra 0.2.2__py3-none-any.whl → 0.2.4__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 +6 -1
- omnibase_infra/capabilities/__init__.py +15 -0
- omnibase_infra/capabilities/capability_inference_rules.py +211 -0
- omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
- omnibase_infra/capabilities/intent_type_extractor.py +160 -0
- omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +1 -1
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +1 -1
- omnibase_infra/enums/__init__.py +6 -0
- omnibase_infra/enums/enum_handler_error_type.py +10 -0
- omnibase_infra/enums/enum_handler_source_mode.py +72 -0
- omnibase_infra/enums/enum_kafka_acks.py +99 -0
- omnibase_infra/event_bus/event_bus_kafka.py +1 -1
- omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +59 -10
- omnibase_infra/handlers/__init__.py +8 -1
- omnibase_infra/handlers/handler_consul.py +7 -1
- omnibase_infra/handlers/handler_db.py +8 -2
- omnibase_infra/handlers/handler_graph.py +860 -4
- omnibase_infra/handlers/handler_http.py +8 -2
- omnibase_infra/handlers/handler_intent.py +387 -0
- omnibase_infra/handlers/handler_mcp.py +10 -1
- omnibase_infra/handlers/handler_vault.py +11 -5
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +7 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +7 -0
- omnibase_infra/mixins/mixin_node_introspection.py +18 -0
- omnibase_infra/models/discovery/model_introspection_config.py +11 -0
- omnibase_infra/models/handlers/__init__.py +38 -5
- omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +4 -4
- omnibase_infra/models/handlers/model_contract_discovery_result.py +6 -4
- omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
- omnibase_infra/models/registration/model_node_introspection_event.py +9 -0
- omnibase_infra/models/runtime/model_handler_contract.py +25 -9
- omnibase_infra/models/runtime/model_loaded_handler.py +9 -0
- omnibase_infra/nodes/node_registration_orchestrator/plugin.py +1 -1
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +7 -7
- omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +4 -3
- omnibase_infra/nodes/node_registration_storage_effect/node.py +4 -1
- omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +1 -1
- omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +4 -1
- omnibase_infra/protocols/__init__.py +2 -0
- omnibase_infra/protocols/protocol_container_aware.py +200 -0
- omnibase_infra/runtime/__init__.py +39 -0
- omnibase_infra/runtime/handler_bootstrap_source.py +26 -33
- omnibase_infra/runtime/handler_contract_config_loader.py +1 -1
- omnibase_infra/runtime/handler_contract_source.py +10 -51
- omnibase_infra/runtime/handler_identity.py +81 -0
- omnibase_infra/runtime/handler_plugin_loader.py +15 -0
- omnibase_infra/runtime/handler_registry.py +11 -3
- omnibase_infra/runtime/handler_source_resolver.py +326 -0
- omnibase_infra/runtime/protocol_lifecycle_executor.py +6 -6
- omnibase_infra/runtime/registry/registry_protocol_binding.py +13 -13
- omnibase_infra/runtime/registry_contract_source.py +693 -0
- omnibase_infra/runtime/service_kernel.py +1 -1
- omnibase_infra/runtime/service_runtime_host_process.py +463 -190
- omnibase_infra/runtime/util_wiring.py +12 -3
- omnibase_infra/services/__init__.py +21 -0
- omnibase_infra/services/corpus_capture.py +7 -1
- omnibase_infra/services/mcp/mcp_server_lifecycle.py +9 -3
- omnibase_infra/services/registry_api/main.py +31 -13
- omnibase_infra/services/registry_api/service.py +10 -19
- omnibase_infra/services/service_timeout_emitter.py +7 -1
- omnibase_infra/services/service_timeout_scanner.py +7 -3
- omnibase_infra/services/session/__init__.py +56 -0
- omnibase_infra/services/session/config_consumer.py +120 -0
- omnibase_infra/services/session/config_store.py +139 -0
- omnibase_infra/services/session/consumer.py +1007 -0
- omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
- omnibase_infra/services/session/store.py +997 -0
- omnibase_infra/utils/__init__.py +19 -0
- omnibase_infra/utils/util_atomic_file.py +261 -0
- omnibase_infra/utils/util_db_transaction.py +239 -0
- omnibase_infra/utils/util_retry_optimistic.py +281 -0
- omnibase_infra/validation/__init__.py +16 -0
- omnibase_infra/validation/validation_exemptions.yaml +27 -0
- {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.4.dist-info}/METADATA +3 -3
- {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.4.dist-info}/RECORD +79 -58
- {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.4.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.4.dist-info}/entry_points.txt +0 -0
- {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -121,14 +121,18 @@ from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationE
|
|
|
121
121
|
from omnibase_infra.event_bus.event_bus_inmemory import EventBusInmemory
|
|
122
122
|
from omnibase_infra.handlers.handler_consul import HandlerConsul
|
|
123
123
|
from omnibase_infra.handlers.handler_db import HandlerDb
|
|
124
|
+
from omnibase_infra.handlers.handler_graph import HandlerGraph
|
|
124
125
|
from omnibase_infra.handlers.handler_http import HandlerHttpRest
|
|
126
|
+
from omnibase_infra.handlers.handler_intent import HandlerIntent
|
|
125
127
|
from omnibase_infra.handlers.handler_mcp import HandlerMCP
|
|
126
128
|
from omnibase_infra.handlers.handler_vault import HandlerVault
|
|
127
129
|
from omnibase_infra.runtime.handler_registry import (
|
|
128
130
|
EVENT_BUS_INMEMORY,
|
|
129
131
|
HANDLER_TYPE_CONSUL,
|
|
130
132
|
HANDLER_TYPE_DATABASE,
|
|
133
|
+
HANDLER_TYPE_GRAPH,
|
|
131
134
|
HANDLER_TYPE_HTTP,
|
|
135
|
+
HANDLER_TYPE_INTENT,
|
|
132
136
|
HANDLER_TYPE_MCP,
|
|
133
137
|
HANDLER_TYPE_VAULT,
|
|
134
138
|
RegistryEventBusBinding,
|
|
@@ -139,7 +143,7 @@ from omnibase_infra.runtime.handler_registry import (
|
|
|
139
143
|
|
|
140
144
|
if TYPE_CHECKING:
|
|
141
145
|
from omnibase_core.protocol.protocol_event_bus import ProtocolEventBus
|
|
142
|
-
from
|
|
146
|
+
from omnibase_infra.protocols import ProtocolContainerAware
|
|
143
147
|
|
|
144
148
|
logger = logging.getLogger(__name__)
|
|
145
149
|
|
|
@@ -166,11 +170,14 @@ logger = logging.getLogger(__name__)
|
|
|
166
170
|
# NOTE: HandlerHttpRest and HandlerDb use legacy execute(envelope: dict) signature.
|
|
167
171
|
# They will be migrated to ProtocolHandler.execute(request, operation_config) in future.
|
|
168
172
|
# Type ignore comments suppress MyPy errors during MVP phase.
|
|
169
|
-
_KNOWN_HANDLERS: dict[str, tuple[type[
|
|
173
|
+
_KNOWN_HANDLERS: dict[str, tuple[type[ProtocolContainerAware], str]] = {
|
|
170
174
|
# NOTE: Handlers implement ProtocolHandler structurally but concrete types differ from protocol.
|
|
171
175
|
HANDLER_TYPE_CONSUL: (HandlerConsul, "HashiCorp Consul service discovery handler"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
172
176
|
HANDLER_TYPE_DATABASE: (HandlerDb, "PostgreSQL database handler"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
177
|
+
HANDLER_TYPE_GRAPH: (HandlerGraph, "Graph database (Memgraph/Neo4j) handler"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
173
178
|
HANDLER_TYPE_HTTP: (HandlerHttpRest, "HTTP REST protocol handler"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
179
|
+
# DEMO: Temporary registration - remove when contract-driven (OMN-1515)
|
|
180
|
+
HANDLER_TYPE_INTENT: (HandlerIntent, "Intent storage and query handler for demo"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
174
181
|
HANDLER_TYPE_MCP: (HandlerMCP, "Model Context Protocol handler for AI agents"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
175
182
|
HANDLER_TYPE_VAULT: (HandlerVault, "HashiCorp Vault secret management handler"), # type: ignore[dict-item] # NOTE: structural subtyping
|
|
176
183
|
}
|
|
@@ -197,7 +204,9 @@ def wire_default_handlers() -> dict[str, list[str]]:
|
|
|
197
204
|
Registered Handlers:
|
|
198
205
|
- CONSUL: HandlerConsul for HashiCorp Consul service discovery
|
|
199
206
|
- DB: HandlerDb for PostgreSQL database operations
|
|
207
|
+
- GRAPH: HandlerGraph for graph database (Memgraph/Neo4j) operations
|
|
200
208
|
- HTTP: HandlerHttpRest for HTTP/REST protocol operations
|
|
209
|
+
- INTENT: HandlerIntent for intent storage and query (demo)
|
|
201
210
|
- MCP: HandlerMCP for Model Context Protocol AI agent integration
|
|
202
211
|
- VAULT: HandlerVault for HashiCorp Vault secret management
|
|
203
212
|
|
|
@@ -487,7 +496,7 @@ def get_known_event_bus_kinds() -> list[str]:
|
|
|
487
496
|
|
|
488
497
|
def wire_custom_handler(
|
|
489
498
|
handler_type: str,
|
|
490
|
-
handler_cls: type[
|
|
499
|
+
handler_cls: type[ProtocolContainerAware],
|
|
491
500
|
registry: RegistryProtocolBinding | None = None,
|
|
492
501
|
) -> None:
|
|
493
502
|
"""Register a custom handler class with the registry.
|
|
@@ -39,6 +39,18 @@ from omnibase_infra.services.service_timeout_scanner import (
|
|
|
39
39
|
ModelTimeoutQueryResult,
|
|
40
40
|
ServiceTimeoutScanner,
|
|
41
41
|
)
|
|
42
|
+
|
|
43
|
+
# Session services (moved from omniclaude in OMN-1526)
|
|
44
|
+
from omnibase_infra.services.session import (
|
|
45
|
+
ConfigSessionConsumer,
|
|
46
|
+
ConfigSessionStorage,
|
|
47
|
+
ConsumerMetrics,
|
|
48
|
+
EnumCircuitState,
|
|
49
|
+
ProtocolSessionAggregator,
|
|
50
|
+
SessionEventConsumer,
|
|
51
|
+
SessionSnapshotStore,
|
|
52
|
+
SessionStoreNotInitializedError,
|
|
53
|
+
)
|
|
42
54
|
from omnibase_infra.services.snapshot import (
|
|
43
55
|
ServiceSnapshot,
|
|
44
56
|
StoreSnapshotInMemory,
|
|
@@ -65,4 +77,13 @@ __all__ = [
|
|
|
65
77
|
"StoreSnapshotPostgres",
|
|
66
78
|
"TimeoutEmitter",
|
|
67
79
|
"TimeoutScanner",
|
|
80
|
+
# Session services (OMN-1526)
|
|
81
|
+
"ConfigSessionConsumer",
|
|
82
|
+
"ConfigSessionStorage",
|
|
83
|
+
"ConsumerMetrics",
|
|
84
|
+
"EnumCircuitState",
|
|
85
|
+
"ProtocolSessionAggregator",
|
|
86
|
+
"SessionEventConsumer",
|
|
87
|
+
"SessionSnapshotStore",
|
|
88
|
+
"SessionStoreNotInitializedError",
|
|
68
89
|
]
|
|
@@ -22,6 +22,7 @@ from datetime import UTC, datetime
|
|
|
22
22
|
from typing import Protocol
|
|
23
23
|
from uuid import UUID
|
|
24
24
|
|
|
25
|
+
from omnibase_core.container import ModelONEXContainer
|
|
25
26
|
from omnibase_core.enums import EnumCoreErrorCode
|
|
26
27
|
from omnibase_core.errors import OnexError
|
|
27
28
|
from omnibase_core.models.manifest.model_execution_manifest import (
|
|
@@ -226,13 +227,15 @@ class CorpusCapture:
|
|
|
226
227
|
- Max executions enforcement with automatic state transitions
|
|
227
228
|
|
|
228
229
|
Example:
|
|
230
|
+
>>> from omnibase_core.container import ModelONEXContainer
|
|
231
|
+
>>> container = ModelONEXContainer(...) # Configure as needed
|
|
229
232
|
>>> config = ModelCaptureConfig(
|
|
230
233
|
... corpus_display_name="regression-suite-v1",
|
|
231
234
|
... max_executions=50,
|
|
232
235
|
... sample_rate=0.5,
|
|
233
236
|
... handler_filter=("compute-handler",),
|
|
234
237
|
... )
|
|
235
|
-
>>> service = CorpusCapture()
|
|
238
|
+
>>> service = CorpusCapture(container)
|
|
236
239
|
>>> service.create_corpus(config)
|
|
237
240
|
>>> service.start_capture()
|
|
238
241
|
>>>
|
|
@@ -249,17 +252,20 @@ class CorpusCapture:
|
|
|
249
252
|
|
|
250
253
|
def __init__(
|
|
251
254
|
self,
|
|
255
|
+
container: ModelONEXContainer,
|
|
252
256
|
persistence: ProtocolManifestPersistence | None = None,
|
|
253
257
|
) -> None:
|
|
254
258
|
"""
|
|
255
259
|
Initialize the corpus capture service.
|
|
256
260
|
|
|
257
261
|
Args:
|
|
262
|
+
container: ONEX container for dependency injection.
|
|
258
263
|
persistence: Optional persistence handler for flushing manifests.
|
|
259
264
|
If provided, manifests can be persisted via flush_to_persistence()
|
|
260
265
|
or by calling close_corpus_async(flush=True). The synchronous
|
|
261
266
|
close_corpus() does NOT automatically flush.
|
|
262
267
|
"""
|
|
268
|
+
self._container = container
|
|
263
269
|
self._state_machine = CaptureLifecycleFSM()
|
|
264
270
|
self._config: ModelCaptureConfig | None = None
|
|
265
271
|
self._corpus: ModelExecutionCorpus | None = None
|
|
@@ -15,7 +15,7 @@ Architecture:
|
|
|
15
15
|
|
|
16
16
|
Usage:
|
|
17
17
|
```python
|
|
18
|
-
lifecycle = MCPServerLifecycle(config)
|
|
18
|
+
lifecycle = MCPServerLifecycle(container=container, config=config)
|
|
19
19
|
await lifecycle.start()
|
|
20
20
|
# ... server is running ...
|
|
21
21
|
await lifecycle.shutdown()
|
|
@@ -28,6 +28,7 @@ import logging
|
|
|
28
28
|
from typing import TYPE_CHECKING
|
|
29
29
|
from uuid import UUID, uuid4
|
|
30
30
|
|
|
31
|
+
from omnibase_core.container import ModelONEXContainer
|
|
31
32
|
from omnibase_infra.adapters.adapter_onex_tool_execution import (
|
|
32
33
|
AdapterONEXToolExecution,
|
|
33
34
|
)
|
|
@@ -68,6 +69,7 @@ class MCPServerLifecycle:
|
|
|
68
69
|
3. shutdown(): Clean up all resources
|
|
69
70
|
|
|
70
71
|
Attributes:
|
|
72
|
+
_container: ONEX container for dependency injection.
|
|
71
73
|
_config: Server configuration.
|
|
72
74
|
_registry: Tool registry instance.
|
|
73
75
|
_discovery: Consul discovery service.
|
|
@@ -80,25 +82,28 @@ class MCPServerLifecycle:
|
|
|
80
82
|
... consul_host="consul.local",
|
|
81
83
|
... http_port=8090,
|
|
82
84
|
... )
|
|
83
|
-
>>> lifecycle = MCPServerLifecycle(config)
|
|
85
|
+
>>> lifecycle = MCPServerLifecycle(container=container, config=config)
|
|
84
86
|
>>> await lifecycle.start()
|
|
85
|
-
>>> handler = lifecycle.get_handler(
|
|
87
|
+
>>> handler = lifecycle.get_handler()
|
|
86
88
|
>>> # Use handler with uvicorn/transport
|
|
87
89
|
>>> await lifecycle.shutdown()
|
|
88
90
|
"""
|
|
89
91
|
|
|
90
92
|
def __init__(
|
|
91
93
|
self,
|
|
94
|
+
container: ModelONEXContainer,
|
|
92
95
|
config: ModelMCPServerConfig,
|
|
93
96
|
bus: EventBusKafka | None = None,
|
|
94
97
|
) -> None:
|
|
95
98
|
"""Initialize the lifecycle manager.
|
|
96
99
|
|
|
97
100
|
Args:
|
|
101
|
+
container: ONEX container for dependency injection.
|
|
98
102
|
config: Server configuration.
|
|
99
103
|
bus: Optional Kafka event bus for hot reload. If not provided,
|
|
100
104
|
Kafka subscription is skipped even if kafka_enabled=True.
|
|
101
105
|
"""
|
|
106
|
+
self._container = container
|
|
102
107
|
self._config = config
|
|
103
108
|
self._bus = bus
|
|
104
109
|
|
|
@@ -162,6 +167,7 @@ class MCPServerLifecycle:
|
|
|
162
167
|
# Create services
|
|
163
168
|
self._registry = ServiceMCPToolRegistry()
|
|
164
169
|
self._executor = AdapterONEXToolExecution(
|
|
170
|
+
container=self._container,
|
|
165
171
|
default_timeout=self._config.default_timeout,
|
|
166
172
|
)
|
|
167
173
|
|
|
@@ -7,13 +7,18 @@ Provides factory function for flexible instantiation with different
|
|
|
7
7
|
backend configurations.
|
|
8
8
|
|
|
9
9
|
Usage:
|
|
10
|
-
# Create app with
|
|
11
|
-
|
|
10
|
+
# Create app with container (required)
|
|
11
|
+
from omnibase_core.container import ModelONEXContainer
|
|
12
|
+
|
|
13
|
+
container = ModelONEXContainer()
|
|
14
|
+
app = create_app(container=container, cors_origins=["http://localhost:3000"])
|
|
12
15
|
|
|
13
16
|
# Create app with full backends
|
|
14
17
|
app = create_app(
|
|
18
|
+
container=container,
|
|
15
19
|
projection_reader=reader,
|
|
16
20
|
consul_handler=handler,
|
|
21
|
+
cors_origins=["http://localhost:3000"],
|
|
17
22
|
)
|
|
18
23
|
|
|
19
24
|
# Run with uvicorn
|
|
@@ -35,6 +40,7 @@ from typing import TYPE_CHECKING
|
|
|
35
40
|
from fastapi import FastAPI
|
|
36
41
|
from fastapi.middleware.cors import CORSMiddleware
|
|
37
42
|
|
|
43
|
+
from omnibase_core.container import ModelONEXContainer
|
|
38
44
|
from omnibase_infra.enums import EnumInfraTransportType
|
|
39
45
|
from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationError
|
|
40
46
|
from omnibase_infra.services.registry_api.routes import router
|
|
@@ -117,6 +123,7 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
|
|
117
123
|
|
|
118
124
|
|
|
119
125
|
def create_app(
|
|
126
|
+
container: ModelONEXContainer,
|
|
120
127
|
projection_reader: ProjectionReaderRegistration | None = None,
|
|
121
128
|
consul_handler: HandlerServiceDiscoveryConsul | None = None,
|
|
122
129
|
widget_mapping_path: Path | None = None,
|
|
@@ -129,6 +136,8 @@ def create_app(
|
|
|
129
136
|
return partial data with warnings when backends are unavailable.
|
|
130
137
|
|
|
131
138
|
Args:
|
|
139
|
+
container: ONEX container for dependency injection. Required for
|
|
140
|
+
ONEX DI pattern compliance.
|
|
132
141
|
projection_reader: Optional projection reader for node registrations.
|
|
133
142
|
consul_handler: Optional Consul handler for live instances.
|
|
134
143
|
widget_mapping_path: Optional path to widget mapping YAML.
|
|
@@ -145,7 +154,9 @@ def create_app(
|
|
|
145
154
|
|
|
146
155
|
Example:
|
|
147
156
|
>>> from omnibase_infra.services.registry_api import create_app
|
|
148
|
-
>>>
|
|
157
|
+
>>> from omnibase_core.container import ModelONEXContainer
|
|
158
|
+
>>> container = ModelONEXContainer()
|
|
159
|
+
>>> app = create_app(container=container)
|
|
149
160
|
>>> # Run with: uvicorn module:app --host 0.0.0.0 --port 8000
|
|
150
161
|
"""
|
|
151
162
|
app = FastAPI(
|
|
@@ -195,6 +206,7 @@ def create_app(
|
|
|
195
206
|
|
|
196
207
|
# Create and attach service
|
|
197
208
|
service = ServiceRegistryDiscovery(
|
|
209
|
+
container=container,
|
|
198
210
|
projection_reader=projection_reader,
|
|
199
211
|
consul_handler=consul_handler,
|
|
200
212
|
widget_mapping_path=widget_mapping_path,
|
|
@@ -226,18 +238,24 @@ def create_app(
|
|
|
226
238
|
return app
|
|
227
239
|
|
|
228
240
|
|
|
229
|
-
#
|
|
230
|
-
#
|
|
231
|
-
#
|
|
232
|
-
#
|
|
233
|
-
#
|
|
241
|
+
# Module-level app instance is not supported since container is required.
|
|
242
|
+
# For production usage, use create_app() with proper configuration:
|
|
243
|
+
#
|
|
244
|
+
# Example:
|
|
245
|
+
# from omnibase_core.container import ModelONEXContainer
|
|
246
|
+
# from omnibase_infra.services.registry_api import create_app
|
|
247
|
+
#
|
|
248
|
+
# container = ModelONEXContainer()
|
|
249
|
+
# app = create_app(
|
|
250
|
+
# container=container,
|
|
251
|
+
# projection_reader=reader,
|
|
252
|
+
# consul_handler=handler,
|
|
253
|
+
# cors_origins=["http://localhost:3000"],
|
|
254
|
+
# )
|
|
255
|
+
# uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
234
256
|
#
|
|
235
|
-
#
|
|
236
|
-
# If CORS_ORIGINS is not set, the module can still be imported but `app` will be None.
|
|
237
|
-
# Use create_app(cors_origins=["..."]) directly for programmatic usage.
|
|
257
|
+
# For uvicorn CLI usage, create a launcher module that instantiates the container.
|
|
238
258
|
app: FastAPI | None = None
|
|
239
|
-
if os.environ.get("CORS_ORIGINS") is not None:
|
|
240
|
-
app = create_app()
|
|
241
259
|
|
|
242
260
|
|
|
243
261
|
__all__ = ["app", "create_app"]
|
|
@@ -77,11 +77,9 @@ class ServiceRegistryDiscovery:
|
|
|
77
77
|
showing complete errors.
|
|
78
78
|
|
|
79
79
|
Dependency Injection:
|
|
80
|
-
This service
|
|
80
|
+
This service requires a ModelONEXContainer for ONEX-style dependency
|
|
81
81
|
injection. Dependencies can also be provided directly via constructor
|
|
82
|
-
parameters for testing
|
|
83
|
-
|
|
84
|
-
Priority: Direct parameters > Container resolution > None (with warnings)
|
|
82
|
+
parameters for testing flexibility.
|
|
85
83
|
|
|
86
84
|
Thread Safety:
|
|
87
85
|
This service is coroutine-safe. All methods are async and
|
|
@@ -89,12 +87,13 @@ class ServiceRegistryDiscovery:
|
|
|
89
87
|
concurrency requirements.
|
|
90
88
|
|
|
91
89
|
Example:
|
|
92
|
-
>>> # Using container for DI
|
|
90
|
+
>>> # Using container for DI (container is required)
|
|
93
91
|
>>> service = ServiceRegistryDiscovery(container=container)
|
|
94
92
|
>>> response = await service.get_discovery()
|
|
95
93
|
>>>
|
|
96
|
-
>>> #
|
|
94
|
+
>>> # With explicit dependencies (for testing)
|
|
97
95
|
>>> service = ServiceRegistryDiscovery(
|
|
96
|
+
... container=container,
|
|
98
97
|
... projection_reader=reader,
|
|
99
98
|
... consul_handler=handler,
|
|
100
99
|
... )
|
|
@@ -110,7 +109,7 @@ class ServiceRegistryDiscovery:
|
|
|
110
109
|
|
|
111
110
|
def __init__(
|
|
112
111
|
self,
|
|
113
|
-
container: ModelONEXContainer
|
|
112
|
+
container: ModelONEXContainer,
|
|
114
113
|
projection_reader: ProjectionReaderRegistration | None = None,
|
|
115
114
|
consul_handler: HandlerServiceDiscoveryConsul | None = None,
|
|
116
115
|
widget_mapping_path: Path | None = None,
|
|
@@ -118,21 +117,14 @@ class ServiceRegistryDiscovery:
|
|
|
118
117
|
"""Initialize the registry discovery service.
|
|
119
118
|
|
|
120
119
|
Args:
|
|
121
|
-
container:
|
|
122
|
-
|
|
123
|
-
if not explicitly passed via other parameters.
|
|
120
|
+
container: ONEX container for dependency injection. Required for
|
|
121
|
+
ONEX DI pattern compliance.
|
|
124
122
|
projection_reader: Optional projection reader for node registrations.
|
|
125
|
-
If not provided, will
|
|
126
|
-
If still None, node queries will return empty results with warnings.
|
|
123
|
+
If not provided, node queries will return empty results with warnings.
|
|
127
124
|
consul_handler: Optional Consul handler for live instances.
|
|
128
|
-
If not provided, will
|
|
129
|
-
If still None, instance queries will return empty results with warnings.
|
|
125
|
+
If not provided, instance queries will return empty results with warnings.
|
|
130
126
|
widget_mapping_path: Path to widget mapping YAML file.
|
|
131
127
|
Defaults to configs/widget_mapping.yaml relative to package.
|
|
132
|
-
|
|
133
|
-
Note:
|
|
134
|
-
Direct dependency parameters take precedence over container resolution.
|
|
135
|
-
This allows easy mocking in tests while supporting full DI in production.
|
|
136
128
|
"""
|
|
137
129
|
self._container = container
|
|
138
130
|
|
|
@@ -157,7 +149,6 @@ class ServiceRegistryDiscovery:
|
|
|
157
149
|
logger.info(
|
|
158
150
|
"ServiceRegistryDiscovery initialized",
|
|
159
151
|
extra={
|
|
160
|
-
"has_container": container is not None,
|
|
161
152
|
"has_projection_reader": self._projection_reader is not None,
|
|
162
153
|
"has_consul_handler": self._consul_handler is not None,
|
|
163
154
|
"widget_mapping_path": str(self._widget_mapping_path),
|
|
@@ -36,6 +36,7 @@ from uuid import UUID
|
|
|
36
36
|
|
|
37
37
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
38
38
|
|
|
39
|
+
from omnibase_core.container import ModelONEXContainer
|
|
39
40
|
from omnibase_core.models.events.model_event_envelope import ModelEventEnvelope
|
|
40
41
|
from omnibase_infra.enums import EnumInfraTransportType
|
|
41
42
|
from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationError
|
|
@@ -217,6 +218,7 @@ class ServiceTimeoutEmitter:
|
|
|
217
218
|
|
|
218
219
|
Usage:
|
|
219
220
|
>>> emitter = ServiceTimeoutEmitter(
|
|
221
|
+
... container=container,
|
|
220
222
|
... timeout_query=timeout_scanner,
|
|
221
223
|
... event_bus=event_bus,
|
|
222
224
|
... projector=projector,
|
|
@@ -262,6 +264,7 @@ class ServiceTimeoutEmitter:
|
|
|
262
264
|
|
|
263
265
|
def __init__(
|
|
264
266
|
self,
|
|
267
|
+
container: ModelONEXContainer,
|
|
265
268
|
timeout_query: ServiceTimeoutScanner,
|
|
266
269
|
event_bus: ProtocolEventBus,
|
|
267
270
|
projector: ProjectorShell,
|
|
@@ -270,6 +273,7 @@ class ServiceTimeoutEmitter:
|
|
|
270
273
|
"""Initialize with required dependencies.
|
|
271
274
|
|
|
272
275
|
Args:
|
|
276
|
+
container: ONEX container for dependency injection.
|
|
273
277
|
timeout_query: Scanner for querying overdue entities.
|
|
274
278
|
Must be initialized with a ProjectionReaderRegistration.
|
|
275
279
|
event_bus: Event bus for publishing timeout events.
|
|
@@ -281,16 +285,18 @@ class ServiceTimeoutEmitter:
|
|
|
281
285
|
|
|
282
286
|
Example:
|
|
283
287
|
>>> reader = ProjectionReaderRegistration(pool)
|
|
284
|
-
>>> timeout_query = ServiceTimeoutScanner(reader)
|
|
288
|
+
>>> timeout_query = ServiceTimeoutScanner(container, reader)
|
|
285
289
|
>>> bus = EventBusKafka.default()
|
|
286
290
|
>>> projector = projector_loader.load("registration_projector")
|
|
287
291
|
>>> emitter = ServiceTimeoutEmitter(
|
|
292
|
+
... container=container,
|
|
288
293
|
... timeout_query=timeout_query,
|
|
289
294
|
... event_bus=bus,
|
|
290
295
|
... projector=projector,
|
|
291
296
|
... config=ModelTimeoutEmissionConfig(environment="dev"),
|
|
292
297
|
... )
|
|
293
298
|
"""
|
|
299
|
+
self._container = container
|
|
294
300
|
self._timeout_query = timeout_query
|
|
295
301
|
self._event_bus = event_bus
|
|
296
302
|
self._projector = projector
|
|
@@ -31,6 +31,7 @@ from uuid import UUID, uuid4
|
|
|
31
31
|
|
|
32
32
|
from pydantic import BaseModel, ConfigDict, Field
|
|
33
33
|
|
|
34
|
+
from omnibase_core.container import ModelONEXContainer
|
|
34
35
|
from omnibase_infra.models.projection import ModelRegistrationProjection
|
|
35
36
|
from omnibase_infra.projectors.projection_reader_registration import (
|
|
36
37
|
ProjectionReaderRegistration,
|
|
@@ -126,7 +127,7 @@ class ServiceTimeoutScanner:
|
|
|
126
127
|
|
|
127
128
|
Usage:
|
|
128
129
|
>>> reader = ProjectionReaderRegistration(pool)
|
|
129
|
-
>>> scanner = ServiceTimeoutScanner(reader)
|
|
130
|
+
>>> scanner = ServiceTimeoutScanner(container, reader)
|
|
130
131
|
>>> result = await scanner.find_overdue_entities(now=tick.now)
|
|
131
132
|
>>>
|
|
132
133
|
>>> for projection in result.ack_timeouts:
|
|
@@ -152,12 +153,14 @@ class ServiceTimeoutScanner:
|
|
|
152
153
|
|
|
153
154
|
def __init__(
|
|
154
155
|
self,
|
|
156
|
+
container: ModelONEXContainer,
|
|
155
157
|
projection_reader: ProjectionReaderRegistration,
|
|
156
158
|
batch_size: int | None = None,
|
|
157
159
|
) -> None:
|
|
158
|
-
"""Initialize
|
|
160
|
+
"""Initialize the timeout scanner service.
|
|
159
161
|
|
|
160
162
|
Args:
|
|
163
|
+
container: ONEX container for dependency injection.
|
|
161
164
|
projection_reader: The projection reader for database queries.
|
|
162
165
|
Must be initialized with an asyncpg connection pool.
|
|
163
166
|
batch_size: Maximum entities to return per query type.
|
|
@@ -166,8 +169,9 @@ class ServiceTimeoutScanner:
|
|
|
166
169
|
Example:
|
|
167
170
|
>>> pool = await asyncpg.create_pool(dsn)
|
|
168
171
|
>>> reader = ProjectionReaderRegistration(pool)
|
|
169
|
-
>>> scanner = ServiceTimeoutScanner(reader)
|
|
172
|
+
>>> scanner = ServiceTimeoutScanner(container, reader)
|
|
170
173
|
"""
|
|
174
|
+
self._container = container
|
|
171
175
|
self._reader = projection_reader
|
|
172
176
|
self._batch_size = batch_size or self.DEFAULT_BATCH_SIZE
|
|
173
177
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Session storage and event consumer services.
|
|
4
|
+
|
|
5
|
+
This module provides infrastructure for persisting Claude Code session
|
|
6
|
+
snapshots and consuming session events from Kafka.
|
|
7
|
+
|
|
8
|
+
Moved from omniclaude as part of OMN-1526 architectural cleanup.
|
|
9
|
+
|
|
10
|
+
Components:
|
|
11
|
+
- SessionSnapshotStore: PostgreSQL storage for session snapshots
|
|
12
|
+
- SessionEventConsumer: Kafka consumer for session events
|
|
13
|
+
- ConfigSessionStorage: Storage configuration
|
|
14
|
+
- ConfigSessionConsumer: Consumer configuration
|
|
15
|
+
- ConsumerMetrics: Metrics for consumer observability
|
|
16
|
+
- EnumCircuitState: Circuit breaker states
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
>>> from omnibase_infra.services.session import (
|
|
20
|
+
... SessionSnapshotStore,
|
|
21
|
+
... ConfigSessionStorage,
|
|
22
|
+
... )
|
|
23
|
+
>>> from pydantic import SecretStr
|
|
24
|
+
>>>
|
|
25
|
+
>>> config = ConfigSessionStorage(postgres_password=SecretStr("secret"))
|
|
26
|
+
>>> store = SessionSnapshotStore(config)
|
|
27
|
+
>>> await store.initialize()
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from omnibase_infra.services.session.config_consumer import ConfigSessionConsumer
|
|
31
|
+
from omnibase_infra.services.session.config_store import ConfigSessionStorage
|
|
32
|
+
from omnibase_infra.services.session.consumer import (
|
|
33
|
+
ConsumerMetrics,
|
|
34
|
+
EnumCircuitState,
|
|
35
|
+
SessionEventConsumer,
|
|
36
|
+
)
|
|
37
|
+
from omnibase_infra.services.session.protocol_session_aggregator import (
|
|
38
|
+
ProtocolSessionAggregator,
|
|
39
|
+
)
|
|
40
|
+
from omnibase_infra.services.session.store import (
|
|
41
|
+
SessionSnapshotStore,
|
|
42
|
+
SessionStoreNotInitializedError,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
# Storage
|
|
47
|
+
"SessionSnapshotStore",
|
|
48
|
+
"SessionStoreNotInitializedError",
|
|
49
|
+
"ConfigSessionStorage",
|
|
50
|
+
# Consumer
|
|
51
|
+
"SessionEventConsumer",
|
|
52
|
+
"ConfigSessionConsumer",
|
|
53
|
+
"ConsumerMetrics",
|
|
54
|
+
"EnumCircuitState",
|
|
55
|
+
"ProtocolSessionAggregator",
|
|
56
|
+
]
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Configuration for session event consumers.
|
|
2
|
+
|
|
3
|
+
Loads from environment variables with OMNIBASE_INFRA_SESSION_CONSUMER_ prefix.
|
|
4
|
+
|
|
5
|
+
Moved from omniclaude as part of OMN-1526 architectural cleanup.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
from pydantic import Field, model_validator
|
|
13
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ConfigSessionConsumer(BaseSettings):
|
|
19
|
+
"""Configuration for the Claude session event Kafka consumer.
|
|
20
|
+
|
|
21
|
+
Environment variables use the OMNIBASE_INFRA_SESSION_CONSUMER_ prefix.
|
|
22
|
+
Example: OMNIBASE_INFRA_SESSION_CONSUMER_BOOTSTRAP_SERVERS=kafka.example.com:9092
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
model_config = SettingsConfigDict(
|
|
26
|
+
env_prefix="OMNIBASE_INFRA_SESSION_CONSUMER_",
|
|
27
|
+
env_file=".env",
|
|
28
|
+
env_file_encoding="utf-8",
|
|
29
|
+
case_sensitive=False,
|
|
30
|
+
extra="ignore",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Kafka connection
|
|
34
|
+
bootstrap_servers: str = Field(
|
|
35
|
+
default="localhost:9092",
|
|
36
|
+
description="Kafka bootstrap servers. Set via OMNIBASE_INFRA_SESSION_CONSUMER_BOOTSTRAP_SERVERS env var for production.",
|
|
37
|
+
)
|
|
38
|
+
group_id: str = Field(
|
|
39
|
+
default="omnibase-infra-session-consumer",
|
|
40
|
+
description="Consumer group ID",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Topics to subscribe
|
|
44
|
+
topics: list[str] = Field(
|
|
45
|
+
default=[
|
|
46
|
+
"dev.omniclaude.session.started.v1",
|
|
47
|
+
"dev.omniclaude.session.ended.v1",
|
|
48
|
+
"dev.omniclaude.prompt.submitted.v1",
|
|
49
|
+
"dev.omniclaude.tool.executed.v1",
|
|
50
|
+
],
|
|
51
|
+
description="Kafka topics to consume",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Consumer behavior
|
|
55
|
+
auto_offset_reset: str = Field(
|
|
56
|
+
default="earliest",
|
|
57
|
+
description="Where to start consuming if no offset exists",
|
|
58
|
+
)
|
|
59
|
+
enable_auto_commit: bool = Field(
|
|
60
|
+
default=False,
|
|
61
|
+
description="Disable auto-commit for at-least-once delivery",
|
|
62
|
+
)
|
|
63
|
+
max_poll_records: int = Field(
|
|
64
|
+
default=100,
|
|
65
|
+
ge=1,
|
|
66
|
+
le=10000,
|
|
67
|
+
description="Maximum records per poll",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Processing
|
|
71
|
+
batch_timeout_ms: int = Field(
|
|
72
|
+
default=5000,
|
|
73
|
+
ge=100,
|
|
74
|
+
le=60000,
|
|
75
|
+
description="Timeout for batch processing in milliseconds",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Circuit breaker
|
|
79
|
+
circuit_breaker_threshold: int = Field(
|
|
80
|
+
default=5,
|
|
81
|
+
ge=1,
|
|
82
|
+
le=100,
|
|
83
|
+
description="Failures before circuit opens",
|
|
84
|
+
)
|
|
85
|
+
circuit_breaker_timeout_seconds: int = Field(
|
|
86
|
+
default=60,
|
|
87
|
+
ge=1,
|
|
88
|
+
le=3600,
|
|
89
|
+
description="Time before circuit half-opens",
|
|
90
|
+
)
|
|
91
|
+
circuit_breaker_half_open_successes: int = Field(
|
|
92
|
+
default=1,
|
|
93
|
+
ge=1,
|
|
94
|
+
le=10,
|
|
95
|
+
description="Number of successful requests required to close circuit from half-open state",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
@model_validator(mode="after")
|
|
99
|
+
def validate_timing_relationships(self) -> ConfigSessionConsumer:
|
|
100
|
+
"""Validate timing relationships between configuration values.
|
|
101
|
+
|
|
102
|
+
Warns if circuit breaker timeout is very short relative to batch processing,
|
|
103
|
+
which could cause premature circuit opens during normal batch operations.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Self if validation passes.
|
|
107
|
+
"""
|
|
108
|
+
batch_timeout_seconds = self.batch_timeout_ms / 1000
|
|
109
|
+
min_recommended_circuit_timeout = batch_timeout_seconds * 2
|
|
110
|
+
|
|
111
|
+
if self.circuit_breaker_timeout_seconds < min_recommended_circuit_timeout:
|
|
112
|
+
logger.warning(
|
|
113
|
+
"Circuit breaker timeout (%ds) is less than 2x batch timeout (%.1fs). "
|
|
114
|
+
"This may cause premature circuit opens during normal batch processing. "
|
|
115
|
+
"Recommended minimum: %ds",
|
|
116
|
+
self.circuit_breaker_timeout_seconds,
|
|
117
|
+
batch_timeout_seconds,
|
|
118
|
+
int(min_recommended_circuit_timeout),
|
|
119
|
+
)
|
|
120
|
+
return self
|