On-demand Scout service (replaces the nightly aggregator): - FastAPI agent-mesh service; card name "matchmaking-service" (HTTP /a2a/tasks + Redis-stream worker matching the sibling-service pattern) - run_search: parallel multi-board sweep (Naukri/blackfalcondata + Foundit + LinkedIn), city-filtered at the board, cheapest-per-result first - per-board adapters + normalizers -> ScoutJob with rich read-only details and offsite apply links; recall mode + MVQ guard - result cache for dev replay ($0); per-board cost knobs; retry-on-5xx - research/: engine design, board cost economics, India-actor shortlist, POC
52 lines
2.3 KiB
Python
52 lines
2.3 KiB
Python
"""
|
|
candidate.py — the board-agnostic candidate model (used by every adapter).
|
|
|
|
= parsed résumé (source of truth) + confirmed facts not on the résumé (the "answer bank") +
|
|
the rule for writing a tailored motivation. This never changes per board — it's the user's data.
|
|
Grow CONFIRMED over time as new screening questions get answered (the answer-bank flywheel).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import date, timedelta
|
|
|
|
from parse_resume import parse_resume_to_profile
|
|
from browser_config import RESUME_PATH
|
|
|
|
# Durable answers that aren't on a résumé. Extend as new questions recur.
|
|
CONFIRMED = {
|
|
"preferred_email": "rahul.gupta2608@yahoo.com",
|
|
"citizenship": "Indian citizen, based in India",
|
|
"work_auth": ("Authorized to work in India. NOT authorized to work in the US; would require "
|
|
"visa sponsorship for any US-based role."),
|
|
"availability": "Available to start soon (within ~2 weeks).",
|
|
}
|
|
|
|
|
|
async def build_candidate(pdf_path: str = RESUME_PATH) -> dict:
|
|
profile = await parse_resume_to_profile(pdf_path)
|
|
return {"profile": profile, "facts": CONFIRMED, "resume_path": pdf_path}
|
|
|
|
|
|
def candidate_prompt_block(cand: dict) -> str:
|
|
today = date.today()
|
|
soon = today + timedelta(days=14)
|
|
f = cand["facts"]
|
|
return f"""CANDIDATE PROFILE (parsed from résumé — source of truth):
|
|
{json.dumps(cand["profile"], indent=2, ensure_ascii=False)}
|
|
|
|
CONFIRMED FACTS (override any résumé guess):
|
|
- Preferred contact email: {f["preferred_email"]}
|
|
- {f["citizenship"]}. {f["work_auth"]}
|
|
* "authorized to work where the job is located?" -> YES if the role is in India, NO if US.
|
|
* "require visa sponsorship?" -> NO if India role, YES if US role.
|
|
Honesty over optimism — never overstate authorization.
|
|
- {f["availability"]} Today is {today:%Y-%m-%d}; for a start-date picker choose a FUTURE date near
|
|
{soon:%Y-%m-%d}, never a past date.
|
|
- MOTIVATION: for any "why this role" / "about you" / "additional information" free-text field,
|
|
write a genuine 2-4 sentence answer TAILORED to THIS specific job (use its title / company /
|
|
description visible on screen) grounded in the candidate's real background above. NEVER leave it
|
|
blank — a blank motivation makes a weak application.
|
|
|
|
Résumé file to upload when asked: {cand["resume_path"]}"""
|