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,338 @@
1
+ """Automatic retrospective for Ouroboros.
2
+
3
+ This module implements Story 6.2: Automatic Retrospective.
4
+
5
+ Retrospectives run periodically (every 3 iterations by default) to:
6
+ 1. Compare current state to original Seed
7
+ 2. Analyze drift components
8
+ 3. Generate course correction recommendations
9
+ 4. Notify humans if drift is high
10
+
11
+ The retrospective is part of Phase 3 resilience, preventing accumulated drift.
12
+
13
+ Usage:
14
+ from ouroboros.observability.retrospective import (
15
+ RetrospectiveAnalyzer,
16
+ should_trigger_retrospective,
17
+ )
18
+
19
+ if should_trigger_retrospective(iteration=3):
20
+ analyzer = RetrospectiveAnalyzer()
21
+ result = analyzer.analyze(
22
+ seed=seed,
23
+ current_output="...",
24
+ constraint_violations=[],
25
+ current_concepts=[],
26
+ iteration=3,
27
+ execution_id="exec-123",
28
+ )
29
+ if result.requires_human_attention:
30
+ # Notify human
31
+ pass
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ from dataclasses import dataclass, field
37
+
38
+ from ouroboros.core.seed import Seed
39
+ from ouroboros.events.base import BaseEvent
40
+ from ouroboros.observability.drift import (
41
+ DRIFT_THRESHOLD,
42
+ DriftMeasurement,
43
+ DriftMetrics,
44
+ )
45
+ from ouroboros.observability.logging import get_logger
46
+
47
+ log = get_logger(__name__)
48
+
49
+ # =============================================================================
50
+ # Constants
51
+ # =============================================================================
52
+
53
+ # Default retrospective interval (every N iterations)
54
+ DEFAULT_RETROSPECTIVE_INTERVAL = 3
55
+
56
+ # Threshold for requiring human attention (default same as drift threshold)
57
+ DEFAULT_NOTIFICATION_THRESHOLD = DRIFT_THRESHOLD
58
+
59
+
60
+ # =============================================================================
61
+ # Data Models
62
+ # =============================================================================
63
+
64
+
65
+ @dataclass(frozen=True, slots=True)
66
+ class RetrospectiveResult:
67
+ """Immutable result of a retrospective analysis.
68
+
69
+ Attributes:
70
+ execution_id: Execution identifier
71
+ iteration: Iteration number when retrospective was run
72
+ drift_metrics: Measured drift from seed
73
+ recommendations: List of course correction recommendations
74
+ needs_correction: Whether correction is recommended
75
+ notification_threshold: Threshold for human notification
76
+ """
77
+
78
+ execution_id: str
79
+ iteration: int
80
+ drift_metrics: DriftMetrics
81
+ recommendations: list[str] = field(default_factory=list)
82
+ needs_correction: bool = False
83
+ notification_threshold: float = DEFAULT_NOTIFICATION_THRESHOLD
84
+
85
+ @property
86
+ def requires_human_attention(self) -> bool:
87
+ """Check if human attention is required.
88
+
89
+ Returns True if combined drift exceeds notification threshold.
90
+
91
+ Returns:
92
+ True if human should be notified
93
+ """
94
+ return self.drift_metrics.combined_drift > self.notification_threshold
95
+
96
+
97
+ # =============================================================================
98
+ # Trigger Logic
99
+ # =============================================================================
100
+
101
+
102
+ def should_trigger_retrospective(
103
+ iteration: int,
104
+ interval: int = DEFAULT_RETROSPECTIVE_INTERVAL,
105
+ ) -> bool:
106
+ """Check if retrospective should run at this iteration.
107
+
108
+ Retrospectives trigger at multiples of the interval (default: 3).
109
+ First retrospective runs after iteration 3 completes.
110
+
111
+ Args:
112
+ iteration: Current iteration number (1-indexed)
113
+ interval: How often to run retrospective (default: 3)
114
+
115
+ Returns:
116
+ True if retrospective should run
117
+ """
118
+ if iteration <= 0:
119
+ return False
120
+ return iteration % interval == 0
121
+
122
+
123
+ # =============================================================================
124
+ # Retrospective Analyzer
125
+ # =============================================================================
126
+
127
+
128
+ class RetrospectiveAnalyzer:
129
+ """Analyzer for periodic retrospectives.
130
+
131
+ Compares current state to original Seed and generates
132
+ course correction recommendations if needed.
133
+
134
+ Stateless - all state passed via parameters.
135
+ """
136
+
137
+ def __init__(
138
+ self,
139
+ notification_threshold: float = DEFAULT_NOTIFICATION_THRESHOLD,
140
+ ) -> None:
141
+ """Initialize analyzer.
142
+
143
+ Args:
144
+ notification_threshold: Drift threshold for human notification
145
+ """
146
+ self._notification_threshold = notification_threshold
147
+ self._drift_measurement = DriftMeasurement()
148
+
149
+ def analyze(
150
+ self,
151
+ seed: Seed,
152
+ current_output: str,
153
+ constraint_violations: list[str],
154
+ current_concepts: list[str],
155
+ iteration: int,
156
+ execution_id: str,
157
+ ) -> RetrospectiveResult:
158
+ """Analyze current state against original seed.
159
+
160
+ Measures drift and generates recommendations for correction.
161
+
162
+ Args:
163
+ seed: Original seed specification
164
+ current_output: Current execution output
165
+ constraint_violations: List of constraint violations
166
+ current_concepts: Current ontology concepts
167
+ iteration: Current iteration number
168
+ execution_id: Execution identifier
169
+
170
+ Returns:
171
+ RetrospectiveResult with analysis and recommendations
172
+ """
173
+ # Measure drift from seed
174
+ drift_metrics = self._drift_measurement.measure(
175
+ current_output=current_output,
176
+ constraint_violations=constraint_violations,
177
+ current_concepts=current_concepts,
178
+ seed=seed,
179
+ )
180
+
181
+ # Generate recommendations based on drift
182
+ recommendations = self._generate_recommendations(
183
+ drift_metrics=drift_metrics,
184
+ seed=seed,
185
+ constraint_violations=constraint_violations,
186
+ )
187
+
188
+ # Determine if correction is needed
189
+ needs_correction = (
190
+ not drift_metrics.is_acceptable
191
+ or len(constraint_violations) > 0
192
+ or len(recommendations) > 0
193
+ )
194
+
195
+ return RetrospectiveResult(
196
+ execution_id=execution_id,
197
+ iteration=iteration,
198
+ drift_metrics=drift_metrics,
199
+ recommendations=recommendations,
200
+ needs_correction=needs_correction,
201
+ notification_threshold=self._notification_threshold,
202
+ )
203
+
204
+ def _generate_recommendations(
205
+ self,
206
+ drift_metrics: DriftMetrics,
207
+ seed: Seed,
208
+ constraint_violations: list[str],
209
+ ) -> list[str]:
210
+ """Generate course correction recommendations.
211
+
212
+ Args:
213
+ drift_metrics: Measured drift metrics
214
+ seed: Original seed for reference
215
+ constraint_violations: Current constraint violations
216
+
217
+ Returns:
218
+ List of recommendation strings
219
+ """
220
+ recommendations: list[str] = []
221
+
222
+ # High goal drift
223
+ if drift_metrics.goal_drift > DRIFT_THRESHOLD:
224
+ # Truncate goal at word boundary
225
+ goal_preview = seed.goal[:100]
226
+ if len(seed.goal) > 100:
227
+ last_space = goal_preview.rfind(" ")
228
+ if last_space > 50:
229
+ goal_preview = goal_preview[:last_space]
230
+ goal_preview += "..."
231
+ recommendations.append(
232
+ f"High goal drift detected ({drift_metrics.goal_drift:.2f}). "
233
+ f"Refocus on original goal: '{goal_preview}'"
234
+ )
235
+
236
+ # Constraint violations
237
+ if constraint_violations:
238
+ recommendations.append(
239
+ f"Constraint violations detected ({len(constraint_violations)}). "
240
+ "Review and address violations to maintain compliance."
241
+ )
242
+
243
+ # Ontology drift
244
+ if drift_metrics.ontology_drift > DRIFT_THRESHOLD:
245
+ recommendations.append(
246
+ f"Ontology drift detected ({drift_metrics.ontology_drift:.2f}). "
247
+ "Concepts have evolved significantly from original schema. "
248
+ "Consider realigning with seed ontology."
249
+ )
250
+
251
+ # Combined drift exceeds threshold
252
+ if drift_metrics.combined_drift > DRIFT_THRESHOLD:
253
+ recommendations.append(
254
+ f"Combined drift ({drift_metrics.combined_drift:.2f}) exceeds "
255
+ f"threshold ({DRIFT_THRESHOLD}). Major course correction recommended."
256
+ )
257
+
258
+ return recommendations
259
+
260
+
261
+ # =============================================================================
262
+ # Event Definitions
263
+ # =============================================================================
264
+
265
+
266
+ class RetrospectiveCompletedEvent(BaseEvent):
267
+ """Event emitted when a retrospective analysis completes.
268
+
269
+ Logs the retrospective results for audit trail.
270
+ """
271
+
272
+ def __init__(
273
+ self,
274
+ execution_id: str,
275
+ seed_id: str,
276
+ result: RetrospectiveResult,
277
+ ) -> None:
278
+ """Create RetrospectiveCompletedEvent.
279
+
280
+ Args:
281
+ execution_id: Unique execution identifier
282
+ seed_id: Seed identifier being analyzed against
283
+ result: Retrospective analysis result
284
+ """
285
+ super().__init__(
286
+ type="observability.retrospective.completed",
287
+ aggregate_type="execution",
288
+ aggregate_id=execution_id,
289
+ data={
290
+ "seed_id": seed_id,
291
+ "iteration": result.iteration,
292
+ "goal_drift": result.drift_metrics.goal_drift,
293
+ "constraint_drift": result.drift_metrics.constraint_drift,
294
+ "ontology_drift": result.drift_metrics.ontology_drift,
295
+ "combined_drift": result.drift_metrics.combined_drift,
296
+ "needs_correction": result.needs_correction,
297
+ "recommendations_count": len(result.recommendations),
298
+ "requires_human_attention": result.requires_human_attention,
299
+ },
300
+ )
301
+
302
+
303
+ class HumanAttentionRequiredEvent(BaseEvent):
304
+ """Event emitted when human attention is required.
305
+
306
+ Indicates high drift that needs manual intervention.
307
+ """
308
+
309
+ def __init__(
310
+ self,
311
+ execution_id: str,
312
+ seed_id: str,
313
+ result: RetrospectiveResult,
314
+ reason: str,
315
+ ) -> None:
316
+ """Create HumanAttentionRequiredEvent.
317
+
318
+ Args:
319
+ execution_id: Unique execution identifier
320
+ seed_id: Seed identifier
321
+ result: Retrospective analysis result
322
+ reason: Why human attention is needed
323
+ """
324
+ super().__init__(
325
+ type="observability.retrospective.human_attention_required",
326
+ aggregate_type="execution",
327
+ aggregate_id=execution_id,
328
+ data={
329
+ "seed_id": seed_id,
330
+ "iteration": result.iteration,
331
+ "goal_drift": result.drift_metrics.goal_drift,
332
+ "constraint_drift": result.drift_metrics.constraint_drift,
333
+ "ontology_drift": result.drift_metrics.ontology_drift,
334
+ "combined_drift": result.drift_metrics.combined_drift,
335
+ "reason": reason,
336
+ "recommendations": result.recommendations,
337
+ },
338
+ )
@@ -0,0 +1,78 @@
1
+ """Orchestrator module for Claude Agent SDK integration.
2
+
3
+ This module provides Epic 8 functionality - executing Ouroboros workflows
4
+ via Claude Agent SDK as an alternative execution mode.
5
+
6
+ Key Components:
7
+ - ClaudeAgentAdapter: Wrapper for Claude Agent SDK with streaming support
8
+ - SessionTracker: Immutable session state tracking
9
+ - SessionRepository: Event-based session persistence
10
+ - OrchestratorRunner: Main orchestration logic
11
+
12
+ Usage:
13
+ from ouroboros.orchestrator import ClaudeAgentAdapter, OrchestratorRunner
14
+
15
+ adapter = ClaudeAgentAdapter()
16
+ runner = OrchestratorRunner(adapter, event_store)
17
+ result = await runner.execute_seed(seed, execution_id)
18
+
19
+ CLI Usage:
20
+ ouroboros run --orchestrator seed.yaml
21
+ ouroboros run --orchestrator seed.yaml --resume <session_id>
22
+ """
23
+
24
+ from ouroboros.orchestrator.adapter import (
25
+ DEFAULT_TOOLS,
26
+ AgentMessage,
27
+ ClaudeAgentAdapter,
28
+ TaskResult,
29
+ )
30
+ from ouroboros.orchestrator.events import (
31
+ create_progress_event,
32
+ create_session_completed_event,
33
+ create_session_failed_event,
34
+ create_session_paused_event,
35
+ create_session_started_event,
36
+ create_task_completed_event,
37
+ create_task_started_event,
38
+ create_tool_called_event,
39
+ )
40
+ from ouroboros.orchestrator.runner import (
41
+ OrchestratorError,
42
+ OrchestratorResult,
43
+ OrchestratorRunner,
44
+ build_system_prompt,
45
+ build_task_prompt,
46
+ )
47
+ from ouroboros.orchestrator.session import (
48
+ SessionRepository,
49
+ SessionStatus,
50
+ SessionTracker,
51
+ )
52
+
53
+ __all__ = [
54
+ # Adapter
55
+ "AgentMessage",
56
+ "ClaudeAgentAdapter",
57
+ "DEFAULT_TOOLS",
58
+ "TaskResult",
59
+ # Session
60
+ "SessionRepository",
61
+ "SessionStatus",
62
+ "SessionTracker",
63
+ # Runner
64
+ "OrchestratorError",
65
+ "OrchestratorResult",
66
+ "OrchestratorRunner",
67
+ "build_system_prompt",
68
+ "build_task_prompt",
69
+ # Events
70
+ "create_progress_event",
71
+ "create_session_completed_event",
72
+ "create_session_failed_event",
73
+ "create_session_paused_event",
74
+ "create_session_started_event",
75
+ "create_task_completed_event",
76
+ "create_task_started_event",
77
+ "create_tool_called_event",
78
+ ]