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