librechat-analytics/README.md
DJP 203ba7197e docs: Add ASCII architecture and data flow diagrams
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 19:44:20 -04:00

299 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`