89 lines
2.5 KiB
Python
89 lines
2.5 KiB
Python
"""Employment extraction entrypoint — llm_setup.llm_call only.
|
|
|
|
Pure module: no FastAPI imports and no HTTPException.
|
|
Called from inbox.tasks.match_inbox_message; no HTTP surface.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
|
|
from employment_agent.decorators import parse_employment_response
|
|
from employment_agent.prompt import CURRENT_TITLE,EDUCATION,NO_CITY,NO_COMPANY,city_list_prompt,prompt,user_prompt
|
|
from llm_setup import llm_call
|
|
|
|
logger=logging.getLogger("employment_agent")
|
|
|
|
|
|
def parse_normalized_cities(data,fallback=None):
|
|
"""Keep unique proper city names from the list-normalizer JSON."""
|
|
rows=None
|
|
if isinstance(data,dict):
|
|
rows=data.get("cities")
|
|
if not isinstance(rows,list):
|
|
return list(fallback or [])
|
|
out=[]
|
|
seen=set()
|
|
for item in rows:
|
|
if not isinstance(item,str):
|
|
continue
|
|
text=item.strip()
|
|
if not text or text.lower() in (NO_CITY.lower(),"none","null","n/a","-"):
|
|
continue
|
|
key=text.lower()
|
|
if key in seen:
|
|
continue
|
|
seen.add(key)
|
|
out.append(text)
|
|
out.sort(key=str.lower)
|
|
return out or list(fallback or [])
|
|
|
|
|
|
async def normalize_cities(values):
|
|
"""OpenAI: messy stored places → the same proper city names the CV agent writes."""
|
|
places=[]
|
|
seen=set()
|
|
for raw in values or []:
|
|
text=(raw or "").strip()
|
|
if not text:
|
|
continue
|
|
key=text.lower()
|
|
if key in seen:
|
|
continue
|
|
seen.add(key)
|
|
places.append(text)
|
|
if not places:
|
|
return []
|
|
try:
|
|
data=await llm_call(
|
|
city_list_prompt(),
|
|
json.dumps({"places":places},ensure_ascii=False),
|
|
json_mode=True,
|
|
)
|
|
except Exception:
|
|
logger.exception("city list normalize failed")
|
|
return places
|
|
return parse_normalized_cities(data,fallback=places)
|
|
|
|
|
|
async def run_employment_agent(*,resume_text=""):
|
|
text=(resume_text or "").strip()
|
|
if not text:
|
|
return {
|
|
"current_employment":NO_COMPANY,
|
|
"education":EDUCATION,
|
|
"current_title":CURRENT_TITLE,
|
|
"linkedin_url":None,
|
|
"phone":None,
|
|
"city":None,
|
|
"skills":[],
|
|
"years_experience":None,
|
|
}
|
|
try:
|
|
data=await llm_call(prompt(),user_prompt(text),json_mode=True)
|
|
return parse_employment_response(data,text)
|
|
except Exception as e:
|
|
logger.exception("employment llm_call failed")
|
|
raise RuntimeError(str(e)) from e
|