Files
abap-llm/harness/mix.py

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}