A self-hosted AI agent with RAG, long-term memory, tool use, and a streaming chat UI β powered by any OpenAI-compatible local model.
| Feature | |
|---|---|
| π§ | Agentic loop β multi-round tool use with automatic context refresh |
| π | RAG β ingest Markdown, text, CSV, JSON, PDF, and log files into a LanceDB vector store |
| πΎ | Long-term memory β SQLite-backed key-value memory the agent can read and write |
| π οΈ | Built-in tools β get_time, read_file, create_file, save_to_memory, remove_from_memory, ingest_documents, forget_document |
| β‘ | Streaming β SSE streaming on both /agent/stream and /chat/stream endpoints |
| π₯οΈ | Web UI β single-file HTML client with dark/light themes, tool-use pills, and Markdown rendering |
| π | Security β rate limiting, request body size cap, input validation, path traversal protection |
| π | Model-agnostic β works with LM Studio, Ollama, or any OpenAI-compatible API |
βββββββββββββββββββββββββββββββββββββββ
β client/index.html β β single-file web UI (dark/light, SSE streaming)
ββββββββββββββββββββ¬βββββββββββββββββββ
β HTTP / SSE
ββββββββββββββββββββΌβββββββββββββββββββ
β src/server.ts β β Express 5 API server
β POST /agent/stream (SSE) β
β POST /agent β
β POST /chat/stream (SSE) β
β POST /chat β
β POST /ingest β
β DELETE /ingest/:filename β
β GET /health β
ββββββββ¬ββββββββββββββββββββ¬βββββββββββ
β β
ββββββββΌβββββββ βββββββββΌβββββββββ
β src/agent β β src/rag/ β
β .ts β β ingest.ts β
β agent loop β β retriever.ts β
ββββββββ¬βββββββ βββββββββ¬βββββββββ
β β
ββββββββΌβββββββ βββββββββΌβββββββββ
β SQLite β β LanceDB β
β memory.db β β vector store β
βββββββββββββββ ββββββββββββββββββ
β
ββββββββΌβββββββββββββββββββββββββββββββ
β OpenAI-compatible API β
β (LM Studio Β· Ollama Β· OpenAI) β
βββββββββββββββββββββββββββββββββββββββ
- Node.js 18+
- An OpenAI-compatible model server running locally β LM Studio is recommended
git clone https://github.com/your-username/AI_Assistant.git
cd AI_Assistant
npm installcp .env.example .envEdit .env:
LMSTUDIO_BASE_URL=http://127.0.0.1:1234/v1
MODEL_ID=qwen2.5-3b-instruct
EMBEDDING_MODEL=text-embedding-nomic-embed-text-v1.5Tip: In LM Studio, load a chat model and an embedding model, then start the local server on port
1234.
Drop any .md, .txt, .csv, .json, .log, or .pdf files into the docs/ folder.
# Development (hot reload)
npm run dev
# Production
npm run build && npm startOpen http://localhost:3000 β the web UI loads automatically.
The client (client/index.html) is a zero-dependency single-file UI served by the Express server.
- Agent mode β full tool use, memory, and RAG retrieval with live streaming
- Chat mode β direct streaming conversation with the model
- Tool pills β animated indicators show which tool is running in real time
- Markdown β rich rendering with syntax-highlighted code blocks and copy buttons
- Dark / Light theme β toggled from the sidebar, persisted in
localStorage - Mobile-friendly β collapsible sidebar with overlay
All endpoints accept and return JSON. Streaming endpoints use Server-Sent Events.
Stream an agent turn with real-time tool and token events.
Non-streaming agent turn. Returns { "answer": "..." }.
Stream a plain chat completion (no tools).
// Request
{ "messages": [{ "role": "user", "content": "Hello!" }] }
// SSE events
{ "token": "Hi" }
{ "done": true }Scan docs/ and (re-)ingest all supported files into the vector store.
Remove all vector chunks for a specific file. Returns { "filename": "...", "removed": 12 }.
Returns { "status": "ok" }.
AI_Assistant/
βββ client/
β βββ index.html # Web UI (no build step needed)
βββ docs/ # Drop documents here for RAG ingestion
βββ src/
β βββ agent.ts # Agentic loop & event streaming
β βββ server.ts # Express server & route definitions
β βββ lib/
β β βββ openai.ts # Shared OpenAI client
β β βββ constants.ts # Shared constants (MAX_CONTENT_CHARS, β¦)
β βββ memory/
β β βββ longterm.ts # SQLite-backed key-value memory
β βββ rag/
β β βββ ingest.ts # Document chunking & embedding
β β βββ retriever.ts # Vector similarity search
β βββ tools/
β βββ filesystem.ts # read_file, create_file
β βββ memory.ts # save_to_memory, remove_from_memory
β βββ rag.ts # ingest_documents, forget_document
β βββ time.ts # get_time
βββ .env.example
βββ package.json
βββ tsconfig.json
- Define the tool schema and handler in
src/tools/:
// src/tools/weather.ts
export const get_weather = {
type: 'function' as const,
function: {
name: 'get_weather',
description: 'Get current weather for a city',
parameters: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
},
};
export async function run_get_weather(args: { city: string }): Promise<string> {
// ... fetch weather API
return `It's sunny in ${args.city}.`;
}- Register it in
src/agent.ts:
import { get_weather, run_get_weather } from './tools/weather.js';
const TOOLS = [ ..., get_weather ];
const toolHandlers = { ..., get_weather: (a) => run_get_weather(a) };Pull requests are welcome! For major changes, please open an issue first to discuss what you'd like to change.
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Commit your changes:
git commit -m 'feat: add my feature' - Push and open a Pull Request