File size: 3,843 Bytes
9bf4a3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"""
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()