Add AI Resume Upload & Automatic Parsing feature
This commit is contained in:
@@ -223,6 +223,105 @@ Extract the following fields accurately and output JSON:
|
||||
}
|
||||
});
|
||||
|
||||
// API 2B: Parse Master Resume Text / Document into User Profile
|
||||
app.post("/api/gemini/parse-resume", async (req, res) => {
|
||||
try {
|
||||
const { resumeText, llmConfig } = req.body;
|
||||
|
||||
if (!resumeText || typeof resumeText !== 'string' || resumeText.trim().length === 0) {
|
||||
return res.status(400).json({ success: false, error: "Resume text content is required." });
|
||||
}
|
||||
|
||||
const prompt = `Analyze the following resume text and extract all candidate information into structured JSON:
|
||||
|
||||
Resume Content:
|
||||
${resumeText}
|
||||
|
||||
Extract the following fields accurately:
|
||||
- fullName: Full Name of candidate
|
||||
- email: Email address
|
||||
- phone: Phone number
|
||||
- location: City, State or location preference
|
||||
- linkedinUrl: LinkedIn profile URL if found
|
||||
- githubUrl: GitHub profile URL if found
|
||||
- portfolioUrl: Personal website/portfolio URL if found
|
||||
- summary: Professional summary or objective paragraph
|
||||
- skills: Array of technical & soft skills
|
||||
- experience: Array of work experience objects, each with:
|
||||
- id: unique string
|
||||
- title: Job title
|
||||
- company: Company name
|
||||
- period: Date range (e.g. 2022 - Present)
|
||||
- bullets: Array of accomplishment bullet points
|
||||
- education: Array of education objects, each with:
|
||||
- degree: Degree title
|
||||
- institution: School or University name
|
||||
- year: Graduation year`;
|
||||
|
||||
const schema = {
|
||||
type: Type.OBJECT,
|
||||
properties: {
|
||||
fullName: { type: Type.STRING },
|
||||
email: { type: Type.STRING },
|
||||
phone: { type: Type.STRING },
|
||||
location: { type: Type.STRING },
|
||||
linkedinUrl: { type: Type.STRING },
|
||||
githubUrl: { type: Type.STRING },
|
||||
portfolioUrl: { type: Type.STRING },
|
||||
summary: { type: Type.STRING },
|
||||
skills: {
|
||||
type: Type.ARRAY,
|
||||
items: { type: Type.STRING }
|
||||
},
|
||||
experience: {
|
||||
type: Type.ARRAY,
|
||||
items: {
|
||||
type: Type.OBJECT,
|
||||
properties: {
|
||||
id: { type: Type.STRING },
|
||||
title: { type: Type.STRING },
|
||||
company: { type: Type.STRING },
|
||||
period: { type: Type.STRING },
|
||||
bullets: {
|
||||
type: Type.ARRAY,
|
||||
items: { type: Type.STRING }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
education: {
|
||||
type: Type.ARRAY,
|
||||
items: {
|
||||
type: Type.OBJECT,
|
||||
properties: {
|
||||
degree: { type: Type.STRING },
|
||||
institution: { type: Type.STRING },
|
||||
year: { type: Type.STRING }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["fullName", "skills", "experience"]
|
||||
};
|
||||
|
||||
const { text, providerUsed } = await generateTextWithLLMFallback(prompt, schema, llmConfig);
|
||||
const parsedProfile = JSON.parse(text || "{}");
|
||||
|
||||
// Ensure IDs on experience items
|
||||
if (Array.isArray(parsedProfile.experience)) {
|
||||
parsedProfile.experience = parsedProfile.experience.map((exp: any, index: number) => ({
|
||||
...exp,
|
||||
id: exp.id || `exp_parsed_${Date.now()}_${index}`
|
||||
}));
|
||||
}
|
||||
|
||||
res.json({ success: true, profile: parsedProfile, providerUsed });
|
||||
} catch (error: any) {
|
||||
console.error("Error parsing resume:", error);
|
||||
res.status(500).json({ success: false, error: error.message || "Failed to parse resume" });
|
||||
}
|
||||
});
|
||||
|
||||
// API 3: Tailor Resume & Calculate Match Score
|
||||
app.post("/api/gemini/tailor-resume", async (req, res) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user