Express.js + Chart.js dashboard for LibreChat usage analytics. Queries MongoDB transactions collection for model/agent usage, costs, and top users. Dark theme with Montserrat font, black/gold (#FFC407) color scheme. Docker-ready with API key authentication. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
90 lines
3.7 KiB
JavaScript
90 lines
3.7 KiB
JavaScript
/**
|
|
* Token pricing (USD per 1M tokens)
|
|
* Copied from LibreChat api/models/tx.js
|
|
*/
|
|
const tokenValues = {
|
|
'8k': { prompt: 30, completion: 60 },
|
|
'32k': { prompt: 60, completion: 120 },
|
|
'4k': { prompt: 1.5, completion: 2 },
|
|
'16k': { prompt: 3, completion: 4 },
|
|
'claude-': { prompt: 0.8, completion: 2.4 },
|
|
deepseek: { prompt: 0.28, completion: 0.42 },
|
|
command: { prompt: 0.38, completion: 0.38 },
|
|
gemini: { prompt: 0.5, completion: 1.5 },
|
|
'gpt-3.5-turbo-1106': { prompt: 1, completion: 2 },
|
|
'gpt-3.5-turbo-0125': { prompt: 0.5, completion: 1.5 },
|
|
'gpt-4-1106': { prompt: 10, completion: 30 },
|
|
'gpt-4.1': { prompt: 2, completion: 8 },
|
|
'gpt-4.1-nano': { prompt: 0.1, completion: 0.4 },
|
|
'gpt-4.1-mini': { prompt: 0.4, completion: 1.6 },
|
|
'gpt-4.5': { prompt: 75, completion: 150 },
|
|
'gpt-4o': { prompt: 2.5, completion: 10 },
|
|
'gpt-4o-2024-05-13': { prompt: 5, completion: 15 },
|
|
'gpt-4o-mini': { prompt: 0.15, completion: 0.6 },
|
|
'gpt-5': { prompt: 1.25, completion: 10 },
|
|
'gpt-5-mini': { prompt: 0.25, completion: 2 },
|
|
'gpt-5-nano': { prompt: 0.05, completion: 0.4 },
|
|
o1: { prompt: 15, completion: 60 },
|
|
'o1-mini': { prompt: 1.1, completion: 4.4 },
|
|
'o1-preview': { prompt: 15, completion: 60 },
|
|
o3: { prompt: 2, completion: 8 },
|
|
'o3-mini': { prompt: 1.1, completion: 4.4 },
|
|
'o4-mini': { prompt: 1.1, completion: 4.4 },
|
|
'claude-instant': { prompt: 0.8, completion: 2.4 },
|
|
'claude-2': { prompt: 8, completion: 24 },
|
|
'claude-2.1': { prompt: 8, completion: 24 },
|
|
'claude-3-haiku': { prompt: 0.25, completion: 1.25 },
|
|
'claude-3-sonnet': { prompt: 3, completion: 15 },
|
|
'claude-3-opus': { prompt: 15, completion: 75 },
|
|
'claude-3-5-haiku': { prompt: 0.8, completion: 4 },
|
|
'claude-3.5-haiku': { prompt: 0.8, completion: 4 },
|
|
'claude-3-5-sonnet': { prompt: 3, completion: 15 },
|
|
'claude-3.5-sonnet': { prompt: 3, completion: 15 },
|
|
'claude-3-7-sonnet': { prompt: 3, completion: 15 },
|
|
'claude-3.7-sonnet': { prompt: 3, completion: 15 },
|
|
'claude-haiku-4-5': { prompt: 1, completion: 5 },
|
|
'claude-opus-4': { prompt: 15, completion: 75 },
|
|
'claude-opus-4-5': { prompt: 5, completion: 25 },
|
|
'claude-opus-4-6': { prompt: 5, completion: 25 },
|
|
'claude-sonnet-4': { prompt: 3, completion: 15 },
|
|
'claude-sonnet-4-6': { prompt: 3, completion: 15 },
|
|
'command-r': { prompt: 0.5, completion: 1.5 },
|
|
'command-r-plus': { prompt: 3, completion: 15 },
|
|
'deepseek-chat': { prompt: 0.28, completion: 0.42 },
|
|
'deepseek-reasoner': { prompt: 0.28, completion: 0.42 },
|
|
'deepseek-r1': { prompt: 0.4, completion: 2.0 },
|
|
'deepseek-v3': { prompt: 0.2, completion: 0.8 },
|
|
'gemini-1.5': { prompt: 2.5, completion: 10 },
|
|
'gemini-1.5-flash': { prompt: 0.15, completion: 0.6 },
|
|
'gemini-2.0-flash': { prompt: 0.1, completion: 0.4 },
|
|
'gemini-2.5-flash': { prompt: 0.3, completion: 2.5 },
|
|
'gemini-2.5-pro': { prompt: 1.25, completion: 10 },
|
|
'grok-2': { prompt: 2.0, completion: 10.0 },
|
|
'grok-3': { prompt: 3.0, completion: 15.0 },
|
|
'grok-3-mini': { prompt: 0.3, completion: 0.5 },
|
|
'mistral-large': { prompt: 2.0, completion: 6.0 },
|
|
'mistral-nemo': { prompt: 0.15, completion: 0.15 },
|
|
};
|
|
|
|
const defaultRate = 6;
|
|
|
|
/**
|
|
* Find pricing for a model name using longest-match strategy (same as LibreChat)
|
|
*/
|
|
function getModelPricing(modelName) {
|
|
if (!modelName) return { prompt: defaultRate, completion: defaultRate };
|
|
|
|
let bestMatch = null;
|
|
let bestLength = 0;
|
|
|
|
for (const key of Object.keys(tokenValues)) {
|
|
if (modelName.includes(key) && key.length > bestLength) {
|
|
bestMatch = key;
|
|
bestLength = key.length;
|
|
}
|
|
}
|
|
|
|
return bestMatch ? tokenValues[bestMatch] : { prompt: defaultRate, completion: defaultRate };
|
|
}
|
|
|
|
module.exports = { tokenValues, getModelPricing, defaultRate };
|