add deep seek

This commit is contained in:
Jicheng Lu 2025-01-27 18:23:33 -06:00
parent 7bae527025
commit a8fa46eff5
15 changed files with 529 additions and 12 deletions

View file

@ -125,6 +125,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Core.Crontab", "sr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Core.Rules", "src\Infrastructure\BotSharp.Core.Rules\BotSharp.Core.Rules.csproj", "{AFD64412-4D6A-452E-82A2-79E5D8842E29}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.DeepSeekAI", "src\Plugins\BotSharp.Plugin.DeepSeekAI\BotSharp.Plugin.DeepSeekAI.csproj", "{AF329442-B48E-4B48-A18A-1C869D1BA6F5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -509,6 +511,14 @@ Global
{AFD64412-4D6A-452E-82A2-79E5D8842E29}.Release|Any CPU.Build.0 = Release|Any CPU
{AFD64412-4D6A-452E-82A2-79E5D8842E29}.Release|x64.ActiveCfg = Release|Any CPU
{AFD64412-4D6A-452E-82A2-79E5D8842E29}.Release|x64.Build.0 = Release|Any CPU
{AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Debug|x64.ActiveCfg = Debug|Any CPU
{AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Debug|x64.Build.0 = Debug|Any CPU
{AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|Any CPU.Build.0 = Release|Any CPU
{AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|x64.ActiveCfg = Release|Any CPU
{AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -569,6 +579,7 @@ Global
{7DA2DCD0-551B-432E-AA5C-22DDD3ED459B} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
{F812BAAE-5A7D-4DF7-8E71-70696B51C61F} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
{AFD64412-4D6A-452E-82A2-79E5D8842E29} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
{AF329442-B48E-4B48-A18A-1C869D1BA6F5} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}

View file

@ -2,7 +2,7 @@ using BotSharp.Abstraction.Statistics.Models;
namespace BotSharp.Abstraction.Statistics.Services;
public interface IBotSharpStatService
public interface IBotSharpStatsService
{
bool UpdateLlmCost(BotSharpStats stats);
bool UpdateAgentCall(BotSharpStats stats);

View file

@ -33,7 +33,7 @@ public class AgentPlugin : IBotSharpPlugin
services.AddScoped<ILlmProviderService, LlmProviderService>();
services.AddScoped<IAgentService, AgentService>();
services.AddScoped<IAgentHook, BasicAgentHook>();
services.AddScoped<IBotSharpStatService, BotSharpStatService>();
services.AddScoped<IBotSharpStatsService, BotSharpStatsService>();
services.AddScoped(provider =>
{

View file

@ -59,7 +59,7 @@ public class TokenStatistics : ITokenStatistics
stat.SetState("llm_total_cost", total_cost, isNeedVersion: false, source: StateSource.Application);
var globalStats = _services.GetRequiredService<IBotSharpStatService>();
var globalStats = _services.GetRequiredService<IBotSharpStatsService>();
var body = new BotSharpStats
{
Category = StatCategory.LlmCost,

View file

@ -3,19 +3,19 @@ using BotSharp.Abstraction.Statistics.Settings;
namespace BotSharp.Core.Statistics.Services;
public class BotSharpStatService : IBotSharpStatService
public class BotSharpStatsService : IBotSharpStatsService
{
private readonly IServiceProvider _services;
private readonly ILogger<BotSharpStatService> _logger;
private readonly ILogger<BotSharpStatsService> _logger;
private readonly StatisticsSettings _settings;
private const string GLOBAL_LLM_COST = "global-llm-cost";
private const string GLOBAL_AGENT_CALL = "global-agent-call";
private const int TIMEOUT_SECONDS = 5;
public BotSharpStatService(
public BotSharpStatsService(
IServiceProvider services,
ILogger<BotSharpStatService> logger,
ILogger<BotSharpStatsService> logger,
StatisticsSettings settings)
{
_services = services;

View file

@ -27,7 +27,7 @@ public class GlobalStatsConversationHook : ConversationHookBase
private void UpdateAgentCall(RoleDialogModel message)
{
// record agent call
var globalStats = _services.GetRequiredService<IBotSharpStatService>();
var globalStats = _services.GetRequiredService<IBotSharpStatsService>();
var body = new BotSharpStats
{

View file

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(TargetFramework)</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
<GenerateDocumentationFile>$(GenerateDocumentationFile)</GenerateDocumentationFile>
<OutputPath>$(SolutionDir)packages</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenAI" Version="2.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Infrastructure\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,18 @@
using BotSharp.Abstraction.Plugins;
using BotSharp.Plugin.DeepSeek.Providers.Text;
using BotSharp.Plugin.DeepSeekAI.Providers.Chat;
namespace BotSharp.Plugin.DeepSeek;
public class DeepSeekAiPlugin : IBotSharpPlugin
{
public string Id => "1f0e73a5-bcaa-44e9-adde-e46cd94d244b";
public string Name => "DeepSeek";
public string Description => "DeepSeek AI";
public string IconUrl => "https://cdn.deepseek.com/logo.png";
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
services.AddScoped<ITextCompletion, TextCompletionProvider>();
services.AddScoped<IChatCompletion, ChatCompletionProvider>();
}
}

View file

@ -0,0 +1,337 @@
using Microsoft.Extensions.Logging;
using OpenAI.Chat;
using BotSharp.Abstraction.Files;
using BotSharp.Plugin.DeepSeek.Providers;
namespace BotSharp.Plugin.DeepSeekAI.Providers.Chat;
public class ChatCompletionProvider : IChatCompletion
{
protected readonly IServiceProvider _services;
protected readonly ILogger<ChatCompletionProvider> _logger;
protected string _model;
public virtual string Provider => "deepseek-ai";
public ChatCompletionProvider(
IServiceProvider services,
ILogger<ChatCompletionProvider> logger)
{
_services = services;
_logger = logger;
}
public async Task<RoleDialogModel> GetChatCompletions(Agent agent, List<RoleDialogModel> conversations)
{
var contentHooks = _services.GetServices<IContentGeneratingHook>().ToList();
// Before chat completion hook
foreach (var hook in contentHooks)
{
await hook.BeforeGenerating(agent, conversations);
}
var client = ProviderHelper.GetClient(Provider, _model, _services);
var chatClient = client.GetChatClient(_model);
var (prompt, messages, options) = PrepareOptions(agent, conversations);
var response = chatClient.CompleteChat(messages, options);
var value = response.Value;
var reason = value.FinishReason;
var content = value.Content;
var text = content.FirstOrDefault()?.Text ?? string.Empty;
RoleDialogModel responseMessage;
if (reason == ChatFinishReason.FunctionCall || reason == ChatFinishReason.ToolCalls)
{
var toolCall = value.ToolCalls.FirstOrDefault();
responseMessage = new RoleDialogModel(AgentRole.Function, text)
{
CurrentAgentId = agent.Id,
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
ToolCallId = toolCall?.Id,
FunctionName = toolCall?.FunctionName,
FunctionArgs = toolCall?.FunctionArguments?.ToString()
};
// Somethings LLM will generate a function name with agent name.
if (!string.IsNullOrEmpty(responseMessage.FunctionName))
{
responseMessage.FunctionName = responseMessage.FunctionName.Split('.').Last();
}
}
else
{
responseMessage = new RoleDialogModel(AgentRole.Assistant, text)
{
CurrentAgentId = agent.Id,
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
};
}
// After chat completion hook
foreach (var hook in contentHooks)
{
await hook.AfterGenerated(responseMessage, new TokenStatsModel
{
Prompt = prompt,
Provider = Provider,
Model = _model,
PromptCount = response.Value?.Usage?.InputTokenCount ?? 0,
CompletionCount = response.Value?.Usage?.OutputTokenCount ?? 0
});
}
return responseMessage;
}
public async Task<bool> GetChatCompletionsAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived, Func<RoleDialogModel, Task> onFunctionExecuting)
{
var hooks = _services.GetServices<IContentGeneratingHook>().ToList();
// Before chat completion hook
foreach (var hook in hooks)
{
await hook.BeforeGenerating(agent, conversations);
}
var client = ProviderHelper.GetClient(Provider, _model, _services);
var chatClient = client.GetChatClient(_model);
var (prompt, messages, options) = PrepareOptions(agent, conversations);
var response = await chatClient.CompleteChatAsync(messages, options);
var value = response.Value;
var reason = value.FinishReason;
var content = value.Content;
var text = content.FirstOrDefault()?.Text ?? string.Empty;
var msg = new RoleDialogModel(AgentRole.Assistant, text)
{
CurrentAgentId = agent.Id
};
// After chat completion hook
foreach (var hook in hooks)
{
await hook.AfterGenerated(msg, new TokenStatsModel
{
Prompt = prompt,
Provider = Provider,
Model = _model,
PromptCount = response.Value?.Usage?.InputTokenCount ?? 0,
CompletionCount = response.Value?.Usage?.OutputTokenCount ?? 0
});
}
if (reason == ChatFinishReason.FunctionCall || reason == ChatFinishReason.ToolCalls)
{
var toolCall = value.ToolCalls?.FirstOrDefault();
_logger.LogInformation($"[{agent.Name}]: {toolCall?.FunctionName}({toolCall?.FunctionArguments})");
var funcContextIn = new RoleDialogModel(AgentRole.Function, text)
{
CurrentAgentId = agent.Id,
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
ToolCallId = toolCall?.Id,
FunctionName = toolCall?.FunctionName,
FunctionArgs = toolCall?.FunctionArguments?.ToString()
};
// Somethings LLM will generate a function name with agent name.
if (!string.IsNullOrEmpty(funcContextIn.FunctionName))
{
funcContextIn.FunctionName = funcContextIn.FunctionName.Split('.').Last();
}
// Execute functions
await onFunctionExecuting(funcContextIn);
}
else
{
// Text response received
await onMessageReceived(msg);
}
return true;
}
public async Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
{
var client = ProviderHelper.GetClient(Provider, _model, _services);
var chatClient = client.GetChatClient(_model);
var (prompt, messages, options) = PrepareOptions(agent, conversations);
var response = chatClient.CompleteChatStreamingAsync(messages, options);
await foreach (var choice in response)
{
if (choice.FinishReason == ChatFinishReason.FunctionCall || choice.FinishReason == ChatFinishReason.ToolCalls)
{
var update = choice.ToolCallUpdates?.FirstOrDefault()?.FunctionArgumentsUpdate?.ToString() ?? string.Empty;
_logger.LogInformation(update);
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, update));
continue;
}
if (choice.ContentUpdate.IsNullOrEmpty()) continue;
_logger.LogInformation(choice.ContentUpdate[0]?.Text);
await onMessageReceived(new RoleDialogModel(choice.Role?.ToString() ?? ChatMessageRole.Assistant.ToString(), choice.ContentUpdate[0]?.Text ?? string.Empty));
}
return true;
}
public void SetModelName(string model)
{
_model = model;
}
protected (string, IEnumerable<ChatMessage>, ChatCompletionOptions) PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
{
var agentService = _services.GetRequiredService<IAgentService>();
var state = _services.GetRequiredService<IConversationStateService>();
var fileStorage = _services.GetRequiredService<IFileStorageService>();
var settingsService = _services.GetRequiredService<ILlmProviderService>();
var settings = settingsService.GetSetting(Provider, _model);
var allowMultiModal = settings != null && settings.MultiModal;
var messages = new List<ChatMessage>();
var temperature = float.Parse(state.GetState("temperature", "0.0"));
var maxTokens = int.Parse(state.GetState("max_tokens", "1024"));
var options = new ChatCompletionOptions()
{
Temperature = temperature,
MaxOutputTokenCount = maxTokens
};
var functions = agent.Functions.Concat(agent.SecondaryFunctions ?? []);
foreach (var function in functions)
{
if (!agentService.RenderFunction(agent, function)) continue;
var property = agentService.RenderFunctionProperty(agent, function);
options.Tools.Add(ChatTool.CreateFunctionTool(
functionName: function.Name,
functionDescription: function.Description,
functionParameters: BinaryData.FromObjectAsJson(property)));
}
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
{
var text = agentService.RenderedInstruction(agent);
messages.Add(new SystemChatMessage(text));
}
if (!string.IsNullOrEmpty(agent.Knowledges))
{
messages.Add(new SystemChatMessage(agent.Knowledges));
}
var filteredMessages = conversations.Select(x => x).ToList();
var firstUserMsgIdx = filteredMessages.FindIndex(x => x.Role == AgentRole.User);
if (firstUserMsgIdx > 0)
{
filteredMessages = filteredMessages.Where((_, idx) => idx >= firstUserMsgIdx).ToList();
}
foreach (var message in filteredMessages)
{
if (message.Role == AgentRole.Function)
{
messages.Add(new AssistantChatMessage(new List<ChatToolCall>
{
ChatToolCall.CreateFunctionToolCall(message.ToolCallId, message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? string.Empty))
}));
messages.Add(new ToolChatMessage(message.ToolCallId, message.Content));
}
else if (message.Role == AgentRole.User)
{
var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content;
var textPart = ChatMessageContentPart.CreateTextPart(text);
var contentParts = new List<ChatMessageContentPart> { textPart };
messages.Add(new UserChatMessage(contentParts));
}
else if (message.Role == AgentRole.Assistant)
{
messages.Add(new AssistantChatMessage(message.Content));
}
}
var prompt = GetPrompt(messages, options);
return (prompt, messages, options);
}
private string GetPrompt(IEnumerable<ChatMessage> messages, ChatCompletionOptions options)
{
var prompt = string.Empty;
if (!messages.IsNullOrEmpty())
{
// System instruction
var verbose = string.Join("\r\n", messages
.Select(x => x as SystemChatMessage)
.Where(x => x != null)
.Select(x =>
{
if (!string.IsNullOrEmpty(x.ParticipantName))
{
// To display Agent name in log
return $"[{x.ParticipantName}]: {x.Content.FirstOrDefault()?.Text ?? string.Empty}";
}
return $"{AgentRole.System}: {x.Content.FirstOrDefault()?.Text ?? string.Empty}";
}));
prompt += $"{verbose}\r\n";
prompt += "\r\n[CONVERSATION]";
verbose = string.Join("\r\n", messages
.Where(x => x as SystemChatMessage == null)
.Select(x =>
{
var fnMessage = x as ToolChatMessage;
if (fnMessage != null)
{
return $"{AgentRole.Function}: {fnMessage.Content.FirstOrDefault()?.Text ?? string.Empty}";
}
var userMessage = x as UserChatMessage;
if (userMessage != null)
{
var content = x.Content.FirstOrDefault()?.Text ?? string.Empty;
return !string.IsNullOrEmpty(userMessage.ParticipantName) && userMessage.ParticipantName != "route_to_agent" ?
$"{userMessage.ParticipantName}: {content}" :
$"{AgentRole.User}: {content}";
}
var assistMessage = x as AssistantChatMessage;
if (assistMessage != null)
{
var toolCall = assistMessage.ToolCalls?.FirstOrDefault();
return toolCall != null ?
$"{AgentRole.Assistant}: Call function {toolCall?.FunctionName}({toolCall?.FunctionArguments})" :
$"{AgentRole.Assistant}: {assistMessage.Content.FirstOrDefault()?.Text ?? string.Empty}";
}
return string.Empty;
}));
prompt += $"\r\n{verbose}\r\n";
}
if (!options.Tools.IsNullOrEmpty())
{
var functions = string.Join("\r\n", options.Tools.Select(fn =>
{
return $"\r\n{fn.FunctionName}: {fn.FunctionDescription}\r\n{fn.FunctionParameters}";
}));
prompt += $"\r\n[FUNCTIONS]{functions}\r\n";
}
return prompt;
}
}

View file

@ -0,0 +1,16 @@
using OpenAI;
using System.ClientModel;
namespace BotSharp.Plugin.DeepSeek.Providers;
public static class ProviderHelper
{
public static OpenAIClient GetClient(string provider, string model, IServiceProvider services)
{
var settingsService = services.GetRequiredService<ILlmProviderService>();
var settings = settingsService.GetSetting(provider, model);
var options = !string.IsNullOrEmpty(settings.Endpoint) ?
new OpenAIClientOptions { Endpoint = new Uri(settings.Endpoint) } : null;
return new OpenAIClient(new ApiKeyCredential(settings.ApiKey), options);
}
}

View file

@ -0,0 +1,95 @@
using Microsoft.Extensions.Logging;
using OpenAI.Chat;
namespace BotSharp.Plugin.DeepSeek.Providers.Text;
public class TextCompletionProvider : ITextCompletion
{
private readonly IServiceProvider _services;
private readonly ILogger<TextCompletionProvider> _logger;
protected string _model;
public string Provider => "deepseek-ai";
public TextCompletionProvider(
IServiceProvider services,
ILogger<TextCompletionProvider> logger)
{
_services = services;
_logger = logger;
}
public async Task<string> GetCompletion(string text, string agentId, string messageId)
{
var contentHooks = _services.GetServices<IContentGeneratingHook>().ToList();
var state = _services.GetRequiredService<IConversationStateService>();
// Before chat completion hook
var agent = new Agent()
{
Id = agentId,
};
var message = new RoleDialogModel(AgentRole.User, text)
{
CurrentAgentId = agentId,
MessageId = messageId
};
foreach (var hook in contentHooks)
{
await hook.BeforeGenerating(agent, new List<RoleDialogModel> { message });
}
var client = ProviderHelper.GetClient(Provider, _model, _services);
var chatClient = client.GetChatClient(_model);
var options = PrepareOptions();
var response = chatClient.CompleteChat([ new UserChatMessage(text) ], options);
// AI response
var content = response.Value?.Content ?? [];
var completion = string.Empty;
foreach (var t in content)
{
completion += t?.Text ?? string.Empty;
};
// After chat completion hook
var responseMessage = new RoleDialogModel(AgentRole.Assistant, completion)
{
CurrentAgentId = agentId,
MessageId = messageId
};
foreach (var hook in contentHooks)
{
await hook.AfterGenerated(responseMessage, new TokenStatsModel
{
Prompt = text,
Provider = Provider,
Model = _model,
PromptCount = response?.Value?.Usage?.InputTokenCount ?? default,
CompletionCount = response?.Value?.Usage?.OutputTokenCount ?? default
});
}
return completion.Trim();
}
public void SetModelName(string model)
{
_model = model;
}
private ChatCompletionOptions PrepareOptions()
{
var state = _services.GetRequiredService<IConversationStateService>();
var temperature = float.Parse(state.GetState("temperature", "0.0"));
var maxTokens = int.Parse(state.GetState("max_tokens", "1024"));
return new ChatCompletionOptions
{
Temperature = temperature,
MaxOutputTokenCount = maxTokens
};
}
}

View file

@ -0,0 +1,19 @@
global using System;
global using System.Collections.Generic;
global using System.Text;
global using System.Threading.Tasks;
global using System.Linq;
global using System.Text.Json;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection;
global using DeepSeek.Core;
global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Agents.Models;
global using BotSharp.Abstraction.MLTasks;
global using BotSharp.Abstraction.Agents;
global using BotSharp.Abstraction.Agents.Enums;
global using BotSharp.Abstraction.Conversations;
global using BotSharp.Abstraction.Loggers;
global using BotSharp.Abstraction.Functions.Models;
global using BotSharp.Abstraction.Utilities;
global using BotSharp.Plugin.DeepSeekAI.Models;

View file

@ -1,7 +1,5 @@
using BotSharp.Abstraction.Files.Utilities;
using BotSharp.Abstraction.Templating;
using OpenAI.Chat;
using static System.Net.Mime.MediaTypeNames;
namespace BotSharp.Plugin.OpenAI.Providers.Chat;
@ -254,10 +252,10 @@ public class ChatCompletionProvider : IChatCompletion
{
messages.Add(new AssistantChatMessage(new List<ChatToolCall>
{
ChatToolCall.CreateFunctionToolCall(message.FunctionName, message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? string.Empty))
ChatToolCall.CreateFunctionToolCall(message.ToolCallId, message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? string.Empty))
}));
messages.Add(new ToolChatMessage(message.FunctionName, message.Content));
messages.Add(new ToolChatMessage(message.ToolCallId, message.Content));
}
else if (message.Role == AgentRole.User)
{

View file

@ -45,6 +45,7 @@
<ProjectReference Include="..\Plugins\BotSharp.Plugin.OpenAI\BotSharp.Plugin.OpenAI.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.AzureOpenAI\BotSharp.Plugin.AzureOpenAI.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.SparkDesk\BotSharp.Plugin.SparkDesk.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.DeepSeekAI\BotSharp.Plugin.DeepSeekAI.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.ChatbotUI\BotSharp.Plugin.ChatbotUI.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.HuggingFace\BotSharp.Plugin.HuggingFace.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.KnowledgeBase\BotSharp.Plugin.KnowledgeBase.csproj" />

View file

@ -348,6 +348,7 @@
"BotSharp.Plugin.AnthropicAI",
"BotSharp.Plugin.GoogleAI",
"BotSharp.Plugin.MetaAI",
"BotSharp.Plugin.DeepSeekAI",
"BotSharp.Plugin.MetaMessenger",
"BotSharp.Plugin.HuggingFace",
"BotSharp.Plugin.KnowledgeBase",