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()