Code / benchmarks/promptsets/make_promptsets.py
benchmarks/promptsets/make_promptsets.py
155 lines
#!/usr/bin/env python3
# =============================================================================
# Project : modelmap
# File : benchmarks/promptsets/make_promptsets.py
# Purpose : Generate versioned, checksummed probing corpora (v1, template)
# Author : Simon-Pierre Boucher
# Contact : contact@spboucher.ai
# Website : https://modelmap.io
# Created : 2026-08-12
# Modified : 2026-08-12
# Platform : macOS / Apple Silicon (arm64)
# License : All rights reserved (research code)
# =============================================================================
"""Probing corpora v1 (charter §0.3: versioned artifacts with checksums).
Three binary properties, TWO deliberately different template families per
property (set A / set B) so that dataset sensitivity is measurable:
lang_id : French vs English sentences
code_prose : code snippets vs English prose
arith : arithmetic-context vs non-arithmetic sentences
Deterministic given SEED. v1 limitation, declared: template-generated text,
not natural corpora — lexical diversity is bounded; natural-corpus v2 is a
registered follow-up. Output: <name>.jsonl + manifest.json with sha256.
"""
from __future__ import annotations
import hashlib
import itertools
import json
import random
from pathlib import Path
SEED = 12345
N_PER_CLASS = 120
VERSION = "v1"
OUT = Path(__file__).resolve().parent
TOPICS_EN = ["the harbor", "the library", "the orchard", "the workshop", "the observatory",
"the market", "the glacier", "the archive", "the vineyard", "the lighthouse"]
TOPICS_FR = ["le port", "la bibliothèque", "le verger", "l'atelier", "l'observatoire",
"le marché", "le glacier", "les archives", "le vignoble", "le phare"]
ADJ_EN = ["quiet", "ancient", "crowded", "restored", "abandoned", "famous", "modest", "vast"]
ADJ_FR = ["calme", "ancien", "bondé", "restauré", "abandonné", "célèbre", "modeste", "vaste"]
VERB_EN = ["remained open despite the storm", "attracted visitors from the region",
"was documented in the survey", "changed hands twice last century",
"stood at the edge of town", "required constant maintenance"]
VERB_FR = ["est resté ouvert malgré la tempête", "a attiré des visiteurs de la région",
"a été documenté dans l'enquête", "a changé de mains deux fois au siècle dernier",
"se trouvait à la limite de la ville", "exigeait un entretien constant"]
Q_EN = ["Could you tell me whether", "Do you happen to know if", "I was wondering whether"]
Q_FR = ["Pourrais-tu me dire si", "Sais-tu par hasard si", "Je me demandais si"]
PY_FUNCS = ["total", "scale", "merge", "clip", "score", "rank", "fold", "trim"]
JS_VARS = ["items", "nodes", "queue", "cache", "rows", "edges", "bins", "keys"]
PROSE_SUBJ = ["The committee", "A local historian", "The lead engineer", "Her assistant",
"The night watchman", "An early visitor", "The town council", "The apprentice"]
PROSE_TAIL = ["reviewed the plans before the meeting.", "kept detailed notes for years.",
"questioned the original estimate.", "preferred the older method.",
"described the process in a letter.", "returned before the first frost."]
def gen(rng: random.Random):
sets: dict[str, list[dict]] = {}
# -------- lang_id: set A = descriptive statements, set B = questions
a, b = [], []
for t, adj, v in itertools.product(TOPICS_EN, ADJ_EN, VERB_EN):
a.append({"text": f"The {adj} site near {t} {v}.", "label": "en"})
for t, adj, v in itertools.product(TOPICS_FR, ADJ_FR, VERB_FR):
a.append({"text": f"Le site {adj} près de {t} {v}.", "label": "fr"})
for q, t, v in itertools.product(Q_EN, TOPICS_EN, VERB_EN):
b.append({"text": f"{q} the place near {t} {v}?", "label": "en"})
for q, t, v in itertools.product(Q_FR, TOPICS_FR, VERB_FR):
b.append({"text": f"{q} l'endroit près de {t} {v} ?", "label": "fr"})
sets["lang_id_A"], sets["lang_id_B"] = a, b
# -------- code_prose: set A = python vs prose, set B = js vs prose
a, b = [], []
for f, op, k in itertools.product(PY_FUNCS, ["+", "-", "*"], [1, 2, 3, 5, 7]):
a.append({"text": f"def {f}(xs):\n return [x {op} {k} for x in xs if x > {k + 1}]",
"label": "code"})
for s, t, adv in itertools.product(PROSE_SUBJ, PROSE_TAIL,
["eventually", "reluctantly", "quietly"]):
a.append({"text": f"{s} {adv} {t.lower()}", "label": "prose"})
for v, meth, k in itertools.product(JS_VARS, ["filter", "map", "find", "some"], [0, 1, 4, 9]):
b.append({"text": f"const out = {v}.{meth}(x => x.size > {k}).length;",
"label": "code"})
for s, t, adj in itertools.product(PROSE_SUBJ, PROSE_TAIL[:3], ADJ_EN[:5]):
b.append({"text": f"{s}, though {adj}, {t.lower()}", "label": "prose"})
sets["code_prose_A"], sets["code_prose_B"] = a, b
# -------- arith: set A = imperative computations, set B = embedded quantities
a, b = [], []
for _ in range(4 * N_PER_CLASS):
x, y = rng.randint(11, 97), rng.randint(11, 97)
a.append({"text": f"Calculate {x} + {y} and report the result.", "label": "arith"})
s, t = rng.choice(PROSE_SUBJ), rng.choice(PROSE_TAIL)
adv = rng.choice(["eventually", "reluctantly", "quietly", "finally", "later"])
a.append({"text": f"{s} {adv} {t.lower()}", "label": "plain"})
p, q = rng.randint(12, 89), rng.randint(12, 89)
b.append({"text": f"If the crate holds {p} jars and {q} more arrive, how many jars are there in total?",
"label": "arith"})
topic, verb, adj = rng.choice(TOPICS_EN), rng.choice(VERB_EN), rng.choice(ADJ_EN)
b.append({"text": f"According to the {adj} report, {topic} {verb}.", "label": "plain"})
sets["arith_A"], sets["arith_B"] = a, b
# balance + subsample every set to N_PER_CLASS per label, deterministic
final = {}
for name, items in sets.items():
by = {}
for it in items:
by.setdefault(it["label"], []).append(it)
chosen = []
for label, pool in sorted(by.items()):
rng.shuffle(pool)
# dedupe by text before sampling
seen, uniq = set(), []
for it in pool:
if it["text"] not in seen:
seen.add(it["text"])
uniq.append(it)
if len(uniq) < N_PER_CLASS:
raise SystemExit(f"{name}/{label}: only {len(uniq)} unique items")
chosen += uniq[:N_PER_CLASS]
rng.shuffle(chosen)
final[name] = chosen
return final
def main() -> int:
rng = random.Random(SEED)
sets = gen(rng)
manifest = {"version": VERSION, "seed": SEED, "n_per_class": N_PER_CLASS,
"author": "Simon-Pierre Boucher", "contact": "contact@spboucher.ai",
"website": "https://modelmap.io",
"limitation": "template-generated v1; natural-corpus v2 registered",
"files": {}}
for name, items in sorted(sets.items()):
path = OUT / f"{name}.jsonl"
payload = "\n".join(json.dumps(it, ensure_ascii=False) for it in items) + "\n"
path.write_text(payload)
manifest["files"][path.name] = {
"sha256": hashlib.sha256(payload.encode()).hexdigest(),
"n": len(items),
}
print(f"{path.name:22s} n={len(items)} sha256={manifest['files'][path.name]['sha256'][:12]}…")
(OUT / "manifest.json").write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n")
print("manifest.json written")
return 0
if __name__ == "__main__":
raise SystemExit(main())