A production-ready, self-hosted RAG AI Agent that grounds every answer in your own documents β with source citations, real-time streaming, and a full browser UI.
Most AI chatbots give you generic answers. NV-Agent is an AI agent that retrieves relevant context from your knowledge base before generating a response β so every answer is grounded, cited, and honest about what it doesn't know.
- The Problem It Solves
- What is NV-Agent?
- Why NVIDIA NIM?
- Architecture
- Key Capabilities
- Project Structure
- Quick Start
- How to Use
- Configuration
- API Reference
- Docker
- Design Decisions
- Security
- Contributing
- License
| Problem | Why It Matters | How NV-Agent Solves It |
|---|---|---|
| Generic AI = generic answers | Off-the-shelf chatbots hallucinate about your domain because they don't know your data | RAG pipeline retrieves your documents before answering β every response is grounded in your knowledge base |
| RAG is hard to set up | Most RAG tutorials leave you with a notebook, not a production-ready app | One command starts a complete system: vector store, embeddings, LLM, session persistence, and browser UI |
| Knowledge trapped in files | PDFs, DOCX, and text docs sit in folders β unsearchable, unqueryable | Drop 12+ file formats into data/ and they're instantly indexed and queryable |
| Conversations survive refresh | Most demo chatbots lose everything when you refresh the browser | Session ID saved to sessionStorage β page refresh reconnects to the same session with full history |
| No infrastructure for AI | Running LLMs locally requires GPUs, drivers, and model management | NVIDIA NIM gives you hosted state-of-the-art models via a single API key β zero local GPU needed |
NV-Agent is a complete, self-contained AI agent system β not a library, not a framework, not a notebook.
It is a RAG (Retrieval-Augmented Generation) agent that:
- Retrieves relevant document chunks from a pluggable vector knowledge base (FAISS / Qdrant / ChromaDB)
- Augments the LLM prompt with retrieved context and source citations
- Generates grounded answers via NVIDIA NIM LLMs with real-time streaming
You run one command, open a browser, and chat over your own documents:
python main.py # β http://localhost:8000The agent follows the ReAct-inspired pattern: given a user query, it reasons about what context to retrieve, fetches the most relevant chunks from the knowledge base, then generates a cited answer β explicitly stating when the KB doesn't contain the answer.
NVIDIA NIM (NVIDIA Inference Microservices) provides hosted, state-of-the-art open models via a single OpenAI-compatible API endpoint. By building on NVIDIA NIM, NV-Agent gives you:
| Benefit | Details |
|---|---|
| Zero infrastructure | No local GPUs, no model downloads, no driver headaches β just an API key |
| Best-in-class models | Nemotron, Llama, DeepSeek, and 100+ models available instantly |
| OpenAI-compatible API | Same openai Python SDK you already know β just a different base_url |
| Enterprise-grade | NVIDIA-hosted inference with reliability, rate limiting, and monitoring |
| Free tier | 1,000 credits/month at build.nvidia.com |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NV-Agent System β
β β
β ββββββββββββ ββββββββββββββββ βββββββββββββ ββββββββββββ β
β β β β β β β β β β
β β Chat βββββΆβ Chat API βββββΆβ RAG Agent βββββΆβKnowledge β β
β β UI β β (FastAPI) β β (LLM+RAG) β β Base β β
β β β β β β β β(Vector β β
β β Browser β β REST / SSE / β β Retrieve ββ β Store) β β
β β WebSocketβ β WebSocket β β Augment β β β β β
β β β β β β Generate β β β β
β ββββββββββββ ββββββββββββββββ βββββββββββββ ββββββββββββ β
β β² β² β² β² β
β β β β β β
β β ββββββ΄ββββββ ββββββ΄ββββββ ββββββ΄βββββ β
β β β Session β β NVIDIA β β Vector β β
β β β Store β β NIM API β β Store β β
β β β (Disk) β β (Cloud) β β(FAISS/ β β
β β ββββββββββββ βββββββββββββ βQdrant/ β β
β β βChromaDB)β β
β ββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β config.py (.env) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Four-layer design β each layer is a Python package with clear __init__.py and explicit __all__ exports:
| Layer | Package | Responsibility |
|---|---|---|
| Presentation | chat/ui/ |
Browser UI β React 18 + TypeScript + Tailwind v4 (built from frontend/) |
| API | chat/ |
FastAPI routes, CORS, request/response models, streaming |
| Agent | agent/ |
RAG logic (retrieve β augment β generate), session management |
| Knowledge Base | kb/ |
Document ingestion, chunking, embedding, pluggable vector store (FAISS/Qdrant/ChromaDB) |
This is the core of the AI agent β how a user question becomes a grounded, cited answer:
User asks: "What does our deployment process look like?"
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. EMBED QUERY β
β User question βββΆ nv-embed-v1 embedding βββΆ 4096-dim vector β
β (NVIDIA NIM embedding API) β
ββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. RETRIEVE β
β Query vector βββΆ Vector store search (FAISS/Qdrant/ β
β ChromaDB) βββΆ top-5 most relevant chunks from KB β
β (Choice of backend via NV_AGENT_VECTOR_STORE) β
ββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. AUGMENT β
β System prompt + retrieved chunks (with source citations) β
β + conversation history + current user message β
β β one rich message list for the LLM β
ββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 4. GENERATE β
β NVIDIA NIM LLM (Nemotron / Llama / DeepSeek / etc.) β
β β Streams answer token-by-token via WebSocket/SSE β
β β Reasoning/thinking tokens shown in collapsible block β
ββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
Agent responds with citations:
"Based on [Source: deploy.md (chunk 2)], the deployment
process involves..." or honestly: "The knowledge base
does not contain information about that."
Every answer cites its source file and chunk number. If the knowledge base doesn't contain the answer, the agent says so honestly β no hallucination.
βββββββββββββββ
β Documents β
β (12 formats)β
ββββββββ¬βββββββ
β
ββββββββΌβββββββ
β Ingestion β
β Pipeline β
ββββββββ¬βββββββ
β
βββββββββββββββΌββββββββββββββ
β β β
ββββββββΌβββββββ βββββΌβββββ ββββββββΌβββββββ
β Chunker β β Embed β β Vector β
β (Boundary β β Client β β Store β
β Aware) β β(Batch) β β(FAISS/Qdrant/β
ββββββββ¬βββββββ βββββ¬βββββ β ChromaDB) β
β β ββββββββ¬βββββββ
ββββββββββββββΌββββββββββββββ
β
βββββββββΌββββββββ
β Knowledge β
β Base Index β
β (kb/index/ or β
β Docker vol) β
βββββββββ¬ββββββββ
β
βββββββββββββββββΌββββββββββββββββ
β β β
ββββββββΌβββββββ βββββββΌβββββββ ββββββββΌβββββββ
β REST API β β SSE Streamβ β WebSocket β
β /api/chat β β /api/chat β β /api/ws/ β
β β β /stream β β chat β
ββββββββ¬βββββββ βββββββ¬βββββββ ββββββββ¬βββββββ
β β β
βββββββββββββββββΌββββββββββββββββ
β
ββββββββΌβββββββ
β Browser β
β Chat UI β
βββββββββββββββ
Browser FastAPI Worker Thread
βββββββ βββββββ βββββββββββββ
β β β
β WebSocket connect β β
βββββββββββββββββββββββββββΆβ β
β β β
β {"message": "hello"} β β
βββββββββββββββββββββββββββΆβ β
β β run_in_executor() β
β βββββββββββββββββββββββββββΆβ
β β β
β β chat_stream() generator
β β ββ yield {"type": "reasoning"}
β β queue.put("reasoning") β
β {"type":"reasoning"} ββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββ β
β β β
β β ββ yield {"type": "text"}
β β queue.put("token") β
β {"type":"token"} ββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββ β
β β ββ generator ends
β β queue.put("done") β
β {"type":"done"} ββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββ β
β β β
Why thread+queue? The OpenAI SDK is synchronous. We run chat_stream() in a worker thread, pipe events through asyncio.Queue to the async FastAPI handler β avoiding the StopIteration-in-async bug.
ββββββββββββ βββββββββββββββββ ββββββββββββββββ
β RAGAgentββββββββββΆβ SessionStore ββββββββββΆβ data/ β
β (memory)β save() β (thread-safe) β write() β sessions/ β
β βββββββββββ mutex lock ββββββββββ β {uuid}.jsonβ
ββββββββββββ load() βββββββββββββββββ read() ββββββββββββββββ
β Atomic write:
β On startup: load_all() temp.json.tmp
β On chat: save() after each message β rename()
β On delete: remove from memory + disk (POSIX atomic)
Conversations survive server restarts. Every message is timestamped and saved to disk atomically.
| Capability | How It Works |
|---|---|
| Grounded answers | Every response is backed by vector search over your documents, with source citations |
| Real-time streaming | WebSocket + SSE streaming β see the answer (and the agent's reasoning) token by token |
| Thinking/reasoning display | Agent's reasoning tokens shown in a collapsible block β see how it thinks |
| Multi-format ingestion | Drop in .pdf, .docx, .txt, .md, .py, .json, .yaml, .yml, .csv, .html, .xml, .rst β auto-indexed on startup |
| Smart chunking | Paragraph-aware β sentence-aware β word-boundary splitting with overlap β no cutting mid-sentence |
| Session persistence | Conversations survive server restarts. Every message is timestamped and saved to disk atomically |
| Session continuity on refresh | Session ID saved to sessionStorage β page refresh reconnects to the same session with full history |
| Browser file upload | Upload PDFs and Word docs directly from the UI β no CLI needed |
| Dark-themed chat UI | Responsive single-page interface β sidebar, session management, KB status panel |
| Multi-model support | Any model on NVIDIA NIM (Nemotron, Llama, DeepSeek, etc.) β just change one config value |
| Production patterns | Custom exceptions, proper HTTP status codes, thread-safe state, filename sanitization, upload size limits |
| API key auth | Optional authentication middleware β protect your instance with NV_AGENT_AUTH_KEY |
| Rate limiting | Per-IP sliding window rate limiter β configurable via NV_AGENT_RATE_LIMIT |
| Pluggable vector stores | Choose FAISS (default, zero-infra), ChromaDB, or Qdrant (high-performance Rust DB) |
nv-agent/
β
βββ main.py # π Entry point β startup sequence, init, run server
βββ config.py # βοΈ Central configuration (dataclasses, reads .env)
βββ test-agent.py # π§ͺ Quick CLI smoke test (bypasses RAG pipeline)
βββ requirements.txt # π¦ Python dependencies
βββ pyproject.toml # π§ Ruff, mypy, pytest config
βββ .env.example # π API key template (copy to .env)
βββ .gitignore # π‘οΈ Excludes .env, sessions, __pycache__, .venv
βββ .pre-commit-config.yaml # πͺ Pre-commit hooks (ruff, mypy, checks)
βββ build/ # ποΈ Build & Docker infrastructure
β βββ Dockerfile # π³ Multi-stage Docker build (Python 3.12-slim)
β βββ .dockerignore # π³ Docker build context exclusions
β βββ docker-compose.yml # π³ Compose: nv-agent + qdrant + chromadb
β βββ docker-compose.prod.yml # π³ Production compose (GHCR, Caddy, limits)
β βββ Caddyfile # π Reverse proxy config (auto HTTPS via Let's Encrypt)
β βββ deploy.sh # π One-command remote deployment script
βββ Makefile # β‘ Build, test, lint, Docker, compose shortcuts
βββ requirements-dev.txt # π¦ Dev dependencies (pytest, ruff, mypy, pre-commit, flake8, pylint)
βββ .pylintrc # π§ Pylint configuration
βββ LICENSE # π MIT License
βββ README.md # π You are here
βββ AGENTS.md # π€ AI agent guidelines and architecture
βββ CLAUDE.md # π€ Claude Code context and conventions
β
βββ data/ # π Your documents β auto-indexed on startup
β βββ sample.md # Example knowledge base doc
β βββ sessions/ # Persisted conversation JSONs (auto-created)
β
βββ kb/ # π§ Knowledge Base Layer
β βββ __init__.py # Exports: VectorStoreBase, Chunk, ingest_*, chunk_text
β βββ chunker.py # Multi-level text splitting (paragraphβsentenceβword)
β βββ embed.py # NVIDIA embedding client (singleton, batched, error-handled)
β βββ ingest.py # File ingestion + readers (PDF, DOCX, 10 text formats)
β βββ vector_store_base.py # Abstract base class (VectorStoreBase)
β βββ vector_store.py # β οΈ Legacy FAISS store (tests only), superseded by factory pattern
β βββ vector_store_faiss.py # FAISS vector store implementation
β βββ vector_store_qdrant.py # Qdrant vector store implementation
β βββ vector_store_chromadb.py # ChromaDB vector store implementation
β βββ vector_store_factory.py # Factory for creating vector stores
β βββ index/ # Saved FAISS index + chunks.json (auto-created)
β
βββ agent/ # π€ Agent Layer
β βββ __init__.py # Exports: RAGAgent, Session, Message, exceptions, SessionStore
β βββ rag_agent.py # RAG logic: retrieve β augment β generate + session mgmt
β βββ session_store.py # Thread-safe disk persistence (atomic JSON writes)
β
βββ frontend/ # βοΈ React Frontend (dev)
β βββ src/
β β βββ components/ # Chat, Sidebar, Modals, Shared UI
β β βββ context/ # Auth, Chat, KB, Toast (React Context + useReducer)
β β βββ hooks/ # useAutoResize, useClipboard, useDragDrop, useKeyboardShortcuts
β β βββ services/ # api.ts (apiFetch), markdown.ts (marked + DOMPurify)
β β βββ types/ # api.ts, chat.ts (TypeScript interfaces)
β β βββ utils/ # cn.ts (clsx + tailwind-merge), constants.tsx
β β βββ App.tsx # Root component with provider composition
β β βββ main.tsx # React 18 entry point
β β βββ index.css # Tailwind v4 + CSS variables + animations
β βββ package.json # React, Vite, Tailwind, marked, DOMPurify, clsx, tailwind-merge
β βββ vite.config.ts # Vite config (React, Tailwind, /api proxy to :8000)
β βββ tsconfig.json # Strict TypeScript config
β
βββ chat/ # π Chat API + UI Layer
β βββ __init__.py # Exports: create_app, router
β βββ app.py # FastAPI factory, middleware, CORS, static file mount
β βββ auth.py # API key authentication middleware
β βββ rate_limit.py # Per-IP sliding window rate limiter
β βββ routes.py # All endpoints: REST, SSE, WebSocket, file upload, health, auth
β βββ ui/ # React production build (generated by `npm run build:deploy`)
β βββ index.html # Entry point (loads /ui/assets/*.js, /ui/assets/*.css)
β βββ assets/ # Hashed JS/CSS bundles
β βββ favicon.svg # NVIDIA hexagon logo
β
βββ tests/ # π§ͺ Pytest test suite
β βββ conftest.py # Shared fixtures (mocks, temp dirs, test client)
β βββ test_chunker.py # Chunking unit tests
β βββ test_config.py # Configuration unit tests
β βββ test_embed.py # Embedding client unit tests (mocked API)
β βββ test_ingest.py # Ingestion pipeline unit tests
β βββ test_session_store.py# Session persistence unit tests
β βββ test_rag_agent.py # RAG agent unit tests
β βββ test_api.py # FastAPI endpoint integration tests
β
βββ .github/ # π CI/CD
β βββ workflows/
β βββ ci.yml # GitHub Actions: lint + type check + test + Docker build
β
βββ .claude/ # π§ Claude Code settings
βββ settings.local.json # Local permissions
| Decision | Why |
|---|---|
| Build step for production | React/TypeScript compiles to chat/ui/ via npm run build:deploy; Docker serves static build. Dev server: npm run dev (HMR, proxies to backend) |
| Pluggable vector stores | Factory pattern with FAISS (default, zero-infra), Qdrant (high-perf Rust), ChromaDB (Python) |
| Singleton OpenAI client | Both embed.py and rag_agent.py lazily create one client β no connection churn |
| Thread+Queue for streaming | OpenAI SDK is synchronous β worker thread + asyncio.Queue bridges syncβasync |
| Atomic file writes | Sessions and index use temp-file + rename() (POSIX atomic) β no corruption on crash |
| Skip dirs in ingestion | data/sessions/, .git, .venv are excluded β session JSONs don't pollute the KB |
| Custom exception hierarchy | RAGAgentError β SessionNotFoundError / LLMError β routes catch specific types, return proper HTTP codes |
| Filename sanitization | Upload endpoint strips path traversal (../../../etc/passwd β _.._.._.._etc_passwd) |
| OpenAI-compatible API | Using NVIDIA NIM's OpenAI-compatible endpoint means we use the standard openai SDK β no vendor lock-in |
- Python 3.11+
- NVIDIA NIM API key β create a free account at build.nvidia.com
NV-Agent accepts any of these environment variables for your API key:
NVIDIA_NIM_API_KEY(preferred)NVIDIA_API_KEYNGC_API_KEY
# 1. Clone and enter
git clone <your-repo-url> nv-agent && cd nv-agent
# 2. Create virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 3. Install dependencies
pip install -r requirements.txt
# 4. Configure your API key (uses NVIDIA_NIM_API_KEY by default)
cp .env.example .env
# Edit .env β set NVIDIA_NIM_API_KEY="nvapi-your-key-here"
# Or export any of: NVIDIA_NIM_API_KEY, NVIDIA_API_KEY, NGC_API_KEY
# 5. Add your documents (optional)
cp ~/my-docs/*.pdf ~/my-docs/*.docx data/
# 6. Start the server
python main.pyThen open http://localhost:8000 in your browser and start chatting.
- π₯οΈ Chat UI: http://localhost:8000
- π Interactive API Docs: http://localhost:8000/docs
β‘ Makefile shortcuts (optional)
make install # Install runtime dependencies
make install-dev # Install runtime + dev dependencies
make run # Run production server
make run-dev # Run with uvicorn --reload
make lint # Run flake8 + ruff + pylint
make format # Auto-format with ruff
make test # Run pytest -v
make test-cov # Run pytest with coverage
make typecheck # Run mypy
make ci # Full CI pipeline (lint + test + typecheck)
make smoke # Quick smoke: unit tests + Docker health check
make clean # Remove caches, temp files, build artifacts
# Docker / Frontend
make frontend # Build React frontend (npm ci + build:deploy)
make compose-build # Build frontend + Docker image
make compose-up # Build frontend + image, start all services (recommended)
make compose-down # Stop all services
make compose-reset # Stop, remove volumes, restart clean
make compose-logs # Follow compose logs
make compose-ps # List compose servicesTo verify your API key before launching the full server:
python test-agent.pyThis runs a standalone CLI smoke test that calls the NVIDIA NIM API directly β no FAISS index, no document ingestion, no web server. It uses a hardcoded model (deepseek-ai/deepseek-v4-pro) for quick testing. If this works, your key is valid and the full server will too.
NV-Agent supports three ways to add documents to your knowledge base:
| Method | How | Best For |
|---|---|---|
| File drop | Copy files into data/ directory before startup |
Bulk loading, initial setup |
| Browser upload | Click "Upload File" in the sidebar | Quick additions from the UI |
| API ingest | POST /api/kb/ingest with raw text, or POST /api/kb/ingest-file by path |
Automation, scripts, CI/CD |
Supported formats: .pdf, .docx, .txt, .md, .py, .json, .yaml, .yml, .csv, .html, .xml, .rst
Tip: After adding files to
data/, hit "Refresh Status" in the sidebar, or callPOST /api/kb/ingest-dirto re-index without restarting the server.
- Open http://localhost:8000
- Click "+ New Chat" in the sidebar
- Ask any question about your documents
- The agent will:
- Search the knowledge base for relevant chunks
- Show its reasoning in a collapsible "π Thinkingβ¦" block
- Stream the answer token-by-token with source citations
- Honestly say when the knowledge base doesn't contain the answer
Example questions:
- "What does the deployment process look like?" (if you have deployment docs)
- "Summarize the key points from the quarterly report" (if you uploaded a PDF)
- "What are the API conventions used in this project?" (if you added code files)
- Create: Click "+ New Chat" or
POST /api/sessions - Switch: Click a session in the sidebar β full history loads from disk
- Delete: Click the Γ button on a session β removes from memory and disk
- Persist: Automatic β every message is saved to
data/sessions/{uuid}.json - Survive restarts: Sessions are loaded from disk on server startup
- Survive browser refresh: Session ID saved to
sessionStorageβ page refresh reconnects to the same session with full history
NV-Agent works with any model on NVIDIA NIM. Change the chat model in config.py:
# In config.py, NVIDIAConfig class:
chat_model: str = "nvidia/nemotron-3-ultra-550b-a55b" # Default
# chat_model: str = "meta/llama-3.1-8b-instruct" # Fast, cheap
# chat_model: str = "deepseek-ai/deepseek-v4-pro" # Strong reasoningOr set the MODEL environment variable to override at runtime.
β οΈ If you change the embedding model: You MUST deletekb/index/to reset the vector index β mismatched dimensions will cause search failures.
All settings are in config.py with sensible defaults. Override via environment variables.
| Variable | Required | Default | Description |
|---|---|---|---|
NVIDIA_NIM_API_KEY |
β | β | Your NVIDIA NIM API key (also accepts NVIDIA_API_KEY, NGC_API_KEY) |
MODEL |
β | nvidia/nemotron-3-ultra-550b-a55b |
Override the chat model |
NV_AGENT_AUTH_KEY |
β | β | API key for auth middleware (when set, all /api/* require X-API-Key) |
NV_AGENT_RATE_LIMIT |
β | 60/minute |
Rate limit per IP (format: N/unit, e.g., 10/second, 100/hour) |
NV_AGENT_VECTOR_STORE |
β | faiss |
Vector store backend: faiss, chromadb, or qdrant |
| Backend | Required Env Vars | Optional Env Vars | Notes |
|---|---|---|---|
| FAISS (default) | β | β | Zero-infrastructure, index on disk, no external service |
| Qdrant | NV_AGENT_VECTOR_STORE=qdrant |
NV_AGENT_QDRANT_COLLECTION, NV_AGENT_QDRANT_PATH (local mode), or NV_AGENT_QDRANT_HOST, NV_AGENT_QDRANT_PORT, NV_AGENT_QDRANT_API_KEY (server mode) |
High-performance Rust vector DB |
| ChromaDB | NV_AGENT_VECTOR_STORE=chromadb |
NV_AGENT_CHROMADB_COLLECTION, NV_AGENT_CHROMADB_PERSIST_DIR |
Open-source embedding DB, local file-based |
Install optional dependencies:
# ChromaDB
pip install chromadb
# Qdrant
pip install qdrant-client| Setting | Default | What It Controls |
|---|---|---|
chat_model |
nvidia/nemotron-3-ultra-550b-a55b |
LLM model ID (any model on NVIDIA NIM) |
embedding_model |
nvidia/nv-embed-v1 |
Embedding model (must match embedding_dim) |
embedding_dim |
4096 | Vector dimension β must match embedding model output |
temperature |
1.0 | LLM sampling temperature |
top_p |
0.95 | Nucleus sampling threshold |
max_tokens |
16384 | Max response tokens |
enable_thinking |
True | Show reasoning/thinking tokens in streaming |
reasoning_budget |
16384 | Token budget for reasoning |
chunk_size |
512 | Characters per chunk |
chunk_overlap |
64 | Overlap characters between chunks |
top_k |
5 | Number of context chunks to retrieve per query |
data_dir |
./data |
Directory for knowledge base documents |
port |
8000 | Server port |
cors_origins |
["*"] |
CORS allowed origins |
All endpoints are under /api. Full interactive docs at /docs.
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/chat |
Send a message, get a full response |
POST |
/api/chat/stream |
Send a message, get a streaming SSE response |
WS |
/api/ws/chat |
WebSocket chat with real-time token streaming |
Request body (chat + stream):
{ "session_id": "uuid", "message": "What does the KB say about X?" }SSE events:
data: {"token": "answer "}
data: {"reasoning": "thinking..."}
data: [DONE]
WebSocket messages:
β {"message": "hello"}
β {"type": "token", "content": "answer "}
β {"type": "reasoning", "content": "thinking..."}
β {"type": "done", "full": "complete answer"}
β {"type": "error", "content": "error message"}| Method | Endpoint | Description |
|---|---|---|
POST |
/api/sessions |
Create a session (optional ?title=My+Chat) |
GET |
/api/sessions |
List all sessions with titles |
GET |
/api/sessions/{id}/history |
Get messages (?limit=50) |
DELETE |
/api/sessions/{id} |
Delete a session |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/kb/status |
Chunk count + index status |
POST |
/api/kb/ingest |
Ingest raw text (JSON body: {text, source}) |
POST |
/api/kb/ingest-dir |
Re-scan data/ and index all files |
POST |
/api/kb/ingest-file |
Ingest a file by server path |
POST |
/api/kb/upload |
Upload file (multipart/form-data, 12 formats supported) |
DELETE |
/api/kb/reset |
Clear the entire knowledge base |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/health |
Returns {"status": "ok", "kb_chunks": N} |
GET |
/api/health/detailed |
Full system info: model, KB status, auth, rate limit, active sessions |
GET |
/api/auth/validate |
Validate your NVIDIA NIM API key β returns {valid, model, error} |
Run NV-Agent in a container β no Python installation needed on the host.
# Build frontend + run with docker compose
cp .env.example .env # Set your NVIDIA API key
make compose-up # Builds frontend (npm run build:deploy) + Docker image, then starts all services
# Or step-by-step:
cd frontend && npm run build:deploy # Build React β chat/ui/
docker compose -f build/docker-compose.yml up -d --build # Build image (copies chat/ui/) + start
# Or build manually
docker build -f build/Dockerfile -t nv-agent .
docker run -p 8000:8000 --env-file .env -v $(pwd)/data:/app/data nv-agentNote: The Docker image copies the pre-built
chat/ui/directory. You must runnpm run build:deploy(ormake frontend) before building the Docker image β the Dockerfile does not run the frontend build. The Makefilecompose-uptarget handles this automatically viacompose-build.
Docker Compose persists your documents and FAISS index in named volumes, so they survive container restarts. Note: the nv-agent-sessions volume overrides the ./data:/app/data bind mount at the /app/data/sessions subpath β sessions live inside the Docker volume, not on the host filesystem at ./data/sessions/.
cp .env.example .env
# Add to .env:
# NV_AGENT_VECTOR_STORE=qdrant
# NV_AGENT_QDRANT_HOST=qdrant
# NV_AGENT_QDRANT_PORT=6333
docker compose up -d --buildThis starts both NV-Agent and Qdrant containers, with Qdrant persisting data in a named volume.
# Add to .env:
# NV_AGENT_VECTOR_STORE=chromadb
docker compose up -d --build# With auth key and custom rate limit
docker run -p 8000:8000 \
--env-file .env \
-e NV_AGENT_AUTH_KEY=my-secret-key \
-e NV_AGENT_RATE_LIMIT="30/minute" \
nv-agent| Backend | Pros | Cons | Best For |
|---|---|---|---|
| FAISS | Zero config, fast, no extra container | Single-process only, no scaling | Dev, single-user, small KB |
| ChromaDB | Easy local setup, good features | Python-based, slower at scale | Medium KB, local dev with persistence |
| Qdrant | Rust performance, filtering, distributed, server mode | Extra container, more resources | Production, large KB, multi-user, filtering |
For production on a remote VPS/server, use the production compose file with a reverse proxy:
# 1. Copy production files to server
scp build/docker-compose.prod.yml build/Caddyfile .env.prod.example user@your-server:~/
# 2. On server: setup
ssh user@your-server
mkdir -p /opt/nv-agent && cd /opt/nv-agent
cp ~/build/docker-compose.prod.yml ~/build/Caddyfile .
cp .env.prod.example .env.prod # Edit with your API keys, domain
# 3. Configure Caddyfile: replace 'yourdomain.com' with your actual domain
# 4. Configure .env.prod: add NVIDIA_NIM_API_KEY, NV_AGENT_AUTH_KEY, etc.
# 5. Deploy (uses pre-built GHCR image)
docker compose -f build/docker-compose.prod.yml up -d
# Or use the deploy script from local machine
./build/deploy.sh --logsProduction compose (docker-compose.prod.yml) includes:
- Pre-built GHCR image (no build on server)
- Named volumes only (no host path dependencies)
- Resource limits (CPU/memory)
- Log rotation (10MB, 3 files)
- Caddy reverse proxy (automatic HTTPS via Let's Encrypt)
- Health checks with restart policies
Required environment (.env.prod):
NVIDIA_NIM_API_KEY=nvapi-your-key
NV_AGENT_AUTH_KEY=your-secure-random-key # openssl rand -hex 32
NV_AGENT_RATE_LIMIT=60/minute
NV_AGENT_VECTOR_STORE=faiss # or qdrant/chromadb
ACME_EMAIL=you@example.com # for Let's Encrypt (optional)CI/CD: Push to main or tag v* β GitHub Actions builds multi-arch image β pushes to GHCR. Server pulls via docker compose pull.
| Measure | Details |
|---|---|
| No API key in source control | .env is in .gitignore β never commit it |
| Optional API key auth | Set NV_AGENT_AUTH_KEY to require X-API-Key header on all /api/* routes |
| Per-IP rate limiting | Default: 60 req/min per IP. Override with NV_AGENT_RATE_LIMIT (e.g., "10/second") |
| Filename sanitization | Path traversal stripped, null bytes removed, special chars replaced with _ |
| Upload size limit | 50 MB max (MAX_UPLOAD_SIZE in routes.py) |
| Extension validation | Only SUPPORTED_EXTENSIONS allowed β 400 if unsupported |
| Generic error responses | Internal details logged server-side, clients get "Internal error" |
| Custom exception hierarchy | Specific HTTP codes: 401 (auth), 429 (rate limit), 404, 502, 500 |
We welcome contributions! See AGENTS.md for the full architecture guidelines and coding standards.
git clone <your-repo-url> && cd nv-agent
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
pip install -r requirements-dev.txt # Dev tools: pytest, ruff, mypy, pre-commit, flake8, pylint
cp .env.example .env # Add your NVIDIA API key
pre-commit install # Optional: set up git hooks- Fork the repo and create a feature branch from
main - Make your changes β follow the code style in AGENTS.md
- Test manually β run
python main.pyand verify your change works end-to-end - Commit clearly β use descriptive messages (
Add X support,Fix Y bug) - Open a Pull Request β describe what you changed and why
- Add a reader function in
kb/ingest.pyβ follow the_read_pdf/_read_docxpattern (lazy import, per-page/per-section error handling) - Register it in the
_READERSdict - Add the extension to
SUPPORTED_EXTENSIONSinchat/routes.py - Update the
<input accept="...">attribute infrontend/src/components/sidebar/KBSection.tsx - Add the extension to
SUPPORTED_EXTENSIONSinfrontend/src/utils/constants.tsx - Add the dependency to
requirements.txt - Test by uploading a file through the UI
NV-Agent uses the OpenAI-compatible API format via config.nvidia.base_url. To switch providers:
- Update
config.pyβ changebase_url,chat_model,embedding_model,embedding_dim - Delete
kb/index/to reset the FAISS vector index (different embedding = different dimensions) - For Qdrant/ChromaDB backends, call
DELETE /api/kb/resetto clear computed vectors - Set the appropriate API key env var
This project is licensed under the MIT License.