cvmatcher / src /main.py
Chirag20's picture
Initial deployment: job application intelligence agent
ec55f11
import os
import sys
from dotenv import load_dotenv
load_dotenv(override=True)
# pyrefly: ignore [missing-import]
from src.crew import build_crew
def validate_cv_path(cv_path: str) -> str:
"""Validate the CV file path exists and is a supported format."""
if not cv_path:
print("❌ CV path cannot be empty.")
sys.exit(1)
if not os.path.isfile(cv_path):
print(f"❌ File not found: {cv_path}")
sys.exit(1)
if not cv_path.lower().endswith((".pdf", ".docx")):
print(f"❌ Unsupported file format. Only .pdf and .docx are supported.")
sys.exit(1)
return cv_path
def validate_not_empty(value: str, field_name: str) -> str:
"""Ensure a required input is not empty."""
if not value:
print(f"❌ {field_name} cannot be empty.")
sys.exit(1)
return value
def validate_env():
missing = [k for k in ("GROQ_API_KEY", "SERPER_API_KEY") if not os.getenv(k)]
if missing:
for key in missing:
print(f"❌ Missing required environment variable: {key}")
print(" Set them in your .env file (see .env.example).")
sys.exit(1)
def run():
validate_env()
print("\nπŸš€ Job Application Intelligence Agent\n")
print("=" * 40)
cv_path = input("Enter path to your CV (PDF/DOCX): ").strip()
cv_path = validate_cv_path(cv_path)
jd_source = input("Enter LinkedIn job URL or paste JD text: ").strip()
jd_source = validate_not_empty(jd_source, "Job description source")
company_name = input("Enter company name: ").strip()
company_name = validate_not_empty(company_name, "Company name")
city = input("Enter company city: ").strip()
city = validate_not_empty(city, "City")
crew = build_crew(
cv_path=cv_path,
jd_source=jd_source,
company_name=company_name,
city=city
)
print("\n⏳ Starting analysis...\n")
try:
result = crew.kickoff()
print("\nβœ… Done! Here's your full report:\n")
# tasks order: cv(0), jd(1), company(2), gap(3), optimization(4), outreach(5)
sections = [
("πŸ“Š SKILL GAP ANALYSIS", 3),
("✏️ CV OPTIMIZATION SUGGESTIONS", 4),
("πŸ“¨ OUTREACH MESSAGES", 5),
]
outputs = result.tasks_output or []
for title, idx in sections:
if idx < len(outputs):
print("\n" + "=" * 60)
print(f" {title}")
print("=" * 60)
print(outputs[idx].raw)
return result
except Exception as e:
error_msg = str(e)
if "rate_limit" in error_msg.lower() or "ratelimit" in error_msg.lower():
print("\n" + "=" * 50)
print("β›” RATE LIMIT ERROR")
print("=" * 50)
print("The Groq API token limit was exceeded.")
print("Options:")
print(" 1. Wait 15-20 minutes and try again")
print(" 2. Upgrade to Groq Dev Tier at https://console.groq.com/settings/billing")
print(" 3. Paste the JD text directly instead of a URL (uses fewer tokens)")
print("=" * 50 + "\n")
else:
print("\n" + "=" * 50)
print("❌ PIPELINE ERROR")
print("=" * 50)
print(f"Something went wrong: {error_msg[:300]}")
print("=" * 50 + "\n")
return None
if __name__ == "__main__":
run()