devils-agent / baim /synthetic.py
devildasdf's picture
Upload experimental BAIM code, research checkpoints and measured evaluations
795f737 verified
Raw
History Blame Contribute Delete
5.51 kB
"""Seeded synthetic supervision. Templates are fixtures, never runtime rules."""
import argparse
from dataclasses import asdict
import html
import json
from pathlib import Path
import random
from .state import Element, digest
TRAIN_TEMPLATES = ('stack', 'grid', 'fieldset')
TEST_TEMPLATES = ('table', 'nested')
WORDS = ('amber birch cobalt delta elm fern granite harbor indigo jade kelp linen '
'maple nectar olive pearl quartz river silver timber umber violet willow xenon yellow zinc').split()
TRAIN_WORDING = {
'C': ['Click {name}.', 'Activate {name}.', 'Choose the control named {name}.'],
'T': ['Enter "{value}" into {name}.', 'Fill {name} with "{value}".', 'Type "{value}" in {name}.'],
'O': ['Select "{value}" from {name}.', 'Set the dropdown {name} to "{value}".', 'Choose "{value}" in the {name} list.'],
}
NOVEL_WORDING = {
'C': ['Press the {name} control.', 'Use the item labelled {name}.'],
'T': ['Populate {name} with "{value}".', 'Put "{value}" into the field labelled {name}.'],
'O': ['Pick "{value}" from the menu labelled {name}.', 'Change the {name} selector to "{value}".'],
}
def generate(split, count, seed, novel_wording=False):
rng = random.Random(seed)
templates = TEST_TEMPLATES if split == 'test' else TRAIN_TEMPLATES
for sample_id in range(count):
kind = rng.choice(('C', 'T', 'O'))
total = rng.randint(8, 32)
names = rng.sample([f'{a} {b}' for a in WORDS for b in WORDS if a != b], total)
target = rng.randrange(total)
rows = []
for index, name in enumerate(names):
role = rng.choice(('button', 'textbox', 'combobox', 'link'))
if index == target:
role = rng.choice(('button', 'link')) if kind == 'C' else {'T':'textbox','O':'combobox'}[kind]
rows.append(asdict(Element(f'e{index}', role, name, str(index))))
value = f'value-{rng.randrange(1000000)}'
wording = NOVEL_WORDING if novel_wording else TRAIN_WORDING
goal = rng.choice(wording[kind]).format(name=names[target], value=value)
yield dict(schema_version=1, source='baim-synthetic-v1', license='CC0-1.0',
split=split, template=rng.choice(templates), seed=seed, sample_id=sample_id,
goal=goal, elements=rows, action=kind, target=target,
argument=value if kind in {'T','O'} else None,
execution_verified=False, content_hash=digest([goal, rows]))
def render(sample):
"""Returns HTML plus an independent DOM-side outcome oracle for test tasks."""
controls = []
for index, row in enumerate(sample['elements']):
name = html.escape(row['name'], quote=True)
ident = f'control-{sample["seed"]}-{sample["sample_id"]}-{index}'
attrs = f'id="{ident}" data-index="{index}" aria-label="{name}"'
role = row['role']
if role == 'textbox':
control = f'<input {attrs}>'
elif role == 'combobox':
value = html.escape(sample['argument'] or 'option', quote=True)
control = f'<select {attrs}><option value="">Unset</option><option value="{value}">{value}</option></select>'
elif role == 'link':
control = f'<a {attrs} href="#result" onclick="window.fixtureResult={index}">{name}</a>'
else:
control = f'<button {attrs} onclick="window.fixtureResult={index}">{name}</button>'
controls.append(control)
template = sample['template']
if template == 'table':
body = '<table>' + ''.join(f'<tr><td>{x}</td></tr>' for x in controls) + '</table>'
elif template == 'nested':
body = '<article>' + ''.join(f'<section><div><aside>{x}</aside></div></section>' for x in controls) + '</article>'
elif template == 'fieldset':
body = '<form>' + ''.join(f'<fieldset>{x}</fieldset>' for x in controls) + '</form>'
elif template == 'grid':
body = '<main style="display:grid;grid-template-columns:repeat(3,1fr)">' + ''.join(controls) + '</main>'
else:
body = '<main>' + ''.join(f'<div>{x}</div>' for x in controls) + '</main>'
return '<!doctype html><meta charset="utf-8"><style>input,button,select,a{margin:6px;padding:5px}</style>' + body
def load(path):
return [json.loads(line) for line in Path(path).read_text(encoding='utf-8').splitlines() if line]
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--output', default='datasets/synthetic-v1')
args = parser.parse_args()
root = Path(args.output)
root.mkdir(parents=True, exist_ok=True)
manifest = {}
for split, count, seed, novel in [('train',2400,101,False),('validation',480,202,False),
('test',480,303,False),('novel_wording',480,404,True)]:
rows = list(generate('test' if split == 'novel_wording' else split, count, seed, novel))
data = ''.join(json.dumps(row, ensure_ascii=False) + '\n' for row in rows)
(root / f'{split}.jsonl').write_text(data, encoding='utf-8')
manifest[split] = dict(count=count, seed=seed, templates=sorted({r['template'] for r in rows}),
sha256=digest(data))
manifest['limitations'] = 'Single-step generated tasks; test layouts held out but vocabulary shared. No arbitrary-site claim.'
(root / 'manifest.json').write_text(json.dumps(manifest, indent=2), encoding='utf-8')
print(json.dumps(manifest, indent=2))
if __name__ == '__main__':
main()