JAA-ATS-Tool / scripts /run_20_job_validation.py
saitejatirunagari's picture
Add model-independent LLM provider abstraction with schema validation
9bf4a3d
Raw
History Blame
3.84 kB
"""
20-job live validation workflow (spec #10).
Selects N jobs, generates resumes through the production provider FALLBACK CHAIN,
exports files, re-parses them, scores internal + independent, prints a batch
table, and exports a validation package per job.
Usage:
PYTHONPATH=. python scripts/run_20_job_validation.py # 20 JD fixtures (offline)
PYTHONPATH=. python scripts/run_20_job_validation.py --n 20 --live # scrape real jobs
PYTHONPATH=. python scripts/run_20_job_validation.py --no-packages
"""
import os
import sys
import io
import json
import argparse
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
if hasattr(sys.stdout, "buffer"):
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
from src.validation_runner import run_validation_batch, select_jobs
from src.provider_eval import load_base_resume
from src.llm_client import LLMClient
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--n", type=int, default=20)
ap.add_argument("--live", action="store_true", help="scrape real jobs (needs network)")
ap.add_argument("--no-packages", action="store_true")
args = ap.parse_args()
base = load_base_resume()
if base is None:
print("ERROR: no base resume (data/resume/_parsed.json or resume.pdf).")
sys.exit(1)
llm = LLMClient.__new__(LLMClient)
jobs = select_jobs(n=args.n, live=args.live, llm=llm)
if not jobs:
print("ERROR: could not select any jobs.")
sys.exit(1)
print(f"Selected {len(jobs)} jobs ({'live' if args.live else 'fixtures'}).")
def _cb(done, total, row):
print(f" [{done}/{total}] {row['company'][:24]:<24} "
f"{row['provider_used'][:12]:<12} {row['status'][:22]:<22} "
f"int={row['internal_score']} ind={row['independent_score']} "
f"dl={'Y' if row['download_allowed'] else 'n'}")
res = run_validation_batch(jobs, base_resume=base, llm=llm,
build_packages=not args.no_packages, progress_cb=_cb)
if res.get("error"):
print("ERROR:", res["error"])
sys.exit(1)
rows, summary = res["rows"], res["summary"]
print("\n=== BATCH TABLE ===")
print(f"{'Job Title':<28} {'Company':<18} {'Platform':<10} {'Provider':<12} "
f"{'Status':<22} {'Int':>4} {'Ind':>4} {'Read':>4} {'Risk':<6} {'Rev':>3} "
f"{'Rep':>3} {'DL':>3}")
for r in rows:
print(f"{r['job_title'][:28]:<28} {r['company'][:18]:<18} {r['platform'][:10]:<10} "
f"{r['provider_used'][:12]:<12} {r['status'][:22]:<22} "
f"{r['internal_score']:>4} {r['independent_score']:>4} {r['ats_readability']:>4} "
f"{r['risk_level']:<6} {r['review_terms']:>3} {r['repair_attempts']:>3} "
f"{'Y' if r['download_allowed'] else 'n':>3}")
print(f"\nChain: {summary['chain']}")
print(f"Ready/downloadable: {summary['ready']}/{summary['total']} "
f"({summary['ready_rate']}%) | needs_user_input: {summary['needs_user_input']} "
f"| blocked: {summary['blocked']}")
print(f"Output: {summary['output_dir']}")
if res.get("packages"):
print(f"Validation packages: {len(res['packages'])} -> data/output/validation/")
out_json = os.path.join(summary["output_dir"], "batch_results.json")
with open(out_json, "w", encoding="utf-8") as f:
json.dump(res, f, ensure_ascii=False, indent=2, default=str)
print(f"Saved -> {out_json}")
if "stub" in summary["chain"] and "->" not in summary["chain"]:
print("\n*** NOTE: ran with StubProvider only (no provider keys). This validates")
print("*** the workflow, NOT production quality. Re-run with Kimi/NVIDIA keys. ***")
if __name__ == "__main__":
main()