9 changed files with 203 additions and 4 deletions
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.Specifications; |
|||
|
|||
namespace LINGYUN.Abp.AIManagement.Tokens; |
|||
public interface ITokenUsageRecordRepository : IBasicRepository<TokenUsageRecord, Guid> |
|||
{ |
|||
Task<TokenUsageRecord?> FindByMessageIdAsync( |
|||
Guid conversationId, |
|||
Guid? messageId, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<int> GetCountAsync( |
|||
ISpecification<TokenUsageRecord> specification, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<TokenUsageRecord>> GetListAsync( |
|||
ISpecification<TokenUsageRecord> specification, |
|||
string sorting = $"{nameof(TokenUsageRecord.CreationTime)}", |
|||
int maxResultCount = 10, |
|||
int skipCount = 0, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
@ -1,8 +1,42 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.AIManagement.Tokens; |
|||
public class TokenUsageRecord |
|||
public class TokenUsageRecord : AuditedEntity<Guid>, IMultiTenant |
|||
{ |
|||
public Guid? TenantId { get; private set; } |
|||
public Guid? MessageId { get; private set; } |
|||
public Guid? ConversationId { get; private set; } |
|||
public long? InputTokenCount { get; set; } |
|||
public long? OutputTokenCount { get; set; } |
|||
public long? TotalTokenCount { get; set; } |
|||
public long? CachedInputTokenCount { get; set; } |
|||
public long? ReasoningTokenCount { get; set; } |
|||
protected TokenUsageRecord() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public TokenUsageRecord( |
|||
Guid id, |
|||
Guid? messageId = null, |
|||
Guid? conversationId = null, |
|||
long? inputTokenCount = null, |
|||
long? outputTokenCount = null, |
|||
long? totalTokenCount = null, |
|||
long? cachedInputTokenCount = null, |
|||
long? reasoningTokenCount = null, |
|||
Guid? tenantId = null) |
|||
: base(id) |
|||
{ |
|||
MessageId = messageId; |
|||
ConversationId = conversationId; |
|||
InputTokenCount = inputTokenCount; |
|||
OutputTokenCount = outputTokenCount; |
|||
TotalTokenCount = totalTokenCount; |
|||
CachedInputTokenCount = cachedInputTokenCount; |
|||
ReasoningTokenCount = reasoningTokenCount; |
|||
TenantId = tenantId; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,68 @@ |
|||
using LINGYUN.Abp.AI.Models; |
|||
using LINGYUN.Abp.AI.Tokens; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.AIManagement.Tokens; |
|||
|
|||
[Dependency(ReplaceServices = true)] |
|||
public class TokenUsageStore : ITokenUsageStore, ITransientDependency |
|||
{ |
|||
private readonly ICurrentTenant _currentTenant; |
|||
private readonly IGuidGenerator _guidGenerator; |
|||
private readonly ITokenUsageRecordRepository _tokenUsageRecordRepository; |
|||
|
|||
public TokenUsageStore( |
|||
ICurrentTenant currentTenant, |
|||
IGuidGenerator guidGenerator, |
|||
ITokenUsageRecordRepository tokenUsageRecordRepository) |
|||
{ |
|||
_currentTenant = currentTenant; |
|||
_guidGenerator = guidGenerator; |
|||
_tokenUsageRecordRepository = tokenUsageRecordRepository; |
|||
} |
|||
|
|||
public async virtual Task SaveTokenUsageAsync(TokenUsageInfo tokenUsageInfo) |
|||
{ |
|||
var tokenUsageRecord = await _tokenUsageRecordRepository.FindByMessageIdAsync( |
|||
tokenUsageInfo.ConversationId, |
|||
tokenUsageInfo.MessageId); |
|||
|
|||
if (tokenUsageRecord == null) |
|||
{ |
|||
tokenUsageRecord = new TokenUsageRecord( |
|||
_guidGenerator.Create(), |
|||
tokenUsageInfo.MessageId, |
|||
tokenUsageInfo.ConversationId, |
|||
tokenUsageInfo.InputTokenCount, |
|||
tokenUsageInfo.OutputTokenCount, |
|||
tokenUsageInfo.TotalTokenCount, |
|||
tokenUsageInfo.CachedInputTokenCount, |
|||
tokenUsageInfo.ReasoningTokenCount, |
|||
_currentTenant.Id); |
|||
|
|||
await _tokenUsageRecordRepository.InsertAsync(tokenUsageRecord); |
|||
} |
|||
else |
|||
{ |
|||
tokenUsageRecord.InputTokenCount ??= 0; |
|||
tokenUsageRecord.InputTokenCount += tokenUsageInfo.InputTokenCount; |
|||
|
|||
tokenUsageRecord.OutputTokenCount ??= 0; |
|||
tokenUsageRecord.OutputTokenCount += tokenUsageInfo.OutputTokenCount; |
|||
|
|||
tokenUsageRecord.CachedInputTokenCount ??= 0; |
|||
tokenUsageRecord.CachedInputTokenCount += tokenUsageInfo.CachedInputTokenCount; |
|||
|
|||
tokenUsageRecord.ReasoningTokenCount ??= 0; |
|||
tokenUsageRecord.ReasoningTokenCount += tokenUsageInfo.ReasoningTokenCount; |
|||
|
|||
tokenUsageRecord.TotalTokenCount ??= 0; |
|||
tokenUsageRecord.TotalTokenCount += tokenUsageInfo.TotalTokenCount; |
|||
|
|||
await _tokenUsageRecordRepository.UpdateAsync(tokenUsageRecord); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
using LINGYUN.Abp.AIManagement.Tokens; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Linq.Dynamic.Core; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.Specifications; |
|||
|
|||
namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; |
|||
public class EfCoreTokenUsageRecordRepository : EfCoreRepository<IAIManagementDbContext, TokenUsageRecord, Guid>, ITokenUsageRecordRepository |
|||
{ |
|||
public EfCoreTokenUsageRecordRepository( |
|||
IDbContextProvider<IAIManagementDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public async virtual Task<TokenUsageRecord?> FindByMessageIdAsync( |
|||
Guid conversationId, |
|||
Guid? messageId, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await (await GetQueryableAsync()) |
|||
.Where(x => x.ConversationId == conversationId && x.MessageId == messageId) |
|||
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public async virtual Task<int> GetCountAsync( |
|||
ISpecification<TokenUsageRecord> specification, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await (await GetQueryableAsync()) |
|||
.Where(specification.ToExpression()) |
|||
.CountAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public async virtual Task<List<TokenUsageRecord>> GetListAsync( |
|||
ISpecification<TokenUsageRecord> specification, |
|||
string sorting = $"{nameof(TokenUsageRecord.CreationTime)}", |
|||
int maxResultCount = 10, |
|||
int skipCount = 0, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await (await GetQueryableAsync()) |
|||
.Where(specification.ToExpression()) |
|||
.OrderBy(!sorting.IsNullOrWhiteSpace() ? sorting : $"{nameof(TokenUsageRecord.CreationTime)}") |
|||
.PageBy(skipCount, maxResultCount) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue