coderofpears commited on
Commit
9d7b292
·
verified ·
1 Parent(s): 5b3ebe8

Upload folder using huggingface_hub

Browse files
Files changed (4) hide show
  1. data/tokenizer.json +0 -0
  2. modal_train.py +26 -34
  3. prep.py +127 -52
  4. push_hf.py +27 -12
data/tokenizer.json CHANGED
The diff for this file is too large to render. See raw diff
 
modal_train.py CHANGED
@@ -1,21 +1,21 @@
1
  """
2
  clankerDiffusion — Modal L4 training.
3
 
4
- Bakes the project code into the image, keeps data + checkpoints on a persistent
5
- Modal Volume (/vol), injects HF_TOKEN from .env, trains a big hybrid model on an
6
- L4 (24 GB), and pushes every checkpoint to the HF repo:
 
7
 
8
  coderofpears/clankerDiffusion-checkpoints
9
 
10
- Launch (after `modal token new` on this machine):
11
- modal run modal_train.py::train_on_l4 --hours 20 --size large
12
 
13
- Data: stage train.bin + tokenizer.json onto the volume once, e.g.
14
- modal volume put clanker-vol C:\Users\User\CalcGPU\clankerDiffusion\data /data
15
- (or it is downloaded from the HF data repo if present there).
16
  """
17
  import os
18
- from modal import App, Image, Volume, Secret, function
19
 
20
  HERE = os.path.dirname(os.path.abspath(__file__))
21
  CODE_REPO = "coderofpears/clankerDiffusion-base"
@@ -25,17 +25,10 @@ DOTENV = os.path.join(HERE, ".env")
25
 
26
  image = (
27
  Image.debian_slim()
28
- .pip_install(
29
- "torch==2.11.0", index_url="https://download.pytorch.org/whl/cu128",
30
- "numpy", "tokenizers", "datasets", "safetensors",
31
- "huggingface_hub", "accelerate", "hf-transfer",
32
- )
33
  .env({"HF_HUB_ENABLE_HF_TRANSFER": "1", "TOKENIZERS_PARALLELISM": "false"})
34
- .add_local_dir(
35
- HERE, "/root/clanker",
36
- ignore=["data", "checkpoints", ".venv", "__pycache__", "*.bin",
37
- "*.pt", ".git", "*.log", "temp_up"],
38
- )
39
  )
40
 
41
  app = App("clanker-diffusion", image=image)
@@ -54,30 +47,27 @@ SIZES = {
54
  timeout=60 * 60 * 25,
55
  volumes={"/vol": volume},
56
  secrets=[Secret.from_dotenv(DOTENV)],
57
- _allow_background_volume_commits=True,
58
  )
59
  def train_on_l4(hours: float = 20.0, ckpt_every: int = 250,
60
- size: str = "large", batch: int = None):
61
- import os
62
  import subprocess
63
  import sys
64
 
 
 
 
 
65
  os.chdir("/root/clanker")
 
66
  data_dir, ckpt_dir = "/vol/data", "/vol/checkpoints"
67
  os.makedirs(data_dir, exist_ok=True)
68
  os.makedirs(ckpt_dir, exist_ok=True)
69
 
70
- # ---- ensure data ----
71
  if not os.path.exists(os.path.join(data_dir, "train.bin")):
72
- print("[modal] train.bin missing; trying HF download ...")
73
- try:
74
- subprocess.run(["hf", "download", DATA_REPO, "train.bin", "-d", data_dir],
75
- check=True)
76
- subprocess.run(["hf", "download", DATA_REPO, "tokenizer.json", "-d", data_dir],
77
- check=True)
78
- except Exception as e:
79
- print(f"[modal] HF data download failed: {e}; bailing.")
80
- raise
81
 
82
  spec = dict(SIZES.get(size, SIZES["large"]))
83
  if batch:
