299 lines
11 KiB
Markdown
299 lines
11 KiB
Markdown
# LibreChat Analytics Dashboard
|
||
|
||
Usage analytics dashboard for LibreChat. Queries the existing MongoDB to show model/agent usage, costs, and top users with time filtering.
|
||
|
||
## Features
|
||
|
||
- **Overview** — Total cost, tokens, active users, conversations with time-series charts
|
||
- **Users** — Top users ranked by cost with token and conversation counts
|
||
- **Models** — Model breakdown with prompt/completion token and cost split
|
||
- **Agents** — Agent usage with underlying model resolution
|
||
- **Time Filtering** — 24H, 7D, 30D, or custom date range
|
||
- **Auto-refresh** — Updates every 60 seconds
|
||
- **API Key Auth** — Dashboard protected by API key (prompted on first visit)
|
||
|
||
## Prerequisites
|
||
|
||
- Docker and Docker Compose
|
||
- LibreChat already running with MongoDB
|
||
- Access to LibreChat's nginx config (for proxy setup)
|
||
|
||
## Installation
|
||
|
||
### 1. Clone the repo
|
||
|
||
```bash
|
||
cd /opt
|
||
git clone https://x-token-auth:{ACCESS_TOKEN}@bitbucket.org/zlalani/librechat-analytics.git
|
||
cd librechat-analytics
|
||
```
|
||
|
||
Or with SSH:
|
||
```bash
|
||
git clone git@bitbucket.org:zlalani/librechat-analytics.git
|
||
```
|
||
|
||
### 2. Configure environment
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
nano .env
|
||
```
|
||
|
||
Set the following values:
|
||
```
|
||
MONGO_URI=mongodb://mongodb:27017/LibreChat
|
||
DASHBOARD_API_KEY=your-secure-key-here
|
||
PORT=3001
|
||
```
|
||
|
||
**Important:**
|
||
- Change `DASHBOARD_API_KEY` to a secure value. This is required to access the dashboard.
|
||
- Do NOT use `&` or other special characters in the API key (breaks URL parsing).
|
||
- `mongodb` in the URI is the container name of LibreChat's MongoDB — change if yours is different.
|
||
|
||
### 3. Build and run
|
||
|
||
```bash
|
||
docker compose up -d --build
|
||
```
|
||
|
||
The container automatically joins LibreChat's Docker network (`librechat_default`) so it can reach MongoDB. If your LibreChat network has a different name, edit `docker-compose.yml` and change the `name:` under the `librechat` network.
|
||
|
||
### 4. Configure Nginx
|
||
|
||
Edit LibreChat's nginx config to proxy the dashboard. The nginx config location depends on your setup:
|
||
|
||
- **Standard deploy:** `/home/<user>/LibreChat/client/nginx.conf`
|
||
- **Alternate location:** `/opt/LibreChat/client/nginx.conf`
|
||
|
||
Add this location block **before** the `location /api` block:
|
||
|
||
```nginx
|
||
location /librechat-analytics/ {
|
||
proxy_pass http://librechat-analytics:3001/;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
```
|
||
|
||
Then reload nginx:
|
||
```bash
|
||
docker exec LibreChat-NGINX nginx -s reload
|
||
```
|
||
|
||
### 5. Verify
|
||
|
||
```bash
|
||
# Health check (no auth required)
|
||
curl http://localhost:3001/health
|
||
|
||
# Test API (requires API key)
|
||
curl -H "x-api-key: your-secure-key-here" 'http://localhost:3001/api/summary?period=30d'
|
||
```
|
||
|
||
### 6. Access the dashboard
|
||
|
||
Open `https://your-domain/librechat-analytics/` in your browser.
|
||
|
||
On first visit you'll be prompted to enter the API key. It gets saved in your browser's localStorage so you only need to enter it once.
|
||
|
||
## Updating
|
||
|
||
```bash
|
||
cd /opt/librechat-analytics
|
||
git pull
|
||
docker compose up -d --build
|
||
```
|
||
|
||
No need to reconfigure nginx or re-enter the API key after updates.
|
||
|
||
## Ports
|
||
|
||
| Service | Port | Notes |
|
||
|---------|------|-------|
|
||
| Analytics Dashboard | 3001 | Bound to 127.0.0.1 only (not exposed externally) |
|
||
| LibreChat | 3080 | No conflict |
|
||
| Code Interpreter | 8000/8005 | No conflict |
|
||
|
||
The dashboard is only accessible externally through the nginx proxy.
|
||
|
||
## Authentication
|
||
|
||
The dashboard is protected by an API key set in `.env`. When accessing the dashboard:
|
||
|
||
1. Browser prompts for the API key on first visit
|
||
2. Key is stored in browser localStorage
|
||
3. All API requests include the key in the `x-api-key` header
|
||
|
||
To reset the key in your browser: open DevTools (F12) > Application > Local Storage > delete `analyticsApiKey`, then refresh.
|
||
|
||
## Architecture
|
||
|
||
```
|
||
https://your-domain
|
||
|
|
||
+-------v--------+
|
||
| Nginx Proxy |
|
||
| (LibreChat) |
|
||
+---+-------+----+
|
||
| |
|
||
/api, /chat | | /librechat-analytics/*
|
||
| |
|
||
+-------v--+ +-v------------------+
|
||
| LibreChat | | Analytics Dashboard |
|
||
| :3080 | | :3001 |
|
||
+-------+---+ +---------+----------+
|
||
| |
|
||
| MongoDB |
|
||
| queries |
|
||
| |
|
||
+---v----------------v---+
|
||
| MongoDB |
|
||
| (LibreChat database) |
|
||
| |
|
||
| collections: |
|
||
| - transactions |
|
||
| - users |
|
||
| - agents |
|
||
+------------------------+
|
||
```
|
||
|
||
```
|
||
User sends message in LibreChat
|
||
|
|
||
v
|
||
+------------------+ +------------------+ +-------------------+
|
||
| LibreChat calls |---->| LLM processes |---->| LibreChat records |
|
||
| LLM API | | prompt + reply | | transaction |
|
||
| (Claude, GPT, | | returns tokens | | in MongoDB |
|
||
| Gemini, etc.) | | used | | |
|
||
+------------------+ +------------------+ +--------+----------+
|
||
|
|
||
Transaction record: |
|
||
+----------------------------+ |
|
||
| user: ObjectId |<+
|
||
| model: "claude-sonnet-4-6" |
|
||
| tokenType: "prompt" |
|
||
| rawAmount: -15234 |
|
||
| tokenValue: -45.702 |
|
||
| (rawAmount x $3/1M) |
|
||
| createdAt: Date |
|
||
+----------------------------+
|
||
|
|
||
v
|
||
+----------------------------+
|
||
| Analytics Dashboard reads |
|
||
| tokenValue from MongoDB |
|
||
| |
|
||
| cost = |tokenValue| / 1M |
|
||
| = 45.702 / 1000000 |
|
||
| = $0.000046 |
|
||
+----------------------------+
|
||
```
|
||
|
||
## Cost Calculation
|
||
|
||
### How it works
|
||
|
||
The dashboard does **not** calculate costs itself. Costs come directly from LibreChat's MongoDB `transactions` collection, which records every API call.
|
||
|
||
When a user sends a message, LibreChat:
|
||
1. Records the token usage in the `transactions` collection with fields like `rawAmount` (token count, stored as negative), `tokenType` (prompt or completion), and `model`
|
||
2. Looks up the model's pricing from its internal pricing table (`api/models/tx.js`)
|
||
3. Calculates `tokenValue = rawAmount × rate` where rate is USD per 1M tokens
|
||
4. Stores `tokenValue` alongside the transaction
|
||
|
||
The dashboard reads `tokenValue` and converts to USD: **`cost = Math.abs(tokenValue) / 1,000,000`**
|
||
|
||
This means costs reflect the rate that was active *at the time of the API call*. If pricing changes, old transactions keep their original cost (which is correct — they were billed at the old rate).
|
||
|
||
### Where pricing lives
|
||
|
||
The pricing table is in LibreChat's codebase at `api/models/tx.js`. It uses a longest-match pattern strategy — e.g. `claude-sonnet-4-6` matches before the generic `claude-` fallback.
|
||
|
||
### Agent transactions
|
||
|
||
When a user interacts with an Agent, LibreChat records `model` as `agent_XYZ` (the agent's ID). The dashboard resolves these to the underlying LLM model via the `agents` collection (cached, refreshed every 5 minutes).
|
||
|
||
### Verified pricing (March 2026)
|
||
|
||
The following rates from LibreChat's `tx.js` have been validated against official provider pricing pages:
|
||
|
||
**Anthropic Claude** (source: platform.claude.com/docs/en/about-claude/pricing)
|
||
|
||
| Model | Input ($/1M tokens) | Output ($/1M tokens) | Verified |
|
||
|-------|--------------------:|---------------------:|----------|
|
||
| claude-opus-4-6 | 5.00 | 25.00 | Yes |
|
||
| claude-sonnet-4-6 | 3.00 | 15.00 | Yes |
|
||
| claude-sonnet-4 | 3.00 | 15.00 | Yes |
|
||
| claude-opus-4-5 | 5.00 | 25.00 | Yes |
|
||
| claude-opus-4 | 15.00 | 75.00 | Yes |
|
||
| claude-haiku-4-5 | 1.00 | 5.00 | Yes |
|
||
| claude-3-5-haiku | 0.80 | 4.00 | Yes |
|
||
| claude-3-opus | 15.00 | 75.00 | Yes |
|
||
| claude-3-haiku | 0.25 | 1.25 | Yes |
|
||
|
||
**OpenAI** (source: openai.com/api/pricing)
|
||
|
||
| Model | Input ($/1M tokens) | Output ($/1M tokens) | Verified |
|
||
|-------|--------------------:|---------------------:|----------|
|
||
| gpt-4o | 2.50 | 10.00 | Yes |
|
||
| gpt-4o-mini | 0.15 | 0.60 | Yes |
|
||
| gpt-4.1 | 2.00 | 8.00 | Yes |
|
||
| gpt-4.1-mini | 0.40 | 1.60 | Yes |
|
||
| gpt-4.1-nano | 0.10 | 0.40 | Yes |
|
||
| o1 | 15.00 | 60.00 | Yes |
|
||
| o1-mini | 1.10 | 4.40 | Mismatch* |
|
||
| o3 | 2.00 | 8.00 | Yes |
|
||
| o3-mini | 1.10 | 4.40 | Yes |
|
||
| o4-mini | 1.10 | 4.40 | Yes |
|
||
|
||
*o1-mini: tx.js has $1.10/$4.40, current OpenAI pricing shows $3/$12. May reflect an older rate. Only affects you if using o1-mini.
|
||
|
||
**Google Gemini** (source: ai.google.dev/gemini-api/docs/pricing)
|
||
|
||
| Model | Input ($/1M tokens) | Output ($/1M tokens) | Verified |
|
||
|-------|--------------------:|---------------------:|----------|
|
||
| gemini-2.5-pro | 1.25 | 10.00 | Yes |
|
||
| gemini-2.5-flash | 0.30 | 2.50 | Yes |
|
||
| gemini-2.0-flash | 0.10 | 0.40 | Yes |
|
||
| gemini-2.0-flash-lite | 0.075 | 0.30 | Yes |
|
||
| gemini-1.5-flash | 0.15 | 0.60 | Deprecated |
|
||
|
||
Note: Gemini 2.5 Pro has tiered pricing — prompts over 200K tokens cost $2.50/$15.00. tx.js uses the base tier which applies to most usage.
|
||
|
||
## API Endpoints
|
||
|
||
All require `x-api-key` header. All accept `?period=24h|7d|30d|custom&start=ISO&end=ISO`.
|
||
|
||
| Endpoint | Description |
|
||
|----------|-------------|
|
||
| `GET /health` | Health check (no auth) |
|
||
| `GET /api/summary` | Total tokens, cost, users, conversations |
|
||
| `GET /api/top-users?limit=10` | Users ranked by cost |
|
||
| `GET /api/top-models?limit=10` | Models ranked by cost (resolves agent IDs to underlying LLM) |
|
||
| `GET /api/top-agents?limit=10` | Agents ranked by cost |
|
||
| `GET /api/cost-breakdown` | Per-model input vs output cost split |
|
||
| `GET /api/usage-over-time` | Time-series tokens and cost |
|
||
|
||
## Troubleshooting
|
||
|
||
**No data showing:**
|
||
- Check the time period — try 30D if there's no recent usage
|
||
- Check container logs: `docker logs librechat-analytics --tail 20`
|
||
- Verify MongoDB connection: the health endpoint should return `{"status":"ok"}`
|
||
|
||
**API key not prompting:**
|
||
- Clear localStorage: DevTools > Application > Local Storage > delete `analyticsApiKey`
|
||
- Hard refresh: Ctrl+Shift+R
|
||
|
||
**Container can't reach MongoDB:**
|
||
- Verify network: `docker network inspect librechat_default` — both `mongodb` and `librechat-analytics` should be listed
|
||
- If LibreChat uses a different network name, update `docker-compose.yml`
|
||
|
||
**Nginx 502 Bad Gateway:**
|
||
- Check the analytics container is running: `docker ps | grep analytics`
|
||
- Check it's on the right network: `docker network inspect librechat_default`
|