feat: 增加 Claude 模型 Token 使用量倍数调整功能

This commit is contained in:
ccnetcore
2025-10-14 23:41:26 +08:00
parent 959eb3f782
commit d6adf9b736
4 changed files with 75 additions and 10 deletions

View File

@@ -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

View File

@@ -60,4 +60,19 @@ public record ThorChatCompletionsResponse
/// </summary>
[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);
}
}
}

View File

@@ -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<AnthropicChatCompletionsService> logger)
public class AnthropicChatCompletionsService(
IHttpClientFactory httpClientFactory,
ILogger<AnthropicChatCompletionsService> logger)
: IAnthropicChatCompletionService
{
public const double ClaudeMultiplier = 1.3d;
public async Task<AnthropicChatCompletionDto> 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<string, string>
@@ -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<AnthropicChatCompletionDto>(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<string, string>
@@ -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<AnthropicStreamDto>(data,
ThorJsonSerializer.DefaultOptions);
result.SupplementalMultiplier(AnthropicChatCompletionsService.ClaudeMultiplier);
yield return (eventType, result);
}
}

View File

@@ -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;
}
}