vanishingradient commited on
Commit
e1bfc06
·
verified ·
1 Parent(s): 47f9048

Smoke mode + detailed train entry

Browse files
Files changed (1) hide show
  1. kaggle/train_grpo.py +275 -1
kaggle/train_grpo.py CHANGED
@@ -775,6 +775,7 @@ class Cfg:
775
  max_eval_turns: int = 8,
776
  max_train_instances: int = 16,
777
  lora_r: int = 16,
 
778
  ) -> None:
779
  self.dataset_id = dataset_id
780
  self.hub_model_id = hub_model_id
@@ -791,6 +792,8 @@ class Cfg:
791
  self.max_eval_turns = max_eval_turns
792
  self.max_train_instances = max_train_instances
793
  self.lora_r = lora_r
 
 
794
 
795
 
796
  def _load_swe_splits(cfg: Cfg):
@@ -829,10 +832,275 @@ def _load_swe_splits(cfg: Cfg):
829
  return Dataset.from_list(train_rows), eval_rows
830
 
831
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
832
  def main(cfg: Cfg | None = None) -> None:
833
  global _TOKENIZER
834
  cfg = cfg or Cfg()
835
- _vlog("main.begin", cfg_dataset=getattr(cfg, "dataset_id", None))
 
 
 
 
836
  load_secrets()
837
  _ensure_package()
838
 
@@ -851,6 +1119,12 @@ def main(cfg: Cfg | None = None) -> None:
851
  except Exception as e: # noqa: BLE001
852
  _vlog("main.logging_configure_failed", error=str(e))
853
 
 
 
 
 
 
 
854
  import torch
855
  import wandb
856
  from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
 
775
  max_eval_turns: int = 8,
776
  max_train_instances: int = 16,
777
  lora_r: int = 16,
778
+ mode: str | None = None,
779
  ) -> None:
780
  self.dataset_id = dataset_id
781
  self.hub_model_id = hub_model_id
 
792
  self.max_eval_turns = max_eval_turns
793
  self.max_train_instances = max_train_instances
794
  self.lora_r = lora_r
795
+ # mode: "smoke" = 1 instance + reward checks then exit; "full" = GRPO train
796
+ self.mode = (mode or os.getenv("DARL_MODE") or "full").strip().lower()
797
 
798
 
799
  def _load_swe_splits(cfg: Cfg):
 
832
  return Dataset.from_list(train_rows), eval_rows
833
 
834
 
