Skip to content

Repository files navigation

SmartHire AI

Resume analysis and job matching built as a RAG system: parse a resume, embed chunks in PostgreSQL (pgvector), retrieve the evidence that matters for a specific job description, then generate grounded match feedback with OpenAI.

Live demo: https://smarthire-web-1w1z.onrender.com
API health: https://smarthire-api-rhhp.onrender.com/api/health

Free Render tier: the API may sleep after idle and take ~1 minute to wake.

Layer Choice
UI React + TypeScript (Vite)
API Spring Boot 3 · Java 17
AI OpenAI embeddings (text-embedding-3-small) + chat (gpt-4o-mini)
Data PostgreSQL 16 + pgvector
Run / ship Docker Compose locally · Render in production

What it does today

  1. Create a profile and upload a master resume (PDF/DOCX)
  2. Paste a job description on Job Match
  3. Backend indexes the resume (chunk → embed → pgvector), retrieves top matching passages, and returns scores, gaps, ATS keywords, rewrite ideas, and evidence snippets
  4. Adjust preferences in Settings (stored as JSON on the user record)

There is no real login yet — the browser keeps a currentUserId in localStorage. Fine for a portfolio demo; not for a wide public launch (anyone with the URL can burn your OpenAI quota).

React UI  ──REST──►  Spring Boot
                        │
                        ├─ parse PDF/DOCX → chunks
                        ├─ OpenAI embeddings → pgvector
                        ├─ similarity search (top-k)
                        └─ chat model → structured JSON analysis
                     PostgreSQL

How the project was built (journey)

This did not start as a finished architecture. It grew one working slice at a time. Full write-up: docs/BUILD_JOURNEY.md. Decision log: docs/DECISIONS.md.

1 — Backend + schema first

Stood up Spring Boot, Flyway, and Postgres with pgvector in Docker. Tables for resumes, chunks (vector(1536)), jobs, and analyses. Health endpoint before any AI.

Friction: machine had JDK 11; Spring Boot 3 needs 17. Installed Microsoft OpenJDK 17 and pinned JAVA_HOME for local runs.

2 — Ingest before AI

Upload → PDFBox/POI text extraction → character chunking (~800 / 100 overlap) → persist chunks. Jobs as their own API. Status on resumes tracks PARSEDCHUNKED → later INDEXED.

Why this order: deterministic prep is easier to debug than jumping straight to LLM calls.

3 — RAG loop

OpenAI client with retries, JDBC VectorStore (kept vectors out of JPA), indexing + analysis endpoints. Analysis retrieves top-k chunks for this resume + JD, then asks the model for JSON (score, gaps, recommendations, evidence).

Friction: empty/placeholder OPENAI_API_KEY401. Added repo-root .env loading and clearer OpenAI error payloads. Full loop worked after a real key: upload → index → analyze.

4 — UI on top of the APIs

Vite React app: upload, paste JD, show results including retrieved chunks (so grounding is visible). Vite /api proxy for local CORS; Spring CORS kept for non-proxy clients.

5 — Product shell (Profile / Match / Settings)

Grew from a single match screen into three sections. Users table + profile_data / account_settings JSONB. Master resume owned by a user. Still deferred real auth.

6 — Dockerize the whole stack

Multi-stage Dockerfiles for API and nginx UI; Compose runs db + api + web. Local port conflicts (e.g. Maven Java vs container API) fixed by stopping the local process and bringing Compose services up cleanly.

7 — Public deploy on Render (and the messy part)

Blueprint: static site + Docker web service + free Postgres. Several real failures along the way:

Problem What I changed
Static site Blueprint rejected region Removed region from the static service
API OOM risk on 512MB free instances Smaller heap (-Xmx256m), SerialGC
DB connection timeouts Discrete DB_* env vars; then external Render hostname + sslmode=require when private host was unreachable
Wrong frontend API URL after Render renamed services Set VITE_API_BASE_URL to the live API host and rebuilt the static site
Free-tier sleep / suspension quirks Documented cold starts; keep demo traffic light

Live app: https://smarthire-web-1w1z.onrender.com


Repo layout

backend/     Spring Boot API, Flyway, RAG services
frontend/    React UI (Profile, Match, Settings)
docs/        Journey, decisions, deploy notes, case study
samples/     Example resume / job payloads
deploy/      VPS helper script
docker-compose.yml / docker-compose.prod.yml
render.yaml  Render Blueprint

Quick start (local)

cd C:\Users\sairo\OneDrive\Desktop\Project1
copy .env.example .env   # set OPENAI_API_KEY
docker compose up -d --build

API-only local (DB in Docker):

docker compose up -d db
$env:JAVA_HOME = "C:\Program Files\Microsoft\jdk-17.0.20.8-hotspot"
$env:PATH = "$env:JAVA_HOME\bin;" + $env:PATH
cd backend; mvn spring-boot:run
cd frontend; npm install; npm run dev   # http://localhost:5173

Docs

Doc What you’ll find
docs/BUILD_JOURNEY.md Step-by-step build history, problems, fixes
docs/DECISIONS.md Why each major technical choice was made
docs/CASE_STUDY.md Product context and what the MVP includes
docs/RENDER_DEPLOY.md How the Render deploy is wired
docs/PUBLIC_DEPLOY.md VPS alternative
docs/AWS_DEPLOY.md ECS/RDS-shaped path

What’s next

  • Real authentication before sharing the demo widely
  • Wire richer profile sections into the RAG prompt
  • CI + optional HNSW once embedding volume grows
  • Harden free-tier DB networking (prefer private URL when it works)

About

SmartHire AI - resume analysis and job match MVP (React, Spring Boot, pgvector, RAG)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages