ouroboros-ai 0.1.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.

Potentially problematic release.


This version of ouroboros-ai might be problematic. Click here for more details.

Files changed (81) hide show
  1. ouroboros/__init__.py +15 -0
  2. ouroboros/__main__.py +9 -0
  3. ouroboros/bigbang/__init__.py +39 -0
  4. ouroboros/bigbang/ambiguity.py +464 -0
  5. ouroboros/bigbang/interview.py +530 -0
  6. ouroboros/bigbang/seed_generator.py +610 -0
  7. ouroboros/cli/__init__.py +9 -0
  8. ouroboros/cli/commands/__init__.py +7 -0
  9. ouroboros/cli/commands/config.py +79 -0
  10. ouroboros/cli/commands/init.py +425 -0
  11. ouroboros/cli/commands/run.py +201 -0
  12. ouroboros/cli/commands/status.py +85 -0
  13. ouroboros/cli/formatters/__init__.py +31 -0
  14. ouroboros/cli/formatters/panels.py +157 -0
  15. ouroboros/cli/formatters/progress.py +112 -0
  16. ouroboros/cli/formatters/tables.py +166 -0
  17. ouroboros/cli/main.py +60 -0
  18. ouroboros/config/__init__.py +81 -0
  19. ouroboros/config/loader.py +292 -0
  20. ouroboros/config/models.py +332 -0
  21. ouroboros/core/__init__.py +62 -0
  22. ouroboros/core/ac_tree.py +401 -0
  23. ouroboros/core/context.py +472 -0
  24. ouroboros/core/errors.py +246 -0
  25. ouroboros/core/seed.py +212 -0
  26. ouroboros/core/types.py +205 -0
  27. ouroboros/evaluation/__init__.py +110 -0
  28. ouroboros/evaluation/consensus.py +350 -0
  29. ouroboros/evaluation/mechanical.py +351 -0
  30. ouroboros/evaluation/models.py +235 -0
  31. ouroboros/evaluation/pipeline.py +286 -0
  32. ouroboros/evaluation/semantic.py +302 -0
  33. ouroboros/evaluation/trigger.py +278 -0
  34. ouroboros/events/__init__.py +5 -0
  35. ouroboros/events/base.py +80 -0
  36. ouroboros/events/decomposition.py +153 -0
  37. ouroboros/events/evaluation.py +248 -0
  38. ouroboros/execution/__init__.py +44 -0
  39. ouroboros/execution/atomicity.py +451 -0
  40. ouroboros/execution/decomposition.py +481 -0
  41. ouroboros/execution/double_diamond.py +1386 -0
  42. ouroboros/execution/subagent.py +275 -0
  43. ouroboros/observability/__init__.py +63 -0
  44. ouroboros/observability/drift.py +383 -0
  45. ouroboros/observability/logging.py +504 -0
  46. ouroboros/observability/retrospective.py +338 -0
  47. ouroboros/orchestrator/__init__.py +78 -0
  48. ouroboros/orchestrator/adapter.py +391 -0
  49. ouroboros/orchestrator/events.py +278 -0
  50. ouroboros/orchestrator/runner.py +597 -0
  51. ouroboros/orchestrator/session.py +486 -0
  52. ouroboros/persistence/__init__.py +23 -0
  53. ouroboros/persistence/checkpoint.py +511 -0
  54. ouroboros/persistence/event_store.py +183 -0
  55. ouroboros/persistence/migrations/__init__.py +1 -0
  56. ouroboros/persistence/migrations/runner.py +100 -0
  57. ouroboros/persistence/migrations/scripts/001_initial.sql +20 -0
  58. ouroboros/persistence/schema.py +56 -0
  59. ouroboros/persistence/uow.py +230 -0
  60. ouroboros/providers/__init__.py +28 -0
  61. ouroboros/providers/base.py +133 -0
  62. ouroboros/providers/claude_code_adapter.py +212 -0
  63. ouroboros/providers/litellm_adapter.py +316 -0
  64. ouroboros/py.typed +0 -0
  65. ouroboros/resilience/__init__.py +67 -0
  66. ouroboros/resilience/lateral.py +595 -0
  67. ouroboros/resilience/stagnation.py +727 -0
  68. ouroboros/routing/__init__.py +60 -0
  69. ouroboros/routing/complexity.py +272 -0
  70. ouroboros/routing/downgrade.py +664 -0
  71. ouroboros/routing/escalation.py +340 -0
  72. ouroboros/routing/router.py +204 -0
  73. ouroboros/routing/tiers.py +247 -0
  74. ouroboros/secondary/__init__.py +40 -0
  75. ouroboros/secondary/scheduler.py +467 -0
  76. ouroboros/secondary/todo_registry.py +483 -0
  77. ouroboros_ai-0.1.0.dist-info/METADATA +607 -0
  78. ouroboros_ai-0.1.0.dist-info/RECORD +81 -0
  79. ouroboros_ai-0.1.0.dist-info/WHEEL +4 -0
  80. ouroboros_ai-0.1.0.dist-info/entry_points.txt +2 -0
  81. ouroboros_ai-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,246 @@
