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,292 @@
1
+ """Configuration loading and management for Ouroboros.
2
+
3
+ This module provides functions for loading, creating, and validating
4
+ Ouroboros configuration files.
5
+
6
+ Functions:
7
+ load_config: Load configuration from ~/.ouroboros/config.yaml
8
+ load_credentials: Load credentials from ~/.ouroboros/credentials.yaml
9
+ create_default_config: Create default configuration files
10
+ ensure_config_dir: Ensure ~/.ouroboros/ directory exists
11
+ """
12
+
13
+ import os
14
+ from pathlib import Path
15
+ import stat
16
+ from typing import Any
17
+
18
+ from pydantic import ValidationError as PydanticValidationError
19
+ import yaml
20
+
21
+ from ouroboros.config.models import (
22
+ CredentialsConfig,
23
+ OuroborosConfig,
24
+ get_config_dir,
25
+ get_default_config,
26
+ get_default_credentials,
27
+ )
28
+ from ouroboros.core.errors import ConfigError
29
+
30
+
31
+ def ensure_config_dir() -> Path:
32
+ """Ensure the configuration directory exists.
33
+
34
+ Creates ~/.ouroboros/ directory and subdirectories if they don't exist.
35
+
36
+ Returns:
37
+ Path to the configuration directory.
38
+ """
39
+ config_dir = get_config_dir()
40
+ config_dir.mkdir(parents=True, exist_ok=True)
41
+
42
+ # Create subdirectories
43
+ (config_dir / "data").mkdir(exist_ok=True)
44
+ (config_dir / "logs").mkdir(exist_ok=True)
45
+
46
+ return config_dir
47
+
48
+
49
+ def _set_secure_permissions(file_path: Path) -> None:
50
+ """Set secure permissions (chmod 600) on a file.
51
+
52
+ Args:
53
+ file_path: Path to the file to secure.
54
+ """
55
+ # Set permissions to owner read/write only (0o600)
56
+ os.chmod(file_path, stat.S_IRUSR | stat.S_IWUSR)
57
+
58
+
59
+ def _model_to_yaml_dict(model: OuroborosConfig | CredentialsConfig) -> dict[str, Any]:
60
+ """Convert a Pydantic model to a YAML-serializable dict.
61
+
62
+ Args:
63
+ model: The Pydantic model to convert.
64
+
65
+ Returns:
66
+ A dict suitable for YAML serialization.
67
+ """
68
+ return model.model_dump(mode="json")
69
+
70
+
71
+ def create_default_config(
72
+ config_dir: Path | None = None,
73
+ *,
74
+ overwrite: bool = False,
75
+ ) -> tuple[Path, Path]:
76
+ """Create default configuration files.
77
+
78
+ Creates config.yaml and credentials.yaml with default templates
79
+ in the specified directory. credentials.yaml is created with
80
+ chmod 600 permissions for security.
81
+
82
+ Args:
83
+ config_dir: Directory to create files in. Defaults to ~/.ouroboros/
84
+ overwrite: If True, overwrite existing files. Defaults to False.
85
+
86
+ Returns:
87
+ Tuple of (config_path, credentials_path).
88
+
89
+ Raises:
90
+ ConfigError: If files exist and overwrite=False.
91
+ """
92
+ if config_dir is None:
93
+ config_dir = ensure_config_dir()
94
+ else:
95
+ config_dir.mkdir(parents=True, exist_ok=True)
96
+ (config_dir / "data").mkdir(exist_ok=True)
97
+ (config_dir / "logs").mkdir(exist_ok=True)
98
+
99
+ config_path = config_dir / "config.yaml"
100
+ credentials_path = config_dir / "credentials.yaml"
101
+
102
+ # Check if files exist
103
+ if not overwrite:
104
+ if config_path.exists():
105
+ raise ConfigError(
106
+ f"Configuration file already exists: {config_path}",
107
+ config_file=str(config_path),
108
+ )
109
+ if credentials_path.exists():
110
+ raise ConfigError(
111
+ f"Credentials file already exists: {credentials_path}",
112
+ config_file=str(credentials_path),
113
+ )
114
+
115
+ # Create config.yaml
116
+ default_config = get_default_config()
117
+ config_dict = _model_to_yaml_dict(default_config)
118
+ with config_path.open("w") as f:
119
+ yaml.dump(
120
+ config_dict,
121
+ f,
122
+ default_flow_style=False,
123
+ sort_keys=False,
124
+ allow_unicode=True,
125
+ )
126
+
127
+ # Create credentials.yaml with secure permissions
128
+ default_credentials = get_default_credentials()
129
+ credentials_dict = _model_to_yaml_dict(default_credentials)
130
+ with credentials_path.open("w") as f:
131
+ yaml.dump(
132
+ credentials_dict,
133
+ f,
134
+ default_flow_style=False,
135
+ sort_keys=False,
136
+ allow_unicode=True,
137
+ )
138
+
139
+ # Set chmod 600 on credentials file
140
+ _set_secure_permissions(credentials_path)
141
+
142
+ return config_path, credentials_path
143
+
144
+
145
+ def load_config(config_path: Path | None = None) -> OuroborosConfig:
146
+ """Load configuration from YAML file.
147
+
148
+ Loads and validates configuration from the specified path or
149
+ the default ~/.ouroboros/config.yaml.
150
+
151
+ Args:
152
+ config_path: Path to config file. Defaults to ~/.ouroboros/config.yaml.
153
+
154
+ Returns:
155
+ Validated OuroborosConfig instance.
156
+
157
+ Raises:
158
+ ConfigError: If file doesn't exist, is malformed, or fails validation.
159
+ """
160
+ if config_path is None:
161
+ config_path = get_config_dir() / "config.yaml"
162
+
163
+ if not config_path.exists():
164
+ raise ConfigError(
165
+ f"Configuration file not found: {config_path}. "
166
+ "Run `ouroboros config init` to create default configuration.",
167
+ config_file=str(config_path),
168
+ )
169
+
170
+ try:
171
+ with config_path.open() as f:
172
+ config_dict = yaml.safe_load(f)
173
+ except yaml.YAMLError as e:
174
+ raise ConfigError(
175
+ f"Failed to parse configuration file: {e}",
176
+ config_file=str(config_path),
177
+ details={"yaml_error": str(e)},
178
+ ) from e
179
+
180
+ if config_dict is None:
181
+ config_dict = {}
182
+
183
+ try:
184
+ return OuroborosConfig.model_validate(config_dict)
185
+ except PydanticValidationError as e:
186
+ # Format validation errors for clarity
187
+ error_messages = []
188
+ for error in e.errors():
189
+ loc = ".".join(str(x) for x in error["loc"])
190
+ msg = error["msg"]
191
+ error_messages.append(f" - {loc}: {msg}")
192
+
193
+ raise ConfigError(
194
+ "Configuration validation failed:\n" + "\n".join(error_messages),
195
+ config_file=str(config_path),
196
+ details={"validation_errors": e.errors()},
197
+ ) from e
198
+
199
+
200
+ def load_credentials(credentials_path: Path | None = None) -> CredentialsConfig:
201
+ """Load credentials from YAML file.
202
+
203
+ Loads and validates credentials from the specified path or
204
+ the default ~/.ouroboros/credentials.yaml.
205
+
206
+ Args:
207
+ credentials_path: Path to credentials file.
208
+ Defaults to ~/.ouroboros/credentials.yaml.
209
+
210
+ Returns:
211
+ Validated CredentialsConfig instance.
212
+
213
+ Raises:
214
+ ConfigError: If file doesn't exist, is malformed, or fails validation.
215
+ """
216
+ if credentials_path is None:
217
+ credentials_path = get_config_dir() / "credentials.yaml"
218
+
219
+ if not credentials_path.exists():
220
+ raise ConfigError(
221
+ f"Credentials file not found: {credentials_path}. "
222
+ "Run `ouroboros config init` to create default configuration.",
223
+ config_file=str(credentials_path),
224
+ )
225
+
226
+ # Check file permissions (warn if too permissive)
227
+ file_mode = credentials_path.stat().st_mode
228
+ if file_mode & (stat.S_IRGRP | stat.S_IROTH):
229
+ # File is readable by group or others - this is a security warning
230
+ # We don't raise an error, but this could be logged
231
+ pass
232
+
233
+ try:
234
+ with credentials_path.open() as f:
235
+ credentials_dict = yaml.safe_load(f)
236
+ except yaml.YAMLError as e:
237
+ raise ConfigError(
238
+ f"Failed to parse credentials file: {e}",
239
+ config_file=str(credentials_path),
240
+ details={"yaml_error": str(e)},
241
+ ) from e
242
+
243
+ if credentials_dict is None:
244
+ credentials_dict = {}
245
+
246
+ try:
247
+ return CredentialsConfig.model_validate(credentials_dict)
248
+ except PydanticValidationError as e:
249
+ error_messages = []
250
+ for error in e.errors():
251
+ loc = ".".join(str(x) for x in error["loc"])
252
+ msg = error["msg"]
253
+ error_messages.append(f" - {loc}: {msg}")
254
+
255
+ raise ConfigError(
256
+ "Credentials validation failed:\n" + "\n".join(error_messages),
257
+ config_file=str(credentials_path),
258
+ details={"validation_errors": e.errors()},
259
+ ) from e
260
+
261
+
262
+ def config_exists() -> bool:
263
+ """Check if configuration files exist.
264
+
265
+ Returns:
266
+ True if both config.yaml and credentials.yaml exist.
267
+ """
268
+ config_dir = get_config_dir()
269
+ return (config_dir / "config.yaml").exists() and (
270
+ config_dir / "credentials.yaml"
271
+ ).exists()
272
+
273
+
274
+ def credentials_file_secure(credentials_path: Path | None = None) -> bool:
275
+ """Check if credentials file has secure permissions.
276
+
277
+ Args:
278
+ credentials_path: Path to credentials file.
279
+ Defaults to ~/.ouroboros/credentials.yaml.
280
+
281
+ Returns:
282
+ True if file has chmod 600 (owner read/write only).
283
+ """
284
+ if credentials_path is None:
285
+ credentials_path = get_config_dir() / "credentials.yaml"
286
+
287
+ if not credentials_path.exists():
288
+ return False
289
+
290
+ file_mode = credentials_path.stat().st_mode
291
+ # Check that only owner has read/write permissions
292
+ return (file_mode & 0o777) == 0o600
@@ -0,0 +1,332 @@
1
+ """Pydantic models for Ouroboros configuration.
2
+
3
+ This module defines the configuration schema using Pydantic v2.
4
+ All configuration validation happens through these models.
5
+
6
+ Classes:
7
+ ModelConfig: Single LLM model configuration
8
+ TierConfig: Tier configuration with cost factor and models
9
+ ProviderCredentials: API credentials for a single provider
10
+ CredentialsConfig: All provider credentials
11
+ EconomicsConfig: Economic model with tier definitions
12
+ ClarificationConfig: Phase 0 configuration
13
+ ExecutionConfig: Phase 2 configuration
14
+ ResilienceConfig: Phase 3 configuration
15
+ EvaluationConfig: Phase 4 configuration
16
+ ConsensusConfig: Phase 5 configuration
17
+ PersistenceConfig: Storage configuration
18
+ LoggingConfig: Logging configuration
19
+ OuroborosConfig: Top-level configuration combining all sections
20
+ """
21
+
22
+ from pathlib import Path
23
+ from typing import Literal
24
+
25
+ from pydantic import BaseModel, Field, field_validator
26
+
27
+
28
+ class ModelConfig(BaseModel, frozen=True):
29
+ """Configuration for a single LLM model.
30
+
31
+ Attributes:
32
+ provider: Provider name (openai, anthropic, google, openrouter)
33
+ model: Model identifier string
34
+ """
35
+
36
+ provider: str
37
+ model: str
38
+
39
+
40
+ class TierConfig(BaseModel, frozen=True):
41
+ """Configuration for a cost tier.
42
+
43
+ Attributes:
44
+ cost_factor: Relative cost multiplier (1 for frugal, 10 for standard, etc.)
45
+ intelligence_range: Tuple of min/max intelligence score
46
+ models: List of models available in this tier
47
+ use_cases: List of use cases this tier is suited for
48
+ """
49
+
50
+ cost_factor: int = Field(ge=1)
51
+ intelligence_range: tuple[int, int] = Field(default=(1, 20))
52
+ models: list[ModelConfig] = Field(default_factory=list)
53
+ use_cases: list[str] = Field(default_factory=list)
54
+
55
+ @field_validator("intelligence_range")
56
+ @classmethod
57
+ def validate_intelligence_range(cls, v: tuple[int, int]) -> tuple[int, int]:
58
+ """Validate that min <= max in intelligence range."""
59
+ if v[0] > v[1]:
60
+ msg = f"Intelligence range min ({v[0]}) must be <= max ({v[1]})"
61
+ raise ValueError(msg)
62
+ return v
63
+
64
+
65
+ class ProviderCredentials(BaseModel, frozen=True):
66
+ """API credentials for a single provider.
67
+
68
+ Attributes:
69
+ api_key: The API key for the provider
70
+ base_url: Optional custom base URL for the provider
71
+ """
72
+
73
+ api_key: str = Field(min_length=1)
74
+ base_url: str | None = None
75
+
76
+
77
+ class CredentialsConfig(BaseModel, frozen=True):
78
+ """Configuration for all provider credentials.
79
+
80
+ Attributes:
81
+ providers: Dict mapping provider name to credentials
82
+ """
83
+
84
+ providers: dict[str, ProviderCredentials] = Field(default_factory=dict)
85
+
86
+
87
+ class EconomicsConfig(BaseModel, frozen=True):
88
+ """Economic model configuration.
89
+
90
+ Attributes:
91
+ default_tier: Default tier to use for tasks
92
+ tiers: Dict mapping tier name to tier configuration
93
+ escalation_threshold: Number of failures before upgrading tier
94
+ downgrade_success_streak: Successes needed to downgrade tier
95
+ """
96
+
97
+ default_tier: Literal["frugal", "standard", "frontier"] = "frugal"
98
+ tiers: dict[str, TierConfig] = Field(default_factory=dict)
99
+ escalation_threshold: int = Field(default=2, ge=1)
100
+ downgrade_success_streak: int = Field(default=5, ge=1)
101
+
102
+
103
+ class ClarificationConfig(BaseModel, frozen=True):
104
+ """Phase 0 (Big Bang) configuration.
105
+
106
+ Attributes:
107
+ ambiguity_threshold: Maximum ambiguity score to proceed
108
+ max_interview_rounds: Maximum number of clarification rounds
109
+ model_tier: Tier to use for clarification
110
+ default_model: Default LLM model for interview and seed generation
111
+ """
112
+
113
+ ambiguity_threshold: float = Field(default=0.2, ge=0.0, le=1.0)
114
+ max_interview_rounds: int = Field(default=10, ge=1)
115
+ model_tier: Literal["frugal", "standard", "frontier"] = "standard"
116
+ default_model: str = "openrouter/google/gemini-2.0-flash-001"
117
+
118
+
119
+ class ExecutionConfig(BaseModel, frozen=True):
120
+ """Phase 2 (Execution) configuration.
121
+
122
+ Attributes:
123
+ max_iterations_per_ac: Maximum iterations per acceptance criteria
124
+ retrospective_interval: Iterations between retrospectives
125
+ """
126
+
127
+ max_iterations_per_ac: int = Field(default=10, ge=1)
128
+ retrospective_interval: int = Field(default=3, ge=1)
129
+
130
+
131
+ class ResilienceConfig(BaseModel, frozen=True):
132
+ """Phase 3 (Resilience) configuration.
133
+
134
+ Attributes:
135
+ stagnation_enabled: Whether stagnation detection is enabled
136
+ lateral_thinking_enabled: Whether lateral thinking is enabled
137
+ lateral_model_tier: Tier for lateral thinking
138
+ lateral_temperature: Temperature for lateral thinking LLM calls
139
+ """
140
+
141
+ stagnation_enabled: bool = True
142
+ lateral_thinking_enabled: bool = True
143
+ lateral_model_tier: Literal["frugal", "standard", "frontier"] = "frontier"
144
+ lateral_temperature: float = Field(default=0.8, ge=0.0, le=2.0)
145
+
146
+
147
+ class EvaluationConfig(BaseModel, frozen=True):
148
+ """Phase 4 (Evaluation) configuration.
149
+
150
+ Attributes:
151
+ stage1_enabled: Whether mechanical checks are enabled
152
+ stage2_enabled: Whether semantic evaluation is enabled
153
+ stage3_enabled: Whether consensus evaluation is enabled
154
+ satisfaction_threshold: Minimum satisfaction score
155
+ uncertainty_threshold: Threshold above which to trigger consensus
156
+ """
157
+
158
+ stage1_enabled: bool = True
159
+ stage2_enabled: bool = True
160
+ stage3_enabled: bool = True
161
+ satisfaction_threshold: float = Field(default=0.8, ge=0.0, le=1.0)
162
+ uncertainty_threshold: float = Field(default=0.3, ge=0.0, le=1.0)
163
+
164
+
165
+ class ConsensusConfig(BaseModel, frozen=True):
166
+ """Phase 5 (Consensus) configuration.
167
+
168
+ Attributes:
169
+ min_models: Minimum number of models for consensus
170
+ threshold: Agreement threshold for consensus
171
+ diversity_required: Whether different providers are required
172
+ """
173
+
174
+ min_models: int = Field(default=3, ge=2)
175
+ threshold: float = Field(default=0.67, ge=0.0, le=1.0)
176
+ diversity_required: bool = True
177
+
178
+
179
+ class PersistenceConfig(BaseModel, frozen=True):
180
+ """Persistence configuration.
181
+
182
+ Attributes:
183
+ enabled: Whether persistence is enabled
184
+ database_path: Path to SQLite database (relative to config dir)
185
+ """
186
+
187
+ enabled: bool = True
188
+ database_path: str = "data/ouroboros.db"
189
+
190
+
191
+ class DriftConfig(BaseModel, frozen=True):
192
+ """Drift monitoring configuration.
193
+
194
+ Attributes:
195
+ warning_threshold: Drift score threshold for warnings
196
+ critical_threshold: Drift score threshold for intervention
197
+ """
198
+
199
+ warning_threshold: float = Field(default=0.3, ge=0.0, le=1.0)
200
+ critical_threshold: float = Field(default=0.5, ge=0.0, le=1.0)
201
+
202
+ @field_validator("critical_threshold")
203
+ @classmethod
204
+ def validate_critical_threshold(cls, v: float, info: object) -> float:
205
+ """Validate that critical threshold >= warning threshold."""
206
+ data = getattr(info, "data", {})
207
+ warning = data.get("warning_threshold", 0.3)
208
+ if v < warning:
209
+ msg = f"critical_threshold ({v}) must be >= warning_threshold ({warning})"
210
+ raise ValueError(msg)
211
+ return v
212
+
213
+
214
+ class LoggingConfig(BaseModel, frozen=True):
215
+ """Logging configuration.
216
+
217
+ Attributes:
218
+ level: Log level (debug, info, warning, error)
219
+ log_path: Path to log file (relative to config dir)
220
+ include_reasoning: Whether to log LLM reasoning
221
+ """
222
+
223
+ level: Literal["debug", "info", "warning", "error"] = "info"
224
+ log_path: str = "logs/ouroboros.log"
225
+ include_reasoning: bool = True
226
+
227
+
228
+ class OuroborosConfig(BaseModel, frozen=True):
229
+ """Top-level Ouroboros configuration.
230
+
231
+ This is the main configuration model that combines all section configs.
232
+ It validates against config.yaml in ~/.ouroboros/.
233
+
234
+ Attributes:
235
+ economics: Economic model and tier configuration
236
+ clarification: Phase 0 (Big Bang) configuration
237
+ execution: Phase 2 configuration
238
+ resilience: Phase 3 configuration
239
+ evaluation: Phase 4 configuration
240
+ consensus: Phase 5 configuration
241
+ persistence: Storage configuration
242
+ drift: Drift monitoring configuration
243
+ logging: Logging configuration
244
+ """
245
+
246
+ economics: EconomicsConfig = Field(default_factory=EconomicsConfig)
247
+ clarification: ClarificationConfig = Field(default_factory=ClarificationConfig)
248
+ execution: ExecutionConfig = Field(default_factory=ExecutionConfig)
249
+ resilience: ResilienceConfig = Field(default_factory=ResilienceConfig)
250
+ evaluation: EvaluationConfig = Field(default_factory=EvaluationConfig)
251
+ consensus: ConsensusConfig = Field(default_factory=ConsensusConfig)
252
+ persistence: PersistenceConfig = Field(default_factory=PersistenceConfig)
253
+ drift: DriftConfig = Field(default_factory=DriftConfig)
254
+ logging: LoggingConfig = Field(default_factory=LoggingConfig)
255
+
256
+
257
+ def get_default_config() -> OuroborosConfig:
258
+ """Get the default Ouroboros configuration.
259
+
260
+ Returns:
261
+ OuroborosConfig with all default values populated.
262
+ """
263
+ return OuroborosConfig(
264
+ economics=EconomicsConfig(
265
+ default_tier="frugal",
266
+ tiers={
267
+ "frugal": TierConfig(
268
+ cost_factor=1,
269
+ intelligence_range=(9, 11),
270
+ models=[
271
+ ModelConfig(provider="openai", model="gpt-4o-mini"),
272
+ ModelConfig(provider="google", model="gemini-2.0-flash"),
273
+ ModelConfig(provider="anthropic", model="claude-3-5-haiku"),
274
+ ],
275
+ use_cases=["routine_coding", "log_analysis", "stage1_fix"],
276
+ ),
277
+ "standard": TierConfig(
278
+ cost_factor=10,
279
+ intelligence_range=(14, 16),
280
+ models=[
281
+ ModelConfig(provider="openai", model="gpt-4o"),
282
+ ModelConfig(provider="anthropic", model="claude-sonnet-4-20250514"),
283
+ ModelConfig(provider="google", model="gemini-2.5-pro"),
284
+ ],
285
+ use_cases=["logic_design", "stage2_evaluation", "refactoring"],
286
+ ),
287
+ "frontier": TierConfig(
288
+ cost_factor=30,
289
+ intelligence_range=(18, 20),
290
+ models=[
291
+ ModelConfig(provider="openai", model="o3"),
292
+ ModelConfig(provider="anthropic", model="claude-opus-4-5-20251101"),
293
+ ],
294
+ use_cases=["consensus", "lateral_thinking", "big_bang"],
295
+ ),
296
+ },
297
+ escalation_threshold=2,
298
+ downgrade_success_streak=5,
299
+ ),
300
+ )
301
+
302
+
303
+ def get_default_credentials() -> CredentialsConfig:
304
+ """Get the default credentials configuration template.
305
+
306
+ Returns:
307
+ CredentialsConfig with placeholder providers.
308
+
309
+ Note:
310
+ The returned credentials have empty API keys and should be
311
+ filled in by the user.
312
+ """
313
+ return CredentialsConfig(
314
+ providers={
315
+ "openrouter": ProviderCredentials(
316
+ api_key="YOUR_OPENROUTER_API_KEY",
317
+ base_url="https://openrouter.ai/api/v1",
318
+ ),
319
+ "openai": ProviderCredentials(api_key="YOUR_OPENAI_API_KEY"),
320
+ "anthropic": ProviderCredentials(api_key="YOUR_ANTHROPIC_API_KEY"),
321
+ "google": ProviderCredentials(api_key="YOUR_GOOGLE_API_KEY"),
322
+ }
323
+ )
324
+
325
+
326
+ def get_config_dir() -> Path:
327
+ """Get the Ouroboros configuration directory path.
328
+
329
+ Returns:
330
+ Path to ~/.ouroboros/
331
+ """
332
+ return Path.home() / ".ouroboros"
@@ -0,0 +1,62 @@
1
+ """Ouroboros core module - shared types, errors, and protocols."""
2
+
3
+ from ouroboros.core.context import (
4
+ CompressionResult,
5
+ ContextMetrics,
6
+ FilteredContext,
7
+ WorkflowContext,
8
+ compress_context,
9
+ compress_context_with_llm,
10
+ count_context_tokens,
11
+ count_tokens,
12
+ create_filtered_context,
13
+ get_context_metrics,
14
+ )
15
+ from ouroboros.core.errors import (
16
+ ConfigError,
17
+ OuroborosError,
18
+ PersistenceError,
19
+ ProviderError,
20
+ ValidationError,
21
+ )
22
+ from ouroboros.core.seed import (
23
+ EvaluationPrinciple,
24
+ ExitCondition,
25
+ OntologyField,
26
+ OntologySchema,
27
+ Seed,
28
+ SeedMetadata,
29
+ )
30
+ from ouroboros.core.types import CostUnits, DriftScore, EventPayload, Result
31
+
32
+ __all__ = [
33
+ # Types
34
+ "Result",
35
+ "EventPayload",
36
+ "CostUnits",
37
+ "DriftScore",
38
+ # Errors
39
+ "OuroborosError",
40
+ "ProviderError",
41
+ "ConfigError",
42
+ "PersistenceError",
43
+ "ValidationError",
44
+ # Seed (Immutable Specification)
45
+ "Seed",
46
+ "SeedMetadata",
47
+ "OntologySchema",
48
+ "OntologyField",
49
+ "EvaluationPrinciple",
50
+ "ExitCondition",
51
+ # Context Management
52
+ "WorkflowContext",
53
+ "ContextMetrics",
54
+ "CompressionResult",
55
+ "FilteredContext",
56
+ "count_tokens",
57
+ "count_context_tokens",
58
+ "get_context_metrics",
59
+ "compress_context",
60
+ "compress_context_with_llm",
61
+ "create_filtered_context",
62
+ ]