omnibase_infra 0.2.7__py3-none-any.whl → 0.2.9__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 (79) hide show
  1. omnibase_infra/__init__.py +1 -1
  2. omnibase_infra/enums/__init__.py +4 -0
  3. omnibase_infra/enums/enum_declarative_node_violation.py +102 -0
  4. omnibase_infra/event_bus/adapters/__init__.py +31 -0
  5. omnibase_infra/event_bus/adapters/adapter_protocol_event_publisher_kafka.py +517 -0
  6. omnibase_infra/mixins/mixin_async_circuit_breaker.py +113 -1
  7. omnibase_infra/models/__init__.py +9 -0
  8. omnibase_infra/models/event_bus/__init__.py +22 -0
  9. omnibase_infra/models/event_bus/model_consumer_retry_config.py +367 -0
  10. omnibase_infra/models/event_bus/model_dlq_config.py +177 -0
  11. omnibase_infra/models/event_bus/model_idempotency_config.py +131 -0
  12. omnibase_infra/models/event_bus/model_offset_policy_config.py +107 -0
  13. omnibase_infra/models/resilience/model_circuit_breaker_config.py +15 -0
  14. omnibase_infra/models/validation/__init__.py +8 -0
  15. omnibase_infra/models/validation/model_declarative_node_validation_result.py +139 -0
  16. omnibase_infra/models/validation/model_declarative_node_violation.py +169 -0
  17. omnibase_infra/nodes/architecture_validator/__init__.py +28 -7
  18. omnibase_infra/nodes/architecture_validator/constants.py +36 -0
  19. omnibase_infra/nodes/architecture_validator/handlers/__init__.py +28 -0
  20. omnibase_infra/nodes/architecture_validator/handlers/contract.yaml +120 -0
  21. omnibase_infra/nodes/architecture_validator/handlers/handler_architecture_validation.py +359 -0
  22. omnibase_infra/nodes/architecture_validator/node.py +1 -0
  23. omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +48 -336
  24. omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +16 -2
  25. omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +14 -4
  26. omnibase_infra/nodes/node_ledger_projection_compute/handlers/__init__.py +18 -0
  27. omnibase_infra/nodes/node_ledger_projection_compute/handlers/contract.yaml +53 -0
  28. omnibase_infra/nodes/node_ledger_projection_compute/handlers/handler_ledger_projection.py +354 -0
  29. omnibase_infra/nodes/node_ledger_projection_compute/node.py +20 -256
  30. omnibase_infra/nodes/node_registry_effect/node.py +20 -73
  31. omnibase_infra/protocols/protocol_dispatch_engine.py +90 -0
  32. omnibase_infra/runtime/__init__.py +11 -0
  33. omnibase_infra/runtime/baseline_subscriptions.py +150 -0
  34. omnibase_infra/runtime/event_bus_subcontract_wiring.py +455 -24
  35. omnibase_infra/runtime/kafka_contract_source.py +13 -5
  36. omnibase_infra/runtime/service_message_dispatch_engine.py +112 -0
  37. omnibase_infra/runtime/service_runtime_host_process.py +6 -11
  38. omnibase_infra/services/__init__.py +36 -0
  39. omnibase_infra/services/contract_publisher/__init__.py +95 -0
  40. omnibase_infra/services/contract_publisher/config.py +199 -0
  41. omnibase_infra/services/contract_publisher/errors.py +243 -0
  42. omnibase_infra/services/contract_publisher/models/__init__.py +28 -0
  43. omnibase_infra/services/contract_publisher/models/model_contract_error.py +67 -0
  44. omnibase_infra/services/contract_publisher/models/model_infra_error.py +62 -0
  45. omnibase_infra/services/contract_publisher/models/model_publish_result.py +112 -0
  46. omnibase_infra/services/contract_publisher/models/model_publish_stats.py +79 -0
  47. omnibase_infra/services/contract_publisher/service.py +617 -0
  48. omnibase_infra/services/contract_publisher/sources/__init__.py +52 -0
  49. omnibase_infra/services/contract_publisher/sources/model_discovered.py +155 -0
  50. omnibase_infra/services/contract_publisher/sources/protocol.py +101 -0
  51. omnibase_infra/services/contract_publisher/sources/source_composite.py +309 -0
  52. omnibase_infra/services/contract_publisher/sources/source_filesystem.py +174 -0
  53. omnibase_infra/services/contract_publisher/sources/source_package.py +221 -0
  54. omnibase_infra/services/observability/__init__.py +40 -0
  55. omnibase_infra/services/observability/agent_actions/__init__.py +64 -0
  56. omnibase_infra/services/observability/agent_actions/config.py +209 -0
  57. omnibase_infra/services/observability/agent_actions/consumer.py +1320 -0
  58. omnibase_infra/services/observability/agent_actions/models/__init__.py +87 -0
  59. omnibase_infra/services/observability/agent_actions/models/model_agent_action.py +142 -0
  60. omnibase_infra/services/observability/agent_actions/models/model_detection_failure.py +125 -0
  61. omnibase_infra/services/observability/agent_actions/models/model_envelope.py +85 -0
  62. omnibase_infra/services/observability/agent_actions/models/model_execution_log.py +159 -0
  63. omnibase_infra/services/observability/agent_actions/models/model_performance_metric.py +130 -0
  64. omnibase_infra/services/observability/agent_actions/models/model_routing_decision.py +138 -0
  65. omnibase_infra/services/observability/agent_actions/models/model_transformation_event.py +124 -0
  66. omnibase_infra/services/observability/agent_actions/tests/__init__.py +20 -0
  67. omnibase_infra/services/observability/agent_actions/tests/test_consumer.py +1154 -0
  68. omnibase_infra/services/observability/agent_actions/tests/test_models.py +645 -0
  69. omnibase_infra/services/observability/agent_actions/tests/test_writer.py +709 -0
  70. omnibase_infra/services/observability/agent_actions/writer_postgres.py +926 -0
  71. omnibase_infra/validation/__init__.py +12 -0
  72. omnibase_infra/validation/contracts/declarative_node.validation.yaml +143 -0
  73. omnibase_infra/validation/validation_exemptions.yaml +93 -0
  74. omnibase_infra/validation/validator_declarative_node.py +850 -0
  75. {omnibase_infra-0.2.7.dist-info → omnibase_infra-0.2.9.dist-info}/METADATA +3 -3
  76. {omnibase_infra-0.2.7.dist-info → omnibase_infra-0.2.9.dist-info}/RECORD +79 -27
  77. {omnibase_infra-0.2.7.dist-info → omnibase_infra-0.2.9.dist-info}/WHEEL +0 -0
  78. {omnibase_infra-0.2.7.dist-info → omnibase_infra-0.2.9.dist-info}/entry_points.txt +0 -0
  79. {omnibase_infra-0.2.7.dist-info → omnibase_infra-0.2.9.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,243 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Contract Publisher Error Classes.
4
+
5
+ This module defines error classes specific to contract publishing operations.
6
+ All error classes extend from RuntimeHostError to maintain consistency with
7
+ ONEX infrastructure error handling patterns.
8
+
9
+ Error Hierarchy:
10
+ RuntimeHostError (from omnibase_infra.errors)
11
+ └── ContractPublisherError (base contract publishing error)
12
+ ├── ContractSourceNotConfiguredError
13
+ ├── ContractPublishingInfraError
14
+ └── NoContractsFoundError
15
+
16
+ Related:
17
+ - OMN-1752: Extract ContractPublisher to omnibase_infra
18
+ - ARCH-002: Runtime owns all Kafka plumbing
19
+
20
+ .. versionadded:: 0.3.0
21
+ Created as part of OMN-1752 (ContractPublisher extraction).
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from typing import TYPE_CHECKING
27
+
28
+ from omnibase_core.enums import EnumCoreErrorCode
29
+ from omnibase_infra.enums import EnumInfraTransportType
30
+ from omnibase_infra.errors import RuntimeHostError
31
+ from omnibase_infra.models.errors import ModelInfraErrorContext
32
+
33
+ if TYPE_CHECKING:
34
+ from omnibase_infra.services.contract_publisher.models import ModelInfraError
35
+
36
+
37
+ class ContractPublisherError(RuntimeHostError):
38
+ """Base error class for contract publishing errors.
39
+
40
+ All contract publishing-specific errors should inherit from this class.
41
+ Provides common context for contract publishing operations.
42
+
43
+ Example:
44
+ >>> raise ContractPublisherError("Publishing failed")
45
+
46
+ .. versionadded:: 0.3.0
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ message: str,
52
+ error_code: EnumCoreErrorCode | None = None,
53
+ context: ModelInfraErrorContext | None = None,
54
+ **extra_context: object,
55
+ ) -> None:
56
+ """Initialize ContractPublisherError.
57
+
58
+ Args:
59
+ message: Human-readable error message
60
+ error_code: Error code (defaults to OPERATION_FAILED)
61
+ context: Bundled infrastructure context
62
+ **extra_context: Additional context information
63
+ """
64
+ # Default to KAFKA transport type for contract publishing
65
+ if context is None:
66
+ context = ModelInfraErrorContext.with_correlation(
67
+ transport_type=EnumInfraTransportType.KAFKA,
68
+ operation="contract_publish",
69
+ )
70
+
71
+ super().__init__(
72
+ message=message,
73
+ error_code=error_code or EnumCoreErrorCode.OPERATION_FAILED,
74
+ context=context,
75
+ **extra_context,
76
+ )
77
+
78
+
79
+ class ContractSourceNotConfiguredError(ContractPublisherError):
80
+ """Raised when no contract source is configured.
81
+
82
+ This error indicates a configuration problem - the ContractPublisherConfig
83
+ does not have the required source configuration for the selected mode.
84
+
85
+ Examples:
86
+ - mode="filesystem" but filesystem_root is None
87
+ - mode="package" but package_module is None
88
+ - mode="composite" but neither source is configured
89
+
90
+ Example:
91
+ >>> raise ContractSourceNotConfiguredError(
92
+ ... mode="filesystem",
93
+ ... missing_field="filesystem_root",
94
+ ... )
95
+
96
+ .. versionadded:: 0.3.0
97
+ """
98
+
99
+ def __init__(
100
+ self,
101
+ mode: str,
102
+ missing_field: str,
103
+ message: str | None = None,
104
+ ) -> None:
105
+ """Initialize ContractSourceNotConfiguredError.
106
+
107
+ Args:
108
+ mode: The configured mode (filesystem, package, composite)
109
+ missing_field: The field that is missing
110
+ message: Optional custom message
111
+ """
112
+ self.mode = mode
113
+ self.missing_field = missing_field
114
+
115
+ default_message = (
116
+ f"Contract source not configured: mode='{mode}' requires '{missing_field}'"
117
+ )
118
+
119
+ super().__init__(
120
+ message=message or default_message,
121
+ error_code=EnumCoreErrorCode.CONFIGURATION_ERROR,
122
+ mode=mode,
123
+ missing_field=missing_field,
124
+ )
125
+
126
+
127
+ class ContractPublishingInfraError(ContractPublisherError):
128
+ """Raised when infrastructure fails during contract publishing.
129
+
130
+ This error wraps one or more ModelInfraError instances that occurred
131
+ during the publishing process. It is raised when fail_fast=True and
132
+ an infrastructure error occurs.
133
+
134
+ Attributes:
135
+ infra_errors: List of infrastructure errors that occurred
136
+
137
+ Example:
138
+ >>> from omnibase_infra.services.contract_publisher.models import ModelInfraError
139
+ >>> error = ModelInfraError(
140
+ ... error_type="publisher_unavailable",
141
+ ... message="Event bus publisher not available",
142
+ ... retriable=False,
143
+ ... )
144
+ >>> raise ContractPublishingInfraError([error])
145
+
146
+ .. versionadded:: 0.3.0
147
+ """
148
+
149
+ def __init__(
150
+ self,
151
+ infra_errors: list[ModelInfraError],
152
+ message: str | None = None,
153
+ ) -> None:
154
+ """Initialize ContractPublishingInfraError.
155
+
156
+ Args:
157
+ infra_errors: List of infrastructure errors that occurred
158
+ message: Optional custom message
159
+ """
160
+ self.infra_errors = infra_errors
161
+
162
+ # Build message from errors if not provided
163
+ if message is None:
164
+ error_count = len(infra_errors)
165
+ if error_count == 1:
166
+ message = f"Contract publishing failed: {infra_errors[0].message}"
167
+ else:
168
+ message = f"Contract publishing failed with {error_count} infrastructure errors"
169
+
170
+ super().__init__(
171
+ message=message,
172
+ error_code=EnumCoreErrorCode.OPERATION_FAILED,
173
+ error_count=len(infra_errors),
174
+ )
175
+
176
+
177
+ class NoContractsFoundError(ContractPublisherError):
178
+ """Raised when no contracts are found and allow_zero_contracts=False.
179
+
180
+ This error indicates that:
181
+ - Discovery found zero contract files, OR
182
+ - Discovery found files but all failed validation (published_count == 0)
183
+
184
+ Both cases result in "no contracts published" which may indicate
185
+ a configuration or deployment problem.
186
+
187
+ Attributes:
188
+ source_description: Description of the source that was searched
189
+ discovered_count: Number of contracts discovered (may be > 0 if all failed)
190
+ valid_count: Number of valid contracts (always 0 when raised)
191
+
192
+ Example:
193
+ >>> raise NoContractsFoundError(
194
+ ... source_description="filesystem: /app/contracts",
195
+ ... discovered_count=0,
196
+ ... )
197
+
198
+ .. versionadded:: 0.3.0
199
+ """
200
+
201
+ def __init__(
202
+ self,
203
+ source_description: str,
204
+ discovered_count: int = 0,
205
+ valid_count: int = 0,
206
+ message: str | None = None,
207
+ ) -> None:
208
+ """Initialize NoContractsFoundError.
209
+
210
+ Args:
211
+ source_description: Description of the source searched
212
+ discovered_count: Number of contracts discovered
213
+ valid_count: Number of valid contracts (should be 0)
214
+ message: Optional custom message
215
+ """
216
+ self.source_description = source_description
217
+ self.discovered_count = discovered_count
218
+ self.valid_count = valid_count
219
+
220
+ if message is None:
221
+ if discovered_count == 0:
222
+ message = f"No contracts found in {source_description}"
223
+ else:
224
+ message = (
225
+ f"No valid contracts: discovered {discovered_count} but "
226
+ f"all failed validation in {source_description}"
227
+ )
228
+
229
+ super().__init__(
230
+ message=message,
231
+ error_code=EnumCoreErrorCode.RESOURCE_NOT_FOUND,
232
+ source_description=source_description,
233
+ discovered_count=discovered_count,
234
+ valid_count=valid_count,
235
+ )
236
+
237
+
238
+ __all__ = [
239
+ "ContractPublisherError",
240
+ "ContractSourceNotConfiguredError",
241
+ "ContractPublishingInfraError",
242
+ "NoContractsFoundError",
243
+ ]
@@ -0,0 +1,28 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Contract Publisher Models.
4
+
5
+ Re-exports all models for clean imports.
6
+
7
+ .. versionadded:: 0.3.0
8
+ """
9
+
10
+ from omnibase_infra.services.contract_publisher.models.model_contract_error import (
11
+ ModelContractError,
12
+ )
13
+ from omnibase_infra.services.contract_publisher.models.model_infra_error import (
14
+ ModelInfraError,
15
+ )
16
+ from omnibase_infra.services.contract_publisher.models.model_publish_result import (
17
+ ModelPublishResult,
18
+ )
19
+ from omnibase_infra.services.contract_publisher.models.model_publish_stats import (
20
+ ModelPublishStats,
21
+ )
22
+
23
+ __all__ = [
24
+ "ModelContractError",
25
+ "ModelInfraError",
26
+ "ModelPublishResult",
27
+ "ModelPublishStats",
28
+ ]
@@ -0,0 +1,67 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Contract Error Model.
4
+
5
+ Non-fatal error for contract parsing/validation failures.
6
+
7
+ .. versionadded:: 0.3.0
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Literal
13
+
14
+ from pydantic import BaseModel, ConfigDict, Field
15
+
16
+
17
+ class ModelContractError(BaseModel):
18
+ """Non-fatal contract-level error.
19
+
20
+ Represents errors that occur during contract parsing, validation, or
21
+ conflict detection. These errors are non-fatal - processing continues
22
+ and the error is collected in the result.
23
+
24
+ Error Types:
25
+ yaml_parse: YAML syntax error
26
+ schema_validation: Contract doesn't match ModelHandlerContract schema
27
+ missing_field: Required field missing (handler_id, etc.)
28
+ invalid_handler_class: handler_class not fully qualified Python path
29
+ duplicate_conflict: Same handler_id with different content hash
30
+
31
+ Attributes:
32
+ contract_path: Path or identifier of the contract that failed
33
+ handler_id: Handler ID if extracted, None if parsing failed early
34
+ error_type: Category of the error
35
+ message: Human-readable error description
36
+
37
+ Example:
38
+ >>> error = ModelContractError(
39
+ ... contract_path="/app/contracts/handlers/foo/contract.yaml",
40
+ ... handler_id=None,
41
+ ... error_type="yaml_parse",
42
+ ... message="Invalid YAML syntax at line 10",
43
+ ... )
44
+
45
+ .. versionadded:: 0.3.0
46
+ """
47
+
48
+ model_config = ConfigDict(frozen=True, extra="forbid")
49
+
50
+ contract_path: str = Field(
51
+ description="Path or identifier of the contract that failed"
52
+ )
53
+ handler_id: str | None = Field(
54
+ default=None,
55
+ description="Handler ID if extracted, None if parsing failed early",
56
+ )
57
+ error_type: Literal[
58
+ "yaml_parse",
59
+ "schema_validation",
60
+ "missing_field",
61
+ "invalid_handler_class",
62
+ "duplicate_conflict",
63
+ ] = Field(description="Category of the error")
64
+ message: str = Field(description="Human-readable error description")
65
+
66
+
67
+ __all__ = ["ModelContractError"]
@@ -0,0 +1,62 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Infrastructure Error Model.
4
+
5
+ Fatal error for infrastructure failures (Kafka, network, etc.).
6
+
7
+ .. versionadded:: 0.3.0
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Literal
13
+
14
+ from pydantic import BaseModel, ConfigDict, Field
15
+
16
+
17
+ class ModelInfraError(BaseModel):
18
+ """Infrastructure error during contract publishing.
19
+
20
+ Represents errors that occur due to infrastructure failures (Kafka,
21
+ network, etc.). These errors are potentially fatal depending on
22
+ fail_fast configuration.
23
+
24
+ Error Types:
25
+ publisher_unavailable: Event bus publisher not available in container
26
+ kafka_timeout: Kafka operation timed out
27
+ broker_down: Kafka broker unreachable
28
+ publish_failed: Generic publish failure
29
+ serialization_failed: Failed to serialize event envelope
30
+
31
+ Attributes:
32
+ error_type: Category of the error
33
+ message: Human-readable error description
34
+ retriable: Whether the operation might succeed on retry
35
+
36
+ Example:
37
+ >>> error = ModelInfraError(
38
+ ... error_type="kafka_timeout",
39
+ ... message="Publish timed out after 30s",
40
+ ... retriable=True,
41
+ ... )
42
+
43
+ .. versionadded:: 0.3.0
44
+ """
45
+
46
+ model_config = ConfigDict(frozen=True, extra="forbid")
47
+
48
+ error_type: Literal[
49
+ "publisher_unavailable",
50
+ "kafka_timeout",
51
+ "broker_down",
52
+ "publish_failed",
53
+ "serialization_failed",
54
+ ] = Field(description="Category of the error")
55
+ message: str = Field(description="Human-readable error description")
56
+ retriable: bool = Field(
57
+ default=False,
58
+ description="Whether the operation might succeed on retry",
59
+ )
60
+
61
+
62
+ __all__ = ["ModelInfraError"]
@@ -0,0 +1,112 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Publish Result Model.
4
+
5
+ Complete result of contract publishing with errors and statistics.
6
+
7
+ .. versionadded:: 0.3.0
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from pydantic import BaseModel, ConfigDict, Field
13
+
14
+ from omnibase_infra.services.contract_publisher.models.model_contract_error import (
15
+ ModelContractError,
16
+ )
17
+ from omnibase_infra.services.contract_publisher.models.model_infra_error import (
18
+ ModelInfraError,
19
+ )
20
+ from omnibase_infra.services.contract_publisher.models.model_publish_stats import (
21
+ ModelPublishStats,
22
+ )
23
+
24
+
25
+ class ModelPublishResult(BaseModel):
26
+ """Complete result of contract publishing operation.
27
+
28
+ Contains three categories of information:
29
+ - published: List of handler_ids successfully published
30
+ - contract_errors: Non-fatal contract-level errors
31
+ - infra_errors: Infrastructure errors
32
+ - stats: Publishing statistics
33
+
34
+ The __bool__ method returns True if any contracts were published
35
+ successfully, enabling idiomatic conditional checks:
36
+
37
+ >>> result = await publisher.publish_all()
38
+ >>> if result:
39
+ ... print(f"Published {len(result.published)} contracts")
40
+ ... else:
41
+ ... print("No contracts published")
42
+
43
+ Warning:
44
+ This model overrides __bool__ to return True only when contracts
45
+ are published. This differs from standard Pydantic behavior where
46
+ bool(model) always returns True. Use explicit checks if you need
47
+ to verify the model exists rather than its success state.
48
+
49
+ Attributes:
50
+ published: List of handler_ids successfully published
51
+ contract_errors: Non-fatal contract-level errors
52
+ infra_errors: Infrastructure errors
53
+ stats: Publishing statistics
54
+
55
+ Example:
56
+ >>> result = ModelPublishResult(
57
+ ... published=["handler.foo", "handler.bar"],
58
+ ... contract_errors=[],
59
+ ... infra_errors=[],
60
+ ... stats=stats,
61
+ ... )
62
+ >>> if result:
63
+ ... print("Success!")
64
+
65
+ .. versionadded:: 0.3.0
66
+ """
67
+
68
+ model_config = ConfigDict(frozen=True, extra="forbid")
69
+
70
+ published: list[str] = Field(
71
+ description="List of handler_ids successfully published"
72
+ )
73
+ contract_errors: list[ModelContractError] = Field(
74
+ default_factory=list,
75
+ description="Non-fatal contract-level errors",
76
+ )
77
+ infra_errors: list[ModelInfraError] = Field(
78
+ default_factory=list,
79
+ description="Infrastructure errors",
80
+ )
81
+ stats: ModelPublishStats = Field(description="Publishing statistics")
82
+
83
+ def __bool__(self) -> bool:
84
+ """Return True if any contracts were published successfully.
85
+
86
+ Warning:
87
+ This differs from standard Pydantic behavior. The model
88
+ evaluates to False when no contracts are published, even
89
+ if the model itself is valid.
90
+
91
+ Returns:
92
+ True if published list is non-empty, False otherwise.
93
+ """
94
+ return bool(self.published)
95
+
96
+ @property
97
+ def has_contract_errors(self) -> bool:
98
+ """Check if any contract errors occurred."""
99
+ return len(self.contract_errors) > 0
100
+
101
+ @property
102
+ def has_infra_errors(self) -> bool:
103
+ """Check if any infrastructure errors occurred."""
104
+ return len(self.infra_errors) > 0
105
+
106
+ @property
107
+ def has_errors(self) -> bool:
108
+ """Check if any errors (contract or infra) occurred."""
109
+ return self.has_contract_errors or self.has_infra_errors
110
+
111
+
112
+ __all__ = ["ModelPublishResult"]
@@ -0,0 +1,79 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Publish Statistics Model.
4
+
5
+ Statistics for contract publishing operations including counts, timing, and per-origin breakdown.
6
+
7
+ .. versionadded:: 0.3.0
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from pydantic import BaseModel, ConfigDict, Field
13
+
14
+
15
+ class ModelPublishStats(BaseModel):
16
+ """Publishing statistics for observability.
17
+
18
+ Tracks counts, timing, and per-origin breakdown for contract publishing.
19
+ Included in every ModelPublishResult for debugging and monitoring.
20
+
21
+ Attributes:
22
+ discovered_count: Total contracts found by discovery
23
+ valid_count: Contracts that passed validation
24
+ published_count: Contracts successfully published to Kafka
25
+ errored_count: Contracts that failed validation
26
+ dedup_count: Contracts deduplicated (same handler_id + hash)
27
+ duration_ms: Total duration of publish_all()
28
+ discover_ms: Time spent in discovery phase
29
+ validate_ms: Time spent in validation phase
30
+ publish_ms: Time spent in publishing phase
31
+ environment: Resolved environment (for log clarity)
32
+ filesystem_count: Contracts from filesystem source
33
+ package_count: Contracts from package source
34
+
35
+ Example:
36
+ >>> stats = ModelPublishStats(
37
+ ... discovered_count=10,
38
+ ... valid_count=8,
39
+ ... published_count=8,
40
+ ... errored_count=2,
41
+ ... dedup_count=0,
42
+ ... duration_ms=1234.5,
43
+ ... discover_ms=100.0,
44
+ ... validate_ms=500.0,
45
+ ... publish_ms=634.5,
46
+ ... environment="dev",
47
+ ... filesystem_count=10,
48
+ ... package_count=0,
49
+ ... )
50
+
51
+ .. versionadded:: 0.3.0
52
+ """
53
+
54
+ model_config = ConfigDict(frozen=True, extra="forbid")
55
+
56
+ # Counts
57
+ discovered_count: int = Field(ge=0, description="Total contracts found")
58
+ valid_count: int = Field(ge=0, description="Contracts that passed validation")
59
+ published_count: int = Field(ge=0, description="Contracts successfully published")
60
+ errored_count: int = Field(ge=0, description="Contracts that failed validation")
61
+ dedup_count: int = Field(ge=0, description="Contracts deduplicated (same hash)")
62
+
63
+ # Timing (milliseconds)
64
+ duration_ms: float = Field(ge=0.0, description="Total duration")
65
+ discover_ms: float = Field(ge=0.0, description="Discovery phase duration")
66
+ validate_ms: float = Field(ge=0.0, description="Validation phase duration")
67
+ publish_ms: float = Field(ge=0.0, description="Publishing phase duration")
68
+
69
+ # Context
70
+ environment: str = Field(description="Resolved environment for topics")
71
+
72
+ # Per-origin breakdown
73
+ filesystem_count: int = Field(
74
+ default=0, ge=0, description="Contracts from filesystem"
75
+ )
76
+ package_count: int = Field(default=0, ge=0, description="Contracts from package")
77
+
78
+
79
+ __all__ = ["ModelPublishStats"]