The Problem

Users need a quick way to query the content of arbitrary PDFs without building a custom Retrieval‑Augmented Generation (RAG) pipeline. The pain point is wiring PDF storage, embedding, vector search, and LLM inference together in a production‑ready UI.

What This Does

pdftochat ships a full‑stack Next.js app that lets authenticated users upload PDFs, stores the files on Bytescale, creates embeddings in Chroma Cloud, and answers free‑form questions via a Mixtral model hosted on Together AI.

Key UI pieces live under app/dashboard/ (dashboard UI) and app/document/[id]/ (chat view). The API layer in app/api/ exposes three routes:

  • app/api/ingestPdf/route.ts – receives a multipart upload, saves the file, and triggers utils/ragChain.ts to create a Chroma collection.
  • app/api/chat/route.ts – receives a user query, calls app/api/utils/embeddings/index.ts to embed the prompt (via Chroma’s hybrid model) and then runs a LangChain RAG chain that fetches relevant chunks and forwards them to the LLM.
  • app/api/document/[id]/route.ts – fetches document metadata from the Prisma‑backed Postgres table (utils/prisma.ts).

All styling is handled by Tailwind (tailwind.config.js, styles/*.css).

How It Is Wired

Entry point → request handling

  1. Client request – The chat UI (app/document/[id]/page.tsx) issues a POST /api/chat fetch.
  2. Route handlerapp/api/chat/route.ts parses the JSON body and calls app/api/utils/vector_store/index.tsvectorStore.query(prompt) (implementation selected by NEXT_PUBLIC_VECTORSTORE).
  3. Vector store – In app/api/utils/vector_store/chroma.ts, query sends the prompt to Chroma Cloud, which returns the top‑k document chunks (dense + sparse via Qwen / SPLADE).
  4. RAG chainutils/ragChain.ts builds a LangChain ConversationalRetrievalQAChain using the retrieved chunks and the LLM client (utils/config.ts holds the Together AI API key).
  5. LLM call – The chain posts the assembled prompt to Together AI’s Mixtral endpoint and streams the answer back to the API route, which returns JSON to the frontend.

Upload flow

  1. UI (app/dashboard/page.tsx) posts a file to POST /api/ingestPdf.
  2. app/api/ingestPdf/route.ts uploads the binary to Bytescale via the helper in utils/config.ts, stores the resulting URL in the Document table (utils/prisma.ts), and invokes utils/ragChain.ts with createCollection(documentId, fileUrl).
  3. utils/ragChain.ts downloads the PDF, extracts text (via pdf-parse or similar), and calls vectorStore.upsert(docs) – the Chroma implementation creates a collection named after the document ID.

Persistence – Prisma schema (prisma/schema.prisma) defines a Document model; npx prisma db push creates the Postgres table used by both API routes.

Key ownership

  • app/api/utils/vector_store/* – all vector‑store communication; Chroma is the default hub.
  • utils/ragChain.ts – the only place LangChain orchestration lives; any change to retrieval or prompt logic touches this file.
  • utils/prisma.ts – Prisma client singleton; used by both upload and metadata routes.
  • utils/config.ts – central place for environment‑driven secrets (Together AI key, Bytescale token, Chroma credentials).

No cyclic imports are present; the vector store module is a leaf, called only from API routes and the RAG chain.

How To Use It

# 1. Clone the repo
git clone https://github.com/moses-y/pdftochat.git
cd pdftochat

# 2. Install dependencies (pnpm is declared in package.json)
pnpm install

# 3. Copy and fill environment variables
cp .env.example .env
# Edit .env with:
#   NEXT_PUBLIC_VECTORSTORE=chroma
#   CHROMA_API_KEY=...
#   CHROMA_TENANT=...
#   CHROMA_DATABASE=...
#   TOGETHER_API_KEY=...
#   BYTESCALE_API_KEY=...
#   DATABASE_URL=postgresql://<user>:<pw>@<host>/<db>

# 4. Push Prisma schema to the Postgres instance
npx prisma db push

# 5. Run locally
pnpm dev   # starts Next.js on http://localhost:3000

The UI is reachable at /sign-up → create an account (Clerk handles auth). After signing in, the dashboard lets you upload PDFs and start chatting.

Real‑World Use

A SaaS product could embed this repo as a micro‑service: a front‑end sends a PDF URL to /api/ingestPdf, the service creates a Chroma collection, and downstream business logic queries /api/chat with domain‑specific prompts. The separation of vector store and LLM config makes swapping Chroma for Pinecone (see app/api/utils/vector_store/pinecone.ts) straightforward.

Code Health & Issues

  • Medium – Untested code – No *.test.* files detected; API routes and RAG logic lack unit or integration tests.
  • Medium – Missing CI/CD – Repository contains no .github/workflows or other pipeline definitions, so builds and linting are not automatically gated.
  • Low – License presentLICENSE file exists (MIT), satisfying open‑source distribution requirements.
  • Low – Secrets in repo – No API keys are committed; however, .env.example exposes required variable names, which is appropriate.

No static analysis warnings were provided beyond the heuristic list above.

The Bottom Line

pdftochat delivers a ready‑to‑run RAG front‑end with clear separation of concerns (upload → vector store → LangChain → LLM). It is well‑structured for extension (alternative vector stores, custom prompts) but lacks automated testing and CI, so production teams should add those safeguards before scaling. Ideal for engineers who need a quick proof‑of‑concept or a baseline for a custom PDF‑chat product.