@@ -102,5 +92,7 @@ def train_on_l4(hours: float = 20.0, ckpt_every: int = 250,
102
 
103
 
104
  @app.local_entrypoint()
105
- def main(hours: float = 20.0, ckpt_every: int = 250, size: str = "large", batch: int = None):
106
- train_on_l4.remote(hours=hours, ckpt_every=ckpt_every, size=size, batch=batch)
 
 
 
1
  """
2
  clankerDiffusion — Modal L4 training.
3
 
4
+ Builds a CUDA image, pulls the latest code from the HF code repo at runtime,
5
+ keeps data + checkpoints on a persistent Modal Volume (/vol), injects HF_TOKEN
6
+ from .env, trains a big hybrid model on an L4 (24 GB), and pushes every
7
+ checkpoint to the HF repo:
8
 
9
  coderofpears/clankerDiffusion-checkpoints
10
 
11
+ Data is generated in-container by prep.py (fast HF egress), scaled by --scale,
12
+ so no large file transfer is needed.
13
 
14
+ Launch (after `modal token new` on this machine):
15
+ modal run modal_train.py::train_on_l4 --hours 20 --size large --scale 3
 
16
  """
17
  import os
18
+ from modal import App, Image, Volume, Secret
19
 
20
  HERE = os.path.dirname(os.path.abspath(__file__))
21
  CODE_REPO = "coderofpears/clankerDiffusion-base"
 
25
 
26
  image = (
27
  Image.debian_slim()
28
+ .pip_install("torch==2.11.0", index_url="https://download.pytorch.org/whl/cu128")
29
+ .pip_install("numpy", "tokenizers", "datasets", "safetensors",
30
+ "huggingface_hub", "hf-transfer")
 
 
31
  .env({"HF_HUB_ENABLE_HF_TRANSFER": "1", "TOKENIZERS_PARALLELISM": "false"})
 
 
 
 
 
32
  )
33
 
34
  app = App("clanker-diffusion", image=image)
 
47
  timeout=60 * 60 * 25,
48
  volumes={"/vol": volume},
49
  secrets=[Secret.from_dotenv(DOTENV)],
 
50
  )
51
  def train_on_l4(hours: float = 20.0, ckpt_every: int = 250,
52
+ size: str = "large", batch: int = None, scale: float = 3.0):
 
53
  import subprocess
54
  import sys
55
 
56
+ # ---- pull latest code from HF ----
57
+ os.makedirs("/root/clanker", exist_ok=True)
58
+ subprocess.run(["hf", "download", CODE_REPO, "/root/clanker",
59
+ "--repo-type", "model"], check=True)
60
  os.chdir("/root/clanker")
61
+
62
  data_dir, ckpt_dir = "/vol/data", "/vol/checkpoints"
63
  os.makedirs(data_dir, exist_ok=True)
64
  os.makedirs(ckpt_dir, exist_ok=True)
65
 
66
+ # ---- ensure data (generate in-container; fast HF egress) ----
67
  if not os.path.exists(os.path.join(data_dir, "train.bin")):
68
+ print(f"[modal] train.bin missing; generating data locally (scale={scale}) ...")
69
+ subprocess.run([sys.executable, "prep.py", "--out-dir", data_dir,
70
+ "--scale", str(scale)], check=True)
 
 
 
 
 
 
71
 
72
  spec = dict(SIZES.get(size, SIZES["large"]))
73
  if batch:
 
92
 
93
 
94
  @app.local_entrypoint()
95
+ def main(hours: float = 20.0, ckpt_every: int = 250, size: str = "large",
96
+ batch: int = None, scale: float = 3.0):
97
+ train_on_l4.remote(hours=hours, ckpt_every=ckpt_every, size=size,
98
+ batch=batch, scale=scale)
prep.py CHANGED
@@ -1,24 +1,27 @@
1
  """
2
- Data preparation for yk_diffusion.
3
-
4
- One streaming pass over FineWeb-edu:
5
- 1. first N docs -> train the byte-level BPE tokenizer (from scratch)
6
- 2. remainder -> tokenize and pack into a flat uint16 .bin until token budget
7
- Then append:
8
- - synthetic tool-use conversations (guarantees the agent's exact tag format)
9
- - Glaive function-calling data (best-effort) if it loads
10
-
11
- Outputs (under ./data):
12
- tokenizer.json + .meta.json
13
- train.bin (flat uint16 tokens)
14
- meta.json ({n_tokens, seq_len, vocab_size})
 
 
15
  """
