LlamaExtract can return a non-None response object but with data=None for certain PDFs, causing 'NoneType' object has no attribute 'get' on notebook_data. Now falls back to LLM extraction instead of failing the task. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .claude | ||
| backend | ||
| frontend | ||
| Old Readmes | ||
| scripts | ||
| .dockerignore | ||
| .DS_Store | ||
| .gitignore | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| README.md | ||
🦙 Sandbox-NotebookLM
Enterprise-ready alternative to Google NotebookLM with multi-user support, powered by LlamaIndex and multiple AI models.
Live Demo: https://ai-sandbox.oliver.solutions/notebookllama/ Repository: https://bitbucket.org/zlalani/sandbox-notebookllamalm-nextjs
🚀 Quick Start (Docker)
Prerequisites
- Docker and Docker Compose
- Git
1. Clone
git clone git@bitbucket.org:zlalani/sandbox-notebookllamalm-nextjs.git
cd sandbox-notebookllamalm-nextjs
2. Configure environment
Create backend/.env:
# Core (required)
OPENAI_API_KEY=sk-...
LLAMACLOUD_API_KEY=llx-...
# Model-specific (optional)
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_API_KEY=AI...
GROQ_API_KEY=gsk_...
DEEPSEEK_API_KEY=sk-...
# Podcast generation (optional)
ELEVENLABS_API_KEY=...
# Database
pgql_user=postgres
pgql_psw=admin
pgql_db=postgres_nextjs
# Microsoft SSO (optional)
AZURE_CLIENT_ID=your-client-id
AZURE_AUTHORITY=https://login.microsoftonline.com/your-tenant-id
AZURE_REDIRECT_URI=https://your-domain.com/notebookllama/
Create frontend/.env.production (baked into build):
NEXT_PUBLIC_API_URL=https://your-domain.com/notebookllama-back
NEXT_PUBLIC_WS_URL=wss://your-domain.com/notebookllama-back
NEXT_PUBLIC_AZURE_CLIENT_ID=your-client-id
NEXT_PUBLIC_AZURE_AUTHORITY=https://login.microsoftonline.com/your-tenant-id
NEXT_PUBLIC_AZURE_REDIRECT_URI=https://your-domain.com/notebookllama/
For local dev, use frontend/.env.local:
NEXT_PUBLIC_API_URL=http://localhost:9000
NEXT_PUBLIC_WS_URL=ws://localhost:9000
3. Build and start
docker compose up -d --build
This starts: backend (port 9000), frontend (port 4000), PostgreSQL (port 5433), Redis (port 6380).
4. Initialize database
On first run:
docker compose exec backend /app/.venv/bin/python -c \
"import sys; sys.path.insert(0, '/app/src/notebookllama'); from database import init_db; init_db(); print('Done')"
🌐 Access Points
| Service | URL |
|---|---|
| Frontend | http://localhost:4000 |
| Backend API | http://localhost:9000 |
| API Docs (Swagger) | http://localhost:9000/docs |
| Health Check | http://localhost:9000/api/health |
📚 Features
Core
- Multi-Notebook Management — organize documents into collections
- 6 AI Models — GPT-5, Claude 4.5, Gemini 2.5 Pro, GPT-4o, Gemini Flash, GPT-4
- 40+ File Formats — PDF, DOCX, PPTX, XLSX, CSV, images (OCR), audio (transcription), video (multimodal)
- Background Processing — non-blocking uploads with real-time status
- Document Summaries — AI-generated summaries, highlights, Q&A pairs
Analysis
- Cross-Document Synthesis — themes, insights, comparative findings across all docs (persists to DB)
- Studio — 7 additional output types generated from your documents:
- Flashcards — 15-20 study cards with 3D flip animation
- Quiz — 10-12 multiple choice questions with scoring
- Mind Map — SVG radial tree visualization
- Slide Deck — 8-12 slide presentation with PPTX download; slide diagrams (flowcharts, bar charts) rendered in preview and exported; supports custom .pptx template upload (Claude analyzes and rebuilds it) and per-slide AI editing without regenerating the full deck
- Report — executive summary + sections + conclusions with PDF download
- Infographic — visual blocks with stats and emojis
- Data Table — structured comparison table
- All Studio modules accept a custom prompt to guide content generation and style
Chat
- Real-time WebSocket Chat — ask questions across all documents
- Multiple Sessions — organize conversations, rename, share, delete
- Source Citations — see which documents were used
- Markdown Rendering — formatted AI responses
Podcast
- AI-Generated Podcast — two-voice audio discussion from your documents
- Customizable — length (5-30 min), voices, theme, instructions
- Built-in Player — listen or download directly
Collaboration
- Notebook Sharing — share by email with Read / Write / Share permissions
- Shared Sessions — make chat sessions visible to collaborators
Admin
- System stats, cost estimation, user management, task monitoring
Auth
- Local email/password + Microsoft SSO (MSAL PKCE)
- Role-based access (admin / regular user)
🎯 AI Models
| Model | Provider | Input | Output |
|---|---|---|---|
| GPT-5 | OpenAI | $1.25/1M | $10/1M |
| Claude 4.5 | Anthropic | $3/1M | $15/1M |
| Gemini 2.5 Pro | $1.25/1M | $5/1M | |
| GPT-4o | OpenAI | $5/1M | $15/1M |
| Gemini Flash | $0.075/1M | $0.30/1M | |
| GPT-4 | OpenAI | $30/1M | $60/1M |
🏗️ Architecture
Tech Stack
Frontend: Next.js 15 (App Router), React 19, TypeScript, Tailwind CSS 4, React Query, Zustand, MSAL, WebSocket, CSS custom property theme system (light/dark)
Backend: FastAPI, SQLAlchemy, Python 3.13, uv package manager
AI/ML: LlamaCloud (indexing), LlamaIndex (RAG), LlamaParse (parsing), LlamaExtract (structured extraction), Gemini 2.5 Pro (video multimodal), ElevenLabs (voice), python-pptx, weasyprint
Infrastructure: Docker Compose, PostgreSQL, Redis
Database Schema (10 Tables)
| Table | Key columns |
|---|---|
| users | email, username, is_admin, auth_provider |
| notebooks | name, model_type, synthesis_data, studio_data, podcast_path |
| documents | filename, llamacloud_file_id |
| notebook_documents | notebook_id, document_id |
| document_summaries | summary, highlights, questions, answers |
| chat_sessions | title, is_shared, notebook_id |
| chat_messages | role, content, sources |
| document_shares | permission_level (READ/WRITE/SHARE/ADMIN) |
| background_tasks | status, task_type |
🔌 API Endpoints
Full docs: http://localhost:9000/docs
Notebooks:
GET/POST /api/notebooks/— list / createGET/PUT/DELETE /api/notebooks/{id}— get / update / deletePOST /api/notebooks/{id}/synthesis— generate cross-doc analysisGET /api/notebooks/{id}/synthesis— get saved synthesisPOST /api/notebooks/{id}/podcast— start podcast generationPOST /api/notebooks/{id}/share— share with userGET /api/notebooks/{id}/studio— get all saved Studio outputsPOST /api/notebooks/{id}/studio/{type}— generate (flashcards / quiz / mindmap / slides / report / infographic / datatable)GET /api/notebooks/{id}/studio/slides/download— PPTX fileGET /api/notebooks/{id}/studio/report/download— PDF fileGET /api/notebooks/{id}/studio/mindmap/download— SVG filePOST /api/notebooks/{id}/studio/slides/from-template— generate PPTX from uploaded template (multipart)POST /api/notebooks/{id}/studio/slides/edit/{index}— regenerate single slide via prompt
Documents:
POST /api/documents/upload/{notebookId}— upload fileGET /api/documents/task/{taskId}— task statusGET /api/documents/{documentId}/summary— get analysis
Chat:
WS /api/chat/ws/{notebookId}?session_id={id}— real-time chatGET /api/chat/{notebookId}/sessions— list sessionsPOST /api/chat/{notebookId}/sessions— create session
Auth:
POST /api/auth/signup— registerPOST /api/auth/login— loginPOST /api/auth/microsoft— SSO login
Note: All download endpoints require the JWT token via
Authorization: Bearerheader. The frontend usesfetch()with the auth header for all binary downloads.
🔐 Microsoft SSO Setup
- Register Azure AD app (SPA with PKCE,
User.Readpermission) - Add redirect URI matching
NEXT_PUBLIC_AZURE_REDIRECT_URI - Set
AZURE_CLIENT_ID,AZURE_AUTHORITY,AZURE_REDIRECT_URIinbackend/.env - Set matching
NEXT_PUBLIC_*vars in frontend env
SSO users are auto-created on first login. Local accounts are merged if the same email logs in via SSO.
📖 Usage Guide
Create a notebook:
- My Notebooks → New Notebook → choose name + AI model
Upload documents:
- Open notebook → Select Files → wait for processing (~1 min/doc, ~1 min/10min of video)
Studio:
- Open notebook with processed documents
- Scroll to Studio section → click any card (Flashcards, Quiz, etc.)
- Results load or generate fresh; PPTX/PDF download available for Slides/Report
Cross-doc analysis:
- Click "Cross-Doc Analysis" → wait 30-60s → results persist
Podcast:
- Click "Podcast" → choose length + voices → "Generate" → wait 3-5 min
Chat:
- Click "Chat" → create/select session → ask questions
🐛 Troubleshooting
Backend 500 on all routes after deploy:
docker compose build backend && docker compose up -d backend
column notebooks.studio_data does not exist:
docker compose exec backend /app/.venv/bin/python -c \
"import sys; sys.path.insert(0, '/app/src/notebookllama'); from database import run_studio_migration; run_studio_migration(); print('Done')"
docker compose restart backend
Frontend shows old UI:
docker compose build frontend && docker compose up -d frontend
Database errors:
docker compose logs postgres --tail=30
docker compose restart postgres
Podcast stuck: Connect to DB and reset stuck tasks:
docker compose exec postgres psql -U postgres -d postgres_nextjs -c \
"UPDATE background_tasks SET status='FAILED', error_message='Timeout', completed_at=NOW() WHERE task_type='podcast_generation' AND status='IN_PROGRESS';"
🚢 Production Deployment
See scripts/ for automated migration scripts:
scripts/1_backup.sh— backup DB + files before migrationscripts/2_deploy.sh— pull, build, switch from systemd to Dockerscripts/3_cleanup.sh— remove build artifacts after verification
For developer notes, see CLAUDE.md.
📁 Project Structure
sandbox-notebookllamalm-nextjs/
├── backend/
│ ├── src/
│ │ ├── api/
│ │ │ ├── main.py
│ │ │ └── routes/ auth, notebooks, documents, chat, admin
│ │ └── notebookllama/
│ │ ├── database.py SQLAlchemy models
│ │ ├── studio_generators.py 7 Studio LLM generators
│ │ ├── audio.py podcast generation
│ │ ├── background_tasks.py
│ │ └── llm_factory.py
│ ├── Dockerfile
│ └── .env
├── frontend/
│ ├── src/
│ │ ├── app/
│ │ │ ├── notebooks/[id]/page.tsx main notebook page
│ │ │ └── ...
│ │ ├── lib/api.ts
│ │ └── types/index.ts
│ ├── Dockerfile
│ └── .env.production
├── scripts/
│ ├── 1_backup.sh
│ ├── 2_deploy.sh
│ └── 3_cleanup.sh
├── docker-compose.yml
├── CLAUDE.md
└── README.md
🎨 Theme System
All pages support light and dark mode. The toggle is in the top navigation bar (☀ / ☾).
The theme is built on CSS custom properties (--bg, --fg, --primary, --border, etc.) defined in globals.css. The selected theme is persisted to localStorage and applied before hydration to prevent flash.
Version: 3.1.0 | Updated: March 15, 2026 | Status: Production