"""Employment response decorators for `parse_employment_response`. Pure module: no FastAPI imports, no HTTPException, and no module-level state. Mirrors job/candidate/decorators.py — stacked wrappers that clean LLM output before the task persists it: raw JSON -> require_json_object -> clamp_company_to_resume -> clamp_education_to_resume -> parse_employment_response """ from __future__ import annotations from functools import wraps from employment_agent.prompt import EDUCATION,NO_COMPANY def require_json_object(func): """Reject non-dict LLM payloads before field parsing runs.""" @wraps(func) def wrapper(data,resume_text="",*args,**kwargs): if not isinstance(data,dict): raise RuntimeError(f"model did not return a JSON object: {data!r}") return func(data,resume_text,*args,**kwargs) return wrapper def clamp_company_to_resume(func): """Keep company only when it appears in resume_text; else NO_COMPANY.""" @wraps(func) def wrapper(data,resume_text="",*args,**kwargs): company,education,current_title=func(data,resume_text,*args,**kwargs) company=(company or "").strip() if not company or company.lower()==NO_COMPANY.lower(): return NO_COMPANY,education,current_title haystack=(resume_text or "").lower() if company.lower() not in haystack: return NO_COMPANY,education,current_title return company,education,current_title return wrapper def clamp_education_to_resume(func): """Keep education only when it appears in resume_text; else EDUCATION.""" @wraps(func) def wrapper(data,resume_text="",*args,**kwargs): company,education,current_title=func(data,resume_text,*args,**kwargs) education=(education or "").strip() if not education or education.lower()==EDUCATION.lower(): return company,EDUCATION,current_title haystack=(resume_text or "").lower() if education.lower() not in haystack: return company,EDUCATION,current_title return company,education,current_title return wrapper @require_json_object @clamp_company_to_resume @clamp_education_to_resume def parse_employment_response(data,resume_text:str="") -> tuple[str,str]: """Pull company + education from LLM JSON; decorators clamp to the resume.""" current=data.get("current_employment") education=data.get("education") current_title=data.get("current_title") if not isinstance(current,str): current="" if not isinstance(education,str): education="" if not isinstance(current_title,str): current_title="" return current.strip(),education.strip(),current_title.strip()