A full-stack application that helps you tailor your resume for specific job applications using AI-powered keyword extraction and minimal, structure-preserving edits.
- Upload PDF resumes (max 10MB)
- Automatic parsing into structured format
- Extract sections: Experience, Education, Skills, etc.
- Intelligent text extraction and normalization
- Extract keywords from job URLs or pasted text
- Multi-strategy web scraping (Ashby, Greenhouse, and more)
- AI-powered keyword categorization using OpenAI GPT
- Resume-JD keyword comparison with auto-selection
- Plan-and-Annotate Workflow: Minimal edits that preserve original structure
- Three Insertion Strategies: Modifier, Parenthetical, Tail
- Strict Validation: Maximum 2 words or 25 characters per insertion
- PDF Annotation: Visual feedback with highlights and sticky notes
- Change Tracking: Comprehensive diff and change logs
- Inline editing of all resume sections
- Live preview with ATS-safe formatting
- Link management and URL validation
- Autosave functionality with 800ms debouncing
- Character limit enforcement for optimal readability
- Three Preview Modes: Clean, Diff, Keywords
- Side-by-side comparison of original vs. tailored resume
- Keyword highlighting in context
- Annotated PDF generation
- Export to PDF/HTML formats
- Python 3.9+
- Node.js 16+
- OpenAI API key
-
Clone the repository
git clone <repo-url> cd ResumeTailor
-
Set up backend
# Install Python dependencies pip install -r requirements.txt # Configure environment cp env.example .env # Edit .env and add your OpenAI API key: # OPENAI_API_KEY=your_actual_api_key_here
-
Set up frontend
cd nextjs-client npm install cd ..
-
Run the application
Terminal 1 - Backend:
python3 main.py
Terminal 2 - Frontend:
cd nextjs-client npm run dev -
Access the application
- Frontend UI: http://localhost:3000 ← Start here!
- Backend API: http://localhost:8000
- API Documentation: http://localhost:8000/docs
- Upload Resume: Click to select or drag-drop your PDF resume
- Enter Job Details: Add job title and job posting URL (or paste job description text)
- Extract Keywords: AI analyzes the job posting and extracts relevant keywords
- Review Keywords: See which keywords you're missing (auto-selected in yellow)
- Edit Resume (Optional): Use the structured editor for manual edits
- Tailor Resume: Click "Tailor Resume" to generate minimal keyword insertions
- Review Results: View diff comparison and download annotated PDF
- Frontend: Next.js 15, React 19, TypeScript, Tailwind CSS
- Backend: FastAPI (Python 3.9+), Pydantic, Uvicorn
- AI/ML: OpenAI GPT-4 (keyword extraction, edit planning)
- PDF Processing: PyMuPDF (annotation), pdfminer.six (parsing)
- Storage: File-based storage (no database required)
ResumeTailor/
├── main.py # FastAPI application entry point
├── models.py # Pydantic models for API
├── models_structured.py # Structured resume data models
├── scraping.py # Web scraping for job postings
├── openai_service.py # OpenAI integration
├── requirements.txt # Python dependencies
├── env.example # Environment variables template
│
├── routers/ # API route handlers
│ ├── resume.py # Resume upload, tailoring endpoints
│ └── structured_resume.py # Structured editor endpoints
│
├── services/ # Business logic (18+ services)
│ ├── edit_plan.py # AI-powered edit planning
│ ├── apply_plan.py # Edit validation & application
│ ├── pdf_annotate.py # PDF annotation with highlights
│ ├── structured_parser.py # Resume parsing engine
│ ├── keyword_placement.py # Intelligent keyword placement
│ ├── resume_loader.py # Resume text extraction
│ ├── storage.py # File storage management
│ └── ... # Additional services
│
├── nextjs-client/ # Frontend application
│ ├── src/
│ │ ├── app/
│ │ │ ├── page.tsx # Main application page
│ │ │ ├── components/ # React components
│ │ │ │ └── PreviewPanel.tsx
│ │ │ ├── structured-editor/ # Structured editor
│ │ │ │ ├── page.tsx
│ │ │ │ ├── components/ # Editor components
│ │ │ │ └── hooks/ # React hooks
│ │ │ └── api/ # API proxy routes
│ │ └── next.config.ts # Next.js configuration
│ └── package.json
│
├── uploads/ # Uploaded PDF files (created on first upload)
├── structured_resumes/ # Parsed resume JSON files
└── docs/ # Additional documentation
├── PLAN_AND_ANNOTATE_README.md
├── STRUCTURED_EDITOR_README.md
└── PREVIEW_FEATURE_README.md
Create a .env file in the root directory:
OPENAI_API_KEY=your_openai_api_key_here # Required for AI features- Uploaded PDFs:
./uploads/{resume_id}.pdf - Structured Data:
./structured_resumes/{resume_id}.json - No database required - all data is stored as files
This application uses file-based storage, which is suitable for:
- Personal use
- Development and testing
- Small-scale deployments
For production use with multiple users, consider migrating to a database (PostgreSQL, SQLite, etc.).
Interactive API documentation is available at http://localhost:8000/docs when the backend is running.
POST /upload_resume- Upload PDF resumeGET /resume/{id}/text- Get resume textGET /resume/{id}/pdf- Download original PDFGET /resume/{id}/annotated.pdf- Download annotated PDF
POST /keywords_url- Extract keywords from job URLPOST /keywords_text- Extract keywords from job text
POST /resume/edit-plan- Generate minimal edit planPOST /resume/apply-plan- Apply edits to resumePOST /resume/{id}/annotate- Create annotated PDF
POST /structured-resume/parse- Parse resume into structured formatGET /structured-resume/{id}- Get structured resume dataPUT /structured-resume/{id}/section- Update a sectionPUT /structured-resume/{id}/bullet- Update a bullet pointPOST /structured-resume/{id}/export- Export to PDF/HTML
The plan-and-annotate workflow preserves your original resume structure while making minimal, targeted keyword insertions:
- Edit Plan Generation: AI analyzes your resume and proposes minimal insertions
- Three Strategies: Modifier (add word before), Parenthetical (add in parens), Tail (add after)
- Strict Validation: Maximum 2 words or 25 characters per line
- PDF Annotation: Highlights and sticky notes show exactly what changed
See PLAN_AND_ANNOTATE_README.md for detailed documentation.
Edit your resume with a modern, structured interface:
- Section-based Editing: Work, Education, Skills, etc.
- Live Preview: See changes in real-time
- Autosave: Changes saved automatically
- Link Management: Validate and manage URLs
- Character Limits: Built-in validation for optimal bullet length
See STRUCTURED_EDITOR_README.md for detailed documentation.
Review your tailored resume in three different modes:
- Clean Mode: Formatted view of the updated resume
- Diff Mode: Side-by-side comparison of original vs. updated
- Keywords Mode: Highlighted keywords in context
See PREVIEW_FEATURE_README.md for detailed documentation.
Issue: python: command not found or python main.py fails
- Solution: Use
python3 main.pyinstead - Check Python version:
python3 --version(need 3.9+) - Install dependencies:
pip install -r requirements.txt
Issue: OPENAI_API_KEY not found
- Solution: Create
.envfile with your OpenAI API key - Copy template:
cp env.example .env - Edit
.envand add your key
Issue: Port 8000 already in use
- Solution: Kill existing process on port 8000
- Find process:
lsof -i :8000 - Kill it:
kill -9 <PID>
Issue: npm: command not found
- Solution: Install Node.js from nodejs.org (version 16+)
Issue: next: command not found
- Solution: Run
npm installin thenextjs-client/directory
Issue: Port 3000 already in use
- Solution: Stop other processes using port 3000 or change the port
- Next.js will automatically use port 3001 if 3000 is busy
Issue: API calls from frontend fail
- Solution: Ensure both servers are running
- Backend on http://localhost:8000
- Frontend on http://localhost:3000
- Check
next.config.tshas the proxy configuration - Restart the frontend after backend is running
Issue: CORS errors in browser console
- Solution: Backend is configured for
http://localhost:3000 - If using a different port, update CORS settings in
main.py
Issue: Upload fails with "File not a PDF"
- Solution: Only PDF files are supported (max 10MB)
Issue: Parsing fails or returns empty sections
- Solution: Resume format may not be recognized
- Try using the structured editor for manual input
Issue: No keywords extracted
- Solution: Check OpenAI API key and quota
- Verify job URL is accessible
- Try pasting job description text instead
Issue: Web scraping fails for job URL
- Solution: Some job sites block scraping
- Use "Paste JD text instead" option
Run the backend test suite:
python test_api.pyRun resume-specific tests:
python test_resume_api.pyThe application uses a simple file-based storage system:
- Uploaded PDFs:
./uploads/{resume_id}.pdf - Structured Resumes:
./structured_resumes/{resume_id}.json - Annotated PDFs:
./uploads/{resume_id}_annotated.pdf
- All data persists between application restarts
- No database setup required
- Resume IDs are UUID v4 strings
For important resumes, regularly backup:
uploads/directory (original PDFs)structured_resumes/directory (parsed data)
- API Keys: Never commit
.envfile to version control - Local Storage: All resume data stays on your machine
- OpenAI Usage: Resume text is sent to OpenAI API for processing
- No Cloud Storage: No third-party storage services used
This project is part of a resume tailoring system. Use responsibly and in accordance with OpenAI's terms of service.
Contributions are welcome! Please feel free to submit issues or pull requests.
For questions or issues:
- Check the troubleshooting section above
- Review the detailed documentation in the
docs/folder - Check the API documentation at http://localhost:8000/docs
- Review logs in the terminal for error details
Future enhancements planned:
- Database integration for multi-user support
- User authentication and authorization
- Resume version history and rollback
- Multiple resume templates
- Batch processing for multiple job applications
- Advanced analytics and insights
- Export to more formats (DOCX, LaTeX, etc.)
- Integration with job boards and ATS systems