Open SourcePersonal Project · Full-Stack + AI · 2025

Job Application AI Tool

AI-powered suite that generates tailored job application emails and optimizes resumes using Google Gemini 2.5 Flash. Evolved from a single-purpose generator into a full Career Operating System with a Kanban pipeline, bulk JSON ingestion, and PostgreSQL-backed persistence.

5+Input modes (text, PDF, image, URL)
2AI endpoints (email + resume)
O(1)Local OCR compute cost

Tech Stack

PythonFastAPIReact.jsGoogle Gemini 2.5 FlashSQLAlchemySQLitePostgreSQLDocker

Stakeholders

Job Seekers (End Users)

Primary users — generate tailored application materials and track pipeline stages

Zafran (Developer)

Full system architecture, AI prompt engineering, backend, frontend, and database design

Open Source Community

Public GitHub contributors — feature requests, bug reports, and pull requests

The Problem

Job seekers waste hours manually tailoring emails and resumes for each application, with no systematic way to track which roles they've applied to or which documents they sent. Traditional OCR tools require heavy system dependencies, making the setup painful.

The Solution

Built a decoupled FastAPI + React.js platform that accepts job descriptions and resumes through any format — text paste, PDF upload, image upload (OCR via Gemini vision), or a direct URL. Gemini 2.5 Flash generates tailored emails and ATS-optimised resume rewrites. A SQLAlchemy-backed application tracker with Kanban and list views manages the full job application lifecycle.

Architecture

Decoupled client-server architecture. A React.js SPA handles all UI state (tone, length, focus strategy, view mode). A FastAPI backend exposes two core endpoints (/generate for emails, /tailor-resume for resumes) plus CRUD routes for the application tracker. Multi-modal inputs are normalised server-side into a unified text context before being fed to the Gemini API. State is persisted via SQLAlchemy ORM — SQLite for local dev, PostgreSQL for production.

  1. 01

    React.js Frontend (SPA)

    State managed via React hooks. Supports Kanban board (drag-to-transition stages), dense list table with badge filters, and a glassmorphic floating drawer for application detail views. Tone, length, and focus strategy selectors drive generation parameters.

  2. 02

    FastAPI Backend (Async)

    Python async event loop via uvicorn. Non-blocking request handlers process multipart/form-data uploads concurrently. Routes: POST /generate, POST /tailor-resume, full CRUD for /applications. Alembic manages schema migrations.

  3. 03

    Multi-Modal Ingestion Layer

    Accepts text strings, PDF byte streams (text extracted server-side), raw image buffers (passed directly to Gemini vision as inline data), and HTTP URLs (fetched and parsed server-side). No Tesseract or poppler binaries required — OCR is entirely offloaded to Gemini's vision API.

  4. 04

    Gemini 2.5 Flash Orchestration

    Structured prompts enforce rigid JSON response schemas. POST /generate returns subject, body, keywords[], match_score, and tips[]. POST /tailor-resume returns header, summary, experience, keywords[], and match_score. Prompt templates are parameterised by tone (professional/concise/formal/etc.), length, and focus strategy.

  5. 05

    Database Layer (SQLAlchemy + Alembic)

    SQLAlchemy ORM abstracts the storage backend. Single env variable switches between SQLite (sqlite:///./career_os.db) and PostgreSQL (postgresql://...). Alembic migration scripts track schema evolution. ACID-compliant transactions protect bulk import operations.

Dev Setup

Prerequisites

  • Python 3.9+
  • Node.js 18+
  • Gemini API key (free at aistudio.google.com)
  • Docker (optional, for PostgreSQL)
bash — setup
$git clone https://github.com/ZafranSY/job-email.git && cd job-email
$# Option 1 — one-command start (Mac/Linux)
$chmod +x start.sh && ./start.sh
$# Option 2 — manual
$cd backend && echo 'GEMINI_API_KEY=your_key_here' > .env
$python -m venv venv && source venv/bin/activate && pip install -r requirements.txt
$uvicorn main:app --reload --port 8000

# Backend API on localhost:8000

$cd ../frontend && npm install && npm start

# Frontend on localhost:3000

$# PostgreSQL (optional): docker run --name career-os-postgres -e POSTGRES_PASSWORD=mysecretpassword -e POSTGRES_DB=career_os -p 5432:5432 -d postgres

Challenges

  1. 01

    Eliminating server-side OCR dependencies

    Traditional OCR pipelines require libtesseract and poppler system binaries, massively increasing container footprint and creating memory bottlenecks under concurrent usage. Refactored to pass raw image byte buffers directly to Gemini's multi-modal API as inline data payloads. This offloads all OCR compute to Google's infrastructure, bringing local OCR cost to O(1) — zero system dependencies, zero memory overhead on the server.

  2. 02

    Enforcing structured JSON from LLM outputs

    Raw Gemini responses were initially free-form text — unparseable by the frontend without post-processing fragility. Implemented a strict response_schema in the Gemini API call that enforces exact field names and types. Added a fallback parsing layer that extracts JSON from markdown code fences if the model wraps output in triple backticks. This eliminated all parsing failures in production.

  3. 03

    Transitioning from ephemeral tool to stateful Career OS

    The original generator had zero persistence — every session was stateless. Adding the application tracker required a full relational schema, migration tooling, and a UI that supports two distinct view modes (Kanban and dense list) without a page reload. Implemented Alembic for schema versioning and a SQLAlchemy session factory that swaps backends via a single environment variable — no code changes required to go from SQLite to PostgreSQL.

What I Learned

  • 01

    Offloading OCR to a vision LLM eliminates an entire class of server dependency problems — the tradeoff is latency, which is acceptable for async user-facing generation.

  • 02

    Enforcing JSON schemas in LLM API calls is more reliable than post-processing free-form text — build the schema contract first, then write the prompt around it.

  • 03

    SQLAlchemy's session abstraction makes database backend swaps genuinely painless — but you must commit to it from day one, not retrofit it after writing raw SQL.

  • 04

    Kanban UI feels simple to design but is deceptively complex to implement correctly with optimistic updates and concurrent stage transitions.

  • 05

    A well-scoped README is a product in itself — the open-source adoption rate correlates directly with setup friction.