"""Command-line entry points for specifications, episodes, and analysis.""" import argparse,json,sys from pathlib import Path import numpy as np from .schema import DesignSpec,canonical def main(): p=argparse.ArgumentParser(prog='peppa');sub=p.add_subparsers(dest='command',required=True) s=sub.add_parser('validate');s.add_argument('spec') s=sub.add_parser('run');s.add_argument('--spec',required=True);s.add_argument('--registry',required=True);s.add_argument('--controller',required=True);s.add_argument('--trace',required=True) s=sub.add_parser('replay');s.add_argument('trace');s.add_argument('--output') s=sub.add_parser('train-ptm');s.add_argument('data');s.add_argument('--output',required=True);s.add_argument('--l2',type=float,default=.001) s=sub.add_parser('fit-ternary');s.add_argument('data');s.add_argument('--output',required=True) s=sub.add_parser('normalize-snooppi');s.add_argument('data');s.add_argument('--output',required=True) s=sub.add_parser('split');s.add_argument('data');s.add_argument('--output',required=True) s=sub.add_parser('summarize');s.add_argument('data');s.add_argument('--value',default='joint_success');s.add_argument('--group',default='task');s.add_argument('--output',required=True) s=sub.add_parser('plan');s.add_argument('--output',required=True) args=p.parse_args() def read(path):return json.loads(Path(path).read_text()) def write(path,x): Path(path).parent.mkdir(parents=True,exist_ok=True);Path(path).write_text(json.dumps(x,indent=2,allow_nan=False)+'\n') if args.command=='validate': spec=DesignSpec.model_validate(read(args.spec));print(canonical(spec));return if args.command=='replay': from .trace import replay state=replay(args.trace) if args.output:write(args.output,state) else:print(json.dumps(state,indent=2)) elif args.command=='run': from .engine import Engine from .builtin import registry_from_config from .controllers import APIController,ScriptedController,AnthropicController spec=DesignSpec.model_validate(read(args.spec));cfg=read(args.controller) controller=ScriptedController(cfg['decisions']) if cfg['type']=='scripted' else AnthropicController(**cfg['settings']) if cfg['type']=='anthropic' else APIController(**cfg['settings']) state=Engine(spec,registry_from_config(read(args.registry)),args.trace).run(controller) print(json.dumps({'stopped':state['stopped'],'candidates':len(state['candidates']),'spent':state['spent'],'errors':len(state['errors'])})) elif args.command=='train-ptm': from .ptm import fit with np.load(args.data,allow_pickle=False) as d: if not np.all(d['split']=='train'):raise ValueError('training file contains nontraining rows') model,report=fit(d['binder'],d['target'],d['labels'],d['pairs'],l2=args.l2) Path(args.output).parent.mkdir(parents=True,exist_ok=True);model.save(args.output);write(args.output+'.json',report) elif args.command=='fit-ternary': from .ternary import fit_cooperativity d=read(args.data);write(args.output,fit_cooperativity(np.array(d['totals_nm']),d['observed_abl_nm'],d['kd_a_nm'],d['kd_b_nm'],d.get('sd_nm'))) elif args.command=='normalize-snooppi': from .data import normalize_snooppi rows=[json.loads(x) for x in Path(args.data).read_text().splitlines() if x] Path(args.output).parent.mkdir(parents=True,exist_ok=True);Path(args.output).write_text(''.join(json.dumps(normalize_snooppi(x))+'\n' for x in rows)) elif args.command=='split': from .data import connected_splits rows=read(args.data);write(args.output,[dict(row,split=sp) for row,sp in zip(rows,connected_splits(rows))]) elif args.command=='summarize': from .metrics import grouped_bootstrap write(args.output,grouped_bootstrap(read(args.data),args.value,args.group)) elif args.command=='plan': rows=[] arms=['fixed', 'react_muse', 'compiled_reduced_checks', 'peppa_muse', 'peppa_astra', 'peppa_claude', 'peppa_no_retrieval'] for family in ['affinity_specificity','motif','conformation']: for task in range(1,9): for arm in arms: for seed in [2027,2028,2029]: rows.append({'task':f'{family}_{task:02d}','family':family,'arm':arm,'seed':seed,'status':'awaiting frozen task manifest','raw_proposals':384,'computational_cycles':2,'structures_per_cycle':18,'gpu_minutes':1440,'controller_tokens':128000,'ranking_batch':12}) write(args.output,rows) if __name__=='__main__':main()