| import os
|
| import json
|
| import time
|
| import numpy as np
|
| import faiss
|
| import polars as pl
|
| from pathlib import Path
|
| from sentence_transformers import SentenceTransformer
|
| from sklearn.feature_extraction.text import TfidfVectorizer
|
|
|
| from src.hard_filter import apply_hard_filter, is_killed
|
| from src.score_career import compute_A, compute_keyword_max
|
| from src.score_skills import compute_B
|
| from src.score_embed import compute_C_all
|
| from src.availability import apply_multipliers
|
| from src.output import write_submission, generate_reasoning
|
| from src.precompute import build_candidate_text, build_jd_text
|
|
|
| def run_fast_pipeline():
|
| start_time = time.perf_counter()
|
|
|
| print("=" * 60)
|
| print("RUNNING HIGH-PERFORMANCE PIPELINE ON 100,000 CANDIDATES")
|
| print("=" * 60)
|
|
|
|
|
| jd_path = Path("data/job_description.json")
|
| candidates_path = Path("data/candidates.jsonl")
|
| output_path = Path("submission.csv")
|
|
|
| if not jd_path.exists():
|
| raise FileNotFoundError(f"Job description not found at {jd_path}")
|
| if not candidates_path.exists():
|
| raise FileNotFoundError(f"Candidates dataset not found at {candidates_path}")
|
|
|
|
|
| print("Loading Job Description...")
|
| with open(jd_path, "r", encoding="utf-8") as f:
|
| jd = json.load(f)
|
|
|
|
|
| print("Pass 1: Streaming 100,000 candidate titles for TF-IDF fit...")
|
| titles = []
|
| with open(candidates_path, "r", encoding="utf-8") as f:
|
| for line in f:
|
| line = line.strip()
|
| if not line:
|
| continue
|
| cand = json.loads(line)
|
| title = cand.get("profile", {}).get("current_title") or cand.get("current_title") or ""
|
| titles.append(title)
|
|
|
| total_count = len(titles)
|
| print(f" Collected {total_count} titles.")
|
|
|
|
|
| print("Fitting TF-IDF Vectorizer on all candidate titles...")
|
| tfidf = TfidfVectorizer(max_features=30000, ngram_range=(1, 2))
|
| tfidf.fit(titles)
|
| del titles
|
|
|
|
|
| print("\n--- STAGE 1: Applying Hard Filters (streaming pass 2) ---")
|
| survivors = []
|
| killed_count = 0
|
| with open(candidates_path, "r", encoding="utf-8") as f:
|
| for line in f:
|
| line = line.strip()
|
| if not line:
|
| continue
|
| cand = json.loads(line)
|
| killed_flag, reason = is_killed(cand, jd, tfidf)
|
| if killed_flag:
|
| killed_count += 1
|
| else:
|
| survivors.append(cand)
|
|
|
| print(f"Killed: {killed_count} candidates.")
|
| print(f"Surviving: {len(survivors)} candidates.")
|
|
|
| if not survivors:
|
| print("No candidates survived. Exiting.")
|
| return
|
|
|
|
|
| print("\n--- STAGE 2: Embedding Survivors & Job Description ---")
|
| print("Loading SentenceTransformer model 'all-MiniLM-L6-v2'...")
|
| model = SentenceTransformer("all-MiniLM-L6-v2")
|
|
|
| print("Embedding Job Description...")
|
| jd_text = build_jd_text(jd)
|
| jd_vec = model.encode(jd_text, normalize_embeddings=True).astype("float32")
|
|
|
| print(f"Embedding {len(survivors)} surviving candidates...")
|
| survivor_texts = [build_candidate_text(s) for s in survivors]
|
| cand_vecs = model.encode(survivor_texts, batch_size=256, normalize_embeddings=True).astype("float32")
|
|
|
|
|
| print("Computing embedding similarities (Stage 2C)...")
|
| C_scores = compute_C_all(jd_vec, cand_vecs)
|
| C_map = {str(s.get("candidate_id") or s.get("id")): float(score) for s, score in zip(survivors, C_scores)}
|
|
|
|
|
| print("Fitting TF-IDF Vectorizer on survivor texts...")
|
| tfidf_surv = TfidfVectorizer(max_features=30000, ngram_range=(1, 2))
|
| tfidf_surv.fit(survivor_texts)
|
|
|
|
|
| print("Computing career and skill scores...")
|
| keyword_max = compute_keyword_max(survivors, jd, tfidf_surv)
|
|
|
| raw_scored = []
|
| for cand in survivors:
|
| cand_id = str(cand.get("candidate_id") or cand.get("id"))
|
| A_res = compute_A(cand, jd, tfidf_surv, keyword_max)
|
| B_res = compute_B(cand, jd)
|
| C = C_map.get(cand_id, 0.0)
|
|
|
| A = A_res["A"]
|
| B = B_res["B"]
|
| raw_score = round(0.40 * A + 0.35 * B + 0.25 * C, 4)
|
|
|
| raw_scored.append({
|
| "candidate_id": cand_id,
|
| "candidate": cand,
|
| "A": A,
|
| "title_sim": A_res["title_sim"],
|
| "industry_match": A_res["industry_match"],
|
| "prod_keyword_density": A_res["prod_keyword_density"],
|
| "yoe_score": A_res["yoe_score"],
|
| "B": B,
|
| "must_have_coverage": B_res["must_have_coverage"],
|
| "nice_coverage": B_res["nice_coverage"],
|
| "cert_bonus": B_res["cert_bonus"],
|
| "C": C,
|
| "raw_score": raw_score
|
| })
|
|
|
|
|
| print("\n--- STAGE 3: Applying Behavioral Multipliers ---")
|
| final_scored = apply_multipliers(raw_scored, jd)
|
|
|
|
|
| print("\n--- STAGE 4: Generating Output ---")
|
| df_final = write_submission(final_scored, jd, str(output_path))
|
|
|
| elapsed_time = time.perf_counter() - start_time
|
| print("=" * 60)
|
| print(f"PIPELINE EXECUTED IN {elapsed_time:.2f} SECONDS")
|
| print("=" * 60)
|
|
|
| print("\nTOP 10 CANDIDATES:")
|
| print(df_final.head(10))
|
|
|
| if __name__ == "__main__":
|
| run_fast_pipeline()
|
|
|