# Grades a run log for invoice_extract / inbox_triage / cashflow against *.truth.json. Uses the last ```json block in the final answer. import sys,json,re,os task,logf=sys.argv[1],sys.argv[2]; T=json.load(open(os.path.join(os.path.dirname(__file__),task+".truth.json"))) t=open(logf).read().split("=====FINAL=====")[-1].split("\nELAPSED")[0] bl=re.findall(r"```(?:json)?\s*(\{.*?\})\s*```",t,re.S) try: J=json.loads(bl[-1]) if bl else json.loads(re.search(r"\{.*\}",t,re.S).group(0)) except Exception as e: print(json.dumps({"parsed":False,"score":0,"max":None})); sys.exit() def num(a,b): if isinstance(a,str): s=re.sub(r"[£, ]","",a) try: a=float(s) except ValueError: return False return isinstance(a,(int,float)) and not isinstance(a,bool) and abs(a-b)<0.005 R={} if task=="invoice_extract": for k,v in T.items(): a=J.get(k) if isinstance(v,float): R[k]=bool(num(a,v)) elif k=="supplier_vat_number": R[k]=isinstance(a,str) and a.replace(" ","").upper()==v elif k=="supplier_name": R[k]=isinstance(a,str) and a.strip().lower().rstrip(".")==v.lower() else: R[k]=isinstance(a,str) and a.strip()==v elif task=="inbox_triage": for i,c in T["labels"].items(): R["cat_"+i]=(J.get("labels") or {}).get(i)==c # E7 priority not scored: nearly every run said "normal"; our rubric said "low", and we judge the rubric wording ambiguous. for i,p in T["priority"].items(): if i=="E7": continue R["pri_"+i]=str((J.get("priority") or {}).get(i,"")).lower()==p R["phishing"]=sorted(J.get("phishing") or [])==T["phishing"] elif task=="cashflow": W={w.get("week_commencing"):w for w in (J.get("weeks") or []) if isinstance(w,dict)} R["week_count"]=len(W)==len(T["weeks"]) for w in T["weeks"]: g=W.get(w["week_commencing"],{}) for k in ("money_in","money_out","net","closing_balance"): R[f"{w['week_commencing']}_{k}"]=bool(num(g.get(k),w[k])) R["closing_balance"]=bool(num(J.get("closing_balance"),T["closing_balance"])) R["largest_outflow_category"]=str(J.get("largest_outflow_category","")).lower()==T["largest_outflow_category"].lower() R["largest_outflow_total"]=bool(num(J.get("largest_outflow_total"),T["largest_outflow_total"])) R["lowest_closing_week"]=J.get("lowest_closing_week")==T["lowest_closing_week"] print(json.dumps({"parsed":True,"score":sum(R.values()),"max":len(R),"failed":[k for k,v in R.items() if not v]}))