diff --git a/.changeset/fix-langfuse-usage-double-counting.md b/.changeset/fix-langfuse-usage-double-counting.md new file mode 100644 index 0000000..28c3944 --- /dev/null +++ b/.changeset/fix-langfuse-usage-double-counting.md @@ -0,0 +1,5 @@ +--- +'@core-ai/langfuse': patch +--- + +Fix double-counting of cache and reasoning tokens in Langfuse usage breakdown. The `input` and `output` keys now report only non-overlapping token counts so Langfuse's aggregation sums correctly. diff --git a/packages/langfuse/src/index.test.ts b/packages/langfuse/src/index.test.ts index 6c11ebd..2c6c178 100644 --- a/packages/langfuse/src/index.test.ts +++ b/packages/langfuse/src/index.test.ts @@ -238,7 +238,6 @@ describe('@core-ai/langfuse', () => { usageDetails: { input: 5, output: 3, - total: 8, cache_read_input: 0, cache_creation_input: 0, }, @@ -273,7 +272,6 @@ describe('@core-ai/langfuse', () => { usageDetails: { input: 5, output: 3, - total: 8, cache_read_input: 0, cache_creation_input: 0, }, @@ -314,7 +312,6 @@ describe('@core-ai/langfuse', () => { usageDetails: { input: 5, output: 3, - total: 8, cache_read_input: 0, cache_creation_input: 0, }, @@ -364,7 +361,6 @@ describe('@core-ai/langfuse', () => { usageDetails: { input: 5, output: 3, - total: 8, cache_read_input: 0, cache_creation_input: 0, }, @@ -491,7 +487,6 @@ describe('@core-ai/langfuse', () => { usageDetails: { input: 5, output: 3, - total: 8, cache_read_input: 0, cache_creation_input: 0, }, @@ -539,7 +534,6 @@ describe('@core-ai/langfuse', () => { expect(observation.update).toHaveBeenNthCalledWith(2, { usageDetails: { input: 3, - total: 3, }, }); }); diff --git a/packages/langfuse/src/index.ts b/packages/langfuse/src/index.ts index 58063ee..3c6aee4 100644 --- a/packages/langfuse/src/index.ts +++ b/packages/langfuse/src/index.ts @@ -107,12 +107,14 @@ function createImageModelParameters( } function createChatUsageDetails(usage: ChatUsage): Record { + const { cacheReadTokens, cacheWriteTokens } = usage.inputTokenDetails; + const reasoningTokens = usage.outputTokenDetails.reasoningTokens ?? 0; + return { - input: usage.inputTokens, - output: usage.outputTokens, - total: usage.inputTokens + usage.outputTokens, - cache_read_input: usage.inputTokenDetails.cacheReadTokens, - cache_creation_input: usage.inputTokenDetails.cacheWriteTokens, + input: usage.inputTokens - cacheReadTokens - cacheWriteTokens, + output: usage.outputTokens - reasoningTokens, + cache_read_input: cacheReadTokens, + cache_creation_input: cacheWriteTokens, ...(usage.outputTokenDetails.reasoningTokens !== undefined ? { reasoning_output: usage.outputTokenDetails.reasoningTokens } : {}), @@ -128,7 +130,6 @@ function createEmbeddingUsageDetails( return { input: usage.inputTokens, - total: usage.inputTokens, }; }