File size: 13,177 Bytes
784f572
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
"""Runtime helpers for zero-shot hard-prefix conditional generation."""

from __future__ import annotations

from contextlib import contextmanager
from dataclasses import dataclass
import json
from pathlib import Path
from typing import Iterator, Sequence

from dlb.conditional_prompts import PromptManifest, PromptRecord
from dlb.io import SampleValidationError, sha256_file


@dataclass(frozen=True)
class ConditioningBatch:
    """A device-materialized batch of immutable prompt conditioning tensors."""

    prompt_ids: tuple[int, ...]
    source_indices: tuple[int, ...]
    prefix_token_ids: object
    reference_token_ids: object
    completion_id: int

    @property
    def prefix(self) -> object:
        return self.prefix_token_ids

    @property
    def reference(self) -> object:
        return self.reference_token_ids


def _require_torch():
    try:
        import torch
    except ImportError as error:  # pragma: no cover - server environments install torch.
        raise RuntimeError("conditional runtime helpers require torch") from error
    return torch


def _shape(value: object) -> tuple[int, ...]:
    return tuple(int(part) for part in value.shape)  # type: ignore[attr-defined]


def clamp_token_prefix(state, prefix_ids):
    """Return a copy of a rank-2 token state with the prompt prefix restored."""

    if getattr(state, "ndim", None) != 2 or getattr(prefix_ids, "ndim", None) != 2:
        raise ValueError("token state and prefix must be rank two")
    if state.shape[0] != prefix_ids.shape[0] or state.shape[1] < prefix_ids.shape[1]:
        raise ValueError("token state is incompatible with prefix batch")
    result = state.clone()
    result[:, : prefix_ids.shape[1]] = prefix_ids.to(device=state.device, dtype=state.dtype)
    return result


def clamp_vocab_prefix(state, prefix_ids):
    """Return a copy of a rank-3 vocabulary state with clean one-hot prefix rows."""

    torch = _require_torch()
    if getattr(state, "ndim", None) != 3:
        raise ValueError("vocabulary state must be rank three")
    if getattr(prefix_ids, "ndim", None) != 2:
        raise ValueError("vocabulary prefix must be rank two")
    if state.shape[0] != prefix_ids.shape[0] or state.shape[1] < prefix_ids.shape[1]:
        raise ValueError("vocabulary state is incompatible with prefix batch")
    if int(prefix_ids.max().item()) >= state.shape[-1] or int(prefix_ids.min().item()) < 0:
        raise ValueError("prefix token is outside vocabulary dimension")
    clean = torch.nn.functional.one_hot(prefix_ids, num_classes=state.shape[-1]).to(state)
    result = state.clone()
    result[:, : prefix_ids.shape[1], :] = clean
    return result


