88 lines
3.5 KiB
Python
88 lines
3.5 KiB
Python
"""Object type mix of the training data (CLAUDE.md section 1, Opus review 2026-10-05).
|
|
|
|
Target share of the accepted tasks (and of the accepted trajectories) per "kind":
|
|
CLAS/INTF 35 % (CLAS 28, INTF 7), CDS (DDLS) 25 %, FUNC 15 %, PROG 10 %, DDIC 10 % (TABL 8, STRU 2),
|
|
MSAG + exception 5 % (MSAG 2.5, EXC 2.5).
|
|
A task has the kind of its main contract object; a CLAS whose reference inherits from CX_ is EXC.
|
|
DTEL / DOMA tasks are not made yet (the DDIC share is covered by TABL and STRU).
|
|
"""
|
|
import json
|
|
import os
|
|
import re
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
POOL = os.path.join(ROOT, "tasks_gen", "train")
|
|
TYPE_SHARE = {"CLAS": 28.0, "INTF": 7.0, "DDLS": 25.0, "FUNC": 15.0, "PROG": 10.0, "TABL": 8.0, "STRU": 2.0,
|
|
"MSAG": 2.5, "EXC": 2.5}
|
|
# categories a kind supports (docs/faz1-tasarim.md 8): the generator picks the one with the biggest deficit
|
|
KIND_CATEGORIES = {"CLAS": "ACDEFGHI", "FUNC": "ACDEFGHI", "PROG": "BCEGH", "DDLS": "BEFHI", "INTF": "A",
|
|
"TABL": "B", "STRU": "B", "MSAG": "D", "EXC": "D"}
|
|
CATEGORY_SHARE = {"A": 10, "B": 15, "C": 15, "D": 10, "E": 15, "F": 10, "G": 10, "I": 5, "H": 10}
|
|
_CACHE = {}
|
|
|
|
|
|
def kind_of_task_dir(task_id):
|
|
"""Kind of an accepted training task (cached)."""
|
|
if task_id in _CACHE:
|
|
return _CACHE[task_id]
|
|
d = os.path.join(POOL, task_id)
|
|
try:
|
|
t = json.load(open(os.path.join(d, "task.json")))
|
|
except (OSError, ValueError):
|
|
return None
|
|
otype = t.get("object_type") or "CLAS"
|
|
for c in t.get("contract", []): # K variants and old tasks: the contract object is the truth
|
|
otype = c.get("type", otype)
|
|
break
|
|
kind = otype
|
|
if otype == "CLAS":
|
|
for o in t.get("reference", []):
|
|
if o.get("file") and o.get("name") == (t.get("contract") or [{}])[0].get("name"):
|
|
try:
|
|
src = open(os.path.join(d, o["file"])).read()
|
|
except OSError:
|
|
src = ""
|
|
if re.search(r"INHERITING\s+FROM\s+\S*CX_", src, re.I):
|
|
kind = "EXC"
|
|
_CACHE[task_id] = kind
|
|
return kind
|
|
|
|
|
|
def deficit_pick(counts, share=TYPE_SHARE, allowed=None):
|
|
"""Kind with the largest (target - actual) for the next item. counts: {kind: n}."""
|
|
total = sum(counts.values()) + 1
|
|
best, best_def = None, None
|
|
tot_share = sum(share.values())
|
|
for k, s in share.items():
|
|
if allowed and k not in allowed:
|
|
continue
|
|
d = total * s / tot_share - counts.get(k, 0)
|
|
if best_def is None or d > best_def:
|
|
best, best_def = k, d
|
|
return best
|
|
|
|
|
|
def accepted_task_counts(logs_dir=None):
|
|
"""{kind: accepted tasks} from the generation logs of the training pool (K variants count by their kind)."""
|
|
import glob
|
|
logs_dir = logs_dir or os.path.join(POOL, "_logs")
|
|
out = {}
|
|
for f in glob.glob(os.path.join(logs_dir, "G*.json")):
|
|
try:
|
|
l = json.load(open(f))
|
|
except (OSError, ValueError):
|
|
continue
|
|
if l.get("accepted"):
|
|
k = kind_of_task_dir(l["id"])
|
|
if k:
|
|
out[k] = out.get(k, 0) + 1
|
|
return out
|
|
|
|
|
|
def below_target(counts, share=TYPE_SHARE):
|
|
"""Kinds whose share of `counts` is below the target for the next item (deficit > 0).
|
|
Since 2026-10-05 (Kral + Opus): only these kinds are generated and run; CLAS and FUNC wait until they are at or
|
|
below their target share."""
|
|
total, ss = sum(counts.values()) + 1, sum(share.values())
|
|
return {k for k, v in share.items() if total * v / ss - counts.get(k, 0) > 0}
|