Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.sh text eol=lf
14 changes: 14 additions & 0 deletions lending-poc/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.git
.venv
**/.venv
**/.venv-windows
**/surya-env
**/node_modules
**/__pycache__
document_processing
field_mapping_poc
gateway
frontend
scripts
docs
*.log
18 changes: 18 additions & 0 deletions lending-poc/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=lending_poc
POSTGRES_HOST_PORT=55439
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:55439/lending_poc
ENCRYPTION_KEY=changeme-generate-a-base64-fernet-key
DEBUG=true

# Selects the surya-inference/ocr variant docker-compose.yml runs: "cpu"
# (default, always works) or "gpu" (requires an NVIDIA GPU + NVIDIA
# Container Toolkit / WSL GPU passthrough on the host).
COMPOSE_PROFILES=cpu

# Model used by the translation and field_mapping services via Ollama.
# Must be pulled into the ollama container first:
# docker compose exec ollama ollama pull <model>
OLLAMA_MODEL=gemma4:e4b-it-qat
# OLLAMA_HOST=http://ollama:11434
7 changes: 6 additions & 1 deletion lending-poc/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ __pycache__/
*.pyo
.venv/
.env
**/.env
*.egg-info/
dist/
build/
.mypy_cache/
.pytest_cache/
.ruff_cache/
venv/
venv/
**/.venv/
.venv-windows/
local.env
*.log
4 changes: 2 additions & 2 deletions lending-poc/Database_setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pip install -e ".[dev]"
Create a `.env` file in the project root:

```bash
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:55432/lending_poc
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:55439/lending_poc
ENCRYPTION_KEY=<32-byte base64 key>
DEBUG=true
```
Expand All @@ -39,7 +39,7 @@ python3 -c "import base64, os; print(base64.b64encode(os.urandom(32)).decode())"
docker compose up -d db
```

This starts Postgres with pgvector on host port `55432` (mapped from container port `5432`), and waits until it reports healthy.
This starts Postgres with pgvector on host port `55439` (mapped from container port `5432`), and waits until it reports healthy.

## 4. Run database migrations

Expand Down
140 changes: 140 additions & 0 deletions lending-poc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Lending POC

Lending POC — FastAPI backend + PostgreSQL (pgvector), plus a document
processing pipeline (OCR, translation, field mapping) fronted by a gateway,
and a React frontend. This guide covers running the **entire stack in
Docker**.

For running the `app` service natively against a containerized DB only
(e.g. for backend development with hot-reload outside Docker), see
[Database_setup.md](Database_setup.md).

## Prerequisites

- [Docker](https://docs.docker.com/get-docker/) and Docker Compose v2
(`docker compose version`)
- Git
- **Optional, for GPU acceleration**: an NVIDIA GPU, the
[NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html),
and (on Windows) WSL2 with GPU passthrough enabled

## 1. Clone and configure environment

```bash
git clone <repo-url>
cd lending-poc
cp .env.example .env
```

Generate a real `ENCRYPTION_KEY` — the app uses it to encrypt sensitive
database fields, and the placeholder value in `.env.example` is not a valid
key:

```bash
python3 -c "import base64, os; print(base64.b64encode(os.urandom(32)).decode())"
```

Paste the result into `ENCRYPTION_KEY=` in `.env`.

Leave `COMPOSE_PROFILES=cpu` as-is unless you have a working GPU setup —
see [Using a GPU](#using-a-gpu) below.

## 2. Start the stack

```bash
docker compose up --build -d
```

This builds and starts every service: `db`, `app`, `ollama`, `field_mapping`,
`translation`, `surya-inference` + `ocr`, `gateway`, and `frontend`.

The first run takes a while — Ollama and Surya both download models on
first use. Watch progress with:

```bash
docker compose logs -f
```

## 3. Run database migrations

The `app` container doesn't run migrations automatically on startup:

```bash
docker compose exec app alembic -c db/alembic.ini upgrade head
```

## 4. Pull the Ollama model

Needed by the `translation` and `field_mapping` services:

```bash
docker compose exec ollama ollama pull gemma4:e4b-it-qat
```

(Substitute whatever `OLLAMA_MODEL` is set to in `.env` if you changed it.
If you're running the GPU profile, use `ollama-gpu` instead of `ollama` in
the command above.)

## 5. Verify it's running

| Service | URL | Notes |
|---|---|---|
| Frontend | http://localhost:5173 | Main UI |
| Gateway | http://localhost:8080 | Fronts OCR / translation / field-mapping |
| App (backend API) | http://localhost:8000 | Docs at `/docs`; health at `/health` |
| Postgres | localhost:55439 | pgvector-enabled |
| OCR | http://localhost:8010 | Not normally called directly |
| Translation | http://localhost:8001 | Not normally called directly |
| Field mapping | http://localhost:8002 | Not normally called directly |
| Surya inference | http://localhost:8500 | OCR's inference backend |

There are effectively two subsystems sharing this compose file: the
`app` + `db` lending backend, and a separate OCR/translation/field-mapping
pipeline fronted by `gateway`. The frontend talks to the gateway for
document processing and to the app for everything else.

## Using a GPU

`surya-inference`/`ocr` and `ollama` each come in a CPU and a GPU variant,
selected by `COMPOSE_PROFILES` in `.env`:

- `COMPOSE_PROFILES=cpu` (default) — always works, no GPU required.
- `COMPOSE_PROFILES=gpu` — requires an NVIDIA GPU on the host plus the
NVIDIA Container Toolkit (and, on Windows, WSL2 GPU passthrough).

To switch:

```bash
# in .env
COMPOSE_PROFILES=gpu
```

```bash
docker compose up --build -d
```

Both GPU containers detect GPU access at startup and fall back to CPU
automatically if it isn't actually usable — but `docker compose up` will
fail to create the containers at all if the toolkit isn't installed,
since the GPU device reservation can't be satisfied.

Ollama's own image auto-detects CUDA at runtime with no separate build, so
switching the profile is enough for it; `surya-inference`/`ocr` are built
from CUDA base images specifically for the `gpu` profile (see
[docker-compose.yml](docker-compose.yml) and
[document_processing/ocr/README.md](document_processing/ocr/README.md)
for details).

## Stopping and cleanup

```bash
docker compose down
```

Add `-v` to also delete the named volumes (`pgdata`, `ollama_models`,
`surya_models`) — this wipes the database and downloaded models, so only
do this if you want a clean slate:

```bash
docker compose down -v
```
2 changes: 1 addition & 1 deletion lending-poc/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra="ignore" lets pydantic-settings skip env vars not declared on Settings (e.g. POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, POSTGRES_HOST_PORT, COMPOSE_PROFILES — used by docker-compose, not read by the app). Without it, pydantic's default extra="forbid" would raise ValidationError on startup since those keys aren't fields on the model.
Tradeoff: a typo in a key we do care about (e.g. DATABASE_URL) would also be silently ignored rather than erroring.


APP_NAME: str = "lending-poc"
APP_VERSION: str = "0.1.0"
Expand Down
Loading