feat: 兼容了用量使用显示

This commit is contained in:
chenchun
2025-07-22 10:40:23 +08:00
parent b6d670c240
commit a0eb234539
8 changed files with 97 additions and 75 deletions

View File

@@ -108,33 +108,33 @@ public class AzureDatabricksChatCompletionsService(ILogger<AzureDatabricksChatCo
continue; continue;
} }
var content = result?.Choices?.FirstOrDefault()?.Delta; // var content = result?.Choices?.FirstOrDefault()?.Delta;
//
if (first && content?.Content == OpenAIConstant.ThinkStart) // if (first && content?.Content == OpenAIConstant.ThinkStart)
{ // {
isThink = true; // isThink = true;
continue; // continue;
// 需要将content的内容转换到其他字段 // // 需要将content的内容转换到其他字段
} // }
//
if (isThink && content?.Content?.Contains(OpenAIConstant.ThinkEnd) == true) // if (isThink && content?.Content?.Contains(OpenAIConstant.ThinkEnd) == true)
{ // {
isThink = false; // isThink = false;
// 需要将content的内容转换到其他字段 // // 需要将content的内容转换到其他字段
continue; // continue;
} // }
//
if (isThink && result?.Choices != null) // if (isThink && result?.Choices != null)
{ // {
// 需要将content的内容转换到其他字段 // // 需要将content的内容转换到其他字段
foreach (var choice in result.Choices) // foreach (var choice in result.Choices)
{ // {
choice.Delta.ReasoningContent = choice.Delta.Content; // choice.Delta.ReasoningContent = choice.Delta.Content;
choice.Delta.Content = string.Empty; // choice.Delta.Content = string.Empty;
} // }
} // }
//
first = false; // first = false;
yield return result; yield return result;
} }

View File

@@ -93,36 +93,36 @@ public sealed class DeepSeekChatCompletionsService(ILogger<DeepSeekChatCompletio
var result = JsonSerializer.Deserialize<ThorChatCompletionsResponse>(line, var result = JsonSerializer.Deserialize<ThorChatCompletionsResponse>(line,
ThorJsonSerializer.DefaultOptions); ThorJsonSerializer.DefaultOptions);
var content = result?.Choices?.FirstOrDefault()?.Delta; // var content = result?.Choices?.FirstOrDefault()?.Delta;
//
if (first && string.IsNullOrWhiteSpace(content?.Content) && string.IsNullOrEmpty(content?.ReasoningContent)) // // if (first && string.IsNullOrWhiteSpace(content?.Content) && string.IsNullOrEmpty(content?.ReasoningContent))
{ // // {
continue; // // continue;
} // // }
//
if (first && content.Content == OpenAIConstant.ThinkStart) // if (first && content.Content == OpenAIConstant.ThinkStart)
{ // {
isThink = true; // isThink = true;
//continue; // //continue;
// 需要将content的内容转换到其他字段 // // 需要将content的内容转换到其他字段
} // }
//
if (isThink && content.Content.Contains(OpenAIConstant.ThinkEnd)) // if (isThink && content.Content.Contains(OpenAIConstant.ThinkEnd))
{ // {
isThink = false; // isThink = false;
// 需要将content的内容转换到其他字段 // // 需要将content的内容转换到其他字段
//continue; // //continue;
} // }
//
if (isThink) // if (isThink)
{ // {
// 需要将content的内容转换到其他字段 // // 需要将content的内容转换到其他字段
foreach (var choice in result.Choices) // foreach (var choice in result.Choices)
{ // {
//choice.Delta.ReasoningContent = choice.Delta.Content; // //choice.Delta.ReasoningContent = choice.Delta.Content;
//choice.Delta.Content = string.Empty; // //choice.Delta.Content = string.Empty;
} // }
} // }
// first = false; // first = false;

View File

@@ -29,10 +29,18 @@ public class MessageAggregateRoot : FullAuditedAggregateRoot<Guid>
ModelId = modelId; ModelId = modelId;
if (tokenUsage is not null) if (tokenUsage is not null)
{ {
long inputTokenCount = tokenUsage.PromptTokens
?? tokenUsage.InputTokens
?? 0;
long outputTokenCount = tokenUsage.CompletionTokens
?? tokenUsage.OutputTokens
?? 0;
this.TokenUsage = new TokenUsageValueObject this.TokenUsage = new TokenUsageValueObject
{ {
OutputTokenCount = tokenUsage.OutputTokens ?? 0, OutputTokenCount = outputTokenCount,
InputTokenCount = tokenUsage.InputTokens ?? 0, InputTokenCount = inputTokenCount,
TotalTokenCount = tokenUsage.TotalTokens ?? 0 TotalTokenCount = tokenUsage.TotalTokens ?? 0
}; };
} }

View File

@@ -37,22 +37,22 @@ public class UsageStatisticsAggregateRoot : FullAuditedAggregateRoot<Guid>
/// <summary> /// <summary>
/// 使用输出token总数 /// 使用输出token总数
/// </summary> /// </summary>
public int UsageOutputTokenCount { get; set; } public long UsageOutputTokenCount { get; set; }
/// <summary> /// <summary>
/// 使用输入总数 /// 使用输入总数
/// </summary> /// </summary>
public int UsageInputTokenCount { get; set; } public long UsageInputTokenCount { get; set; }
/// <summary> /// <summary>
/// 总token使用数量 /// 总token使用数量
/// </summary> /// </summary>
public int TotalTokenCount { get; set; } public long TotalTokenCount { get; set; }
/// <summary> /// <summary>
/// 新增一次聊天统计 /// 新增一次聊天统计
/// </summary> /// </summary>
public void AddOnceChat(int inputTokenCount, int outputTokenCount) public void AddOnceChat(long inputTokenCount, long outputTokenCount)
{ {
UsageTotalNumber += 1; UsageTotalNumber += 1;
UsageOutputTokenCount += outputTokenCount; UsageOutputTokenCount += outputTokenCount;

View File

@@ -2,9 +2,9 @@
public class TokenUsageValueObject public class TokenUsageValueObject
{ {
public int OutputTokenCount { get; set; } public long OutputTokenCount { get; set; }
public int InputTokenCount { get; set; } public long InputTokenCount { get; set; }
public long TotalTokenCount { get; set; } public long TotalTokenCount { get; set; }
} }

View File

@@ -132,8 +132,7 @@ public class AiGateWayManager : DomainService
TokenUsage = data.Usage TokenUsage = data.Usage
}); });
await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, data.Usage.InputTokens ?? 0, await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, data.Usage);
data.Usage.OutputTokens ?? 0);
} }
await response.WriteAsJsonAsync(data, cancellationToken); await response.WriteAsJsonAsync(data, cancellationToken);
@@ -200,7 +199,7 @@ public class AiGateWayManager : DomainService
{ {
await foreach (var data in completeChatResponse) await foreach (var data in completeChatResponse)
{ {
if (data.Usage is not null && data.Usage.TotalTokens is not null) if (data.Usage is not null)
{ {
tokenUsage = data.Usage; tokenUsage = data.Usage;
} }
@@ -261,8 +260,7 @@ public class AiGateWayManager : DomainService
TokenUsage = tokenUsage TokenUsage = tokenUsage
}); });
await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, tokenUsage.InputTokens ?? 0, await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, tokenUsage);
tokenUsage.OutputTokens ?? 0);
} }
} }
} }

