ironbcc commited on
Commit
8bb25a9
·
verified ·
1 Parent(s): 085f380

add serve/kev_convert.py

Browse files
Files changed (1) hide show
  1. serve/kev_convert.py +84 -0
serve/kev_convert.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Kev / TypeSafe System One records -> candidate-v2 rows. Exact inverse of final/jev_fresh.request_for: converting a Kev
2
+ record here and sending the row through request_for reproduces the record's state, instructions and criteria meanings.
3
+ Kev record: {"state": ..., "questions": {qid: {"type", "instructions", "criteria", "label", "src", "target_distribution"?}}, "_meta": {...}}.
4
+ Choice criteria may be a dict {name: description|None} or a list of names; choice questions with more than 20 options are
5
+ capped to 20 (gold kept, others drawn deterministically from the record id) because candidate-v2 has 20 keys. Serving passes
6
+ keys=WIDE_CHOICE_KEYS, so a request is never capped below its own option count."""
7
+ import hashlib
8
+ import json
9
+ import random
10
+
11
+ from system1_client import CHOICE_KEYS, validate_row
12
+
13
+ NOUL_DEFAULT = {"true": "the answer is yes", "false": "the answer is no"}
14
+
15
+
16
+ def _sha(text):
17
+ return hashlib.sha256(text.encode("utf-8")).hexdigest()
18
+
19
+
20
+ def _text(value):
21
+ return value if isinstance(value, str) else json.dumps(value, ensure_ascii=False)
22
+
23
+
24
+ def _criteria_items(criteria):
25
+ if criteria is None:
26
+ return []
27
+ if isinstance(criteria, dict):
28
+ return [(k, v) for k, v in criteria.items()]
29
+ return [(c, None) for c in criteria]
30
+
31
+
32
+ def kev_record_to_rows(record, source, source_revision, split, keys=CHOICE_KEYS):
33
+ meta = record.get("_meta") or {}
34
+ state = record["state"]
35
+ rid = meta.get("id") or _sha(_text(state))[:16]
36
+ gid = meta.get("group_id") or rid
37
+ rows = []
38
+ for qid, q in record["questions"].items():
39
+ kind = q["type"]
40
+ question = _text(q.get("instructions") or "")
41
+ row_meta = {"rubric_family": f"kev:{source}:{qid}", "novelty": "seen", "domain": source, "label_origin": "kev_public",
42
+ "kev_src": q.get("src"), "kev_question_id": qid}
43
+ soft = q.get("target_distribution")
44
+ if kind == "noul":
45
+ crit = dict(_criteria_items(q.get("criteria")))
46
+ crit = {k: v for k, v in crit.items() if isinstance(v, str) and v}
47
+ options = {"Y": f"true: {crit.get('true', NOUL_DEFAULT['true'])}", "N": f"false: {crit.get('false', NOUL_DEFAULT['false'])}"}
48
+ label = q["label"]
49
+ target = "Y" if (label is True or label in ("true", "yes", 1)) else "N"
50
+ dist = {"Y": float(soft.get("true", 0.0)), "N": float(soft.get("false", 0.0))} if isinstance(soft, dict) else None
51
+ elif kind == "choice":
52
+ items = _criteria_items(q["criteria"])
53
+ names = [n for n, _ in items]
54
+ gold = q["label"]
55
+ if gold not in names:
56
+ raise ValueError(f"{rid}/{qid}: label {gold!r} not among criteria")
57
+ if len(items) > len(keys):
58
+ rng = random.Random(_sha(f"{rid}:{qid}:cap"))
59
+ others = [it for it in items if it[0] != gold]
60
+ rng.shuffle(others)
61
+ keep = {gold} | {n for n, _ in others[: len(keys) - 1]}
62
+ items = [it for it in items if it[0] in keep] # original relative order, gold kept
63
+ row_meta["capped_from"] = len(names)
64
+ ks = keys[: len(items)]
65
+ options = {k: (f"{n}: {_text(d)}" if d not in (None, "") else n) for k, (n, d) in zip(ks, items)}
66
+ row_meta["candidate_ids"] = {k: n for k, (n, _) in zip(ks, items)}
67
+ target = ks[[n for n, _ in items].index(gold)]
68
+ dist = {k: float(soft.get(n, 0.0)) for k, (n, _) in zip(ks, items)} if isinstance(soft, dict) else None
69
+ elif kind == "score":
70
+ levels = [_text(c) for c in q["criteria"]]
71
+ options = {str(i): f"{i}: {lvl}" for i, lvl in enumerate(levels)}
72
+ target = str(int(q["label"]))
73
+ dist = {str(i): float(soft.get(str(i), soft.get(i, 0.0))) for i in range(len(levels))} if isinstance(soft, dict) else None
74
+ else:
75
+ raise ValueError(f"unknown question type {kind}")
76
+ row = {"id": f"kev:{source}:{rid}:{qid}", "group_id": f"kev:{source}:{gid}", "source": f"kev_{source}",
77
+ "source_revision": meta.get("revision") or source_revision, "source_split": meta.get("split") or split, "split": split,
78
+ "task": "general", "type": kind, "state": state, "question": question, "options": options, "target": target, "meta": row_meta}
79
+ if dist and abs(sum(dist.values()) - 1) < 1e-6:
80
+ row["target_distribution"] = dist
81
+ row["target"] = max(dist, key=dist.get)
82
+ validate_row(row)
83
+ rows.append(row)
84
+ return rows