Spaces:
Sleeping
Sleeping
| import asyncio | |
| import os, sys | |
| import time | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| from typing import AsyncIterable | |
| from typing import List, Optional, Dict | |
| from langchain.schema import HumanMessage, AIMessage | |
| from langchain.callbacks import AsyncIteratorCallbackHandler | |
| # from fastapi.middleware.cors import CORSMiddleware | |
| # from fastapi.responses import StreamingResponse | |
| # from langchain.chat_models import ChatOpenAI | |
| # from pydantic import BaseModel | |
| # from langchain_google_genai import ChatGoogleGenerativeAI | |
| from models.data_model import OutProfile, OutMatching | |
| from services.llms.LLM import model_gemini, model_4o, model_4omini, model_5mini | |
| from services.agents.LLMAgent import LLMAgent | |
| from services.prompts.profile_matching import match_one_profile | |
| from utils.utils import pdf_reader, ingest_one_profile, ingest_bulk_profile | |
| async def helper_profile_match_prompt(profile: OutProfile) -> str: | |
| profile.high_edu_major_1 = profile.high_edu_major_1 if profile.high_edu_major_1 else "-" | |
| profile.high_edu_univ_1 = profile.high_edu_univ_1 if profile.high_edu_univ_1 else "-" | |
| profile.high_edu_gpa_1 = profile.high_edu_gpa_1 if profile.high_edu_gpa_1 else "-" | |
| profile.high_edu_major_2 = profile.high_edu_major_2 if profile.high_edu_major_2 else "-" | |
| profile.high_edu_univ_2 = profile.high_edu_univ_2 if profile.high_edu_univ_2 else "-" | |
| profile.high_edu_gpa_2 = profile.high_edu_gpa_2 if profile.high_edu_gpa_2 else "-" | |
| profile.high_edu_major_3 = profile.high_edu_major_3 if profile.high_edu_major_3 else "-" | |
| profile.high_edu_univ_3 = profile.high_edu_univ_3 if profile.high_edu_univ_3 else "-" | |
| profile.high_edu_gpa_3 = profile.high_edu_gpa_3 if profile.high_edu_gpa_3 else "-" | |
| profile.hardskills = profile.hardskills if profile.hardskills else [] | |
| profile.softskills = profile.softskills if profile.softskills else [] | |
| profile.certifications = profile.certifications if profile.certifications else [] | |
| profile.business_domain_experiences = profile.business_domain_experiences if profile.business_domain_experiences else [] | |
| profile_text = f""" | |
| # Candidate Profile | |
| **Education** | |
| - Bachelor degree major: {profile.high_edu_major_1 or "-"} | |
| - Bachelor university: {profile.high_edu_univ_1 or "-"} | |
| - Bachelor GPA: {profile.high_edu_gpa_1 or "-"} | |
| - Master degree major: {profile.high_edu_major_2 or "-"} | |
| - Master university: {profile.high_edu_univ_2 or "-"} | |
| - Master GPA: {profile.high_edu_gpa_2 or "-"} | |
| - Doctor/Phd degree major: {profile.high_edu_major_3 or "-"} | |
| - Doctor/Phd university: {profile.high_edu_univ_3 or "-"} | |
| - Doctor/Phd GPA: {profile.high_edu_gpa_3 or "-"} | |
| **Year of Working Experience** | |
| {profile.yoe} year | |
| **Hardskills** | |
| {", ".join([p for p in profile.hardskills if p not in ['null', '']])} | |
| **Softskills** | |
| {", ".join([p for p in profile.softskills if p not in ['null', '']])} | |
| **Certifications** | |
| {", ".join([p for p in profile.certifications if p not in ['null', '']])} | |
| **Business domain experiences** | |
| {", ".join([p for p in profile.business_domain_experiences if p not in ['null', '']])} | |
| """.strip() | |
| return profile_text | |
| class ProfileMatchingAgent(LLMAgent): | |
| def __init__(self, model=model_5mini): | |
| super().__init__(model) | |
| self.agent_name = "ProfileMatchingAgent" | |
| self.prompt_template = match_one_profile | |
| async def _helper_matching_one(self, profile_id:int, profile_dict:Dict, profile_text:str, criteria:str) -> Dict: | |
| llm = self.model.with_structured_output(OutMatching) | |
| chain = self.prompt_template | llm | |
| input_chain = { | |
| "profile_text":profile_text, | |
| "criteria":criteria, | |
| } | |
| matched_profile = await chain.ainvoke(input_chain, config=None) | |
| print(f"_helper_matching_one/matched_profile:\n{matched_profile}") | |
| # matched_profile = matched_profile.model_dump() | |
| # matched_profile["profile_id"] = profile_id | |
| profile_dict["score"] = matched_profile.score | |
| profile_dict["reason"] = matched_profile.reason | |
| profile_dict["profile_id"] = profile_id | |
| return profile_dict | |
| async def matching_profile_bulk(self, profiles:List[OutProfile], criteria:str) -> Optional[List]: | |
| try: | |
| st = time.time() | |
| tasks = [] | |
| profiles_text = [] | |
| profiles_dict = [] | |
| n_profiles = len(profiles) | |
| for profile_id, p in enumerate(profiles): | |
| print(f"Processing [{profile_id+1}/{n_profiles}]") | |
| profile_text = await helper_profile_match_prompt(p) | |
| profile_dict = p.model_dump() | |
| profiles_dict.append(profile_dict) | |
| profiles_text.append(profile_text) | |
| task = asyncio.create_task(self._helper_matching_one(profile_id=profile_id, | |
| profile_dict=profile_dict, | |
| profile_text=profile_text, | |
| criteria=criteria)) | |
| tasks.append(task) | |
| profiles_with_scores = await asyncio.gather(*tasks) | |
| print(f"✅ Profile matching finished in {(time.time() - st)//60} min, {(time.time() - st)%60} sec") | |
| return profiles_with_scores | |
| except Exception as E: | |
| error_message = f"Processing profile matching error: {E}" | |
| print(error_message) | |
| exc_type, exc_obj, exc_tb = sys.exc_info() | |
| fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] | |
| print(exc_type, fname, exc_tb.tb_lineno) | |