#!/usr/bin/env python3 """GIG AI-Visibility runner. Asks a prompt set across answer engines, detects whether GIG Gulf (and competitors) get cited, scores by GEO phase, saves a dated snapshot. Action-first weekly report is generated by report.py from the saved snapshots.""" import json, os, re, sys, time, datetime, urllib.request, concurrent.futures as cf ROOT = os.path.dirname(os.path.abspath(__file__)) CRED = os.path.expanduser("~/ClaudeWork/Claude/credentials.md") # host path; in sandbox use mounted # Resolve credentials.md across host/sandbox for p in [CRED, "/sessions/jolly-practical-lamport/mnt/ClaudeWork/Claude/credentials.md"]: if os.path.exists(p): CRED = p; break def key(section, pat): f=False for l in open(CRED, errors="ignore"): if l.startswith("### "+section): f=True; continue if f and re.search(pat, l): m=re.search(pat, l); return m.group(0) return None ANTH = key("Anthropic API", r"sk-ant-[A-Za-z0-9_-]{20,}") OAI = key("OpenAI", r"sk-[A-Za-z0-9_-]{20,}") SERP = key("Serper", r"[a-f0-9]{40}") # GIG Gulf is the rebrand of AXA Gulf (UAE, 2022). AXA legacy equity is OWNED demand — # AXA queries deliver up to ~25% of GIG GWP. So in this UAE tool, an "AXA" mention counts # as GIG-favourable (legacy equity), NOT a competitor. GIG = [r"gig gulf", r"giggulf", r"gig insurance", r"gulf insurance group", r"gig-gulf", r"\baxa\b"] COMP = {"Sukoon":[r"sukoon","oman insurance"],"Tawuniya":["tawuniya"],"Salama":["salama"], "Orient":["orient insurance"],"Watania":["watania"],"Daman":["daman"], "ADNIC":["adnic"],"RSA/Liva":["liva","rsa "],"Dubai Insurance":["dubai insurance"], "Noor Takaful":["noor takaful"],"Yallacompare":["yallacompare","policybazaar","insurancemarket","souqalmal"]} def mentioned(text, pats): t=text.lower() return any(re.search(p, t) for p in pats) def comp_hits(text): return [c for c,pats in COMP.items() if mentioned(text,pats)] def http(url, data, headers, timeout=30): req=urllib.request.Request(url, data=json.dumps(data).encode(), headers=headers) with urllib.request.urlopen(req, timeout=timeout) as r: return json.loads(r.read()) def ask_openai(q): d=http("https://api.openai.com/v1/chat/completions", {"model":"gpt-4o-mini","max_tokens":400,"messages":[ {"role":"system","content":"You are an insurance shopping assistant for the UAE. Name specific insurers and brands."}, {"role":"user","content":q}]}, {"Authorization":f"Bearer {OAI}","Content-Type":"application/json"}) return d["choices"][0]["message"]["content"] def ask_claude(q): d=http("https://api.anthropic.com/v1/messages", {"model":"claude-haiku-4-5-20251001","max_tokens":400,"messages":[ {"role":"user","content":"You are an insurance shopping assistant for the UAE. Name specific insurers and brands.\n\n"+q}]}, {"x-api-key":ANTH,"anthropic-version":"2023-06-01","content-type":"application/json"}) return d["content"][0]["text"] def ask_google(q): d=http("https://google.serper.dev/search",{"q":q,"gl":"ae","num":10}, {"X-API-KEY":SERP,"Content-Type":"application/json"}) parts=[] if "answerBox" in d: parts.append(json.dumps(d["answerBox"])) for o in d.get("organic",[])[:10]: parts.append(o.get("title","")+" "+o.get("link","")+" "+o.get("snippet","")) return "\n".join(parts) ENGINES={"ChatGPT":ask_openai,"Claude":ask_claude,"Google":ask_google} def run_one(p, eng): try: txt=ENGINES[eng](p["q"]) return {"id":p["id"],"phase":p["phase"],"lob":p["lob"],"q":p["q"],"engine":eng, "gig":mentioned(txt,GIG),"competitors":comp_hits(txt),"ok":True,"text":txt[:1200]} except Exception as e: return {"id":p["id"],"phase":p["phase"],"lob":p["lob"],"q":p["q"],"engine":eng, "gig":None,"competitors":[],"ok":False,"err":str(e)[:200]} def main(): prompts=json.load(open(os.path.join(ROOT,"prompts.json"))) jobs=[(p,e) for p in prompts for e in ENGINES] rows=[] with cf.ThreadPoolExecutor(max_workers=12) as ex: futs=[ex.submit(run_one,p,e) for p,e in jobs] for f in cf.as_completed(futs): rows.append(f.result()) date=datetime.date.today().isoformat() os.makedirs(os.path.join(ROOT,"snapshots"),exist_ok=True) out=os.path.join(ROOT,"snapshots",f"{date}.json") json.dump({"date":date,"rows":rows},open(out,"w"),indent=1) ok=[r for r in rows if r["ok"]] gig=sum(1 for r in ok if r["gig"]); n=len(ok) print(f"saved {out} | {n} calls ok / {len(rows)} | GIG cited in {gig}/{n} = {gig/n*100:.0f}%") fails=[r for r in rows if not r["ok"]] if fails: print("FAILS:",len(fails),fails[0].get("err")) if __name__=="__main__": main()