16
- import os, sys, json, random, argparse
17
  import numpy as np
18
  from datasets import load_dataset
19
 
20
  import tokenizer as tokmod
21
  from tokenizer import YKTokenizer, SPECIAL
 
22
 
23
  random.seed(1234)
24
  np.random.seed(1234)
@@ -28,9 +31,11 @@ DATADIR = os.path.join(OUT, "data")
28
  os.makedirs(DATADIR, exist_ok=True)
29
 
30
  SEQ_LEN = 1024
31
- TOK_BUDGET = 900_000_000 # fineweb portion target
32
- TOK_BUDGET_TOOL = 200_000_000 # synthetic + glaive portion cap
33
- TOK_TRAIN_DOCS = 60_000 # docs used to train the tokenizer
 
 
34
 
35
 
36
  # --------------------------------------------------------------------------
@@ -58,7 +63,8 @@ def train_tokenizer():
58
  # --------------------------------------------------------------------------
59
  CALC_TEMPLATES = [
60
  "What is {a} {op} {b}?", "Compute {a} {op} {b} for me.",
61
- "Calculate the result of {a} {op} {b}.", "If I have {a} and add/subtract/multiply/divide by {b} what do I get?",
 
62
  ]
63
  OPS = {"+": "plus", "-": "minus", "*": "times", "/": "divided by"}
