Fix cost tracking accuracy and OpenRouter model pricing (#3189)
Co-authored-by: jack <> Co-authored-by: angiejones <jones.angie@gmail.com>
This commit is contained in:
@@ -619,10 +619,22 @@ function ChatContent({
|
||||
}));
|
||||
}
|
||||
|
||||
// Reset token counters for the new model
|
||||
setSessionTokenCount(0);
|
||||
setSessionInputTokens(0);
|
||||
setSessionOutputTokens(0);
|
||||
// Restore token counters from session metadata instead of resetting to 0
|
||||
// This preserves the accumulated session tokens when switching models
|
||||
// and ensures cost tracking remains accurate across model changes
|
||||
if (sessionMetadata) {
|
||||
// Use Math.max to ensure non-negative values and handle potential data issues
|
||||
setSessionTokenCount(Math.max(0, sessionMetadata.totalTokens || 0));
|
||||
setSessionInputTokens(Math.max(0, sessionMetadata.accumulatedInputTokens || 0));
|
||||
setSessionOutputTokens(Math.max(0, sessionMetadata.accumulatedOutputTokens || 0));
|
||||
} else {
|
||||
// Fallback: if no session metadata, preserve current session tokens instead of resetting
|
||||
// This handles edge cases where metadata might not be available yet
|
||||
console.warn(
|
||||
'No session metadata available during model change, preserving current tokens'
|
||||
);
|
||||
}
|
||||
// Only reset local token estimation counters since they're model-specific
|
||||
setLocalInputTokens(0);
|
||||
setLocalOutputTokens(0);
|
||||
|
||||
@@ -631,7 +643,7 @@ function ChatContent({
|
||||
`${prevProviderRef.current}/${prevModelRef.current}`,
|
||||
'to',
|
||||
`${currentProvider}/${currentModel}`,
|
||||
'- saved costs and reset token counters'
|
||||
'- saved costs and restored session token counters'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -644,6 +656,7 @@ function ChatContent({
|
||||
sessionOutputTokens,
|
||||
localInputTokens,
|
||||
localOutputTokens,
|
||||
sessionMetadata,
|
||||
]);
|
||||
|
||||
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
|
||||
|
||||
@@ -66,9 +66,7 @@ export function CostTracker({ inputTokens = 0, outputTokens = 0, sessionCosts }:
|
||||
initializeCostDatabase();
|
||||
|
||||
// Update costs for all models in background
|
||||
updateAllModelCosts().catch((error) => {
|
||||
console.error('Failed to update model costs:', error);
|
||||
});
|
||||
updateAllModelCosts().catch(() => {});
|
||||
}, [getProviders]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -78,18 +76,12 @@ export function CostTracker({ inputTokens = 0, outputTokens = 0, sessionCosts }:
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`CostTracker: Loading cost info for ${currentProvider}/${currentModel}`);
|
||||
|
||||
try {
|
||||
// First check sync cache
|
||||
let costData = getCostForModel(currentProvider, currentModel);
|
||||
|
||||
if (costData) {
|
||||
// We have cached data
|
||||
console.log(
|
||||
`CostTracker: Found cached data for ${currentProvider}/${currentModel}:`,
|
||||
costData
|
||||
);
|
||||
setCostInfo(costData);
|
||||
setPricingFailed(false);
|
||||
setModelNotFound(false);
|
||||
@@ -97,30 +89,19 @@ export function CostTracker({ inputTokens = 0, outputTokens = 0, sessionCosts }:
|
||||
setHasAttemptedFetch(true);
|
||||
} else {
|
||||
// Need to fetch from backend
|
||||
console.log(
|
||||
`CostTracker: No cached data, fetching from backend for ${currentProvider}/${currentModel}`
|
||||
);
|
||||
setIsLoading(true);
|
||||
const result = await fetchAndCachePricing(currentProvider, currentModel);
|
||||
setHasAttemptedFetch(true);
|
||||
|
||||
if (result && result.costInfo) {
|
||||
console.log(
|
||||
`CostTracker: Fetched data for ${currentProvider}/${currentModel}:`,
|
||||
result.costInfo
|
||||
);
|
||||
setCostInfo(result.costInfo);
|
||||
setPricingFailed(false);
|
||||
setModelNotFound(false);
|
||||
} else if (result && result.error === 'model_not_found') {
|
||||
console.log(
|
||||
`CostTracker: Model not found in pricing data for ${currentProvider}/${currentModel}`
|
||||
);
|
||||
// Model not found in pricing database, but API call succeeded
|
||||
setModelNotFound(true);
|
||||
setPricingFailed(false);
|
||||
} else {
|
||||
console.log(`CostTracker: API failed for ${currentProvider}/${currentModel}`);
|
||||
// API call failed or other error
|
||||
const freeProviders = ['ollama', 'local', 'localhost'];
|
||||
if (!freeProviders.includes(currentProvider.toLowerCase())) {
|
||||
@@ -131,7 +112,6 @@ export function CostTracker({ inputTokens = 0, outputTokens = 0, sessionCosts }:
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading cost info:', error);
|
||||
setHasAttemptedFetch(true);
|
||||
// Only set pricing failed if we're not dealing with a known free provider
|
||||
const freeProviders = ['ollama', 'local', 'localhost'];
|
||||
@@ -194,8 +174,6 @@ export function CostTracker({ inputTokens = 0, outputTokens = 0, sessionCosts }:
|
||||
return cost.toFixed(6);
|
||||
};
|
||||
|
||||
// Debug logging removed
|
||||
|
||||
// Show loading state or when we don't have model/provider info
|
||||
if (!currentModel || !currentProvider) {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user