omnibase_infra 0.3.2__py3-none-any.whl → 0.4.0__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/errors/__init__.py +4 -0
- omnibase_infra/errors/error_infra.py +60 -0
- omnibase_infra/handlers/__init__.py +3 -0
- omnibase_infra/handlers/handler_slack_webhook.py +426 -0
- omnibase_infra/handlers/models/__init__.py +14 -0
- omnibase_infra/handlers/models/enum_alert_severity.py +36 -0
- omnibase_infra/handlers/models/model_slack_alert.py +24 -0
- omnibase_infra/handlers/models/model_slack_alert_payload.py +77 -0
- omnibase_infra/handlers/models/model_slack_alert_result.py +73 -0
- omnibase_infra/mixins/mixin_node_introspection.py +42 -20
- omnibase_infra/models/discovery/model_dependency_spec.py +1 -0
- omnibase_infra/models/discovery/model_discovered_capabilities.py +1 -1
- omnibase_infra/models/discovery/model_introspection_config.py +28 -1
- omnibase_infra/models/discovery/model_introspection_performance_metrics.py +1 -0
- omnibase_infra/models/discovery/model_introspection_task_config.py +1 -0
- omnibase_infra/models/runtime/__init__.py +4 -0
- omnibase_infra/models/runtime/model_resolved_dependencies.py +116 -0
- omnibase_infra/nodes/contract_registry_reducer/contract.yaml +6 -5
- omnibase_infra/nodes/contract_registry_reducer/reducer.py +9 -26
- omnibase_infra/nodes/node_contract_persistence_effect/node.py +18 -1
- omnibase_infra/nodes/node_contract_persistence_effect/registry/registry_infra_contract_persistence_effect.py +33 -2
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +8 -12
- omnibase_infra/nodes/node_slack_alerter_effect/__init__.py +33 -0
- omnibase_infra/nodes/node_slack_alerter_effect/contract.yaml +291 -0
- omnibase_infra/nodes/node_slack_alerter_effect/node.py +106 -0
- omnibase_infra/runtime/__init__.py +7 -0
- omnibase_infra/runtime/baseline_subscriptions.py +13 -6
- omnibase_infra/runtime/contract_dependency_resolver.py +455 -0
- omnibase_infra/runtime/contract_registration_event_router.py +5 -5
- omnibase_infra/runtime/emit_daemon/event_registry.py +34 -22
- omnibase_infra/runtime/event_bus_subcontract_wiring.py +63 -23
- omnibase_infra/runtime/publisher_topic_scoped.py +16 -11
- omnibase_infra/runtime/registry_policy.py +29 -15
- omnibase_infra/runtime/request_response_wiring.py +15 -7
- omnibase_infra/runtime/service_runtime_host_process.py +149 -5
- omnibase_infra/runtime/util_version.py +5 -1
- omnibase_infra/schemas/schema_latency_baseline.sql +135 -0
- omnibase_infra/services/contract_publisher/config.py +4 -4
- omnibase_infra/services/contract_publisher/service.py +8 -5
- omnibase_infra/services/observability/injection_effectiveness/__init__.py +67 -0
- omnibase_infra/services/observability/injection_effectiveness/config.py +295 -0
- omnibase_infra/services/observability/injection_effectiveness/consumer.py +1461 -0
- omnibase_infra/services/observability/injection_effectiveness/models/__init__.py +32 -0
- omnibase_infra/services/observability/injection_effectiveness/models/model_agent_match.py +79 -0
- omnibase_infra/services/observability/injection_effectiveness/models/model_context_utilization.py +118 -0
- omnibase_infra/services/observability/injection_effectiveness/models/model_latency_breakdown.py +107 -0
- omnibase_infra/services/observability/injection_effectiveness/models/model_pattern_utilization.py +46 -0
- omnibase_infra/services/observability/injection_effectiveness/writer_postgres.py +596 -0
- omnibase_infra/utils/__init__.py +7 -0
- omnibase_infra/utils/util_db_error_context.py +292 -0
- omnibase_infra/validation/validation_exemptions.yaml +11 -0
- {omnibase_infra-0.3.2.dist-info → omnibase_infra-0.4.0.dist-info}/METADATA +2 -2
- {omnibase_infra-0.3.2.dist-info → omnibase_infra-0.4.0.dist-info}/RECORD +57 -36
- {omnibase_infra-0.3.2.dist-info → omnibase_infra-0.4.0.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.3.2.dist-info → omnibase_infra-0.4.0.dist-info}/entry_points.txt +0 -0
- {omnibase_infra-0.3.2.dist-info → omnibase_infra-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Database operation error handling context manager.
|
|
4
|
+
|
|
5
|
+
This module provides an async context manager for consistent error handling
|
|
6
|
+
in database operations using asyncpg. It transforms low-level asyncpg exceptions
|
|
7
|
+
into ONEX infrastructure errors with proper context propagation.
|
|
8
|
+
|
|
9
|
+
Key Features:
|
|
10
|
+
- Transforms asyncpg exceptions to ONEX infrastructure errors
|
|
11
|
+
- Propagates correlation IDs for distributed tracing
|
|
12
|
+
- Integrates with circuit breaker failure recording
|
|
13
|
+
- Handles timeout errors with proper timeout context
|
|
14
|
+
|
|
15
|
+
Exception Mapping:
|
|
16
|
+
| asyncpg Exception | ONEX Error | When |
|
|
17
|
+
|---------------------------|----------------------|-------------------------|
|
|
18
|
+
| QueryCanceledError | InfraTimeoutError | statement_timeout hit |
|
|
19
|
+
| PostgresConnectionError | InfraConnectionError | Connection lost/failed |
|
|
20
|
+
| PostgresError (other) | RuntimeHostError | Other database errors |
|
|
21
|
+
|
|
22
|
+
Usage Pattern:
|
|
23
|
+
This context manager is designed to wrap the body of database write methods,
|
|
24
|
+
handling the exception transformation consistently. It does NOT manage
|
|
25
|
+
transactions or connections - use ``transaction_context()`` for that.
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
>>> from omnibase_infra.utils import db_operation_error_context
|
|
29
|
+
>>> from omnibase_infra.mixins import MixinAsyncCircuitBreaker
|
|
30
|
+
>>>
|
|
31
|
+
>>> class MyWriter(MixinAsyncCircuitBreaker):
|
|
32
|
+
... async def write_data(self, data: list[Model], correlation_id: UUID | None = None):
|
|
33
|
+
... op_correlation_id = correlation_id or uuid4()
|
|
34
|
+
...
|
|
35
|
+
... async with db_operation_error_context(
|
|
36
|
+
... operation="write_data",
|
|
37
|
+
... target_name="my_table",
|
|
38
|
+
... correlation_id=op_correlation_id,
|
|
39
|
+
... timeout_seconds=30.0,
|
|
40
|
+
... circuit_breaker=self, # Pass self for circuit breaker integration
|
|
41
|
+
... ):
|
|
42
|
+
... async with self._pool.acquire() as conn:
|
|
43
|
+
... await conn.executemany(sql, data)
|
|
44
|
+
|
|
45
|
+
Related Modules:
|
|
46
|
+
- ``util_db_transaction.py``: Transaction context manager for asyncpg
|
|
47
|
+
- ``omnibase_infra.mixins.MixinAsyncCircuitBreaker``: Circuit breaker mixin
|
|
48
|
+
|
|
49
|
+
Related Tickets:
|
|
50
|
+
- OMN-1890: Store injection metrics with corrected schema
|
|
51
|
+
- PR #237: Extract shared error handling into context manager
|
|
52
|
+
|
|
53
|
+
.. versionadded:: 0.11.0
|
|
54
|
+
Created to extract shared error handling from WriterInjectionEffectivenessPostgres.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
from __future__ import annotations
|
|
58
|
+
|
|
59
|
+
import asyncio
|
|
60
|
+
import logging
|
|
61
|
+
from collections.abc import AsyncIterator
|
|
62
|
+
from contextlib import asynccontextmanager
|
|
63
|
+
from typing import Protocol
|
|
64
|
+
from uuid import UUID, uuid4
|
|
65
|
+
|
|
66
|
+
import asyncpg
|
|
67
|
+
|
|
68
|
+
from omnibase_infra.enums import EnumInfraTransportType
|
|
69
|
+
from omnibase_infra.errors import (
|
|
70
|
+
InfraConnectionError,
|
|
71
|
+
InfraTimeoutError,
|
|
72
|
+
ModelInfraErrorContext,
|
|
73
|
+
ModelTimeoutErrorContext,
|
|
74
|
+
RuntimeHostError,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
logger = logging.getLogger(__name__)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class ProtocolCircuitBreakerFailureRecorder(Protocol):
|
|
81
|
+
"""Protocol for circuit breaker failure recording.
|
|
82
|
+
|
|
83
|
+
This protocol defines the minimal interface required for recording
|
|
84
|
+
circuit breaker failures. It allows the error context manager to
|
|
85
|
+
work with any object that implements these methods.
|
|
86
|
+
|
|
87
|
+
The protocol uses asyncio.Lock for thread-safe circuit breaker state
|
|
88
|
+
access. This matches the MixinAsyncCircuitBreaker implementation.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
_circuit_breaker_lock: asyncio.Lock
|
|
92
|
+
"""Lock for thread-safe circuit breaker state access."""
|
|
93
|
+
|
|
94
|
+
async def _record_circuit_failure(
|
|
95
|
+
self,
|
|
96
|
+
operation: str,
|
|
97
|
+
correlation_id: UUID,
|
|
98
|
+
) -> None:
|
|
99
|
+
"""Record a circuit breaker failure.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
operation: Name of the operation that failed.
|
|
103
|
+
correlation_id: Correlation ID for tracing.
|
|
104
|
+
"""
|
|
105
|
+
...
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@asynccontextmanager
|
|
109
|
+
async def db_operation_error_context(
|
|
110
|
+
operation: str,
|
|
111
|
+
target_name: str,
|
|
112
|
+
correlation_id: UUID | None = None,
|
|
113
|
+
timeout_seconds: float | None = None,
|
|
114
|
+
circuit_breaker: ProtocolCircuitBreakerFailureRecorder | None = None,
|
|
115
|
+
) -> AsyncIterator[tuple[UUID, ModelInfraErrorContext]]:
|
|
116
|
+
"""Async context manager for database operation error handling.
|
|
117
|
+
|
|
118
|
+
Wraps database operations with consistent exception handling, converting
|
|
119
|
+
asyncpg exceptions to ONEX infrastructure errors. Optionally integrates
|
|
120
|
+
with circuit breaker failure recording.
|
|
121
|
+
|
|
122
|
+
This context manager yields a tuple of (correlation_id, error_context)
|
|
123
|
+
that can be used within the wrapped code for logging or additional
|
|
124
|
+
error context.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
operation: Name of the operation being performed (e.g., "write_data").
|
|
128
|
+
Used in error messages and circuit breaker failure recording.
|
|
129
|
+
target_name: Name of the target table or resource (e.g., "users").
|
|
130
|
+
Used in error context for debugging.
|
|
131
|
+
correlation_id: Optional correlation ID for distributed tracing.
|
|
132
|
+
If not provided, a new UUID is generated.
|
|
133
|
+
timeout_seconds: Optional query timeout in seconds. If provided,
|
|
134
|
+
timeout errors will include this value in the error context.
|
|
135
|
+
circuit_breaker: Optional circuit breaker instance for failure recording.
|
|
136
|
+
If provided, failures will be recorded via ``_record_circuit_failure()``.
|
|
137
|
+
Must implement ``ProtocolCircuitBreakerFailureRecorder`` protocol.
|
|
138
|
+
|
|
139
|
+
Yields:
|
|
140
|
+
Tuple of (correlation_id, ModelInfraErrorContext):
|
|
141
|
+
- correlation_id: The correlation ID (either provided or generated)
|
|
142
|
+
- error_context: Pre-built error context for use in the operation
|
|
143
|
+
|
|
144
|
+
Raises:
|
|
145
|
+
InfraTimeoutError: When asyncpg.QueryCanceledError is caught
|
|
146
|
+
(statement_timeout exceeded).
|
|
147
|
+
InfraConnectionError: When asyncpg.PostgresConnectionError is caught
|
|
148
|
+
(connection lost or failed).
|
|
149
|
+
RuntimeHostError: When other asyncpg.PostgresError exceptions are caught.
|
|
150
|
+
|
|
151
|
+
Example:
|
|
152
|
+
Basic usage without circuit breaker:
|
|
153
|
+
|
|
154
|
+
>>> async with db_operation_error_context(
|
|
155
|
+
... operation="insert_users",
|
|
156
|
+
... target_name="users",
|
|
157
|
+
... timeout_seconds=30.0,
|
|
158
|
+
... ) as (corr_id, context):
|
|
159
|
+
... async with pool.acquire() as conn:
|
|
160
|
+
... await conn.executemany(sql, users)
|
|
161
|
+
|
|
162
|
+
With circuit breaker integration:
|
|
163
|
+
|
|
164
|
+
>>> class MyWriter(MixinAsyncCircuitBreaker):
|
|
165
|
+
... async def write(self, data: list[Model]) -> int:
|
|
166
|
+
... async with db_operation_error_context(
|
|
167
|
+
... operation="write",
|
|
168
|
+
... target_name="my_table",
|
|
169
|
+
... timeout_seconds=self._query_timeout,
|
|
170
|
+
... circuit_breaker=self,
|
|
171
|
+
... ) as (corr_id, context):
|
|
172
|
+
... # Database operations here
|
|
173
|
+
... pass
|
|
174
|
+
|
|
175
|
+
Note:
|
|
176
|
+
This context manager does NOT:
|
|
177
|
+
- Manage transactions (use ``transaction_context()`` for that)
|
|
178
|
+
- Acquire connections from the pool
|
|
179
|
+
- Set statement_timeout (caller must do this)
|
|
180
|
+
- Check circuit breaker state (use ``_check_circuit_breaker()`` first)
|
|
181
|
+
|
|
182
|
+
The caller is responsible for:
|
|
183
|
+
1. Checking circuit breaker state before entering
|
|
184
|
+
2. Acquiring connections and starting transactions
|
|
185
|
+
3. Setting statement_timeout on the connection
|
|
186
|
+
4. Resetting circuit breaker on success
|
|
187
|
+
|
|
188
|
+
Warning:
|
|
189
|
+
Circuit breaker failure recording requires the circuit breaker lock.
|
|
190
|
+
This context manager acquires the lock internally when recording
|
|
191
|
+
failures to ensure thread-safety.
|
|
192
|
+
"""
|
|
193
|
+
op_correlation_id = correlation_id or uuid4()
|
|
194
|
+
|
|
195
|
+
# Build error context upfront for use in exception handlers
|
|
196
|
+
context = ModelInfraErrorContext(
|
|
197
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
198
|
+
operation=operation,
|
|
199
|
+
target_name=target_name,
|
|
200
|
+
correlation_id=op_correlation_id,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
yield (op_correlation_id, context)
|
|
205
|
+
|
|
206
|
+
except asyncpg.QueryCanceledError as e:
|
|
207
|
+
# Record circuit breaker failure if provided
|
|
208
|
+
if circuit_breaker is not None:
|
|
209
|
+
async with circuit_breaker._circuit_breaker_lock:
|
|
210
|
+
await circuit_breaker._record_circuit_failure(
|
|
211
|
+
operation=operation,
|
|
212
|
+
correlation_id=op_correlation_id,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# Build timeout-specific context
|
|
216
|
+
timeout_context = ModelTimeoutErrorContext(
|
|
217
|
+
transport_type=context.transport_type,
|
|
218
|
+
operation=context.operation,
|
|
219
|
+
target_name=context.target_name,
|
|
220
|
+
correlation_id=context.correlation_id,
|
|
221
|
+
timeout_seconds=timeout_seconds or 0.0,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
logger.warning(
|
|
225
|
+
"Database operation timed out",
|
|
226
|
+
extra={
|
|
227
|
+
"operation": operation,
|
|
228
|
+
"target_name": target_name,
|
|
229
|
+
"correlation_id": str(op_correlation_id),
|
|
230
|
+
"timeout_seconds": timeout_seconds,
|
|
231
|
+
},
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
raise InfraTimeoutError(
|
|
235
|
+
f"{operation} timed out",
|
|
236
|
+
context=timeout_context,
|
|
237
|
+
) from e
|
|
238
|
+
|
|
239
|
+
except asyncpg.PostgresConnectionError as e:
|
|
240
|
+
# Record circuit breaker failure if provided
|
|
241
|
+
if circuit_breaker is not None:
|
|
242
|
+
async with circuit_breaker._circuit_breaker_lock:
|
|
243
|
+
await circuit_breaker._record_circuit_failure(
|
|
244
|
+
operation=operation,
|
|
245
|
+
correlation_id=op_correlation_id,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
logger.warning(
|
|
249
|
+
"Database connection failed",
|
|
250
|
+
extra={
|
|
251
|
+
"operation": operation,
|
|
252
|
+
"target_name": target_name,
|
|
253
|
+
"correlation_id": str(op_correlation_id),
|
|
254
|
+
"error": str(e),
|
|
255
|
+
},
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
raise InfraConnectionError(
|
|
259
|
+
f"Database connection failed during {operation}",
|
|
260
|
+
context=context,
|
|
261
|
+
) from e
|
|
262
|
+
|
|
263
|
+
except asyncpg.PostgresError as e:
|
|
264
|
+
# Record circuit breaker failure if provided
|
|
265
|
+
if circuit_breaker is not None:
|
|
266
|
+
async with circuit_breaker._circuit_breaker_lock:
|
|
267
|
+
await circuit_breaker._record_circuit_failure(
|
|
268
|
+
operation=operation,
|
|
269
|
+
correlation_id=op_correlation_id,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
logger.warning(
|
|
273
|
+
"Database error occurred",
|
|
274
|
+
extra={
|
|
275
|
+
"operation": operation,
|
|
276
|
+
"target_name": target_name,
|
|
277
|
+
"correlation_id": str(op_correlation_id),
|
|
278
|
+
"error_type": type(e).__name__,
|
|
279
|
+
"error": str(e),
|
|
280
|
+
},
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
raise RuntimeHostError(
|
|
284
|
+
f"Database error during {operation}: {type(e).__name__}",
|
|
285
|
+
context=context,
|
|
286
|
+
) from e
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
__all__: list[str] = [
|
|
290
|
+
"ProtocolCircuitBreakerFailureRecorder",
|
|
291
|
+
"db_operation_error_context",
|
|
292
|
+
]
|
|
@@ -546,6 +546,17 @@ pattern_exemptions:
|
|
|
546
546
|
- CLAUDE.md (Registry Naming Conventions)
|
|
547
547
|
ticket: OMN-1139
|
|
548
548
|
# ==========================================================================
|
|
549
|
+
# ModelIntrospectionConfig node_name Exemption (omnibase-core 0.14.0 upgrade)
|
|
550
|
+
# ==========================================================================
|
|
551
|
+
# node_name is a canonical identifier for consumer group derivation, not an entity reference.
|
|
552
|
+
- file_pattern: 'model_introspection_config\.py'
|
|
553
|
+
violation_pattern: "Field 'node_name' might reference an entity"
|
|
554
|
+
reason: >
|
|
555
|
+
node_name is a canonical identifier string (e.g., "claude_hook_effect") used for consumer group derivation in event bus subscriptions. It is part of the node identity model (env.service.node_name.purpose.version) and does not reference an external entity.
|
|
556
|
+
|
|
557
|
+
documentation:
|
|
558
|
+
- CLAUDE.md (Node Identity Model)
|
|
559
|
+
# ==========================================================================
|
|
549
560
|
# TransitionNotificationOutbox Method Count Exemption (OMN-1139)
|
|
550
561
|
# ==========================================================================
|
|
551
562
|
# Outbox pattern requires lifecycle (start/stop), storage (store/cleanup),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: omnibase_infra
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: ONEX Infrastructure - Service integration and database infrastructure tools
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -28,7 +28,7 @@ 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.
|
|
31
|
+
Requires-Dist: omnibase-core (>=0.14.0,<0.15.0)
|
|
32
32
|
Requires-Dist: omnibase-spi (>=0.6.4,<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)
|