File size: 3,228 Bytes
761d149
2b16e51
feb1b1c
 
2b16e51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86


from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING

from redstack.pipelines.context import RunContext, canonical_config_hash

if TYPE_CHECKING:
    from redstack.config.schema import RedstackConfig, OfflineRuntimeConfig
    from redstack.features.registry import FeatureRegistry
    from redstack.ports.artifact_store import ArtifactStorePort
    from redstack.ports.candidate_source import CandidateSourcePort
    from redstack.ports.embedding import EmbeddingModelPort
    from redstack.ports.rng import DeterministicEntropyPort

__all__: tuple[str, ...] = ("OfflinePipelineContext",)


@dataclass(frozen=True, slots=True, kw_only=True)
class OfflinePipelineContext(RunContext):


    candidate_source: CandidateSourcePort
    embedding_model: EmbeddingModelPort
    artifact_store: ArtifactStorePort
    entropy: DeterministicEntropyPort
    feature_registry: FeatureRegistry
    artifacts_root: Path
    data_root: Path
    quarantine_root: Path
    checkpoints_root: Path
    layout_version: str

    @classmethod
    def build(
        cls,
        *,
        config: RedstackConfig,
        candidate_source: CandidateSourcePort,
        embedding_model: EmbeddingModelPort,
        artifact_store: ArtifactStorePort,
        entropy: DeterministicEntropyPort,
        feature_registry: FeatureRegistry,
        code_version: str,
    ) -> OfflinePipelineContext:
        """Resolve a build context from validated config + already-bound ports.

        Derives ``seed`` and ``as_of`` from the offline runtime block and the
        injected entropy port (the single clock/RNG seam), computes the canonical
        ``config_hash``, and resolves the output roots under ``paths``. The
        roots are resolved to absolute paths for stable, traversal-safe IO by the
        adapters; this constructor performs no IO itself.

        Raises:
            ValueError: if invoked on a config whose ``run_mode`` is not offline
                (the offline block must be present — guaranteed by
                ``RedstackConfig`` mode-consistency, re-asserted here for the
                type narrower).
        """
        offline: OfflineRuntimeConfig | None = config.offline
        if offline is None:
            msg = "OfflinePipelineContext requires an offline runtime config block"
            raise ValueError(msg)

        artifacts_root = Path(config.paths.artifacts_root).resolve()
        layout_version = feature_registry.layout_version
        return cls(
            config=config,
            seed=offline.seed,
            as_of=entropy.as_of(),
            code_version=code_version,
            config_hash=canonical_config_hash(config),
            candidate_source=candidate_source,
            embedding_model=embedding_model,
            artifact_store=artifact_store,
            entropy=entropy,
            feature_registry=feature_registry,
            artifacts_root=artifacts_root,
            data_root=Path(config.paths.data_root).resolve(),
            quarantine_root=artifacts_root / "_quarantine",
            checkpoints_root=artifacts_root / "_checkpoints",
            layout_version=layout_version,
        )