From 0419396555822fdac600f5f88bf0f3b85d385d3c Mon Sep 17 00:00:00 2001 From: Oceania2018 Date: Sat, 16 Mar 2019 11:34:32 -0500 Subject: [PATCH] add articulate ai platform --- BotSharp.Platform.Articulate/ArticulateAi.cs | 170 ++++++++++++++++++ .../BotSharp.Platform.Articulate.csproj | 19 ++ .../Controllers/AgentController.cs | 76 ++++++++ .../Controllers/DomainController.cs | 66 +++++++ .../Controllers/EntityController.cs | 66 +++++++ .../Controllers/IntentController.cs | 129 +++++++++++++ .../Controllers/ParseControllercs.cs | 45 +++++ .../Controllers/ScenarioController.cs | 64 +++++++ .../Controllers/SettingsController.cs | 52 ++++++ .../Controllers/TrainController.cs | 38 ++++ .../Models/AgentModel.cs | 41 +++++ .../Models/DomainModel.cs | 31 ++++ .../Models/EntityModel.cs | 23 +++ .../Models/EntitySynonymModel.cs | 13 ++ .../Models/IntentExampleModel.cs | 20 +++ .../Models/IntentModel.cs | 31 ++++ .../Models/LanguageModel.cs | 13 ++ .../Models/PipelineModel.cs | 11 ++ .../Models/ScenarioModel.cs | 22 +++ .../Models/SettingsModel.cs | 38 ++++ .../Models/SlotModel.cs | 19 ++ .../ModuleInjector.cs | 29 +++ .../PlatformSettings.cs | 11 ++ BotSharp.Platform.Articulate/README.md | 2 + .../ViewModels/DomainPageViewModel.cs | 14 ++ .../ViewModels/EntityPageViewModel.cs | 14 ++ .../ViewModels/IntentPageViewModel.cs | 13 ++ .../ViewModels/IntentScenarioViewModel.cs | 15 ++ .../ViewModels/IntentViewModel.cs | 23 +++ .../ViewModels/ResponseViewModel.cs | 11 ++ BotSharp.WebHost/BotSharp.WebHost.csproj | 1 + BotSharp.WebHost/Settings/app.json | 4 + .../Settings/platforms.ArticulateAi.json | 9 + .../Settings/platforms.RasaAi.json | 9 + BotSharp.sln | 10 ++ 35 files changed, 1152 insertions(+) create mode 100644 BotSharp.Platform.Articulate/ArticulateAi.cs create mode 100644 BotSharp.Platform.Articulate/BotSharp.Platform.Articulate.csproj create mode 100644 BotSharp.Platform.Articulate/Controllers/AgentController.cs create mode 100644 BotSharp.Platform.Articulate/Controllers/DomainController.cs create mode 100644 BotSharp.Platform.Articulate/Controllers/EntityController.cs create mode 100644 BotSharp.Platform.Articulate/Controllers/IntentController.cs create mode 100644 BotSharp.Platform.Articulate/Controllers/ParseControllercs.cs create mode 100644 BotSharp.Platform.Articulate/Controllers/ScenarioController.cs create mode 100644 BotSharp.Platform.Articulate/Controllers/SettingsController.cs create mode 100644 BotSharp.Platform.Articulate/Controllers/TrainController.cs create mode 100644 BotSharp.Platform.Articulate/Models/AgentModel.cs create mode 100644 BotSharp.Platform.Articulate/Models/DomainModel.cs create mode 100644 BotSharp.Platform.Articulate/Models/EntityModel.cs create mode 100644 BotSharp.Platform.Articulate/Models/EntitySynonymModel.cs create mode 100644 BotSharp.Platform.Articulate/Models/IntentExampleModel.cs create mode 100644 BotSharp.Platform.Articulate/Models/IntentModel.cs create mode 100644 BotSharp.Platform.Articulate/Models/LanguageModel.cs create mode 100644 BotSharp.Platform.Articulate/Models/PipelineModel.cs create mode 100644 BotSharp.Platform.Articulate/Models/ScenarioModel.cs create mode 100644 BotSharp.Platform.Articulate/Models/SettingsModel.cs create mode 100644 BotSharp.Platform.Articulate/Models/SlotModel.cs create mode 100644 BotSharp.Platform.Articulate/ModuleInjector.cs create mode 100644 BotSharp.Platform.Articulate/PlatformSettings.cs create mode 100644 BotSharp.Platform.Articulate/README.md create mode 100644 BotSharp.Platform.Articulate/ViewModels/DomainPageViewModel.cs create mode 100644 BotSharp.Platform.Articulate/ViewModels/EntityPageViewModel.cs create mode 100644 BotSharp.Platform.Articulate/ViewModels/IntentPageViewModel.cs create mode 100644 BotSharp.Platform.Articulate/ViewModels/IntentScenarioViewModel.cs create mode 100644 BotSharp.Platform.Articulate/ViewModels/IntentViewModel.cs create mode 100644 BotSharp.Platform.Articulate/ViewModels/ResponseViewModel.cs create mode 100644 BotSharp.WebHost/Settings/platforms.ArticulateAi.json create mode 100644 BotSharp.WebHost/Settings/platforms.RasaAi.json diff --git a/BotSharp.Platform.Articulate/ArticulateAi.cs b/BotSharp.Platform.Articulate/ArticulateAi.cs new file mode 100644 index 00000000..1dac33f8 --- /dev/null +++ b/BotSharp.Platform.Articulate/ArticulateAi.cs @@ -0,0 +1,170 @@ +using BotSharp.Core; +using BotSharp.Core.Engines; +using BotSharp.Platform.Abstraction; +using BotSharp.Platform.Articulate.Models; +using BotSharp.Platform.Models; +using BotSharp.Platform.Models.AiRequest; +using BotSharp.Platform.Models.AiResponse; +using BotSharp.Platform.Models.Contexts; +using BotSharp.Platform.Models.Entities; +using DotNetToolkit; +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace BotSharp.Platform.Articulate +{ + /// + /// A platform for building conversational interfaces with intelligent agents (chatbots) + /// http://spg.ai/projects/articulate + /// This implementation takes over APIs of Articulate's 7500 port. + /// + public class ArticulateAi : + PlatformBuilderBase, + IPlatformBuilder + where TAgent : AgentModel + { + public ArticulateAi(IAgentStorageFactory agentStorageFactory, IContextStorageFactory contextStorageFactory, IPlatformSettings settings, IConfiguration config) + : base(agentStorageFactory, contextStorageFactory, settings) + { + + } + + public async Task> GetAgentByDomainId(String domainId) + { + var results = await GetAllAgents(); + + foreach (TAgent agent in results) + { + var domain = agent.Domains.FirstOrDefault(x => x.Id == domainId); + + if (domain != null) + { + return new Tuple(agent, domain); + } + } + + return null; + } + + public async Task> GetAgentByIntentId(String intentId) + { + var results = await GetAllAgents(); + + foreach (TAgent agent in results) + { + foreach (DomainModel domain in agent.Domains) + { + var intent = domain.Intents.FirstOrDefault(x => x.Id == intentId); + if (intent != null) + { + return new Tuple(agent, domain, intent); + } + } + } + + return null; + } + + public async Task> GetReferencedIntentsByEntity(string entityId) + { + var intents = new List(); + var allAgents = await GetAllAgents(); + foreach (TAgent agent in allAgents) + { + foreach (DomainModel domain in agent.Domains) + { + foreach (IntentModel intent in domain.Intents) + { + if(intent.Examples.Exists(x => x.Entities.Exists(y => y.EntityId == entityId))) + { + intents.Add(intent); + } + } + } + } + + return intents; + } + + public async Task ExtractorCorpus(TAgent agent) + { + var corpus = new TrainingCorpus(); + corpus.Entities = agent.Entities.Select(x => new TrainingEntity + { + Entity = x.EntityName, + Values = x.Examples.Select(y => new TrainingEntitySynonym + { + Value = y.Value, + Synonyms = y.Synonyms + }).ToList() + }).ToList(); + + corpus.UserSays = new List>(); + + foreach(DomainModel domain in agent.Domains) + { + foreach(IntentModel intent in domain.Intents) + { + foreach(IntentExampleModel example in intent.Examples) + { + var say = new TrainingIntentExpression() + { + Intent = intent.IntentName, + Text = example.UserSays, + Entities = example.Entities.Select(x => new TrainingIntentExpressionPart + { + Entity = x.Entity, + Start = x.Start, + Value = x.Value + }).ToList() + }; + + corpus.UserSays.Add(say); + } + } + } + + return corpus; + } + + public override async Task SaveAgent(TAgent agent) + { + agent.Status = "Changed"; + agent.LastTraining = DateTime.UtcNow; + return await base.SaveAgent(agent); + } + + public async Task TextRequest(AiRequest request) + { + var aiResponse = new AiResponse(); + + // Load agent + var projectPath = Path.Combine(AppDomain.CurrentDomain.GetData("DataPath").ToString(), "Projects", request.AgentId); + var model = Directory.GetDirectories(projectPath).Where(x => x.Contains("model_")).Last().Split(Path.DirectorySeparatorChar).Last(); + var modelPath = Path.Combine(projectPath, model); + request.AgentDir = projectPath; + request.Model = model; + + var agent = await GetAgentById(request.AgentId); + + var preditor = new BotPredictor(); + var doc = await preditor.Predict(agent, request); + + var parameters = new Dictionary(); + if (doc.Sentences[0].Entities == null) + { + doc.Sentences[0].Entities = new List(); + } + doc.Sentences[0].Entities.ForEach(x => parameters[x.Entity] = x.Value); + + aiResponse.Intent = doc.Sentences[0].Intent.Label; + //aiResponse.Speech = aiResponse.Intent; + + return aiResponse; + } + } +} diff --git a/BotSharp.Platform.Articulate/BotSharp.Platform.Articulate.csproj b/BotSharp.Platform.Articulate/BotSharp.Platform.Articulate.csproj new file mode 100644 index 00000000..47e7c0fb --- /dev/null +++ b/BotSharp.Platform.Articulate/BotSharp.Platform.Articulate.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.2 + + + + DEBUG;TRACE + + + + + + + + + + + diff --git a/BotSharp.Platform.Articulate/Controllers/AgentController.cs b/BotSharp.Platform.Articulate/Controllers/AgentController.cs new file mode 100644 index 00000000..d6bc5f5a --- /dev/null +++ b/BotSharp.Platform.Articulate/Controllers/AgentController.cs @@ -0,0 +1,76 @@ +using BotSharp.Core; +using BotSharp.Core.Engines; +using BotSharp.Platform.Abstraction; +using BotSharp.Platform.Articulate.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace BotSharp.Platform.Articulate.Controllers +{ + [Route("[controller]")] + public class AgentController : ControllerBase + { + private ArticulateAi builder; + + /// + /// Initialize agent controller and get a platform instance + /// + /// + public AgentController(ArticulateAi platform) + { + builder = platform; + } + + [HttpPost] + public async Task PostAgent() + { + AgentModel agent = null; + + using (var reader = new StreamReader(Request.Body)) + { + string body = reader.ReadToEnd(); + agent = JsonConvert.DeserializeObject(body); + } + + // convert to standard Agent structure + agent.Id = new Random().Next(Int32.MaxValue).ToString(); + agent.Name = agent.AgentName; + await builder.SaveAgent(agent); + + return agent; + } + + [HttpGet] + public async Task> GetAgent() + { + var results = await builder.GetAllAgents(); + var agents = results.ToList(); + + return agents; + } + + [HttpGet("{agentId}")] + public async Task GetAgentById([FromRoute] string agentId) + { + var agent = await builder.GetAgentById(agentId); + + return agent; + } + + [HttpGet("name/{agentName}")] + public async Task GetAgentByName([FromRoute] string agentName) + { + var agent = await builder.GetAgentByName(agentName); + + return agent; + } + } +} diff --git a/BotSharp.Platform.Articulate/Controllers/DomainController.cs b/BotSharp.Platform.Articulate/Controllers/DomainController.cs new file mode 100644 index 00000000..1ad5d3f6 --- /dev/null +++ b/BotSharp.Platform.Articulate/Controllers/DomainController.cs @@ -0,0 +1,66 @@ +using BotSharp.Platform.Articulate.Models; +using BotSharp.Platform.Articulate.ViewModels; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace BotSharp.Platform.Articulate.Controllers +{ + [Route("[controller]")] + public class DomainController : ControllerBase + { + private ArticulateAi builder; + + public DomainController(ArticulateAi platform) + { + builder = platform; + } + + [HttpGet("{domainId}")] + public async Task GetDomain([FromRoute] int domainId) + { + string dataDir = Path.Combine(AppDomain.CurrentDomain.GetData("DataPath").ToString(), "Articulate"); + + var dataPath = Directory.GetFiles(dataDir).FirstOrDefault(x => Regex.IsMatch(x, $"-domain-{domainId}.json")); + string json = System.IO.File.ReadAllText(dataPath); + + var domain = JsonConvert.DeserializeObject(json); + + return domain; + } + + [HttpPost] + public async Task PostDomain() + { + DomainModel domain = null; + + using (var reader = new StreamReader(Request.Body)) + { + string body = reader.ReadToEnd(); + domain = JsonConvert.DeserializeObject(body); + } + + var agent = await builder.GetAgentByName(domain.Agent); + domain.Id = Guid.NewGuid().ToString(); + (agent as AgentModel).Domains.Add(domain); + await builder.SaveAgent(agent); + + return domain; + } + + [HttpGet("/agent/{agentId}/domain")] + public async Task GetAgentDomains([FromRoute] string agentId, [FromQuery] int start, [FromQuery] int limit) + { + var agent = await builder.GetAgentById(agentId); + + return new DomainPageViewModel { Domains = agent.Domains, Total = agent.Domains.Count }; + } + } +} diff --git a/BotSharp.Platform.Articulate/Controllers/EntityController.cs b/BotSharp.Platform.Articulate/Controllers/EntityController.cs new file mode 100644 index 00000000..5310f919 --- /dev/null +++ b/BotSharp.Platform.Articulate/Controllers/EntityController.cs @@ -0,0 +1,66 @@ +using BotSharp.Platform.Articulate.Models; +using BotSharp.Platform.Articulate.ViewModels; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.Platform.Articulate.Controllers +{ + [Route("[controller]")] + public class EntityController : ControllerBase + { + private ArticulateAi builder; + + public EntityController(ArticulateAi platform) + { + builder = platform; + } + + [HttpGet("{entityId}")] + public async Task GetEntity([FromRoute] string entityId) + { + string dataDir = Path.Combine(AppDomain.CurrentDomain.GetData("DataPath").ToString(), "Articulate"); + + string dataPath = Directory.GetFiles(dataDir).FirstOrDefault(x => x.EndsWith($"-entity-{entityId}.json")); + + string json = System.IO.File.ReadAllText(dataPath); + + var entity = JsonConvert.DeserializeObject(json); + + return entity; + } + + [HttpGet("/agent/{agentId}/entity")] + public async Task GetAgentEntities([FromRoute] string agentId, [FromQuery] int start, [FromQuery] int limit) + { + var agent = await builder.GetAgentById(agentId); + return new EntityPageViewModel { Entities = agent.Entities.Select(x => x as EntityModel).ToList(), Total = agent.Entities.Count }; + } + + [HttpPost] + public async Task PostEntity() + { + EntityModel entity = null; + + using (var reader = new StreamReader(Request.Body)) + { + string body = reader.ReadToEnd(); + entity = JsonConvert.DeserializeObject(body); + } + + var agent = await builder.GetAgentByName(entity.Agent); + entity.Id = Guid.NewGuid().ToString(); + agent.Entities.Add(entity); + + await builder.SaveAgent(agent); + + return entity; + } + } +} diff --git a/BotSharp.Platform.Articulate/Controllers/IntentController.cs b/BotSharp.Platform.Articulate/Controllers/IntentController.cs new file mode 100644 index 00000000..90edaa2e --- /dev/null +++ b/BotSharp.Platform.Articulate/Controllers/IntentController.cs @@ -0,0 +1,129 @@ +using BotSharp.Core; +using BotSharp.Platform.Articulate.Models; +using BotSharp.Platform.Articulate.ViewModels; +using BotSharp.Platform.Models; +using DotNetToolkit; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.Platform.Articulate.Controllers +{ + [Route("[controller]")] + public class IntentController : ControllerBase + { + private ArticulateAi builder; + + public IntentController(ArticulateAi platform) + { + builder = platform; + } + + [HttpGet("{intentId}")] + public async Task GetIntent([FromRoute] string intentId) + { + var agent = await builder.GetAgentByIntentId(intentId); + + return agent.Item3.ToObject(); + } + + [HttpPost] + public async Task PostIntent() + { + IntentViewModel intent = null; + + using (var reader = new StreamReader(Request.Body)) + { + string body = reader.ReadToEnd(); + intent = JsonConvert.DeserializeObject(body); + } + + var agent = await builder.GetAgentByName(intent.Agent); + intent.Id = Guid.NewGuid().ToString(); + var domain = agent.Domains.First(x => x.DomainName == intent.Domain); + domain.Intents.Add(intent.ToObject()); + await builder.SaveAgent(agent); + + return intent; + } + + [HttpPut("{intentId}")] + public async Task PutIntent([FromRoute] string intentId) + { + IntentViewModel intent = null; + + using (var reader = new StreamReader(Request.Body)) + { + string body = reader.ReadToEnd(); + intent = JsonConvert.DeserializeObject(body); + } + + var agent = await builder.GetAgentByIntentId(intentId); + + var updateAgent = agent.Item1; + var updateIntents = updateAgent.Domains.First(x => x.Id == agent.Item2.Id).Intents; + var updateIntent = updateIntents.First(x => x.Id == agent.Item3.Id); + + updateIntent.IntentName = intent.IntentName; + updateIntent.Examples = intent.Examples; + + await builder.SaveAgent(updateAgent); + + return intent; + } + + [HttpGet("{intentId}/webhook")] + public async Task GetIntentWebhook([FromRoute] string intentId) + { + + } + + [HttpGet("{intentId}/postFormat")] + public async Task GetIntentPostFormat([FromRoute] string intentId) + { + + } + + [HttpGet("/agent/{agentId}/intent")] + public async Task GetAgentIntents([FromRoute] string agentId, [FromQuery] int start, [FromQuery] int limit) + { + var intents = new List(); + + string dataDir = Path.Combine(AppDomain.CurrentDomain.GetData("DataPath").ToString(), "Articulate"); + + var agentPaths = Directory.GetFiles(dataDir).Where(x => x.Contains($"agent-{agentId}-intent-")).ToList(); + for (int i = 0; i < agentPaths.Count; i++) + { + string json = System.IO.File.ReadAllText(agentPaths[i]); + + var intent = JsonConvert.DeserializeObject(json); + + intents.Add(intent); + } + + return new IntentPageViewModel { Intents = intents, Total = intents.Count }; + } + + [HttpGet("/entity/{entityId}/intent")] + public async Task> GetReferencedIntentsByEntity([FromRoute] string entityId, [FromQuery] int start, [FromQuery] int limit) + { + var models = await builder.GetReferencedIntentsByEntity(entityId); + return models.Select(x => x.ToObject()).ToList(); + } + + [HttpGet("/domain/{domainId}/intent")] + public async Task GetReferencedIntentsByDomain([FromRoute] string domainId, [FromQuery] int start, [FromQuery] int limit) + { + var agent = await builder.GetAgentByDomainId(domainId); + var intents = agent.Item2.Intents.Select(x => x.ToObject()).ToList(); + + return new IntentPageViewModel { Intents = intents, Total = intents.Count }; + } + } +} diff --git a/BotSharp.Platform.Articulate/Controllers/ParseControllercs.cs b/BotSharp.Platform.Articulate/Controllers/ParseControllercs.cs new file mode 100644 index 00000000..9c4ab7b6 --- /dev/null +++ b/BotSharp.Platform.Articulate/Controllers/ParseControllercs.cs @@ -0,0 +1,45 @@ +using BotSharp.Platform.Articulate.Models; +using BotSharp.Platform.Articulate.ViewModels; +using BotSharp.Platform.Models.AiRequest; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Console = Colorful.Console; + +namespace BotSharp.Platform.Articulate.Controllers +{ + [Route("[controller]")] + public class ParseControllercs : ControllerBase + { + private ArticulateAi builder; + + public ParseControllercs(ArticulateAi platform) + { + builder = platform; + } + + [HttpGet("/agent/{agentId}/converse")] + public async Task ParseText([FromRoute] string agentId, [FromQuery] string text, [FromQuery] string sessionId) + { + var response = new ResponseViewModel(); + + Console.WriteLine($"Got message from {Request.Host}: {text}", Color.Green); + + var aiResponse = await builder.TextRequest(new AiRequest + { + AgentId = agentId, + Text = text + }); + + response.TextResponse = aiResponse.Text; + + return Ok(response); + } + } +} diff --git a/BotSharp.Platform.Articulate/Controllers/ScenarioController.cs b/BotSharp.Platform.Articulate/Controllers/ScenarioController.cs new file mode 100644 index 00000000..d37fc612 --- /dev/null +++ b/BotSharp.Platform.Articulate/Controllers/ScenarioController.cs @@ -0,0 +1,64 @@ +using BotSharp.Core; +using BotSharp.Platform.Articulate.Models; +using DotNetToolkit; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.Platform.Articulate.Controllers +{ + [Route("[controller]")] + public class ScenarioController : ControllerBase + { + private ArticulateAi builder; + + public ScenarioController(ArticulateAi platform) + { + builder = platform; + } + + [HttpGet("/intent/{intentId}/scenario")] + public async Task GetIntentScenario([FromRoute] string intentId) + { + var agent = await builder.GetAgentByIntentId(intentId); + + var view = agent.Item3.Scenario.ToObject(); + + view.Agent = agent.Item1.AgentName; + view.Domain = agent.Item2.DomainName; + view.Intent = agent.Item3.IntentName; + + return view; + } + + [HttpPost("/intent/{intentId}/scenario")] + public async Task PostIntentScenario() + { + IntentScenarioViewModel scenario = null; + + using (var reader = new StreamReader(Request.Body)) + { + string body = reader.ReadToEnd(); + scenario = JsonConvert.DeserializeObject(body); + } + + scenario.Id = Guid.NewGuid().ToString(); + + var agent = await builder.GetAgentByName(scenario.Agent); + + var domain = agent.Domains.FirstOrDefault(x => x.DomainName == scenario.Domain); + var intent = domain.Intents.FirstOrDefault(x => x.IntentName == scenario.Intent); + intent.Scenario = scenario.ToObject(); + + await builder.SaveAgent(agent); + + return scenario; + } + } +} diff --git a/BotSharp.Platform.Articulate/Controllers/SettingsController.cs b/BotSharp.Platform.Articulate/Controllers/SettingsController.cs new file mode 100644 index 00000000..ff319430 --- /dev/null +++ b/BotSharp.Platform.Articulate/Controllers/SettingsController.cs @@ -0,0 +1,52 @@ +using BotSharp.Platform.Articulate.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.Platform.Articulate.Controllers +{ + [Route("[controller]")] + public class SettingsController : ControllerBase + { + [HttpGet] + public async Task GetSettings() + { + string dataPath = Path.Combine(AppDomain.CurrentDomain.GetData("DataPath").ToString(), "Articulate", "settings.json"); + + string json = System.IO.File.ReadAllText(dataPath); + + var settings = JsonConvert.DeserializeObject(json); + + return settings; + } + + [HttpGet("/agent/{agentId}/settings")] + public async Task GetSettingsByAgent([FromRoute] string agentId) + { + string dataPath = Path.Combine(AppDomain.CurrentDomain.GetData("DataPath").ToString(), "Articulate", "settings.json"); + + string json = System.IO.File.ReadAllText(dataPath); + + var settings = JsonConvert.DeserializeObject(json); + + return settings; + } + + [HttpPut("/agent/{agentId}/settings")] + public async Task PutSettingsByAgent([FromRoute] string agentId) + { + string dataPath = Path.Combine(AppDomain.CurrentDomain.GetData("DataPath").ToString(), "Articulate", "settings.json"); + + string json = System.IO.File.ReadAllText(dataPath); + + var settings = JsonConvert.DeserializeObject(json); + + return settings; + } + } +} diff --git a/BotSharp.Platform.Articulate/Controllers/TrainController.cs b/BotSharp.Platform.Articulate/Controllers/TrainController.cs new file mode 100644 index 00000000..80af944e --- /dev/null +++ b/BotSharp.Platform.Articulate/Controllers/TrainController.cs @@ -0,0 +1,38 @@ +using BotSharp.Platform.Articulate.Models; +using BotSharp.Platform.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.Platform.Articulate.Controllers +{ + [Route("[controller]")] + public class TrainController : ControllerBase + { + private ArticulateAi builder; + + public TrainController(ArticulateAi articulateAi) + { + builder = articulateAi; + } + + [HttpGet("/agent/{agentId}/train")] + public async Task TrainAgent([FromRoute] string agentId) + { + var agent = await builder.GetAgentById(agentId); + + var corpus = await builder.ExtractorCorpus(agent); + + await builder.Train(agent, corpus, new BotTrainOptions { }); + + agent.Status = "Ready"; + + await builder.SaveAgent(agent); + + return agent; + } + } +} diff --git a/BotSharp.Platform.Articulate/Models/AgentModel.cs b/BotSharp.Platform.Articulate/Models/AgentModel.cs new file mode 100644 index 00000000..416bc124 --- /dev/null +++ b/BotSharp.Platform.Articulate/Models/AgentModel.cs @@ -0,0 +1,41 @@ +using BotSharp.Platform.Models; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.Models +{ + public class AgentModel : AgentBase + { + public AgentModel() + { + Domains = new List(); + Entities = new List(); + } + + public string Status { get; set; } + + public string Timezone { get; set; } + + public string AgentName { get; set; } + + public bool UseWebhook { get; set; } + + public bool UsePostFormat { get; set; } + + public bool ExtraTrainingData { get; set; } + + public List FallbackResponses { get; set; } + + public bool EnableModelsPerDomain { get; set; } + + public decimal DomainClassifierThreshold { get; set; } + + public List Domains { get; set; } + + public List Entities { get; set; } + + public DateTime LastTraining { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/Models/DomainModel.cs b/BotSharp.Platform.Articulate/Models/DomainModel.cs new file mode 100644 index 00000000..7bcacfc4 --- /dev/null +++ b/BotSharp.Platform.Articulate/Models/DomainModel.cs @@ -0,0 +1,31 @@ +using BotSharp.Platform.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.Models +{ + public class DomainModel + { + public DomainModel() + { + Intents = new List(); + } + + public string Id { get; set; } + + public string Agent { get; set; } + + public string DomainName { get; set; } + + public bool Enabled { get; set; } + + public bool ExtraTrainingData { get; set; } + + public decimal IntentThreshold { get; set; } + + public string Status { get; set; } + + public List Intents { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/Models/EntityModel.cs b/BotSharp.Platform.Articulate/Models/EntityModel.cs new file mode 100644 index 00000000..417ae540 --- /dev/null +++ b/BotSharp.Platform.Articulate/Models/EntityModel.cs @@ -0,0 +1,23 @@ +using BotSharp.Platform.Abstraction; +using BotSharp.Platform.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.Models +{ + public class EntityModel : EntityBase + { + public string Regex { get; set; } + + public string Agent { get; set; } + + public string EntityName { get; set; } + + public string Type { get; set; } + + public string UiColor { get; set; } + + public List Examples { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/Models/EntitySynonymModel.cs b/BotSharp.Platform.Articulate/Models/EntitySynonymModel.cs new file mode 100644 index 00000000..31cab6ac --- /dev/null +++ b/BotSharp.Platform.Articulate/Models/EntitySynonymModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.Models +{ + public class EntitySynonymModel + { + public string Value { get; set; } + + public List Synonyms { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/Models/IntentExampleModel.cs b/BotSharp.Platform.Articulate/Models/IntentExampleModel.cs new file mode 100644 index 00000000..8466f2d1 --- /dev/null +++ b/BotSharp.Platform.Articulate/Models/IntentExampleModel.cs @@ -0,0 +1,20 @@ +using BotSharp.Core.Engines; +using BotSharp.Platform.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.Models +{ + public class IntentExampleModel + { + public string UserSays { get; set; } + + public List Entities { get; set; } + } + + public class ArticulateTrainingIntentExpressionPart : TrainingIntentExpressionPart + { + public string EntityId { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/Models/IntentModel.cs b/BotSharp.Platform.Articulate/Models/IntentModel.cs new file mode 100644 index 00000000..97b4bf2e --- /dev/null +++ b/BotSharp.Platform.Articulate/Models/IntentModel.cs @@ -0,0 +1,31 @@ +using BotSharp.Platform.Models; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.Models +{ + public class IntentModel : IntentBase + { + public string IntentName { get; set; } + + public string Agent { get; set; } + + public string Domain { get; set; } + + public bool UsePostFormat { get; set; } + + public bool UseWebhook { get; set; } + + /// + /// User says + /// + public List Examples { get; set; } + + /// + /// Intent responses + /// + public ScenarioModel Scenario { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/Models/LanguageModel.cs b/BotSharp.Platform.Articulate/Models/LanguageModel.cs new file mode 100644 index 00000000..ef65cc85 --- /dev/null +++ b/BotSharp.Platform.Articulate/Models/LanguageModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.Models +{ + public class LanguageModel + { + public string Text { get; set; } + + public string Value { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/Models/PipelineModel.cs b/BotSharp.Platform.Articulate/Models/PipelineModel.cs new file mode 100644 index 00000000..6312d08f --- /dev/null +++ b/BotSharp.Platform.Articulate/Models/PipelineModel.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.Models +{ + public class PipelineModel + { + public string Name { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/Models/ScenarioModel.cs b/BotSharp.Platform.Articulate/Models/ScenarioModel.cs new file mode 100644 index 00000000..ec6bb9b7 --- /dev/null +++ b/BotSharp.Platform.Articulate/Models/ScenarioModel.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace BotSharp.Platform.Articulate.Models +{ + public class ScenarioModel + { + /// + /// Guid + /// + [StringLength(36)] + public String Id { get; set; } + + public string ScenarioName { get; set; } + + public List IntentResponses { get; set; } + + public List Slots { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/Models/SettingsModel.cs b/BotSharp.Platform.Articulate/Models/SettingsModel.cs new file mode 100644 index 00000000..eede2900 --- /dev/null +++ b/BotSharp.Platform.Articulate/Models/SettingsModel.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.Models +{ + public class SettingsModel + { + public string DucklingURL { get; set; } + + public string UiLanguage { get; set; } + + public string DefaultAgentLanguage { get; set; } + + public string DefaultTimezone { get; set; } + + public List Timezones { get; set; } + + public List DomainClassifierPipeline { get; set; } + + public List IntentClassifierPipeline { get; set; } + + public List DucklingDimension { get; set; } + + public List EntityClassifierPipeline { get; set; } + + public List DefaultAgentFallbackResponses { get; set; } + + public string RasaURL { get; set; } + + public List SpacyPretrainedEntities { get; set; } + + public List AgentLanguages { get; set; } + + public List UiLanguages { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/Models/SlotModel.cs b/BotSharp.Platform.Articulate/Models/SlotModel.cs new file mode 100644 index 00000000..1489d10a --- /dev/null +++ b/BotSharp.Platform.Articulate/Models/SlotModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.Models +{ + public class SlotModel + { + public string Entity { get; set; } + + public bool IsList { get; set; } + + public bool IsRequired { get; set; } + + public string SlotName { get; set; } + + public List TextPrompts { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/ModuleInjector.cs b/BotSharp.Platform.Articulate/ModuleInjector.cs new file mode 100644 index 00000000..fe149927 --- /dev/null +++ b/BotSharp.Platform.Articulate/ModuleInjector.cs @@ -0,0 +1,29 @@ +using BotSharp.Core; +using BotSharp.Core.AgentStorage; +using BotSharp.Core.Modules; +using BotSharp.Platform.Abstraction; +using BotSharp.Platform.Articulate.Models; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using System; + +namespace BotSharp.Platform.Articulate +{ + public class ModuleInjector : IModule + { + public void ConfigureServices(IServiceCollection services, IConfiguration config) + { + services.AddSingleton>(); + AgentStorageServiceRegister.Register(services); + PlatformConfigServiceRegister.Register("articulateAi", services, config); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + + } + } +} diff --git a/BotSharp.Platform.Articulate/PlatformSettings.cs b/BotSharp.Platform.Articulate/PlatformSettings.cs new file mode 100644 index 00000000..2bfeb90e --- /dev/null +++ b/BotSharp.Platform.Articulate/PlatformSettings.cs @@ -0,0 +1,11 @@ +using BotSharp.Core; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate +{ + public class PlatformSettings : PlatformSettingsBase + { + } +} diff --git a/BotSharp.Platform.Articulate/README.md b/BotSharp.Platform.Articulate/README.md new file mode 100644 index 00000000..4a6095e3 --- /dev/null +++ b/BotSharp.Platform.Articulate/README.md @@ -0,0 +1,2 @@ +# botsharp-articulate +BotSharp platform emulator extension which is compatible with Articulate AI. diff --git a/BotSharp.Platform.Articulate/ViewModels/DomainPageViewModel.cs b/BotSharp.Platform.Articulate/ViewModels/DomainPageViewModel.cs new file mode 100644 index 00000000..95758703 --- /dev/null +++ b/BotSharp.Platform.Articulate/ViewModels/DomainPageViewModel.cs @@ -0,0 +1,14 @@ +using BotSharp.Platform.Articulate.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.ViewModels +{ + public class DomainPageViewModel + { + public List Domains { get; set; } + + public int Total { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/ViewModels/EntityPageViewModel.cs b/BotSharp.Platform.Articulate/ViewModels/EntityPageViewModel.cs new file mode 100644 index 00000000..0cffb31a --- /dev/null +++ b/BotSharp.Platform.Articulate/ViewModels/EntityPageViewModel.cs @@ -0,0 +1,14 @@ +using BotSharp.Platform.Articulate.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.ViewModels +{ + public class EntityPageViewModel + { + public List Entities { get; set; } + + public int Total { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/ViewModels/IntentPageViewModel.cs b/BotSharp.Platform.Articulate/ViewModels/IntentPageViewModel.cs new file mode 100644 index 00000000..d45ede8b --- /dev/null +++ b/BotSharp.Platform.Articulate/ViewModels/IntentPageViewModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.ViewModels +{ + public class IntentPageViewModel + { + public List Intents { get; set; } + + public int Total { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/ViewModels/IntentScenarioViewModel.cs b/BotSharp.Platform.Articulate/ViewModels/IntentScenarioViewModel.cs new file mode 100644 index 00000000..962ab917 --- /dev/null +++ b/BotSharp.Platform.Articulate/ViewModels/IntentScenarioViewModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.Models +{ + public class IntentScenarioViewModel : ScenarioModel + { + public string Domain { get; set; } + + public string Agent { get; set; } + + public string Intent { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/ViewModels/IntentViewModel.cs b/BotSharp.Platform.Articulate/ViewModels/IntentViewModel.cs new file mode 100644 index 00000000..c461b250 --- /dev/null +++ b/BotSharp.Platform.Articulate/ViewModels/IntentViewModel.cs @@ -0,0 +1,23 @@ +using BotSharp.Platform.Articulate.Models; +using BotSharp.Platform.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.ViewModels +{ + public class IntentViewModel : IntentBase + { + public string IntentName { get; set; } + + public string Agent { get; set; } + + public string Domain { get; set; } + + public bool UsePostFormat { get; set; } + + public bool UseWebhook { get; set; } + + public List Examples { get; set; } + } +} diff --git a/BotSharp.Platform.Articulate/ViewModels/ResponseViewModel.cs b/BotSharp.Platform.Articulate/ViewModels/ResponseViewModel.cs new file mode 100644 index 00000000..f2589c9a --- /dev/null +++ b/BotSharp.Platform.Articulate/ViewModels/ResponseViewModel.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Platform.Articulate.ViewModels +{ + public class ResponseViewModel + { + public string TextResponse { get; set; } + } +} diff --git a/BotSharp.WebHost/BotSharp.WebHost.csproj b/BotSharp.WebHost/BotSharp.WebHost.csproj index 0067cd65..a8555d62 100644 --- a/BotSharp.WebHost/BotSharp.WebHost.csproj +++ b/BotSharp.WebHost/BotSharp.WebHost.csproj @@ -90,6 +90,7 @@ + diff --git a/BotSharp.WebHost/Settings/app.json b/BotSharp.WebHost/Settings/app.json index e6c4b99e..f4d5943b 100644 --- a/BotSharp.WebHost/Settings/app.json +++ b/BotSharp.WebHost/Settings/app.json @@ -18,6 +18,10 @@ "Name": "OwnThink", "Type": "BotSharp.Platform.OwnThink" }, + { + "Name": "Articulate", + "Type": "BotSharp.Platform.Articulate" + }, { "Name": "WeixinChannel", "Type": "BotSharp.Channel.Weixin" diff --git a/BotSharp.WebHost/Settings/platforms.ArticulateAi.json b/BotSharp.WebHost/Settings/platforms.ArticulateAi.json new file mode 100644 index 00000000..21103dc3 --- /dev/null +++ b/BotSharp.WebHost/Settings/platforms.ArticulateAi.json @@ -0,0 +1,9 @@ +{ + // if you want to override platform setting, please set corresponding value, + // otherwise you don't need this section. + "articulateAi": { + "botEngine": "BotSharpNLU", + "agentStorage": "AgentStorageInFile", + "contextStorage": "ContextStorageInFile" + } +} diff --git a/BotSharp.WebHost/Settings/platforms.RasaAi.json b/BotSharp.WebHost/Settings/platforms.RasaAi.json new file mode 100644 index 00000000..56ad126c --- /dev/null +++ b/BotSharp.WebHost/Settings/platforms.RasaAi.json @@ -0,0 +1,9 @@ +{ + // if you want to override platform setting, please set corresponding value, + // otherwise you don't need this section. + "rasaAi": { + "botEngine": "BotSharpNLU", + "agentStorage": "AgentStorageInFile", + "contextStorage": "ContextStorageInFile" + } +} diff --git a/BotSharp.sln b/BotSharp.sln index da196d72..dea0915f 100644 --- a/BotSharp.sln +++ b/BotSharp.sln @@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Platform.Rasa", "B EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Platform.OwnThink", "BotSharp.Platform.OwnThink\BotSharp.Platform.OwnThink.csproj", "{96820DD6-0806-40A3-943A-7F2EF69E8EDB}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Platform.Articulate", "BotSharp.Platform.Articulate\BotSharp.Platform.Articulate.csproj", "{7290AC89-88DE-4CF8-946A-A1BB0E4C30EB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -91,6 +93,14 @@ Global {96820DD6-0806-40A3-943A-7F2EF69E8EDB}.Release|Any CPU.Build.0 = Release|Any CPU {96820DD6-0806-40A3-943A-7F2EF69E8EDB}.Release|x64.ActiveCfg = Release|Any CPU {96820DD6-0806-40A3-943A-7F2EF69E8EDB}.Release|x64.Build.0 = Release|Any CPU + {7290AC89-88DE-4CF8-946A-A1BB0E4C30EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7290AC89-88DE-4CF8-946A-A1BB0E4C30EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7290AC89-88DE-4CF8-946A-A1BB0E4C30EB}.Debug|x64.ActiveCfg = Debug|Any CPU + {7290AC89-88DE-4CF8-946A-A1BB0E4C30EB}.Debug|x64.Build.0 = Debug|Any CPU + {7290AC89-88DE-4CF8-946A-A1BB0E4C30EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7290AC89-88DE-4CF8-946A-A1BB0E4C30EB}.Release|Any CPU.Build.0 = Release|Any CPU + {7290AC89-88DE-4CF8-946A-A1BB0E4C30EB}.Release|x64.ActiveCfg = Release|Any CPU + {7290AC89-88DE-4CF8-946A-A1BB0E4C30EB}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE