diff --git a/.gitignore b/.gitignore
index 1f5c1ad7..b529228b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -283,6 +283,7 @@ __pycache__/
*.btm.cs
*.odx.cs
*.xsd.cs
-conversations
+
/docs/_build
*.bin
+/src/WebStarter/data/conversations
diff --git a/BotSharp.sln b/BotSharp.sln
index e8de4184..258fd099 100644
--- a/BotSharp.sln
+++ b/BotSharp.sln
@@ -43,6 +43,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.PaddleSharp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.MetaMessenger", "src\Plugins\BotSharp.Plugin.MetaMessenger\BotSharp.Plugin.MetaMessenger.csproj", "{8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.OpenAPI", "src\Infrastructure\BotSharp.OpenAPI\BotSharp.OpenAPI.csproj", "{7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -147,6 +149,14 @@ Global
{8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Release|Any CPU.Build.0 = Release|Any CPU
{8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Release|x64.ActiveCfg = Release|Any CPU
{8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Release|x64.Build.0 = Release|Any CPU
+ {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Debug|x64.Build.0 = Debug|Any CPU
+ {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Release|x64.ActiveCfg = Release|Any CPU
+ {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -168,6 +178,7 @@ Global
{B797EC3E-B5B9-4047-A6C8-6D330B7C6763} = {2635EC9B-2E5F-4313-AC21-0B847F31F36C}
{0308FBFD-57EB-4709-9AE4-A80D516AD84D} = {B797EC3E-B5B9-4047-A6C8-6D330B7C6763}
{8300F66D-9EB8-438A-BF0F-70DFBE07D9DE} = {64264688-0F5C-4AB0-8F2B-B59B717CCE00}
+ {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj
new file mode 100644
index 00000000..ef6a4193
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net6.0
+ enable
+ enable
+ 0.8.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs
new file mode 100644
index 00000000..fb19ac5a
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs
@@ -0,0 +1,38 @@
+using BotSharp.Abstraction.ApiAdapters;
+using BotSharp.OpenAPI.ViewModels.Agents;
+
+namespace BotSharp.OpenAPI.Controllers;
+
+[Authorize]
+[ApiController]
+public class AgentController : ControllerBase, IApiAdapter
+{
+ private readonly IAgentService _agentService;
+ public AgentController(IAgentService agentService)
+ {
+ _agentService = agentService;
+ }
+
+ [HttpPost("/agent")]
+ public async Task CreateAgent(AgentCreationModel agent)
+ {
+ var createdAgent = await _agentService.CreateAgent(agent.ToAgent());
+ return AgentViewModel.FromAgent(createdAgent);
+ }
+
+ [HttpPut("/agent/{agentId}")]
+ public async Task UpdateAgent([FromRoute] string agentId,
+ [FromBody] AgentUpdateModel agent)
+ {
+ var model = agent.ToAgent();
+ model.Id = agentId;
+ await _agentService.UpdateAgent(model);
+ }
+
+ [HttpGet("/agents")]
+ public async Task> GetAgents()
+ {
+ var agents = await _agentService.GetAgents();
+ return agents.Select(x => AgentViewModel.FromAgent(x)).ToList();
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
new file mode 100644
index 00000000..dab95e50
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
@@ -0,0 +1,62 @@
+using BotSharp.Abstraction.ApiAdapters;
+using BotSharp.Abstraction.Conversations.Models;
+using BotSharp.OpenAPI.ViewModels.Conversations;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace BotSharp.OpenAPI.Controllers;
+
+[Authorize]
+[ApiController]
+public class ConversationController : ControllerBase, IApiAdapter
+{
+ private readonly IServiceProvider _services;
+ private readonly IUserIdentity _user;
+
+ public ConversationController(IServiceProvider services,
+ IUserIdentity user)
+ {
+ _services = services;
+ _user = user;
+ }
+
+ [HttpPost("/conversation/{agentId}")]
+ public async Task NewConversation([FromRoute] string agentId)
+ {
+ var service = _services.GetRequiredService();
+ var sess = new Conversation
+ {
+ UserId = _user.Id,
+ AgentId = agentId
+ };
+ sess = await service.NewConversation(sess);
+ return ConversationViewModel.FromSession(sess);
+ }
+
+ [HttpDelete("/conversation/{agentId}/{conversationId}")]
+ public async Task DeleteConversation([FromRoute] string agentId, [FromRoute] string conversationId)
+ {
+ var service = _services.GetRequiredService();
+ }
+
+ [HttpPost("/conversation/{agentId}/{conversationId}")]
+ public async Task SendMessage([FromRoute] string agentId,
+ [FromRoute] string conversationId,
+ [FromBody] NewMessageModel input)
+ {
+ var conv = _services.GetRequiredService();
+
+ var response = new MessageResponseModel();
+ var stackMsg = new List();
+
+ await conv.SendMessage(agentId, conversationId,
+ new RoleDialogModel("user", input.Text),
+ async msg =>
+ stackMsg.Add(msg),
+ async fn
+ => await Task.CompletedTask);
+
+ response.Text = string.Join("\r\n", stackMsg.Select(x => x.Content));
+ return response;
+ }
+}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeController.cs
new file mode 100644
index 00000000..4d63885e
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeController.cs
@@ -0,0 +1,77 @@
+using BotSharp.Abstraction.ApiAdapters;
+using BotSharp.Abstraction.Knowledges.Models;
+using Microsoft.AspNetCore.Http;
+using UglyToad.PdfPig.Content;
+using UglyToad.PdfPig;
+
+namespace BotSharp.OpenAPI.Controllers;
+
+[Authorize]
+[ApiController]
+public class KnowledgeController : ControllerBase, IApiAdapter
+{
+ private readonly IKnowledgeService _knowledgeService;
+ public KnowledgeController(IKnowledgeService knowledgeService)
+ {
+ _knowledgeService = knowledgeService;
+ }
+
+ [HttpGet("/knowledge/{agentId}")]
+ public async Task> RetrieveKnowledge([FromRoute] string agentId, [FromQuery(Name = "q")] string question)
+ {
+ return await _knowledgeService.GetAnswer(new KnowledgeRetrievalModel
+ {
+ AgentId = agentId,
+ Question = question
+ });
+ }
+
+ [HttpPost("/knowledge/{agentId}")]
+ public async Task FeedKnowledge([FromRoute] string agentId, List files, [FromQuery] int? startPageNum, [FromQuery] int? endPageNum)
+ {
+ long size = files.Sum(f => f.Length);
+
+ foreach (var formFile in files)
+ {
+ if (formFile.Length <= 0)
+ {
+ continue;
+ }
+
+ var filePath = Path.GetTempFileName();
+
+ using (var stream = System.IO.File.Create(filePath))
+ {
+ await formFile.CopyToAsync(stream);
+ }
+
+ var document = PdfDocument.Open(filePath);
+ var content = "";
+ foreach (Page page in document.GetPages())
+ {
+ if (startPageNum.HasValue && page.Number < startPageNum.Value)
+ {
+ continue;
+ }
+
+ if (endPageNum.HasValue && page.Number > endPageNum.Value)
+ {
+ continue;
+ }
+
+ content += page.Text;
+ }
+
+ // Process uploaded files
+ // Don't rely on or trust the FileName property without validation.
+
+ await _knowledgeService.Feed(new KnowledgeFeedModel
+ {
+ AgentId = agentId,
+ Content = content
+ });
+ }
+
+ return Ok(new { count = files.Count, size });
+ }
+}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs
new file mode 100644
index 00000000..1952c9c1
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs
@@ -0,0 +1,44 @@
+using BotSharp.Abstraction.ApiAdapters;
+using BotSharp.Abstraction.Users.Models;
+using BotSharp.OpenAPI.ViewModels.Users;
+
+namespace BotSharp.OpenAPI.Controllers;
+
+[Authorize]
+[ApiController]
+public class UserController : ControllerBase, IApiAdapter
+{
+ private readonly IUserService _userService;
+ public UserController(IUserService userService)
+ {
+ _userService = userService;
+ }
+
+ [AllowAnonymous]
+ [HttpPost("/token")]
+ public async Task> GetToken()
+ {
+ var authcode = Request.Headers["Authorization"].ToString();
+ var token = await _userService.GetToken(authcode.Split(' ')[1]);
+ if (token == null)
+ {
+ return Unauthorized();
+ }
+ return Ok(token);
+ }
+
+ [AllowAnonymous]
+ [HttpPost("/user")]
+ public async Task CreateUser(UserCreationModel user)
+ {
+ var createdUser = await _userService.CreateUser(user.ToUser());
+ return UserViewModel.FromUser(createdUser);
+ }
+
+ [HttpGet("/user/my")]
+ public async Task GetMyUserProfile()
+ {
+ var user = await _userService.GetMyProfile();
+ return UserViewModel.FromUser(user);
+ }
+}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Using.cs b/src/Infrastructure/BotSharp.OpenAPI/Using.cs
new file mode 100644
index 00000000..75f7274b
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/Using.cs
@@ -0,0 +1,18 @@
+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.AspNetCore.Authorization;
+global using Microsoft.AspNetCore.Mvc;
+global using Microsoft.Extensions.DependencyInjection;
+global using Microsoft.Extensions.Logging;
+global using BotSharp.Abstraction.Plugins;
+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.Abstraction.Agents.Settings;
+global using BotSharp.Abstraction.Conversations.Settings;
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs
new file mode 100644
index 00000000..f8fead86
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs
@@ -0,0 +1,18 @@
+using BotSharp.Abstraction.Agents.Models;
+
+namespace BotSharp.OpenAPI.ViewModels.Agents;
+
+public class AgentCreationModel
+{
+ public string Name { get; set; }
+ public string Description { get; set; }
+
+ public Agent ToAgent()
+ {
+ return new Agent
+ {
+ Name = Name,
+ Description = Description
+ };
+ }
+}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs
new file mode 100644
index 00000000..1a87a2d7
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs
@@ -0,0 +1,46 @@
+using BotSharp.Abstraction.Agents.Models;
+
+namespace BotSharp.OpenAPI.ViewModels.Agents;
+
+public class AgentUpdateModel
+{
+ public string Name { get; set; } = string.Empty;
+ public string? Description { get; set; }
+
+ ///
+ /// Instruction
+ ///
+ public string? Instruction { get; set; }
+
+ ///
+ /// Samples
+ ///
+ public string? Samples { get; set; }
+
+ ///
+ /// Functions
+ ///
+ public string? Functions { get; set; }
+
+ public Agent ToAgent()
+ {
+ var agent = new Agent
+ {
+ Name = Name
+ };
+
+ if (Description != null)
+ agent.Description = Description;
+
+ if (Instruction != null)
+ agent.Instruction = Instruction;
+
+ if (Samples != null)
+ agent.Samples = Samples;
+
+ if (Functions != null)
+ agent.Functions = Functions;
+
+ return agent;
+ }
+}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs
new file mode 100644
index 00000000..5a2b8969
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs
@@ -0,0 +1,22 @@
+using BotSharp.Abstraction.Agents.Models;
+
+namespace BotSharp.OpenAPI.ViewModels.Agents;
+
+public class AgentViewModel
+{
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Description { get; set; }
+ public DateTime UpdatedDateTime { get; set; }
+
+ public static AgentViewModel FromAgent(Agent agent)
+ {
+ return new AgentViewModel
+ {
+ Id = agent.Id,
+ Name = agent.Name,
+ Description = agent.Description,
+ UpdatedDateTime = agent.UpdatedDateTime
+ };
+ }
+}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationCreationModel.cs
new file mode 100644
index 00000000..645de634
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationCreationModel.cs
@@ -0,0 +1,6 @@
+namespace BotSharp.OpenAPI.ViewModels.Conversations;
+
+public class ConversationCreationModel
+{
+
+}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs
new file mode 100644
index 00000000..f47c5cd6
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs
@@ -0,0 +1,24 @@
+using BotSharp.Abstraction.Conversations.Models;
+
+namespace BotSharp.OpenAPI.ViewModels.Conversations;
+
+public class ConversationViewModel
+{
+ public string Id { get; set; }
+ public string AgentId { get; set; }
+ public string Title { get; set; } = string.Empty;
+ public DateTime UpdatedTime { get; set; } = DateTime.UtcNow;
+ public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
+
+ public static ConversationViewModel FromSession(Conversation sess)
+ {
+ return new ConversationViewModel
+ {
+ Id = sess.Id,
+ AgentId = sess.AgentId,
+ Title = sess.Title,
+ CreatedTime = sess.CreatedTime,
+ UpdatedTime = sess.UpdatedTime
+ };
+ }
+}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs
new file mode 100644
index 00000000..b6a6e1e4
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MessageResponseModel.cs
@@ -0,0 +1,6 @@
+namespace BotSharp.OpenAPI.ViewModels.Conversations;
+
+public class MessageResponseModel
+{
+ public string Text { get; set; }
+}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/NewMessageModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/NewMessageModel.cs
new file mode 100644
index 00000000..45716e21
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/NewMessageModel.cs
@@ -0,0 +1,6 @@
+namespace BotSharp.OpenAPI.ViewModels.Conversations;
+
+public class NewMessageModel
+{
+ public string Text { get; set; }
+}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserCreationModel.cs
new file mode 100644
index 00000000..76fdb443
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserCreationModel.cs
@@ -0,0 +1,22 @@
+using BotSharp.Abstraction.Users.Models;
+
+namespace BotSharp.OpenAPI.ViewModels.Users;
+
+public class UserCreationModel
+{
+ public string FirstName { get; set; }
+ public string LastName { get; set; }
+ public string Email { get; set; }
+ public string Password { get; set; }
+
+ public User ToUser()
+ {
+ return new User
+ {
+ FirstName = FirstName,
+ LastName = LastName,
+ Email = Email,
+ Password = Password
+ };
+ }
+}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs
new file mode 100644
index 00000000..4e452334
--- /dev/null
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs
@@ -0,0 +1,22 @@
+using BotSharp.Abstraction.Users.Models;
+
+namespace BotSharp.OpenAPI.ViewModels.Users;
+
+public class UserViewModel
+{
+ public string Id { get; set; }
+ public string FirstName { get; set; }
+ public string LastName { get; set; }
+ public string Email { get; set; }
+
+ public static UserViewModel FromUser(User user)
+ {
+ return new UserViewModel
+ {
+ Id = user.Id,
+ FirstName = user.FirstName,
+ LastName = user.LastName,
+ Email = user.Email
+ };
+ }
+}
diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj
index 41765502..d03a2f39 100644
--- a/src/WebStarter/WebStarter.csproj
+++ b/src/WebStarter/WebStarter.csproj
@@ -35,6 +35,7 @@
+