Samarth0710 commited on
Commit
411e59d
·
verified ·
1 Parent(s): 6d1d1fc

Upload reasoning pipeline script

Browse files
Files changed (1) hide show
  1. reasoning_pipeline.py +438 -1
reasoning_pipeline.py CHANGED
@@ -1 +1,438 @@
1
- # empty placeholder; real upload below
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Cross-Model LoRA Adapter Prediction — Reasoning validation (GSM8K held-out).
3
+
4
+ Pipeline:
5
+ 1) Build 9 reasoning datasets in a uniform "Q -> rationale + #### answer" format.
6
+ 2) Train LoRA adapters on Qwen2.5-0.5B-Instruct (X) and Llama-3.2-1B-Instruct (Y) for
7
+ 8 anchor tasks + GSM8K (oracle) per model. Y for GSM8K is held-out from the mapping.
8
+ 3) Fit mappings (mean, global_ridge, topk_global_ridge K=4) on the 8 anchor pairs and
9
+ predict Y_GSM8K from X_GSM8K.
10
+ 4) Eval base_Y, mean, global_ridge, topk_global_ridge, oracle_Y on 250 GSM8K test problems
11
+ using greedy generation + numeric-answer match on the final number after '####'.
12
+ 5) Save adapters + results.json under /app/reasoning/, then push to the Hub.
13
+ """
14
+
15
+ import os, re, json, gc, time, math, argparse, random, shutil
16
+ from pathlib import Path
17
+ from typing import List, Dict, Tuple
18
+
19
+ import numpy as np
20
+ import torch
21
+ from datasets import load_dataset, Dataset
22
+ from transformers import AutoTokenizer, AutoModelForCausalLM
23
+ from peft import LoraConfig, PeftModel, get_peft_model
24
+ from trl import SFTTrainer, SFTConfig
25
+
26
+ ROOT = Path("/app/reasoning")
27
+ ROOT.mkdir(parents=True, exist_ok=True)
28
+
29
+ MODEL_X = "Qwen/Qwen2.5-0.5B-Instruct"
30
+ MODEL_Y = "meta-llama/Llama-3.2-1B-Instruct"
31
+
32
+ LORA_KWARGS = dict(r=8, lora_alpha=16, lora_dropout=0.0, target_modules=["q_proj", "v_proj"], bias="none")
33
+
34
+ # ---------------- dataset preparation ----------------
35
+ def _final_num(s: str):
36
+ nums = re.findall(r"-?\d+\.?\d*", s.replace(",", ""))
37
+ return nums[-1] if nums else None
38
+
39
+ def fmt(question: str, answer_text: str, gold: str) -> Dict:
40
+ """Return chat-format messages. assistant ends with '#### gold'."""
41
+ rationale = answer_text.strip()
42
+ if "####" not in rationale:
43
+ rationale = f"{rationale}\n#### {gold}"
44
+ return {
45
+ "messages": [
46
+ {"role": "user", "content": question.strip()},
47
+ {"role": "assistant", "content": rationale},
48
+ ],
49
+ "gold": str(gold),
50
+ }
51
+
52
+ def load_gsm8k(split, n):
53
+ ds = load_dataset("openai/gsm8k", "main", split=split)
54
+ ds = ds.shuffle(seed=0).select(range(min(n, len(ds))))
55
+ out = []
56
+ for r in ds:
57
+ gold = _final_num(r["answer"].split("####")[-1])
58
+ out.append(fmt(r["question"], r["answer"], gold))
59
+ return out
60
+
61
+ def load_svamp(n):
62
+ ds = load_dataset("ChilleD/SVAMP", split="train").shuffle(seed=0).select(range(min(n, 700)))
63
+ out = []
64
+ for r in ds:
65
+ q = (r["Body"] + " " + r["Question"]).strip()
66
+ gold = str(r["Answer"]).rstrip(".0") if "." in str(r["Answer"]) else str(r["Answer"])
67
+ try:
68
+ f = float(r["Answer"])
69
+ gold = str(int(f)) if f.is_integer() else str(f)
70
+ except Exception:
71
+ gold = str(r["Answer"])
72
+ rationale = f"{r['Equation']} = {gold}"
73
+ out.append(fmt(q, rationale, gold))
74
+ return out
75
+
76
+ def load_multiarith(n):
77
+ ds = load_dataset("ChilleD/MultiArith", split="train").shuffle(seed=0).select(range(min(n, 400)))
78
+ out = []
79
+ for r in ds:
80
+ gold = str(r["final_ans"]).strip()
81
+ out.append(fmt(r["question"], f"The answer is {gold}.", gold))
82
+ return out
83
+
84
+ def load_aqua(n):
85
+ ds = load_dataset("deepmind/aqua_rat", "raw", split="train").shuffle(seed=0).select(range(min(n, 800)))
86
+ out = []
87
+ for r in ds:
88
+ opts = "\n".join(r["options"])
89
+ q = f"{r['question']}\nOptions:\n{opts}"
90
+ gold = str(r["correct"]).strip()
91
+ out.append(fmt(q, r["rationale"], gold))
92
+ return out
93
+
94
+ def load_mathplus(n):
95
+ ds = load_dataset("TIGER-Lab/MATH-plus", split="train").shuffle(seed=0).select(range(min(n*3, 3000)))
96
+ out = []
97
+ for r in ds:
98
+ out_text = r["output"]
99
+ # extract a final number/expression — fallback: last token
100
+ gold = _final_num(out_text) or out_text.strip().split()[-1][:32]
101
+ out.append(fmt(r["instruction"], out_text, gold))
102
+ if len(out) >= n:
103
+ break
104
+ return out
105
+
106
+ def load_strategyqa(n):
107
+ ds = load_dataset("tasksource/strategy-qa", split="train").shuffle(seed=0).select(range(min(n, 2000)))
108
+ out = []
109
+ for r in ds:
110
+ gold = "yes" if r["answer"] else "no"
111
+ facts = " ".join(r["facts"])
112
+ rationale = facts
113
+ out.append(fmt(r["question"] + "\nAnswer yes or no.", rationale, gold))
114
+ return out
115
+
116
+ def _mc_format(stem, choices, label):
117
+ if isinstance(choices, dict):
118
+ texts, labels = choices["text"], choices["label"]
119
+ else:
120
+ texts, labels = choices.text, choices.label
121
+ body = "\n".join(f"{l}. {t}" for l, t in zip(labels, texts))
122
+ return f"{stem}\n{body}\nAnswer with the letter only.", str(label).strip()
123
+
124
+ def load_openbookqa(n):
125
+ ds = load_dataset("allenai/openbookqa", "main", split="train").shuffle(seed=0).select(range(min(n, 4000)))
126
+ out = []
127
+ for r in ds:
128
+ q, gold = _mc_format(r["question_stem"], r["choices"], r["answerKey"])
129
+ out.append(fmt(q, f"The answer is {gold}.", gold))
130
+ return out
131
+
132
+ def load_arc(n, cfg):
133
+ ds = load_dataset("allenai/ai2_arc", cfg, split="train").shuffle(seed=0).select(range(min(n, 2000)))
134
+ out = []
135
+ for r in ds:
136
+ q, gold = _mc_format(r["question"], r["choices"], r["answerKey"])
137
+ out.append(fmt(q, f"The answer is {gold}.", gold))
138
+ return out
139
+
140
+ DATASETS = {
141
+ "svamp": lambda n: load_svamp(n),
142
+ "multiarith": lambda n: load_multiarith(n),
143
+ "aqua": lambda n: load_aqua(n),
144
+ "mathplus": lambda n: load_mathplus(n),
145
+ "strategyqa": lambda n: load_strategyqa(n),
146
+ "openbookqa": lambda n: load_openbookqa(n),
147
+ "arc_easy": lambda n: load_arc(n, "ARC-Easy"),
148
+ "arc_chall": lambda n: load_arc(n, "ARC-Challenge"),
149
+ "gsm8k": lambda n: load_gsm8k("train", n),
150
+ }
151
+
152
+ ANCHOR_TASKS = ["svamp", "multiarith", "aqua", "mathplus", "strategyqa", "openbookqa", "arc_easy", "arc_chall"]
153
+ HELD_OUT = "gsm8k"
154
+
155
+ # ---------------- training ----------------
156
+ def train_one(model_id: str, task: str, examples: List[Dict], out_dir: Path, max_len: int = 384, n_train: int = 800):
157
+ out_dir.mkdir(parents=True, exist_ok=True)
158
+ if (out_dir / "adapter_config.json").exists():
159
+ print(f" [skip] {out_dir} already exists")
160
+ return
161
+ tok = AutoTokenizer.from_pretrained(model_id)
162
+ if tok.pad_token is None:
163
+ tok.pad_token = tok.eos_token
164
+ model = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype=torch.bfloat16, device_map="cuda")
165
+ ds = Dataset.from_list([{"messages": e["messages"]} for e in examples[:n_train]])
166
+ cfg = SFTConfig(
167
+ output_dir=str(out_dir / "_trainer"),
168
+ per_device_train_batch_size=8,
169
+ gradient_accumulation_steps=1,
170
+ num_train_epochs=1,
171
+ learning_rate=2e-4,
172
+ lr_scheduler_type="cosine",
173
+ warmup_ratio=0.05,
174
+ bf16=True,
175
+ max_seq_length=max_len,
176
+ logging_strategy="steps",
177
+ logging_steps=20,
178
+ logging_first_step=True,
179
+ save_strategy="no",
180
+ report_to="none",
181
+ disable_tqdm=True,
182
+ gradient_checkpointing=False,
183
+ )
184
+ peft_cfg = LoraConfig(task_type="CAUSAL_LM", **LORA_KWARGS)
185
+ trainer = SFTTrainer(
186
+ model=model,
187
+ args=cfg,
188
+ train_dataset=ds,
189
+ peft_config=peft_cfg,
190
+ processing_class=tok,
191
+ )
192
+ trainer.train()
193
+ trainer.model.save_pretrained(str(out_dir))
194
+ tok.save_pretrained(str(out_dir))
195
+ shutil.rmtree(out_dir / "_trainer", ignore_errors=True)
196
+ del trainer, model
197
+ gc.collect(); torch.cuda.empty_cache()
198
+
199
+ # ---------------- adapter I/O ----------------
200
+ def load_adapter_state(path: Path) -> Dict[str, torch.Tensor]:
201
+ """Return {param_name: tensor (cpu, float32)} for the LoRA-only weights."""
202
+ from safetensors.torch import load_file
203
+ f = path / "adapter_model.safetensors"
204
+ sd = load_file(str(f))
205
+ return {k: v.detach().to(torch.float32).cpu() for k, v in sd.items()}
206
+
207
+ def save_adapter_state(reference_dir: Path, new_state: Dict[str, torch.Tensor], out_dir: Path):
208
+ """Copy adapter_config.json from reference, write new safetensors."""
209
+ from safetensors.torch import save_file
210
+ out_dir.mkdir(parents=True, exist_ok=True)
211
+ shutil.copy(reference_dir / "adapter_config.json", out_dir / "adapter_config.json")
212
+ for extra in ["tokenizer.json", "tokenizer_config.json", "special_tokens_map.json"]:
213
+ if (reference_dir / extra).exists():
214
+ shutil.copy(reference_dir / extra, out_dir / extra)
215
+ save_file({k: v.to(torch.bfloat16).contiguous() for k, v in new_state.items()},
216
+ str(out_dir / "adapter_model.safetensors"))
217
+
218
+ def flatten(state: Dict[str, torch.Tensor]) -> Tuple[np.ndarray, List[Tuple[str, tuple]]]:
219
+ keys = sorted(state.keys())
220
+ vecs, schema = [], []
221
+ for k in keys:
222
+ t = state[k]
223
+ schema.append((k, tuple(t.shape)))
224
+ vecs.append(t.flatten().numpy())
225
+ return np.concatenate(vecs), schema
226
+
227
+ def unflatten(vec: np.ndarray, schema) -> Dict[str, torch.Tensor]:
228
+ out, off = {}, 0
229
+ for k, shp in schema:
230
+ n = int(np.prod(shp))
231
+ out[k] = torch.from_numpy(vec[off:off+n].reshape(shp).copy())
232
+ off += n
233
+ return out
234
+
235
+ # ---------------- mappings ----------------
236
+ def mean_baseline(Y_anchors: np.ndarray, X_target: np.ndarray) -> np.ndarray:
237
+ return Y_anchors.mean(axis=0)
238
+
239
+ def global_ridge(X_anchors: np.ndarray, Y_anchors: np.ndarray, X_target: np.ndarray, lam=1e-3) -> np.ndarray:
240
+ Xb, Yb = X_anchors.mean(0), Y_anchors.mean(0)
241
+ Xc, Yc = X_anchors - Xb, Y_anchors - Yb
242
+ N = Xc.shape[0]
243
+ G = Xc @ Xc.T # (N,N)
244
+ alpha = np.linalg.solve(G + lam * np.eye(N), Xc @ (X_target - Xb))
245
+ return Yb + alpha @ Yc
246
+
247
+ def topk_global_ridge(X_anchors, Y_anchors, X_target, K=4, lam=1e-3):
248
+ Xb_full = X_anchors.mean(0)
249
+ sims = []
250
+ for i in range(X_anchors.shape[0]):
251
+ a = X_anchors[i] - Xb_full
252
+ b = X_target - Xb_full
253
+ denom = (np.linalg.norm(a) * np.linalg.norm(b)) + 1e-12
254
+ sims.append(float(a @ b) / denom)
255
+ sims = np.array(sims)
256
+ idx = np.argsort(-sims)[:K]
257
+ return global_ridge(X_anchors[idx], Y_anchors[idx], X_target, lam=lam), idx.tolist()
258
+
259
+ # ---------------- eval ----------------
260
+ def extract_pred(text: str, mode: str):
261
+ """mode = 'num' or 'letter' or 'yesno'."""
262
+ if mode == "num":
263
+ if "####" in text:
264
+ text = text.split("####")[-1]
265
+ return _final_num(text)
266
+ if mode == "letter":
267
+ m = re.search(r"\b([A-E])\b", text.upper())
268
+ return m.group(1) if m else None
269
+ if mode == "yesno":
270
+ t = text.lower()
271
+ if "yes" in t and "no" not in t.split("yes")[0][-10:]:
272
+ return "yes"
273
+ if "no" in t:
274
+ return "no"
275
+ return None
276
+ return text.strip()
277
+
278
+ def eval_adapter(base_id: str, adapter_dir: Path | None, eval_set: List[Dict], mode: str, max_new=200):
279
+ tok = AutoTokenizer.from_pretrained(base_id, padding_side="left")
280
+ if tok.pad_token is None:
281
+ tok.pad_token = tok.eos_token
282
+ model = AutoModelForCausalLM.from_pretrained(base_id, torch_dtype=torch.bfloat16, device_map="cuda")
283
+ if adapter_dir is not None:
284
+ model = PeftModel.from_pretrained(model, str(adapter_dir))
285
+ model.eval()
286
+ correct = 0
287
+ preds = []
288
+ bs = 8
289
+ for i in range(0, len(eval_set), bs):
290
+ batch = eval_set[i:i+bs]
291
+ prompts = [tok.apply_chat_template([e["messages"][0]], tokenize=False, add_generation_prompt=True) for e in batch]
292
+ enc = tok(prompts, return_tensors="pt", padding=True, truncation=True, max_length=512).to("cuda")
293
+ with torch.no_grad():
294
+ out = model.generate(**enc, max_new_tokens=max_new, do_sample=False, pad_token_id=tok.pad_token_id)
295
+ gen = tok.batch_decode(out[:, enc["input_ids"].shape[1]:], skip_special_tokens=True)
296
+ for e, g in zip(batch, gen):
297
+ p = extract_pred(g, mode)
298
+ gold = e["gold"]
299
+ if mode == "num":
300
+ try:
301
+ ok = (p is not None) and (abs(float(p) - float(gold)) < 1e-3)
302
+ except Exception:
303
+ ok = False
304
+ else:
305
+ ok = (p is not None) and (str(p).strip().lower() == str(gold).strip().lower())
306
+ correct += int(ok)
307
+ preds.append({"gold": gold, "pred": p, "raw": g[:200]})
308
+ acc = correct / len(eval_set)
309
+ del model; gc.collect(); torch.cuda.empty_cache()
310
+ return acc, preds
311
+
312
+ # ---------------- orchestrator ----------------
313
+ def stage_train(n_train=800, n_per_task=900):
314
+ for task in ANCHOR_TASKS + [HELD_OUT]:
315
+ print(f"\n=== {task} ===")
316
+ examples = DATASETS[task](n_per_task)
317
+ print(f" examples: {len(examples)}")
318
+ for tag, mid in [("X", MODEL_X), ("Y", MODEL_Y)]:
319
+ outd = ROOT / tag / task
320
+ print(f" training {tag}/{task} -> {outd}")
321
+ t0 = time.time()
322
+ train_one(mid, task, examples, outd, n_train=n_train)
323
+ print(f" done in {time.time()-t0:.1f}s")
324
+
325
+ def stage_predict():
326
+ # load all anchor X, Y
327
+ X_states = {t: load_adapter_state(ROOT / "X" / t) for t in ANCHOR_TASKS + [HELD_OUT]}
328
+ Y_states = {t: load_adapter_state(ROOT / "Y" / t) for t in ANCHOR_TASKS + [HELD_OUT]}
329
+ # use Y schema for output
330
+ Y_ref_dir = ROOT / "Y" / ANCHOR_TASKS[0]
331
+ Y_vecs, Y_schema = {}, None
332
+ for t, s in Y_states.items():
333
+ v, sch = flatten(s)
334
+ Y_vecs[t] = v
335
+ Y_schema = sch
336
+ X_vecs, _ = {}, None
337
+ for t, s in X_states.items():
338
+ v, _ = flatten(s)
339
+ X_vecs[t] = v
340
+ Xa = np.stack([X_vecs[t] for t in ANCHOR_TASKS])
341
+ Ya = np.stack([Y_vecs[t] for t in ANCHOR_TASKS])
342
+ Xt = X_vecs[HELD_OUT]
343
+ Yt_oracle = Y_vecs[HELD_OUT]
344
+
345
+ preds = {}
346
+ preds["mean"] = mean_baseline(Ya, Xt)
347
+ preds["global_ridge"] = global_ridge(Xa, Ya, Xt)
348
+ yhat, idx = topk_global_ridge(Xa, Ya, Xt, K=4)
349
+ preds["topk4_global_ridge"] = yhat
350
+ print("topk4 indices:", [ANCHOR_TASKS[i] for i in idx])
351
+
352
+ cos = lambda a, b: float(a @ b / (np.linalg.norm(a)*np.linalg.norm(b)+1e-12))
353
+ cosines = {m: cos(v, Yt_oracle) for m, v in preds.items()}
354
+ print("cosines:", cosines)
355
+
356
+ out_pred = ROOT / "Y_pred"
357
+ for m, v in preds.items():
358
+ save_adapter_state(Y_ref_dir, unflatten(v, Y_schema), out_pred / m)
359
+ return cosines
360
+
361
+ def stage_eval(n_eval=250):
362
+ eval_set = load_gsm8k("test", n_eval)
363
+ print(f"eval set size: {len(eval_set)}")
364
+ results = {}
365
+ # base Y (no adapter)
366
+ print("evaluating base Y ...")
367
+ acc, _ = eval_adapter(MODEL_Y, None, eval_set, mode="num")
368
+ results["base_Y"] = acc; print(" base_Y:", acc)
369
+ # base X
370
+ print("evaluating base X ...")
371
+ acc, _ = eval_adapter(MODEL_X, None, eval_set, mode="num")
372
+ results["base_X"] = acc; print(" base_X:", acc)
373
+ # oracle X
374
+ print("evaluating oracle X ...")
375
+ acc, _ = eval_adapter(MODEL_X, ROOT / "X" / HELD_OUT, eval_set, mode="num")
376
+ results["oracle_X"] = acc; print(" oracle_X:", acc)
377
+ # oracle Y
378
+ print("evaluating oracle Y ...")
379
+ acc, _ = eval_adapter(MODEL_Y, ROOT / "Y" / HELD_OUT, eval_set, mode="num")
380
+ results["oracle_Y"] = acc; print(" oracle_Y:", acc)
381
+ # predicted Y
382
+ for m in ["mean", "global_ridge", "topk4_global_ridge"]:
383
+ d = ROOT / "Y_pred" / m
384
+ if not (d / "adapter_config.json").exists():
385
+ continue
386
+ print(f"evaluating predicted Y/{m} ...")
387
+ acc, _ = eval_adapter(MODEL_Y, d, eval_set, mode="num")
388
+ results[m] = acc; print(f" {m}:", acc)
389
+ return results
390
+
391
+ if __name__ == "__main__":
392
+ ap = argparse.ArgumentParser()
393
+ ap.add_argument("--stage", default="all", choices=["train", "predict", "eval", "all", "smoke"])
394
+ ap.add_argument("--n_train", type=int, default=800)
395
+ ap.add_argument("--n_eval", type=int, default=250)
396
+ ap.add_argument("--push_repo", type=str, default=None)
397
+ args = ap.parse_args()
398
+
399
+ random.seed(0); np.random.seed(0); torch.manual_seed(0)
400
+
401
+ if args.stage == "smoke":
402
+ # tiny: 2 anchors, 50 examples, 20 eval
403
+ ANCHOR_TASKS[:] = ["svamp", "multiarith"]
404
+ stage_train(n_train=50, n_per_task=60)
405
+ cos = stage_predict()
406
+ res = stage_eval(n_eval=20)
407
+ print("SMOKE results:", res, "cos:", cos)
408
+ else:
409
+ all_results = {}
410
+ if args.stage in ("train", "all"):
411
+ stage_train(n_train=args.n_train)
412
+ if args.stage in ("predict", "all"):
413
+ all_results["cosines"] = stage_predict()
414
+ if args.stage in ("eval", "all"):
415
+ all_results["accuracy"] = stage_eval(n_eval=args.n_eval)
416
+ (ROOT / "results.json").write_text(json.dumps(all_results, indent=2))
417
+ print("\nFINAL:", json.dumps(all_results, indent=2))
418
+
419
+ if args.push_repo:
420
+ from huggingface_hub import HfApi
421
+ api = HfApi()
422
+ api.create_repo(args.push_repo, exist_ok=True)
423
+ print(f"Uploading reasoning/ to {args.push_repo} ...")
424
+ api.upload_folder(
425
+ folder_path=str(ROOT),
426
+ path_in_repo="reasoning",
427
+ repo_id=args.push_repo,
428
+ repo_type="model",
429
+ commit_message="Add cross-model LoRA adapter prediction — reasoning validation (GSM8K)",
430
+ )
431
+ api.upload_file(
432
+ path_or_fileobj="/app/reasoning_pipeline.py",
433
+ path_in_repo="reasoning_pipeline.py",
434
+ repo_id=args.push_repo,
435
+ repo_type="model",
436
+ commit_message="Add reasoning pipeline script",
437
+ )
438
+ print("Push complete.")