64
  PY_SNIPPETS = [
@@ -66,27 +72,29 @@ PY_SNIPPETS = [
66
  "import math\nprint(round(math.sqrt({n}), 4))",
67
  "print(sorted([{a}, {b}, {c}]))",
68
  "print({n} ** 2 + {n})",
 
69
  ]
70
  FILE_Q = [
71
  "Read the file {path} and tell me what is on the first line.",
72
- "What is inside {path}?",
73
- "List the files in {dir}.",
74
  ]
75
  SYSTEM = ("You are clanker, a helpful assistant that can THINK and USE TOOLS. "
76
  "When you need to compute or inspect something, wrap a tool call in "
77
  "<tool name=\"...\">arguments</tool>. Available tools: calc(expr), "
78
- "python(code), read_file(path), list_dir(path). After a tool result "
79
- "appears in <result>...</result>, continue and give the final answer. "
80
- "You may use <think>...</think> to reason first.")
 
81
 
82
 
83
- def gen_synthetic(n=25000):
84
  out = []
85
  for _ in range(n):
86
  kind = random.random()
87
- if kind < 0.5:
88
  a = random.randint(2, 999); b = random.randint(2, 999)
89
- op = random.choice(["+", "-", "*", "/"]); b = max(2, b if op != "/" else random.randint(2, 50))
 
90
  if op == "/":
91
  a = a * b
92
  ans = eval(f"{a}{op}{b}")
@@ -101,7 +109,7 @@ def gen_synthetic(n=25000):
101
  tool = f'<tool name="python">{code}</tool>'
102
  try:
103
  import io, contextlib
104
- buf = io.StringIO();
105
  with contextlib.redirect_stdout(buf):
106
  exec(code, {"__builtins__": __builtins__}, {})
107
  result = buf.getvalue().strip()
@@ -128,7 +136,29 @@ def gen_synthetic(n=25000):
128
 
129
 
130
  # --------------------------------------------------------------------------
131
- # 3) Glaive function-calling (best-effort)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  # --------------------------------------------------------------------------
133
  def gen_glaive(max_examples=20000):
134
  out = []
@@ -152,7 +182,6 @@ def gen_glaive(max_examples=20000):
152
  elif role in ("human", "user"):
153
  parts.append(f"<user>{val}</user>")
154
  elif role in ("gpt", "assistant", "function"):
155
- # function_call style -> our <tool> tag
156
  val = val.replace("{\"name\":", "<tool name=\"").replace("\"function_call\"", "")
157
  parts.append(f"<assistant>{val}</assistant>")
158
  elif role == "tool":
@@ -166,12 +195,9 @@ def gen_glaive(max_examples=20000):
166
 
167
 
168
  # --------------------------------------------------------------------------
169
- # 4) Packing
170
  # --------------------------------------------------------------------------
171
  def pack(tok, texts, bin_path, budget, seq_len):
172
- """Append documents (packed into seq_len chunks) to bin_path until `budget`
173
- tokens are written. Each call tracks its OWN counter so budgets are
174
- independent across calls (fineweb vs synthetic vs glaive)."""
175
  n = 0
176
  buf = []
177
  with open(bin_path, "ab") as f:
@@ -198,52 +224,101 @@ def pack(tok, texts, bin_path, budget, seq_len):
198
 
199
  def main():
200
  ap = argparse.ArgumentParser()
201
- ap.add_argument("--seed-tok-only", action="store_true")
 
 
 
 
 
202
  args = ap.parse_args()
203
 
204
- tok_path = os.path.join(DATADIR, "tokenizer.json")
 
 
 
 
 
 
 
 
205
  if os.path.exists(tok_path):
206
  print("[prep] loading existing tokenizer")
207
  tok = YKTokenizer.load(tok_path)
208
  else:
209
- tok = train_tokenizer()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
 
211
- bin_path = os.path.join(DATADIR, "train.bin")
212
  if os.path.exists(bin_path):
213
  os.remove(bin_path)
214
 
215
- # --- fineweb-edu remainder (same stream, continue) ---
216
- print("[prep] tokenizing FineWeb-edu (this is the bandwidth-heavy step) ...")
217
  ds = load_dataset("HuggingFaceFW/fineweb-edu", "sample-10BT",
218
  streaming=True, split="train")
219
  gen = iter(ds)
220
- for _ in range(TOK_TRAIN_DOCS): # advance past tokenizer docs
221
  next(gen)
222
  def fineweb_iter():
223
  for ex in gen:
224
  yield ex["text"]
225
- n = pack(tok, fineweb_iter(), bin_path, TOK_BUDGET, SEQ_LEN)
226
  print(f"[prep] fineweb packed: {n:,} tokens")
227
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  # --- synthetic tool data ---
229
- print("[prep] generating synthetic tool-use conversations ...")
230
- synth = gen_synthetic(60000)
231
- n2 = pack(tok, synth, bin_path, TOK_BUDGET_TOOL, SEQ_LEN)
232
- print(f"[prep] synthetic packed: {n2:,} tokens")
 
 
 
 
 
 
233
 
234
  # --- glaive ---
235
- gl = gen_glaive(20000)
236
- n3 = 0
237
- if gl:
238
- n3 = pack(tok, gl, bin_path, TOK_BUDGET_TOOL, SEQ_LEN)
 
239
 
240
  total = n + n2 + n3
241
  meta = {"n_tokens": int(total), "seq_len": SEQ_LEN,
242
- "vocab_size": tok.vocab_size, "path": "data/train.bin"}
243
- with open(os.path.join(DATADIR, "meta.json"), "w") as f:
244
  json.dump(meta, f)
245
  print(f"[prep] DONE. total tokens={total:,} vocab={tok.vocab_size}")
246
- print(f"[prep] files: {bin_path}")
247
 
248
 
249
  if __name__ == "__main__":
 
1
  """
2
+ Data preparation for clankerDiffusion.
3
+
4
+ One streaming pass over FineWeb-edu (+ a Wikipedia slice for world knowledge,
5
+ + synthetic tool-use and RAG/retrieval examples that teach the special tags):
6
+ 1. first N docs -> train the byte-level BPE tokenizer (from scratch)
7
+ 2. remainder -> tokenize and pack into a flat uint16 .bin until token budget
8
+
9
+ Outputs (under --out-dir, default ./data):
10
+ tokenizer.json meta.json
11
+ train.bin (flat uint16 tokens)
12
+ meta.json ({n_tokens, seq_len, vocab_size})
13
+
14
+ Designed so EVERY training environment (local / Modal / Kaggle / TPU) can
15
+ regenerate the corpus itself with fast HF egress -- no 2 GB file transfer needed.
16
+ Use --scale to grow the corpus (e.g. --scale 4 for a multi-billion-token run).
17
  """
18
+ import os, json, random, argparse
19
  import numpy as np
20
  from datasets import load_dataset
21
 
22
  import tokenizer as tokmod
23
  from tokenizer import YKTokenizer, SPECIAL
24
+ from build_rag import FACTS, SYSTEM as RAG_SYSTEM
25
 
26
  random.seed(1234)
27
  np.random.seed(1234)
 
31
  os.makedirs(DATADIR, exist_ok=True)
32
 
33
  SEQ_LEN = 1024
34
+ TOK_TRAIN_DOCS = 80_000
35
+ TOK_BUDGET_FINEWEB = 900_000_000 # scaled by --scale
36
+ TOK_BUDGET_WIKI = 300_000_000 # scaled by --scale
37
+ TOK_BUDGET_TOOL = 250_000_000 # scaled by --scale
38
+ TOK_BUDGET_RAG = 150_000_000 # scaled by --scale
39
 
40
 
41
  # --------------------------------------------------------------------------
 
63
  # --------------------------------------------------------------------------
64
  CALC_TEMPLATES = [
65
  "What is {a} {op} {b}?", "Compute {a} {op} {b} for me.",
66
+ "Calculate the result of {a} {op} {b}.",
67
+ "If I start at {a} and apply {op} {b}, what do I get?",
68
  ]
69
  OPS = {"+": "plus", "-": "minus", "*": "times", "/": "divided by"}
70
  PY_SNIPPETS = [
 
72
  "import math\nprint(round(math.sqrt({n}), 4))",
73
  "print(sorted([{a}, {b}, {c}]))",
74
  "print({n} ** 2 + {n})",
75
+ "s='clanker'; print(s[::-1])",
76
  ]
77
  FILE_Q = [
78
  "Read the file {path} and tell me what is on the first line.",
79
+ "What is inside {path}?", "List the files in {dir}.",
 
80
  ]
81
  SYSTEM = ("You are clanker, a helpful assistant that can THINK and USE TOOLS. "
82
  "When you need to compute or inspect something, wrap a tool call in "
83
  "<tool name=\"...\">arguments</tool>. Available tools: calc(expr), "
84
+ "python(code), read_file(path), list_dir(path), retrieve(query). After "
85
+ "a tool result appears in <result>...</result>, continue and give the "
86
+ "final answer. If <context>...</context> is provided, use it. You may "
87
+ "use <think>...</think> to reason first.")
88
 
89
 
90
+ def gen_synthetic(n=60000):
91
  out = []
92
  for _ in range(n):
93
  kind = random.random()
94
+ if kind < 0.45:
95
  a = random.randint(2, 999); b = random.randint(2, 999)
96
+ op = random.choice(["+", "-", "*", "/"])
97
+ b = max(2, b if op != "/" else random.randint(2, 50))
98
  if op == "/":
99
  a = a * b
100
  ans = eval(f"{a}{op}{b}")
 
109
  tool = f'<tool name="python">{code}</tool>'
110
  try:
111
  import io, contextlib
112
+ buf = io.StringIO()
113
  with contextlib.redirect_stdout(buf):
114
  exec(code, {"__builtins__": __builtins__}, {})
115
  result = buf.getvalue().strip()
 
136
 
137
 
138
  # --------------------------------------------------------------------------
139
+ # 3) RAG / retrieval examples (teach <tool name="retrieve"> and <context>)
140
+ # --------------------------------------------------------------------------
141
+ def gen_rag(n=40000):
142
+ out = []
143
+ for _ in range(n):
144
+ topic, doc, q, a = random.choice(FACTS)
145
+ mode = random.random()
146
+ if mode < 0.5:
147
+ conv = (f"<bos><system>{RAG_SYSTEM}</system><user>{q}</user>"
148
+ f"<assistant><tool name=\"retrieve\">{q}</tool>"
149
+ f"<result>{doc}</result>{a}</assistant><eos>")
150
+ elif mode < 0.85:
151
+ conv = (f"<bos><system>{RAG_SYSTEM}</system><user>{q}</user>"
152
+ f"<assistant><context>{doc}</context>{a}</assistant><eos>")
153
+ else:
154
+ conv = (f"<bos><system>{RAG_SYSTEM}</system><user>{q}</user>"
155
+ f"<assistant><think>{doc}</think>{a}</assistant><eos>")
156
+ out.append(conv)
157
+ return out
158
+
159
+
160
+ # --------------------------------------------------------------------------
161
+ # 4) Glaive function-calling (best-effort)
162
  # --------------------------------------------------------------------------
163
  def gen_glaive(max_examples=20000):
164
  out = []
 
182
  elif role in ("human", "user"):
183
  parts.append(f"<user>{val}</user>")
184
  elif role in ("gpt", "assistant", "function"):
 
185
  val = val.replace("{\"name\":", "<tool name=\"").replace("\"function_call\"", "")
186
  parts.append(f"<assistant>{val}</assistant>")
187
  elif role == "tool":
 
195
 
196
 
197
  # --------------------------------------------------------------------------
198
+ # 5) Packing
199
  # --------------------------------------------------------------------------
200
  def pack(tok, texts, bin_path, budget, seq_len):
 
 
 
201
  n = 0
202
  buf = []
203
  with open(bin_path, "ab") as f:
 
224
 
225
  def main():
226
  ap = argparse.ArgumentParser()
227
+ ap.add_argument("--out-dir", default=DATADIR)
228
+ ap.add_argument("--scale", type=float, default=1.0,
229
+ help="multiply token budgets (e.g. 4 -> ~4x more data)")
230
+ ap.add_argument("--no-wiki", action="store_true")
231
+ ap.add_argument("--no-rag", action="store_true")
232
+ ap.add_argument("--no-glaive", action="store_true")
233
  args = ap.parse_args()
234
 
235
+ out_dir = args.out_dir
236
+ os.makedirs(out_dir, exist_ok=True)
237
+ scale = args.scale
238
+ bw_fw = int(TOK_BUDGET_FINEWEB * scale)
239
+ bw_wiki = int(TOK_BUDGET_WIKI * scale)
240
+ bw_tool = int(TOK_BUDGET_TOOL * scale)
241
+ bw_rag = int(TOK_BUDGET_RAG * scale)
242
+
243
+ tok_path = os.path.join(out_dir, "tokenizer.json")
244
  if os.path.exists(tok_path):
245
  print("[prep] loading existing tokenizer")
246
  tok = YKTokenizer.load(tok_path)
247
  else:
248
+ # try to reuse the canonical tokenizer from HF (keeps all runs compatible)
249
+ try:
250
+ print("[prep] no local tokenizer; downloading canonical one from HF ...")
251
+ import subprocess, shutil, tempfile
252
+ tmp = tempfile.mkdtemp()
253
+ subprocess.run(["hf", "download", "coderofpears/clankerDiffusion-base",
254
+ "data/tokenizer.json", "-d", tmp], check=True)
255
+ for root, _, files in os.walk(tmp):
256
+ if "tokenizer.json" in files:
257
+ shutil.copy(os.path.join(root, "tokenizer.json"), tok_path)
258
+ break
259
+ tok = YKTokenizer.load(tok_path)
260
+ except Exception as e:
261
+ print(f"[prep] HF tokenizer download failed ({e}); training a new one.")
262
+ tok = train_tokenizer()
263
 
264
+ bin_path = os.path.join(out_dir, "train.bin")
265
  if os.path.exists(bin_path):
266
  os.remove(bin_path)
267
 
268
+ # --- fineweb-edu ---
269
+ print(f"[prep] FineWeb-edu (budget {bw_fw:,}) ...")
270
  ds = load_dataset("HuggingFaceFW/fineweb-edu", "sample-10BT",
271
  streaming=True, split="train")
272
  gen = iter(ds)
273
+ for _ in range(TOK_TRAIN_DOCS):
274
  next(gen)
275
  def fineweb_iter():
276
  for ex in gen:
277
  yield ex["text"]
278
+ n = pack(tok, fineweb_iter(), bin_path, bw_fw, SEQ_LEN)
279
  print(f"[prep] fineweb packed: {n:,} tokens")
280
 
281
+ # --- wikipedia (world knowledge) ---
282
+ if not args.no_wiki:
283
+ print(f"[prep] Wikipedia (budget {bw_wiki:,}) ...")
284
+ try:
285
+ wds = load_dataset("wikipedia", "20220301.en",
286
+ streaming=True, split="train")
287
+ def wiki_iter():
288
+ for ex in wds:
289
+ yield ex["text"]
290
+ nw = pack(tok, wiki_iter(), bin_path, bw_wiki, SEQ_LEN)
291
+ print(f"[prep] wikipedia packed: {nw:,} tokens")
292
+ n += nw
293
+ except Exception as e:
294
+ print(f"[prep] wikipedia skipped: {e}")
295
+
296
  # --- synthetic tool data ---
297
+ synth = gen_synthetic(int(60_000 * scale) + 60000)
298
+ n2 = pack(tok, synth, bin_path, bw_tool, SEQ_LEN)
299
+ print(f"[prep] synthetic tool packed: {n2:,} tokens")
300
+
301
+ # --- RAG / retrieval data ---
302
+ if not args.no_rag:
303
+ rag = gen_rag(int(40_000 * scale) + 40000)
304
+ nr = pack(tok, rag, bin_path, bw_rag, SEQ_LEN)
305
+ print(f"[prep] RAG packed: {nr:,} tokens")
306
+ n2 += nr
307
 
308
  # --- glaive ---
309
+ if not args.no_glaive:
310
+ gl = gen_glaive(20000)
311
+ n3 = pack(tok, gl, bin_path, bw_tool, SEQ_LEN) if gl else 0
312
+ else:
313
+ n3 = 0
314
 
315
  total = n + n2 + n3
316
  meta = {"n_tokens": int(total), "seq_len": SEQ_LEN,
317
+ "vocab_size": tok.vocab_size, "path": "train.bin", "scale": scale}
318
+ with open(os.path.join(out_dir, "meta.json"), "w") as f:
319
  json.dump(meta, f)
320
  print(f"[prep] DONE. total tokens={total:,} vocab={tok.vocab_size}")
321
+ print(f"[prep] files in {out_dir}")
322
 
323
 
324
  if __name__ == "__main__":
push_hf.py CHANGED
@@ -1,6 +1,5 @@
1
  """
2
- clankerDiffusion — push artifacts to HuggingFace (via the `hf` CLI, which
3
- correctly creates the first commit on a brand-new repo).
4
 
5
  python push_hf.py --what all # code + tokenizer + rag corpus + train.bin
6
  python push_hf.py --what code # .py sources + tokenizer + rag corpus
@@ -8,13 +7,15 @@ correctly creates the first commit on a brand-new repo).
8
  python push_hf.py --what ckpt # latest checkpoints
9
 
10
  Repos (under your HF user):
11
- clankerDiffusion-base code + tokenizer + rag corpus
12
- clankerDiffusion-data train.bin (packed training corpus)
13
- clankerDiffusion-checkpoints model checkpoints (.pt)
14
  """
15
  import os
16
- import subprocess
17
  import argparse
 
 
 
18
 
19
  HERE = os.path.dirname(os.path.abspath(__file__))
20
  DATADIR = os.path.join(HERE, "data")
@@ -29,14 +30,28 @@ PY_SOURCES = ["model.py", "tokenizer.py", "prep.py", "train.py", "infer.py",
29
  "colab_train.py", "modal_train.py", "push_hf.py", "upload_artifacts.py"]
30
 
31
 
32
- def _run(cmd):
33
- print("+", " ".join(cmd), flush=True)
34
- subprocess.run(cmd, check=True)
 
 
 
 
 
 
 
 
 
35
 
36
 
37
  def _upload(repo, local, patterns):
38
- # upload only the matching files via a temp staging dir
39
- import tempfile, shutil, fnmatch
 
 
 
 
 
40
  stage = tempfile.mkdtemp()
41
  try:
42
  for root, _, files in os.walk(local):
@@ -49,7 +64,7 @@ def _upload(repo, local, patterns):
49
  if not os.listdir(stage):
50
  print(f"[hf] nothing matched for {repo}")
51
  return
52
- _run(["hf", "upload", repo, stage, "--repo-type", "model"])
53
  print(f"[hf] -> {repo}")
54
  finally:
55
  shutil.rmtree(stage, ignore_errors=True)
 
1
  """
2
+ clankerDiffusion — push artifacts to HuggingFace (via huggingface_hub).
 
3
 
4
  python push_hf.py --what all # code + tokenizer + rag corpus + train.bin
5
  python push_hf.py --what code # .py sources + tokenizer + rag corpus
 
7
  python push_hf.py --what ckpt # latest checkpoints
8
 
9
  Repos (under your HF user):
10
+ coderofpears/clankerDiffusion-base code + tokenizer + rag corpus
11
+ coderofpears/clankerDiffusion-data train.bin (packed training corpus)
12
+ coderofpears/clankerDiffusion-checkpoints model checkpoints (.pt)
13
  """
14
  import os
 
15
  import argparse
16
+ import tempfile
17
+ import shutil
18
+ import fnmatch
19
 
20
  HERE = os.path.dirname(os.path.abspath(__file__))
21
  DATADIR = os.path.join(HERE, "data")
 
30
  "colab_train.py", "modal_train.py", "push_hf.py", "upload_artifacts.py"]
