all builds >> N. GUGLIELMI
>> nicola@guglielmi ~ / labs / grantbrain

GrantBrain

A multi-tenant AI platform that maps public-funding eligibility (regional, national, EU) for consulting firms and co-drafts the proposals. Built to answer one question end-to-end: can an LLM pipeline be portable, resumable, and tenant-safe enough to trust in production?

Vertex AI Document AI Gemini / google-genai RAG Postgres RLS FastAPI Playwright Cloud Run
>> THE_PROBLEM

Public-funding advisors spend hours doing two things by hand: reading hundreds of scattered grant calls to figure out which clients are eligible, and re-writing the same boilerplate proposal sections for each one. The source data lives across dozens of regional and national portals, in PDFs of wildly different quality, and it changes weekly.

GrantBrain turns that into a workspace: crawl the sources, extract the structured rules, match them to a tenant's client base, and draft the proposal with the advisor in the loop. The interesting part isn't the UI, it's making the ingestion and the AI layer reliable enough that an advisor trusts the output.

>> ARCHITECTURE
Client · React Vite SPA Smart Matching Engineeligibility mapping Co-Drafting Workspacehuman-in-the-loop Ops & Logs Consolelive scraper feed Service · FastAPI gateway API + Tenant AuthJWT · tenant context RAG / Semantic Querygrounded retrieval Resumable Scraperparallel · restartable Infrastructure & AI PostgreSQLrow-level securityFOR UPDATE SKIP LOCKED Document AI→ Gemini multimodalfallback extraction Vertex AIgoogle-genai wrapperVertex ⇄ API key Cloud StorageSHA-256 dedup cachegs://… JSON + stream live logs RLS session grounded gen extract cache queue · skip-locked

> three tiers, one rule: every risky thing (concurrency, AI portability, extraction failure) is handled at the layer that owns it, not patched in the UI.

>> DECISIONS_THAT_MATTERED
[ 01 ]

A portable AI layer

A GenAIModelWrapper emulates the classic Vertex AI GenerativeModel interface over the newer google-genai client. The app never knows whether it's talking to dedicated Vertex AI or a direct Gemini API key, you flip an env var, not the code. Extraction has the same posture: Document AI first, and if it's unconfigured or fails, native PDFs go straight to Gemini for multimodal extraction. No hard dependency, no single point of failure.

[ 02 ]

Ingestion that survives a restart

The funding portals are crawled by parallel Playwright browsers (a bounded ThreadPoolExecutor, 5 workers). Downloaded PDFs are named by the SHA-256 of their source URL before landing in Cloud Storage, so re-runs are idempotent, storage never duplicates, and there are no orphan files. Every step streams into an in-memory log exposed over the API, so the operator watches the crawl live instead of guessing.

[ 03 ]

Correctness at the database

With parallel workers pulling from a shared queue, two of them will eventually grab the same link. Rather than coordinate in application code, the queue claim uses SELECT … FOR UPDATE SKIP LOCKED, Postgres hands each worker a different row atomically. The hard concurrency problem is solved where the data lives, in one line, with no race left to reason about.

[ 04 ]

Tenant isolation you can't forget

Multi-tenancy is enforced with PostgreSQL row-level security, not with WHERE tenant_id = ? scattered through the codebase. The tenant context is set on the session; the database refuses to return another tenant's rows even if a query forgets the filter. Isolation becomes a property of the system, not a discipline the developer has to remember.

>> WHY_THIS_MATTERS_TO_YOU

GrantBrain is a lab project, but the moves are the ones I bring to client work: keep the AI layer portable so no vendor owns you, make pipelines resumable so an outage is a shrug, and push correctness (concurrency, isolation) down to the layer that can actually guarantee it. That's what "production-grade" means when I say it.