An AI-powered platform for comparing resumes against job descriptions, providing actionable insights and skill gap analysis.

Resume Diff AI is a full-stack application that leverages OpenAI's language models to analyze and compare resumes with job descriptions. The backend, built with FastAPI, processes uploaded files and text, extracts relevant information, and interacts with OpenAI to generate a detailed comparison. The frontend, a mobile-first React SPA, allows users to upload resumes and job descriptions, view match percentages, skill gaps, and export results. The system features robust validation, error handling, and a modern, accessible UI.
Ensuring accurate skill extraction and matching from varied resume and JD formats
Used OpenAI prompt engineering to enforce strict JSON output and reliable skill matching
OpenAI Comparison Prompt Template
COMPARISON_PROMPT_TEMPLATE = "You are a strict JSON generator. Compare the following Job Description (JD) text and Resume text, and output a single JSON object with exactly these keys: matchPercent, matchedSkills, missingSkills, highlights (optional), warnings (optional). The JSON must be the only content in your response. matchPercent must be an integer from 0 to 100. matchedSkills and missingSkills must be arrays of strings. highlights (optional) can include jdMatches and resumeMatches each being arrays of objects with term and context.
JD text (start):
{{JD_TEXT}}
Resume text (start):
{{RESUME_TEXT}}
Instructions:
1. Identify skill tokens and role requirements from JD.
2. Find which JD skills/requirements are present in the resume (matched), and which are not (missing).
3. Compute matchPercent as round(100 * matched / (matched + missing)). If missing + matched = 0, set matchPercent to 0.
4. Provide matchedSkills (deduplicated), and missingSkills (deduplicated).
5. Optionally provide highlights.jdMatches and highlights.resumeMatches where each highlight object contains term and a short context excerpt showing the occurrence.
6. If you had to truncate text, or if there is ambiguity, include an entry in warnings.
Return EXACTLY one JSON object and nothing else.
Example output format:
{
"matchPercent": 80,
...
}
Handling large file uploads and text extraction reliably
Implemented robust file parsing and validation for multiple formats
Frontend API Integration Hook
export const useCompare = ({ apiBaseUrl = '/api' }: UseCompareParams = {}) => {
const [state, setState] = useState<CompareState>({
data: null,
loading: false,
error: null,
});
const abortControllerRef = useRef<AbortController | null>(null);
const compare = useCallback(async (options: CompareOptions): Promise<void> => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
abortControllerRef.current = new AbortController();
setState({ data: null, loading: true, error: null });
try {
const formData = new FormData();
if (options.jdFile) {
formData.append('jd_file', options.jdFile);
}
if (options.jdText) {
formData.append('jd_text', options.jdText);
}
formData.append('resume_file', options.resumeFile);
const response = await fetch(`${apiBaseUrl}/compare`, {
method: 'POST',
body: formData,
signal: abortControllerRef.current.signal,
});
if (!response.ok) {
throw new Error('API error');
}
const data = await response.json();
setState({ data, loading: false, error: null });
} catch (error: any) {
if (error.name === 'AbortError') return;
setState({ data: null, loading: false, error: error.message });
}
}, [apiBaseUrl]);
const cancel = useCallback(() => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
}, []);
const reset = useCallback(() => {
setState({ data: null, loading: false, error: null });
}, []);
return { ...state, compare, cancel, reset };
};
Maintaining a responsive UI during long-running AI requests
Added request cancellation and optimistic UI updates for better user experience