Skip to content

Repository files navigation

LearnForge

Turn sources into understanding.

LearnForge is a self-hosted study notebook (inspired by Google NotebookLM). You collect materials in a workspace, then chat with them and generate study tools — summaries, mind maps, quizzes, and flashcards — with answers grounded in your sources.


Features

  • Workspaces — One notebook per topic, course, or exam. Search, rename, or delete them from the home page.
  • Many source types — PDF, pasted text, web links, YouTube videos (captions), and VTT caption files.
  • Grounded chat — Ask questions and get streaming answers with clickable citations. Click a citation to see the original chunk (PDF page, video timestamp, URL, or transcript cue).
  • Optional web search — Turn on the globe in chat when your sources are not enough (uses Tavily).
  • Learn (Studio) — Generate a summary, mind map, quiz, or flashcards from ready sources. Pick which sources to include; quizzes and flashcards let you set how many items.
  • Source viewer — Open a source to inspect it; PDFs can be viewed in-app when the file is stored.
  • Sign in — Email + password, or Google / GitHub OAuth.
  • Themes — Dark by default, with a light/dark toggle that remembers your preference.

How to use

1. Sign in

Open the app and go to /login.

  • Create an account with email and password, or
  • Sign in with Google or GitHub, or
  • After seeding (see Setup), click Use demo account (testuser@email.com / 12345678) to explore sample workspaces.

2. Create a workspace

On the home page, click New workspace and give it a name — for example “Intro to ML” or “Exam revision”. Open a workspace to start adding materials.

3. Add sources

In the Sources tab, click add source and choose a type:

Type What to provide
PDF Upload a .pdf (max 20MB)
Text Paste notes or excerpts
Web link A public article or page URL
YouTube A video URL that has captions
VTT Upload a caption / subtitle file

Wait until the status is ready (green). Indexing can take a few seconds. Chat and Learn tools only use sources that have finished indexing.

4. Ask in chat

Type a question in the chat panel. Answers are based on the workspace’s indexed sources and include numbered citations.

  • Click a citation badge to open the Source Viewer on the cited passage.
  • Toggle the globe if you want live web results alongside your sources (needs TAVILY_API_KEY).

5. Study in Learn

Open the Learn tab and generate:

  • Summary — a markdown overview of the selected sources
  • Mind map — a visual map of the main ideas
  • Quiz — multiple-choice questions you can take in the app
  • Flashcards — cards you can flip through

Choose which ready sources to include. Generated items stay in the workspace so you can reopen them later.

6. Manage as you go

  • Rename or delete a workspace from the home page or the workspace header menu.
  • Re-index or delete a source from its card if content failed or you want a fresh index.

Setup

Prerequisites

Steps

git clone <this-repo>
cd learnforge
npm install

cp .env.example .env
# fill in DATABASE_URL, QDRANT_URL, QDRANT_API_KEY, OPENAI_API_KEY,
# FIRECRAWL_API_KEY (required for web links), JWT_SECRET

npm run db:push   # creates tables in Neon via Drizzle
npm run db:seed   # optional: populate demo account (needs OpenAI + Firecrawl + Qdrant)
npm run dev       # http://localhost:3000

Then sign up on /login, create a workspace, add a source, and start asking questions.

Demo data: after npm run db:seed, click Use demo account on /login. Re-run npm run db:seed anytime to reset and rebuild that account’s workspaces, sources, and Learn artifacts (takes a few minutes).

Environment variables

Variable Description
DATABASE_URL Neon Postgres connection string
QDRANT_URL Qdrant Cloud cluster URL
QDRANT_API_KEY Qdrant Cloud API key
OPENAI_API_KEY OpenAI API key
OPENAI_EMBEDDING_MODEL default text-embedding-3-small
OPENAI_CHAT_MODEL default gpt-4o-mini
FIRECRAWL_API_KEY Required for URL sources
TAVILY_API_KEY Optional. Chat web-search toggle
BLOB_READ_WRITE_TOKEN Optional. Store uploaded PDFs and VTT files
JWT_SECRET Random string for signing session cookies
NEXT_PUBLIC_APP_URL Optional. Canonical app URL for OAuth callbacks
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET Optional. Google sign-in
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET Optional. GitHub sign-in

Architecture (for developers)

Built with Next.js (App Router), Postgres on Neon, Qdrant Cloud for vectors, OpenAI for embeddings + chat, and LangChain for chunking.

┌─────────────┐      ┌──────────────────────┐      ┌────────────────────┐
│   Next.js    │      │   Neon Postgres      │      │   Qdrant Cloud      │
│  App Router  │◄────►│  users / workspaces /  │      │  1 collection per   │
│  (UI + API)  │      │  sources / messages   │      │  user, filtered by  │
│              │      │  studio artifacts     │      │  workspaceId/sourceId│
└──────┬───────┘      └──────────────────────┘      └─────────┬───────────┘
       │                                                        ▲
       │ extract → chunk (LangChain) → embed (OpenAI)            │
       └────────────────────────────────────────────────────────┘
  • Postgres stores accounts, workspaces, source status, chat history, and Learn artifacts.
  • Qdrant stores vectors plus a small payload (chunk text, source id, page/timestamp).
  • Isolation: each user gets a Qdrant collection (user_<uuid>). Search is always filtered by workspaceId.

Ingestion runs as an async job from POST /api/sources; the UI polls until the source is ready.

How retrieval works

Indexing: extract text (Firecrawl for web links) → split into ~800-character chunks → embed with OpenAI → upsert into Qdrant → status ready.

Chat: embed the question → search Qdrant in the current workspace → stream an answer that cites numbered excerpts. The model is instructed to stay grounded in those excerpts and to treat source text as data, not as instructions.

Guardrails

  • Rate limits on workspace creation, uploads, and chat
  • File size (20MB) and MIME allow-list
  • Length caps on pasted text and questions
  • SSRF guard on URL/YouTube sources (blocks private/local addresses)
  • Session cookie (JWT) plus ownership checks on every API route

Known limitations

  • Qdrant cleanup: deleting a workspace cascades in Postgres but does not always remove leftover vectors. Call deleteSourceChunks per source before deleting workspaces in production.
  • Rate limiting is in-memory per server instance — fine for a single process, not for many replicas.
  • Web search and file uploads need TAVILY_API_KEY and BLOB_READ_WRITE_TOKEN respectively; without them those paths will fail or skip storage.

Design

Dark mode is the default. The palette uses deep ink tones with a warm saffron accent. Cards use rounded corners, soft shadows, and thin borders; the header uses a frosted-glass blur. The sun/moon toggle switches themes immediately and remembers your preference.

Releases

Packages

Contributors

Languages