76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
"""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=func(data,resume_text,*args,**kwargs)
|
|
company=(company or "").strip()
|
|
if not company or company.lower()==NO_COMPANY.lower():
|
|
return NO_COMPANY,education
|
|
haystack=(resume_text or "").lower()
|
|
if company.lower() not in haystack:
|
|
return NO_COMPANY,education
|
|
return company,education
|
|
|
|
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=func(data,resume_text,*args,**kwargs)
|
|
education=(education or "").strip()
|
|
if not education or education.lower()==EDUCATION.lower():
|
|
return company,EDUCATION
|
|
haystack=(resume_text or "").lower()
|
|
if education.lower() not in haystack:
|
|
return company,EDUCATION
|
|
return company,education
|
|
|
|
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")
|
|
if not isinstance(current,str):
|
|
current=""
|
|
if not isinstance(education,str):
|
|
education=""
|
|
return current.strip(),education.strip()
|