DEV Community

thedarkking01
thedarkking01

Posted on

I Built an AI Expense Tracker for Freelancers — No Cloud, No API Key, No Data Leaks

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

I Built FreelanceFlow: A Local-First AI Receipt Manager for Freelancers

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle


What I Built

FreelanceFlow is a privacy-first AI business assistant for freelancers. It turns a folder of scattered receipts into an organized financial dashboard — without uploading a single file to the cloud.

The problem it solves:

Every freelancer I know has the same pain: receipts scattered across email, phone photos, and random folders. At the end of the month they either spend hours manually entering everything into a spreadsheet, or they hand a mess to their accountant and hope for the best. Neither is good.

FreelanceFlow fixes this with a simple workflow:

  1. Upload a receipt photo or PDF
  2. AI extracts the merchant, date, amount, currency, and expense category
  3. You review and correct before anything is saved — no blind trust in the model
  4. Dashboard shows income, expenses, net cash flow, and category breakdowns
  5. Invoice tracker flags unpaid and overdue invoices automatically
  6. Monthly report generates an AI-written summary and a downloadable CSV

Who would pay for this:
Freelance developers, designers, consultants, and solo founders who hate admin work. The recurring pain is real — recording expenses, chasing invoices, and preparing records for an accountant. A Pro tier at ₹249/month (roughly $3) for higher document limits and invoice reminders is a realistic starting point to validate.


Demo

Local-first app — runs entirely on your machine.

To run it yourself:

# 1. Pull the AI model
ollama pull gemma3:4b
ollama serve

# 2. Backend
cd backend
python -m venv venv
.\venv\Scripts\activate
pip install -r requirements.txt
python seed.py        # loads sample data
uvicorn main:app --reload

# 3. Frontend
cd frontend
npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:5173 — the dashboard loads with seeded sample data immediately.

What you'll see:

  • Dashboard with income/expense cards, a monthly bar chart, and a category pie chart
  • Receipts page — upload any JPG, PNG, or PDF and watch the AI fill in the form
  • Invoice tracker with color-coded paid / unpaid / overdue status
  • Reports page — pick a month, generate an AI summary, download CSV

Code

GitHub logo thedarkking01 / FreelanceFlow-AI-Business-Assistant

a privacy-focused AI assistant that helps freelancers manage receipts, invoices, expenses, and monthly financial reports without manually entering every transaction

FreelanceFlow — AI Business Assistant

A local-first AI receipt and expense manager for freelancers.
Upload receipts → AI extracts the details → review → track invoices → generate monthly reports.
Everything runs on your machine. No data leaves your device.

Built for the Hacktoberfest 2026 Open-Source AI Challenge.


Features






































Feature Description
Smart Receipt Scanner Upload JPG, PNG, or PDF — OCR + Gemma extracts merchant, date, amount, category
Human-in-the-loop Review AI suggestions are editable before saving — no blind trust in model output
AI Expense Categorization Software, Hardware, Travel, Food, Office Supplies, Marketing, Other
Financial Dashboard Income, expenses, net cash flow, category pie chart, monthly bar chart
Invoice Tracker Record invoices, auto-detect overdue, mark as paid, delete
Monthly Report Generator AI-written summary + CSV export — fallback summary if Ollama is offline
Privacy by Design All OCR, AI inference, and storage happen locally — zero telemetry


Tech Stack







Layer
…





Project structure at a glance:

freelanceflow/
├── backend/
│   ├── main.py              # FastAPI entry point
│   ├── database.py          # SQLAlchemy models
│   ├── models.py            # Pydantic validation schemas
│   ├── seed.py              # Sample data loader
│   ├── routes/
│   │   ├── receipts.py      # Upload → OCR → AI → confirm
│   │   ├── invoices.py      # Invoice CRUD + overdue detection
│   │   ├── dashboard.py     # Financial aggregations (pure Python math)
│   │   └── reports.py       # AI monthly summary + CSV export
│   └── services/
│       ├── ocr.py           # Tesseract + pdfplumber
│       ├── ai_extractor.py  # Ollama + Gemma structured extraction
│       └── categorizer.py   # Keyword-based category fallback
├── frontend/
│   └── src/
│       ├── pages/           # Dashboard, Receipts, Invoices, Reports
│       ├── components/      # Sidebar navigation
│       └── api/             # Axios client
└── tests/                   # 23 tests — validators, math, report logic
Enter fullscreen mode Exit fullscreen mode

How I Built It

The AI pipeline

The core AI workflow is a three-stage pipeline:

Stage 1 — OCR

Before the AI sees anything, the document is converted to plain text. PDFs go through pdfplumber for clean text extraction. Images (JPG, PNG) go through Tesseract OCR. This keeps the pipeline debuggable — you can inspect the raw text at any point.

# services/ocr.py
def extract_text(file_path: str, content_type: str) -> str:
    if content_type == "application/pdf":
        return extract_text_from_pdf(file_path)
    return extract_text_from_image(file_path)
Enter fullscreen mode Exit fullscreen mode

Stage 2 — Structured AI extraction with Gemma

The OCR text is sent to a locally running Gemma 3 4B model via Ollama. The prompt asks for a strict JSON response with defined fields — merchant, date, amount, currency, category, tax. Ollama's format: "json" parameter constrains the output to valid JSON.

# services/ai_extractor.py
response = httpx.post(
    "http://localhost:11434/api/generate",
    json={"model": "gemma3:4b", "prompt": prompt, "stream": False, "format": "json"},
    timeout=60.0,
)
data = json.loads(response.json().get("response", "{}"))
Enter fullscreen mode Exit fullscreen mode

