|
|
▶ Watch on YouTube |
This repository contains a full-stack application for tailoring CVs with AI, analyzing job descriptions, generating cover letters and connection messages, and automating LinkedIn job workflows.
It currently includes:
- A FastAPI backend for CV editing, PDF rendering, job analysis, and browser automation
- A React + Vite frontend for editing resumes, reviewing job matches, and generating content
- Playwright-based LinkedIn automation
- Local LLM inference through Ollama (Qwen 3.5)
- SQLite-backed storage for jobs and LLM evaluation data
- FastAPI (Python) – API layer and request handling
- SQLite – Lightweight database for jobs and evaluation data
- Playwright – Browser automation for LinkedIn workflows
- Pydantic – Validation and schema enforcement
- React – Component-based UI development
- Bootstrap – Styling and responsive layout
- Vite – Frontend build and dev server
- Ollama – Local LLM runtime
- Qwen 3.5 – Local inference model for CV and cover-letter generation
- UV – Python dependency and environment management
- Node.js + npm – Frontend package management
- Pytest – Backend test suite
- Feature Showcase
- Project Structure
- Architecture Diagram
- Setup
- Ollama Setup
- Running the App
- Testing
- Additional Information
AI_CV_Customization/
├── cv_editing_react_app/
│ ├── backend/
│ │ ├── api/ # FastAPI routes and app entrypoint
│ │ ├── schema/ # Request/response validation schemas
│ │ ├── services/ # Business logic, LLM integration, PDF rendering, Playwright automation
│ │ │ ├── browser_automation_playwright/
│ │ │ ├── database/
│ │ │ └── llm.py
│ │ └── test/ # Backend tests
│ └── frontend/my-app/ # React + Vite frontend app
│ ├── src/ # React components and pages
│ ├── public/ # Static assets and HTML templates
│ └── package.json # Frontend dependencies and scripts
├── job_listings/ # Saved job listings data
├── package.json # Root scripts for running backend/frontend together
├── pyproject.toml # Python dependencies and project config
└── assets/ # Project screenshots and media
The app is now organized under the cv_editing_react_app directory, with the backend and frontend split into separate subprojects.
Even a simple text diagram helps:
Frontend (React)
↓
FastAPI Backend
↓
Playwright Automation → LinkedIn
↓
Database (SQLite)
↓
Ollama (Local LLM)
git clone git@github.com:mohan696matlab/AI_CV_Customization.git
cd AI_CV_CustomizationIf you do not already have UV installed:
pip install uvVerify the installation:
uv --versionInstall the project dependencies and create the virtual environment from the repository root:
uv syncPlaywright is used for LinkedIn automation. Install the browser binaries with:
uv run --directory cv_editing_react_app/backend playwright installCreate a .env file inside the backend directory:
cv_editing_react_app/backend/.env
Add your LinkedIn credentials:
username=your_username
password=your_passwordExample:
username=john.doe@gmail.com
password=my_passwordRun the database bootstrap from the repository root or from the backend directory:
uv run --directory cv_editing_react_app/backend python -m services.database.dbTo reset the database and recreate its tables:
uv run --directory cv_editing_react_app/backend python -m services.database.db --resetYou can start the backend from the repository root with the helper script:
npm run backendOr start it directly:
uv run --directory cv_editing_react_app/backend uvicorn api.server:app --reload --port 8000The API will be available at:
- Application: http://localhost:8000
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
Ollama is used for local LLM inference.
Download and install Ollama from:
Verify the installation:
ollama --versionollama pull qwen3.5:9bThe backend currently reads its model configuration from the LLM service file at cv_editing_react_app/backend/services/llm.py. Update the values there if needed:
MODEL_NAME = "qwen3.5:9b"
HOST_ADDRESS = "http://localhost:11434"ollama rm qwen3.5:9bMake sure Node.js and npm are installed:
node -v
npm -vFrom the repository root, start the frontend with:
npm run frontendYou can also start it directly from the frontend folder:
cd cv_editing_react_app/frontend/my-app
npm ci
npm run devFrom the repository root, run both services together:
npm run devThe frontend will be available at:
- Application: http://localhost:5173/
The backend will be available at:
- Application: http://localhost:8000
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
Run the backend test suite from the repository root:
uv run pytest -qDocker Compose runs the React frontend behind Nginx and the FastAPI backend as separate services. SQLite, generated PDFs, and exported job listings are stored in named volumes.
Rotate any credentials that have previously been committed or shared, then create a local Docker env file:
Copy-Item .env.example .envFill in GEMINI_API_KEY and APIFY_API_KEY. For LinkedIn automation, also fill in LINKEDIN_USERNAME and LINKEDIN_PASSWORD.
Build and start the default Gemini-backed stack:
docker compose up --buildOpen the application at http://localhost:8080. The backend API and Swagger UI are available through http://localhost:8080/api/docs.
To use the optional Ollama container, set MODEL_NAME to the model you pulled and start the profile:
docker compose --profile ollama up --build
docker compose --profile ollama exec ollama ollama pull qwen3.5:9bStop the services with Ctrl+C, or use docker compose down. Do not add -v unless you intentionally want to delete the persisted SQLite database and generated files.
uv syncuv lock --upgrade
uv syncuv run --directory cv_editing_react_app/backend playwright installnpm run backendnpm run frontendnpm run devThis saves your LinkedIn login session so you do not need to authenticate every time.
uv run --directory cv_editing_react_app/backend python -m services.browser_automation_playwright.save_login_cookiesuv run --directory cv_editing_react_app/backend python -m services.browser_automation_playwright.browser_automationuv run --directory cv_editing_react_app/backend python -c "from services.database.db import delete_all_jobs; delete_all_jobs()"uv run --directory cv_editing_react_app/backend python -c "from services.database.db import remove_db; remove_db()"