31
 
32
 
33
+ def _token():
34
+ # prefer explicit env, else read from .env
35
+ tok = os.environ.get("HF_TOKEN")
36
+ if tok:
37
+ return tok
38
+ for p in (os.path.join(HERE, ".env"), os.path.join(os.path.expanduser("~"), ".env")):
39
+ if os.path.exists(p):
40
+ for line in open(p, encoding="utf-8"):
41
+ line = line.strip()
42
+ if line.startswith("HF_TOKEN"):
43
+ return line.split("=", 1)[1].strip().strip('"').strip("'")
44
+ raise SystemExit("HF_TOKEN not found in env or .env")
45
 
46
 
47
  def _upload(repo, local, patterns):
48
+ from huggingface_hub import HfApi
49
+ api = HfApi(token=_token())
50
+ try:
51
+ api.create_repo(repo_id=repo, repo_type="model", exist_ok=True)
52
+ except Exception as e:
53
+ print(f"[hf] create_repo note: {e}")
54
+
55
  stage = tempfile.mkdtemp()
56
  try:
57
  for root, _, files in os.walk(local):
 
64
  if not os.listdir(stage):
65
  print(f"[hf] nothing matched for {repo}")
66
  return
67
+ api.upload_folder(folder_path=stage, repo_id=repo, repo_type="model")
68
  print(f"[hf] -> {repo}")
69
  finally:
70
  shutil.rmtree(stage, ignore_errors=True)