A local-first, RAG-powered AI chat platform β chat with your documents using a private LLM.
Most AI chat tools send your documents to third-party servers, making them unsuitable for private or sensitive data. ThinkStack runs entirely on your machine β your documents never leave your environment.
Beyond privacy, generic AI chat lacks context memory. ThinkStack organises your work into Projects, each with its own persistent knowledge base built from the documents you upload. When you ask a question, it doesn't just query an LLM blindly β it first retrieves the most relevant chunks from your documents and past conversations, then feeds that context to the model. This is Retrieval-Augmented Generation (RAG) in action.
| Feature | Description |
|---|---|
| π Project-based Knowledge | Organise your work into projects. Each project has its own isolated document store and chat history. |
| π Document Ingestion | Upload PDFs and text files to a project. They are parsed, chunked, embedded, and stored in a local vector database. |
| π RAG-Powered Chat | Every query retrieves the most semantically relevant document chunks before the LLM responds β answers are grounded in your actual files. |
| π§΅ Persistent Chat Memory | Past conversations are also embedded and retrieved, giving the AI long-term context within a project. |
| π Fully Local LLM | Uses Ollama to run language and embedding models on your own machine. No data leaves your environment. |
| π User Authentication | Google OAuth via Firebase Authentication with secure session management. |
| π Streaming Responses | LLM responses stream token-by-token to the UI for a real-time chat feel. |
| π― Career Coach Mode | Detects CV/resume-related queries and switches to a specialised Career Coach prompt with structured output. |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser (User) β
β Next.js App (React + TypeScript) β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββ
β API Routes (Next.js Server)
β
βββββββββΌβββββββββ
β RAG Pipeline β
β β
β 1. Embed Query ββββββββββββββββΆ Ollama
β 2. Retrieve ββββββββββββββββΆ ChromaDB (Vector Store)
β 3. Build Promptβ
β 4. Stream LLM ββββββββββββββββΆ Ollama (LLM)
β 5. Store MemoryββββββββββββββββΆ ChromaDB + Firestore
βββββββββββββββββββ
β
βββββββββΌβββββββββ
β Firebase β
β Auth, Firestoreβ
β (Cloud) β
βββββββββββββββββββ
-
Ingestion β When you upload a file, it is parsed (
pdf-parse), split into overlapping chunks, embedded vianomic-embed-texton Ollama, and stored in ChromaDB tagged with project and file metadata. -
Inference β When you send a message:
- The query is embedded using the same model.
- ChromaDB performs a nearest-neighbour search to retrieve the top-8 relevant document chunks (project memory) and top-5 relevant past conversation snippets (chat memory).
- A structured prompt is assembled with both memory types and recent chat history.
- The prompt is streamed to the Ollama LLM.
- The full response is stored back into ChromaDB as a new chat memory entry.
| Layer | Technology |
|---|---|
| Frontend | Next.js 16, React 19, TypeScript, Tailwind CSS, shadcn/ui |
| State Management | Zustand |
| Backend | Next.js API Routes (serverless functions) |
| Auth | Firebase Authentication (Google OAuth) |
| Database | Firebase Firestore (projects, chats, file metadata) |
| Vector Store | ChromaDB (local Docker container) |
| LLM | Ollama (local β any compatible model) |
| Embeddings | Ollama (nomic-embed-text) |
| File Parsing | pdf-parse for PDFs, plain text support |
| Markdown Rendering | react-markdown + rehype-raw |
| Tool | Version | Purpose |
|---|---|---|
| Node.js | 18+ | Run the Next.js app |
| Docker | Latest | Run ChromaDB |
| Ollama | Latest | Run the LLM and embedding model locally |
| Firebase project | β | Auth and Firestore |
git clone https://github.com/<your-username>/thinkstack.git
cd thinkstack
npm installdocker run -p 8000:8000 chromadb/chroma# Pull the embedding model
ollama pull nomic-embed-text
# Pull a chat model (example)
ollama pull qwen2.5-coder:7bCreate a .env.local file at the project root and fill in your Firebase credentials:
# Firebase
NEXT_PUBLIC_FIREBASE_API_KEY=your-key
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your-project-id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your-project.firebasestorage.app
NEXT_PUBLIC_FIREBASE_APP_ID=your-app-id
NEXT_PUBLIC_FIREBASE_DATABASE_URL=https://your-project-default-rtdb.region.firebasedatabase.app
# Services
OLLAMA_URL=http://localhost:11434
CHROMA_URL=http://localhost:8000npm run devOpen http://localhost:3000.
thinkstack/
βββ app/
β βββ (auth)/ # Login and Register pages
β β βββ login/
β β βββ register/
β βββ api/ # Next.js API Routes
β β βββ chat/ # RAG pipeline + Ollama streaming
β β βββ project_files/ # File upload, chunking, embedding, storage
β β βββ parse-file/ # File text extraction
β β βββ upload/ # Firebase Storage upload
β βββ components/
β β βββ chat/ # Chat interface with streaming support
β β βββ layout/ # Dashboard layout, sidebar, header
β β βββ project/ # Project CRUD, file management, overview
β β βββ prompt/ # Custom input, prompt helpers
β βββ dashboard/
β β βββ [projectId]/
β β βββ [chat]/ # Dynamic chat route per project/session
β βββ hooks/
β β βββ useChat.ts # Chat state + streaming logic
β βββ lib/
β β βββ chroma/ # ChromaDB client
β β βββ embeddings/ # Ollama embedding functions
β β βββ firebase/ # Firestore services (projects, chats, files)
β β βββ utils/
β β β βββ chunkText.ts # Sliding-window text chunker
β β βββ file-parser.ts # PDF + text parsing
β β βββ prompt.ts # Prompt builder (RAG + chat history)
β β βββ rag.ts # Vector retrieval and context assembly
β βββ services/
β βββ chat.service.ts # Chat message persistence + memory storage
βββ components/
β βββ ui/ # shadcn/ui base components
βββ store/
β βββ store.ts # Zustand global store
βββ types/
β βββ memory.ts # TypeScript types for memory/chat
βββ config/
β βββ env.ts # Environment variable helpers
βββ middleware.ts # Auth route protection
Text is split using a sliding-window chunker in app/lib/utils/chunkText.ts:
- Chunk size: 1000 characters (default)
- Overlap: 200 characters
Overlap ensures that sentences near chunk boundaries appear in adjacent chunks, preventing context loss during retrieval.
Configured in app/lib/rag.ts:
- Project memory (document chunks): top 8 results
- Chat memory (past conversations): top 5 results
- Ranked by cosine distance β lower is better.
Update the model name in app/api/chat/route.ts:
model: "qwen2.5-coder:7b", // replace with any model available via `ollama list`| Method | Endpoint | Description |
|---|---|---|
POST |
/api/chat |
Send a message; returns a streaming RAG response |
POST |
/api/project_files |
Upload and index a file into a project |
DELETE |
/api/project_files?fileId=&projectId= |
Remove a file from Firestore and ChromaDB |
POST |
/api/parse-file |
Parse a file and return raw text |
POST |
/api/upload |
Upload a file to Firebase Storage |
GET |
/api/debug-chroma |
Debug endpoint to inspect ChromaDB collections |
A full Docker Compose setup is in progress that will containerise the Next.js app alongside ChromaDB and Ollama, eliminating the need for any local native installs. See analysis_results.md for the planned architecture.
- Full Docker Compose setup (Next.js + ChromaDB + Ollama)
- Support for more file types (DOCX, TXT, Markdown)
- Configurable chunking strategy per project
- Model selection UI per project
- Export chat history
Pull requests are welcome. For major changes, please open an issue first to discuss what you'd like to change.
- Fork the repository
- Create your feature branch (
git checkout -b feature/your-feature) - Commit your changes (
git commit -m 'Add your feature') - Push to the branch (
git push origin feature/your-feature) - Open a Pull Request
This project is licensed under the MIT License.
Built for developers who want AI that respects their privacy. π