JAA-ATS-Tool / config.py
saitejatirunagari's picture
Fix location/freshness filters and make ever-jobs URL configurable
39623f2
Raw
History Blame
11.1 kB
import os
from dotenv import load_dotenv
load_dotenv()
NVIDIA_BASE_URL = "https://integrate.api.nvidia.com/v1"
# ── Candidate contact ────────────────────────────────────────────────────────
# Resume-header address line. ATS checkers (Jobalytics / Resume Worded) flag a
# missing address; a "City, State, Country" line satisfies that check. Used as
# the fallback when the uploaded resume has no parseable location.
CONTACT_LOCATION = os.getenv(
"CONTACT_LOCATION", "Hyderabad, Telangana, India Β· Open to relocate"
)
# Primary key β€” GLM 5.1 (most reliable, used for resume parsing + customization)
NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY")
GLM_BASE_URL = NVIDIA_BASE_URL
GLM_MODEL = "z-ai/glm-5.1"
# ── 10 assessment models ────────────────────────────────────────────────────
# phase1 = used for quick keyword scoring (instant, no LLM needed now)
# phase2 = used for detailed LLM assessment of top jobs
ASSESSMENT_MODELS = [
# ─────────────────────────────────────────────────────────────────────
# Phase 2 (detailed LLM assessment) speed results from test run:
# Kimi-K2.6 ~5s/batch FAST βœ… phase2=True
# Step-3.7-Flash ~8-35s FAST βœ… phase2=True
# Qwen3.5-397b ~9s FAST βœ… phase2=True
# Qwen3.5-122b-v2 ~12s FAST βœ… phase2=True
# GPT-OSS-120b ~11s FAST βœ… phase2=True
# Qwen3.5-122b ~40s OK βœ… phase2=True
# DeepSeek-v4-Pro ~42s OK βœ… phase2=True
# DeepSeek-v4-Flash ~229s SLOW ⚠️ phase2=False (too slow)
# GLM-5.1 ~234s SLOW ⚠️ phase2=False (too slow)
# MiniMax-M2.7 ~908s BLOCKED❌ phase2=False
# ─────────────────────────────────────────────────────────────────────
{
"name": "Kimi-K2.6",
"model": "moonshotai/kimi-k2.6",
"api_key": os.getenv("NVIDIA_API_KEY_3"),
"base_url": NVIDIA_BASE_URL,
"extra_body": {},
"phase1": True,
"phase2": True, # ~5s per batch FAST
"tailor": True, # capable tier β€” strong at structured JSON rewrites
},
{
"name": "Step-3.7-Flash",
"model": "stepfun-ai/step-3.7-flash",
"api_key": os.getenv("NVIDIA_API_KEY_8"),
"base_url": NVIDIA_BASE_URL,
"extra_body": {},
"phase1": True,
"phase2": True, # ~8-35s FAST
"tailor": False, # weaker β€” drops roles / sparse bullets; not for tailoring
},
{
"name": "Qwen3.5-397b",
"model": "qwen/qwen3.5-397b-a17b",
"api_key": os.getenv("NVIDIA_API_KEY_7"),
"base_url": NVIDIA_BASE_URL,
"extra_body": {},
"phase1": True,
"phase2": True, # ~9s FAST
"tailor": True, # capable tier β€” large model, good instruction following
},
{
"name": "Qwen3.5-122b-v2",
"model": "qwen/qwen3.5-122b-a10b",
"api_key": os.getenv("NVIDIA_API_KEY_7"),
"base_url": NVIDIA_BASE_URL,
"extra_body": {},
"phase1": True,
"phase2": True, # ~12s FAST
"tailor": False, # smaller β€” keep for assessment, not tailoring
},
{
"name": "GPT-OSS-120b",
"model": "openai/gpt-oss-120b",
"api_key": os.getenv("NVIDIA_API_KEY_5"),
"base_url": NVIDIA_BASE_URL,
"extra_body": {},
"phase1": True,
"phase2": True, # ~11s FAST
"tailor": True, # capable tier
},
{
"name": "Qwen3.5-122b",
"model": "qwen/qwen3.5-122b-a10b",
"api_key": os.getenv("NVIDIA_API_KEY_4"),
"base_url": NVIDIA_BASE_URL,
"extra_body": {},
"phase1": True,
"phase2": True, # ~40s OK
"tailor": False, # smaller
},
{
"name": "DeepSeek-v4-Pro",
"model": "deepseek-ai/deepseek-v4-pro",
"api_key": os.getenv("NVIDIA_API_KEY_2"),
"base_url": NVIDIA_BASE_URL,
"extra_body": {"chat_template_kwargs": {"thinking": False}},
"phase1": True,
"phase2": True, # ~42s OK
"tailor": True, # capable tier β€” strong reasoning
},
{
"name": "DeepSeek-v4-Flash",
"model": "deepseek-ai/deepseek-v4-flash",
"api_key": os.getenv("NVIDIA_API_KEY_6"),
"base_url": NVIDIA_BASE_URL,
"extra_body": {"chat_template_kwargs": {"thinking": True, "reasoning_effort": "high"}},
"phase1": True,
"phase2": False, # ~229s too slow for phase2
},
{
"name": "GLM-5.1",
"model": "z-ai/glm-5.1",
"api_key": os.getenv("NVIDIA_API_KEY"),
"base_url": NVIDIA_BASE_URL,
"extra_body": {},
"phase1": True,
"phase2": False, # ~234s too slow for phase2; used for resume customization
},
{
"name": "MiniMax-M2.7",
"model": "minimaxai/minimax-m2.7",
"api_key": os.getenv("NVIDIA_API_KEY_2"),
"base_url": NVIDIA_BASE_URL,
"extra_body": {},
"phase1": True,
"phase2": False, # ~908s BLOCKED
},
]
# ── Google Sheets / Drive ──────────────────────────────────────────────────
GOOGLE = {
"sheet_id": os.getenv("GOOGLE_SHEET_ID", "1Ehxt3eortehbtySdtgcSrMhCqmxIMUAmvRqSkII0HJk"),
"sheet_tab": "Job Applications",
"drive_folder": "Job Automation Agent β€” Resumes",
"credentials_file": "google_credentials.json", # Service account JSON
"token_file": "google_token.json", # OAuth token (auto-created)
}
# ── Job Search β€” Product Manager roles ONLY ───────────────────────────────
JOB_SEARCH = {
"roles": [
"Product Manager",
"Senior Product Manager",
"AI Product Manager",
"Technical Product Manager",
"Product Owner",
],
# Required words in job title β€” scraper DROPS any job without these
"required_title_words": ["product"],
# Blocked words in title β€” scraper DROPS any job with these
"blocked_title_words": [
"teacher", "faculty", "engineer", "developer", "designer",
"accountant", "analyst", "consultant", "manager data",
"project manager", "program manager", "marketing manager",
"sales", "hr ", "recruiter", "finance", "legal",
"civil", "mechanical", "electrical", "hardware", "network",
"software engineer", "data engineer", "ml engineer",
],
"locations": ["India", "Bangalore", "Mumbai", "Delhi NCR", "Hyderabad", "Pune", "Remote"],
"experience_min": 2,
"experience_max": 15,
"max_jobs_per_platform": 30,
"days_posted": 7,
"primary_role": "Product Manager",
}
# Platforms (Naukri blocked by Akamai)
PLATFORMS = {
"linkedin": True,
"naukri": False,
"indeed": True,
"glassdoor": True,
}
# Assessment β€” process ALL PM jobs
ASSESSMENT = {
"min_score_for_llm_resume": 6, # LLM-tailored resume for score >= this
"generate_all_resumes": True, # Template resume for ALL PM-relevant jobs
"max_llm_resumes": 30, # Max LLM-tailored resumes per run
"process_all_jobs": True, # Assess ALL scraped jobs (not just top N)
"dedup_days": 30, # Skip jobs seen in last N days (0 = no dedup)
"test_mode": False, # Set True for quick test run
"test_jobs_limit": 10, # Max jobs in test mode
}
# ── Production LLM generation (provider abstraction + fallback chain) ──────────
# The pipeline is model-independent: any provider may generate content, but
# deterministic validation (schema + independent score + parse) decides if it
# passes. Providers are tried in order until one yields a READY (>=90) resume.
LLM_GENERATION = {
"provider_order": ["kimi", "nvidia_primary", "deterministic"],
"temperature": 0.1,
"max_retries": 2,
"require_json_schema": True,
"fallback_on_schema_error": True,
"fallback_on_low_independent_score": True,
"fallback_on_parse_failure": True,
}
# ── Resume automation policy (AUTO_AGGRESSIVE) ─────────────────────────────────
# Controls how aggressively plausible JD terms are auto-included and which
# resumes are downloadable, so a 100-job batch runs mostly hands-off.
AUTOMATION = {
"automation_mode": "auto_aggressive",
# What to do per risk severity (see candidate_fit.severity):
"review_policy": {
"LOW_RISK_AUTO_INCLUDED": "auto_include",
"MEDIUM_RISK_REVIEW_RECOMMENDED": "auto_include_with_review_flag",
"HIGH_RISK_NEEDS_CONFIRMATION": "ask_user",
"BLOCKED_DO_NOT_INCLUDE": "exclude",
},
# Which statuses are downloadable:
"download_policy": {
"READY_90_PLUS": "allow",
"READY_90_PLUS_REVIEW_RECOMMENDED": "allow_with_warning",
"WEAK_90_INTERNAL_ONLY": "block",
"NEEDS_REPAIR": "block",
"NEEDS_USER_INPUT": "block",
"NOT_ELIGIBLE_LOW_FIT": "block",
"PARSE_FAILED": "block",
},
}
# ── ever-jobs integration ─────────────────────────────────────────────────────
from src.ever_jobs_bridge.platforms import INDIA_DEFAULT_PLATFORMS
EVER_JOBS = {
# Point this at a HOSTED ever-jobs sidecar to enable the 150+ extra
# platforms on HF Spaces (where a local Node process can't run). Defaults
# to the local dev sidecar.
"api_url": os.getenv("EVER_JOBS_API_URL", "http://localhost:3001"),
"default_platforms": INDIA_DEFAULT_PLATFORMS,
"max_results": 25,
"request_timeout": 30,
"http_timeout": 120,
"max_platforms_per_call": 20,
}
# Output
OUTPUT = {
"excel_path": "data/output/reports/job_report.xlsx",
"resumes_dir": "data/output/resumes/",
"report_dir": "data/output/reports/",
}
# Resume
RESUME = {
"pdf_path": os.getenv("RESUME_PATH", "data/resume/resume.pdf"),
}