From 5157eac35c0cd73c5c4ec7201948ae66f578a00d Mon Sep 17 00:00:00 2001 From: chenchun Date: Mon, 5 Jan 2026 19:34:48 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20Anthropic=20TokenUs?= =?UTF-8?q?age=20=E8=AE=A1=E7=AE=97=E4=B8=8E=E6=B5=81=E5=BC=8F=E5=93=8D?= =?UTF-8?q?=E5=BA=94=E7=9A=84=E7=94=A8=E9=87=8F=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Anthropic/AnthropicChatCompletionDto.cs | 65 +------------------ .../Managers/AiGateWayManager.cs | 57 +++++++++++----- 2 files changed, 43 insertions(+), 79 deletions(-) diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/Anthropic/AnthropicChatCompletionDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/Anthropic/AnthropicChatCompletionDto.cs index 05242651..e6f6c0df 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/Anthropic/AnthropicChatCompletionDto.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/Anthropic/AnthropicChatCompletionDto.cs @@ -18,39 +18,7 @@ public class AnthropicStreamDto [JsonPropertyName("usage")] public AnthropicCompletionDtoUsage? Usage { get; set; } [JsonPropertyName("error")] public AnthropicStreamErrorDto? Error { get; set; } - - [JsonIgnore] - public ThorUsageResponse TokenUsage => new ThorUsageResponse - { - PromptTokens = Usage?.InputTokens??0 + Usage?.CacheCreationInputTokens??0 + Usage?.CacheReadInputTokens??0, - InputTokens = Usage?.InputTokens??0 + Usage?.CacheCreationInputTokens??0 + Usage?.CacheReadInputTokens??0, - OutputTokens = Usage?.OutputTokens??0, - InputTokensDetails = null, - CompletionTokens = Usage?.OutputTokens??0, - TotalTokens = Usage?.InputTokens??0 + Usage?.CacheCreationInputTokens??0 + Usage?.CacheReadInputTokens??0 + - Usage?.OutputTokens??0, - PromptTokensDetails = null, - CompletionTokensDetails = null - }; - - - public void SupplementalMultiplier(decimal multiplier) - { - if (this.Usage is not null) - { - this.Usage.CacheCreationInputTokens = - (int)Math.Round((this.Usage.CacheCreationInputTokens ?? 0) * multiplier); - - this.Usage.CacheReadInputTokens = - (int)Math.Round((this.Usage.CacheReadInputTokens ?? 0) * multiplier); - - this.Usage.InputTokens = - (int)Math.Round((this.Usage.InputTokens ?? 0) * multiplier); - - this.Usage.OutputTokens = - (int)Math.Round((this.Usage.OutputTokens ?? 0) * multiplier); - } - } + } public class AnthropicStreamErrorDto @@ -115,38 +83,7 @@ public class AnthropicChatCompletionDto public object stop_sequence { get; set; } public AnthropicCompletionDtoUsage? Usage { get; set; } - - [JsonIgnore] - public ThorUsageResponse TokenUsage => new ThorUsageResponse - { - PromptTokens = Usage?.InputTokens??0 + Usage?.CacheCreationInputTokens??0 + Usage?.CacheReadInputTokens??0, - InputTokens = Usage?.InputTokens??0 + Usage?.CacheCreationInputTokens??0 + Usage?.CacheReadInputTokens??0, - OutputTokens = Usage?.OutputTokens??0, - InputTokensDetails = null, - CompletionTokens = Usage?.OutputTokens??0, - TotalTokens = Usage?.InputTokens??0 + Usage?.CacheCreationInputTokens??0 + Usage?.CacheReadInputTokens??0 + - Usage?.OutputTokens??0, - PromptTokensDetails = null, - CompletionTokensDetails = null - }; - public void SupplementalMultiplier(decimal multiplier) - { - if (this.Usage is not null) - { - this.Usage.CacheCreationInputTokens = - (int)Math.Round((this.Usage?.CacheCreationInputTokens ?? 0) * multiplier); - - this.Usage.CacheReadInputTokens = - (int)Math.Round((this.Usage?.CacheReadInputTokens ?? 0) * multiplier); - - this.Usage.InputTokens = - (int)Math.Round((this.Usage?.InputTokens ?? 0) * multiplier); - - this.Usage.OutputTokens = - (int)Math.Round((this.Usage?.OutputTokens ?? 0) * multiplier); - } - } } public class AnthropicChatCompletionDtoContent diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs index 9b9a4a59..2b80d844 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs @@ -549,7 +549,16 @@ public class AiGateWayManager : DomainService LazyServiceProvider.GetRequiredKeyedService(modelDescribe.HandlerName); var data = await chatService.ChatCompletionsAsync(modelDescribe, request, cancellationToken); - data.SupplementalMultiplier(modelDescribe.Multiplier); + var currentUsage = data.Usage; + ThorUsageResponse tokenUsage = new ThorUsageResponse + { + InputTokens = (currentUsage?.InputTokens??0) + (currentUsage?.CacheCreationInputTokens??0)+ (currentUsage?.CacheReadInputTokens??0), + OutputTokens = (currentUsage?.OutputTokens??0), + TotalTokens = (currentUsage?.InputTokens??0) + (currentUsage?.CacheCreationInputTokens??0)+ (currentUsage?.CacheReadInputTokens??0)+(currentUsage?.OutputTokens??0) + }; + + + tokenUsage.SetSupplementalMultiplier(modelDescribe.Multiplier); if (userId is not null) { @@ -558,7 +567,7 @@ public class AiGateWayManager : DomainService { Content = "不予存储", ModelId = sourceModelId, - TokenUsage = data.TokenUsage, + TokenUsage = tokenUsage, }, tokenId); await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId, @@ -566,13 +575,13 @@ public class AiGateWayManager : DomainService { Content = "不予存储", ModelId = sourceModelId, - TokenUsage = data.TokenUsage + TokenUsage = tokenUsage }, tokenId); - await _usageStatisticsManager.SetUsageAsync(userId.Value, sourceModelId, data.TokenUsage, tokenId); + await _usageStatisticsManager.SetUsageAsync(userId.Value, sourceModelId, tokenUsage, tokenId); // 直接扣减尊享token包用量 - var totalTokens = data.TokenUsage.TotalTokens ?? 0; + var totalTokens = tokenUsage.TotalTokens ?? 0; if (totalTokens > 0) { await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens); @@ -620,24 +629,41 @@ public class AiGateWayManager : DomainService } var completeChatResponse = chatService.StreamChatCompletionsAsync(modelDescribe, request, cancellationToken); - ThorUsageResponse? tokenUsage = null; - StringBuilder backupSystemContent = new StringBuilder(); + ThorUsageResponse? tokenUsage = new ThorUsageResponse(); try { await foreach (var responseResult in completeChatResponse) { - responseResult.Item2.SupplementalMultiplier(modelDescribe.Multiplier); - //message_start是为了保底机制 - if (responseResult.Item1.Contains("message_delta") || responseResult.Item1.Contains("message_start")||responseResult.Item1.Contains("message_stop")) + //部分供应商message_start放一部分 + if (responseResult.Item1.Contains("message_start")) { - if (responseResult.Item2?.TokenUsage is not null&&responseResult.Item2?.TokenUsage.TotalTokens>0) + var currentTokenUsage = responseResult.Item2.Message.Usage; + if ((currentTokenUsage.InputTokens ?? 0) != 0) { - tokenUsage = responseResult.Item2?.TokenUsage; + tokenUsage.InputTokens = (currentTokenUsage?.InputTokens??0) + (currentTokenUsage?.CacheCreationInputTokens??0)+ (currentTokenUsage?.CacheReadInputTokens??0); + } + if ((currentTokenUsage.OutputTokens ?? 0) != 0) + { + tokenUsage.OutputTokens = currentTokenUsage.OutputTokens; } - } - backupSystemContent.Append(responseResult.Item2?.Delta?.Text); + //message_delta又放一部分 + if (responseResult.Item1.Contains("message_delta")) + { + var currentTokenUsage = responseResult.Item2.Usage; + + if ((currentTokenUsage.InputTokens ?? 0) != 0) + { + tokenUsage.InputTokens = (currentTokenUsage?.InputTokens??0) + (currentTokenUsage?.CacheCreationInputTokens??0)+ (currentTokenUsage?.CacheReadInputTokens??0);; + } + if ((currentTokenUsage.OutputTokens ?? 0) != 0) + { + tokenUsage.OutputTokens = currentTokenUsage.OutputTokens; + } + } + + await WriteAsEventStreamDataAsync(httpContext, responseResult.Item1, responseResult.Item2, cancellationToken); } @@ -648,7 +674,8 @@ public class AiGateWayManager : DomainService var errorContent = $"对话Ai异常,异常信息:\n当前Ai模型:{sourceModelId}\n异常信息:{e.Message}\n异常堆栈:{e}"; throw new UserFriendlyException(errorContent); } - + tokenUsage.TotalTokens = (tokenUsage.InputTokens ?? 0) + (tokenUsage.OutputTokens ?? 0); + tokenUsage.SetSupplementalMultiplier(modelDescribe.Multiplier); await _aiMessageManager.CreateUserMessageAsync(userId, sessionId, new MessageInputDto {