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.
Files changed (57) hide show
  1. omnibase_infra/__init__.py +1 -1
  2. omnibase_infra/errors/__init__.py +4 -0
  3. omnibase_infra/errors/error_infra.py +60 -0
  4. omnibase_infra/handlers/__init__.py +3 -0
  5. omnibase_infra/handlers/handler_slack_webhook.py +426 -0
  6. omnibase_infra/handlers/models/__init__.py +14 -0
  7. omnibase_infra/handlers/models/enum_alert_severity.py +36 -0
  8. omnibase_infra/handlers/models/model_slack_alert.py +24 -0
  9. omnibase_infra/handlers/models/model_slack_alert_payload.py +77 -0
  10. omnibase_infra/handlers/models/model_slack_alert_result.py +73 -0
  11. omnibase_infra/mixins/mixin_node_introspection.py +42 -20
  12. omnibase_infra/models/discovery/model_dependency_spec.py +1 -0
  13. omnibase_infra/models/discovery/model_discovered_capabilities.py +1 -1
  14. omnibase_infra/models/discovery/model_introspection_config.py +28 -1
  15. omnibase_infra/models/discovery/model_introspection_performance_metrics.py +1 -0
  16. omnibase_infra/models/discovery/model_introspection_task_config.py +1 -0
  17. omnibase_infra/models/runtime/__init__.py +4 -0
  18. omnibase_infra/models/runtime/model_resolved_dependencies.py +116 -0
  19. omnibase_infra/nodes/contract_registry_reducer/contract.yaml +6 -5
  20. omnibase_infra/nodes/contract_registry_reducer/reducer.py +9 -26
  21. omnibase_infra/nodes/node_contract_persistence_effect/node.py +18 -1
  22. omnibase_infra/nodes/node_contract_persistence_effect/registry/registry_infra_contract_persistence_effect.py +33 -2
  23. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +8 -12
  24. omnibase_infra/nodes/node_slack_alerter_effect/__init__.py +33 -0
  25. omnibase_infra/nodes/node_slack_alerter_effect/contract.yaml +291 -0
  26. omnibase_infra/nodes/node_slack_alerter_effect/node.py +106 -0
  27. omnibase_infra/runtime/__init__.py +7 -0
  28. omnibase_infra/runtime/baseline_subscriptions.py +13 -6
  29. omnibase_infra/runtime/contract_dependency_resolver.py +455 -0
  30. omnibase_infra/runtime/contract_registration_event_router.py +5 -5
  31. omnibase_infra/runtime/emit_daemon/event_registry.py +34 -22
  32. omnibase_infra/runtime/event_bus_subcontract_wiring.py +63 -23
  33. omnibase_infra/runtime/publisher_topic_scoped.py +16 -11
  34. omnibase_infra/runtime/registry_policy.py +29 -15
  35. omnibase_infra/runtime/request_response_wiring.py +15 -7
  36. omnibase_infra/runtime/service_runtime_host_process.py +149 -5
  37. omnibase_infra/runtime/util_version.py +5 -1
  38. omnibase_infra/schemas/schema_latency_baseline.sql +135 -0
  39. omnibase_infra/services/contract_publisher/config.py +4 -4
  40. omnibase_infra/services/contract_publisher/service.py +8 -5
  41. omnibase_infra/services/observability/injection_effectiveness/__init__.py +67 -0
  42. omnibase_infra/services/observability/injection_effectiveness/config.py +295 -0
  43. omnibase_infra/services/observability/injection_effectiveness/consumer.py +1461 -0
  44. omnibase_infra/services/observability/injection_effectiveness/models/__init__.py +32 -0
  45. omnibase_infra/services/observability/injection_effectiveness/models/model_agent_match.py +79 -0
  46. omnibase_infra/services/observability/injection_effectiveness/models/model_context_utilization.py +118 -0
  47. omnibase_infra/services/observability/injection_effectiveness/models/model_latency_breakdown.py +107 -0
  48. omnibase_infra/services/observability/injection_effectiveness/models/model_pattern_utilization.py +46 -0
  49. omnibase_infra/services/observability/injection_effectiveness/writer_postgres.py +596 -0
  50. omnibase_infra/utils/__init__.py +7 -0
  51. omnibase_infra/utils/util_db_error_context.py +292 -0
  52. omnibase_infra/validation/validation_exemptions.yaml +11 -0
  53. {omnibase_infra-0.3.2.dist-info → omnibase_infra-0.4.0.dist-info}/METADATA +2 -2
  54. {omnibase_infra-0.3.2.dist-info → omnibase_infra-0.4.0.dist-info}/RECORD +57 -36
  55. {omnibase_infra-0.3.2.dist-info → omnibase_infra-0.4.0.dist-info}/WHEEL +0 -0
  56. {omnibase_infra-0.3.2.dist-info → omnibase_infra-0.4.0.dist-info}/entry_points.txt +0 -0
  57. {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.2
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.13.1,<0.14.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)