Spaces:
Running
Running
Download src/miniweb/scripts/audit_task_verifiers.py from sparklabuofu/MiniWeb-demo: direct link, hf CLI and curl.
- Browser
- Download file 8.32 kB
-
https://huggingface.co/spaces/sparklabuofu/MiniWeb-demo/resolve/main/src/miniweb/scripts/audit_task_verifiers.py
- Command line
-
hf download hf://spaces/sparklabuofu/MiniWeb-demo/src/miniweb/scripts/audit_task_verifiers.py
-
curl -L -o audit_task_verifiers.py https://huggingface.co/spaces/sparklabuofu/MiniWeb-demo/resolve/main/src/miniweb/scripts/audit_task_verifiers.py
8.32 kB
| """Offline verifier audit. Uses saved evidence; never fabricates successful walks. | |
| Judge-dependent comparisons are marked unresolved in the deterministic pass. | |
| Reports record every task, including exclusions and missing verifiers. | |
| """ | |
| import argparse | |
| import copy | |
| import json | |
| from pathlib import Path | |
| import sys | |
| from contextlib import nullcontext | |
| from unittest.mock import patch | |
| ROOT = Path(__file__).resolve().parents[3] # the repository | |
| sys.path.insert(0, str(ROOT / "src")) | |
| from miniweb.annotation.macros import canon, all_canonical | |
| from miniweb.annotation.quality import effective_macros | |
| from miniweb.evaluation.verifiers import verify_task, CHECKS | |
| from miniweb.evaluation.trajectory import merge_server_log | |
| def nodes(tree, path=()): | |
| if not isinstance(tree, dict): | |
| yield path, tree | |
| return | |
| yield path, tree | |
| for field in ('checks', 'steps'): | |
| for i, child in enumerate(tree.get(field, [])): | |
| yield from nodes(child, path + (field, i)) | |
| for field in ('request', 'match'): | |
| if isinstance(tree.get(field), dict): | |
| yield from nodes(tree[field], path + (field,)) | |
| def load_evidence(directory, task): | |
| walk = directory / 'verification_walk.json' | |
| # Excluded tasks may have a failed exploratory walk; preserve that distinction. | |
| runs = directory / 'verifier_runs.json' | |
| prior = json.loads(runs.read_text()).get('runs', {}) if runs.exists() else {} | |
| if walk.exists() and prior.get('walk', {}).get('passed'): | |
| data = json.loads(walk.read_text()) | |
| return data.get('trajectory', []), data.get('answer', ''), 'walk' | |
| path = directory / 'trajectory.json' | |
| traj = json.loads(path.read_text()) if path.exists() else [] | |
| traj = merge_server_log(traj, directory / 'server_log.json') | |
| return traj, task.get('answer') or task.get('expected_answer') or '', 'gold' | |
| def audit_one(key, task, spec): | |
| row = {'key': key, 'recommendation': task.get('ai_review', {}).get('recommendation', 'unreviewed'), | |
| 'instruction': task.get('instruction', ''), 'findings': [], 'probes': []} | |
| if not spec or not spec.get('macros'): | |
| row['findings'].append('No saved verifier') | |
| return row | |
| spec = copy.deepcopy(spec) | |
| spec['macros'] = effective_macros(task, spec['macros']) | |
| want = {canon(m) for m in task.get('macros', [])} | |
| if want != set(spec['macros']): | |
| row['findings'].append('Task and verifier macro keys differ') | |
| for macro, tree in spec['macros'].items(): | |
| for path, node in nodes(tree): | |
| if not isinstance(node, dict): | |
| row['findings'].append('Malformed node: ' + macro + str(path));continue | |
| if 'op' in node and (not node.get('checks') or node['op'] not in ('AND', 'OR')): | |
| row['findings'].append('Empty/invalid group: ' + macro + str(path)) | |
| if 'type' in node and node['type'] not in CHECKS: | |
| row['findings'].append('Unknown check: ' + str(node['type'])) | |
| if any(isinstance(v, dict) and v.get('open') for v in node.values()) and not node.get('advisory'): | |
| row['findings'].append('Unfilled gating check: ' + macro + str(path)) | |
| traj, answer, source = load_evidence(ROOT / 'data/annotations' / key, task) | |
| row['source'] = source | |
| def probe(name, events, output, expected=None): | |
| report = verify_task(spec, events, output, question=task.get('instruction', '')) | |
| errors=[] | |
| def failed(n): | |
| if n.get('reason') and ('unavailable' in n['reason'].lower() or 'audit: judge' in n['reason'] or 'raised ' in n['reason']):errors.append(n['reason']) | |
| for child in n.get('checks',[]):failed(child) | |
| for n in report['macros'].values():failed(n) | |
| row['probes'].append({'name': name, 'passed': report['passed'], 'expected': expected, | |
| 'by_macro': report['by_macro'], 'unresolved': errors}) | |
| return report['passed'] | |
| passed=probe('saved_'+source, traj, answer, True) | |
| probe('empty', [], '', False) | |
| probe('answer_only', [], answer, False) | |
| probe('unrelated_activity', [{'type':'action','action':'click','target':'unrelated'}, {'type':'network','method':'GET','url':'/unrelated','status':200}, {'type':'observation','url':'/unrelated','snapshot':'Unrelated page'}], answer, False) | |
| all_nodes=[n for tree in spec['macros'].values() for _,n in nodes(tree) if isinstance(n,dict)] | |
| answers=[n for n in all_nodes if (n.get('type') in ('answer_matches','qa_answer') and n.get('leaf', True) is not False) | |
| or (n.get('type') == 'request_sequence' and 'answer' in n)] | |
| row['answer_gated']=bool(answers) | |
| if answers: | |
| probe('missing_answer',traj,'',False) | |
| probe('denies_expected_answer',traj,'The answer is not '+str(answer)+'.',False) | |
| probe('uncertain_answer',traj,'I do not know whether the answer is '+str(answer)+'.',False) | |
| probe('wrong_answer',traj,'Unrelated answer 987654321',False) | |
| # Diagnostic ablations require semantic review: outcome-only tasks may | |
| # legitimately pass without action events or an unnecessary request. | |
| for kind in ('network','action','observation'): | |
| probe('without_'+kind,[e for e in traj if e.get('type')!=kind],answer) | |
| requests=[e for e in traj if e.get('type')=='network' and e.get('method') in ('POST','PUT','PATCH','DELETE')] | |
| if requests: | |
| altered=[dict(e,status=500) if e in requests else e for e in traj] | |
| probe('mutations_return_500',altered,answer) | |
| from miniweb.evaluation.verifiers import RequestMade, _parse_body | |
| # Counterfactual copies are diagnostic probes, never stored as walkthroughs. | |
| for ni,n in enumerate(all_nodes): | |
| if n.get('type') != 'request_made' or not n.get('last_for_resource') or '{{' in n.get('url',''): | |
| continue | |
| matches=[e for e in traj if RequestMade(n).run([e], '')[0]] | |
| if not matches:continue | |
| e=copy.deepcopy(matches[-1]);resp=_parse_body(e.get('responseBody')) | |
| inverse={'liked':'unliked','saved':'unsaved','starred':'unstarred','followed':'unfollowed', | |
| 'joined':'left','blocked':'unblocked','subscribed':'unsubscribed','up':'down'} | |
| changed=False | |
| for field,w in n.get('response_fields',{}).items(): | |
| if field not in resp:continue | |
| value=resp[field] | |
| if isinstance(value,bool):resp[field]=not value;changed=True | |
| elif isinstance(value,str) and value in inverse:resp[field]=inverse[value];changed=True | |
| if changed: | |
| e['responseBody']=resp;e.pop('timestamp',None);e.pop('_source',None) | |
| probe('undo_final_state_'+str(ni),traj+[e],answer,False) | |
| if passed: | |
| probe('harmless_extra_action',traj+[{'type':'action','action':'scroll','target':'page'}],answer,True) | |
| row['unexpected']=[p['name'] for p in row['probes'] if p['expected'] is not None and p['passed']!=p['expected'] and not p['unresolved']] | |
| return row | |
| def main(): | |
| parser=argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument('--output',required=True);parser.add_argument('--only',default='') | |
| parser.add_argument('--judge',action='store_true',help='Run configured semantic judge instead of leaving those comparisons unresolved') | |
| args=parser.parse_args();out=Path(args.output);out.mkdir(parents=True,exist_ok=True) | |
| rows=[] | |
| with nullcontext() if args.judge else patch('miniweb.evaluation.verifiers._judge_alignment', return_value=(False,'audit: judge-dependent comparison not executed')): | |
| for i,path in enumerate(sorted((ROOT/'data/annotations').glob('*/*/task.json')),1): | |
| key=str(path.parent.relative_to(ROOT/'data/annotations')) | |
| if args.only and args.only not in key:continue | |
| task=json.loads(path.read_text());vf=path.parent/'verifier.json';spec=json.loads(vf.read_text()) if vf.exists() else None | |
| rows.append(audit_one(key,task,spec)) | |
| (out/'automated.json').write_text(json.dumps(rows,indent=2,ensure_ascii=False)) | |
| if i%25==0:print('Audited',i,flush=True) | |
| print('Complete:',len(rows),'tasks;',sum(len(r.get('probes',[])) for r in rows),'probes;',sum(bool(r.get('unexpected') or r['findings']) for r in rows),'tasks flagged',flush=True) | |
| if __name__=='__main__':main() | |