Every field is then validated and sanitized — dates are checked with date.fromisoformat(), amounts must be positive floats, categories must be from the allowed set. If Ollama is offline, the endpoint returns an empty scaffold so the user can fill it in manually.

Stage 3 — Keyword fallback categorization

If Gemma returns "Other" for the category, a keyword-based categorizer runs over the merchant name and raw OCR text as a second pass:

# services/categorizer.py
_KEYWORDS = {
    "Software": ["saas", "subscription", "adobe", "github", "aws", "hosting"],
    "Travel":   ["uber", "ola", "flight", "hotel", "airbnb"],
    "Food":     ["swiggy", "zomato", "restaurant", "cafe", "coffee"],
    # ...
}
Enter fullscreen mode Exit fullscreen mode

This catches common cases even when the AI is uncertain.

Human-in-the-loop review

AI output is never saved directly. Every upload returns a pre-filled editable form. The user corrects any mistakes, then clicks "Confirm & Save." Only confirmed records enter the expense ledger. This is the most important design decision in the whole app.

Financial calculations

All arithmetic — totals, category breakdowns, net cash flow, monthly trends — is done in Python with SQLAlchemy queries. The AI is never asked to do math. This is a hard rule: AI for understanding documents, Python for numbers.

Monthly reports

The report endpoint aggregates the month's data in Python, then sends a structured prompt to Gemma asking for a 3–4 sentence professional summary. If Ollama is unavailable, a rule-based fallback generates a plain-English summary from the same numbers — so the feature always works.

Tech stack

Layer Technology Why
Frontend Vite + React + TailwindCSS + Recharts Fast dev, great charts
Backend Python FastAPI Async, auto-docs, Pydantic validation
AI Ollama + Gemma 3 4B Local inference, no API key, open-weight
OCR Tesseract + pdfplumber Handles both images and PDFs
Database SQLite via SQLAlchemy Zero config, local file, portable

Why Does Open Innovation Matter?

Three things that a closed API simply cannot give you:

1. Privacy is a real feature, not a marketing claim

When a freelancer uploads a receipt, it contains their client's name, the amount they were paid, what software they use, where they travel. With a closed cloud API, that data leaves the device. With Ollama + Gemma running locally, the receipt text never touches an external server. The only network call the app makes is to localhost:11434. That's a meaningful difference for anyone working with confidential client information.

2. It works offline and costs nothing to run

No API key. No monthly inference bill that scales with usage. No rate limits. No service going down and breaking your app. Once Gemma is pulled, the app runs indefinitely on your own hardware. For a bootstrapped side project targeting price-sensitive freelancers, this matters enormously — the marginal cost of an AI-powered feature is zero.

3. You can inspect, replace, and improve the model

With a closed API you accept whatever the provider ships. With Ollama you can swap gemma3:4b for llama3.2, mistral, or any other compatible model in one line. You can test different models against your receipt test set and pick the one that performs best for your use case. That kind of control is only possible with open-weight models.


What I Measured

I tested FreelanceFlow against a set of 20 synthetic receipts covering different formats — printed PDFs, handwritten-style images, multi-line receipts, and receipts with tax lines.

Metric Result
Merchant name accuracy 17/20 correct (85%)
Date extraction accuracy 19/20 correct (95%)
Amount extraction accuracy 18/20 correct (90%)
Category accuracy (AI + keyword fallback) 16/20 correct (80%)
Average processing time per receipt ~4–6 seconds on CPU
Receipts needing manual correction ~25%

The 25% manual correction rate is honest and expected. The human-in-the-loop design exists precisely because the model isn't perfect. The goal isn't 100% accuracy — it's to eliminate 75–80% of manual data entry while keeping the user in control of the rest.

Failures were mostly on low-quality images (blurry phone photos) and receipts with unusual formatting. PDF extraction was consistently more reliable than image OCR.


What I Learned

Structured output prompting is worth the effort. Asking Gemma to return JSON with format: "json" and a clear field-by-field prompt description dramatically improved consistency compared to asking for free-form text and parsing it.

The fallback matters more than you think. Ollama wasn't running during one of my test sessions and the whole upload flow broke silently. Adding graceful degradation — empty scaffold for manual entry, rule-based report summary — made the app usable even without the AI layer.

OCR quality is the real bottleneck. The AI extraction is only as good as the text it receives. A blurry phone photo produces garbage OCR output, and no amount of prompt engineering fixes that. Preprocessing images (contrast, deskew) before OCR would be the highest-leverage improvement.


What's Next

  • Image preprocessing before OCR (contrast enhancement, deskew) to improve accuracy on phone photos
  • Recurring expense detection — flag when the same merchant appears every month
  • Invoice reminder notifications — email or desktop notification when due dates approach
  • Multi-currency support with exchange rate conversion for international freelancers
  • Export to PDF for accountant-ready monthly reports

Running the Tests

cd backend
.\venv\Scripts\activate
python -m pytest ..\tests\ -v
Enter fullscreen mode Exit fullscreen mode
23 passed in 10.15s

tests/test_validators.py   — 9 tests  (Pydantic model validation)
tests/test_reports.py      — 7 tests  (month filtering, AI fallback, CSV)
tests/test_dashboard.py    — 7 tests  (expense totals, net cash flow, overdue)
Enter fullscreen mode Exit fullscreen mode

devchallenge #hf26challenge #python #react #opensource

Top comments (0)