Download code/validator.py from Modularcomputing/dynamic-accent-duplex: direct link, hf CLI and curl.
- Browser
- Download file 4.7 kB
-
https://huggingface.co/Modularcomputing/dynamic-accent-duplex/resolve/ac573f1b218a7c549ca46573107d3833a92d0ed3/code/validator.py
- Command line
-
hf download hf://Modularcomputing/dynamic-accent-duplex@ac573f1b218a7c549ca46573107d3833a92d0ed3/code/validator.py
-
curl -L -o validator.py https://huggingface.co/Modularcomputing/dynamic-accent-duplex/resolve/ac573f1b218a7c549ca46573107d3833a92d0ed3/code/validator.py
4.7 kB
| """Reply validator: tags well-formed, language proportions vs target mix, greeting pattern kept, length, loops. | |
| Scores a candidate; the agent keeps the best passing candidate, regenerates up to N rounds, then falls back.""" | |
| import re | |
| from collections import Counter | |
| import mix as M | |
| TAG = re.compile(r"<(eng|ibo|yor|hau|pcm)>(.*?)</\1>", re.S) | |
| def parse(out): | |
| out = out.strip().splitlines()[0].strip() if out.strip() else "" | |
| spans = TAG.findall(out) | |
| rest = TAG.sub(" ", out) | |
| return out, spans, re.sub(r"[\s.,!?;:'\"\-–—…]", "", rest) | |
| def check(out, target, max_words=40, min_words=2, lid="glotlid", tvd_max=0.3, user=None): | |
| out, spans, rest = parse(out) | |
| r = dict(text=out, ok=False, why=[], tvd=1.0) | |
| if not spans: | |
| r["why"].append("no tags"); return r | |
| if rest: | |
| r["why"].append("text outside tags") | |
| words = " ".join(t for _, t in spans).split() | |
| if not (min_words <= len(words) <= max_words): | |
| r["why"].append(f"length {len(words)}") | |
| low = [w.lower().strip(".,!?") for w in words] | |
| tri = Counter(tuple(low[i:i + 3]) for i in range(len(low) - 2)) | |
| if tri and tri.most_common(1)[0][1] > 2: | |
| r["why"].append("loop") | |
| if user: # parroting: the LLM copies the user's (often mis-recognised) words instead of answering | |
| uw = [w.lower().strip(".,!?") for w in user.split()] | |
| ug = {tuple(uw[i:i + 3]) for i in range(len(uw) - 2)} | |
| rg = [tuple(low[i:i + 3]) for i in range(len(low) - 2)] | |
| r["echo"] = round(sum(g in ug for g in rg) / len(rg), 2) if rg else 0.0 | |
| if r["echo"] > 0.5 and len(low) >= 6: r["why"].append(f"echo {r['echo']}") # short closings may legitimately mirror the user | |
| # declared mix (by tag) and measured mix (re-tag the plain text with the LID tagger) | |
| n = max(1, len(words)) | |
| decl = Counter() | |
| for l, t in spans: decl[l] += len(t.split()) | |
| decl = {k: v / n for k, v in decl.items()} | |
| plain = " ".join(t for _, t in spans) | |
| meas = M.estimate(plain, lid=lid) | |
| # measured labels can confuse English/Pidgin; merge eng+pcm disagreement softly by averaging declared and measured | |
| mixd = {k: 0.5 * decl.get(k, 0) + 0.5 * meas["mix"].get(k, 0) for k in set(decl) | set(meas["mix"])} | |
| tgt = target["mix"] | |
| d = M.tvd(mixd, tgt) | |
| r.update(declared={k: round(v, 2) for k, v in decl.items()}, measured=meas["mix"], mix=({k: round(v, 2) for k, v in mixd.items() if v > 0.01}), tvd=round(d, 3)) | |
| # tag honesty: words tagged ibo/yor/hau that the tagger thinks are English | |
| lab = meas["labels"]; i = 0; mism = 0; ng = 0 | |
| for l, t in spans: | |
| k = len(M.tagger(lid).word_langs(t)[1]) if t.strip() else 0 | |
| if l in ("ibo", "yor", "hau"): | |
| ng += k; mism += sum(1 for x in lab[i:i + k] if x == "eng") | |
| i += k | |
| r["tag_mismatch"] = round(mism / ng, 2) if ng else 0.0 | |
| if r["tag_mismatch"] > 0.5: r["why"].append("non-English tags over English words") | |
| if d > tvd_max: r["why"].append(f"mix drift tvd={d:.2f}") | |
| for l, p in tgt.items(): # every substantial target language must appear | |
| if p >= 0.15 and mixd.get(l, 0) < 0.05: r["why"].append(f"missing {l}") | |
| for p in target.get("pattern", []): # greeting pattern preserved | |
| k, _, l = p.partition(":") | |
| if k == "greeting" and spans and spans[0][0] != l: r["why"].append(f"no {l} greeting") | |
| r["ok"] = not r["why"] | |
| r["score"] = d + 0.25 * len(r["why"]) + 0.2 * r["tag_mismatch"] | |
| return r | |
| SAFE = {"ibo": "Ndewo!", "yor": "Ẹ pẹ̀lẹ́ o!", "hau": "Sannu!", "pcm": "Abeg no vex,"} | |
| def fallback(text_candidates, target): | |
| """all rounds failed. Echoing/drifting native-language output -> an honest short reply (a correct phrasebook opener in the user's | |
| language + a clarification request); otherwise the best-scoring candidate if its tags parse; else plain text in the matrix language.""" | |
| best = min(text_candidates, key=lambda c: c["score"] if "score" in c else 9, default=None) | |
| m = target.get("matrix", "eng") | |
| if m in SAFE and (best is None or any(w.startswith(("echo", "mix drift", "missing", "no tags", "non-English")) for w in best.get("why", []))): | |
| tail = "<pcm>I no too hear you well. Abeg talk am again?</pcm>" if m == "pcm" else "<eng>Sorry, I did not catch all of that. Could you say it again, or in English?</eng>" | |
| return f"<{m}>{SAFE[m]}</{m}> {tail}", "safe_phrasebook" | |
| if best and TAG.search(best["text"] or ""): | |
| return best["text"], "best_failed" | |
| m = target.get("matrix", "eng"); raw = re.sub(r"</?\w+>", "", (best or {}).get("text", "") or "Sorry, say that again?") | |
| return f"<{m}>{raw.strip()}</{m}>", "plain_matrix" | |