Merge pull request #85 from hchen2020/master
Fix IConversationCompletionHook
This commit is contained in:
commit
1ecb441ae8
|
|
@ -8,6 +8,12 @@
|
|||
<PackageIcon>Icon.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Infrastructures\**" />
|
||||
<EmbeddedResource Remove="Infrastructures\**" />
|
||||
<None Remove="Infrastructures\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\..\arts\Icon.png">
|
||||
<Pack>True</Pack>
|
||||
|
|
@ -22,8 +28,4 @@
|
|||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Infrastructures\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using BotSharp.Abstraction.MLTasks;
|
||||
|
||||
namespace BotSharp.Abstraction.Conversations;
|
||||
|
||||
public abstract class ConversationCompletionHookBase : IConversationCompletionHook
|
||||
{
|
||||
protected Agent _agent;
|
||||
public Agent Agent => _agent;
|
||||
|
||||
protected Conversation _conversation;
|
||||
public Conversation Conversation => _conversation;
|
||||
|
||||
protected List<RoleDialogModel> _dialogs;
|
||||
public List<RoleDialogModel> Dialogs => _dialogs;
|
||||
|
||||
protected IChatCompletion _chatCompletion;
|
||||
public IChatCompletion ChatCompletion => _chatCompletion;
|
||||
|
||||
public IConversationCompletionHook SetAgent(Agent agent)
|
||||
{
|
||||
_agent = agent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IConversationCompletionHook SetConversation(Conversation conversation)
|
||||
{
|
||||
_conversation = conversation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IConversationCompletionHook SetDialogs(List<RoleDialogModel> dialogs)
|
||||
{
|
||||
_dialogs = dialogs;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IConversationCompletionHook SetChatCompletion(IChatCompletion chatCompletion)
|
||||
{
|
||||
_chatCompletion = chatCompletion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual Task BeforeCompletion()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task<string> AfterCompletion(string response)
|
||||
{
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,22 @@
|
|||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using BotSharp.Abstraction.MLTasks;
|
||||
|
||||
namespace BotSharp.Abstraction.Conversations;
|
||||
|
||||
public interface IConversationCompletionHook
|
||||
{
|
||||
Task BeforeCompletion(Agent agent, List<RoleDialogModel> conversations);
|
||||
Task<string> AfterCompletion(Agent agent, string response);
|
||||
Agent Agent { get; }
|
||||
IConversationCompletionHook SetAgent(Agent agent);
|
||||
|
||||
Conversation Conversation { get; }
|
||||
IConversationCompletionHook SetConversation(Conversation conversation);
|
||||
|
||||
List<RoleDialogModel> Dialogs { get; }
|
||||
IConversationCompletionHook SetDialogs(List<RoleDialogModel> dialogs);
|
||||
|
||||
IChatCompletion ChatCompletion { get; }
|
||||
IConversationCompletionHook SetChatCompletion(IChatCompletion chatCompletion);
|
||||
|
||||
Task BeforeCompletion();
|
||||
Task<string> AfterCompletion(string response);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace BotSharp.Abstraction.Conversations;
|
|||
public interface IConversationService
|
||||
{
|
||||
Task<Conversation> NewConversation(Conversation conversation);
|
||||
Task<Conversation> GetConversation(string id);
|
||||
Task<List<Conversation>> GetConversations();
|
||||
Task DeleteConversation(string id);
|
||||
Task<string> SendMessage(string agentId, string conversationId, RoleDialogModel lastDalog);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,12 @@ public class RoleDialogModel
|
|||
public string Role { get; set; }
|
||||
public string Text { get; set; }
|
||||
|
||||
public RoleDialogModel(string role, string text)
|
||||
{
|
||||
Role = role;
|
||||
Text = text;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Role}: {Text}";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
namespace BotSharp.Abstraction.Utilities;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static string IfNullOrEmptyAs(this string str, string defaultValue)
|
||||
=> string.IsNullOrEmpty(str) ? defaultValue : str;
|
||||
}
|
||||
|
|
@ -34,14 +34,25 @@
|
|||
<PackageIconUrl>https://raw.githubusercontent.com/SciSharp/BotSharp/master/docs/static/logos/BotSharp.png</PackageIconUrl>
|
||||
<PackageLicenseUrl>https://raw.githubusercontent.com/SciSharp/BotSharp/master/LICENSE</PackageLicenseUrl>
|
||||
<PackageIcon>Icon.png</PackageIcon>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
<NoWarn>1701;1702</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DefineConstants>TRACE;</DefineConstants>
|
||||
<NoWarn>1701;1702</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<NoWarn>1701;1702</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NoWarn>1701;1702</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ public class ConversationController : ControllerBase, IApiAdapter
|
|||
var service = _services.GetRequiredService<IConversationService>();
|
||||
var sess = new Conversation
|
||||
{
|
||||
UserId = _user.Id,
|
||||
AgentId = agentId
|
||||
};
|
||||
sess = await service.NewConversation(sess);
|
||||
|
|
@ -45,15 +46,11 @@ public class ConversationController : ControllerBase, IApiAdapter
|
|||
{
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
|
||||
var result = await conv.SendMessage(agentId, conversationId, new RoleDialogModel
|
||||
{
|
||||
Role = "user",
|
||||
Text = input.Text
|
||||
});
|
||||
var result = await conv.SendMessage(agentId, conversationId, new RoleDialogModel("user", input.Text));
|
||||
|
||||
return new MessageResponseModel
|
||||
{
|
||||
Content = result
|
||||
Text = result
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,16 @@ public class ConversationService : IConversationService
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<Conversation> GetConversation(string id)
|
||||
{
|
||||
var db = _services.GetRequiredService<AgentDbContext>();
|
||||
var query = from sess in db.Conversation
|
||||
where sess.Id == id
|
||||
orderby sess.CreatedTime descending
|
||||
select sess.ToConversation();
|
||||
return query.FirstOrDefault();
|
||||
}
|
||||
|
||||
public async Task<List<Conversation>> GetConversations()
|
||||
{
|
||||
var db = _services.GetRequiredService<AgentDbContext>();
|
||||
|
|
@ -43,8 +53,8 @@ public class ConversationService : IConversationService
|
|||
var db = _services.GetRequiredService<AgentDbContext>();
|
||||
|
||||
var record = ConversationRecord.FromConversation(sess);
|
||||
record.Id = Guid.NewGuid().ToString();
|
||||
record.UserId = _user.Id;
|
||||
record.Id = sess.Id.IfNullOrEmptyAs(Guid.NewGuid().ToString());
|
||||
record.UserId = sess.UserId.IfNullOrEmptyAs(_user.Id);
|
||||
record.Title = "New Conversation";
|
||||
|
||||
db.Transaction<IAgentTable>(delegate
|
||||
|
|
@ -65,11 +75,7 @@ public class ConversationService : IConversationService
|
|||
|
||||
var response = await SendMessage(agentId, conversationId, wholeDialogs);
|
||||
|
||||
_storage.Append(agentId, conversationId, new RoleDialogModel
|
||||
{
|
||||
Role = "assistant",
|
||||
Text = response
|
||||
});
|
||||
_storage.Append(agentId, conversationId, new RoleDialogModel("assistant", response));
|
||||
|
||||
return response;
|
||||
}
|
||||
|
|
@ -77,6 +83,7 @@ public class ConversationService : IConversationService
|
|||
public async Task<string> SendMessage(string agentId, string conversationId, List<RoleDialogModel> wholeDialogs)
|
||||
{
|
||||
var agent = await _services.GetRequiredService<IAgentService>().GetAgent(agentId);
|
||||
var converation = await GetConversation(conversationId);
|
||||
|
||||
// Get relevant domain knowledge
|
||||
if (_settings.EnableKnowledgeBase)
|
||||
|
|
@ -94,14 +101,21 @@ public class ConversationService : IConversationService
|
|||
// Before chat completion hook
|
||||
var hooks = _services.GetServices<IConversationCompletionHook>().ToList();
|
||||
|
||||
hooks.ForEach(hook => hook.BeforeCompletion(agent, wholeDialogs));
|
||||
hooks.ForEach(hook =>
|
||||
{
|
||||
hook.SetAgent(agent)
|
||||
.SetConversation(converation)
|
||||
.SetDialogs(wholeDialogs)
|
||||
.SetChatCompletion(chatCompletion)
|
||||
.BeforeCompletion();
|
||||
});
|
||||
|
||||
var response = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs);
|
||||
|
||||
// After chat completion hook
|
||||
hooks.ForEach(async hook =>
|
||||
{
|
||||
response = await hook.AfterCompletion(agent, response);
|
||||
response = await hook.AfterCompletion(response);
|
||||
});
|
||||
|
||||
return response;
|
||||
|
|
|
|||
|
|
@ -26,11 +26,7 @@ public class ConversationStorage : IConversationStorage
|
|||
var pos = x.IndexOf(':');
|
||||
var role = x.Substring(0, pos);
|
||||
var text = x.Substring(pos + 1);
|
||||
return new RoleDialogModel
|
||||
{
|
||||
Role = role,
|
||||
Text = text
|
||||
};
|
||||
return new RoleDialogModel(role, text);
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,5 +2,5 @@ namespace BotSharp.Core.Conversations.ViewModels;
|
|||
|
||||
public class MessageResponseModel
|
||||
{
|
||||
public string Content { get; set; }
|
||||
public string Text { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@ public static class DataContextHelper
|
|||
dc.BindDbContext<IAgentTable, DbContext4SqlServer2>(new DatabaseBind
|
||||
{
|
||||
ServiceProvider = serviceProvider,
|
||||
MasterConnection = new SqlConnection(settings.Agent.Master),
|
||||
SlaveConnections = settings.Agent.Slavers.Length == 0 ?
|
||||
new List<DbConnection> { new SqlConnection(settings.Agent.Master) } :
|
||||
settings.Agent.Slavers.Select(x => new SqlConnection(x) as DbConnection).ToList(),
|
||||
MasterConnection = new SqlConnection(settings.BotSharp.Master),
|
||||
SlaveConnections = settings.BotSharp.Slavers.Length == 0 ?
|
||||
new List<DbConnection> { new SqlConnection(settings.BotSharp.Master) } :
|
||||
settings.BotSharp.Slavers.Select(x => new SqlConnection(x) as DbConnection).ToList(),
|
||||
CreateDbIfNotExist = true
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
using EntityFrameworkCore.BootKit;
|
||||
|
||||
namespace BotSharp.Core.Repository;
|
||||
|
||||
public class MyDatabaseSettings : DatabaseSettings
|
||||
{
|
||||
public string[] Assemblies { get; set; }
|
||||
public DbConnectionSetting MongoDb { get; set; }
|
||||
public DbConnectionSetting Agent { get; set; }
|
||||
public DbConnectionSetting BotSharp { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ global using BotSharp.Abstraction.Agents;
|
|||
global using BotSharp.Abstraction.Conversations;
|
||||
global using BotSharp.Abstraction.Knowledges;
|
||||
global using BotSharp.Abstraction.Users;
|
||||
global using BotSharp.Abstraction.Utilities;
|
||||
global using BotSharp.Core.Repository;
|
||||
global using BotSharp.Core.Repository.Abstraction;
|
||||
global using BotSharp.Core.Repository.DbTables;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public class AzureOpenAiPlugin : IBotSharpPlugin
|
|||
config.Bind("AzureOpenAi", settings);
|
||||
services.AddSingleton(x => settings);
|
||||
|
||||
services.AddSingleton<ITextCompletion, TextCompletionProvider>();
|
||||
services.AddScoped<ITextCompletion, TextCompletionProvider>();
|
||||
services.AddScoped<IChatCompletion, ChatCompletionProvider>();
|
||||
}
|
||||
}
|
||||
|
|
@ -48,22 +48,31 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
public List<RoleDialogModel> GetChatSamples(string sampleText)
|
||||
{
|
||||
var samples = new List<RoleDialogModel>();
|
||||
if (!string.IsNullOrEmpty(sampleText))
|
||||
if (string.IsNullOrEmpty(sampleText))
|
||||
{
|
||||
var lines = sampleText.Split('\n');
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i];
|
||||
var role = line.Substring(0, line.IndexOf(' ') - 1);
|
||||
var content = line.Substring(line.IndexOf(' ') + 1);
|
||||
|
||||
samples.Add(new RoleDialogModel
|
||||
{
|
||||
Role = role,
|
||||
Text = content
|
||||
});
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
var lines = sampleText.Split('\n');
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i];
|
||||
if (string.IsNullOrEmpty(line.Trim()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var role = line.Substring(0, line.IndexOf(' ') - 1).Trim();
|
||||
var content = line.Substring(line.IndexOf(' ') + 1).Trim();
|
||||
|
||||
// comments
|
||||
if (role == "##")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
samples.Add(new RoleDialogModel(role, content));
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
|
@ -104,8 +113,9 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.System, agent.Knowledges));
|
||||
}
|
||||
|
||||
foreach (var message in GetChatSamples(agent.Samples))
|
||||
|
||||
var samples = GetChatSamples(agent.Samples);
|
||||
foreach (var message in samples)
|
||||
{
|
||||
chatCompletionsOptions.Messages.Add(new ChatMessage(message.Role, message.Text));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,11 @@ using BotSharp.Plugin.ChatbotUI.ViewModels;
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using BotSharp.Abstraction.Conversations;
|
||||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace BotSharp.Plugin.ChatbotUI.Controllers;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
public class ChatbotUiController : ControllerBase, IApiAdapter
|
||||
{
|
||||
|
|
@ -59,15 +61,25 @@ public class ChatbotUiController : ControllerBase, IApiAdapter
|
|||
Response.Headers.Add(HeaderNames.Connection, "keep-alive");
|
||||
var outputStream = Response.Body;
|
||||
|
||||
var conversations = input.Messages.Skip(1).Select(x => new RoleDialogModel
|
||||
var conversations = input.Messages
|
||||
.Select(x => new RoleDialogModel(x.Role, x.Content))
|
||||
.ToList();
|
||||
|
||||
var conversationService = _services.GetRequiredService<IConversationService>();
|
||||
|
||||
// Check if this conversation exists
|
||||
var converation = await conversationService.GetConversation(input.ConversationId);
|
||||
if(converation == null)
|
||||
{
|
||||
Role = x.Role,
|
||||
Text = x.Content
|
||||
}).ToList();
|
||||
var sess = new Conversation
|
||||
{
|
||||
Id = input.ConversationId,
|
||||
AgentId = input.AgentId
|
||||
};
|
||||
converation = await conversationService.NewConversation(sess);
|
||||
}
|
||||
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
|
||||
var result = await conv.SendMessage("", "", conversations.Last());
|
||||
var result = await conversationService.SendMessage(input.AgentId, input.ConversationId, conversations);
|
||||
|
||||
await OnChunkReceived(outputStream, result);
|
||||
await OnEventCompleted(outputStream);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ namespace BotSharp.Plugin.ChatbotUI.ViewModels;
|
|||
|
||||
public class OpenAiMessageInput
|
||||
{
|
||||
public string AgentId { get; set; }
|
||||
public string ConversationId { get; set; }
|
||||
public string Model { get; set; } = string.Empty;
|
||||
public List<OpenAiMessageBody> Messages { get; set; } = new List<OpenAiMessageBody>();
|
||||
[JsonPropertyName("max_tokens")]
|
||||
|
|
|
|||
Loading…
Reference in a new issue