Spaces:
Sleeping
Sleeping
| """ | |
| Simulate the WORST production case: LLM returns only 2 roles (drops BYJU's | |
| and ML Edutech), no recruiter pitch, 3 bullets per role. Verify the v4 | |
| backfill + pitch enforcement + weaving brings it to 90%+. | |
| """ | |
| import os, sys, io, shutil | |
| sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') | |
| src_pdf = r'C:\Users\Nxtwave\Desktop\resume\Saiteja_Tirunagari_Resume A 26 - Copy.pdf' | |
| dst_pdf = 'data/resume/resume.pdf' | |
| os.makedirs(os.path.dirname(dst_pdf), exist_ok=True) | |
| if not os.path.exists(dst_pdf): | |
| shutil.copyfile(src_pdf, dst_pdf) | |
| # Monkey-patch the LLM call to return a deliberately weak v4 response | |
| from src.resume_customizer import ResumeCustomizer, _read_docx_text | |
| from src.llm_client import LLMClient | |
| from src.ats_scorer import score_resume | |
| def make_weak_response(resume_dict, job_title, company): | |
| """Mimic worst-case LLM: 2 roles only, no pitch, 3 bullets each.""" | |
| return { | |
| "name": resume_dict["name"], | |
| "contact": resume_dict["contact"], | |
| # NO recruiter pitch — generic 2-sentence summary | |
| "summary": ( | |
| "Product Manager with 5+ years of experience driving product development " | |
| "in fast-paced startups. Built funnel optimization and AI-powered features " | |
| "that scaled to 140,000+ users with measurable business impact." | |
| ), | |
| "roles": [ | |
| # Only 2 of the 4 original roles — LLM dropped older ones | |
| { | |
| "title": resume_dict["roles"][0]["title"], | |
| "company": resume_dict["roles"][0]["company"], | |
| "location": resume_dict["roles"][0]["location"], | |
| "dates": resume_dict["roles"][0]["dates"], | |
| "bullets": [ | |
| "Led NIAT Application Portal revamp scaling to 141,269 verified leads", | |
| "Drove payment conversion lift of +35.87 percentage points", | |
| "Built AI chatbot generating 6,776 leads", | |
| ], | |
| }, | |
| { | |
| "title": resume_dict["roles"][1]["title"], | |
| "company": resume_dict["roles"][1]["company"], | |
| "location": resume_dict["roles"][1]["location"], | |
| "dates": resume_dict["roles"][1]["dates"], | |
| "bullets": [ | |
| "Managed 20 customer-success specialists covering 40,000 customers", | |
| "Maintained refund rate below 5% and satisfaction above 95%", | |
| ], | |
| }, | |
| ], | |
| "achievements": [ | |
| "Scaled to 141,269 verified leads", | |
| "Lifted payment conversion +35.87 pp", | |
| ], | |
| "education": resume_dict["education"], | |
| } | |
| # Patch LLMClient.tailor_resume_v4 to return our weak response | |
| _original_v4 = LLMClient.tailor_resume_v4 | |
| def _mocked_v4(self, cfg, resume_dict, jd_text, job_title, company, assessment): | |
| return make_weak_response(resume_dict, job_title, company) | |
| LLMClient.tailor_resume_v4 = _mocked_v4 | |
| from src.llm_client import LLMClient as LC | |
| rc = ResumeCustomizer.__new__(ResumeCustomizer) | |
| rc.llm = LC.__new__(LC) | |
| rc.resume_text = '' | |
| rc.output_dir = 'data/output/resumes/_v4_backfill_test' | |
| os.makedirs(rc.output_dir, exist_ok=True) | |
| rc.fast_model_cfg = {"model": "fake", "api_key": "fake", "base_url": "https://fake"} | |
| rc._pending_summary_inject = [] | |
| print('━' * 78) | |
| print(f'{"JD":<48} {"v4_only":>9} {"FinalATS":>9} {"Roles":>6} {"Words":>6}') | |
| print('━' * 78) | |
| jd_files = { | |
| 'airtel_pm': ('Product Manager', 'Airtel'), | |
| 'sumo_logic_pm': ('Product Manager', 'Sumo Logic'), | |
| 'edgeverve_pm': ('Product Manager', 'EdgeVerve'), | |
| 'aditya_birla_apm': ('Associate Product Manager', 'Aditya Birla Capital'), | |
| } | |
| for jd_file, (role_title, company) in jd_files.items(): | |
| with open(f'tests/fixtures/jds/{jd_file}.txt', encoding='utf-8') as f: | |
| jd = f.read() | |
| job = { | |
| "title": role_title, | |
| "company": company, | |
| "description": jd, | |
| "_raw_assessment": {"ats_keywords": []}, | |
| } | |
| filepath = os.path.join(rc.output_dir, f'{company.replace(" ", "_")}_{jd_file}.docx') | |
| # Call the actual v4 flow | |
| result = rc._generate_resume_v4(job, cfg=rc.fast_model_cfg, filepath=filepath) | |
| if not result: | |
| print(f'{(role_title + " @ " + company):<48} v4 FAILED') | |
| continue | |
| text = _read_docx_text(result) | |
| r = score_resume(text, jd) | |
| # Count roles + words in the actual generated DOCX | |
| from docx import Document | |
| doc = Document(result) | |
| role_lines = sum( | |
| 1 for p in doc.paragraphs | |
| if p.text and any(co in p.text for co in ["NxtWave", "Think & Learn", "ML Edutech", "BYJU"]) | |
| ) | |
| print(f'{(role_title + " @ " + company):<48} {"weak":>9} {r["ats_score"]:>8} {role_lines:>4} {r["word_count"]:>5}') | |
| print('━' * 78) | |
| print('\nThis test simulates the WORST case: LLM returns 2 roles only, no') | |
| print('pitch, 3 bullets each. v4 backfill + pitch enforcement + weaving') | |
| print('should still produce 90%+ resumes with all 4 candidate roles preserved.') | |