1
+ """Error hierarchy for Ouroboros.
2
+
3
+ This module defines the exception hierarchy for Ouroboros. These exceptions
4
+ are used for unexpected errors (programming bugs) and as error types in
5
+ Result for expected failures.
6
+
7
+ Exception Hierarchy:
8
+ OuroborosError (base)
9
+ ├── ProviderError - LLM provider failures (rate limits, API errors)
10
+ ├── ConfigError - Configuration and credentials issues
11
+ ├── PersistenceError - Database and storage issues
12
+ └── ValidationError - Schema and data validation failures
13
+ """
14
+
15
+ from typing import Any
16
+
17
+
18
+ class OuroborosError(Exception):
19
+ """Base exception for all Ouroboros errors.
20
+
21
+ All Ouroboros-specific exceptions inherit from this class.
22
+ This allows catching all Ouroboros errors with a single except clause.
23
+
24
+ Attributes:
25
+ message: Human-readable error description.
26
+ details: Optional dict with additional context.
27
+ """
28
+
29
+ def __init__(self, message: str, details: dict[str, Any] | None = None) -> None:
30
+ """Initialize the error.
31
+
32
+ Args:
33
+ message: Human-readable error description.
34
+ details: Optional dict with additional context about the error.
35
+ """
36
+ super().__init__(message)
37
+ self.message = message
38
+ self.details = details or {}
39
+
40
+ def __str__(self) -> str:
41
+ """Return string representation of the error."""
42
+ if self.details:
43
+ return f"{self.message} (details: {self.details})"
44
+ return self.message
45
+
46
+
47
+ class ProviderError(OuroborosError):
48
+ """Error from LLM provider operations.
49
+
50
+ Raised when LLM provider calls fail (rate limits, API errors, timeouts).
51
+ Can be converted from provider-specific exceptions.
52
+
53
+ Attributes:
54
+ provider: Name of the provider (e.g., "openai", "anthropic").
55
+ status_code: HTTP status code if applicable.
56
+ """
57
+
58
+ def __init__(
59
+ self,
60
+ message: str,
61
+ *,
62
+ provider: str | None = None,
63
+ status_code: int | None = None,
64
+ details: dict[str, Any] | None = None,
65
+ ) -> None:
66
+ """Initialize provider error.
67
+
68
+ Args:
69
+ message: Human-readable error description.
70
+ provider: Name of the LLM provider.
71
+ status_code: HTTP status code if applicable.
72
+ details: Optional dict with additional context.
73
+ """
74
+ super().__init__(message, details)
75
+ self.provider = provider
76
+ self.status_code = status_code
77
+
78
+ @classmethod
79
+ def from_exception(
80
+ cls, exc: Exception, *, provider: str | None = None
81
+ ) -> ProviderError:
82
+ """Create ProviderError from a provider exception.
83
+
84
+ Args:
85
+ exc: The original exception from the provider.
86
+ provider: Name of the LLM provider.
87
+
88
+ Returns:
89
+ A ProviderError wrapping the original exception with __cause__ set
90
+ for proper traceback preservation.
91
+ """
92
+ status_code = getattr(exc, "status_code", None)
93
+ error = cls(
94
+ str(exc),
95
+ provider=provider,
96
+ status_code=status_code,
97
+ details={"original_exception": type(exc).__name__},
98
+ )
99
+ error.__cause__ = exc # Preserve original traceback
100
+ return error
101
+
102
+
103
+ class ConfigError(OuroborosError):
104
+ """Error from configuration operations.
105
+
106
+ Raised when configuration loading, parsing, or validation fails.
107
+
108
+ Attributes:
109
+ config_key: The configuration key that caused the error.
110
+ config_file: Path to the config file if applicable.
111
+ """
112
+
113
+ def __init__(
114
+ self,
115
+ message: str,
116
+ *,
117
+ config_key: str | None = None,
118
+ config_file: str | None = None,
119
+ details: dict[str, Any] | None = None,
120
+ ) -> None:
121
+ """Initialize config error.
122
+
123
+ Args:
124
+ message: Human-readable error description.
125
+ config_key: The configuration key that caused the error.
126
+ config_file: Path to the config file if applicable.
127
+ details: Optional dict with additional context.
128
+ """
129
+ super().__init__(message, details)
130
+ self.config_key = config_key
131
+ self.config_file = config_file
132
+
133
+
134
+ class PersistenceError(OuroborosError):
135
+ """Error from database and storage operations.
136
+
137
+ Raised when database queries, event storage, or checkpoint operations fail.
138
+
139
+ Attributes:
140
+ operation: The operation that failed (e.g., "insert", "query").
141
+ table: The database table involved if applicable.
142
+ """
143
+
144
+ def __init__(
145
+ self,
146
+ message: str,
147
+ *,
148
+ operation: str | None = None,
149
+ table: str | None = None,
150
+ details: dict[str, Any] | None = None,
151
+ ) -> None:
152
+ """Initialize persistence error.
153
+
154
+ Args:
155
+ message: Human-readable error description.
156
+ operation: The operation that failed.
157
+ table: The database table involved.
158
+ details: Optional dict with additional context.
159
+ """
160
+ super().__init__(message, details)
161
+ self.operation = operation
162
+ self.table = table
163
+
164
+
165
+ class ValidationError(OuroborosError):
166
+ """Error from data validation operations.
167
+
168
+ Raised when input data fails schema validation or business rule checks.
169
+
170
+ Attributes:
171
+ field: The field that failed validation.
172
+ value: The invalid value if safe to include.
173
+
174
+ Security Note:
175
+ Use safe_value property instead of value when logging to avoid
176
+ exposing sensitive data like API keys or credentials.
177
+ """
178
+
179
+ # Fields that should never have their values exposed
180
+ _SENSITIVE_FIELDS = frozenset({
181
+ "password", "api_key", "secret", "token", "credential",
182
+ "auth", "key", "private", "apikey", "api-key",
183
+ })
184
+
185
+ def __init__(
186
+ self,
187
+ message: str,
188
+ *,
189
+ field: str | None = None,
190
+ value: Any | None = None,
191
+ details: dict[str, Any] | None = None,
192
+ ) -> None:
193
+ """Initialize validation error.
194
+
195
+ Args:
196
+ message: Human-readable error description.
197
+ field: The field that failed validation.
198
+ value: The invalid value (only include if safe).
199
+ details: Optional dict with additional context.
200
+ """
201
+ super().__init__(message, details)
202
+ self.field = field
203
+ self.value = value
204
+
205
+ @property
206
+ def safe_value(self) -> str:
207
+ """Return a safe representation of the value for logging.
208
+
209
+ Masks potentially sensitive values based on field name or value type.
210
+ Always use this instead of value when logging or displaying errors.
211
+
212
+ Returns:
213
+ A safe string representation: masked for sensitive fields,
214
+ truncated for long values, or type info for complex objects.
215
+ """
216
+ if self.value is None:
217
+ return "<None>"
218
+
219
+ # Check if field name suggests sensitive data
220
+ if self.field:
221
+ field_lower = self.field.lower()
222
+ if any(sensitive in field_lower for sensitive in self._SENSITIVE_FIELDS):
223
+ return "<REDACTED>"
224
+
225
+ # Check if value looks like a secret (starts with common prefixes)
226
+ if isinstance(self.value, str):
227
+ value_str = self.value
228
+ secret_prefixes = ("sk-", "pk-", "api-", "bearer ", "token ", "secret_")
229
+ if any(value_str.lower().startswith(p) for p in secret_prefixes):
230
+ return "<REDACTED>"
231
+ # Truncate long strings
232
+ if len(value_str) > 50:
233
+ return f"{value_str[:20]}...({len(value_str)} chars)"
234
+ return repr(value_str)
235
+
236
+ # For other types, show type info
237
+ return f"<{type(self.value).__name__}>"
238
+
239
+ def __str__(self) -> str:
240
+ """Return string representation using safe_value."""
241
+ base = self.message
242
+ if self.field:
243
+ base = f"{base} (field: {self.field}, value: {self.safe_value})"
244
+ if self.details:
245
+ base = f"{base} (details: {self.details})"
246
+ return base
ouroboros/core/seed.py ADDED
@@ -0,0 +1,212 @@
1
+ """Immutable Seed schema for workflow execution.
2
+
3
+ The Seed is the "constitution" of a workflow - an immutable specification
4
+ generated from the Big Bang interview phase when ambiguity score <= 0.2.
5
+
6
+ Key properties:
7
+ - Seed.direction (goal, constraints, acceptance_criteria) is IMMUTABLE
8
+ - Effective ontology can evolve with consensus during iterations
9
+ - Contains all information needed to execute and evaluate the workflow
10
+
11
+ This module defines:
12
+ - Seed: The immutable Pydantic model with frozen=True
13
+ - SeedMetadata: Version and creation metadata
14
+ - Supporting types for seed components
15
+ """
16
+
17
+ from datetime import UTC, datetime
18
+ from typing import Any
19
+ from uuid import uuid4
20
+
21
+ from pydantic import BaseModel, Field
22
+
23
+
24
+ class ExitCondition(BaseModel, frozen=True):
25
+ """Defines when the workflow should terminate.
26
+
27
+ Attributes:
28
+ name: Short identifier for the condition.
29
+ description: Detailed explanation of the exit condition.
30
+ evaluation_criteria: How to determine if condition is met.
31
+ """
32
+
33
+ name: str = Field(..., min_length=1)
34
+ description: str = Field(..., min_length=1)
35
+ evaluation_criteria: str = Field(..., min_length=1)
36
+
37
+
38
+ class EvaluationPrinciple(BaseModel, frozen=True):
39
+ """A principle for evaluating workflow outputs.
40
+
41
+ Attributes:
42
+ name: Short identifier for the principle.
43
+ description: What this principle evaluates.
44
+ weight: Relative importance (0.0 to 1.0).
45
+ """
46
+
47
+ name: str = Field(..., min_length=1)
48
+ description: str = Field(..., min_length=1)
49
+ weight: float = Field(default=1.0, ge=0.0, le=1.0)
50
+
51
+
52
+ class OntologyField(BaseModel, frozen=True):
53
+ """A field in the ontology schema.
54
+
55
+ Attributes:
56
+ name: Field identifier.
57
+ field_type: Type of the field (string, number, boolean, array, object).
58
+ description: Purpose of this field.
59
+ required: Whether this field is required.
60
+ """
61
+
62
+ name: str = Field(..., min_length=1)
63
+ field_type: str = Field(..., min_length=1)
64
+ description: str = Field(..., min_length=1)
65
+ required: bool = Field(default=True)
66
+
67
+
68
+ class OntologySchema(BaseModel, frozen=True):
69
+ """Schema defining the structure of workflow outputs.
70
+
71
+ The ontology schema defines what data structure the workflow should
72
+ produce and maintain throughout iterations.
73
+
74
+ Attributes:
75
+ name: Name of the ontology.
76
+ description: Purpose and scope of this ontology.
77
+ fields: List of fields in the ontology.
78
+ """
79
+
80
+ name: str = Field(..., min_length=1)
81
+ description: str = Field(..., min_length=1)
82
+ fields: tuple[OntologyField, ...] = Field(default_factory=tuple)
83
+
84
+
85
+ class SeedMetadata(BaseModel, frozen=True):
86
+ """Metadata about the Seed generation.
87
+
88
+ Attributes:
89
+ seed_id: Unique identifier for this seed.
90
+ version: Schema version for forward compatibility.
91
+ created_at: When this seed was generated.
92
+ ambiguity_score: The ambiguity score at generation time.
93
+ interview_id: Reference to the source interview.
94
+ """
95
+
96
+ seed_id: str = Field(default_factory=lambda: f"seed_{uuid4().hex[:12]}")
97
+ version: str = Field(default="1.0.0")
98
+ created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
99
+ ambiguity_score: float = Field(..., ge=0.0, le=1.0)
100
+ interview_id: str | None = Field(default=None)
101
+
102
+
103
+ class Seed(BaseModel, frozen=True):
104
+ """Immutable specification for workflow execution.
105
+
106
+ The Seed is the "constitution" of the workflow - once generated, it cannot
107
+ be modified. This ensures consistency throughout the workflow lifecycle.
108
+
109
+ Direction (goal, constraints, acceptance_criteria) is IMMUTABLE:
110
+ - These define WHAT the workflow should achieve
111
+ - Cannot be changed after generation
112
+ - Serves as the ground truth for evaluation
113
+
114
+ Attributes:
115
+ goal: The primary objective of the workflow.
116
+ constraints: Hard constraints that must be satisfied.
117
+ acceptance_criteria: Specific criteria for success.
118
+ ontology_schema: Schema for workflow output structure.
119
+ evaluation_principles: Principles for evaluating outputs.
120
+ exit_conditions: Conditions for terminating the workflow.
121
+ metadata: Generation metadata (version, timestamp, etc.).
122
+
123
+ Example:
124
+ seed = Seed(
125
+ goal="Build a CLI task management tool",
126
+ constraints=("Python 3.14+", "No external database"),
127
+ acceptance_criteria=("Tasks can be created", "Tasks can be listed"),
128
+ ontology_schema=OntologySchema(
129
+ name="TaskManager",
130
+ description="Task management ontology",
131
+ fields=(
132
+ OntologyField(
133
+ name="tasks",
134
+ field_type="array",
135
+ description="List of tasks",
136
+ ),
137
+ ),
138
+ ),
139
+ evaluation_principles=(
140
+ EvaluationPrinciple(
141
+ name="completeness",
142
+ description="All requirements are met",
143
+ ),
144
+ ),
145
+ exit_conditions=(
146
+ ExitCondition(
147
+ name="all_criteria_met",
148
+ description="All acceptance criteria satisfied",
149
+ evaluation_criteria="100% criteria pass",
150
+ ),
151
+ ),
152
+ metadata=SeedMetadata(ambiguity_score=0.15),
153
+ )
154
+
155
+ # Attempting to modify raises an error:
156
+ seed.goal = "New goal" # Raises ValidationError (frozen)
157
+ """
158
+
159
+ # Direction - IMMUTABLE
160
+ goal: str = Field(..., min_length=1, description="Primary objective of the workflow")
161
+ constraints: tuple[str, ...] = Field(
162
+ default_factory=tuple,
163
+ description="Hard constraints that must be satisfied",
164
+ )
165
+ acceptance_criteria: tuple[str, ...] = Field(
166
+ default_factory=tuple,
167
+ description="Specific criteria for success evaluation",
168
+ )
169
+
170
+ # Structure
171
+ ontology_schema: OntologySchema = Field(
172
+ ...,
173
+ description="Schema defining the structure of workflow outputs",
174
+ )
175
+
176
+ # Evaluation
177
+ evaluation_principles: tuple[EvaluationPrinciple, ...] = Field(
178
+ default_factory=tuple,
179
+ description="Principles for evaluating workflow outputs",
180
+ )
181
+
182
+ # Termination
183
+ exit_conditions: tuple[ExitCondition, ...] = Field(
184
+ default_factory=tuple,
185
+ description="Conditions for terminating the workflow",
186
+ )
187
+
188
+ # Metadata
189
+ metadata: SeedMetadata = Field(
190
+ ...,
191
+ description="Generation metadata (version, timestamp, etc.)",
192
+ )
193
+
194
+ def to_dict(self) -> dict[str, Any]:
195
+ """Convert seed to dictionary for serialization.
196
+
197
+ Returns:
198
+ Dictionary representation of the seed.
199
+ """
200
+ return self.model_dump(mode="json")
201
+
202
+ @classmethod
203
+ def from_dict(cls, data: dict[str, Any]) -> "Seed":
204
+ """Create seed from dictionary.
205
+
206
+ Args:
207
+ data: Dictionary representation of the seed.
208
+
209
+ Returns:
210
+ Seed instance.
211
+ """
212
+ return cls.model_validate(data)
@@ -0,0 +1,205 @@
1
+ """Core types for Ouroboros - Result type and type aliases.
2
+
3
+ This module provides:
4
+ - Result[T, E]: A generic type for handling expected failures without exceptions
5
+ - Type aliases for common domain types
6
+ """
7
+
8
+ from collections.abc import Callable
9
+ from dataclasses import dataclass
10
+ from typing import Any, cast
11
+
12
+
13
+ @dataclass(frozen=True, slots=True)
14
+ class Result[T, E]:
15
+ """A type that represents either success (Ok) or failure (Err).
16
+
17
+ Result is used for expected failures (rate limits, API errors, validation failures)
18
+ instead of exceptions. Exceptions are reserved for programming errors (bugs).
19
+
20
+ Usage:
21
+ # Construction
22
+ ok_result: Result[int, str] = Result.ok(42)
23
+ err_result: Result[int, str] = Result.err("something went wrong")
24
+
25
+ # Pattern matching
26
+ if result.is_ok:
27
+ process(result.value)
28
+ else:
29
+ handle_error(result.error)
30
+
31
+ # Transform values
32
+ mapped = result.map(lambda x: x * 2)
33
+ mapped_err = result.map_err(lambda e: CustomError(e))
34
+
35
+ # Extract values
36
+ value = result.unwrap() # Raises if Err
37
+ value = result.unwrap_or(default) # Returns default if Err
38
+ """
39
+
40
+ _value: T | None
41
+ _error: E | None
42
+ _is_ok: bool
43
+
44
+ @classmethod
45
+ def ok(cls, value: T) -> Result[T, E]:
46
+ """Create a successful Result containing the given value.
47
+
48
+ Args:
49
+ value: The success value to wrap.
50
+
51
+ Returns:
52
+ A Result in the Ok state.
53
+ """
54
+ return cls(_value=value, _error=None, _is_ok=True)
55
+
56
+ @classmethod
57
+ def err(cls, error: E) -> Result[T, E]:
58
+ """Create a failed Result containing the given error.
59
+
60
+ Args:
61
+ error: The error value to wrap.
62
+
63
+ Returns:
64
+ A Result in the Err state.
65
+ """
66
+ return cls(_value=None, _error=error, _is_ok=False)
67
+
68
+ @property
69
+ def is_ok(self) -> bool:
70
+ """Return True if this Result is Ok (success)."""
71
+ return self._is_ok
72
+
73
+ @property
74
+ def is_err(self) -> bool:
75
+ """Return True if this Result is Err (failure)."""
76
+ return not self._is_ok
77
+
78
+ def __repr__(self) -> str:
79
+ """Return a semantic string representation of the Result.
80
+
81
+ Returns:
82
+ 'Ok(value)' for success, 'Err(error)' for failure.
83
+ """
84
+ if self._is_ok:
85
+ return f"Ok({self._value!r})"
86
+ return f"Err({self._error!r})"
87
+
88
+ @property
89
+ def value(self) -> T:
90
+ """Return the Ok value.
91
+
92
+ Note: Only access this when is_ok is True.
93
+ For safe access, use unwrap() or unwrap_or().
94
+ """
95
+ if not self._is_ok:
96
+ msg = "Cannot access value on Err result"
97
+ raise ValueError(msg)
98
+ return cast(T, self._value)
99
+
100
+ @property
101
+ def error(self) -> E:
102
+ """Return the Err value.
103
+
104
+ Note: Only access this when is_err is True.
105
+ """
106
+ if self._is_ok:
107
+ msg = "Cannot access error on Ok result"
108
+ raise ValueError(msg)
109
+ return cast(E, self._error)
110
+
111
+ def unwrap(self) -> T:
112
+ """Return the Ok value or raise ValueError if Err.
113
+
114
+ Raises:
115
+ ValueError: If this Result is Err, with the error as the message.
116
+
117
+ Returns:
118
+ The success value.
119
+ """
120
+ if self._is_ok:
121
+ return cast(T, self._value)
122
+ raise ValueError(str(self._error))
123
+
124
+ def unwrap_or(self, default: T) -> T:
125
+ """Return the Ok value or the provided default if Err.
126
+
127
+ Args:
128
+ default: Value to return if this Result is Err.
129
+
130
+ Returns:
131
+ The success value if Ok, otherwise the default.
132
+ """
133
+ if self._is_ok:
134
+ return cast(T, self._value)
135
+ return default
136
+
137
+ def map[U](self, fn: Callable[[T], U]) -> Result[U, E]:
138
+ """Transform the Ok value using the given function.
139
+
140
+ If this Result is Ok, apply fn to the value and return Ok(fn(value)).
141
+ If this Result is Err, return Err unchanged.
142
+
143
+ Args:
144
+ fn: Function to apply to the Ok value.
145
+
146
+ Returns:
147
+ A new Result with the transformed value or the original error.
148
+ """
149
+ if self._is_ok:
150
+ return Result.ok(fn(cast(T, self._value)))
151
+ return Result.err(cast(E, self._error))
152
+
153
+ def map_err[F](self, fn: Callable[[E], F]) -> Result[T, F]:
154
+ """Transform the Err value using the given function.
155
+
156
+ If this Result is Err, apply fn to the error and return Err(fn(error)).
157
+ If this Result is Ok, return Ok unchanged.
158
+
159
+ Args:
160
+ fn: Function to apply to the Err value.
161
+
162
+ Returns:
163
+ A new Result with the original value or the transformed error.
164
+ """
165
+ if self._is_ok:
166
+ return Result.ok(cast(T, self._value))
167
+ return Result.err(fn(cast(E, self._error)))
168
+
169
+ def and_then[U](self, fn: Callable[[T], "Result[U, E]"]) -> "Result[U, E]":
170
+ """Chain Result-producing operations (flatMap/bind).
171
+
172
+ If this Result is Ok, apply fn to the value and return the result.
173
+ If this Result is Err, return Err unchanged.
174
+
175
+ This is useful for chaining operations that may fail.
176
+
177
+ Args:
178
+ fn: Function that takes the Ok value and returns a new Result.
179
+
180
+ Returns:
181
+ The result of fn if Ok, or the original Err.
182
+
183
+ Example:
184
+ def divide(a: int, b: int) -> Result[int, str]:
185
+ if b == 0:
186
+ return Result.err("division by zero")
187
+ return Result.ok(a // b)
188
+
189
+ result = Result.ok(10).and_then(lambda x: divide(x, 2))
190
+ # Returns Ok(5)
191
+ """
192
+ if self._is_ok:
193
+ return fn(cast(T, self._value))
194
+ return Result.err(cast(E, self._error))
195
+
196
+
197
+ # Type aliases for common domain types
198
+ EventPayload = dict[str, Any]
199
+ """Type alias for event payload data - arbitrary JSON-serializable dict."""
200
+
201
+ CostUnits = int
202
+ """Type alias for cost tracking - integer units (e.g., token counts)."""
203
+
204
+ DriftScore = float
205
+ """Type alias for drift measurement - float between 0.0 and 1.0."""