diff --git a/backend/internal/service/openai_gateway_response_handling.go b/backend/internal/service/openai_gateway_response_handling.go index 18c4d0de5a..3802be34c2 100644 --- a/backend/internal/service/openai_gateway_response_handling.go +++ b/backend/internal/service/openai_gateway_response_handling.go @@ -981,15 +981,15 @@ func extractOpenAIUsageFromJSONBytes(body []byte) (OpenAIUsage, bool) { } // 部分 OpenAI 兼容上游(例如 Cline API)会将标准响应包在 data 字段中: // {"data":{"choices": [...], "usage": {...}}, "success":true}。 - // 按优先级尝试原生 OpenAI Responses、兼容层 data 包装和 Responses 包装, + // 按优先级先保留原有路径,再尝试兼容层 data 包装, // 避免同步请求能正常返回但用量被静默记录为 0。 candidates := []struct { usagePath string imageUsagePath string }{ {usagePath: "usage", imageUsagePath: "tool_usage.image_gen"}, - {usagePath: "data.usage", imageUsagePath: "data.tool_usage.image_gen"}, {usagePath: "response.usage", imageUsagePath: "response.tool_usage.image_gen"}, + {usagePath: "data.usage", imageUsagePath: "data.tool_usage.image_gen"}, {usagePath: "data.response.usage", imageUsagePath: "data.response.tool_usage.image_gen"}, } for _, candidate := range candidates { diff --git a/backend/internal/service/openai_gateway_service_test.go b/backend/internal/service/openai_gateway_service_test.go index 95a26bc148..423da89bd3 100644 --- a/backend/internal/service/openai_gateway_service_test.go +++ b/backend/internal/service/openai_gateway_service_test.go @@ -483,6 +483,27 @@ func TestExtractOpenAIUsage_ReadsClineDataEnvelope(t *testing.T) { require.Equal(t, 4, usage.CacheReadInputTokens) } +func TestExtractOpenAIUsage_ReadsWrappedResponsesDataEnvelope(t *testing.T) { + body := []byte(`{"data":{"response":{"usage":{"input_tokens":11,"output_tokens":5,"total_tokens":16,"input_tokens_details":{"cached_tokens":2}}}}}`) + + usage, ok := extractOpenAIUsageFromJSONBytes(body) + + require.True(t, ok) + require.Equal(t, 11, usage.InputTokens) + require.Equal(t, 5, usage.OutputTokens) + require.Equal(t, 2, usage.CacheReadInputTokens) +} + +func TestExtractOpenAIUsage_PreservesResponseUsagePriority(t *testing.T) { + body := []byte(`{"data":{"usage":{"prompt_tokens":100,"completion_tokens":50}},"response":{"usage":{"input_tokens":11,"output_tokens":5}}}`) + + usage, ok := extractOpenAIUsageFromJSONBytes(body) + + require.True(t, ok) + require.Equal(t, 11, usage.InputTokens) + require.Equal(t, 5, usage.OutputTokens) +} + func TestOpenAIGatewayService_BindHTTPResponseAccount(t *testing.T) { gin.SetMode(gin.TestMode) rec := httptest.NewRecorder()