Wenlan
Wenlan is a source-backed AI knowledge base and LLM wiki for AI work: organize documents and decisions into pages you can find, inspect, and review.
翻訳待ち
No updates detected yet. GitHub releases will appear here.
DocMind: chat with your PDFs and notes, with answers that cite the page. RAG on Gemini embeddings + TiDB vector search, FastAPI + React, live on Vercel.
翻訳待ち
Loading the full README.
Read the full README ↗Live demo: https://docmind-kohl-pi.vercel.app (free, no card; sign up with any email. It's 18+ only, and uploads go to Google's free Gemini tier, so don't upload anything confidential.) The API is at https://docmind-api-kappa.vercel.app/health.
A multi-user AI knowledge workspace: upload documents, organize them into
collections, and chat against one document, several, or a whole collection —
with every answer exposing exactly which chunks (and, for PDFs, which real
page numbers) it was grounded in, and an explicit refusal when nothing in
your documents supports the question. Started as a small single-file RAG
demo (chat-with-your-documents); this is the full-stack, production-shaped
version: JWT auth (including password reset), a relational data model,
per-user document CRUD, collections, persisted conversations, page-accurate
citations, a command palette, real usage analytics, and a small honest RAG
evaluation framework — all on top of an embedding retrieval pipeline that runs
entirely inside TiDB Cloud (native vector search).
| Landing page | Documents |
|---|---|
The screenshots are from the live deployment, using a demo account and a fictional sample handbook (both deleted afterwards).
Upload → Organize → Search → Chat → Cite → Save
Public pages (Landing, Features, Login, Signup, Forgot/Reset Password) sit in front of the authenticated workspace (Dashboard, Documents, Collections, Chat, Conversations, Sources, Analytics, Settings) — a "Liquid Glass" design system (blue/cyan/white/soft-neutral glass, restrained — no neon) used consistently across both.
backend/ FastAPI service
main.py app wiring, CORS, lifespan (DB init)
config.py env var loading
db.py SQLAlchemy engine/session
models.py User, Document, Collection, Conversation, Message, Source,
PasswordResetToken, EvalCase
schemas.py Pydantic request/response models
auth.py password hashing (bcrypt) + JWT + reset-token hashing
ratelimit.py hand-rolled in-memory per-IP rate limiter
deps.py current-user dependency, ownership-check helper, injectable Gemini client
ingestion.py file validation + per-page text extraction (page numbers survive here)
rag.py chunking, Gemini embeddings, vector retrieval in TiDB, scoped by user_id/document_id
chat_logic.py Gemini prompting + streaming (shared by /conversations/{id}/messages);
distinguishes "no documents in scope" (casual chat) from "documents in
scope but nothing relevant retrieved" (must refuse, not free-answer)
routers/ one router per resource: auth, documents, collections, conversations,
dashboard, sources, evaluation
tests/ pytest suite (73 tests) — see "Testing" below
frontend/ React 19 + Vite + React Router + Tailwind CSS v4
src/api/ fetch wrapper + one module per resource
src/context/ AuthContext, ToastContext, ThemeContext
src/hooks/useChatStream.js streaming fetch + citation parsing, shared by the chat page
src/components/glass/ Glass* component library (Card, Button, Input, Modal, Toast wrapper,
Skeleton, EmptyState, DocumentCard, ChatBubble/SourceCard re-exports,
Navbar, Sidebar)
src/components/ layout/, chat/, documents/, collections/, dashboard/, common/
src/pages/ Landing, Features, Login, Signup, ForgotPassword, ResetPassword,
Dashboard, Documents, DocumentDetail, Collections, CollectionDetail,
NewChat, Chat, Conversations, Sources, Analytics (Usage + Evaluation
tabs), Settings, NotFound
Data model. One database holds everything. In production that's TiDB
Cloud (MySQL-compatible, free tier): relational rows, the original uploaded
bytes (documents.file_data), and chunk embeddings in a native
VECTOR(768) column (chunks.embedding). Locally and in tests it's SQLite,
with vectors stored as JSON and cosine distance computed in Python. The
structure: Document.collection_id is a nullable one-to-one FK (a
document lives in at most one collection, matching the "folder" metaphor —
University/{Database.pdf, DSA.pdf, Networks.pdf}). Conversation records
its retrieval scope as either a collection_id or a many-to-many set of
documents (conversation_documents). Source is its own table: one row
per citation on a specific assistant Message (which document, which
chunk, which real page number if any, the retrieved snippet, the retrieval
distance, and its rank) — not a duplicate of Document, since it's
per-message citation facts, not document identity. PasswordResetToken
follows the same "never store the secret itself" discipline as password
hashing — only a SHA-256 hash of the token is persisted.
This is the beginning of the README. Read the rest at the original. Read the full README ↗