mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-11 12:04:24 +01:00
feat: enhance token usage computation by integrating agent details and optimizing message fetching logic
This commit is contained in:
parent
ec430581d1
commit
07e3098cee
1 changed files with 58 additions and 25 deletions
|
|
@ -1,10 +1,10 @@
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useSetAtom, useAtomValue } from 'jotai';
|
import { useSetAtom, useAtomValue } from 'jotai';
|
||||||
import { getModelMaxTokens } from 'librechat-data-provider';
|
import { getModelMaxTokens, isAgentsEndpoint } from 'librechat-data-provider';
|
||||||
import type { TMessage } from 'librechat-data-provider';
|
import type { TMessage } from 'librechat-data-provider';
|
||||||
import { tokenUsageAtom, type TokenUsage } from '~/store/tokenUsage';
|
import { tokenUsageAtom, type TokenUsage } from '~/store/tokenUsage';
|
||||||
import { useGetMessagesByConvoId } from '~/data-provider';
|
import { useGetMessagesByConvoId, useGetAgentByIdQuery } from '~/data-provider';
|
||||||
import { useChatContext } from '~/Providers';
|
import { useChatContext } from '~/Providers';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -17,27 +17,47 @@ export function useTokenUsageComputation() {
|
||||||
const setTokenUsage = useSetAtom(tokenUsageAtom);
|
const setTokenUsage = useSetAtom(tokenUsageAtom);
|
||||||
const { conversationId: paramId } = useParams();
|
const { conversationId: paramId } = useParams();
|
||||||
|
|
||||||
// Determine the query key to use - same logic as useChatHelpers
|
// Determine if we need to fetch agent details for token limits
|
||||||
const queryParam = paramId === 'new' ? paramId : conversationId || paramId || '';
|
const endpoint = conversation?.endpoint ?? '';
|
||||||
|
const isAgent = isAgentsEndpoint(endpoint);
|
||||||
|
const agentId = isAgent ? conversation?.agent_id : null;
|
||||||
|
|
||||||
// Use the query hook to get reactive messages
|
// Fetch full agent details (includes model and provider) when using agents endpoint
|
||||||
// Subscribe to both the paramId-based key and conversationId-based key
|
const { data: agent } = useGetAgentByIdQuery(agentId, {
|
||||||
const { data: messages } = useGetMessagesByConvoId(queryParam, {
|
enabled: !!agentId,
|
||||||
enabled: !!queryParam,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Also subscribe to the actual conversationId if different from queryParam
|
/**
|
||||||
// This ensures we get updates when conversation transitions from 'new' to actual ID
|
* Determine the effective conversation ID for querying messages.
|
||||||
const { data: messagesById } = useGetMessagesByConvoId(conversationId, {
|
*
|
||||||
enabled: !!conversationId && conversationId !== 'new' && conversationId !== queryParam,
|
* Priority logic:
|
||||||
|
* 1. If URL param is 'new' -> use 'new' (landing page, no conversation yet)
|
||||||
|
* 2. If conversation has actual ID -> use it (active conversation)
|
||||||
|
* 3. Fallback to URL param (handles direct navigation to /c/:id)
|
||||||
|
*
|
||||||
|
* This ensures we always query the correct messages cache key and avoids
|
||||||
|
* dual subscriptions that would cause duplicate reactivity and API calls.
|
||||||
|
*/
|
||||||
|
const effectiveConversationId = useMemo(() => {
|
||||||
|
if (paramId === 'new') {
|
||||||
|
return 'new';
|
||||||
|
}
|
||||||
|
return conversationId || paramId || '';
|
||||||
|
}, [paramId, conversationId]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single subscription to messages for the effective conversation ID.
|
||||||
|
* React Query caches messages by [QueryKeys.messages, id], so we'll
|
||||||
|
* automatically get updates when:
|
||||||
|
* - New messages are added via setMessages() in useChatHelpers
|
||||||
|
* - Conversation transitions from 'new' to actual ID (cache is synced in setMessages)
|
||||||
|
* - Messages are fetched from the server
|
||||||
|
*/
|
||||||
|
const { data: messages } = useGetMessagesByConvoId(effectiveConversationId, {
|
||||||
|
enabled: !!effectiveConversationId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Use whichever has more messages (handles transition from new -> actual ID)
|
const effectiveMessages = useMemo<TMessage[]>(() => messages ?? [], [messages]);
|
||||||
const effectiveMessages = useMemo(() => {
|
|
||||||
const msgArray = messages ?? [];
|
|
||||||
const msgByIdArray = messagesById ?? [];
|
|
||||||
return msgByIdArray.length > msgArray.length ? msgByIdArray : msgArray;
|
|
||||||
}, [messages, messagesById]);
|
|
||||||
|
|
||||||
// Compute token usage whenever messages change
|
// Compute token usage whenever messages change
|
||||||
const tokenData = useMemo(() => {
|
const tokenData = useMemo(() => {
|
||||||
|
|
@ -45,7 +65,7 @@ export function useTokenUsageComputation() {
|
||||||
let outputTokens = 0;
|
let outputTokens = 0;
|
||||||
|
|
||||||
if (effectiveMessages && Array.isArray(effectiveMessages)) {
|
if (effectiveMessages && Array.isArray(effectiveMessages)) {
|
||||||
for (const msg of effectiveMessages as TMessage[]) {
|
for (const msg of effectiveMessages) {
|
||||||
const count = msg.tokenCount ?? 0;
|
const count = msg.tokenCount ?? 0;
|
||||||
if (msg.isCreatedByUser) {
|
if (msg.isCreatedByUser) {
|
||||||
inputTokens += count;
|
inputTokens += count;
|
||||||
|
|
@ -59,11 +79,22 @@ export function useTokenUsageComputation() {
|
||||||
let maxContext: number | null = conversation?.maxContextTokens ?? null;
|
let maxContext: number | null = conversation?.maxContextTokens ?? null;
|
||||||
|
|
||||||
// If no explicit maxContextTokens, try to look up model default
|
// If no explicit maxContextTokens, try to look up model default
|
||||||
if (maxContext === null && conversation?.model) {
|
if (maxContext === null) {
|
||||||
const endpoint = conversation.endpointType ?? conversation.endpoint ?? '';
|
// For agents endpoint, get the actual model from the fetched agent
|
||||||
const modelDefault = getModelMaxTokens(conversation.model, endpoint);
|
if (isAgent && agent?.model) {
|
||||||
if (modelDefault !== undefined) {
|
// Use agent's provider, or fall back to 'agents' endpoint for lookup
|
||||||
maxContext = modelDefault;
|
const provider = agent.provider ?? endpoint;
|
||||||
|
const modelDefault = getModelMaxTokens(agent.model, provider);
|
||||||
|
if (modelDefault !== undefined) {
|
||||||
|
maxContext = modelDefault;
|
||||||
|
}
|
||||||
|
} else if (conversation?.model) {
|
||||||
|
// For other endpoints, use conversation model directly
|
||||||
|
const effectiveEndpoint = conversation?.endpointType ?? endpoint;
|
||||||
|
const modelDefault = getModelMaxTokens(conversation.model, effectiveEndpoint);
|
||||||
|
if (modelDefault !== undefined) {
|
||||||
|
maxContext = modelDefault;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,8 +107,10 @@ export function useTokenUsageComputation() {
|
||||||
effectiveMessages,
|
effectiveMessages,
|
||||||
conversation?.maxContextTokens,
|
conversation?.maxContextTokens,
|
||||||
conversation?.model,
|
conversation?.model,
|
||||||
conversation?.endpoint,
|
|
||||||
conversation?.endpointType,
|
conversation?.endpointType,
|
||||||
|
isAgent,
|
||||||
|
agent,
|
||||||
|
endpoint,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Update the atom when computed values change
|
// Update the atom when computed values change
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue