vanishingradient commited on
Commit
488f5fc
·
verified ·
1 Parent(s): 8cf91b5

Add kaggle/train_grpo.py

Browse files
Files changed (1) hide show
  1. kaggle/train_grpo.py +125 -26
kaggle/train_grpo.py CHANGED
@@ -22,18 +22,39 @@ import traceback
22
  from pathlib import Path
23
  from typing import Any, Callable
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  # ---------------------------------------------------------------------------
26
  # Bootstrap package (notebook may only download this file)
27
  # ---------------------------------------------------------------------------
28
 
29
 
30
  def _ensure_package() -> None:
 
31
  try:
32
  import deepagents_rl # noqa: F401
33
 
 
34
  return
35
  except ImportError:
36
- pass
37
 
38
  token = os.environ.get("HF_TOKEN")
39
  if not token:
@@ -55,6 +76,8 @@ def _ensure_package() -> None:
55
  sys.path.insert(0, str(pkg_parent))
56
  import deepagents_rl # noqa: F401
57
 
 
 
58
 
59
  # ---------------------------------------------------------------------------
60
  # Secrets
@@ -62,6 +85,7 @@ def _ensure_package() -> None:
62
 
63
 
64
  def load_secrets() -> None:
 
65
  try:
66
  from kaggle_secrets import UserSecretsClient
67
 
@@ -123,6 +147,13 @@ def load_secrets() -> None:
123
  os.environ.setdefault("WANDB_PROJECT", "deepagents-rl-coding")
124
  os.environ.setdefault("HF_HUB_ENABLE_HF_TRANSFER", "0")
125
  os.environ.setdefault("TRL_EXPERIMENTAL_SILENCE", "1")
 
 
 
 
 
 
 
126
 
127
 
128
  # ---------------------------------------------------------------------------
@@ -217,12 +248,16 @@ def run_heldout_agent_eval(
217
 
218
  rows = []
219
  model.eval()
220
- for p in problems:
 
221
  env = DeepAgentSWEEnv()
 
 
222
  try:
223
  env.reset(**{k: v for k, v in p.items() if k != "prompt"})
224
  tools = _env_tool_callables(env)
225
  tool_map = {t.__name__: t for t in tools}
 
226
  prompt = p.get("prompt")
227
  if isinstance(prompt, list):
228
  messages = list(prompt)
@@ -232,6 +267,7 @@ def run_heldout_agent_eval(
232
  final_text = ""
233
  lf = getattr(env, "_lf", None)
234
  for turn in range(max_turns):
 
235
  try:
236
  prompt_text = tokenizer.apply_chat_template(
237
  messages,
@@ -279,11 +315,26 @@ def run_heldout_agent_eval(
279
  completion = _generate_once()
280
  calls = _parse_tool_calls(completion)
281
 
 
 
 
 
 
 
 
 
282
  final_text = completion
283
  messages.append({"role": "assistant", "content": completion})
284
  if not calls:
 
285
  break
286
  for call in calls:
 
 
 
 
 
 
287
  fn = tool_map.get(call["name"])
288
  if fn is None:
289
  result = f"Unknown tool: {call['name']}"
@@ -307,31 +358,34 @@ def run_heldout_agent_eval(
307
 
308
  reward = float(env.get_reward())
309
  report = getattr(env, "_last_report", {}) or {}
310
- rows.append(
311
- {
312
- "task_id": p.get("task_id") or p.get("instance_id"),
313
- "prompt": (
314
- prompt
315
- if isinstance(prompt, str)
316
- else json.dumps(prompt)[:2000]
317
- ),
318
- "completion": final_text[:4000],
319
- "pass_frac": float(report.get("pass_frac") or reward),
320
- "reward_total": reward,
321
- "resolved": bool(report.get("resolved")),
322
- "resolution": report.get("resolution"),
323
- "n_tool_calls": len(getattr(env, "_tool_calls", []) or []),
324
- "tools_used": [
325
- c.get("name") for c in (getattr(env, "_tool_calls", []) or [])
326
- ],
327
- "langfuse_trace_id": getattr(env, "_trace_id", "") or "",
328
- }
329
- )
 
 
330
  except Exception as e: # noqa: BLE001
331
  traceback.print_exc()
 
332
  rows.append(
333
  {
334
- "task_id": p.get("task_id") or p.get("instance_id"),
335
  "prompt": str(p.get("prompt"))[:2000],
336
  "completion": f"EVAL_ERROR: {e}",
337
  "pass_frac": 0.0,
@@ -340,6 +394,7 @@ def run_heldout_agent_eval(
340
  "resolution": "ERROR",
341
  }
342
  )
 
343
  finally:
344
  try:
345
  env._end_trace()
@@ -488,11 +543,21 @@ def make_eval_before_save_callback(
488
 
489
  rev = f"grpo-step-{step}"
490
  problems = eval_problems[:n_eval]
491
- print(f"\n=== AGENT EVAL BEFORE CHECKPOINT step={step} rev={rev} n={len(problems)} ===")
 
 
 
 
492
  try:
493
  results = run_heldout_agent_eval(
494
  model, tokenizer, problems, max_turns=max_eval_turns
495
  )
 
 
 
 
 
 
496
  import wandb
497
 
498
  wandb_run_id = None
@@ -668,9 +733,25 @@ def _load_swe_splits(cfg: Cfg):
668
  def main(cfg: Cfg | None = None) -> None:
669
  global _TOKENIZER
670
  cfg = cfg or Cfg()
 
671
  load_secrets()
672
  _ensure_package()
673
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
674
  import torch
675
  import wandb
676
  from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
@@ -686,15 +767,20 @@ def main(cfg: Cfg | None = None) -> None:
686
  headers = {"ngrok-skip-browser-warning": "1"}
687
  if os.environ.get("SWE_GRADER_TOKEN"):
688
  headers["Authorization"] = f"Bearer {os.environ['SWE_GRADER_TOKEN']}"
 
689
  health = requests.get(f"{grader}/health", headers=headers, timeout=30)
690
  health.raise_for_status()
691
- print("[grader] health", health.json())
692
 
693
  os.environ["WANDB_PROJECT"] = os.getenv("WANDB_PROJECT", "deepagents-rl-coding")
 
694
  wandb.login(key=os.environ["WANDB_API_KEY"])
695
 
 
696
  model_id = resolve_model()
 
697
  train, eval_problems = _load_swe_splits(cfg)
 
698
 
699
  bnb = BitsAndBytesConfig(
700
  load_in_4bit=True,
@@ -702,6 +788,7 @@ def main(cfg: Cfg | None = None) -> None:
702
  bnb_4bit_quant_type="nf4",
703
  bnb_4bit_use_double_quant=True,
704
  )
 
705
  tokenizer = AutoTokenizer.from_pretrained(
706
  model_id, token=os.environ["HF_TOKEN"], trust_remote_code=True
707
  )
@@ -709,6 +796,7 @@ def main(cfg: Cfg | None = None) -> None:
709
  tokenizer.pad_token = tokenizer.eos_token
710
  _TOKENIZER = tokenizer
711
 
 
712
  model = AutoModelForCausalLM.from_pretrained(
713
  model_id,
714
  quantization_config=bnb,
@@ -716,6 +804,7 @@ def main(cfg: Cfg | None = None) -> None:
716
  token=os.environ["HF_TOKEN"],
717
  trust_remote_code=True,
718
  )
 
719
  model = prepare_model_for_kbit_training(model)
720
  lora = LoraConfig(
721
  r=cfg.lora_r,
@@ -758,7 +847,8 @@ def main(cfg: Cfg | None = None) -> None:
758
  },
759
  )
760
 
761
- print("=== BASELINE AGENT EVAL (step 0) ===")
 
762
  baseline = run_heldout_agent_eval(
763
  model,
764
  tokenizer,
@@ -845,6 +935,13 @@ def main(cfg: Cfg | None = None) -> None:
845
  max_eval_turns=cfg.max_eval_turns,
846
  )
847
 
 
 
 
 
 
 
 
848
  trainer = GRPOTrainer(
849
  model=model,
850
  args=args,
@@ -855,7 +952,9 @@ def main(cfg: Cfg | None = None) -> None:
855
  )
856
  cb.trainer = trainer
857
 
 
858
  trainer.train()
 
859
 
860
  final_step = int(trainer.state.global_step)
861
  rev = f"grpo-step-{final_step}-final"
 
22
  from pathlib import Path
23
  from typing import Any, Callable
24
 
25
+
26
+ def _vlog(msg: str, **fields: Any) -> None:
27
+ extra = ""
28
+ if fields:
29
+ try:
30
+ extra = " | " + json.dumps(fields, default=str)[:1500]
31
+ except Exception:
32
+ extra = f" | {fields!r}"[:1500]
33
+ print(f"[darl] {msg}{extra}", flush=True)
34
+
35
+
36
+ def _truncate_preview(obj: Any, n: int = 400) -> str:
37
+ try:
38
+ s = obj if isinstance(obj, str) else json.dumps(obj, default=str)
39
+ except Exception:
40
+ s = repr(obj)
41
+ return s if len(s) <= n else s[:n] + "..."
42
+
43
+
44
  # ---------------------------------------------------------------------------
45
  # Bootstrap package (notebook may only download this file)
46
  # ---------------------------------------------------------------------------
47
 
48
 
49
  def _ensure_package() -> None:
50
+ _vlog("bootstrap.ensure_package.begin")
51
  try:
52
  import deepagents_rl # noqa: F401
53
 
54
+ _vlog("bootstrap.ensure_package.already_importable")
55
  return
56
  except ImportError:
57
+ _vlog("bootstrap.ensure_package.download_from_hf")
58
 
59
  token = os.environ.get("HF_TOKEN")
60
  if not token:
 
76
  sys.path.insert(0, str(pkg_parent))
77
  import deepagents_rl # noqa: F401
78
 
79
+ _vlog("bootstrap.ensure_package.done", path=str(pkg_parent))
80
+
81
 
82
  # ---------------------------------------------------------------------------
83
  # Secrets
 
85
 
86
 
87
  def load_secrets() -> None:
88
+ _vlog("secrets.load.begin")
89
  try:
90
  from kaggle_secrets import UserSecretsClient
91
 
 
147
  os.environ.setdefault("WANDB_PROJECT", "deepagents-rl-coding")
148
  os.environ.setdefault("HF_HUB_ENABLE_HF_TRANSFER", "0")
149
  os.environ.setdefault("TRL_EXPERIMENTAL_SILENCE", "1")
150
+ os.environ.setdefault("DARL_LOG_LEVEL", "DEBUG")
151
+ _vlog(
152
+ "secrets.load.done",
153
+ has_grader=bool(os.getenv("SWE_GRADER_URL")),
154
+ grader_host=(os.getenv("SWE_GRADER_URL") or "")[:60],
155
+ wandb_project=os.getenv("WANDB_PROJECT"),
156
+ )
157
 
158
 
159
  # ---------------------------------------------------------------------------
 
248
 
249
  rows = []
250
  model.eval()
251
+ _vlog("heldout_eval.begin", n_problems=len(problems), max_turns=max_turns)
252
+ for pi, p in enumerate(problems):
253
  env = DeepAgentSWEEnv()
254
+ task_id = p.get("task_id") or p.get("instance_id")
255
+ _vlog("heldout_eval.problem.begin", i=pi, task_id=task_id)
256
  try:
257
  env.reset(**{k: v for k, v in p.items() if k != "prompt"})
258
  tools = _env_tool_callables(env)
259
  tool_map = {t.__name__: t for t in tools}
260
+ _vlog("heldout_eval.tools", tools=list(tool_map.keys()))
261
  prompt = p.get("prompt")
262
  if isinstance(prompt, list):
263
  messages = list(prompt)
 
267
  final_text = ""
268
  lf = getattr(env, "_lf", None)
269
  for turn in range(max_turns):
270
+ _vlog("heldout_eval.llm_turn.begin", task_id=task_id, turn=turn)
271
  try:
272
  prompt_text = tokenizer.apply_chat_template(
273
  messages,
 
315
  completion = _generate_once()
316
  calls = _parse_tool_calls(completion)
317
 
318
+ _vlog(
319
+ "heldout_eval.llm_turn.end",
320
+ task_id=task_id,
321
+ turn=turn,
322
+ completion_preview=completion[:400],
323
+ n_tool_calls=len(calls),
324
+ tool_names=[c["name"] for c in calls],
325
+ )
326
  final_text = completion
327
  messages.append({"role": "assistant", "content": completion})
328
  if not calls:
329
+ _vlog("heldout_eval.stop_no_tools", task_id=task_id, turn=turn)
330
  break
331
  for call in calls:
332
+ _vlog(
333
+ "heldout_eval.tool_dispatch",
334
+ task_id=task_id,
335
+ tool=call["name"],
336
+ arguments=_truncate_preview(call.get("arguments")),
337
+ )
338
  fn = tool_map.get(call["name"])
339
  if fn is None:
340
  result = f"Unknown tool: {call['name']}"
 
358
 
359
  reward = float(env.get_reward())
360
  report = getattr(env, "_last_report", {}) or {}
361
+ row = {
362
+ "task_id": task_id,
363
+ "prompt": (
364
+ prompt
365
+ if isinstance(prompt, str)
366
+ else json.dumps(prompt)[:2000]
367
+ ),
368
+ "completion": final_text[:4000],
369
+ "pass_frac": float(report.get("pass_frac") or reward),
370
+ "reward_total": reward,
371
+ "resolved": bool(report.get("resolved")),
372
+ "resolution": report.get("resolution"),
373
+ "n_tool_calls": len(getattr(env, "_tool_calls", []) or []),
374
+ "tools_used": [
375
+ c.get("name") for c in (getattr(env, "_tool_calls", []) or [])
376
+ ],
377
+ "langfuse_trace_id": getattr(env, "_trace_id", "") or "",
378
+ }
379
+ rows.append(row)
380
+ _vlog("heldout_eval.problem.done", **{k: row[k] for k in (
381
+ "task_id", "reward_total", "pass_frac", "resolved", "n_tool_calls", "tools_used"
382
+ )})
383
  except Exception as e: # noqa: BLE001
384
  traceback.print_exc()
385
+ _vlog("heldout_eval.problem.error", task_id=task_id, error=str(e))
386
  rows.append(
387
  {
388
+ "task_id": task_id,
389
  "prompt": str(p.get("prompt"))[:2000],
390
  "completion": f"EVAL_ERROR: {e}",
391
  "pass_frac": 0.0,
 
394
  "resolution": "ERROR",
395
  }
396
  )
397
+ _vlog("heldout_eval.done", n=len(rows))
398
  finally:
399
  try:
400
  env._end_trace()
 
543
 
544
  rev = f"grpo-step-{step}"
545
  problems = eval_problems[:n_eval]
546
+ _vlog("ckpt_eval.begin", step=step, rev=rev, n=len(problems))
547
+ print(
548
+ f"\n=== AGENT EVAL BEFORE CHECKPOINT step={step} rev={rev} n={len(problems)} ===",
549
+ flush=True,
550
+ )
551
  try:
552
  results = run_heldout_agent_eval(
553
  model, tokenizer, problems, max_turns=max_eval_turns
554
  )
555
+ _vlog(
556
+ "ckpt_eval.rollouts_done",
557
+ step=step,
558
+ mean_reward=sum(float(r["reward_total"]) for r in results)
559
+ / max(len(results), 1),
560
+ )
561
  import wandb
562
 
563
  wandb_run_id = None
 
733
  def main(cfg: Cfg | None = None) -> None:
734
  global _TOKENIZER
735
  cfg = cfg or Cfg()
736
+ _vlog("main.begin", cfg_dataset=getattr(cfg, "dataset_id", None))
737
  load_secrets()
738
  _ensure_package()
739
 
740
+ try:
741
+ from deepagents_rl.observability.logging import configure_logging
742
+
743
+ out_log = Path("/kaggle/working/grpo-out")
744
+ out_log.mkdir(parents=True, exist_ok=True)
745
+ configure_logging(
746
+ level=os.getenv("DARL_LOG_LEVEL", "DEBUG"),
747
+ log_file=out_log / "run.log",
748
+ events_jsonl=out_log / "events.jsonl",
749
+ force=True,
750
+ )
751
+ _vlog("main.logging_configured", log_file=str(out_log / "run.log"))
752
+ except Exception as e: # noqa: BLE001
753
+ _vlog("main.logging_configure_failed", error=str(e))
754
+
755
  import torch
756
  import wandb
757
  from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
 
767
  headers = {"ngrok-skip-browser-warning": "1"}
768
  if os.environ.get("SWE_GRADER_TOKEN"):
769
  headers["Authorization"] = f"Bearer {os.environ['SWE_GRADER_TOKEN']}"
770
+ _vlog("main.grader_health_check", url=grader)
771
  health = requests.get(f"{grader}/health", headers=headers, timeout=30)
772
  health.raise_for_status()
773
+ _vlog("main.grader_health_ok", body=health.json())
774
 
775
  os.environ["WANDB_PROJECT"] = os.getenv("WANDB_PROJECT", "deepagents-rl-coding")
776
+ _vlog("main.wandb_login")
777
  wandb.login(key=os.environ["WANDB_API_KEY"])
778
 
779
+ _vlog("main.resolve_model")
780
  model_id = resolve_model()
781
+ _vlog("main.load_dataset")
782
  train, eval_problems = _load_swe_splits(cfg)
783
+ _vlog("main.dataset_ready", train_n=len(train), eval_n=len(eval_problems))
784
 
785
  bnb = BitsAndBytesConfig(
786
  load_in_4bit=True,
 
788
  bnb_4bit_quant_type="nf4",
789
  bnb_4bit_use_double_quant=True,
790
  )
791
+ _vlog("main.load_tokenizer", model_id=model_id)
792
  tokenizer = AutoTokenizer.from_pretrained(
793
  model_id, token=os.environ["HF_TOKEN"], trust_remote_code=True
794
  )
 
796
  tokenizer.pad_token = tokenizer.eos_token
797
  _TOKENIZER = tokenizer
798
 
799
+ _vlog("main.load_model_4bit", model_id=model_id, cuda=torch.cuda.is_available())
800
  model = AutoModelForCausalLM.from_pretrained(
801
  model_id,
802
  quantization_config=bnb,
 
804
  token=os.environ["HF_TOKEN"],
805
  trust_remote_code=True,
806
  )
807
+ _vlog("main.prepare_lora")
808
  model = prepare_model_for_kbit_training(model)
809
  lora = LoraConfig(
810
  r=cfg.lora_r,
 
847
  },
848
  )
849
 
850
+ _vlog("main.baseline_eval.begin", n_eval=cfg.n_eval)
851
+ print("=== BASELINE AGENT EVAL (step 0) ===", flush=True)
852
  baseline = run_heldout_agent_eval(
853
  model,
854
  tokenizer,
 
935
  max_eval_turns=cfg.max_eval_turns,
936
  )
937
 
938
+ _vlog(
939
+ "main.trainer_init",
940
+ environment="DeepAgentSWEEnv",
941
+ max_steps=cfg.max_steps,
942
+ num_generations=cfg.num_generations,
943
+ max_tool_calling_iterations=cfg.max_tool_calling_iterations,
944
+ )
945
  trainer = GRPOTrainer(
946
  model=model,
947
  args=args,
 
952
  )
953
  cb.trainer = trainer
954
 
955
+ _vlog("main.trainer.train.begin")
956
  trainer.train()
957
+ _vlog("main.trainer.train.done", global_step=int(trainer.state.global_step))
958
 
959
  final_step = int(trainer.state.global_step)
960
  rev = f"grpo-step-{final_step}-final"