Abastract chat service zone.

This commit is contained in:
Haiping Chen 2023-06-22 23:43:00 -05:00
parent 2c5fa98dfe
commit e3c9d0632c
21 changed files with 159 additions and 101 deletions

View file

@ -2,13 +2,22 @@ namespace BotSharp.Abstraction.Agents.Models;
public class Agent
{
[StringLength(36)]
public string Id { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public DateTime CreatedDateTime { get; set; }
public DateTime UpdatedDateTime { get; set; }
/// <summary>
/// Instruction
/// </summary>
public string Instruction { get; set; }
/// <summary>
/// Samples
/// </summary>
public string Samples { get; set; }
/// <summary>
/// Owner user id
/// </summary>

View file

@ -0,0 +1,11 @@
using BotSharp.Abstraction.Infrastructures.ContentTransfers;
namespace BotSharp.Abstraction.Conversations;
/// <summary>
/// IChatServiceZone is used to manage the chat function.
/// When user send message to controller, all the registered service zone will process the message respectively.
/// </summary>
public interface IChatServiceZone : IServiceZone
{
}

View file

@ -4,5 +4,6 @@ namespace BotSharp.Abstraction.Infrastructures.ContentTransfers;
public interface IServiceZone
{
int Priority { get; }
Task Serving(ContentContainer content);
}

View file

@ -3,5 +3,5 @@ namespace BotSharp.Abstraction.Models;
public class RoleDialogModel
{
public string Role { get; set; }
public string Content { get; set; }
public string Text { get; set; }
}

View file

@ -0,0 +1,16 @@
namespace BotSharp.Core.Agents.Services;
public partial class AgentService : IChatServiceZone
{
public int Priority => 10;
/// <summary>
/// Prepare agent profile and configurations
/// </summary>
/// <param name="content"></param>
/// <returns></returns>
public async Task Serving(ContentContainer content)
{
}
}

View file

@ -0,0 +1,29 @@
using BotSharp.Abstraction.Agents.Models;
namespace BotSharp.Core.Agents.Services;
public partial class AgentService
{
public async Task<Agent> CreateAgent(Agent agent)
{
var db = _services.GetRequiredService<AgentDbContext>();
var record = db.Agent.FirstOrDefault(x => x.OwnerId == _user.Id && x.Name == agent.Name);
if (record != null)
{
return record.ToAgent();
}
record = AgentRecord.FromAgent(agent);
record.Id = Guid.NewGuid().ToString();
record.OwnerId = _user.Id;
record.CreatedDateTime = DateTime.UtcNow;
record.UpdatedDateTime = DateTime.UtcNow;
db.Transaction<IAgentTable>(delegate
{
db.Add<IAgentTable>(record);
});
return record.ToAgent();
}
}

View file

@ -0,0 +1,9 @@
namespace BotSharp.Core.Agents.Services;
public partial class AgentService
{
public async Task<bool> DeleteAgent(string id)
{
throw new NotImplementedException();
}
}

View file

@ -0,0 +1,15 @@
using BotSharp.Abstraction.Agents.Models;
namespace BotSharp.Core.Agents.Services;
public partial class AgentService
{
public async Task<List<Agent>> GetAgents()
{
var db = _services.GetRequiredService<AgentDbContext>();
var query = from agent in db.Agent
where agent.OwnerId == _user.Id
select agent.ToAgent();
return query.ToList();
}
}

View file

@ -0,0 +1,11 @@
using BotSharp.Abstraction.Agents.Models;
namespace BotSharp.Core.Agents.Services;
public partial class AgentService
{
public async Task UpdateAgent(Agent agent)
{
throw new NotImplementedException();
}
}

View file

@ -1,10 +1,6 @@
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Users;
namespace BotSharp.Core.Agents.Services;
public class AgentService : IAgentService
public partial class AgentService : IAgentService
{
private readonly IServiceProvider _services;
private readonly IUserIdentity _user;
@ -14,46 +10,4 @@ public class AgentService : IAgentService
_services = services;
_user = user;
}
public async Task<Agent> CreateAgent(Agent agent)
{
var db = _services.GetRequiredService<AgentDbContext>();
var record = db.Agent.FirstOrDefault(x => x.OwnerId == _user.Id && x.Name == agent.Name);
if (record != null)
{
return record.ToAgent();
}
record = AgentRecord.FromAgent(agent);
record.Id = Guid.NewGuid().ToString();
record.OwnerId = _user.Id;
record.CreatedDateTime = DateTime.UtcNow;
record.UpdatedDateTime = DateTime.UtcNow;
db.Transaction<IAgentTable>(delegate
{
db.Add<IAgentTable>(record);
});
return record.ToAgent();
}
public Task<bool> DeleteAgent(string id)
{
throw new NotImplementedException();
}
public async Task<List<Agent>> GetAgents()
{
var db = _services.GetRequiredService<AgentDbContext>();
var query = from agent in db.Agent
where agent.OwnerId == _user.Id
select agent.ToAgent();
return query.ToList();
}
public Task UpdateAgent(Agent agent)
{
throw new NotImplementedException();
}
}

View file

@ -9,8 +9,12 @@ public static class BotSharpServiceCollectionExtensions
{
services.AddScoped<IUserIdentity, UserIdentity>();
services.AddScoped<IUserService, UserService>();
services.AddScoped<IAgentService, AgentService>();
services.AddScoped<IChatServiceZone, AgentService>();
services.AddScoped<ISessionService, SessionService>();
services.AddScoped<IConversationService, ConversationService>();
services.AddScoped<ITextChopper, TextChopperService>();

View file

@ -1,7 +1,5 @@
using BotSharp.Abstraction.ApiAdapters;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Infrastructures.ContentTransmitters;
using BotSharp.Abstraction.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Core.Conversations.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -13,49 +11,60 @@ namespace BotSharp.Core.Conversations;
public class ConversationController : ControllerBase, IApiAdapter
{
private readonly IServiceProvider _services;
private readonly IUserIdentity _user;
public ConversationController(IServiceProvider services)
public ConversationController(IServiceProvider services,
IUserIdentity user)
{
_services = services;
_user = user;
}
[HttpPost("/conversation/session")]
public async Task<SessionViewModel> NewSession([FromBody] SessionCreationModel session)
[HttpPost("/conversation/{agentId}")]
public async Task<SessionViewModel> NewSession([FromRoute] string agentId)
{
var service = _services.GetRequiredService<ISessionService>();
var sess = session.ToSession();
var sess = new Session
{
AgentId = agentId
};
sess = await service.NewSession(sess);
return SessionViewModel.FromSession(sess);
}
[HttpDelete("/conversation/session/{sessionId}")]
public async Task DeleteSession([FromRoute] string sessionId)
[HttpDelete("/conversation/{agentId}/{sessionId}")]
public async Task DeleteSession([FromRoute] string agentId, [FromRoute] string sessionId)
{
var service = _services.GetRequiredService<ISessionService>();
}
[HttpPost("/conversation/{sessionId}")]
public async Task<MessageResponseModel> SendMessage([FromBody] NewMessageModel input)
[HttpPost("/conversation/{agentId}/{sessionId}")]
public async Task<MessageResponseModel> SendMessage([FromRoute] string agentId,
[FromRoute] string sessionId,
[FromBody] NewMessageModel input)
{
var transmitter = _services.GetRequiredService<IContentTransfer>();
var container = new ContentContainer
{
AgentId = agentId,
SessionId = sessionId,
Conversations = new List<RoleDialogModel>
{
new RoleDialogModel
{
Role = "user",
Content = input.Content
Text = input.Text
}
}
},
UserId = _user.Id
};
var result = await transmitter.Transport(container);
return new MessageResponseModel
{
Content = result.IsSuccess ? container.Output.Content : result.Messages.First()
Content = result.IsSuccess ? container.Output.Text : result.Messages.First()
};
}
}

View file

@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Mvc;
namespace BotSharp.Core.Conversations.ViewModels;
public class NewMessageModel
{
public string Content { get; set; }
public string Text { get; set; }
}

View file

@ -4,13 +4,5 @@ namespace BotSharp.Core.Conversations.ViewModels;
public class SessionCreationModel
{
public string AgentId { get; set; }
public Session ToSession()
{
return new Session
{
AgentId = AgentId
};
}
}

View file

@ -1,23 +1,17 @@
using BotSharp.Abstraction.Infrastructures.ContentTransfers;
using BotSharp.Abstraction.Infrastructures.ContentTransmitters;
using BotSharp.Abstraction.Users;
namespace BotSharp.Core.Infrastructures;
public class ContentTransfer : IContentTransfer
{
private readonly IServiceProvider _services;
private readonly IUserIdentity _user;
public ContentTransfer(IServiceProvider services, IUserIdentity user)
public ContentTransfer(IServiceProvider services)
{
_services = services;
_user = user;
}
public async Task<TransportResult> Transport(ContentContainer input)
{
input.UserId = _user.Id;
input.Output = new RoleDialogModel();
var result = new TransportResult
{
@ -25,12 +19,12 @@ public class ContentTransfer : IContentTransfer
Messages = new List<string>()
};
var zones = _services.GetServices<IServiceZone>();
var zones = _services.GetServices<IChatServiceZone>()
.OrderBy(x => x.Priority)
.ToList();
foreach (var zone in zones)
{
input.Output = null;
try
{
await zone.Serving(input);

View file

@ -1,12 +1,9 @@
using BotSharp.Abstraction.Infrastructures.ContentTransfers;
using BotSharp.Abstraction.Models;
using BotSharp.Core.Repository.Collections;
using LLama;
using System.IO;
namespace BotSharp.Plugins.LLamaSharp;
public class ChatCompletionProvider : IServiceZone
public class ChatCompletionProvider : IChatServiceZone
{
private readonly IChatModel _model;
private readonly LlamaSharpSettings _settings;
@ -26,12 +23,14 @@ public class ChatCompletionProvider : IServiceZone
_model.InitChatAntiprompt(new string[] { "user:" });
}
public int Priority => 100;
public async Task GetChatCompletionsAsync(List<RoleDialogModel> conversations,
Func<string, Task> onChunkReceived)
{
string totalResponse = "";
var prompt = GetInstruction();
var content = string.Join("\n ", conversations.Select(x => $"{x.Role}: {x.Content.Replace("user:", "")}")).Trim();
var content = string.Join("\n ", conversations.Select(x => $"{x.Role}: {x.Text.Replace("user:", "")}")).Trim();
content += "\n assistant: ";
foreach (var response in _model.Chat(content, prompt, "UTF-8"))
{
@ -58,7 +57,7 @@ public class ChatCompletionProvider : IServiceZone
samples.Add(new RoleDialogModel
{
Role = role,
Content = content
Text = content
});
}
}
@ -76,7 +75,7 @@ public class ChatCompletionProvider : IServiceZone
instruction += "\n";
foreach (var message in GetChatSamples())
{
instruction += $"\n{message.Role}: {message.Content}";
instruction += $"\n{message.Role}: {message.Text}";
}
return instruction;
@ -86,7 +85,7 @@ public class ChatCompletionProvider : IServiceZone
{
string output = "";
var prompt = GetInstruction();
var conversations = string.Join("\n ", content.Conversations.Select(x => $"{x.Role}: {x.Content.Replace("user:", "")}")).Trim();
var conversations = string.Join("\n ", content.Conversations.Select(x => $"{x.Role}: {x.Text.Replace("user:", "")}")).Trim();
conversations += "\n assistant: ";
foreach (var response in _model.Chat(conversations, prompt, "UTF-8"))
{

View file

@ -12,6 +12,7 @@ global using BotSharp.Abstraction.Infrastructures.ContentTransmitters;
global using BotSharp.Abstraction.Infrastructures.ContentTransfers;
global using BotSharp.Abstraction.Knowledges;
global using BotSharp.Abstraction.Users;
global using BotSharp.Abstraction.Models;
global using BotSharp.Core.Repository;
global using BotSharp.Core.Repository.Abstraction;
global using BotSharp.Core.Repository.DbTables;

View file

@ -1,4 +1,4 @@
using BotSharp.Abstraction.Infrastructures.ContentTransfers;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Plugins;
using BotSharp.Plugin.AzureOpenAI.Providers;
@ -19,6 +19,6 @@ public class AzureOpenAiPlugin : IBotSharpPlugin
services.AddSingleton<ITextCompletion, TextCompletionProvider>();
services.AddScoped<IChatCompletion, ChatCompletionProvider>();
services.AddScoped<IServiceZone, ChatCompletionService>();
services.AddScoped<IChatServiceZone, ChatCompletionService>();
}
}

View file

@ -61,7 +61,7 @@ public class ChatCompletionProvider : IChatCompletion
samples.Add(new RoleDialogModel
{
Role = role,
Content = content
Text = content
});
}
}
@ -113,12 +113,12 @@ public class ChatCompletionProvider : IChatCompletion
foreach (var message in GetChatSamples())
{
chatCompletionsOptions.Messages.Add(new ChatMessage(message.Role, message.Content));
chatCompletionsOptions.Messages.Add(new ChatMessage(message.Role, message.Text));
}
foreach (var message in conversations)
{
chatCompletionsOptions.Messages.Add(new ChatMessage(message.Role, message.Content));
chatCompletionsOptions.Messages.Add(new ChatMessage(message.Role, message.Text));
}
return chatCompletionsOptions;

View file

@ -1,5 +1,5 @@
using Azure.AI.OpenAI;
using BotSharp.Abstraction.Infrastructures.ContentTransfers;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Infrastructures.ContentTransmitters;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Models;
@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace BotSharp.Plugin.AzureOpenAI.Services;
public class ChatCompletionService : IServiceZone
public class ChatCompletionService : IChatServiceZone
{
private readonly IChatCompletion _chatCompletion;
@ -16,6 +16,8 @@ public class ChatCompletionService : IServiceZone
_chatCompletion = chatCompletion;
}
public int Priority => 100;
public async Task Serving(ContentContainer content)
{
var output = await _chatCompletion.GetChatCompletionsAsync(content.Conversations);
@ -23,7 +25,7 @@ public class ChatCompletionService : IServiceZone
content.Output = new RoleDialogModel
{
Role = ChatRole.Assistant.ToString(),
Content = output
Text = output
};
}
}

View file

@ -61,7 +61,7 @@ public class ChatbotUiController : ControllerBase, IApiAdapter
var conversations = input.Messages.Skip(1).Select(x => new RoleDialogModel
{
Role = x.Role,
Content = x.Content
Text = x.Content
}).ToList();
/*await _chatCompletionProvider.GetChatCompletionsAsync(conversations,
@ -79,7 +79,7 @@ public class ChatbotUiController : ControllerBase, IApiAdapter
var result = await transmitter.Transport(container);
await OnChunkReceived(outputStream, container.Output.Content);
await OnChunkReceived(outputStream, container.Output.Text);
await OnEventCompleted(outputStream);
}