JAA-ATS-Tool / scripts /verify_latex_resume.py
saitejatirunagari's picture
feat: LaTeX resume input + recruiter-grade keyword placement + resilient Run (Phase 7)
7759bfb
Raw
History Blame
7.68 kB
"""
Deterministic verification for the LaTeX resume flow (src/latex_resume.py).
Proves, without any LLM or LaTeX engine:
1. LaTeX → text extraction recovers the resume's words.
2. Honestly-includable JD keywords (PM/AI/SaaS craft) get injected into the
LaTeX and therefore appear in the extracted text.
3. BLOCKED / fabrication-risk terms (certifications, seniority, employers,
specialised engineering) are NEVER injected in ANY placement.
4. External-style coverage rises sharply after injection.
5. Keywords are DISTRIBUTED, not dumped: a Summary sentence, woven experience
\\item clauses, and at most ONE compact competencies line — and re-running
does not stack fragments (idempotent).
Run: python scripts/verify_latex_resume.py
"""
import os
import re
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from src.latex_resume import (
latex_to_text, inject_keywords, optimize_latex_resume,
_INJECT_MARKER, _INJECT_END, _INJECT_WEAVE,
)
from src.external_ats import extract_external_keywords, external_coverage
SAMPLE_LATEX = r"""
\documentclass[11pt]{article}
\usepackage[margin=0.75in]{geometry}
\begin{document}
\textbf{Jane Doe} \\
Product Manager
\section*{Summary}
Product manager who ships data-informed features and works with engineering and
design to deliver customer value. Owned the product roadmap for a consumer app.
\section*{Experience}
\textbf{Product Manager, Acme} \\
\begin{itemize}
\item Led discovery and defined the product backlog for a B2B dashboard.
\item Ran A/B tests and shipped onboarding improvements that lifted activation.
\end{itemize}
\section*{Education}
B.Tech, Computer Science
\end{document}
"""
# A JD full of normal PM/Product/AI/SaaS vocabulary + a few fabrication-risk terms.
SAMPLE_JD = """
We are hiring a Senior AI Product Manager. You will own product strategy and the
product roadmap, drive stakeholder management, and partner with engineering on
machine learning and generative AI features. Strong skills in product analytics,
experimentation, A/B testing, go-to-market, user research, and agile delivery.
SaaS and B2B experience preferred. Must have a CISSP certification, a PMP, and
12+ years of experience. Hands-on Kubernetes and CUDA kernel programming required.
"""
# Terms we must NEVER fabricate even though they appear in the JD.
MUST_NOT_INJECT = ["cissp", "pmp", "12+ years", "cuda"]
def main() -> int:
print("=" * 70)
print("LaTeX resume flow verification")
print("=" * 70)
# 1. Text extraction recovers resume words.
base_text = latex_to_text(SAMPLE_LATEX)
low = base_text.lower()
assert "product manager" in low, "extraction lost 'product manager'"
assert "backlog" in low, "extraction lost 'backlog'"
assert "\\section" not in base_text, "extraction left raw LaTeX commands"
print(f"[1] LaTeX-to-text OK ({len(base_text)} chars, no raw commands)")
# 2/3/4. Full optimize in Maximum ATS Mode (no compile — text path only).
report = optimize_latex_resume(
SAMPLE_LATEX, SAMPLE_JD,
maximum_ats_mode=True,
compile_pdf=False,
)
injected = [t.lower() for t in report["injected"]]
expected = report["expected_terms"]
before = external_coverage(expected, latex_to_text(SAMPLE_LATEX))
after_pct = report["pct"]
print(f"[2] expected terms: {len(expected)} | injected: {len(injected)}")
print(f" coverage before: {before['pct']}% -> after: {after_pct}%")
# Some craft terms that should be safely injected (present in JD, low risk).
want_some = [t for t in ("product strategy", "product roadmap",
"stakeholder management", "generative ai",
"machine learning", "a/b testing", "go-to-market",
"user research", "saas", "b2b")
if t in [e.lower() for e in expected]]
injected_or_present = set(injected) | {p.lower() for p in report["present"]}
covered_craft = [t for t in want_some if t in injected_or_present]
assert len(covered_craft) >= 5, (
f"expected >=5 craft terms covered, got {covered_craft}")
print(f"[3] craft terms covered ({len(covered_craft)}): {covered_craft}")
# 3. No fabrication-risk term was injected in ANY placement (summary
# sentence, woven \item clauses, or the competencies line).
tex = report["tex"]
frags = _injected_fragments(tex)
bad = [t for t in MUST_NOT_INJECT if t in injected or t in frags]
assert not bad, f"FABRICATION: injected blocked terms {bad}"
print(f"[4] no fabrication: blocked terms absent from EVERY injected "
f"placement ({MUST_NOT_INJECT})")
# 4. Coverage improved meaningfully.
assert after_pct >= before["pct"], "coverage did not improve"
assert after_pct >= 70, f"coverage only {after_pct}% (expected >=70%)"
print(f"[5] coverage improved {before['pct']}% -> {after_pct}% (>=70 target)")
# 5. Distribution: at most one competencies line, a Summary sentence, and
# woven experience / multi-section placement (no single dump block).
comp_lines = tex.count(r"\textbf{Core Competencies:}")
assert comp_lines <= 1, f"expected <=1 competencies line, got {comp_lines}"
assert "Core focus areas include" in tex, (
"no Summary placement sentence (SAMPLE_LATEX has \\section*{Summary})")
woven = ("(applying" in tex) and (_INJECT_WEAVE in tex)
added_sections = {k["section"] for k in report["keywords"]
if k["found_in_export"] and k["section"].endswith("(added)")}
multi_section = any(s in ("Summary (added)", "Experience (added)")
for s in added_sections)
assert woven or multi_section, (
f"no distributed placement detected (sections={sorted(added_sections)})")
print(f"[6] distribution OK: {comp_lines} competencies line, Summary sentence "
f"present, woven={woven}, sections={sorted(added_sections)}")
# 6. Structure + idempotency: balanced document, and re-running injection
# reproduces the same fragments rather than stacking them.
assert tex.count(r"\end{document}") == 1, "broken document env"
again, _ = inject_keywords(tex, report["injected"])
assert again.count("Core focus areas include") == \
tex.count("Core focus areas include"), "summary not idempotent"
assert again.count(r"\textbf{Core Competencies:}") == comp_lines, \
"competencies line not idempotent"
assert again.count(_INJECT_WEAVE) == tex.count(_INJECT_WEAVE), \
"woven clauses not idempotent"
assert again.count(_INJECT_MARKER) == tex.count(_INJECT_MARKER), \
"marker fragments not idempotent"
print("[7] structure OK: balanced document, fragments stable on re-run "
"(idempotent)")
print("=" * 70)
print("PASS — LaTeX flow DISTRIBUTES honest keywords (summary + experience +")
print(f" one competencies line), blocks fabrication, coverage {after_pct}%.")
print("=" * 70)
return 0
def _injected_fragments(tex: str) -> str:
"""Concatenate the lowercased text of every injected fragment: the fenced
blocks (summary sentence + competencies line) AND inline woven clauses."""
low = tex.lower()
parts = []
block_pat = re.escape(_INJECT_MARKER.lower()) + r".*?" + \
re.escape(_INJECT_END.lower())
parts += re.findall(block_pat, low, flags=re.DOTALL)
weave_pat = r" \(applying [^\n]*?\)" + re.escape(_INJECT_WEAVE.lower())
parts += re.findall(weave_pat, low)
return " ".join(parts)
if __name__ == "__main__":
sys.exit(main())