LibreChat stopped tagging agent transactions with model: agent_xxx around 2026-03; new agent transactions record the underlying LLM and link to the message via messageId. Aggregate from messages -> transactions and union with the legacy path so historical and current data both show. Also create createdAt / messageId / (createdAt, model) indexes on startup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| config | ||
| docs | ||
| public | ||
| routes | ||
| services | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| server.js | ||
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
cd /opt
git clone https://x-token-auth:{ACCESS_TOKEN}@bitbucket.org/zlalani/librechat-analytics.git
cd librechat-analytics
Or with SSH:
git clone git@bitbucket.org:zlalani/librechat-analytics.git
2. Configure environment
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_KEYto a secure value. This is required to access the dashboard. - Do NOT use
&or other special characters in the API key (breaks URL parsing). mongodbin the URI is the container name of LibreChat's MongoDB — change if yours is different.
3. Build and run
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:
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:
docker exec LibreChat-NGINX nginx -s reload
5. Verify
# 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
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:
- Browser prompts for the API key on first visit
- Key is stored in browser localStorage
- All API requests include the key in the
x-api-keyheader
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:
- Records the token usage in the
transactionscollection with fields likerawAmount(token count, stored as negative),tokenType(prompt or completion), andmodel - Looks up the model's pricing from its internal pricing table (
api/models/tx.js) - Calculates
tokenValue = rawAmount × ratewhere rate is USD per 1M tokens - Stores
tokenValuealongside 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— bothmongodbandlibrechat-analyticsshould 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