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