835
+ def run_smoke_one_case(cfg: Cfg) -> dict[str, Any]:
836
+ """One SWE instance end-to-end: env tools + empty-patch reward + 1 agent rollout.
837
+
838
+ Hard-fails if grader/env/reward plumbing is broken. Soft-warns if the small
839
+ model makes zero tool calls or scores 0 (common before training).
840
+ """
841
+ import torch
842
+ import wandb
843
+ from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
844
+ from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
845
+
846
+ from deepagents_rl.train.swe_env import DeepAgentSWEEnv
847
+
848
+ out_dir = Path("/kaggle/working/grpo-out")
849
+ out_dir.mkdir(parents=True, exist_ok=True)
850
+
851
+ # Force single-case data
852
+ cfg.max_train_instances = 1
853
+ cfg.n_eval = 1
854
+ cfg.max_eval_turns = max(cfg.max_eval_turns, 10)
855
+ train, eval_problems = _load_swe_splits(cfg)
856
+ if eval_problems:
857
+ problem = dict(eval_problems[0])
858
+ else:
859
+ problem = dict(train[0])
860
+ instance_id = problem.get("instance_id") or problem.get("task_id")
861
+ _vlog("smoke.instance", instance_id=instance_id, keys=list(problem.keys())[:20])
862
+
863
+ checks: dict[str, Any] = {
864
+ "instance_id": instance_id,
865
+ "grader_health": False,
866
+ "env_reset": False,
867
+ "ls_root_ok": False,
868
+ "ls_src_aliased": False,
869
+ "empty_patch_reward_ok": False,
870
+ "empty_patch_reward": None,
871
+ "empty_patch_report": {},
872
+ "agent_reward": None,
873
+ "agent_n_tool_calls": 0,
874
+ "agent_tools_used": [],
875
+ "agent_resolved": False,
876
+ "langfuse_trace_id": "",
877
+ "hard_ok": False,
878
+ "soft_ok": False,
879
+ "errors": [],
880
+ }
881
+
882
+ import requests
883
+
884
+ grader = os.environ["SWE_GRADER_URL"].rstrip("/")
885
+ headers = {"ngrok-skip-browser-warning": "1"}
886
+ if os.environ.get("SWE_GRADER_TOKEN"):
887
+ headers["Authorization"] = f"Bearer {os.environ['SWE_GRADER_TOKEN']}"
888
+ health = requests.get(f"{grader}/health", headers=headers, timeout=30)
889
+ health.raise_for_status()
890
+ checks["grader_health"] = True
891
+ _vlog("smoke.grader_health", body=health.json())
892
+
893
+ os.environ["WANDB_PROJECT"] = os.getenv("WANDB_PROJECT", "deepagents-rl-coding")
894
+ wandb.login(key=os.environ["WANDB_API_KEY"])
895
+ wandb.init(
896
+ project=os.environ["WANDB_PROJECT"],
897
+ name="kaggle-grpo-swe-smoke",
898
+ job_type="smoke",
899
+ config={"mode": "smoke", "instance_id": instance_id},
900
+ )
901
+
902
+ # --- Phase 1: deterministic env + empty-patch grader ---
903
+ env = DeepAgentSWEEnv()
904
+ try:
905
+ reset_kwargs = {
906
+ k: v
907
+ for k, v in problem.items()
908
+ if k
909
+ not in (
910
+ "prompt",
911
+ "problem_statement",
912
+ )
913
+ }
914
+ # Ensure docker_image key for reset
915
+ if "docker_image" not in reset_kwargs and problem.get("docker_image"):
916
+ reset_kwargs["docker_image"] = problem["docker_image"]
917
+ tip = env.reset(**reset_kwargs)
918
+ checks["env_reset"] = True
919
+ _vlog("smoke.env.reset", tip_preview=str(tip)[:200])
920
+
921
+ ls_root = env.ls(".")
922
+ checks["ls_root_ok"] = bool(ls_root) and "path_not_found" not in ls_root.lower()
923
+ _vlog("smoke.ls_root", preview=str(ls_root)[:500], ok=checks["ls_root_ok"])
924
+
925
+ ls_src = env.ls("src")
926
+ # Must NOT be bare ERROR path_not_found — alias/fallback should list root
927
+ checks["ls_src_aliased"] = (
928
+ "path_not_found" not in ls_src.lower()
929
+ or "repo root" in ls_src.lower()
930
+ or "showing repo root" in ls_src.lower()
931
+ or "listing repo root" in ls_src.lower()
932
+ or "not used in this repo" in ls_src.lower()
933
+ )
934
+ _vlog("smoke.ls_src", preview=str(ls_src)[:500], ok=checks["ls_src_aliased"])
935
+
936
+ empty_reward = float(env.get_reward())
937
+ empty_report = getattr(env, "_last_report", {}) or {}
938
+ checks["empty_patch_reward"] = empty_reward
939
+ checks["empty_patch_report"] = {
940
+ k: empty_report.get(k)
941
+ for k in (
942
+ "pass_frac",
943
+ "resolved",
944
+ "resolution",
945
+ "error",
946
+ "reward_total",
947
+ )
948
+ }
949
+ # Hard OK if grader returned a numeric reward without transport failure
950
+ err = str(empty_report.get("error") or "")
951
+ transport_fail = any(
952
+ x in err.lower()
953
+ for x in ("connection", "timeout", "refused", "ngrok", "502", "503", "404")
954
+ )
955
+ checks["empty_patch_reward_ok"] = (not transport_fail) and isinstance(
956
+ empty_reward, float
957
+ )
958
+ checks["langfuse_trace_id"] = getattr(env, "_trace_id", "") or ""
959
+ _vlog(
960
+ "smoke.empty_patch_reward",
961
+ reward=empty_reward,
962
+ report=checks["empty_patch_report"],
963
+ ok=checks["empty_patch_reward_ok"],
964
+ )
965
+ except Exception as e: # noqa: BLE001
966
+ checks["errors"].append(f"env_phase: {type(e).__name__}: {e}")
967
+ _vlog("smoke.env_phase.error", error=str(e))
968
+ traceback.print_exc()
969
+ finally:
970
+ try:
971
+ env._end_trace()
972
+ except Exception:
973
+ pass
974
+ try:
975
+ env._cleanup()
976
+ except Exception:
977
+ pass
978
+
979
+ # --- Phase 2: one agent rollout (model may score 0; still validates loop) ---
980
+ model_id = resolve_model()
981
+ major, minor = (0, 0)
982
+ if torch.cuda.is_available():
983
+ major, minor = torch.cuda.get_device_capability(0)
984
+ use_bf16 = major >= 8 and torch.cuda.is_bf16_supported()
985
+ compute_dtype = torch.bfloat16 if use_bf16 else torch.float16
986
+ bnb = BitsAndBytesConfig(
987
+ load_in_4bit=True,
988
+ bnb_4bit_compute_dtype=compute_dtype,
989
+ bnb_4bit_quant_type="nf4",
990
+ bnb_4bit_use_double_quant=True,
991
+ )
992
+ tokenizer = AutoTokenizer.from_pretrained(
993
+ model_id, token=os.environ["HF_TOKEN"], trust_remote_code=True
994
+ )
995
+ if tokenizer.pad_token is None:
996
+ tokenizer.pad_token = tokenizer.eos_token
997
+ model = AutoModelForCausalLM.from_pretrained(
998
+ model_id,
999
+ quantization_config=bnb,
1000
+ device_map="auto",
1001
+ token=os.environ["HF_TOKEN"],
1002
+ trust_remote_code=True,
1003
+ )
1004
+ model = prepare_model_for_kbit_training(model)
1005
+ model = get_peft_model(
1006
+ model,
1007
+ LoraConfig(
1008
+ r=cfg.lora_r,
1009
+ lora_alpha=cfg.lora_r * 2,
1010
+ lora_dropout=0.0,
1011
+ bias="none",
1012
+ task_type="CAUSAL_LM",
1013
+ target_modules=[
1014
+ "q_proj",
1015
+ "k_proj",
1016
+ "v_proj",
1017
+ "o_proj",
1018
+ "gate_proj",
1019
+ "up_proj",
1020
+ "down_proj",
1021
+ ],
1022
+ ),
1023
+ )
1024
+
1025
+ agent_rows = run_heldout_agent_eval(
1026
+ model,
1027
+ tokenizer,
1028
+ [problem],
1029
+ max_turns=cfg.max_eval_turns,
1030
+ max_new_tokens=512,
1031
+ )
1032
+ row = agent_rows[0] if agent_rows else {}
1033
+ checks["agent_reward"] = float(row.get("reward_total") or 0.0)
1034
+ checks["agent_n_tool_calls"] = int(row.get("n_tool_calls") or 0)
1035
+ checks["agent_tools_used"] = list(row.get("tools_used") or [])
1036
+ checks["agent_resolved"] = bool(row.get("resolved"))
1037
+ if row.get("langfuse_trace_id"):
1038
+ checks["langfuse_trace_id"] = row["langfuse_trace_id"]
1039
+ checks["agent_completion_preview"] = str(row.get("completion") or "")[:800]
1040
+ _vlog(
1041
+ "smoke.agent_done",
1042
+ reward=checks["agent_reward"],
1043
+ n_tool_calls=checks["agent_n_tool_calls"],
1044
+ tools=checks["agent_tools_used"],
1045
+ resolved=checks["agent_resolved"],
1046
+ )
1047
+
1048
+ checks["hard_ok"] = all(
1049
+ [
1050
+ checks["grader_health"],
1051
+ checks["env_reset"],
1052
+ checks["ls_root_ok"],
1053
+ checks["ls_src_aliased"],
1054
+ checks["empty_patch_reward_ok"],
1055
+ not checks["errors"],
1056
+ ]
1057
+ )
1058
+ checks["soft_ok"] = checks["agent_n_tool_calls"] > 0
1059
+
1060
+ report_path = out_dir / "smoke_report.json"
1061
+ report_path.write_text(json.dumps(checks, indent=2, default=str), encoding="utf-8")
1062
+ _vlog("smoke.report_written", path=str(report_path), hard_ok=checks["hard_ok"])
1063
+
1064
+ wandb.log(
1065
+ {
1066
+ "smoke/hard_ok": 1.0 if checks["hard_ok"] else 0.0,
1067
+ "smoke/soft_ok": 1.0 if checks["soft_ok"] else 0.0,
1068
+ "smoke/empty_patch_reward": float(checks["empty_patch_reward"] or 0.0),
1069
+ "smoke/agent_reward": float(checks["agent_reward"] or 0.0),
1070
+ "smoke/agent_n_tool_calls": float(checks["agent_n_tool_calls"]),
1071
+ "smoke/instance_id": str(instance_id or ""),
1072
+ "smoke/ls_root_ok": 1.0 if checks["ls_root_ok"] else 0.0,
1073
+ "smoke/ls_src_aliased": 1.0 if checks["ls_src_aliased"] else 0.0,
1074
+ }
1075
+ )
1076
+ wandb.finish()
1077
+
1078
+ print("=== SMOKE REPORT ===", flush=True)
1079
+ print(json.dumps(checks, indent=2, default=str), flush=True)
1080
+ if not checks["hard_ok"]:
1081
+ raise RuntimeError(
1082
+ "SMOKE HARD FAIL — grader/env/reward plumbing broken. "
1083
+ f"errors={checks['errors']} report={report_path}"
1084
+ )
1085
+ if not checks["soft_ok"]:
1086
+ print(
1087
+ "SMOKE SOFT WARN: agent made 0 tool calls (prompt/model issue). "
1088
+ "Plumbing OK — full GRPO may still train tool use.",
1089
+ flush=True,
1090
+ )
1091
+ else:
1092
+ print("SMOKE PASS (hard+soft). Safe to run full GRPO.", flush=True)
1093
+ return checks
1094
+
1095
+
1096
  def main(cfg: Cfg | None = None) -> None:
1097
  global _TOKENIZER
1098
  cfg = cfg or Cfg()
1099
+ _vlog(
1100
+ "main.begin",
1101
+ cfg_dataset=getattr(cfg, "dataset_id", None),
1102
+ mode=getattr(cfg, "mode", "full"),
1103
+ )
1104
  load_secrets()
1105
  _ensure_package()
1106
 
 
1119
  except Exception as e: # noqa: BLE001
1120
  _vlog("main.logging_configure_failed", error=str(e))
1121
 
1122
+ if str(getattr(cfg, "mode", "full")).lower() == "smoke":
1123
+ _vlog("main.branch_smoke")
1124
+ run_smoke_one_case(cfg)
1125
+ _vlog("main.smoke_done_exit")
1126
+ return
1127
+
1128
  import torch
1129
  import wandb
1130
  from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training