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 f505bc32..65c82081 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 @@ -32,6 +32,25 @@ public class AnthropicStreamDto PromptTokensDetails = null, CompletionTokensDetails = null }; + + + public void SupplementalMultiplier(double 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 @@ -93,7 +112,7 @@ public class AnthropicChatCompletionDto public object stop_sequence { get; set; } - public AnthropicCompletionDtoUsage Usage { get; set; } + public AnthropicCompletionDtoUsage? Usage { get; set; } [JsonIgnore] public ThorUsageResponse TokenUsage => new ThorUsageResponse @@ -108,6 +127,24 @@ public class AnthropicChatCompletionDto PromptTokensDetails = null, CompletionTokensDetails = null }; + + public void SupplementalMultiplier(double 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.Shared/Dtos/OpenAi/ThorChatCompletionsResponse.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/OpenAi/ThorChatCompletionsResponse.cs index 6c367502..a63d663c 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/OpenAi/ThorChatCompletionsResponse.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/OpenAi/ThorChatCompletionsResponse.cs @@ -60,4 +60,19 @@ public record ThorChatCompletionsResponse /// [JsonPropertyName("error")] public ThorError? Error { get; set; } + + public void SupplementalMultiplier(double multiplier) + { + if (this.Usage is not null) + { + this.Usage.InputTokens = + (int)Math.Round((this.Usage.InputTokens ?? 0) * multiplier); + this.Usage.OutputTokens = + (int)Math.Round((this.Usage.OutputTokens ?? 0) * multiplier); + this.Usage.CompletionTokens = + (int)Math.Round((this.Usage.CompletionTokens ?? 0) * multiplier); + this.Usage.PromptTokens = + (int)Math.Round((this.Usage.PromptTokens ?? 0) * multiplier); + } + } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorClaude/Chats/AnthropicChatCompletionsService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorClaude/Chats/AnthropicChatCompletionsService.cs index e08b95e2..bba2f091 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorClaude/Chats/AnthropicChatCompletionsService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorClaude/Chats/AnthropicChatCompletionsService.cs @@ -9,19 +9,24 @@ using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi; namespace Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorClaude.Chats; -public class AnthropicChatCompletionsService(IHttpClientFactory httpClientFactory,ILogger logger) +public class AnthropicChatCompletionsService( + IHttpClientFactory httpClientFactory, + ILogger logger) : IAnthropicChatCompletionService { + public const double ClaudeMultiplier = 1.3d; + public async Task ChatCompletionsAsync(AiModelDescribe options, AnthropicInput input, CancellationToken cancellationToken = default) { - using var openai = + using var openai = Activity.Current?.Source.StartActivity("Claudia 对话补全"); if (string.IsNullOrEmpty(options.Endpoint)) { options.Endpoint = "https://api.anthropic.com/"; } + var client = httpClientFactory.CreateClient(); var headers = new Dictionary @@ -71,30 +76,33 @@ public class AnthropicChatCompletionsService(IHttpClientFactory httpClientFactor if (response.StatusCode >= HttpStatusCode.BadRequest) { var error = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); - logger.LogError("OpenAI对话异常 请求地址:{Address}, StatusCode: {StatusCode} Response: {Response}", options.Endpoint, + logger.LogError("OpenAI对话异常 请求地址:{Address}, StatusCode: {StatusCode} Response: {Response}", + options.Endpoint, response.StatusCode, error); throw new Exception("OpenAI对话异常" + response.StatusCode.ToString()); } - + var value = await response.Content.ReadFromJsonAsync(ThorJsonSerializer.DefaultOptions, cancellationToken: cancellationToken); + value.SupplementalMultiplier(AnthropicChatCompletionsService.ClaudeMultiplier); return value; } - public async IAsyncEnumerable<(string, AnthropicStreamDto?)> StreamChatCompletionsAsync(AiModelDescribe options, AnthropicInput input, + public async IAsyncEnumerable<(string, AnthropicStreamDto?)> StreamChatCompletionsAsync(AiModelDescribe options, + AnthropicInput input, CancellationToken cancellationToken = default) { - using var openai = + using var openai = Activity.Current?.Source.StartActivity("Claudia 对话补全"); if (string.IsNullOrEmpty(options.Endpoint)) { options.Endpoint = "https://api.anthropic.com/"; } - + var client = httpClientFactory.CreateClient(); var headers = new Dictionary @@ -117,7 +125,8 @@ public class AnthropicChatCompletionsService(IHttpClientFactory httpClientFactor if (response.StatusCode >= HttpStatusCode.BadRequest) { var error = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); - logger.LogError("OpenAI对话异常 请求地址:{Address}, StatusCode: {StatusCode} Response: {Response}", options.Endpoint, + logger.LogError("OpenAI对话异常 请求地址:{Address}, StatusCode: {StatusCode} Response: {Response}", + options.Endpoint, response.StatusCode, error); throw new Exception("OpenAI对话异常" + response.StatusCode); @@ -161,6 +170,7 @@ public class AnthropicChatCompletionsService(IHttpClientFactory httpClientFactor var result = JsonSerializer.Deserialize(data, ThorJsonSerializer.DefaultOptions); + result.SupplementalMultiplier(AnthropicChatCompletionsService.ClaudeMultiplier); yield return (eventType, result); } } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorClaude/Chats/ClaudiaChatCompletionsService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorClaude/Chats/ClaudiaChatCompletionsService.cs index 99ed2da0..e868e93a 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorClaude/Chats/ClaudiaChatCompletionsService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorClaude/Chats/ClaudiaChatCompletionsService.cs @@ -687,7 +687,7 @@ public sealed class ClaudiaChatCompletionsService( first = false; - yield return new ThorChatCompletionsResponse() + var output = new ThorChatCompletionsResponse() { Choices = chat, Model = input.Model, @@ -701,6 +701,8 @@ public sealed class ClaudiaChatCompletionsService( TotalTokens = result.Message.Usage?.InputTokens + result.Message.Usage?.OutputTokens } }; + output.SupplementalMultiplier(AnthropicChatCompletionsService.ClaudeMultiplier); + yield return output; } } @@ -856,6 +858,7 @@ public sealed class ClaudiaChatCompletionsService( } thor.Usage.TotalTokens = thor.Usage.InputTokens + thor.Usage.OutputTokens; + thor.SupplementalMultiplier(AnthropicChatCompletionsService.ClaudeMultiplier); return thor; } } \ No newline at end of file