Falln87 commited on
Commit
c498706
·
verified ·
1 Parent(s): 865342a

CLERK SFT dataset builder: timelines -> messages JSONL (CONSOLIDATE + ANSWER mixture), seeded shuffle

Browse files
Files changed (1) hide show
  1. clerk/build_sft.py +107 -0
clerk/build_sft.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Build the CLERK SFT dataset from generated persona timelines.
2
+
3
+ Reads the JSONL timelines produced by ``clerk.generator`` and emits a
4
+ seeded, shuffled mixture of two example types in TRL ``messages`` format:
5
+
6
+ CONSOLIDATE : ledger@t + session transcript -> gold edit program
7
+ ANSWER : ledger@t + question -> gold answer
8
+
9
+ Supervision is exact by construction (the generator knows the gold ledger
10
+ transitions); there is no model- or LLM-judged label anywhere.
11
+
12
+ Usage:
13
+ python -m clerk.build_sft --train clerk_train.jsonl --val clerk_val.jsonl \
14
+ --out-train data/sft_train.jsonl --out-val data/sft_val.jsonl
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import argparse
20
+ import json
21
+ import random
22
+ from typing import Any, Dict, List
23
+
24
+ from clerk.common import parse_ledger, ledger_stats
25
+ from clerk.prompts import (
26
+ ANSWER_SYSTEM,
27
+ ANSWER_USER_TMPL,
28
+ CONSOLIDATE_SYSTEM,
29
+ CONSOLIDATE_USER_TMPL,
30
+ render_transcript,
31
+ )
32
+
33
+
34
+ def persona_examples(persona: Dict[str, Any]) -> List[Dict[str, Any]]:
35
+ out: List[Dict[str, Any]] = []
36
+ for sess in persona["sessions"]:
37
+ ledger = parse_ledger(sess["ledger_before"])
38
+ stats = ledger_stats(ledger)
39
+ idx = sess["session_idx"]
40
+
41
+ # --- CONSOLIDATE example (all sessions, including the intro)
42
+ out.append({
43
+ "task": "consolidate",
44
+ "persona": persona["name"],
45
+ "session_idx": idx,
46
+ "messages": [
47
+ {"role": "system", "content": CONSOLIDATE_SYSTEM},
48
+ {"role": "user", "content": CONSOLIDATE_USER_TMPL.format(
49
+ ledger=sess["ledger_before"], idx=idx,
50
+ transcript=render_transcript(sess["turns"]))},
51
+ {"role": "assistant", "content": json.dumps(
52
+ {"ops": sess["gold_ops"]}, separators=(",", ":"),
53
+ ensure_ascii=False)},
54
+ ],
55
+ "meta": stats,
56
+ })
57
+
58
+ # --- ANSWER examples (from session 1 onward)
59
+ for qa in sess["qas"]:
60
+ out.append({
61
+ "task": "answer",
62
+ "persona": persona["name"],
63
+ "session_idx": idx,
64
+ "qtype": qa["type"],
65
+ "messages": [
66
+ {"role": "system", "content": ANSWER_SYSTEM},
67
+ {"role": "user", "content": ANSWER_USER_TMPL.format(
68
+ ledger=sess["ledger_before"], question=qa["q"])},
69
+ {"role": "assistant", "content": qa["a"]},
70
+ ],
71
+ "meta": stats,
72
+ })
73
+ return out
74
+
75
+
76
+ def main() -> None:
77
+ ap = argparse.ArgumentParser(description="CLERK SFT dataset builder")
78
+ ap.add_argument("--train", type=str, required=True,
79
+ help="timelines JSONL (train personas)")
80
+ ap.add_argument("--val", type=str, default=None,
81
+ help="timelines JSONL (validation personas)")
82
+ ap.add_argument("--out-train", type=str, required=True)
83
+ ap.add_argument("--out-val", type=str, default=None)
84
+ ap.add_argument("--seed", type=int, default=137)
85
+ args = ap.parse_args()
86
+
87
+ for src, dst in [(args.train, args.out_train)] + (
88
+ [(args.val, args.out_val)] if args.val else []
89
+ ):
90
+ examples: List[Dict[str, Any]] = []
91
+ with open(src) as f:
92
+ for line in f:
93
+ examples.extend(persona_examples(json.loads(line)))
94
+ rng = random.Random(args.seed)
95
+ rng.shuffle(examples)
96
+ with open(dst := (args.out_train if src == args.train else args.out_val),
97
+ "w") as f:
98
+ for ex in examples:
99
+ f.write(json.dumps(ex, ensure_ascii=False) + "\n")
100
+ n_c = sum(1 for e in examples if e["task"] == "consolidate")
101
+ n_a = sum(1 for e in examples if e["task"] == "answer")
102
+ print(f"{dst}: {len(examples)} examples "
103
+ f"({n_c} consolidate, {n_a} answer)")
104
+
105
+
106
+ if __name__ == "__main__":
107
+ main()