27 changed files with 517 additions and 307 deletions
@ -0,0 +1,110 @@ |
|||
using LINGYUN.Abp.AI.Workspaces; |
|||
using Microsoft.Agents.AI; |
|||
using Microsoft.Extensions.AI; |
|||
using Microsoft.Extensions.Localization; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AI; |
|||
using Volo.Abp.Authorization; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.SimpleStateChecking; |
|||
|
|||
namespace LINGYUN.Abp.AI.Agent; |
|||
public class AgentFactory : IAgentFactory, IScopedDependency |
|||
{ |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
protected IChatClientFactory ChatClientFactory { get; } |
|||
protected IStringLocalizerFactory StringLocalizerFactory { get; } |
|||
protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; } |
|||
protected ISimpleStateCheckerManager<WorkspaceDefinition> StateCheckerManager { get; } |
|||
public AgentFactory( |
|||
IServiceProvider serviceProvider, |
|||
IChatClientFactory chatClientFactory, |
|||
IStringLocalizerFactory stringLocalizerFactory, |
|||
IWorkspaceDefinitionManager workspaceDefinitionManager, |
|||
ISimpleStateCheckerManager<WorkspaceDefinition> stateCheckerManager) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
ChatClientFactory = chatClientFactory; |
|||
StringLocalizerFactory = stringLocalizerFactory; |
|||
WorkspaceDefinitionManager = workspaceDefinitionManager; |
|||
StateCheckerManager = stateCheckerManager; |
|||
} |
|||
|
|||
public async virtual Task<AIAgent> CreateAsync<TWorkspace>() |
|||
{ |
|||
var workspace = WorkspaceNameAttribute.GetWorkspaceName<TWorkspace>(); |
|||
|
|||
var chatClient = await ChatClientFactory.CreateAsync<TWorkspace>(); |
|||
|
|||
var workspaceDefine = await WorkspaceDefinitionManager.GetOrNullAsync(workspace); |
|||
|
|||
if (workspaceDefine != null) |
|||
{ |
|||
await CheckWorkspaceStateAsync(workspaceDefine); |
|||
} |
|||
|
|||
return await CreateAgentAsync(chatClient, workspaceDefine); |
|||
} |
|||
|
|||
public async virtual Task<AIAgent> CreateAsync(string workspace) |
|||
{ |
|||
var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace); |
|||
|
|||
await CheckWorkspaceStateAsync(workspaceDefine); |
|||
|
|||
var chatClient = await ChatClientFactory.CreateAsync(workspace); |
|||
|
|||
return await CreateAgentAsync(chatClient, workspaceDefine); |
|||
} |
|||
|
|||
protected async virtual Task<AIAgent> CreateAgentAsync(IChatClient chatClient, WorkspaceDefinition? workspace) |
|||
{ |
|||
string? description = null; |
|||
if (workspace?.Description != null) |
|||
{ |
|||
description = workspace.Description.Localize(StringLocalizerFactory); |
|||
} |
|||
|
|||
var tools = await GetAgentToolsAsync(workspace); |
|||
|
|||
var clientAgentOptions = new ChatClientAgentOptions |
|||
{ |
|||
ChatOptions = new ChatOptions |
|||
{ |
|||
Instructions = workspace?.Instructions, |
|||
Temperature = workspace?.Temperature, |
|||
MaxOutputTokens = workspace?.MaxOutputTokens, |
|||
PresencePenalty = workspace?.PresencePenalty, |
|||
FrequencyPenalty = workspace?.FrequencyPenalty, |
|||
Tools = tools, |
|||
}, |
|||
Name = workspace?.Name, |
|||
Description = description |
|||
}; |
|||
|
|||
var aiAgent = chatClient.CreateAIAgent(clientAgentOptions) |
|||
.AsBuilder() |
|||
.UseLogging() |
|||
.UseOpenTelemetry() |
|||
.Build(ServiceProvider); |
|||
|
|||
return new WorkspaceAIAgent(aiAgent, workspace); |
|||
} |
|||
|
|||
protected virtual Task<AITool[]> GetAgentToolsAsync(WorkspaceDefinition? workspace) |
|||
{ |
|||
return Task.FromResult<AITool[]>([]); |
|||
} |
|||
|
|||
protected async virtual Task CheckWorkspaceStateAsync(WorkspaceDefinition workspace) |
|||
{ |
|||
if (!await StateCheckerManager.IsEnabledAsync(workspace)) |
|||
{ |
|||
throw new AbpAuthorizationException( |
|||
$"Workspace is not enabled: {workspace.Name}!", |
|||
AbpAIErrorCodes.WorkspaceIsNotEnabled) |
|||
.WithData("Workspace", workspace.Name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
using LINGYUN.Abp.AI.Messages; |
|||
using LINGYUN.Abp.AI.Models; |
|||
using LINGYUN.Abp.AI.Tokens; |
|||
using Microsoft.Extensions.AI; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.AI.Agent; |
|||
public class AgentService : IAgentService, IScopedDependency |
|||
{ |
|||
private readonly IAgentFactory _agentFactory; |
|||
private readonly ITokenUsageStore _tokenUsageStore; |
|||
private readonly IUserMessageStore _userMessageStore; |
|||
public AgentService( |
|||
IAgentFactory agentFactory, |
|||
ITokenUsageStore tokenUsageStore, |
|||
IUserMessageStore userMessageStore) |
|||
{ |
|||
_agentFactory = agentFactory; |
|||
_tokenUsageStore = tokenUsageStore; |
|||
_userMessageStore = userMessageStore; |
|||
} |
|||
|
|||
public async virtual IAsyncEnumerable<string> SendMessageAsync(UserMessage message) |
|||
{ |
|||
var messages = await BuildChatMessages(message); |
|||
|
|||
var agent = await _agentFactory.CreateAsync(message.Workspace); |
|||
|
|||
var agentRunRes = agent.RunStreamingAsync(messages); |
|||
|
|||
var agentMessageBuilder = new StringBuilder(); |
|||
|
|||
await foreach (var item in agentRunRes) |
|||
{ |
|||
agentMessageBuilder.Append(item); |
|||
yield return item.Text; |
|||
|
|||
await StoreTokenUsageInfo(message, item.RawRepresentation); |
|||
} |
|||
|
|||
await StoreChatMessage(message, agentMessageBuilder.ToString()); |
|||
} |
|||
|
|||
protected virtual async Task<IEnumerable<ChatMessage>> BuildChatMessages(UserMessage message) |
|||
{ |
|||
var messages = new List<ChatMessage>(); |
|||
|
|||
var historyMessages = await _userMessageStore.GetHistoryMessagesAsync(message.ChatId); |
|||
|
|||
foreach (var chatMessage in historyMessages) |
|||
{ |
|||
messages.Add(new ChatMessage(ChatRole.System, chatMessage.Content)); |
|||
} |
|||
|
|||
messages.Add(new ChatMessage(ChatRole.User, message.Content)); |
|||
|
|||
return messages; |
|||
} |
|||
|
|||
protected async virtual Task StoreChatMessage(UserMessage message, string agentMessage) |
|||
{ |
|||
message.WithReply(agentMessage); |
|||
|
|||
await _userMessageStore.SaveMessageAsync(message); |
|||
} |
|||
|
|||
protected async virtual Task StoreTokenUsageInfo(UserMessage message, object? rawRepresentation) |
|||
{ |
|||
if (rawRepresentation is ChatResponseUpdate update) |
|||
{ |
|||
var tokenUsageInfos = update.Contents |
|||
.OfType<UsageContent>() |
|||
.Where(usage => usage.Details != null) |
|||
.Select(usage => new TokenUsageInfo(message.Workspace) |
|||
{ |
|||
TotalTokenCount = usage.Details.TotalTokenCount, |
|||
CachedInputTokenCount = usage.Details.CachedInputTokenCount, |
|||
InputTokenCount = usage.Details.InputTokenCount, |
|||
AdditionalCounts = usage.Details.AdditionalCounts, |
|||
OutputTokenCount = usage.Details.OutputTokenCount, |
|||
ReasoningTokenCount = usage.Details.ReasoningTokenCount, |
|||
}); |
|||
|
|||
await _tokenUsageStore.SaveTokenUsagesAsync(tokenUsageInfos); |
|||
} |
|||
} |
|||
} |
|||
@ -1,140 +0,0 @@ |
|||
using LINGYUN.Abp.AI.Workspaces; |
|||
using Microsoft.Agents.AI; |
|||
using Microsoft.Extensions.AI; |
|||
using Microsoft.Extensions.Localization; |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AI; |
|||
using Volo.Abp.Authorization; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.SimpleStateChecking; |
|||
|
|||
namespace LINGYUN.Abp.AI.Agent; |
|||
public class ChatClientAgentFactory : IChatClientAgentFactory, ISingletonDependency |
|||
{ |
|||
private readonly static ConcurrentDictionary<string, WorkspaceAIAgent> _chatClientAgentCache = new(); |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
protected IChatClientFactory ChatClientFactory { get; } |
|||
protected IStringLocalizerFactory StringLocalizerFactory { get; } |
|||
protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; } |
|||
protected ISimpleStateCheckerManager<WorkspaceDefinition> StateCheckerManager { get; } |
|||
public ChatClientAgentFactory( |
|||
IServiceProvider serviceProvider, |
|||
IChatClientFactory chatClientFactory, |
|||
IStringLocalizerFactory stringLocalizerFactory, |
|||
IWorkspaceDefinitionManager workspaceDefinitionManager, |
|||
ISimpleStateCheckerManager<WorkspaceDefinition> stateCheckerManager) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
ChatClientFactory = chatClientFactory; |
|||
StringLocalizerFactory = stringLocalizerFactory; |
|||
WorkspaceDefinitionManager = workspaceDefinitionManager; |
|||
StateCheckerManager = stateCheckerManager; |
|||
} |
|||
|
|||
public async virtual Task<WorkspaceAIAgent> CreateAsync<TWorkspace>() |
|||
{ |
|||
var workspace = WorkspaceNameAttribute.GetWorkspaceName<TWorkspace>(); |
|||
if (_chatClientAgentCache.TryGetValue(workspace, out var chatClientAgent)) |
|||
{ |
|||
return chatClientAgent; |
|||
} |
|||
|
|||
var chatClient = await ChatClientFactory.CreateAsync<TWorkspace>(); |
|||
|
|||
var workspaceDefine = await WorkspaceDefinitionManager.GetOrNullAsync(workspace); |
|||
|
|||
if (workspaceDefine != null) |
|||
{ |
|||
await CheckWorkspaceStateAsync(workspaceDefine); |
|||
} |
|||
|
|||
string? description = null; |
|||
if (workspaceDefine?.Description != null) |
|||
{ |
|||
description = workspaceDefine.Description.Localize(StringLocalizerFactory); |
|||
} |
|||
|
|||
var clientAgentOptions = new ChatClientAgentOptions |
|||
{ |
|||
ChatOptions = new ChatOptions |
|||
{ |
|||
Instructions = workspaceDefine?.Instructions, |
|||
Temperature = workspaceDefine?.Temperature, |
|||
MaxOutputTokens = workspaceDefine?.MaxOutputTokens, |
|||
PresencePenalty = workspaceDefine?.PresencePenalty, |
|||
FrequencyPenalty = workspaceDefine?.FrequencyPenalty, |
|||
}, |
|||
Name = workspaceDefine?.Name, |
|||
Description = description |
|||
}; |
|||
|
|||
chatClientAgent = new WorkspaceAIAgent( |
|||
new AIAgentBuilder(chatClient.CreateAIAgent(clientAgentOptions)) |
|||
.UseLogging() |
|||
.UseOpenTelemetry() |
|||
.Build(ServiceProvider), |
|||
workspaceDefine); |
|||
|
|||
_chatClientAgentCache.TryAdd(workspace, chatClientAgent); |
|||
|
|||
return chatClientAgent; |
|||
} |
|||
|
|||
public async virtual Task<WorkspaceAIAgent> CreateAsync(string workspace) |
|||
{ |
|||
if (_chatClientAgentCache.TryGetValue(workspace, out var chatClientAgent)) |
|||
{ |
|||
return chatClientAgent; |
|||
} |
|||
var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace); |
|||
|
|||
await CheckWorkspaceStateAsync(workspaceDefine); |
|||
|
|||
var chatClient = await ChatClientFactory.CreateAsync(workspace); |
|||
|
|||
string? description = null; |
|||
if (workspaceDefine.Description != null) |
|||
{ |
|||
description = workspaceDefine.Description.Localize(StringLocalizerFactory); |
|||
} |
|||
|
|||
var clientAgentOptions = new ChatClientAgentOptions |
|||
{ |
|||
ChatOptions = new ChatOptions |
|||
{ |
|||
Instructions = workspaceDefine.Instructions, |
|||
Temperature = workspaceDefine.Temperature, |
|||
MaxOutputTokens = workspaceDefine.MaxOutputTokens, |
|||
PresencePenalty = workspaceDefine.PresencePenalty, |
|||
FrequencyPenalty = workspaceDefine.FrequencyPenalty, |
|||
}, |
|||
Name = workspaceDefine.Name, |
|||
Description = description |
|||
}; |
|||
|
|||
chatClientAgent = new WorkspaceAIAgent( |
|||
new AIAgentBuilder(chatClient.CreateAIAgent(clientAgentOptions)) |
|||
.UseLogging() |
|||
.UseOpenTelemetry() |
|||
.Build(ServiceProvider), |
|||
workspaceDefine); |
|||
|
|||
_chatClientAgentCache.TryAdd(workspace, chatClientAgent); |
|||
|
|||
return chatClientAgent; |
|||
} |
|||
|
|||
protected async virtual Task CheckWorkspaceStateAsync(WorkspaceDefinition workspace) |
|||
{ |
|||
if (!await StateCheckerManager.IsEnabledAsync(workspace)) |
|||
{ |
|||
throw new AbpAuthorizationException( |
|||
$"Workspace is not enabled: {workspace.Name}!", |
|||
AbpAIErrorCodes.WorkspaceIsNotEnabled) |
|||
.WithData("Workspace", workspace.Name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Agents.AI; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.AI.Agent; |
|||
public interface IAgentFactory |
|||
{ |
|||
[NotNull] |
|||
Task<AIAgent> CreateAsync<TWorkspace>(); |
|||
|
|||
[NotNull] |
|||
Task<AIAgent> CreateAsync(string workspace); |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using LINGYUN.Abp.AI.Models; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.AI.Agent; |
|||
public interface IAgentService |
|||
{ |
|||
IAsyncEnumerable<string> SendMessageAsync(UserMessage message); |
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
using JetBrains.Annotations; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.AI.Agent; |
|||
public interface IChatClientAgentFactory |
|||
{ |
|||
[NotNull] |
|||
Task<WorkspaceAIAgent> CreateAsync<TWorkspace>(); |
|||
|
|||
[NotNull] |
|||
Task<WorkspaceAIAgent> CreateAsync(string workspace); |
|||
} |
|||
@ -1,12 +1,13 @@ |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.AI; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.AI; |
|||
public interface IChatClientFactory |
|||
{ |
|||
[NotNull] |
|||
Task<IWorkspaceChatClient> CreateAsync<TWorkspace>(); |
|||
Task<IChatClient> CreateAsync<TWorkspace>(); |
|||
|
|||
[NotNull] |
|||
Task<IWorkspaceChatClient> CreateAsync(string workspace); |
|||
Task<IChatClient> CreateAsync(string workspace); |
|||
} |
|||
|
|||
@ -1,8 +0,0 @@ |
|||
using LINGYUN.Abp.AI.Workspaces; |
|||
using Microsoft.Extensions.AI; |
|||
|
|||
namespace LINGYUN.Abp.AI; |
|||
public interface IWorkspaceChatClient : IChatClient |
|||
{ |
|||
WorkspaceDefinition? Workspace { get; } |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using LINGYUN.Abp.AI.Workspaces; |
|||
using Microsoft.SemanticKernel; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.AI; |
|||
public abstract class KernelProvider : IKernelProvider, ITransientDependency |
|||
{ |
|||
public abstract string Name { get; } |
|||
|
|||
protected IServiceProvider ServiceProvider { get; } |
|||
protected KernelProvider(IServiceProvider serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
} |
|||
|
|||
public abstract Task<Kernel> CreateAsync(WorkspaceDefinition workspace); |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using LINGYUN.Abp.AI.Models; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.AI.Messages; |
|||
public interface IUserMessageStore |
|||
{ |
|||
Task<string> SaveMessageAsync(UserMessage message); |
|||
|
|||
Task<IEnumerable<UserMessage>> GetHistoryMessagesAsync(string chatId); |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using LINGYUN.Abp.AI.Models; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.AI.Messages; |
|||
|
|||
[Dependency(ServiceLifetime.Singleton, TryRegister = true)] |
|||
public class InMemoryUserMessageStore : IUserMessageStore |
|||
{ |
|||
private static readonly ConcurrentDictionary<string, List<UserMessage>> _userMessageCache = new ConcurrentDictionary<string, List<UserMessage>>(); |
|||
|
|||
public Task<IEnumerable<UserMessage>> GetHistoryMessagesAsync(string chatId) |
|||
{ |
|||
if (_userMessageCache.TryGetValue(chatId, out var messages)) |
|||
{ |
|||
return Task.FromResult(messages.Take(5)); |
|||
} |
|||
|
|||
return Task.FromResult<IEnumerable<UserMessage>>(Array.Empty<UserMessage>()); |
|||
} |
|||
|
|||
public Task<string> SaveMessageAsync(UserMessage message) |
|||
{ |
|||
if (_userMessageCache.ContainsKey(message.ChatId)) |
|||
{ |
|||
_userMessageCache[message.ChatId].Add(message); |
|||
} |
|||
else |
|||
{ |
|||
_userMessageCache[message.ChatId] = new List<UserMessage>() { message }; |
|||
} |
|||
|
|||
return Task.FromResult(message.Id); |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace LINGYUN.Abp.AI.Models; |
|||
internal class ChatMessageInfo |
|||
{ |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using Volo.Abp.Content; |
|||
|
|||
namespace LINGYUN.Abp.AI.Models; |
|||
public class MediaMessage |
|||
{ |
|||
public string Id { get; } |
|||
public IRemoteStreamContent Content { get; } |
|||
public MediaMessage(string id, IRemoteStreamContent content) |
|||
{ |
|||
Id = id; |
|||
Content = content; |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.AI.Models; |
|||
public class TokenUsageInfo |
|||
{ |
|||
public string Workspace { get; } |
|||
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; } |
|||
public IDictionary<string, long>? AdditionalCounts { get; set; } |
|||
public TokenUsageInfo(string workspace) |
|||
{ |
|||
Workspace = workspace; |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using System.Linq; |
|||
|
|||
namespace LINGYUN.Abp.AI.Models; |
|||
public class UserMessage |
|||
{ |
|||
public string Id { get; } |
|||
public string ChatId { get; } |
|||
public string Content { get; } |
|||
public string Workspace { get; } |
|||
public string ReplyMessage { get; private set; } |
|||
public MediaMessage[]? Medias { get; private set; } |
|||
public UserMessage( |
|||
string workspace, |
|||
string id, |
|||
string chatId, |
|||
string content) |
|||
{ |
|||
Workspace = workspace; |
|||
Id = id; |
|||
ChatId = chatId; |
|||
Content = content; |
|||
} |
|||
|
|||
public UserMessage WithMedia(MediaMessage media) |
|||
{ |
|||
Medias ??= []; |
|||
Medias = Medias.Union([media]).ToArray(); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public UserMessage WithReply(string replyMessage) |
|||
{ |
|||
ReplyMessage = replyMessage; |
|||
return this; |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using LINGYUN.Abp.AI.Workspaces; |
|||
using Microsoft.SemanticKernel; |
|||
using OpenAI; |
|||
using System; |
|||
using System.ClientModel; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Abp.AI; |
|||
public class OpenAIKernelProvider : KernelProvider |
|||
{ |
|||
private const string DefaultEndpoint = "https://api.openai.com/v1"; |
|||
|
|||
public const string ProviderName = "OpenAI"; |
|||
public override string Name => ProviderName; |
|||
public OpenAIKernelProvider(IServiceProvider serviceProvider) |
|||
: base(serviceProvider) |
|||
{ |
|||
} |
|||
|
|||
public override Task<Kernel> CreateAsync(WorkspaceDefinition workspace) |
|||
{ |
|||
Check.NotNull(workspace, nameof(workspace)); |
|||
Check.NotNullOrWhiteSpace(workspace.ApiKey, nameof(WorkspaceDefinition.ApiKey)); |
|||
|
|||
var openAIClient = new OpenAIClient( |
|||
new ApiKeyCredential(workspace.ApiKey), |
|||
new OpenAIClientOptions |
|||
{ |
|||
Endpoint = new Uri(workspace.ApiBaseUrl ?? DefaultEndpoint), |
|||
}); |
|||
|
|||
var kernel = Kernel.CreateBuilder() |
|||
.AddOpenAIChatClient(workspace.ModelName, openAIClient) |
|||
.Build(); |
|||
|
|||
return Task.FromResult(kernel); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using LINGYUN.Abp.AI.Models; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tokens; |
|||
public interface ITokenUsageStore |
|||
{ |
|||
Task SaveTokenUsagesAsync(IEnumerable<TokenUsageInfo> usageInfos); |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
using LINGYUN.Abp.AI.Models; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tokens; |
|||
|
|||
[Dependency(ServiceLifetime.Singleton, TryRegister = true)] |
|||
public class InMemoryTokenUsageStore : ITokenUsageStore |
|||
{ |
|||
private static readonly ConcurrentDictionary<string, TokenUsageInfo> _tokenUsageCache = new ConcurrentDictionary<string, TokenUsageInfo>(); |
|||
|
|||
public Task SaveTokenUsagesAsync(IEnumerable<TokenUsageInfo> usageInfos) |
|||
{ |
|||
foreach (var usageInfo in usageInfos.GroupBy(x => x.Workspace)) |
|||
{ |
|||
var tokenUsageInfo = new TokenUsageInfo(usageInfo.Key) |
|||
{ |
|||
InputTokenCount = usageInfo.Sum(x => x.InputTokenCount), |
|||
TotalTokenCount = usageInfo.Sum(x => x.TotalTokenCount), |
|||
OutputTokenCount = usageInfo.Sum(x => x.OutputTokenCount), |
|||
ReasoningTokenCount = usageInfo.Sum(x => x.ReasoningTokenCount), |
|||
CachedInputTokenCount = usageInfo.Sum(x => x.CachedInputTokenCount), |
|||
}; |
|||
tokenUsageInfo.AdditionalCounts ??= new Dictionary<string, long>(); |
|||
foreach (var item in usageInfo) |
|||
{ |
|||
if (item.AdditionalCounts == null) |
|||
{ |
|||
continue; |
|||
} |
|||
tokenUsageInfo.AdditionalCounts.AddIfNotContains(item.AdditionalCounts); |
|||
} |
|||
|
|||
if (!_tokenUsageCache.ContainsKey(usageInfo.Key)) |
|||
{ |
|||
_tokenUsageCache.TryAdd(usageInfo.Key, tokenUsageInfo); |
|||
} |
|||
else |
|||
{ |
|||
_tokenUsageCache[usageInfo.Key] = tokenUsageInfo; |
|||
} |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using Microsoft.Extensions.AI; |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public static class GlobalFunctions |
|||
{ |
|||
public static AITool Now => AIFunctionFactory.Create( |
|||
() => DateTime.Now, |
|||
nameof(Now), |
|||
"Get now time"); |
|||
} |
|||
@ -1,69 +0,0 @@ |
|||
using LINGYUN.Abp.AI.Tools; |
|||
using LINGYUN.Abp.AI.Workspaces; |
|||
using Microsoft.Extensions.AI; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.AI; |
|||
public class WorkspaceChatClient : IWorkspaceChatClient |
|||
{ |
|||
public IChatClient Client { get; } |
|||
public WorkspaceDefinition? Workspace { get; } |
|||
public WorkspaceChatClient( |
|||
IChatClient chatClient, |
|||
WorkspaceDefinition? workspace = null) |
|||
{ |
|||
Workspace = workspace; |
|||
Client = chatClient; |
|||
} |
|||
|
|||
public virtual Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default) |
|||
{ |
|||
return Client.GetResponseAsync(GetChatMessages(messages), GetChatOptions(options), cancellationToken); |
|||
} |
|||
|
|||
public virtual IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default) |
|||
{ |
|||
return Client.GetStreamingResponseAsync(GetChatMessages(messages), GetChatOptions(options), cancellationToken); |
|||
} |
|||
|
|||
public virtual object? GetService(Type serviceType, object? serviceKey = null) |
|||
{ |
|||
return Client.GetService(serviceType, serviceKey); |
|||
} |
|||
|
|||
public virtual void Dispose() |
|||
{ |
|||
Client.Dispose(); |
|||
} |
|||
|
|||
protected virtual ChatOptions GetChatOptions(ChatOptions? options) |
|||
{ |
|||
options ??= new ChatOptions(); |
|||
options.Instructions = Workspace?.Instructions; |
|||
options.Temperature ??= Workspace?.Temperature; |
|||
options.MaxOutputTokens ??= Workspace?.MaxOutputTokens; |
|||
options.FrequencyPenalty ??= Workspace?.FrequencyPenalty; |
|||
options.PresencePenalty ??= Workspace?.PresencePenalty; |
|||
|
|||
return options; |
|||
} |
|||
|
|||
protected virtual IEnumerable<ChatMessage> GetChatMessages(IEnumerable<ChatMessage> messages) |
|||
{ |
|||
if (Workspace?.SystemPrompt?.IsNullOrWhiteSpace() == false && |
|||
!messages.Any(msg => msg.Role == ChatRole.System)) |
|||
{ |
|||
// 加入系统提示词
|
|||
messages = new List<ChatMessage> |
|||
{ |
|||
new ChatMessage(ChatRole.System, Workspace.SystemPrompt) |
|||
}.Union(messages); |
|||
} |
|||
|
|||
return messages; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue