tokenfold-select 0.5.2__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.
- tokenfold_select/__init__.py +7 -0
- tokenfold_select/__main__.py +133 -0
- tokenfold_select/_model.py +62 -0
- tokenfold_select/_windows.py +107 -0
- tokenfold_select/artifact.py +83 -0
- tokenfold_select/bootstrap.py +21 -0
- tokenfold_select/onnx.py +85 -0
- tokenfold_select/protocol.py +69 -0
- tokenfold_select/runtime.py +122 -0
- tokenfold_select-0.5.2.dist-info/METADATA +332 -0
- tokenfold_select-0.5.2.dist-info/RECORD +14 -0
- tokenfold_select-0.5.2.dist-info/WHEEL +5 -0
- tokenfold_select-0.5.2.dist-info/licenses/LICENSE +201 -0
- tokenfold_select-0.5.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Optional development-only scorer; importing never loads a model/framework."""
|
|
2
|
+
from .artifact import inspect_artifact
|
|
3
|
+
from .runtime import DevelopmentScorer
|
|
4
|
+
from .onnx import OnnxScorer, inspect_onnx
|
|
5
|
+
|
|
6
|
+
__all__ = ["OnnxScorer", "inspect_onnx", "DevelopmentScorer", "inspect_artifact"]
|
|
7
|
+
__version__ = "0.5.2"
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Inspection or the existing one-shot Select file protocol; no downloads."""
|
|
2
|
+
import argparse
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import sys
|
|
8
|
+
import threading
|
|
9
|
+
|
|
10
|
+
from .artifact import inspect_artifact
|
|
11
|
+
from .protocol import REQUEST_BYTES, encode_response, load_request
|
|
12
|
+
from .runtime import DevelopmentScorer
|
|
13
|
+
from .onnx import DEFAULT_MANIFEST_SHA256, DEFAULT_REVISION, OnnxScorer, inspect_onnx
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def configure(args):
|
|
17
|
+
"""Generate approval only on explicit request; never download or invoke a model."""
|
|
18
|
+
model = inspect_onnx(args.artifact, args.manifest_sha256)
|
|
19
|
+
if model['source_checkpoint_sha256'] != args.revision:
|
|
20
|
+
raise ValueError('model_revision_mismatch')
|
|
21
|
+
if (not args.approved_by.strip() or not args.scratch_root.is_absolute()
|
|
22
|
+
or not args.scratch_root.is_dir() or not args.site_packages.is_absolute()
|
|
23
|
+
or not args.site_packages.is_dir()):
|
|
24
|
+
raise ValueError('explicit_approval_and_absolute_existing_directories_required')
|
|
25
|
+
executable = Path(sys._base_executable).resolve(strict=True)
|
|
26
|
+
if executable.stat().st_size > 64 * 1024 * 1024:
|
|
27
|
+
raise ValueError('executable_byte_limit')
|
|
28
|
+
bootstrap = Path(__file__).with_name('bootstrap.py').resolve()
|
|
29
|
+
with executable.open('rb') as stream:
|
|
30
|
+
pin = hashlib.file_digest(stream, 'sha256').hexdigest()
|
|
31
|
+
return dict(executable=str(executable), executable_sha256=pin,
|
|
32
|
+
arguments=['-I', '-S', str(bootstrap), '--site-packages', str(args.site_packages.resolve()),
|
|
33
|
+
'worker', '--artifact', str(args.artifact.resolve()), '--revision', args.revision,
|
|
34
|
+
'--manifest-sha256', args.manifest_sha256, '--development'],
|
|
35
|
+
model_revision=args.revision, approved_by=args.approved_by,
|
|
36
|
+
scratch_root=str(args.scratch_root.resolve()))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _worker(args):
|
|
40
|
+
# A native direct child is required. This timer never starts on import/inspection.
|
|
41
|
+
timer = threading.Timer(30, lambda: os._exit(124))
|
|
42
|
+
timer.daemon = True
|
|
43
|
+
timer.start()
|
|
44
|
+
code = 1
|
|
45
|
+
try:
|
|
46
|
+
# Set before importing any inference library. ONNX is explicitly CPU-only.
|
|
47
|
+
if args.backend == 'onnx':
|
|
48
|
+
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
|
|
49
|
+
os.environ['TOKENIZERS_PARALLELISM'] = 'false'
|
|
50
|
+
if os.name == 'nt':
|
|
51
|
+
# The Rust parent clears the environment. Obtain the system path
|
|
52
|
+
# from Windows, not an inherited/user-controlled environment value.
|
|
53
|
+
import ctypes
|
|
54
|
+
system = ctypes.create_unicode_buffer(32768)
|
|
55
|
+
length = ctypes.windll.kernel32.GetWindowsDirectoryW(system, len(system))
|
|
56
|
+
if not 0 < length < len(system):
|
|
57
|
+
raise ValueError('windows_system_directory_unavailable')
|
|
58
|
+
os.environ['SystemRoot'] = system.value
|
|
59
|
+
request_path = Path(os.environ['TOKENFOLD_SELECT_REQUEST_PATH'])
|
|
60
|
+
response_path = Path(os.environ['TOKENFOLD_SELECT_RESPONSE_PATH'])
|
|
61
|
+
if (not request_path.is_absolute() or not response_path.is_absolute()
|
|
62
|
+
or request_path.resolve().parent != response_path.resolve().parent
|
|
63
|
+
or request_path.resolve() == response_path.resolve() or response_path.exists()):
|
|
64
|
+
raise ValueError('caller_owned_request_response_paths_required')
|
|
65
|
+
def offline(event, _):
|
|
66
|
+
if event in ('socket.connect', 'socket.getaddrinfo', 'socket.bind', 'subprocess.Popen', 'os.system'):
|
|
67
|
+
raise ValueError('offline_direct_child_only')
|
|
68
|
+
sys.addaudithook(offline)
|
|
69
|
+
with request_path.open('rb') as stream:
|
|
70
|
+
request = load_request(stream.read(REQUEST_BYTES + 1), args.revision)
|
|
71
|
+
if args.backend == 'onnx':
|
|
72
|
+
scorer = OnnxScorer(args.artifact, args.manifest_sha256, development=args.development)
|
|
73
|
+
if scorer.revision != args.revision:
|
|
74
|
+
raise ValueError('model_revision_mismatch')
|
|
75
|
+
else:
|
|
76
|
+
scorer = DevelopmentScorer(args.artifact, args.revision, cache_directory=request_path.parent / 'model-cache',
|
|
77
|
+
development=args.development, precision='fp32')
|
|
78
|
+
response = scorer.score(request)
|
|
79
|
+
raw = encode_response(request, [value for _, value in response['scores']])
|
|
80
|
+
with response_path.open('xb') as stream:
|
|
81
|
+
stream.write(raw)
|
|
82
|
+
if args.backend == 'pytorch':
|
|
83
|
+
scorer._torch.cuda.synchronize()
|
|
84
|
+
code = 0
|
|
85
|
+
except BaseException:
|
|
86
|
+
print('tokenfold-select: refused', file=sys.stderr, flush=True)
|
|
87
|
+
finally:
|
|
88
|
+
# Avoid potentially unbounded framework teardown; native parent owns kill/wait.
|
|
89
|
+
sys.stdout.flush()
|
|
90
|
+
sys.stderr.flush()
|
|
91
|
+
os._exit(code)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def main(arguments=None):
|
|
95
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
96
|
+
sub = parser.add_subparsers(dest='command', required=True)
|
|
97
|
+
for command in ('inspect', 'inspect-onnx', 'configure', 'worker'):
|
|
98
|
+
mode = sub.add_parser(command)
|
|
99
|
+
mode.add_argument('--artifact', type=Path, required=True)
|
|
100
|
+
mode.add_argument('--revision', default=DEFAULT_REVISION)
|
|
101
|
+
mode.add_argument('--manifest-sha256', default=DEFAULT_MANIFEST_SHA256)
|
|
102
|
+
if command == 'worker':
|
|
103
|
+
mode.add_argument('--backend', choices=('onnx', 'pytorch'), default='onnx')
|
|
104
|
+
mode.add_argument('--development', action='store_true', required=True)
|
|
105
|
+
if command == 'configure':
|
|
106
|
+
mode.add_argument('--approved-by', required=True)
|
|
107
|
+
mode.add_argument('--site-packages', type=Path, required=True)
|
|
108
|
+
mode.add_argument('--scratch-root', type=Path, required=True)
|
|
109
|
+
args = parser.parse_args(arguments)
|
|
110
|
+
if args.command == 'worker':
|
|
111
|
+
_worker(args)
|
|
112
|
+
try:
|
|
113
|
+
if args.command == 'configure':
|
|
114
|
+
print(json.dumps(configure(args)))
|
|
115
|
+
return 0
|
|
116
|
+
if args.command == 'inspect-onnx':
|
|
117
|
+
report = inspect_onnx(args.artifact, args.manifest_sha256)
|
|
118
|
+
if report['source_checkpoint_sha256'] != args.revision:
|
|
119
|
+
raise ValueError('model_revision_mismatch')
|
|
120
|
+
report = dict(checkpoint_sha256=report['source_checkpoint_sha256'])
|
|
121
|
+
else:
|
|
122
|
+
report = inspect_artifact(args.artifact, args.revision)
|
|
123
|
+
except (OSError, ValueError, KeyError, TypeError, AttributeError):
|
|
124
|
+
print('tokenfold-select: refused', file=sys.stderr)
|
|
125
|
+
return 1
|
|
126
|
+
print(json.dumps(dict(verification='metadata_and_hashes_only', checkpoint_tensors_loaded=False,
|
|
127
|
+
model_revision=report['checkpoint_sha256'], development_only=True,
|
|
128
|
+
qualification=False, release_approved=False, redistribution_cleared=False)))
|
|
129
|
+
return 0
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
if __name__ == '__main__':
|
|
133
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Development-only joint group encoder. Random head: NOT a usable Select scorer.
|
|
2
|
+
|
|
3
|
+
No downloads, truncation, windowing, generated text or production integration.
|
|
4
|
+
The caller owns request validation and supplies the provisioned encoder/tokenizer.
|
|
5
|
+
"""
|
|
6
|
+
import torch
|
|
7
|
+
from torch import nn
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def pack_groups(tokenizer, query, groups, cap=8192):
|
|
11
|
+
"""Return one sequence and explicit content ranges in caller order."""
|
|
12
|
+
if not 1 <= cap <= 8192 or not query or not 1 <= len(groups) <= 512:
|
|
13
|
+
raise ValueError("invalid_admission")
|
|
14
|
+
ids_seen = set()
|
|
15
|
+
cls, sep = tokenizer.cls_token_id, tokenizer.sep_token_id
|
|
16
|
+
if cls is None or sep is None:
|
|
17
|
+
raise ValueError("missing_special_tokens")
|
|
18
|
+
ids = [cls] + tokenizer.encode(query, add_special_tokens=False, truncation=False) + [sep]
|
|
19
|
+
ranges = []
|
|
20
|
+
for group in groups:
|
|
21
|
+
gid, text = group['id'], group['text']
|
|
22
|
+
if not isinstance(gid, str) or not gid or gid in ids_seen or not isinstance(text, str) or not text:
|
|
23
|
+
raise ValueError("invalid_group")
|
|
24
|
+
ids_seen.add(gid)
|
|
25
|
+
content = tokenizer.encode(text, add_special_tokens=False, truncation=False)
|
|
26
|
+
if not content:
|
|
27
|
+
raise ValueError("empty_group_range")
|
|
28
|
+
start = len(ids)
|
|
29
|
+
ids.extend(content)
|
|
30
|
+
ranges.append((start, len(ids)))
|
|
31
|
+
ids.append(sep)
|
|
32
|
+
if len(ids) > cap:
|
|
33
|
+
raise ValueError("token_cap")
|
|
34
|
+
return ids, ranges
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class JointGroupModel(nn.Module):
|
|
38
|
+
"""One encoder invocation, masked content mean and a shared linear head."""
|
|
39
|
+
|
|
40
|
+
def __init__(self, encoder):
|
|
41
|
+
super().__init__()
|
|
42
|
+
self.encoder = encoder
|
|
43
|
+
self.head = nn.Linear(encoder.config.hidden_size, 1)
|
|
44
|
+
|
|
45
|
+
def forward(self, input_ids, attention_mask, ranges):
|
|
46
|
+
if input_ids.ndim != 2 or input_ids.shape[0] != 1 or attention_mask.shape != input_ids.shape:
|
|
47
|
+
raise ValueError("single_sequence_required")
|
|
48
|
+
previous = 0
|
|
49
|
+
if not 1 <= len(ranges) <= 512:
|
|
50
|
+
raise ValueError("invalid_ranges")
|
|
51
|
+
for start, end in ranges:
|
|
52
|
+
if not previous <= start < end <= input_ids.shape[1] or not attention_mask[0, start:end].all():
|
|
53
|
+
raise ValueError("invalid_ranges")
|
|
54
|
+
previous = end
|
|
55
|
+
positions = torch.arange(input_ids.shape[1], device=input_ids.device).unsqueeze(0)
|
|
56
|
+
states = self.encoder(input_ids=input_ids, attention_mask=attention_mask,
|
|
57
|
+
position_ids=positions).last_hidden_state[0]
|
|
58
|
+
pooled = torch.stack([states[start:end].mean(0) for start, end in ranges])
|
|
59
|
+
scores = self.head(pooled).squeeze(-1)
|
|
60
|
+
if not torch.isfinite(scores).all():
|
|
61
|
+
raise ValueError("nonfinite_scores")
|
|
62
|
+
return scores
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Bounded source-conditioned windows, shared by future training and inference.
|
|
2
|
+
|
|
3
|
+
Original groups are never cropped or split. A bounded auxiliary source-tail copy
|
|
4
|
+
is unscored encoder context, not extra retained text or consumer input.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import hashlib
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
PROFILE = "source-tail-1536-capacity-v2"
|
|
11
|
+
CONTEXT_TOKENS = 1536
|
|
12
|
+
WINDOW_TOKENS = 8192
|
|
13
|
+
MAX_GROUPS = 512
|
|
14
|
+
MAX_WINDOWS = 64
|
|
15
|
+
MAX_TOTAL_TOKENS = MAX_WINDOWS * WINDOW_TOKENS
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def code_hash():
|
|
19
|
+
return hashlib.sha256(Path(__file__).read_bytes()).hexdigest()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def verify_profile(report):
|
|
23
|
+
recipe = report.get("recipe", {})
|
|
24
|
+
if (recipe.get("scoring_window_profile") != PROFILE
|
|
25
|
+
or recipe.get("scoring_context_token_cap") != CONTEXT_TOKENS
|
|
26
|
+
or recipe.get("scoring_window_code_sha256") != code_hash()):
|
|
27
|
+
raise ValueError("matching_trained_source_context_profile_required")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def pack_windows(tokenizer, config, request):
|
|
31
|
+
"""Return legacy-shaped windows and a non-qualifying packing receipt.
|
|
32
|
+
|
|
33
|
+
Tokenization is once per query/group. Each original group has one scored
|
|
34
|
+
range; auxiliary copies have no ranges. All admission finishes before forward.
|
|
35
|
+
"""
|
|
36
|
+
groups = request["groups"]
|
|
37
|
+
if not isinstance(groups, list) or not 1 <= len(groups) <= MAX_GROUPS:
|
|
38
|
+
raise ValueError("bounded_nonempty_groups_required")
|
|
39
|
+
if (not isinstance(request.get("query"), str) or not request["query"].strip()
|
|
40
|
+
or any(not isinstance(g, dict) or not isinstance(g.get("text"), str) or not g["text"] for g in groups)):
|
|
41
|
+
raise ValueError("original_query_and_group_text_required")
|
|
42
|
+
tokenizer.no_truncation(); tokenizer.no_padding()
|
|
43
|
+
def special(name):
|
|
44
|
+
value = config[name]
|
|
45
|
+
token = tokenizer.token_to_id(value["content"] if isinstance(value, dict) else value)
|
|
46
|
+
if token is None: raise ValueError("missing_special_token")
|
|
47
|
+
return token
|
|
48
|
+
cls, sep = special("cls_token"), special("sep_token")
|
|
49
|
+
query = [cls] + tokenizer.encode(request["query"], add_special_tokens=False).ids + [sep]
|
|
50
|
+
if len(query) >= WINDOW_TOKENS: raise ValueError("query_token_cap")
|
|
51
|
+
encoded = [tokenizer.encode(group["text"], add_special_tokens=False).ids for group in groups]
|
|
52
|
+
if any(not ids or len(query) + len(ids) + 1 > WINDOW_TOKENS for ids in encoded):
|
|
53
|
+
raise ValueError("whole_group_token_cap")
|
|
54
|
+
source_tokens = sum(len(ids) + 1 for ids in encoded)
|
|
55
|
+
if source_tokens + len(query) > MAX_TOTAL_TOKENS:
|
|
56
|
+
raise ValueError("total_token_cap")
|
|
57
|
+
context = []; capacity_checks = 0; compatible_allowance = 0; reduced = False
|
|
58
|
+
if source_tokens + len(query) > WINDOW_TOKENS:
|
|
59
|
+
allowance = min(CONTEXT_TOKENS, WINDOW_TOKENS - len(query) - max(map(len, encoded)) - 2)
|
|
60
|
+
if allowance < 1: raise ValueError("whole_group_leaves_no_source_context")
|
|
61
|
+
compatible_allowance = allowance
|
|
62
|
+
def demand(amount):
|
|
63
|
+
nonlocal capacity_checks
|
|
64
|
+
capacity_checks += 1
|
|
65
|
+
header = len(query) + amount + 1
|
|
66
|
+
count = 1; used = header
|
|
67
|
+
for content in encoded:
|
|
68
|
+
length = len(content) + 1
|
|
69
|
+
if used + length > WINDOW_TOKENS:
|
|
70
|
+
count += 1; used = header
|
|
71
|
+
used += length
|
|
72
|
+
return count, source_tokens + count * header
|
|
73
|
+
count, total = demand(allowance)
|
|
74
|
+
if count > MAX_WINDOWS or total > MAX_TOTAL_TOKENS:
|
|
75
|
+
count, total = demand(1)
|
|
76
|
+
if count > MAX_WINDOWS: raise ValueError("window_count_cap")
|
|
77
|
+
if total > MAX_TOTAL_TOKENS: raise ValueError("total_token_cap")
|
|
78
|
+
# Largest feasible auxiliary copy, never fewer original source tokens.
|
|
79
|
+
low, high = 1, allowance
|
|
80
|
+
while low < high:
|
|
81
|
+
mid = (low + high + 1) // 2
|
|
82
|
+
count, total = demand(mid)
|
|
83
|
+
if count <= MAX_WINDOWS and total <= MAX_TOTAL_TOKENS: low = mid
|
|
84
|
+
else: high = mid - 1
|
|
85
|
+
allowance = low; reduced = True
|
|
86
|
+
for ids in encoded:
|
|
87
|
+
context = (context + [sep] + ids)[-allowance:]
|
|
88
|
+
header = query + context + [sep] if context else query
|
|
89
|
+
windows = []
|
|
90
|
+
ids, ranges, indices = list(header), [], []
|
|
91
|
+
for index, content in enumerate(encoded):
|
|
92
|
+
if len(ids) + len(content) + 1 > WINDOW_TOKENS:
|
|
93
|
+
windows.append((ids, ranges, indices))
|
|
94
|
+
if len(windows) >= MAX_WINDOWS: raise ValueError("window_count_cap")
|
|
95
|
+
ids, ranges, indices = list(header), [], []
|
|
96
|
+
start = len(ids); ids.extend(content); ids.append(sep)
|
|
97
|
+
ranges.append((start, start + len(content))); indices.append(index)
|
|
98
|
+
windows.append((ids, ranges, indices))
|
|
99
|
+
total = sum(len(window[0]) for window in windows)
|
|
100
|
+
if total > MAX_TOTAL_TOKENS: raise ValueError("total_token_cap")
|
|
101
|
+
return windows, {"profile": PROFILE, "context_tokens_per_window": len(context),
|
|
102
|
+
"max_context_tokens": CONTEXT_TOKENS,
|
|
103
|
+
"whole_group_compatible_context_tokens": compatible_allowance,
|
|
104
|
+
"context_reduced_for_capacity": reduced, "capacity_checks": capacity_checks,
|
|
105
|
+
"scored_source_tokens_with_separators": source_tokens,
|
|
106
|
+
"total_encoded_tokens_including_copies": total,
|
|
107
|
+
"windows": len(windows), "groups": len(groups), "qualification": False}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Portable integrity/development admission, never a model or rights certificate."""
|
|
2
|
+
import hashlib
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from . import _windows
|
|
6
|
+
from .protocol import decode
|
|
7
|
+
|
|
8
|
+
TOKENIZER_FILES = {'tokenizer.json', 'tokenizer_config.json', 'special_tokens_map.json'}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def sha(path, limit):
|
|
12
|
+
total = 0
|
|
13
|
+
digest = hashlib.sha256()
|
|
14
|
+
with Path(path).open('rb') as stream:
|
|
15
|
+
while chunk := stream.read(1024 * 1024):
|
|
16
|
+
total += len(chunk)
|
|
17
|
+
if total > limit:
|
|
18
|
+
raise ValueError('artifact_byte_limit')
|
|
19
|
+
digest.update(chunk)
|
|
20
|
+
return digest.hexdigest()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _file(root, name):
|
|
24
|
+
path = (root / name).resolve(strict=True)
|
|
25
|
+
if not path.is_relative_to(root) or not path.is_file():
|
|
26
|
+
raise ValueError('artifact_path_escape')
|
|
27
|
+
return path
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _json(root, name, limit):
|
|
31
|
+
path = _file(root, name)
|
|
32
|
+
with path.open('rb') as stream:
|
|
33
|
+
data = stream.read(limit + 1)
|
|
34
|
+
if len(data) > limit:
|
|
35
|
+
raise ValueError('artifact_byte_limit')
|
|
36
|
+
return decode(data)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def inspect_artifact(directory, revision):
|
|
40
|
+
"""Check bytes/declared completion only, with no imports of ML frameworks."""
|
|
41
|
+
if (not isinstance(revision, str) or len(revision) != 64
|
|
42
|
+
or any(c not in '0123456789abcdef' for c in revision)):
|
|
43
|
+
raise ValueError('checkpoint_SHA256_required')
|
|
44
|
+
root = Path(directory)
|
|
45
|
+
if not root.is_absolute():
|
|
46
|
+
raise ValueError('absolute_artifact_path_required')
|
|
47
|
+
root = root.resolve(strict=True)
|
|
48
|
+
report = _json(root, 'training-report.json', 1024 * 1024)
|
|
49
|
+
if (not isinstance(report, dict) or not isinstance(report.get('recipe'), dict)
|
|
50
|
+
or report.get('status') != 'completed_development_only'
|
|
51
|
+
or report.get('checkpoint_sha256') != revision
|
|
52
|
+
or report.get('mode') != 'supervised-foundation-feature-distilled'
|
|
53
|
+
or report.get('recipe', {}).get('objective_profile') != 'optional-balanced-window-hardpair-headwarm-v4'
|
|
54
|
+
or report.get('recipe', {}).get('precision') != 'fp32'
|
|
55
|
+
or report.get('recipe', {}).get('student_token_cap') != 8192):
|
|
56
|
+
raise ValueError('completed_clean_compact12_development_artifact_required')
|
|
57
|
+
_windows.verify_profile(report)
|
|
58
|
+
if sha(Path(__file__).with_name('_model.py'), 128 * 1024) != report.get('joint_model_code_sha256'):
|
|
59
|
+
raise ValueError('model_code_hash_mismatch')
|
|
60
|
+
config = _json(root, 'config.json', 65536)
|
|
61
|
+
if (not isinstance(config, dict) or config.get('model_type') != 'modernbert' or config.get('num_hidden_layers') != 12
|
|
62
|
+
or config.get('hidden_size') != 768 or config.get('max_position_embeddings') != 8192
|
|
63
|
+
or config.get('layer_types') != ['full_attention' if i % 3 == 0 else 'sliding_attention' for i in range(12)]):
|
|
64
|
+
raise ValueError('compact12_configuration_required')
|
|
65
|
+
if sha(_file(root, 'config.json'), 65536) != report.get('config_sha256'):
|
|
66
|
+
raise ValueError('config_hash_mismatch')
|
|
67
|
+
if sha(_file(root, 'checkpoint.pt'), 1024**3) != revision:
|
|
68
|
+
raise ValueError('weights_hash_mismatch')
|
|
69
|
+
pins = report.get('tokenizer_sha256')
|
|
70
|
+
if not isinstance(pins, dict) or set(pins) != TOKENIZER_FILES:
|
|
71
|
+
raise ValueError('complete_tokenizer_pins_required')
|
|
72
|
+
if {p.name for p in (root / 'tokenizer').iterdir()} != TOKENIZER_FILES:
|
|
73
|
+
raise ValueError('tokenizer_files_mismatch')
|
|
74
|
+
for name, digest in pins.items():
|
|
75
|
+
if sha(_file(root, 'tokenizer/' + name), 32 * 1024**2) != digest:
|
|
76
|
+
raise ValueError('tokenizer_hash_mismatch')
|
|
77
|
+
decision = _json(root, 'DEVELOPMENT-DECISION.json', 1024 * 1024)
|
|
78
|
+
if (not isinstance(decision, dict) or decision.get('checkpoint_sha256') != revision
|
|
79
|
+
or any(decision.get(key) is not True for key in
|
|
80
|
+
('training_completed', 'completion_content_audit_passed', 'development_stop_go_passed'))
|
|
81
|
+
or any(decision.get(key) is not False for key in ('qualification', 'release_approved', 'redistribution_cleared'))):
|
|
82
|
+
raise ValueError('accepted_development_decision_required_not_release')
|
|
83
|
+
return report
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Direct-native entrypoint for an approved caller-owned Python environment."""
|
|
2
|
+
import argparse
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
parser = argparse.ArgumentParser(add_help=False)
|
|
9
|
+
parser.add_argument('--site-packages', type=Path, required=True)
|
|
10
|
+
args, remaining = parser.parse_known_args()
|
|
11
|
+
if (not sys.flags.isolated or not sys.flags.no_site
|
|
12
|
+
or not args.site_packages.is_absolute() or not args.site_packages.is_dir()):
|
|
13
|
+
raise ValueError('isolated_native_caller_environment_required')
|
|
14
|
+
# No .pth processing, checkout imports, inherited PYTHONPATH or shell/shim.
|
|
15
|
+
sys.path[:0] = [str(Path(__file__).resolve().parents[1]), str(args.site_packages.resolve())]
|
|
16
|
+
from tokenfold_select.__main__ import main as run
|
|
17
|
+
return run(remaining)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == '__main__':
|
|
21
|
+
raise SystemExit(main())
|
tokenfold_select/onnx.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Bounded ONNX CPU reference scorer; no PyTorch, downloads or generated text."""
|
|
2
|
+
import hashlib
|
|
3
|
+
import importlib.metadata
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from . import _windows as packing
|
|
9
|
+
from . import protocol
|
|
10
|
+
|
|
11
|
+
CAPS={'model.onnx':64*1024**2,'model.onnx.data':1024**3,
|
|
12
|
+
'tokenizer/tokenizer.json':32*1024**2,'tokenizer/tokenizer_config.json':65536,
|
|
13
|
+
'tokenizer/special_tokens_map.json':65536,
|
|
14
|
+
'runtime/onnx_score.py':65536,'runtime/packing.py':65536,'runtime/protocol.py':65536}
|
|
15
|
+
DEFAULT_REVISION='4dbdb5071e7d6316d8eb70dc161b1f2f77f555012f6616d7fa34311188e24cec'
|
|
16
|
+
DEFAULT_MANIFEST_SHA256='267313718e80aa91a56923dea30363fd5c24fde3782c716d6d048891860a2054'
|
|
17
|
+
ENVIRONMENT={'onnxruntime':'1.30.0','numpy':'2.5.3','tokenizers':'0.23.2'}
|
|
18
|
+
|
|
19
|
+
def digest(path):
|
|
20
|
+
with Path(path).open('rb') as stream:return hashlib.file_digest(stream,'sha256').hexdigest()
|
|
21
|
+
|
|
22
|
+
def inspect_onnx(directory,manifest_sha256=DEFAULT_MANIFEST_SHA256):
|
|
23
|
+
root=Path(directory)
|
|
24
|
+
if not root.is_absolute() or not re.fullmatch(r'[0-9a-f]{64}',manifest_sha256):
|
|
25
|
+
raise ValueError('absolute_root_and_manifest_pin_required')
|
|
26
|
+
root=root.resolve(strict=True);path=root/'onnx-manifest.json'
|
|
27
|
+
if path.is_symlink() or path.stat().st_size>65536 or digest(path)!=manifest_sha256:
|
|
28
|
+
raise ValueError('manifest_pin_mismatch')
|
|
29
|
+
m=protocol.decode(path.read_bytes())
|
|
30
|
+
if (m.get('status')!='development_ONNX_export' or m.get('precision')!='fp32'
|
|
31
|
+
or m.get('provider')!='CPUExecutionProvider' or m.get('opset')!=18
|
|
32
|
+
or m.get('limits')!={'tokens':8192,'groups':512,'request_bytes':1048576,'response_bytes':65536}
|
|
33
|
+
or not re.fullmatch(r'[0-9a-f]{64}',m.get('source_checkpoint_sha256',''))
|
|
34
|
+
or m.get('qualification') is not False or set(m.get('files',{}))!=set(CAPS)):
|
|
35
|
+
raise ValueError('bounded_development_ONNX_profile_required')
|
|
36
|
+
for name,cap in CAPS.items():
|
|
37
|
+
path=root/name;pin=m['files'][name]
|
|
38
|
+
if (path.is_symlink() or not path.resolve(strict=True).is_relative_to(root)
|
|
39
|
+
or type(pin.get('bytes')) is not int or not 0<pin['bytes']<=cap
|
|
40
|
+
or path.stat().st_size!=pin['bytes'] or digest(path)!=pin['sha256']):
|
|
41
|
+
raise ValueError('executed_file_pin_mismatch')
|
|
42
|
+
# Package code is installed locally, never imported from the model directory.
|
|
43
|
+
for module,name in ((packing,'packing'),(protocol,'protocol')):
|
|
44
|
+
if digest(module.__file__)!=m['files'][f'runtime/{name}.py']['sha256']:
|
|
45
|
+
raise ValueError('installed_helpers_must_match_model')
|
|
46
|
+
return m
|
|
47
|
+
|
|
48
|
+
class OnnxScorer:
|
|
49
|
+
"""One complete window; callers own process lifetime and final allocation."""
|
|
50
|
+
def __init__(self,directory,manifest_sha256=DEFAULT_MANIFEST_SHA256,*,development=False):
|
|
51
|
+
if development is not True:raise ValueError('explicit_development_opt_in_required')
|
|
52
|
+
self.root=Path(directory)
|
|
53
|
+
self.manifest_sha256=manifest_sha256
|
|
54
|
+
self.manifest=inspect_onnx(self.root,manifest_sha256)
|
|
55
|
+
self.revision=self.manifest['source_checkpoint_sha256']
|
|
56
|
+
self._tokenizer=self._session=None
|
|
57
|
+
|
|
58
|
+
def score(self,request):
|
|
59
|
+
request=protocol.validate_request(request,self.revision)
|
|
60
|
+
for name,version in ENVIRONMENT.items():
|
|
61
|
+
if importlib.metadata.version(name)!=version:raise ValueError('pinned_ONNX_environment_required')
|
|
62
|
+
if self._tokenizer is None:
|
|
63
|
+
from tokenizers import Tokenizer
|
|
64
|
+
self._tokenizer=Tokenizer.from_file(str(self.root/'tokenizer/tokenizer.json'))
|
|
65
|
+
self._token_config=protocol.decode((self.root/'tokenizer/tokenizer_config.json').read_bytes())
|
|
66
|
+
windows,receipt=packing.pack_windows(self._tokenizer,self._token_config,request)
|
|
67
|
+
if len(windows)!=1 or receipt['context_tokens_per_window']:
|
|
68
|
+
raise ValueError('single_complete_window_required_no_truncation')
|
|
69
|
+
ids,ranges,indices=windows[0]
|
|
70
|
+
if indices!=list(range(len(request['groups']))):raise ValueError('group_alignment')
|
|
71
|
+
import numpy as np
|
|
72
|
+
if self._session is None:
|
|
73
|
+
inspect_onnx(self.root,self.manifest_sha256)
|
|
74
|
+
import onnxruntime as ort
|
|
75
|
+
options=ort.SessionOptions();options.intra_op_num_threads=4;options.inter_op_num_threads=1
|
|
76
|
+
session=ort.InferenceSession(str(self.root/'model.onnx'),options,providers=['CPUExecutionProvider'])
|
|
77
|
+
session.disable_fallback()
|
|
78
|
+
if session.get_providers()!=['CPUExecutionProvider']:
|
|
79
|
+
raise ValueError('explicit_CPU_provider_required')
|
|
80
|
+
self._session=session
|
|
81
|
+
inputs=np.asarray([ids],dtype=np.int64)
|
|
82
|
+
output=self._session.run(['token_scores'],{'input_ids':inputs,'attention_mask':np.ones_like(inputs)})[0]
|
|
83
|
+
if output.shape!=inputs.shape or not np.isfinite(output).all():raise ValueError('finite_token_scores_required')
|
|
84
|
+
scores=[float(output[0,a:b].mean()) for a,b in ranges]
|
|
85
|
+
return protocol.decode(protocol.encode_response(request,scores))
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Existing Select schema one, with bounded fail-closed JSON handling."""
|
|
2
|
+
import json
|
|
3
|
+
import math
|
|
4
|
+
|
|
5
|
+
REQUEST_BYTES = 1024 * 1024
|
|
6
|
+
RESPONSE_BYTES = 65536
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _unique(pairs):
|
|
10
|
+
value = {}
|
|
11
|
+
for key, item in pairs:
|
|
12
|
+
if key in value:
|
|
13
|
+
raise ValueError('duplicate_JSON_key')
|
|
14
|
+
value[key] = item
|
|
15
|
+
return value
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _number(text):
|
|
19
|
+
value = float(text)
|
|
20
|
+
if not math.isfinite(value):
|
|
21
|
+
raise ValueError('nonfinite_JSON')
|
|
22
|
+
return value
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _constant(_):
|
|
26
|
+
raise ValueError('nonfinite_JSON')
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def decode(data):
|
|
30
|
+
return json.loads(data, object_pairs_hook=_unique, parse_float=_number, parse_constant=_constant)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def load_request(data, revision):
|
|
34
|
+
if not isinstance(data, bytes) or len(data) > REQUEST_BYTES:
|
|
35
|
+
raise ValueError('request_byte_limit')
|
|
36
|
+
request = decode(data)
|
|
37
|
+
return validate_request(request, revision)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def validate_request(request, revision):
|
|
41
|
+
if (not isinstance(request, dict)
|
|
42
|
+
or set(request) != {'schema_version', 'model_revision', 'query', 'groups'}
|
|
43
|
+
or type(request['schema_version']) is not int or request['schema_version'] != 1
|
|
44
|
+
or request['model_revision'] != revision
|
|
45
|
+
or not isinstance(request['query'], str) or not request['query'].strip()
|
|
46
|
+
or len(request['query'].encode('utf8')) > 4096):
|
|
47
|
+
raise ValueError('invalid_select_request')
|
|
48
|
+
groups = request['groups']
|
|
49
|
+
if (not isinstance(groups, list) or not 1 <= len(groups) <= 512
|
|
50
|
+
or any(not isinstance(g, dict) or set(g) != {'id', 'text'}
|
|
51
|
+
or not isinstance(g['id'], str) or not g['id'] or len(g['id'].encode('utf8')) > 256
|
|
52
|
+
or not isinstance(g['text'], str) or not g['text'] for g in groups)
|
|
53
|
+
or len({g['id'] for g in groups}) != len(groups)):
|
|
54
|
+
raise ValueError('invalid_groups')
|
|
55
|
+
if len(json.dumps(request, ensure_ascii=False, allow_nan=False).encode('utf8')) > REQUEST_BYTES:
|
|
56
|
+
raise ValueError('request_byte_limit')
|
|
57
|
+
return request
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def encode_response(request, scores):
|
|
61
|
+
if (not isinstance(scores, list) or len(scores) != len(request['groups'])
|
|
62
|
+
or any(type(value) not in (int, float) or not math.isfinite(value) for value in scores)):
|
|
63
|
+
raise ValueError('invalid_scores')
|
|
64
|
+
response = dict(schema_version=1, model_revision=request['model_revision'],
|
|
65
|
+
scores=[[g['id'], value] for g, value in zip(request['groups'], scores)])
|
|
66
|
+
raw = json.dumps(response, ensure_ascii=False, allow_nan=False).encode('utf8')
|
|
67
|
+
if len(raw) > RESPONSE_BYTES:
|
|
68
|
+
raise ValueError('response_byte_limit')
|
|
69
|
+
return raw
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Dedicated-process, explicitly opted-in development scoring; no text generation."""
|
|
2
|
+
import importlib.metadata
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from . import _windows
|
|
8
|
+
from .artifact import inspect_artifact
|
|
9
|
+
from .protocol import encode_response, validate_request
|
|
10
|
+
|
|
11
|
+
ENVIRONMENT = {
|
|
12
|
+
'torch': '2.11.0+cu128', 'transformers': '5.19.0', 'safetensors': '0.8.0',
|
|
13
|
+
'tokenizers': '0.23.2', 'huggingface_hub': '1.33.0', 'numpy': '2.5.3',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def initialize_windows_system_root():
|
|
18
|
+
if os.name == 'nt':
|
|
19
|
+
import ctypes
|
|
20
|
+
buffer = ctypes.create_unicode_buffer(32768)
|
|
21
|
+
get = ctypes.WinDLL('kernel32', use_last_error=True).GetWindowsDirectoryW
|
|
22
|
+
get.argtypes = [ctypes.c_wchar_p, ctypes.c_uint]
|
|
23
|
+
get.restype = ctypes.c_uint
|
|
24
|
+
size = get(buffer, len(buffer))
|
|
25
|
+
if not 0 < size < len(buffer):
|
|
26
|
+
raise ValueError('windows_directory_unavailable')
|
|
27
|
+
os.environ['SystemRoot'] = buffer.value
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DevelopmentScorer:
|
|
31
|
+
"""Single-window prototype. Use in its own process; GPU parity is still required."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, artifact, revision, *, cache_directory, development=False, precision='fp32'):
|
|
34
|
+
if development is not True or precision not in ('fp32', 'fp16'):
|
|
35
|
+
raise ValueError('explicit_development_precision_required')
|
|
36
|
+
self.root = Path(artifact)
|
|
37
|
+
self.report = inspect_artifact(self.root, revision)
|
|
38
|
+
self.root = self.root.resolve()
|
|
39
|
+
self.revision, self.precision = revision, precision
|
|
40
|
+
self.cache = Path(cache_directory)
|
|
41
|
+
if not self.cache.is_absolute() or self.cache.resolve().is_relative_to(self.root):
|
|
42
|
+
raise ValueError('separate_absolute_cache_directory_required')
|
|
43
|
+
self._tokenizer = self._model = self._torch = None
|
|
44
|
+
self._token_config = None
|
|
45
|
+
self.last_metrics = None
|
|
46
|
+
|
|
47
|
+
def _prepare_environment(self):
|
|
48
|
+
initialization = self.report.get('initialization_recipe')
|
|
49
|
+
declared = initialization.get('environment', []) if isinstance(initialization, dict) else []
|
|
50
|
+
if (not isinstance(declared, list) or len(declared) != len(ENVIRONMENT)
|
|
51
|
+
or set(declared) != {name + '==' + value for name, value in ENVIRONMENT.items()}):
|
|
52
|
+
raise ValueError('supported_provisioned_environment_required')
|
|
53
|
+
for name, expected in ENVIRONMENT.items():
|
|
54
|
+
if importlib.metadata.version(name) != expected:
|
|
55
|
+
raise ValueError('supported_provisioned_environment_required')
|
|
56
|
+
initialize_windows_system_root()
|
|
57
|
+
self.cache.mkdir(parents=True, exist_ok=True)
|
|
58
|
+
os.environ.update(HF_HUB_OFFLINE='1', TRANSFORMERS_OFFLINE='1',
|
|
59
|
+
HF_HOME=str(self.cache / 'hf'), TORCHINDUCTOR_CACHE_DIR=str(self.cache / 'torch'),
|
|
60
|
+
TOKENIZERS_PARALLELISM='false', CUBLAS_WORKSPACE_CONFIG=':4096:8')
|
|
61
|
+
|
|
62
|
+
def _load_tokenizer(self):
|
|
63
|
+
self._prepare_environment()
|
|
64
|
+
from tokenizers import Tokenizer
|
|
65
|
+
self._tokenizer = Tokenizer.from_file(str(self.root / 'tokenizer/tokenizer.json'))
|
|
66
|
+
self._token_config = json.loads((self.root / 'tokenizer/tokenizer_config.json').read_bytes())
|
|
67
|
+
|
|
68
|
+
def _load_model(self):
|
|
69
|
+
self.report = inspect_artifact(self.root, self.revision)
|
|
70
|
+
import torch
|
|
71
|
+
if (not torch.cuda.is_available() or torch.cuda.get_device_capability() != (12, 0)
|
|
72
|
+
or torch.cuda.get_device_name() != 'NVIDIA GeForce RTX 5080'):
|
|
73
|
+
raise ValueError('tested_local_CUDA_target_required_no_fallback')
|
|
74
|
+
from transformers.initialization import no_init_weights
|
|
75
|
+
from transformers.models.modernbert.configuration_modernbert import ModernBertConfig
|
|
76
|
+
from transformers.models.modernbert.modeling_modernbert import ModernBertModel
|
|
77
|
+
from ._model import JointGroupModel
|
|
78
|
+
torch.set_num_threads(4)
|
|
79
|
+
torch.set_num_interop_threads(1)
|
|
80
|
+
torch.use_deterministic_algorithms(True)
|
|
81
|
+
torch.backends.cuda.matmul.allow_tf32 = False
|
|
82
|
+
torch.backends.cudnn.allow_tf32 = False
|
|
83
|
+
torch.backends.cudnn.benchmark = False
|
|
84
|
+
config = ModernBertConfig.from_pretrained(self.root, local_files_only=True)
|
|
85
|
+
config._attn_implementation = 'sdpa'
|
|
86
|
+
with no_init_weights():
|
|
87
|
+
model = JointGroupModel(ModernBertModel(config))
|
|
88
|
+
state = torch.load(self.root / 'checkpoint.pt', map_location='cpu', weights_only=True, mmap=True)
|
|
89
|
+
if any(not isinstance(value, torch.Tensor) or value.dtype != torch.float32
|
|
90
|
+
or not torch.isfinite(value).all() for value in state.values()):
|
|
91
|
+
raise ValueError('finite_FP32_source_tensors_required')
|
|
92
|
+
model.load_state_dict(state, strict=True, assign=True)
|
|
93
|
+
dtype = torch.float32 if self.precision == 'fp32' else torch.float16
|
|
94
|
+
self._model = model.to(device='cuda', dtype=dtype).eval()
|
|
95
|
+
self._torch = torch
|
|
96
|
+
|
|
97
|
+
def score(self, request):
|
|
98
|
+
"""Admit the entire request before model creation, then one encoder forward."""
|
|
99
|
+
self.last_metrics = None
|
|
100
|
+
request = validate_request(request, self.revision)
|
|
101
|
+
if self._tokenizer is None:
|
|
102
|
+
self._load_tokenizer()
|
|
103
|
+
windows, packing = _windows.pack_windows(self._tokenizer, self._token_config, request)
|
|
104
|
+
if len(windows) != 1 or packing['context_tokens_per_window']:
|
|
105
|
+
raise ValueError('single_complete_window_required_no_truncation')
|
|
106
|
+
if self._model is None:
|
|
107
|
+
self._load_model()
|
|
108
|
+
torch = self._torch
|
|
109
|
+
dtype = torch.float32 if self.precision == 'fp32' else torch.float16
|
|
110
|
+
parameters = list(self._model.parameters())
|
|
111
|
+
if not parameters or any(p.device.type != 'cuda' or p.dtype != dtype for p in parameters):
|
|
112
|
+
raise ValueError('declared_CUDA_precision_required_no_fallback')
|
|
113
|
+
ids, ranges, indices = windows[0]
|
|
114
|
+
if indices != list(range(len(request['groups']))):
|
|
115
|
+
raise ValueError('score_alignment')
|
|
116
|
+
inputs = torch.tensor([ids], device='cuda')
|
|
117
|
+
with torch.inference_mode():
|
|
118
|
+
scores = self._model(inputs, torch.ones_like(inputs), ranges).cpu().tolist()
|
|
119
|
+
encoded = encode_response(request, scores)
|
|
120
|
+
self.last_metrics = dict(quality='unqualified', qualification=False, encoder_forwards=1,
|
|
121
|
+
device='cuda', precision=self.precision, packing=packing)
|
|
122
|
+
return json.loads(encoded)
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tokenfold-select
|
|
3
|
+
Version: 0.5.2
|
|
4
|
+
Summary: Opt-in ONNX Select v2 scorer for Tokenfold
|
|
5
|
+
License: Apache License
|
|
6
|
+
Version 2.0, January 2004
|
|
7
|
+
http://www.apache.org/licenses/
|
|
8
|
+
|
|
9
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
10
|
+
|
|
11
|
+
1. Definitions.
|
|
12
|
+
|
|
13
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
14
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
15
|
+
|
|
16
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
17
|
+
the copyright owner that is granting the License.
|
|
18
|
+
|
|
19
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
20
|
+
other entities that control, are controlled by, or are under common
|
|
21
|
+
control with that entity. For the purposes of this definition,
|
|
22
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
23
|
+
direction or management of such entity, whether by contract or
|
|
24
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
25
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
26
|
+
|
|
27
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
28
|
+
exercising permissions granted by this License.
|
|
29
|
+
|
|
30
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
31
|
+
including but not limited to software source code, documentation
|
|
32
|
+
source, and configuration files.
|
|
33
|
+
|
|
34
|
+
"Object" form shall mean any form resulting from mechanical
|
|
35
|
+
transformation or translation of a Source form, including but
|
|
36
|
+
not limited to compiled object code, generated documentation,
|
|
37
|
+
and conversions to other media types.
|
|
38
|
+
|
|
39
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
40
|
+
Object form, made available under the License, as indicated by a
|
|
41
|
+
copyright notice that is included in or attached to the work
|
|
42
|
+
(an example is provided in the Appendix below).
|
|
43
|
+
|
|
44
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
45
|
+
form, that is based on (or derived from) the Work and for which the
|
|
46
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
47
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
48
|
+
of this License, Derivative Works shall not include works that remain
|
|
49
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
50
|
+
the Work and Derivative Works thereof.
|
|
51
|
+
|
|
52
|
+
"Contribution" shall mean any work of authorship, including
|
|
53
|
+
the original version of the Work and any modifications or additions
|
|
54
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
55
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
56
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
57
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
58
|
+
means any form of electronic, verbal, or written communication sent
|
|
59
|
+
to the Licensor or its representatives, including but not limited to
|
|
60
|
+
communication on electronic mailing lists, source code control systems,
|
|
61
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
62
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
63
|
+
excluding communication that is conspicuously marked or otherwise
|
|
64
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
65
|
+
|
|
66
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
67
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
68
|
+
subsequently incorporated within the Work.
|
|
69
|
+
|
|
70
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
71
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
72
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
73
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
74
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
75
|
+
Work and such Derivative Works in Source or Object form.
|
|
76
|
+
|
|
77
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
78
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
79
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
80
|
+
(except as stated in this section) patent license to make, have made,
|
|
81
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
82
|
+
where such license applies only to those patent claims licensable
|
|
83
|
+
by such Contributor that are necessarily infringed by their
|
|
84
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
85
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
86
|
+
institute patent litigation against any entity (including a
|
|
87
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
88
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
89
|
+
or contributory patent infringement, then any patent licenses
|
|
90
|
+
granted to You under this License for that Work shall terminate
|
|
91
|
+
as of the date such litigation is filed.
|
|
92
|
+
|
|
93
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
94
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
95
|
+
modifications, and in Source or Object form, provided that You
|
|
96
|
+
meet the following conditions:
|
|
97
|
+
|
|
98
|
+
(a) You must give any other recipients of the Work or
|
|
99
|
+
Derivative Works a copy of this License; and
|
|
100
|
+
|
|
101
|
+
(b) You must cause any modified files to carry prominent notices
|
|
102
|
+
stating that You changed the files; and
|
|
103
|
+
|
|
104
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
105
|
+
that You distribute, all copyright, patent, trademark, and
|
|
106
|
+
attribution notices from the Source form of the Work,
|
|
107
|
+
excluding those notices that do not pertain to any part of
|
|
108
|
+
the Derivative Works; and
|
|
109
|
+
|
|
110
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
111
|
+
distribution, then any Derivative Works that You distribute must
|
|
112
|
+
include a readable copy of the attribution notices contained
|
|
113
|
+
within such NOTICE file, excluding those notices that do not
|
|
114
|
+
pertain to any part of the Derivative Works, in at least one
|
|
115
|
+
of the following places: within a NOTICE text file distributed
|
|
116
|
+
as part of the Derivative Works; within the Source form or
|
|
117
|
+
documentation, if provided along with the Derivative Works; or,
|
|
118
|
+
within a display generated by the Derivative Works, if and
|
|
119
|
+
wherever such third-party notices normally appear. The contents
|
|
120
|
+
of the NOTICE file are for informational purposes only and
|
|
121
|
+
do not modify the License. You may add Your own attribution
|
|
122
|
+
notices within Derivative Works that You distribute, alongside
|
|
123
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
124
|
+
that such additional attribution notices cannot be construed
|
|
125
|
+
as modifying the License.
|
|
126
|
+
|
|
127
|
+
You may add Your own copyright statement to Your modifications and
|
|
128
|
+
may provide additional or different license terms and conditions
|
|
129
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
130
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
131
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
132
|
+
the conditions stated in this License.
|
|
133
|
+
|
|
134
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
135
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
136
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
137
|
+
this License, without any additional terms or conditions.
|
|
138
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
139
|
+
the terms of any separate license agreement you may have executed
|
|
140
|
+
with Licensor regarding such Contributions.
|
|
141
|
+
|
|
142
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
143
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
144
|
+
except as required for reasonable and customary use in describing the
|
|
145
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
146
|
+
|
|
147
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
148
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
149
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
150
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
151
|
+
implied, including, without limitation, any warranties or conditions
|
|
152
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
153
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
154
|
+
appropriateness of using or redistributing the Work and assume any
|
|
155
|
+
risks associated with Your exercise of permissions under this License.
|
|
156
|
+
|
|
157
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
158
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
159
|
+
unless required by applicable law (such as deliberate and grossly
|
|
160
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
161
|
+
liable to You for damages, including any direct, indirect, special,
|
|
162
|
+
incidental, or consequential damages of any character arising as a
|
|
163
|
+
result of this License or out of the use or inability to use the
|
|
164
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
165
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
166
|
+
other commercial damages or losses), even if such Contributor
|
|
167
|
+
has been advised of the possibility of such damages.
|
|
168
|
+
|
|
169
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
170
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
171
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
172
|
+
or other liability obligations and/or rights consistent with this
|
|
173
|
+
License. However, in accepting such obligations, You may act only
|
|
174
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
175
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
176
|
+
defend, and hold each Contributor harmless for any liability
|
|
177
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
178
|
+
of your accepting any such warranty or additional liability.
|
|
179
|
+
|
|
180
|
+
END OF TERMS AND CONDITIONS
|
|
181
|
+
|
|
182
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
183
|
+
|
|
184
|
+
To apply the Apache License to your work, attach the following
|
|
185
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
186
|
+
replaced with your own identifying information. (Don't include
|
|
187
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
188
|
+
comment syntax for the file format. We also recommend that a
|
|
189
|
+
file or class name and description of purpose be included on the
|
|
190
|
+
same "printed page" as the copyright notice for easier
|
|
191
|
+
identification within third-party archives.
|
|
192
|
+
|
|
193
|
+
Copyright [yyyy] [name of copyright owner]
|
|
194
|
+
|
|
195
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
196
|
+
you may not use this file except in compliance with the License.
|
|
197
|
+
You may obtain a copy of the License at
|
|
198
|
+
|
|
199
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
200
|
+
|
|
201
|
+
Unless required by applicable law or agreed to in writing, software
|
|
202
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
203
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
204
|
+
See the License for the specific language governing permissions and
|
|
205
|
+
limitations under the License.
|
|
206
|
+
|
|
207
|
+
Requires-Python: >=3.12
|
|
208
|
+
Description-Content-Type: text/markdown
|
|
209
|
+
License-File: LICENSE
|
|
210
|
+
Provides-Extra: onnx
|
|
211
|
+
Requires-Dist: onnxruntime==1.30.0; extra == "onnx"
|
|
212
|
+
Requires-Dist: numpy==2.5.3; extra == "onnx"
|
|
213
|
+
Requires-Dist: tokenizers==0.23.2; extra == "onnx"
|
|
214
|
+
Dynamic: license-file
|
|
215
|
+
|
|
216
|
+
# Tokenfold Select v2
|
|
217
|
+
|
|
218
|
+
**Smaller contexts. Original words. Local ONNX scoring.**
|
|
219
|
+
|
|
220
|
+
The optional `tokenfold-select` package defaults to the **Select v2 FP32 ONNX CPU
|
|
221
|
+
scorer**. It ranks whole caller-declared groups jointly in one bounded encoder
|
|
222
|
+
window; Tokenfold's existing allocator keeps required content, original order
|
|
223
|
+
and the final token-budget check. It never generates replacement text.
|
|
224
|
+
|
|
225
|
+
This is an **experimental preview**, not a quality- or performance-qualified
|
|
226
|
+
production model. Core compression stays model-free and deterministic. Installing
|
|
227
|
+
this package does not enable selection, download weights, send data to a service,
|
|
228
|
+
or change the `tokenfold` Python/TypeScript compression APIs.
|
|
229
|
+
|
|
230
|
+
## Install and inspect
|
|
231
|
+
|
|
232
|
+
```sh
|
|
233
|
+
python -m pip install 'tokenfold-select[onnx]==0.5.2'
|
|
234
|
+
python -m tokenfold_select inspect-onnx --artifact /absolute/select-v2-model
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Provision the separate ONNX model directory before use. It must contain
|
|
238
|
+
`model.onnx`, `model.onnx.data`, the pinned tokenizer, runtime reference files and
|
|
239
|
+
`onnx-manifest.json`. The default manifest hash is
|
|
240
|
+
`267313718e80aa91a56923dea30363fd5c24fde3782c716d6d048891860a2054`;
|
|
241
|
+
model revision is the source checkpoint hash
|
|
242
|
+
`4dbdb5071e7d6316d8eb70dc161b1f2f77f555012f6616d7fa34311188e24cec`.
|
|
243
|
+
The package verifies every computational file before inference. Do not substitute
|
|
244
|
+
an untrusted manifest pin. Artifact inspection requires no inference dependencies.
|
|
245
|
+
|
|
246
|
+
## Use with the CLI
|
|
247
|
+
|
|
248
|
+
Create an existing, private scratch directory. Explicitly approve your native
|
|
249
|
+
Python executable, installed package/dependency directory and model files:
|
|
250
|
+
|
|
251
|
+
```sh
|
|
252
|
+
python -m tokenfold_select configure \
|
|
253
|
+
--artifact /absolute/select-v2-model \
|
|
254
|
+
--site-packages /absolute/venv/lib/python3.12/site-packages \
|
|
255
|
+
--scratch-root /absolute/private-scratch \
|
|
256
|
+
--approved-by your-name > select-scorer.json
|
|
257
|
+
|
|
258
|
+
tokenfold --experimental select context.json --query 'find the relevant item' \
|
|
259
|
+
--target-tokens 256 --scorer-config select-scorer.json \
|
|
260
|
+
--inference-timeout-ms 30000
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Use actual absolute paths for your platform (Windows uses `Lib/site-packages`).
|
|
264
|
+
`configure` checks model-file integrity and emits the existing approved-scorer
|
|
265
|
+
JSON; it performs no inference. Inspect the file before use. The command pins the
|
|
266
|
+
**native base interpreter**, not a Windows venv shim; the isolated bootstrap loads
|
|
267
|
+
this installed package and the explicitly provisioned site-packages, without
|
|
268
|
+
`.pth` processing. Protect auxiliary code, libraries, model files and scratch
|
|
269
|
+
storage. This is not an OS sandbox.
|
|
270
|
+
|
|
271
|
+
A minimal context document is:
|
|
272
|
+
|
|
273
|
+
```json
|
|
274
|
+
{"prefix":"","suffix":"","groups":[{"id":"required","text":"Keep this.\n","required":true},{"id":"optional","text":"Other context.\n","fallback_score":0.1}]}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The worker defaults to `--backend onnx`, explicitly selects
|
|
278
|
+
`CPUExecutionProvider`, disables provider fallback and hides GPUs before loading
|
|
279
|
+
inference libraries. It uses bounded file IPC, no network or descendants, and an
|
|
280
|
+
owned direct-child deadline. A receipt with `used_scorer: false` means fallback,
|
|
281
|
+
not successful model-backed selection. Missing approval retains the existing
|
|
282
|
+
caller-declared deterministic fallback; there is no implicit model execution.
|
|
283
|
+
|
|
284
|
+
## Resident API
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
from tokenfold_select import OnnxScorer
|
|
288
|
+
|
|
289
|
+
scorer = OnnxScorer('/absolute/select-v2-model', development=True)
|
|
290
|
+
request = {
|
|
291
|
+
'schema_version': 1,
|
|
292
|
+
'model_revision': scorer.revision,
|
|
293
|
+
'query': 'find the relevant item',
|
|
294
|
+
'groups': [{'id': 'item', 'text': 'Original context.'}],
|
|
295
|
+
}
|
|
296
|
+
response = scorer.score(request) # finite, id-aligned scores only
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Use a dedicated process with caller-owned deadlines for the resident API. The
|
|
300
|
+
CLI owns and waits for its one-shot worker; resident callers own their process.
|
|
301
|
+
Neither API owns grouping, protected-content policy or final allocation.
|
|
302
|
+
|
|
303
|
+
## Bounds and evidence
|
|
304
|
+
|
|
305
|
+
One complete window: at most 8,192 tokens, 512 groups, a 1 MiB request and a
|
|
306
|
+
64 KiB response. Overflow is refused, not truncated or silently windowed. ONNX
|
|
307
|
+
Runtime 1.30.0, NumPy 2.5.3 and tokenizers 0.23.2 are pinned; no PyTorch inference
|
|
308
|
+
dependency is required for this backend. No runtime binaries or weights are bundled.
|
|
309
|
+
|
|
310
|
+
The exported graph passed six synthetic CPU parity cases against the source
|
|
311
|
+
model, including dynamic lengths through 2,048 tokens and 512 pooled ranges.
|
|
312
|
+
This is mechanics evidence, **not** full-8k execution, representative downstream
|
|
313
|
+
quality, clean-user capacity or cold/warm compression latency qualification.
|
|
314
|
+
Historical PyTorch FP16 timings do not measure this ONNX CPU backend. Keep
|
|
315
|
+
`--experimental` and the explicit development acknowledgement until qualified.
|
|
316
|
+
|
|
317
|
+
## Legacy development backend
|
|
318
|
+
|
|
319
|
+
`DevelopmentScorer` and `inspect` retain the private PyTorch artifact contract.
|
|
320
|
+
Use `worker --backend pytorch` explicitly for that FP32 direct-child prototype;
|
|
321
|
+
it still requires its original pinned environment and artifact. The resident API
|
|
322
|
+
also retains its explicit development-only FP16 option. Select v1's separately
|
|
323
|
+
published LoRA model and historical benchmark results are not v2 evidence.
|
|
324
|
+
|
|
325
|
+
## Safetensors model distribution
|
|
326
|
+
|
|
327
|
+
The separate model release bundle also includes `model.safetensors` plus its
|
|
328
|
+
custom joint-group architecture and pinned CPU reference loader. Safetensors is
|
|
329
|
+
an alternative weight format, not a stock Transformers classification checkpoint
|
|
330
|
+
or an automatic backend in this package. ONNX remains this worker's default.
|
|
331
|
+
Use the model card's `SafetensorsScorer` instructions and manifest; representative
|
|
332
|
+
quality/performance qualification is separate from format/parity validation.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
tokenfold_select/__init__.py,sha256=cIdHOhgD9FU5SHpU-Y6Q_r9ewl7YXL-MK_8uyW2s6u8,307
|
|
2
|
+
tokenfold_select/__main__.py,sha256=6Fz-gEqNK0F6JyOG-htP5eH500ZY3ifVDf_xMq89-U0,6822
|
|
3
|
+
tokenfold_select/_model.py,sha256=qQzx7TJ05NzZqWjitRF2q_iDK_o2B24HvhRn0cSaTWQ,2741
|
|
4
|
+
tokenfold_select/_windows.py,sha256=-oefuJbjBaYP8Q8_WmojiH2hG5dBohDi3AWBVOFI2Sk,5353
|
|
5
|
+
tokenfold_select/artifact.py,sha256=jMRnhT4bVkQyt_kEJCYZXWtQ6rJf47saYVuDiTMouqA,4096
|
|
6
|
+
tokenfold_select/bootstrap.py,sha256=uk7Sg6pTUXGpITfi-s_hWaLHEatn-N7QyQHUT5R8LX0,848
|
|
7
|
+
tokenfold_select/onnx.py,sha256=a5sLuAcZ0hNlDjZpCGDwcyvmjHn6OuFkEJOnJPDM6WQ,4985
|
|
8
|
+
tokenfold_select/protocol.py,sha256=EVFf5elo1eOeoi6bMKcXkDqj-qH5c9T2XYmkvSkt6Qg,2635
|
|
9
|
+
tokenfold_select/runtime.py,sha256=5zERlr59oGe6fo6VPrdKgw1IdFGzvJy8Z-jTFlQnMeY,6404
|
|
10
|
+
tokenfold_select-0.5.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
11
|
+
tokenfold_select-0.5.2.dist-info/METADATA,sha256=1KGRtynpOCLAH41Ul4N_EWbcrxCHLQsgvNHq-_sUDKM,18921
|
|
12
|
+
tokenfold_select-0.5.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
13
|
+
tokenfold_select-0.5.2.dist-info/top_level.txt,sha256=DTILb8zd3o0tMbQk4GXLjqKOCNxqd6J5sTtpOqOcYOo,17
|
|
14
|
+
tokenfold_select-0.5.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tokenfold_select
|