fix: correct token extraction for Claude non-streaming responses (#131)

- Add response logging for non-streaming requests (5_res_provider.json, 7_res_client.json)
- Fix extractUsageFromResponse() to check Claude format before OpenAI format
- Prevents format misidentification that caused tokens to show as 0
- Claude uses input_tokens/output_tokens vs OpenAI's prompt_tokens/completion_tokens

Fixes dashboard Details tab showing 0 tokens for Claude requests
This commit is contained in:
Thiên Toán
2026-02-20 14:24:21 +07:00
committed by GitHub
parent f933dd9c61
commit 9fbd6e619d

View File

@@ -194,18 +194,8 @@ function translateNonStreamingResponse(responseBody, targetFormat, sourceFormat)
function extractUsageFromResponse(responseBody, provider) {
if (!responseBody || typeof responseBody !== 'object') return null;
// OpenAI format
if (responseBody.usage && typeof responseBody.usage === 'object') {
return {
prompt_tokens: responseBody.usage.prompt_tokens || 0,
completion_tokens: responseBody.usage.completion_tokens || 0,
cached_tokens: responseBody.usage.prompt_tokens_details?.cached_tokens,
reasoning_tokens: responseBody.usage.completion_tokens_details?.reasoning_tokens
};
}
// Claude format
if (responseBody.usage && typeof responseBody.usage === 'object' && (responseBody.usage.input_tokens !== undefined || responseBody.usage.output_tokens !== undefined)) {
// Claude format - check first to avoid conflict with OpenAI check
if (responseBody.usage && typeof responseBody.usage === 'object' && responseBody.usage.input_tokens !== undefined) {
return {
prompt_tokens: responseBody.usage.input_tokens || 0,
completion_tokens: responseBody.usage.output_tokens || 0,
@@ -214,6 +204,16 @@ function extractUsageFromResponse(responseBody, provider) {
};
}
// OpenAI format
if (responseBody.usage && typeof responseBody.usage === 'object' && responseBody.usage.prompt_tokens !== undefined) {
return {
prompt_tokens: responseBody.usage.prompt_tokens || 0,
completion_tokens: responseBody.usage.completion_tokens || 0,
cached_tokens: responseBody.usage.prompt_tokens_details?.cached_tokens,
reasoning_tokens: responseBody.usage.completion_tokens_details?.reasoning_tokens
};
}
// Gemini format
if (responseBody.usageMetadata && typeof responseBody.usageMetadata === 'object') {
return {
@@ -723,6 +723,14 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred
}
}
// Log provider response (raw response from provider)
reqLogger.logProviderResponse(
providerResponse.status,
providerResponse.statusText,
providerResponse.headers,
responseBody
);
// Notify success - caller can clear error status if needed
if (onRequestSuccess) {
await onRequestSuccess();
@@ -758,6 +766,9 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred
translatedResponse.usage = filterUsageForFormat(buffered, sourceFormat);
}
// Log converted response (final response to client)
reqLogger.logConvertedResponse(translatedResponse);
const totalLatency = Date.now() - requestStartTime;
const requestDetail = {
provider: provider || "unknown",