Add AI Resume Upload & Automatic Parsing feature

This commit is contained in:
2026-07-22 17:12:51 -04:00
parent 735b0eb298
commit 9ccaec0e7c
3 changed files with 177 additions and 9 deletions
+99
View File
@@ -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 {