fix: 修复 Anthropic TokenUsage 计算与流式响应的用量统计
This commit is contained in:
@@ -18,39 +18,7 @@ public class AnthropicStreamDto
|
|||||||
[JsonPropertyName("usage")] public AnthropicCompletionDtoUsage? Usage { get; set; }
|
[JsonPropertyName("usage")] public AnthropicCompletionDtoUsage? Usage { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("error")] public AnthropicStreamErrorDto? Error { 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
|
public class AnthropicStreamErrorDto
|
||||||
@@ -115,38 +83,7 @@ public class AnthropicChatCompletionDto
|
|||||||
public object stop_sequence { get; set; }
|
public object stop_sequence { get; set; }
|
||||||
|
|
||||||
public AnthropicCompletionDtoUsage? Usage { 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
|
public class AnthropicChatCompletionDtoContent
|
||||||
|
|||||||
@@ -549,7 +549,16 @@ public class AiGateWayManager : DomainService
|
|||||||
LazyServiceProvider.GetRequiredKeyedService<IAnthropicChatCompletionService>(modelDescribe.HandlerName);
|
LazyServiceProvider.GetRequiredKeyedService<IAnthropicChatCompletionService>(modelDescribe.HandlerName);
|
||||||
var data = await chatService.ChatCompletionsAsync(modelDescribe, request, cancellationToken);
|
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)
|
if (userId is not null)
|
||||||
{
|
{
|
||||||
@@ -558,7 +567,7 @@ public class AiGateWayManager : DomainService
|
|||||||
{
|
{
|
||||||
Content = "不予存储",
|
Content = "不予存储",
|
||||||
ModelId = sourceModelId,
|
ModelId = sourceModelId,
|
||||||
TokenUsage = data.TokenUsage,
|
TokenUsage = tokenUsage,
|
||||||
}, tokenId);
|
}, tokenId);
|
||||||
|
|
||||||
await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId,
|
await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId,
|
||||||
@@ -566,13 +575,13 @@ public class AiGateWayManager : DomainService
|
|||||||
{
|
{
|
||||||
Content = "不予存储",
|
Content = "不予存储",
|
||||||
ModelId = sourceModelId,
|
ModelId = sourceModelId,
|
||||||
TokenUsage = data.TokenUsage
|
TokenUsage = tokenUsage
|
||||||
}, tokenId);
|
}, tokenId);
|
||||||
|
|
||||||
await _usageStatisticsManager.SetUsageAsync(userId.Value, sourceModelId, data.TokenUsage, tokenId);
|
await _usageStatisticsManager.SetUsageAsync(userId.Value, sourceModelId, tokenUsage, tokenId);
|
||||||
|
|
||||||
// 直接扣减尊享token包用量
|
// 直接扣减尊享token包用量
|
||||||
var totalTokens = data.TokenUsage.TotalTokens ?? 0;
|
var totalTokens = tokenUsage.TotalTokens ?? 0;
|
||||||
if (totalTokens > 0)
|
if (totalTokens > 0)
|
||||||
{
|
{
|
||||||
await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens);
|
await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens);
|
||||||
@@ -620,24 +629,41 @@ public class AiGateWayManager : DomainService
|
|||||||
}
|
}
|
||||||
|
|
||||||
var completeChatResponse = chatService.StreamChatCompletionsAsync(modelDescribe, request, cancellationToken);
|
var completeChatResponse = chatService.StreamChatCompletionsAsync(modelDescribe, request, cancellationToken);
|
||||||
ThorUsageResponse? tokenUsage = null;
|
ThorUsageResponse? tokenUsage = new ThorUsageResponse();
|
||||||
StringBuilder backupSystemContent = new StringBuilder();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await foreach (var responseResult in completeChatResponse)
|
await foreach (var responseResult in completeChatResponse)
|
||||||
{
|
{
|
||||||
responseResult.Item2.SupplementalMultiplier(modelDescribe.Multiplier);
|
//部分供应商message_start放一部分
|
||||||
//message_start是为了保底机制
|
if (responseResult.Item1.Contains("message_start"))
|
||||||
if (responseResult.Item1.Contains("message_delta") || responseResult.Item1.Contains("message_start")||responseResult.Item1.Contains("message_stop"))
|
|
||||||
{
|
{
|
||||||
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,
|
await WriteAsEventStreamDataAsync(httpContext, responseResult.Item1, responseResult.Item2,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
@@ -648,7 +674,8 @@ public class AiGateWayManager : DomainService
|
|||||||
var errorContent = $"对话Ai异常,异常信息:\n当前Ai模型:{sourceModelId}\n异常信息:{e.Message}\n异常堆栈:{e}";
|
var errorContent = $"对话Ai异常,异常信息:\n当前Ai模型:{sourceModelId}\n异常信息:{e.Message}\n异常堆栈:{e}";
|
||||||
throw new UserFriendlyException(errorContent);
|
throw new UserFriendlyException(errorContent);
|
||||||
}
|
}
|
||||||
|
tokenUsage.TotalTokens = (tokenUsage.InputTokens ?? 0) + (tokenUsage.OutputTokens ?? 0);
|
||||||
|
tokenUsage.SetSupplementalMultiplier(modelDescribe.Multiplier);
|
||||||
await _aiMessageManager.CreateUserMessageAsync(userId, sessionId,
|
await _aiMessageManager.CreateUserMessageAsync(userId, sessionId,
|
||||||
new MessageInputDto
|
new MessageInputDto
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user