dreamforge 0.2.0__tar.gz

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 (46) hide show
  1. dreamforge-0.2.0/LICENSE +21 -0
  2. dreamforge-0.2.0/PKG-INFO +156 -0
  3. dreamforge-0.2.0/README.md +111 -0
  4. dreamforge-0.2.0/pyproject.toml +72 -0
  5. dreamforge-0.2.0/setup.cfg +4 -0
  6. dreamforge-0.2.0/src/dreamforge/__init__.py +8 -0
  7. dreamforge-0.2.0/src/dreamforge/core/__init__.py +6 -0
  8. dreamforge-0.2.0/src/dreamforge/core/config.py +142 -0
  9. dreamforge-0.2.0/src/dreamforge/core/models/__init__.py +1 -0
  10. dreamforge-0.2.0/src/dreamforge/core/models/dream_context.py +323 -0
  11. dreamforge-0.2.0/src/dreamforge/core/models/events.py +310 -0
  12. dreamforge-0.2.0/src/dreamforge/core/models/memory_graph.py +385 -0
  13. dreamforge-0.2.0/src/dreamforge/core/models/neurochemistry.py +124 -0
  14. dreamforge-0.2.0/src/dreamforge/core/models/sleep_cycle.py +394 -0
  15. dreamforge-0.2.0/src/dreamforge/core/provenance/__init__.py +1 -0
  16. dreamforge-0.2.0/src/dreamforge/core/provenance/clock.py +38 -0
  17. dreamforge-0.2.0/src/dreamforge/core/providers/__init__.py +1 -0
  18. dreamforge-0.2.0/src/dreamforge/core/providers/narrative.py +230 -0
  19. dreamforge-0.2.0/src/dreamforge/core/scoring/__init__.py +1 -0
  20. dreamforge-0.2.0/src/dreamforge/core/scoring/bizarreness.py +87 -0
  21. dreamforge-0.2.0/src/dreamforge/core/serialization/__init__.py +1 -0
  22. dreamforge-0.2.0/src/dreamforge/core/serialization/dqcj.py +283 -0
  23. dreamforge-0.2.0/src/dreamforge/demo.py +120 -0
  24. dreamforge-0.2.0/src/dreamforge/integrations/__init__.py +5 -0
  25. dreamforge-0.2.0/src/dreamforge/integrations/anthropic_compat.py +174 -0
  26. dreamforge-0.2.0/src/dreamforge/integrations/errors.py +33 -0
  27. dreamforge-0.2.0/src/dreamforge/integrations/openai_compat.py +177 -0
  28. dreamforge-0.2.0/src/dreamforge/integrations/retry.py +72 -0
  29. dreamforge-0.2.0/src/dreamforge/integrations/transport.py +69 -0
  30. dreamforge-0.2.0/src/dreamforge/simulation/__init__.py +1 -0
  31. dreamforge-0.2.0/src/dreamforge/simulation/counterfactual.py +178 -0
  32. dreamforge-0.2.0/src/dreamforge/simulation/engine.py +310 -0
  33. dreamforge-0.2.0/src/dreamforge/simulation/ensemble.py +135 -0
  34. dreamforge-0.2.0/src/dreamforge/simulation/export_import.py +471 -0
  35. dreamforge-0.2.0/src/dreamforge/simulation/report.py +153 -0
  36. dreamforge-0.2.0/src/dreamforge/simulation/run_repository.py +176 -0
  37. dreamforge-0.2.0/src/dreamforge/simulation/sweeps.py +133 -0
  38. dreamforge-0.2.0/src/dreamforge/visualization/__init__.py +1 -0
  39. dreamforge-0.2.0/src/dreamforge/visualization/dashboard.py +183 -0
  40. dreamforge-0.2.0/src/dreamforge/visualization/loader.py +68 -0
  41. dreamforge-0.2.0/src/dreamforge.egg-info/PKG-INFO +156 -0
  42. dreamforge-0.2.0/src/dreamforge.egg-info/SOURCES.txt +44 -0
  43. dreamforge-0.2.0/src/dreamforge.egg-info/dependency_links.txt +1 -0
  44. dreamforge-0.2.0/src/dreamforge.egg-info/entry_points.txt +3 -0
  45. dreamforge-0.2.0/src/dreamforge.egg-info/requires.txt +15 -0
  46. dreamforge-0.2.0/src/dreamforge.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 JToSound
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: dreamforge
3
+ Version: 0.2.0
4
+ Summary: Deterministic, offline sandbox for non-clinical sleep/dream simulation proxies (research and visualization simulator; no medical claims).
5
+ Author: JToSound
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 JToSound
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Requires-Python: >=3.11
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE
31
+ Requires-Dist: numpy<3,>=2
32
+ Requires-Dist: pydantic<3,>=2.7
33
+ Requires-Dist: networkx<4,>=3.3
34
+ Provides-Extra: dashboard
35
+ Requires-Dist: streamlit>=1.37; extra == "dashboard"
36
+ Requires-Dist: plotly>=5.22; extra == "dashboard"
37
+ Provides-Extra: dev
38
+ Requires-Dist: pytest>=9.0.3; extra == "dev"
39
+ Requires-Dist: pytest-cov<7,>=5; extra == "dev"
40
+ Requires-Dist: hypothesis<7,>=6; extra == "dev"
41
+ Requires-Dist: ruff>=0.6; extra == "dev"
42
+ Requires-Dist: black>=24; extra == "dev"
43
+ Requires-Dist: mypy>=1.10; extra == "dev"
44
+ Dynamic: license-file
45
+
46
+ # DreamForge AI
47
+
48
+ > DreamForge is a research and visualization simulator. It does not measure brains, diagnose conditions, predict dreams, infer psychological meaning, or provide medical advice.
49
+
50
+ An open-source, deterministic, offline sandbox for simulating explicit,
51
+ configurable, **non-clinical proxies** related to sleep regulation, sleep-stage
52
+ dynamics, normalized neuromodulatory patterns, synthetic-memory graph
53
+ selection, and structured dream-context features — under active construction
54
+ toward the milestone plan in [`MASTER_PROMPT.md`](MASTER_PROMPT.md).
55
+
56
+ ## Status
57
+
58
+ First execution slice + M1 (complete) and M2 groundwork: deterministic core with
59
+ an 8-hour (960 × 30 s epoch) offline trace, event sourcing, DQCJ-1 canonical
60
+ serialization, hash-verified exports, structured dream context/features/score,
61
+ and the mandatory offline narrative provider with labeled report blocks. See
62
+ [`ARCHITECTURE.md`](ARCHITECTURE.md), [`RESEARCH.md`](RESEARCH.md),
63
+ [`LIMITATIONS.md`](LIMITATIONS.md), and `docs/`.
64
+
65
+ **Not yet built** (by explicit scope control): API, plugins, notebooks,
66
+ streaming/tool-use adapter features.
67
+
68
+ ## Optional: local / OpenAI-compatible narrative provider (opt-in)
69
+
70
+ The offline mock is always the default. To use a local model via Ollama's
71
+ OpenAI-compatible endpoint (or any `/chat/completions` service), construct the
72
+ adapter explicitly in your own script — nothing is enabled by configuration
73
+ alone:
74
+
75
+ ```python
76
+ from dreamforge.core.providers.narrative import NarrativeRequest
77
+ from dreamforge.integrations.openai_compat import OpenAICompatConfig, OpenAICompatProvider
78
+ from dreamforge.integrations.transport import UrllibTransport
79
+
80
+ config = OpenAICompatConfig(
81
+ base_url="http://127.0.0.1:11434/v1", # Ollama loopback example
82
+ model="llama3.2:3b",
83
+ timeout_seconds=20,
84
+ max_retries=2,
85
+ )
86
+ provider = OpenAICompatProvider(config, UrllibTransport())
87
+ response = provider.generate(request) # request = validated NarrativeRequest
88
+ ```
89
+
90
+ Guarantees (ADR 0005): allowlisted projection only; strict response schema;
91
+ per-attempt timeout; bounded retries then fail-closed; errors redacted to
92
+ status code + response hash; every response labeled `generative_interpretation`.
93
+
94
+ An Anthropic-native adapter with the same guarantees ships alongside it
95
+ (`AnthropicCompatProvider` — messages API, `x-api-key` +
96
+ `anthropic-version` headers, content-block extraction); both share one
97
+ vetted bounded-retry/redaction implementation (`integrations/retry.py`).
98
+
99
+ ## Dashboard (optional extras)
100
+
101
+ ```bash
102
+ ".venv/Scripts/python.exe" -m pip install -c constraints.txt streamlit plotly
103
+ ".venv/Scripts/python.exe" -m streamlit run src/dreamforge/visualization/dashboard.py -- exports/demo_8h
104
+ ```
105
+
106
+ Renders verified exports only; accessibility/theme documentation in
107
+ [`DASHBOARD.md`](DASHBOARD.md).
108
+
109
+ ## What it simulates (and what that means)
110
+
111
+ - Conceptual two-process-inspired sleep-regulation equations (homeostatic "S"
112
+ pressure + sinusoidal circadian proxy) — mathematical constructs, not physiology.
113
+ - A semi-Markov Wake/N1/N2/N3/REM stage process at 30-second resolution —
114
+ explicitly *not* PSG scoring.
115
+ - Four normalized `[0,1]` neuromodulatory proxy indices — qualitative synthetic
116
+ values, never concentrations or measurements.
117
+ - Selection over a synthetic directed weighted memory graph — graph selection,
118
+ not neural replay.
119
+ - Deterministic structured features and scores derived only from the above.
120
+
121
+ All parameters carry evidence grades (`assumption`, `synthetic_demo`, …) in
122
+ [`docs/scientific_model/claim_registry.yaml`](docs/scientific_model/claim_registry.yaml).
123
+ No empirical or clinical claim is made anywhere in this repository.
124
+
125
+ ## Quick start (offline demo)
126
+
127
+ Requires Python 3.11+ (developed on 3.12, Windows). No network egress at runtime;
128
+ install needs PyPI once.
129
+
130
+ ```bash
131
+ python -m venv .venv
132
+ ".venv/Scripts/python.exe" -m pip install -c constraints.txt -e . # Windows path; use .venv/bin elsewhere
133
+ ".venv/Scripts/python.exe" -m dreamforge.demo # runs examples/configs/demo_8h.json
134
+ ```
135
+
136
+ The demo validates its configuration, runs 960 epochs, verifies stage-transition
137
+ legality, writes an export under `exports/demo_8h/`
138
+ (`events.ndjson` + `manifest.json`), re-imports it, and re-verifies all hashes.
139
+
140
+ ## Development checks
141
+
142
+ ```bash
143
+ ".venv/Scripts/python.exe" -m pytest -q # tests
144
+ ".venv/Scripts/python.exe" -m pytest -q --cov=src/dreamforge --cov-report=term-missing
145
+ ".venv/Scripts/python.exe" -m ruff check src tests examples # lint
146
+ ".venv/Scripts/python.exe" -m black --check src tests examples # format-check
147
+ ".venv/Scripts/python.exe" -m mypy src/dreamforge/core # typecheck
148
+ ```
149
+
150
+ ## Repository layout
151
+
152
+ See [`ARCHITECTURE.md`](ARCHITECTURE.md) for the diagram and component table.
153
+
154
+ ## License
155
+
156
+ MIT — see [`LICENSE`](LICENSE). Copyright (c) 2026 JToSound.
@@ -0,0 +1,111 @@
1
+ # DreamForge AI
2
+
3
+ > DreamForge is a research and visualization simulator. It does not measure brains, diagnose conditions, predict dreams, infer psychological meaning, or provide medical advice.
4
+
5
+ An open-source, deterministic, offline sandbox for simulating explicit,
6
+ configurable, **non-clinical proxies** related to sleep regulation, sleep-stage
7
+ dynamics, normalized neuromodulatory patterns, synthetic-memory graph
8
+ selection, and structured dream-context features — under active construction
9
+ toward the milestone plan in [`MASTER_PROMPT.md`](MASTER_PROMPT.md).
10
+
11
+ ## Status
12
+
13
+ First execution slice + M1 (complete) and M2 groundwork: deterministic core with
14
+ an 8-hour (960 × 30 s epoch) offline trace, event sourcing, DQCJ-1 canonical
15
+ serialization, hash-verified exports, structured dream context/features/score,
16
+ and the mandatory offline narrative provider with labeled report blocks. See
17
+ [`ARCHITECTURE.md`](ARCHITECTURE.md), [`RESEARCH.md`](RESEARCH.md),
18
+ [`LIMITATIONS.md`](LIMITATIONS.md), and `docs/`.
19
+
20
+ **Not yet built** (by explicit scope control): API, plugins, notebooks,
21
+ streaming/tool-use adapter features.
22
+
23
+ ## Optional: local / OpenAI-compatible narrative provider (opt-in)
24
+
25
+ The offline mock is always the default. To use a local model via Ollama's
26
+ OpenAI-compatible endpoint (or any `/chat/completions` service), construct the
27
+ adapter explicitly in your own script — nothing is enabled by configuration
28
+ alone:
29
+
30
+ ```python
31
+ from dreamforge.core.providers.narrative import NarrativeRequest
32
+ from dreamforge.integrations.openai_compat import OpenAICompatConfig, OpenAICompatProvider
33
+ from dreamforge.integrations.transport import UrllibTransport
34
+
35
+ config = OpenAICompatConfig(
36
+ base_url="http://127.0.0.1:11434/v1", # Ollama loopback example
37
+ model="llama3.2:3b",
38
+ timeout_seconds=20,
39
+ max_retries=2,
40
+ )
41
+ provider = OpenAICompatProvider(config, UrllibTransport())
42
+ response = provider.generate(request) # request = validated NarrativeRequest
43
+ ```
44
+
45
+ Guarantees (ADR 0005): allowlisted projection only; strict response schema;
46
+ per-attempt timeout; bounded retries then fail-closed; errors redacted to
47
+ status code + response hash; every response labeled `generative_interpretation`.
48
+
49
+ An Anthropic-native adapter with the same guarantees ships alongside it
50
+ (`AnthropicCompatProvider` — messages API, `x-api-key` +
51
+ `anthropic-version` headers, content-block extraction); both share one
52
+ vetted bounded-retry/redaction implementation (`integrations/retry.py`).
53
+
54
+ ## Dashboard (optional extras)
55
+
56
+ ```bash
57
+ ".venv/Scripts/python.exe" -m pip install -c constraints.txt streamlit plotly
58
+ ".venv/Scripts/python.exe" -m streamlit run src/dreamforge/visualization/dashboard.py -- exports/demo_8h
59
+ ```
60
+
61
+ Renders verified exports only; accessibility/theme documentation in
62
+ [`DASHBOARD.md`](DASHBOARD.md).
63
+
64
+ ## What it simulates (and what that means)
65
+
66
+ - Conceptual two-process-inspired sleep-regulation equations (homeostatic "S"
67
+ pressure + sinusoidal circadian proxy) — mathematical constructs, not physiology.
68
+ - A semi-Markov Wake/N1/N2/N3/REM stage process at 30-second resolution —
69
+ explicitly *not* PSG scoring.
70
+ - Four normalized `[0,1]` neuromodulatory proxy indices — qualitative synthetic
71
+ values, never concentrations or measurements.
72
+ - Selection over a synthetic directed weighted memory graph — graph selection,
73
+ not neural replay.
74
+ - Deterministic structured features and scores derived only from the above.
75
+
76
+ All parameters carry evidence grades (`assumption`, `synthetic_demo`, …) in
77
+ [`docs/scientific_model/claim_registry.yaml`](docs/scientific_model/claim_registry.yaml).
78
+ No empirical or clinical claim is made anywhere in this repository.
79
+
80
+ ## Quick start (offline demo)
81
+
82
+ Requires Python 3.11+ (developed on 3.12, Windows). No network egress at runtime;
83
+ install needs PyPI once.
84
+
85
+ ```bash
86
+ python -m venv .venv
87
+ ".venv/Scripts/python.exe" -m pip install -c constraints.txt -e . # Windows path; use .venv/bin elsewhere
88
+ ".venv/Scripts/python.exe" -m dreamforge.demo # runs examples/configs/demo_8h.json
89
+ ```
90
+
91
+ The demo validates its configuration, runs 960 epochs, verifies stage-transition
92
+ legality, writes an export under `exports/demo_8h/`
93
+ (`events.ndjson` + `manifest.json`), re-imports it, and re-verifies all hashes.
94
+
95
+ ## Development checks
96
+
97
+ ```bash
98
+ ".venv/Scripts/python.exe" -m pytest -q # tests
99
+ ".venv/Scripts/python.exe" -m pytest -q --cov=src/dreamforge --cov-report=term-missing
100
+ ".venv/Scripts/python.exe" -m ruff check src tests examples # lint
101
+ ".venv/Scripts/python.exe" -m black --check src tests examples # format-check
102
+ ".venv/Scripts/python.exe" -m mypy src/dreamforge/core # typecheck
103
+ ```
104
+
105
+ ## Repository layout
106
+
107
+ See [`ARCHITECTURE.md`](ARCHITECTURE.md) for the diagram and component table.
108
+
109
+ ## License
110
+
111
+ MIT — see [`LICENSE`](LICENSE). Copyright (c) 2026 JToSound.
@@ -0,0 +1,72 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "dreamforge"
7
+ version = "0.2.0"
8
+ description = "Deterministic, offline sandbox for non-clinical sleep/dream simulation proxies (research and visualization simulator; no medical claims)."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { file = "LICENSE" }
12
+ authors = [{ name = "JToSound" }]
13
+ dependencies = [
14
+ "numpy>=2,<3",
15
+ "pydantic>=2.7,<3",
16
+ "networkx>=3.3,<4",
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ dashboard = [
21
+ "streamlit>=1.37",
22
+ "plotly>=5.22",
23
+ ]
24
+ dev = [
25
+ "pytest>=9.0.3",
26
+ "pytest-cov>=5,<7",
27
+ "hypothesis>=6,<7",
28
+ "ruff>=0.6",
29
+ "black>=24",
30
+ "mypy>=1.10",
31
+ ]
32
+
33
+ [project.scripts]
34
+ dreamforge-demo = "dreamforge.demo:main"
35
+ dreamforge-dashboard = "dreamforge.visualization.dashboard:main"
36
+
37
+ [tool.setuptools.packages.find]
38
+ where = ["src"]
39
+
40
+ [tool.ruff]
41
+ line-length = 100
42
+ target-version = "py311"
43
+ src = ["src", "tests", "examples"]
44
+
45
+ [tool.ruff.lint]
46
+ select = ["E", "W", "F", "I", "B", "UP"]
47
+ ignore = []
48
+
49
+ [tool.black]
50
+ line-length = 100
51
+ target-version = ["py311"]
52
+
53
+ [tool.mypy]
54
+ strict = true
55
+ files = ["src/dreamforge/core", "src/dreamforge/integrations"]
56
+ # Checked under 3.12 semantics because numpy>=2.5 stubs use 3.12-only syntax;
57
+ # the package runtime floor stays >=3.11 and no source uses 3.12-only features.
58
+ python_version = "3.12"
59
+ warn_unreachable = true
60
+ show_error_codes = true
61
+
62
+ [[tool.mypy.overrides]]
63
+ module = "networkx.*"
64
+ ignore_missing_imports = true
65
+
66
+ [tool.pytest.ini_options]
67
+ testpaths = ["tests"]
68
+ addopts = "-ra"
69
+ markers = [
70
+ "integration: long-running deterministic integration tests",
71
+ "property: hypothesis property-based tests",
72
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,8 @@
1
+ """DreamForge — deterministic research and visualization simulator.
2
+
3
+ DreamForge is a research and visualization simulator. It does not measure
4
+ brains, diagnose conditions, predict dreams, infer psychological meaning, or
5
+ provide medical advice.
6
+ """
7
+
8
+ __version__ = "0.2.0"
@@ -0,0 +1,6 @@
1
+ """DreamForge deterministic core.
2
+
3
+ Nothing in this package may import FastAPI, Streamlit, LangGraph, provider
4
+ SDKs, environment variables, clocks, filesystem/network clients, or runtime
5
+ configuration loaders (MASTER_PROMPT.md section 3).
6
+ """
@@ -0,0 +1,142 @@
1
+ """Strict simulation configuration loading (fail closed).
2
+
3
+ The configuration is the only external input to a run. It is validated into
4
+ frozen Pydantic models before any engine object is constructed; any violation
5
+ raises :class:`ConfigError` with a machine-readable ``code``. No environment
6
+ variables, clocks, or network access participate.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
15
+
16
+ from dreamforge.core.models.events import ALL_STAGES, StageName
17
+ from dreamforge.core.models.memory_graph import GraphSpecConfig, ReplaySelectorConfig
18
+ from dreamforge.core.models.neurochemistry import NeurochemistryConfig
19
+ from dreamforge.core.models.sleep_cycle import (
20
+ CircadianConfig,
21
+ DwellDistribution,
22
+ ProcessSConfig,
23
+ TransitionMatrixConfig,
24
+ )
25
+
26
+ #: Hard limits guarding against oversized/hostile configs (section 6.3).
27
+ MAX_EPOCH_SECONDS = 3600.0
28
+ MIN_EPOCH_SECONDS = 1.0
29
+ MAX_TOTAL_TICKS = 100_000
30
+
31
+
32
+ class ConfigError(ValueError):
33
+ """Raised when configuration fails validation; ``code`` is stable."""
34
+
35
+ def __init__(self, code: str, message: str) -> None:
36
+ super().__init__(message)
37
+ self.code = code
38
+
39
+
40
+ class ReplayPolicyConfig(BaseModel):
41
+ """Where replay selection gets its graph and its policy."""
42
+
43
+ model_config = ConfigDict(frozen=True, extra="forbid")
44
+
45
+ synthetic_graph: GraphSpecConfig = Field(default_factory=GraphSpecConfig)
46
+ selector: ReplaySelectorConfig = Field(default_factory=ReplaySelectorConfig)
47
+ replay_every_n_epochs: int = Field(default=8, ge=1)
48
+
49
+
50
+ class SimulationConfig(BaseModel):
51
+ """Fully validated run configuration (immutable after load)."""
52
+
53
+ model_config = ConfigDict(frozen=True, extra="forbid")
54
+
55
+ schema_version: str = Field(pattern=r"^\d+\.\d+$")
56
+ run_id: str = Field(min_length=8, max_length=64, pattern=r"^[A-Za-z0-9._-]+$")
57
+ run_seed: int = Field(ge=0, le=2**64 - 1)
58
+ epoch_seconds: float = Field(default=30.0)
59
+ total_ticks: int = Field(ge=1)
60
+ initial_stage: StageName = "Wake"
61
+ process_s: ProcessSConfig = Field(default_factory=ProcessSConfig)
62
+ circadian: CircadianConfig = Field(default_factory=CircadianConfig)
63
+ transitions: TransitionMatrixConfig
64
+ dwells: dict[StageName, DwellDistribution]
65
+ chemistry: NeurochemistryConfig
66
+ replay_policy: ReplayPolicyConfig = Field(default_factory=ReplayPolicyConfig)
67
+
68
+ @field_validator("epoch_seconds")
69
+ @classmethod
70
+ def _epoch_bounds(cls, value: float) -> float:
71
+ if not MIN_EPOCH_SECONDS <= value <= MAX_EPOCH_SECONDS:
72
+ msg = f"epoch_seconds must be within " f"[{MIN_EPOCH_SECONDS}, {MAX_EPOCH_SECONDS}]"
73
+ raise ValueError(msg)
74
+ return value
75
+
76
+ @field_validator("total_ticks")
77
+ @classmethod
78
+ def _ticks_cap(cls, value: int) -> int:
79
+ if value > MAX_TOTAL_TICKS:
80
+ msg = f"total_ticks exceeds MAX_TOTAL_TICKS={MAX_TOTAL_TICKS}"
81
+ raise ValueError(msg)
82
+ return value
83
+
84
+ @model_validator(mode="after")
85
+ def _dwell_support(self) -> SimulationConfig:
86
+ missing = [s for s in ALL_STAGES if s not in self.dwells]
87
+ if missing:
88
+ msg = f"dwells missing stages: {missing}"
89
+ raise ValueError(msg)
90
+ for stage, dwell in self.dwells.items():
91
+ longest = dwell.min_epochs + len(dwell.weights) - 1
92
+ if longest > self.total_ticks:
93
+ msg = f"dwell support for {stage} exceeds total_ticks"
94
+ raise ValueError(msg)
95
+ return self
96
+
97
+ def simulated_time_minutes(self, tick: int) -> float:
98
+ """Documented conversion: tick × epoch_seconds / 60."""
99
+ return tick * self.epoch_seconds / 60.0
100
+
101
+
102
+ def dumps_config_canonical(config: SimulationConfig) -> bytes:
103
+ """Canonical configuration snapshot bytes (embedded into exports).
104
+
105
+ Lives here rather than in the simulation package so that export/import
106
+ verification never needs to load the engine module at all.
107
+ """
108
+ from dreamforge.core.serialization.dqcj import dumps_canonical
109
+
110
+ return dumps_canonical(config.model_dump(mode="json"))
111
+
112
+
113
+ def load_config(source: str | Path | dict[str, Any]) -> SimulationConfig:
114
+ """Load and strictly validate a run configuration.
115
+
116
+ ``source`` may be an already-parsed dict or a path to UTF-8 JSON. Raises
117
+ :class:`ConfigError` on any violation (schema, ranges, structure).
118
+ """
119
+ if isinstance(source, dict):
120
+ raw = source
121
+ else:
122
+ path = Path(source)
123
+ try:
124
+ text = path.read_text(encoding="utf-8")
125
+ except OSError as exc:
126
+ msg = f"config file unreadable: {exc.__class__.__name__}"
127
+ raise ConfigError("config_unreadable", msg) from exc
128
+ from dreamforge.core.serialization.dqcj import loads_strict
129
+
130
+ try:
131
+ raw = loads_strict(text)
132
+ except ValueError as exc:
133
+ raise ConfigError("config_invalid_json", str(exc)) from exc
134
+ if not isinstance(raw, dict):
135
+ raise ConfigError(
136
+ "config_invalid_json",
137
+ "top-level configuration must be a JSON object",
138
+ )
139
+ try:
140
+ return SimulationConfig.model_validate(raw)
141
+ except Exception as exc: # noqa: BLE001 - re-raised typed below
142
+ raise ConfigError("config_validation_failed", str(exc)) from exc
@@ -0,0 +1 @@
1
+ """Domain models: sleep cycle, neurochemistry, memory graph, events."""