View File

@@ -1,5 +1,6 @@
using Medallion.Threading; using Medallion.Threading;
using Volo.Abp.Domain.Services; using Volo.Abp.Domain.Services;
using Yi.Framework.AiHub.Application.Contracts.Dtos.OpenAi;
using Yi.Framework.AiHub.Domain.Entities; using Yi.Framework.AiHub.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions; using Yi.Framework.SqlSugarCore.Abstractions;
@@ -17,8 +18,16 @@ public class UsageStatisticsManager : DomainService
private IDistributedLockProvider DistributedLock => private IDistributedLockProvider DistributedLock =>
LazyServiceProvider.LazyGetRequiredService<IDistributedLockProvider>(); LazyServiceProvider.LazyGetRequiredService<IDistributedLockProvider>();
public async Task SetUsageAsync(Guid userId, string modelId, int inputTokenCount, int outputTokenCount) public async Task SetUsageAsync(Guid userId, string modelId, ThorUsageResponse? tokenUsage)
{ {
long inputTokenCount = tokenUsage?.PromptTokens
?? tokenUsage.InputTokens
?? 0;
long outputTokenCount = tokenUsage?.CompletionTokens
?? tokenUsage.OutputTokens
?? 0;
await using (await DistributedLock.AcquireLockAsync($"UsageStatistics:{userId.ToString()}")) await using (await DistributedLock.AcquireLockAsync($"UsageStatistics:{userId.ToString()}"))
{ {
var entity = await _repository._DbQueryable.FirstAsync(x => x.UserId == userId && x.ModelId == modelId); var entity = await _repository._DbQueryable.FirstAsync(x => x.UserId == userId && x.ModelId == modelId);
@@ -37,8 +46,4 @@ public class UsageStatisticsManager : DomainService
} }
} }
} }
}
internal class LazyServiceProvider
{
} }

View File

@@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Domain; using Volo.Abp.Domain;
using Yi.Framework.AiHub.Application.Contracts.Dtos.OpenAi;
using Yi.Framework.AiHub.Domain.AiGateWay; using Yi.Framework.AiHub.Domain.AiGateWay;
using Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorAzureDatabricks.Chats; using Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorAzureDatabricks.Chats;
using Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorAzureOpenAI.Chats; using Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorAzureOpenAI.Chats;
@@ -48,6 +49,16 @@ namespace Yi.Framework.AiHub.Domain
request.Temperature = null; request.Temperature = null;
} }
}); });
options.Handles.Add(request =>
{
if (request.Stream == true)
{
request.StreamOptions = new ThorStreamOptions()
{
IncludeUsage = true
};
}
});
}); });
} }