File size: 9,998 Bytes
e1acc15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Text-free sequence provenance, token-ID spans and explicit label policy.

This is additive sidecar metadata (own schema), not a tensor-schema revision.
No decoding, model call, source text or target tensor is needed.
"""
from __future__ import annotations

from collections.abc import Mapping, Sequence
import hashlib
import json
import re
from pathlib import Path
from typing import cast

from .split_audit import field
from .assembly_paths import AssemblyPaths

SCHEMA = 'd2.sequence-audit.v1'
MARKERS = ('think_start', 'think_end', 'assistant', 'user', 'eos', 'dsml')


def object_map(value: object, name: str) -> Mapping[str, object]:
    if not isinstance(value, dict):
        raise ValueError(f'{name} must be a metadata object')
    return cast(Mapping[str, object], value)


def digest(value: object) -> str:
    return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(',', ':'),
                                     allow_nan=False).encode()).hexdigest()


def hash_field(row: Mapping[str, object], key: str) -> str:
    value = field(row, key)
    if not re.fullmatch('[0-9a-f]{64}', value):
        raise ValueError(f'{key} must be a SHA256 identity')
    return value


def validate_context(context: Mapping[str, object], prompt_manifest: Mapping[str, object],
                     *, require_chat_match: bool = True, paths: AssemblyPaths | None = None,
                     mode: str = 'prefill_replay') -> None:
    if context.get('schema') != 'd2.sequence-context.v1':
        raise ValueError('expected d2.sequence-context.v1')
    target = object_map(context.get('target'), 'target')
    key = 'decode_capture_target' if mode == 'decode_capture' else 'replay_target'
    if mode == 'decode_capture' and context.get('mode') != 'decode_capture':
        raise ValueError('decode capture requires an explicit decode-capture context')
    replay = object_map(context.get(key), key)
    for identity in (target, replay):
        for key in ('weights_manifest_sha256', 'quantization_manifest_sha256', 'receipt_sha256'):
            hash_field(identity, key)
        for key in ('model_root', 'quantization', 'engine_revision'):
            field(identity, key)
    identity_keys = ('model_root', 'quantization', 'engine_revision',
                     'weights_manifest_sha256', 'quantization_manifest_sha256')
    same = (all(target[k] == replay[k] for k in identity_keys) if mode == 'decode_capture' else
            {k: v for k, v in target.items() if k != 'receipt_sha256'} ==
            {k: v for k, v in replay.items() if k != 'receipt_sha256'})
    if not same:
        raise ValueError('generation and replay target/quantization/engine identity differ')
    inputs = object_map(prompt_manifest.get('inputs'), 'prompt manifest inputs')
    for key in ('encoder_sha256', 'tokenizer_sha256'):
        if hash_field(context, key) != inputs.get(key):
            raise ValueError(f'{key} differs from prompt manifest')
    hash_field(context, 'chat_template_sha256')
    markers = object_map(context.get('markers'), 'markers')
    values: set[int] = set()
    for key in MARKERS:
        value = markers.get(key)
        if not isinstance(value, int) or isinstance(value, bool) or not 0 <= value < 129280 or value in values:
            raise ValueError('markers must be distinct tokenizer-verified token IDs')
        values.add(value)
    # Verify marker IDs from the hashed tokenizer, never trust an arbitrary map.
    from tokenizers import Tokenizer
    for label in ('tokenizer', 'encoder', 'chat_template'):
        original = field(context, label + '_path')
        path = paths.resolve(original) if paths else Path(original)
        if hashlib.sha256(path.read_bytes()).hexdigest() != context[label + '_sha256']:
            raise ValueError(f'{label} file hash differs from context')
    tokenizer_path = field(context, 'tokenizer_path')
    tokenizer = Tokenizer.from_file(str(paths.resolve(tokenizer_path)) if paths else tokenizer_path)
    strings = ('<think>', '</think>', '<|Assistant|>', '<|User|>', '<|end▁of▁sentence|>', '|DSML|')
    if any(tokenizer.token_to_id(text) != markers[key] for key, text in zip(MARKERS, strings)):
        raise ValueError('marker IDs differ from hashed tokenizer')
    sampling = object_map(context.get('sampling'), 'sampling')
    if sampling.get('temperature') != 1.0 or sampling.get('top_p') != 1.0:
        raise ValueError('D2 sampling must preserve target T=1 / top_p=1')
    for key in ('top_k', 'min_p', 'structured_outputs', 'stop', 'stop_token_ids', 'seed'):
        if key not in sampling:
            raise ValueError(f'sampling must explicitly record {key}, including null/default')
    formatting = object_map(context.get('formatting'), 'formatting')
    if require_chat_match and formatting.get('chat_template_kwargs') != prompt_manifest.get('chat_template_kwargs'):
        raise ValueError('chat template kwargs differ from prompt manifest')
    if 'tools' not in formatting or 'tool_choice' not in formatting:
        raise ValueError('formatting must explicitly record tools and tool_choice')


def token_spans(tokens: Sequence[int], gen_start: int, markers: Mapping[str, int]) -> list[dict[str, object]]:
    """Half-open absolute intervals; token controls are separate from content.

    Carry reasoning state from the raw prompt across every cancellation boundary.
    A response cut before </think> stays reasoning, never an invented answer.
    DSML generation is marked tool_call until EOS; raw IDs remain authoritative.
    """
    if not 1 <= gen_start <= len(tokens):
        raise ValueError('invalid generated span')
    controls = {value: key for key, value in markers.items()}
    phase = 'answer'
    spans: list[dict[str, object]] = []
    for pos, token in enumerate(tokens):
        control = controls.get(token)
        kind = 'format' if control else phase
        if pos >= gen_start:
            if spans and spans[-1]['kind'] == kind:
                spans[-1]['end'] = pos + 1
            else:
                spans.append({'start': pos, 'end': pos + 1, 'kind': kind})
        if control == 'think_start':
            phase = 'reasoning'
        elif control in ('think_end', 'assistant', 'user', 'eos'):
            phase = 'answer'
        elif control == 'dsml':
            phase = 'tool_call'
    return spans


def segment_coverage(segments: Sequence[Mapping[str, object]], length: int) -> dict[str, object]:
    """Generated-token offsets; gaps and explicit unrecorded segments carry no inferred revision."""
    cursor = recorded = 0
    unknown: list[list[int]] = []
    for segment in segments:
        start, n = segment.get('start'), segment.get('n')
        if (not isinstance(start, int) or isinstance(start, bool) or not isinstance(n, int)
                or isinstance(n, bool) or start < cursor or n < 0 or start + n > length):
            raise ValueError('invalid or overlapping generation segment coverage')
        if start > cursor:
            unknown.append([cursor, start])
        if segment.get('unrecorded') is True:
            if n:
                unknown.append([start, start + n])
        else:
            recorded += n
        cursor = start + n
    if cursor < length:
        unknown.append([cursor, length])
    return {'offset_origin': 'gen_start', 'recorded_tokens': recorded,
            'unrecorded_tokens': length - recorded, 'unrecorded_intervals': unknown}


def sequence_audit(tokens: Sequence[int], gen_start: int, context: Mapping[str, object],
                   source: Mapping[str, object], kind: str) -> dict[str, object]:
    markers = cast(Mapping[str, int], object_map(context['markers'], 'markers'))
    spans = token_spans(tokens, gen_start, markers)
    segments = source.get('segments', [])
    if kind == 'gen' and not isinstance(segments, list):
        raise ValueError('generation segments must be a list')
    for segment in cast(list[Mapping[str, object]], segments if isinstance(segments, list) else []):
        recorded = segment.get('sampling')
        if recorded is not None:
            sampling = object_map(recorded, 'segment sampling')
            expected = object_map(context['sampling'], 'context sampling')
            if any(key in expected and expected[key] != value for key, value in sampling.items()):
                raise ValueError('segment sampling differs from context receipt')
    return {'schema': SCHEMA, 'context_sha256': digest(context), 'identity': dict(context),
            'absolute_positions': {'origin': 0, 'length': len(tokens), 'gen_start': gen_start,
                                   'tap_start': max(0, gen_start - 128)},
            'response_spans': spans,
            'label_policy': {'scope': 'response_tokens_only_including_reasoning_and_format',
                'prompt_labels': False, 'response_interval': [gen_start, len(tokens)],
                'eligible_draft_targets': [gen_start + 1, len(tokens)],
                'first_response_token': 'anchor/correction, not a draft-label position',
                'tail': 'P+2+i < L; other labels=-1, position_mask=false'},
            'alignment': {'tap_last': 'P', 'anchor': 'P+1', 'query_positions': 'P+1..P+5',
                          'target': 'P+2+i', 'teacher_forced_previous': 'P+1+i',
                          'teacher_logprob': 'p(tokens[t] | tokens[:t])', 'future_taps_allowed': False},
            'source_state_schema': source.get('schema'), 'source_kind': kind,
            'local_prompt_ids_match': source.get('local_ids_match'),
            'segments': source.get('segments'),
            'segment_coverage': segment_coverage(cast(list[Mapping[str, object]], segments), len(tokens) - gen_start)
                                if kind == 'gen' else None,
            'legacy_segment_policy': 'recorded segments missing priority/revision = historical e2b2186 priority5; '
                                     'gaps and unrecorded segments have unknown provenance; never relabel'}