@tuned-tensor/cli 0.21.0 → 0.22.1

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.
@@ -0,0 +1,116 @@
1
+ """Prefetch the dataset files a behavior spec trains on from the Hugging Face Hub.
2
+
3
+ Mirrors prefetch.py for models: the files land in the shared Hugging Face cache
4
+ (``<HF_HOME>/hub/datasets--org--name``), the revision is pinned to an immutable
5
+ commit, and every requested file is checked against its content-addressed blob.
6
+ Only the split files named in the spec are downloaded, never the whole repo.
7
+ """
8
+ import argparse
9
+ import json
10
+ import re
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from hub_download import configure_download_environment, download_snapshot, verify_blob_digest
15
+ from prefetch import configure_hugging_face_cache, write_json
16
+
17
+ HUGGING_FACE_REPO_ID = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,95}/[A-Za-z0-9][A-Za-z0-9._-]{0,95}")
18
+ COMMIT_SHA = re.compile(r"[0-9a-fA-F]{40}")
19
+
20
+
21
+ def validate_repo_path(value: Any) -> str:
22
+ if not isinstance(value, str) or not value or value.startswith(("/", "\\")):
23
+ raise ValueError(f"Dataset file must be a repository-relative path: {value!r}")
24
+ parts = re.split(r"[/\\]", value)
25
+ if any(part in ("", ".", "..") for part in parts) or re.match(r"^[A-Za-z]:", value):
26
+ raise ValueError(f"Dataset file must be a repository-relative path: {value!r}")
27
+ return "/".join(parts)
28
+
29
+
30
+ def verify_dataset_files(snapshot: Path, files: dict[str, str]) -> dict[str, dict[str, Any]]:
31
+ """Check each split file is a complete cache entry inside the dataset repo.
32
+
33
+ Normal caches link snapshot files to content-addressed blobs, which are
34
+ checked against their digest. Caches without symlink support (Windows
35
+ without Developer Mode) hold plain copies; those are contained and hashed.
36
+ """
37
+ repository = snapshot.parent.parent.resolve()
38
+ verified: dict[str, dict[str, Any]] = {}
39
+ for split, relative in files.items():
40
+ path = snapshot / relative
41
+ if not path.is_file():
42
+ raise ValueError(f"Dataset file {relative} ({split}) is missing from snapshot {snapshot}")
43
+ target = path.resolve()
44
+ if repository not in target.parents:
45
+ raise ValueError(f"Dataset file {relative} escapes the Hugging Face dataset cache: {target}")
46
+ size = target.stat().st_size
47
+ if size == 0:
48
+ raise ValueError(f"Dataset file {relative} ({split}) is empty")
49
+ _, sha256 = verify_blob_digest(path)
50
+ verified[split] = {
51
+ "path": relative,
52
+ "local_path": str(path),
53
+ "size_bytes": size,
54
+ "sha256": sha256,
55
+ }
56
+ return verified
57
+
58
+
59
+ def main() -> None:
60
+ parser = argparse.ArgumentParser(description="Prefetch Hugging Face dataset files for a TT behavior spec.")
61
+ parser.add_argument("--input", required=True)
62
+ parser.add_argument("--output", required=True)
63
+ args = parser.parse_args()
64
+
65
+ payload = json.loads(Path(args.input).read_text(encoding="utf-8"))
66
+ repo = str(payload["repo"])
67
+ if not HUGGING_FACE_REPO_ID.fullmatch(repo):
68
+ raise ValueError(f"Dataset repo must be a Hugging Face id like org/name: {repo!r}")
69
+ revision = payload.get("revision")
70
+ if revision is not None and not COMMIT_SHA.fullmatch(str(revision)):
71
+ raise ValueError("Dataset revision must be a 40-character Hugging Face commit SHA")
72
+ raw_files = payload.get("files")
73
+ if not isinstance(raw_files, dict) or not raw_files:
74
+ raise ValueError("Dataset prefetch requires at least one split file")
75
+ files = {str(split): validate_repo_path(path) for split, path in raw_files.items()}
76
+
77
+ configure_hugging_face_cache(payload.get("model_cache"))
78
+ configure_download_environment()
79
+ from huggingface_hub import constants
80
+
81
+ local_only = bool(payload.get("local_files_only", False))
82
+ action = "Verifying cached" if local_only else "Prefetching"
83
+ print(f"{action} dataset {repo} in {constants.HF_HUB_CACHE}...", flush=True)
84
+ snapshot_path, commit = download_snapshot(
85
+ repo_id=repo,
86
+ repo_type="dataset",
87
+ revision=revision,
88
+ allow_patterns=sorted(set(files.values())),
89
+ local_files_only=local_only,
90
+ label="dataset_prefetch",
91
+ )
92
+ snapshot = Path(snapshot_path)
93
+ if not COMMIT_SHA.fullmatch(snapshot.name):
94
+ raise ValueError("Hugging Face dataset snapshot did not resolve to a 40-character immutable commit SHA")
95
+ if revision and snapshot.name.lower() != str(revision).lower():
96
+ raise ValueError(f"Hugging Face returned dataset revision {snapshot.name}, not requested revision {revision}")
97
+ print(f"Verifying dataset files under {snapshot}...", flush=True)
98
+ verified = verify_dataset_files(snapshot, files)
99
+
100
+ write_json(args.output, {
101
+ "ok": True,
102
+ "repo": repo,
103
+ "repo_type": "dataset",
104
+ "requested_revision": revision,
105
+ "snapshot_revision": snapshot.name.lower(),
106
+ "snapshot_path": str(snapshot),
107
+ "hf_home": str(constants.HF_HOME),
108
+ "hub_cache": str(constants.HF_HUB_CACHE),
109
+ "files": verified,
110
+ "size_bytes": sum(item["size_bytes"] for item in verified.values()),
111
+ "commit": commit,
112
+ })
113
+
114
+
115
+ if __name__ == "__main__":
116
+ main()
@@ -109,6 +109,8 @@ def build_vllm_args(model_source: str, adapter_path: str | None, temp_dir: Path)
109
109
  prompt = os.environ.get("TT_SYSTEM_PROMPT", "").strip()
110
110
  if prompt:
111
111
  template = native_chat_template(model_source)
112
+ from vllm.renderers.hf import _detect_content_format
113
+ content_format = _detect_content_format(template, default="string")
112
114
  # Literal data, never prompt text interpolated as Jinja source. Preserve
113
115
  # tool calls/results verbatim while merging leading system context.
114
116
  prefix = "{%- set tt = namespace(system=" + json.dumps(prompt) + ", history=[]) -%}"
@@ -121,7 +123,9 @@ def build_vllm_args(model_source: str, adapter_path: str | None, temp_dir: Path)
121
123
  prefix += "{%- set tt.system = tt.system + '\\n\\n' + part.text -%}"
122
124
  prefix += "{%- endif -%}{%- endfor -%}{%- endif -%}"
123
125
  prefix += "{%- else -%}{%- set tt.history = tt.history + [message] -%}{%- endif -%}{%- endfor -%}"
124
- prefix += "{%- set messages = [{'role': 'system', 'content': tt.system}] + tt.history -%}"
126
+ # Match the ORIGINAL native template's expected content representation.
127
+ system_content = "[{'type': 'text', 'text': tt.system}]" if content_format == "openai" else "tt.system"
128
+ prefix += "{%- set messages = [{'role': 'system', 'content': " + system_content + "}] + tt.history -%}"
125
129
  path = temp_dir / "chat-template.jinja"
126
130
  path.write_text(prefix + template, encoding="utf-8")
127
131
  args += ["--chat-template", str(path)]