From 85ddf3aeb5d4ec0efac2e1a267bd6d5ea0b99d64 Mon Sep 17 00:00:00 2001 From: sususu Date: Sat, 12 Sep 2026 21:20:14 +0800 Subject: [PATCH] fix(devin): calculate total_input_tokens and total_tokens correctly --- internal/runtime/executor/devin_executor.go | 18 ++++++++++++++---- .../runtime/executor/devin_executor_test.go | 7 +++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/internal/runtime/executor/devin_executor.go b/internal/runtime/executor/devin_executor.go index a606ea616..1f9eb49d4 100644 --- a/internal/runtime/executor/devin_executor.go +++ b/internal/runtime/executor/devin_executor.go @@ -704,9 +704,14 @@ func (e *DevinExecutor) streamDevinFrames( completedEvent, _ = sjson.SetBytes(completedEvent, "interaction.id", interactionID) completedEvent, _ = sjson.SetBytes(completedEvent, "interaction.model", req.Model) if finalUsage != nil { - completedEvent, _ = sjson.SetBytes(completedEvent, "interaction.usage.total_input_tokens", finalUsage.PromptTokens) - completedEvent, _ = sjson.SetBytes(completedEvent, "interaction.usage.total_output_tokens", finalUsage.CompletionTokens) + totalInput := finalUsage.PromptTokens + finalUsage.CachedTokens + totalOutput := finalUsage.CompletionTokens + totalTokens := totalInput + totalOutput + + completedEvent, _ = sjson.SetBytes(completedEvent, "interaction.usage.total_input_tokens", totalInput) + completedEvent, _ = sjson.SetBytes(completedEvent, "interaction.usage.total_output_tokens", totalOutput) completedEvent, _ = sjson.SetBytes(completedEvent, "interaction.usage.total_cached_tokens", finalUsage.CachedTokens) + completedEvent, _ = sjson.SetBytes(completedEvent, "interaction.usage.total_tokens", totalTokens) if detail, ok := helps.ParseInteractionsStreamUsage(completedEvent); ok { reporter.Publish(ctx, detail) } @@ -895,9 +900,14 @@ func consumeDevinFramesToInteractions(body io.Reader, model, chatModelUID string } if finalUsage != nil { - out, _ = sjson.SetBytes(out, "usage.total_input_tokens", finalUsage.PromptTokens) - out, _ = sjson.SetBytes(out, "usage.total_output_tokens", finalUsage.CompletionTokens) + totalInput := finalUsage.PromptTokens + finalUsage.CachedTokens + totalOutput := finalUsage.CompletionTokens + totalTokens := totalInput + totalOutput + + out, _ = sjson.SetBytes(out, "usage.total_input_tokens", totalInput) + out, _ = sjson.SetBytes(out, "usage.total_output_tokens", totalOutput) out, _ = sjson.SetBytes(out, "usage.total_cached_tokens", finalUsage.CachedTokens) + out, _ = sjson.SetBytes(out, "usage.total_tokens", totalTokens) } respLog := &helps.DevinUpstreamResponseLog{ diff --git a/internal/runtime/executor/devin_executor_test.go b/internal/runtime/executor/devin_executor_test.go index 5170da187..0ce245434 100644 --- a/internal/runtime/executor/devin_executor_test.go +++ b/internal/runtime/executor/devin_executor_test.go @@ -351,8 +351,8 @@ func TestConsumeDevinFramesToInteractions(t *testing.T) { if root.Get("status").String() != "completed" { t.Errorf("status = %q, want completed", root.Get("status").String()) } - if root.Get("usage.total_input_tokens").Int() != 100 { - t.Errorf("input tokens = %d, want 100", root.Get("usage.total_input_tokens").Int()) + if root.Get("usage.total_input_tokens").Int() != 120 { + t.Errorf("input tokens = %d, want 120", root.Get("usage.total_input_tokens").Int()) } if root.Get("usage.total_output_tokens").Int() != 50 { t.Errorf("output tokens = %d, want 50", root.Get("usage.total_output_tokens").Int()) @@ -360,6 +360,9 @@ func TestConsumeDevinFramesToInteractions(t *testing.T) { if root.Get("usage.total_cached_tokens").Int() != 20 { t.Errorf("cached tokens = %d, want 20", root.Get("usage.total_cached_tokens").Int()) } + if root.Get("usage.total_tokens").Int() != 170 { + t.Errorf("total tokens = %d, want 170", root.Get("usage.total_tokens").Int()) + } steps := root.Get("steps").Array() if len(steps) != 3 {