def base_n_digits(token_ids, *, base: int, digits_per_token: int):
    """Encode rank-2 token IDs into RDLM's flattened base-n digit rows."""

    torch = _require_torch()
    if getattr(token_ids, "ndim", None) != 2:
        raise ValueError("token IDs must be rank two")
    if type(base) is not int or base <= 1:
        raise ValueError("base must be greater than one")
    if type(digits_per_token) is not int or digits_per_token <= 0:
        raise ValueError("digits_per_token must be positive")
    powers = base ** torch.arange(digits_per_token, device=token_ids.device)
    digits = (token_ids.unsqueeze(-1) // powers) % base
    return digits.reshape(token_ids.shape[0], -1)


def clamp_rdlm_prefix(state, prefix_ids, *, base: int, digits_per_token: int):
    """Return a copy of an RDLM manifold state with clean base-n prefix digits."""

    torch = _require_torch()
    if getattr(state, "ndim", None) != 3:
        raise ValueError("RDLM state must be rank three")
    prefix_digits = base_n_digits(
        prefix_ids.to(device=state.device),
        base=base,
        digits_per_token=digits_per_token,
    )
    if state.shape[0] != prefix_digits.shape[0] or state.shape[1] < prefix_digits.shape[1]:
        raise ValueError("RDLM state is incompatible with prefix batch")
    if int(prefix_digits.max().item()) >= state.shape[-1] or int(prefix_digits.min().item()) < 0:
        raise ValueError("RDLM prefix digit is outside state dimension")
    clean = torch.nn.functional.one_hot(prefix_digits, num_classes=state.shape[-1]).to(state)
    result = state.clone()
    result[:, : prefix_digits.shape[1], :] = clean
    return result


def clamp_embedding_prefix(state, clean_embeddings):
    """Return a copy of an embedding state with clean prefix embeddings restored."""

    if getattr(state, "ndim", None) != 3 or getattr(clean_embeddings, "ndim", None) != 3:
        raise ValueError("embedding state and clean prefix must be rank three")
    if (
        state.shape[0] != clean_embeddings.shape[0]
        or state.shape[1] < clean_embeddings.shape[1]
        or state.shape[2] != clean_embeddings.shape[2]
    ):
        raise ValueError("embedding state is incompatible with clean prefix embeddings")
    result = state.clone()
    result[:, : clean_embeddings.shape[1], :] = clean_embeddings.to(state)
    return result


def token_project_fn(prefix_ids):
    """Build a repeatable token-prefix projector closure."""

    if getattr(prefix_ids, "ndim", None) != 2:
        raise ValueError("token prefix must be rank two")
    prefix = prefix_ids.clone()

    def project(state):
        return clamp_token_prefix(state, prefix)

    project.conditioning_implementation = "zero_shot_runtime_projection"  # type: ignore[attr-defined]
    return project


def vocab_project_fn(prefix_ids):
    """Build a repeatable one-hot-prefix projector closure."""

    if getattr(prefix_ids, "ndim", None) != 2:
        raise ValueError("vocabulary prefix must be rank two")
    prefix = prefix_ids.clone()

    def project(state):
        return clamp_vocab_prefix(state, prefix.to(device=state.device))

    project.conditioning_implementation = "zero_shot_runtime_projection"  # type: ignore[attr-defined]
    return project


def rdlm_project_fn(prefix_ids, *, base: int, digits_per_token: int):
    """Build a repeatable RDLM base-n digit prefix projector closure."""

    if getattr(prefix_ids, "ndim", None) != 2:
        raise ValueError("RDLM prefix must be rank two")
    prefix = prefix_ids.clone()

    def project(state):
        return clamp_rdlm_prefix(
            state,
            prefix.to(device=state.device),
            base=base,
            digits_per_token=digits_per_token,
        )

    project.conditioning_implementation = "zero_shot_runtime_projection"  # type: ignore[attr-defined]
    return project


def embedding_project_fn(clean_embeddings):
    """Build a repeatable embedding-prefix projector closure."""

    if getattr(clean_embeddings, "ndim", None) != 3:
        raise ValueError("clean embedding prefix must be rank three")
    clean = clean_embeddings.clone()

    def project(state):
        return clamp_embedding_prefix(state, clean.to(device=state.device, dtype=state.dtype))

    project.conditioning_implementation = "zero_shot_runtime_projection"  # type: ignore[attr-defined]
    return project


def candi_prompt_conditioning(prefix_ids, *, sequence_length: int):
    """Return full-canvas prompt tokens and numeric mask for CANDI's sampler."""

    torch = _require_torch()
    if getattr(prefix_ids, "ndim", None) != 2:
        raise ValueError("CANDI prompt prefix must be rank two")
    if type(sequence_length) is not int or sequence_length < int(prefix_ids.shape[1]):
        raise ValueError("CANDI prompt sequence_length cannot be shorter than prefix")
    prompt_tokens = torch.zeros(
        (prefix_ids.shape[0], sequence_length),
        dtype=prefix_ids.dtype,
        device=prefix_ids.device,
    )
    prompt_tokens[:, : prefix_ids.shape[1]] = prefix_ids
    prompt_mask = torch.zeros(
        (prefix_ids.shape[0], sequence_length),
        dtype=torch.float32,
        device=prefix_ids.device,
    )
    prompt_mask[:, : prefix_ids.shape[1]] = 1.0
    return prompt_tokens, prompt_mask


def adapt_candi_generate_sample_prompt(original):
    """Adapt CANDI's prompt hook across its mixed numeric/bool mask uses."""

    torch = _require_torch()

    def generate_sample_prompt(prompt_tokens, x, prompt_mask):
        numeric_mask = prompt_mask.to(device=x.device, dtype=x.dtype)
        prompt_tokens = prompt_tokens.to(device=x.device)
        prompted, clean_mask = original(prompt_tokens, x, numeric_mask)
        if clean_mask.dtype != torch.bool:
            clean_mask = clean_mask.to(dtype=torch.bool)
        return prompted, clean_mask

    return generate_sample_prompt


@contextmanager
def patched_attribute(owner: object, name: str, replacement: object) -> Iterator[None]:
    """Temporarily replace an attribute and always restore the original value."""

    original = getattr(owner, name)
    setattr(owner, name, replacement)
    try:
        yield
    finally:
        setattr(owner, name, original)


def _unique_object(pairs: list[tuple[str, object]]) -> dict[str, object]:
    result: dict[str, object] = {}
    for key, value in pairs:
        if key in result:
            raise SampleValidationError(f"duplicate JSON key {key!r}")
        result[key] = value
    return result


def _read_prompt_records(manifest: PromptManifest, root: Path) -> list[PromptRecord]:
    prompt_path = root / manifest.prompt_file
    if prompt_path.is_symlink() or not prompt_path.is_file():
        raise ValueError(f"conditional prompt file is missing or unsafe: {prompt_path}")
    if sha256_file(prompt_path) != manifest.prompt_file_sha256:
        raise ValueError("conditional prompt file SHA-256 differs from manifest")
    records: list[PromptRecord] = []
    with prompt_path.open("r", encoding="utf-8") as handle:
        for index, raw_line in enumerate(handle):
            if not raw_line.strip():
                raise ValueError(f"conditional prompt record {index} is blank")
            value = json.loads(raw_line, object_pairs_hook=_unique_object)
            record = PromptRecord.model_validate(value)
            if record.prompt_id != index:
                raise ValueError(f"expected prompt_id {index}, found {record.prompt_id}")
            records.append(record)
    if len(records) != manifest.prompt_count:
        raise ValueError(
            f"expected {manifest.prompt_count} prompt records, found {len(records)}"
        )
    return records


def _schedule_limit(manifest: PromptManifest, completion_id: int) -> int:
    if type(completion_id) is not int or completion_id < 0 or completion_id > 4:
        raise ValueError("completion_id must be an integer in [0, 4]")
    if completion_id == 0:
        return manifest.prompt_count
    return min(256, manifest.prompt_count)


def load_conditioning_batch(
    manifest_path: Path,
    expected_manifest_sha256: str,
    *,
    completion_id: int,
    prompt_start: int,
    batch_size: int,
    device: str,
    vocab_size: int,
) -> ConditioningBatch:
    """Load one exact prompt/completion batch from a SHA-bound prompt manifest."""

    torch = _require_torch()
    manifest_path = manifest_path.resolve()
    if manifest_path.is_symlink() or not manifest_path.is_file():
        raise ValueError(f"conditional prompt manifest is missing or unsafe: {manifest_path}")
    if sha256_file(manifest_path) != expected_manifest_sha256:
        raise ValueError("conditional prompt manifest SHA-256 differs from request")
    if type(prompt_start) is not int or prompt_start < 0:
        raise ValueError("prompt_start must be a non-negative integer")
    if type(batch_size) is not int or batch_size <= 0:
        raise ValueError("batch_size must be a positive integer")
    if type(vocab_size) is not int or vocab_size <= 0:
        raise ValueError("vocab_size must be a positive integer")
    manifest = PromptManifest.model_validate(
        json.loads(manifest_path.read_text(encoding="utf-8"), object_pairs_hook=_unique_object)
    )
    manifest_vocab_size = int(manifest.vocabulary_size)
    if vocab_size < manifest_vocab_size:
        raise ValueError("conditioning runtime vocabulary size is smaller than prompt manifest")
    limit = _schedule_limit(manifest, completion_id)
    if prompt_start + batch_size > limit:
        raise ValueError("conditioning batch crosses its completion schedule boundary")
    records = _read_prompt_records(manifest, manifest_path.parents[2])
    selected = records[prompt_start : prompt_start + batch_size]
    prefix_rows = [record.prefix_token_ids for record in selected]
    reference_rows = [record.reference_token_ids for record in selected]
    for row in (*prefix_rows, *reference_rows):
        if len(row) != 64 or any(token_id >= manifest_vocab_size for token_id in row):
            raise ValueError("conditioning prompt tokens violate length or vocabulary bounds")
    return ConditioningBatch(
        prompt_ids=tuple(record.prompt_id for record in selected),
        source_indices=tuple(record.source_index for record in selected),
        prefix_token_ids=torch.as_tensor(prefix_rows, dtype=torch.long, device=device),
        reference_token_ids=torch.as_tensor(reference_rows, dtype=torch.long, device=device),
        completion_id=completion_id,
    )