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, PromptTokensDetails = null,
CompletionTokensDetails = 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 public class AnthropicStreamErrorDto
@@ -93,7 +112,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] [JsonIgnore]
public ThorUsageResponse TokenUsage => new ThorUsageResponse public ThorUsageResponse TokenUsage => new ThorUsageResponse
@@ -108,6 +127,24 @@ public class AnthropicChatCompletionDto
PromptTokensDetails = null, PromptTokensDetails = null,
CompletionTokensDetails = 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 public class AnthropicChatCompletionDtoContent

View File

@@ -60,4 +60,19 @@ public record ThorChatCompletionsResponse
/// </summary> /// </summary>
[JsonPropertyName("error")] [JsonPropertyName("error")]
public ThorError? Error { get; set; } 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,9 +9,13 @@ using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi;
namespace Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorClaude.Chats; 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 : IAnthropicChatCompletionService
{ {
public const double ClaudeMultiplier = 1.3d;
public async Task<AnthropicChatCompletionDto> ChatCompletionsAsync(AiModelDescribe options, AnthropicInput input, public async Task<AnthropicChatCompletionDto> ChatCompletionsAsync(AiModelDescribe options, AnthropicInput input,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
@@ -22,6 +26,7 @@ public class AnthropicChatCompletionsService(IHttpClientFactory httpClientFactor
{ {
options.Endpoint = "https://api.anthropic.com/"; options.Endpoint = "https://api.anthropic.com/";
} }
var client = httpClientFactory.CreateClient(); var client = httpClientFactory.CreateClient();
var headers = new Dictionary<string, string> var headers = new Dictionary<string, string>
@@ -71,7 +76,8 @@ public class AnthropicChatCompletionsService(IHttpClientFactory httpClientFactor
if (response.StatusCode >= HttpStatusCode.BadRequest) if (response.StatusCode >= HttpStatusCode.BadRequest)
{ {
var error = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); 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); response.StatusCode, error);
throw new Exception("OpenAI对话异常" + response.StatusCode.ToString()); throw new Exception("OpenAI对话异常" + response.StatusCode.ToString());
@@ -80,11 +86,13 @@ public class AnthropicChatCompletionsService(IHttpClientFactory httpClientFactor
var value = var value =
await response.Content.ReadFromJsonAsync<AnthropicChatCompletionDto>(ThorJsonSerializer.DefaultOptions, await response.Content.ReadFromJsonAsync<AnthropicChatCompletionDto>(ThorJsonSerializer.DefaultOptions,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
value.SupplementalMultiplier(AnthropicChatCompletionsService.ClaudeMultiplier);
return value; 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) CancellationToken cancellationToken = default)
{ {
using var openai = using var openai =
@@ -117,7 +125,8 @@ public class AnthropicChatCompletionsService(IHttpClientFactory httpClientFactor
if (response.StatusCode >= HttpStatusCode.BadRequest) if (response.StatusCode >= HttpStatusCode.BadRequest)
{ {
var error = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); 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); response.StatusCode, error);
throw new Exception("OpenAI对话异常" + response.StatusCode); throw new Exception("OpenAI对话异常" + response.StatusCode);
@@ -161,6 +170,7 @@ public class AnthropicChatCompletionsService(IHttpClientFactory httpClientFactor
var result = JsonSerializer.Deserialize<AnthropicStreamDto>(data, var result = JsonSerializer.Deserialize<AnthropicStreamDto>(data,
ThorJsonSerializer.DefaultOptions); ThorJsonSerializer.DefaultOptions);
result.SupplementalMultiplier(AnthropicChatCompletionsService.ClaudeMultiplier);
yield return (eventType, result); yield return (eventType, result);
} }
} }

View File

@@ -687,7 +687,7 @@ public sealed class ClaudiaChatCompletionsService(
first = false; first = false;
yield return new ThorChatCompletionsResponse() var output = new ThorChatCompletionsResponse()
{ {
Choices = chat, Choices = chat,
Model = input.Model, Model = input.Model,
@@ -701,6 +701,8 @@ public sealed class ClaudiaChatCompletionsService(
TotalTokens = result.Message.Usage?.InputTokens + result.Message.Usage?.OutputTokens 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.Usage.TotalTokens = thor.Usage.InputTokens + thor.Usage.OutputTokens;
thor.SupplementalMultiplier(AnthropicChatCompletionsService.ClaudeMultiplier);
return thor; return thor;
} }
} }