calcul accurate

This commit is contained in:
Biqoz
2025-11-27 15:12:57 +01:00
parent e6a9d41ebd
commit 414253aff0
2 changed files with 80 additions and 184 deletions

View File

@@ -11,7 +11,6 @@ import {
LibreChatUser,
LibreChatConversation,
LibreChatBalance,
LibreChatMessage,
} from "@/lib/types";
interface DashboardUser {
@@ -22,30 +21,15 @@ interface DashboardUser {
credits: number;
}
// Interfaces pour les collections tokens et toolcalls
interface TokenDocument {
// Interface pour les transactions (vraie source de consommation)
interface TransactionDocument {
_id: string;
user?: string;
userId?: string;
amount?: number;
tokens?: number;
count?: number;
user: unknown; // ObjectId dans MongoDB
rawAmount?: number;
tokenType?: string;
model?: string;
}
interface ToolcallDocument {
_id: string;
user?: string;
userId?: string;
tokens?: number;
tokenCount?: number;
}
const isValidDate = (value: unknown): value is string | number | Date => {
if (!value) return false;
const date = new Date(value as string | number | Date);
return !isNaN(date.getTime());
};
export function DashboardUsersList() {
const [topUsers, setTopUsers] = useState<DashboardUser[]>([]);
const [isLoading, setIsLoading] = useState(true);
@@ -57,20 +41,12 @@ export function DashboardUsersList() {
useCollection<LibreChatConversation>("conversations", { limit: 1000 });
const { data: balances, loading: balancesLoading } =
useCollection<LibreChatBalance>("balances", { limit: 1000 });
const { data: messages, loading: messagesLoading } =
useCollection<LibreChatMessage>("messages", { limit: 1000 });
const { data: tokens, loading: tokensLoading } =
useCollection<TokenDocument>("tokens", { limit: 1000 });
const { data: toolcalls, loading: toolcallsLoading } =
useCollection<ToolcallDocument>("toolcalls", { limit: 1000 });
// Transactions = vraie source de consommation de tokens
const { data: transactions, loading: transactionsLoading } =
useCollection<TransactionDocument>("transactions", { limit: 10000 });
const processUsers = useCallback(() => {
if (
!users?.length ||
!conversations?.length ||
!balances?.length ||
!messages?.length
) {
if (!users?.length || !conversations?.length || !balances?.length) {
return;
}
@@ -78,153 +54,45 @@ export function DashboardUsersList() {
console.log("Users:", users.length);
console.log("Conversations:", conversations.length);
console.log("Balances:", balances.length);
console.log("Messages:", messages.length);
console.log("Tokens collection:", tokens?.length || 0);
console.log("Toolcalls collection:", toolcalls?.length || 0);
console.log("Transactions:", transactions?.length || 0);
const processedUsers: DashboardUser[] = [];
// Créer une map des transactions par utilisateur (user est un ObjectId, on compare en string)
const tokensByUser = new Map<string, number>();
if (transactions?.length) {
transactions.forEach((tx: TransactionDocument) => {
// tx.user est un ObjectId, on le convertit en string pour la comparaison
const txUserId = String(tx.user);
const tokens = Math.abs(tx.rawAmount || 0);
tokensByUser.set(txUserId, (tokensByUser.get(txUserId) || 0) + tokens);
});
}
users.forEach((user: LibreChatUser) => {
// Obtenir les conversations de l'utilisateur
const userId = user._id;
// Obtenir les conversations de l'utilisateur (user dans conversations est un string)
const userConversations = conversations.filter(
(conv: LibreChatConversation) => conv.user === user._id
(conv: LibreChatConversation) => String(conv.user) === userId
);
// Obtenir les messages de l'utilisateur
const userMessages = messages.filter(
(msg: LibreChatMessage) => msg.user === user._id
// Obtenir les tokens depuis les transactions (vraie consommation)
const tokensFromTransactions = tokensByUser.get(userId) || 0;
// Obtenir le balance de l'utilisateur
const userBalance = balances.find(
(balance: LibreChatBalance) => String(balance.user) === userId
);
const credits = userBalance?.tokenCredits || 0;
// Calculer les tokens depuis les messages
const totalTokensFromMessages = userMessages.reduce(
(sum: number, msg: LibreChatMessage) => sum + (msg.tokenCount || 0),
0
);
// Calculer les tokens depuis les conversations de l'utilisateur
const userConversationIds = userConversations.map(
(conv) => conv.conversationId
);
const conversationMessages = messages.filter((msg: LibreChatMessage) =>
userConversationIds.includes(msg.conversationId)
);
const totalTokensFromConversations = conversationMessages.reduce(
(sum: number, msg: LibreChatMessage) => sum + (msg.tokenCount || 0),
0
);
// Vérifier les collections tokens et toolcalls
let tokensFromTokensCollection = 0;
let tokensFromToolcalls = 0;
if (tokens?.length) {
const userTokens = tokens.filter(
(token: TokenDocument) =>
token.user === user._id || token.userId === user._id
);
tokensFromTokensCollection = userTokens.reduce(
(sum: number, token: TokenDocument) => {
return sum + (token.amount || token.tokens || token.count || 0);
},
0
);
}
if (toolcalls?.length) {
const userToolcalls = toolcalls.filter(
(toolcall: ToolcallDocument) =>
toolcall.user === user._id || toolcall.userId === user._id
);
tokensFromToolcalls = userToolcalls.reduce(
(sum: number, toolcall: ToolcallDocument) => {
return sum + (toolcall.tokens || toolcall.tokenCount || 0);
},
0
);
}
// Obtenir les balances de l'utilisateur
const userBalances = balances.filter(
(balance: LibreChatBalance) => balance.user === user._id
);
// Trier par date de mise à jour (plus récent en premier)
const sortedBalances = userBalances.sort((a, b) => {
const dateA = a.updatedAt || a.createdAt;
const dateB = b.updatedAt || b.createdAt;
// Vérifier que les dates sont valides avant de les comparer
if (isValidDate(dateA) && isValidDate(dateB)) {
return (
new Date(dateB as string | number | Date).getTime() -
new Date(dateA as string | number | Date).getTime()
);
}
// Si seulement une date existe et est valide, la privilégier
if (isValidDate(dateA) && !isValidDate(dateB)) return -1;
if (!isValidDate(dateA) && isValidDate(dateB)) return 1;
// Si aucune date n'existe ou n'est valide, garder l'ordre actuel
return 0;
});
// Prendre la balance la plus récente
const latestBalance = sortedBalances[0];
const credits = latestBalance ? latestBalance.tokenCredits || 0 : 0;
// Calculer les tokens réellement consommés depuis les messages (approche principale)
const totalTokens = Math.max(
totalTokensFromMessages,
totalTokensFromConversations,
tokensFromTokensCollection,
tokensFromToolcalls
);
// Calculer les tokens depuis les crédits seulement si on n'a pas de données de messages
const INITIAL_CREDITS = 3000000;
const creditsUsed = INITIAL_CREDITS - credits;
const tokensFromCredits = creditsUsed > 0 ? creditsUsed : 0;
// Si on n'a pas de tokens depuis les messages mais qu'on a une consommation de crédits significative
const finalTokens = totalTokens > 0 ? totalTokens :
(tokensFromCredits > 0 && tokensFromCredits < INITIAL_CREDITS) ? tokensFromCredits : 0;
// Log de débogage très détaillé
console.log(`👤 User ${user.name || user.email}:`, {
conversations: userConversations.length,
userMessages: userMessages.length,
conversationMessages: conversationMessages.length,
tokensFromMessages: totalTokensFromMessages,
tokensFromConversations: totalTokensFromConversations,
tokensFromTokensCollection: tokensFromTokensCollection,
tokensFromToolcalls: tokensFromToolcalls,
currentCredits: credits,
creditsUsed: creditsUsed,
tokensFromCredits: tokensFromCredits,
finalTokens: finalTokens,
willBeIncluded: finalTokens > 0,
messagesSample: userMessages.slice(0, 2).map((m) => ({
tokenCount: m.tokenCount,
model: m.model,
isCreatedByUser: m.isCreatedByUser,
conversationId: m.conversationId,
})),
conversationsSample: userConversations.slice(0, 2).map((c) => ({
conversationId: c.conversationId,
messagesCount: c.messages?.length || 0,
})),
});
// Ajouter l'utilisateur s'il a consommé des tokens (éviter les faux positifs de 3M tokens)
if (finalTokens > 0 && finalTokens < INITIAL_CREDITS) {
// Ajouter l'utilisateur s'il a consommé des tokens
if (tokensFromTransactions > 0) {
processedUsers.push({
userId: user._id,
userName:
user.name || user.username || user.email || "Utilisateur inconnu",
userId: userId,
userName: user.name || user.username || user.email || "Utilisateur inconnu",
conversations: userConversations.length,
tokens: finalTokens,
tokens: tokensFromTransactions,
credits: credits,
});
}
@@ -239,26 +107,18 @@ export function DashboardUsersList() {
totalUsers: users.length,
usersWithActivity: processedUsers.length,
top5Users: sortedUsers.length,
allProcessedUsers: processedUsers.map(u => ({
name: u.userName,
conversations: u.conversations,
tokens: u.tokens,
credits: u.credits
}))
});
console.log("✅ Top 5 users processed:", sortedUsers);
console.log("✅ Top 5 users:", sortedUsers);
setTopUsers(sortedUsers);
setIsLoading(false);
}, [users, conversations, balances, messages, tokens, toolcalls]);
}, [users, conversations, balances, transactions]);
useEffect(() => {
const allDataLoaded =
!usersLoading &&
!conversationsLoading &&
!balancesLoading &&
!messagesLoading &&
!tokensLoading &&
!toolcallsLoading;
!transactionsLoading;
if (allDataLoaded) {
processUsers();
@@ -269,9 +129,7 @@ export function DashboardUsersList() {
usersLoading,
conversationsLoading,
balancesLoading,
messagesLoading,
tokensLoading,
toolcallsLoading,
transactionsLoading,
processUsers,
]);

38
debug-tokens.js Normal file
View File

@@ -0,0 +1,38 @@
const { MongoClient } = require('mongodb');
async function debug() {
const uri = 'mongodb://7qV5rRanD6UwANNO:NK1EFfaKpJZcTQlmm7pDUUI7Yk7yqxN6@51.254.197.189:27017/librechat?authSource=admin';
const client = new MongoClient(uri);
await client.connect();
const db = client.db('librechat');
const usersToCheck = ['Kleyntssens', 'Lilou Guillaume', 'Isabelle De Witte', 'Flavie Pochet'];
console.log('=== VERIFICATION TOKENS DEPUIS TRANSACTIONS ===\n');
for (const searchName of usersToCheck) {
const user = await db.collection('users').findOne({ name: { $regex: searchName, $options: 'i' } });
if (!user) {
console.log('NOT FOUND:', searchName);
continue;
}
const convCount = await db.collection('conversations').countDocuments({ user: user._id.toString() });
const txs = await db.collection('transactions').find({ user: user._id }).toArray();
const tokensFromTx = txs.reduce((sum, t) => sum + Math.abs(t.rawAmount || 0), 0);
const balance = await db.collection('balances').findOne({ user: user._id });
const credits = balance?.tokenCredits || 0;
console.log('---');
console.log('User:', user.name);
console.log('Conversations:', convCount);
console.log('Tokens (VRAI depuis transactions):', tokensFromTx.toLocaleString());
console.log('Credits:', credits.toLocaleString());
console.log('Nb transactions:', txs.length);
}
await client.close();
}
debug().catch(console.error);