Taimwe commited on
Commit
51a4d63
·
verified ·
1 Parent(s): 6e7526a

Fix: repair JSON-escaped newlines in training data (v1/v2 scored 0% ast_rate vs base 93%)

Browse files
Files changed (1) hide show
  1. train_securecoder.py +30 -3
train_securecoder.py CHANGED
@@ -582,6 +582,29 @@ def render_record(tokenizer, messages: list[dict], tools: list[dict] | None = No
582
 
583
 
584
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
585
  def build_dataset(tokenizer, sources: list[Source], token: str | None, validate: bool):
586
  """Returns (records, stats). ``records`` are {"text", "source"} dicts."""
587
  records: list[dict] = []
@@ -589,7 +612,7 @@ def build_dataset(tokenizer, sources: list[Source], token: str | None, validate:
589
 
590
  for src in sources:
591
  entry = {"source": source_name(src), "note": src.note, "kept": 0, "skipped": 0,
592
- "tool_samples": 0, "chars": 0, "error": None}
593
  try:
594
  rows = load_source(src, token, progress=validate)
595
  for row in rows:
@@ -603,6 +626,7 @@ def build_dataset(tokenizer, sources: list[Source], token: str | None, validate:
603
  continue
604
  try:
605
  text = render_record(tokenizer, messages, tools)
 
606
  except Exception as exc: # noqa: BLE001 - bad template input, skip row
607
  if entry["skipped"] < 3:
608
  log.warning(" render failed (%s): %s", source_name(src), exc)
@@ -614,6 +638,8 @@ def build_dataset(tokenizer, sources: list[Source], token: str | None, validate:
614
  records.append({"text": text, "source": source_name(src)})
615
  entry["kept"] += 1
616
  entry["chars"] += len(text)
 
 
617
  if tools:
618
  entry["tool_samples"] += 1
619
  except Exception as exc: # noqa: BLE001 - one bad dataset must not kill the run
@@ -621,8 +647,9 @@ def build_dataset(tokenizer, sources: list[Source], token: str | None, validate:
621
  log.error(" %s failed: %s", source_name(src), exc)
622
 
623
  stats.append(entry)
624
- log.info(" %-58s kept=%-6d skipped=%-5d tools=%-5d",
625
- entry["source"], entry["kept"], entry["skipped"], entry["tool_samples"])
 
626
 
627
  random.shuffle(records)
628
  return records, stats
 
582
 
583
 
584
 
585
+ def _unescape_if_needed(text: str) -> tuple[str, bool]:
586
+ """Repair JSON-escaped newlines before training.
587
+
588
+ Parts of the data mix store message text already JSON-escaped, so the rendered
589
+ training text carries the two-character sequence backslash+n instead of a real
590
+ newline. Fine-tuning on that teaches the model to emit a literal "\\n", which is
591
+ why both v1 and v2 scored 0% ast_rate while the untouched base model scored 93%.
592
+
593
+ Only fires when escaped newlines clearly dominate, so ordinary code samples that
594
+ legitimately contain a backslash are left untouched.
595
+ """
596
+ literal = text.count("\\n")
597
+ if literal == 0:
598
+ return text, False
599
+ if text.count("\n") > literal:
600
+ return text, False
601
+ fixed = (text.replace("\\r\\n", "\n")
602
+ .replace("\\n", "\n")
603
+ .replace("\\t", "\t")
604
+ .replace('\\"', '"'))
605
+ return fixed, True
606
+
607
+
608
  def build_dataset(tokenizer, sources: list[Source], token: str | None, validate: bool):
609
  """Returns (records, stats). ``records`` are {"text", "source"} dicts."""
610
  records: list[dict] = []
 
612
 
613
  for src in sources:
614
  entry = {"source": source_name(src), "note": src.note, "kept": 0, "skipped": 0,
615
+ "tool_samples": 0, "chars": 0, "repaired": 0, "error": None}
616
  try:
617
  rows = load_source(src, token, progress=validate)
618
  for row in rows:
 
626
  continue
627
  try:
628
  text = render_record(tokenizer, messages, tools)
629
+ text, repaired = _unescape_if_needed(text)
630
  except Exception as exc: # noqa: BLE001 - bad template input, skip row
631
  if entry["skipped"] < 3:
632
  log.warning(" render failed (%s): %s", source_name(src), exc)
 
638
  records.append({"text": text, "source": source_name(src)})
639
  entry["kept"] += 1
640
  entry["chars"] += len(text)
641
+ if repaired:
642
+ entry["repaired"] += 1
643
  if tools:
644
  entry["tool_samples"] += 1
645
  except Exception as exc: # noqa: BLE001 - one bad dataset must not kill the run
 
647
  log.error(" %s failed: %s", source_name(src), exc)
648
 
649
  stats.append(entry)
650
+ log.info(" %-58s kept=%-6d skipped=%-5d tools=%-5d repaired=%-5d",
651
+ entry["source"], entry["kept"], entry["skipped"], entry["tool_samples"],
652
+ entry.get("repaired", 0))
653
 
654
  random.shuffle(records)
655
  return records, stats