2018-03-28 22:08:49 +00:00
|
|
|
|
using BotSharp.Core.Agents;
|
|
|
|
|
|
using BotSharp.Core.Intents;
|
|
|
|
|
|
using BotSharp.Core.Models;
|
2018-05-06 17:41:36 +00:00
|
|
|
|
using BotSharp.Core.Conversations;
|
2018-03-21 21:07:43 +00:00
|
|
|
|
using DotNetToolkit;
|
2017-12-30 20:26:35 +00:00
|
|
|
|
using EntityFrameworkCore.BootKit;
|
2018-03-21 21:07:43 +00:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
2017-12-18 05:30:20 +00:00
|
|
|
|
using Newtonsoft.Json;
|
2018-03-21 21:07:43 +00:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
2017-12-18 05:30:20 +00:00
|
|
|
|
using Newtonsoft.Json.Serialization;
|
|
|
|
|
|
using RestSharp;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
2018-03-21 21:07:43 +00:00
|
|
|
|
using System.Linq;
|
2017-12-18 05:30:20 +00:00
|
|
|
|
using System.Text;
|
2018-05-09 14:50:16 +00:00
|
|
|
|
using System.Text.RegularExpressions;
|
2017-12-18 05:30:20 +00:00
|
|
|
|
|
2018-03-28 22:08:49 +00:00
|
|
|
|
namespace BotSharp.Core.Engines
|
2017-12-18 05:30:20 +00:00
|
|
|
|
{
|
|
|
|
|
|
public static class RequestExtension
|
|
|
|
|
|
{
|
2018-03-28 22:08:49 +00:00
|
|
|
|
public static AIResponse TextRequest(this RasaAi rasa, string text, RequestExtras requestExtras)
|
|
|
|
|
|
{
|
|
|
|
|
|
return rasa.TextRequest(new AIRequest(text, requestExtras));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static AIResponse TextRequest(this RasaAi rasa, AIRequest request)
|
2017-12-18 05:30:20 +00:00
|
|
|
|
{
|
2018-03-21 21:07:43 +00:00
|
|
|
|
AIResponse aiResponse = new AIResponse();
|
2018-03-28 22:08:49 +00:00
|
|
|
|
Database dc = rasa.dc;
|
2017-12-18 05:30:20 +00:00
|
|
|
|
|
2018-04-06 22:39:30 +00:00
|
|
|
|
var result = CallRasa(rasa.agent.Id, request.Query.First(), rasa.agent.Id);
|
2018-05-07 00:48:10 +00:00
|
|
|
|
RasaResponse response = result.Data;
|
2018-04-06 22:39:30 +00:00
|
|
|
|
aiResponse.Id = Guid.NewGuid().ToString();
|
|
|
|
|
|
aiResponse.Lang = rasa.agent.Language;
|
|
|
|
|
|
aiResponse.Status = new AIResponseStatus { };
|
|
|
|
|
|
aiResponse.SessionId = rasa.AiConfig.SessionId;
|
|
|
|
|
|
aiResponse.Timestamp = DateTime.UtcNow;
|
2018-04-30 22:06:52 +00:00
|
|
|
|
|
2018-05-24 13:10:10 +00:00
|
|
|
|
var intentResponse = HandleIntentPerContextIn(rasa, request, result.Data);
|
2018-06-11 19:48:25 +00:00
|
|
|
|
bool missedRequiredField = HandleParameter(rasa.agent, intentResponse, response, request);
|
2018-04-30 22:06:52 +00:00
|
|
|
|
|
|
|
|
|
|
HandleMessage(intentResponse);
|
|
|
|
|
|
|
|
|
|
|
|
aiResponse.Result = new AIResponseResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Source = "agent",
|
|
|
|
|
|
ResolvedQuery = request.Query.First(),
|
2018-05-24 13:10:10 +00:00
|
|
|
|
Action = intentResponse?.Action,
|
|
|
|
|
|
Parameters = intentResponse?.Parameters?.ToDictionary(x => x.Name, x=> x.Value),
|
2018-04-30 22:06:52 +00:00
|
|
|
|
Score = response.Intent.Confidence,
|
2018-05-24 13:10:10 +00:00
|
|
|
|
Metadata = new AIResponseMetadata { IntentId = intentResponse?.IntentId, IntentName = intentResponse?.IntentName },
|
2018-04-30 22:06:52 +00:00
|
|
|
|
Fulfillment = new AIResponseFulfillment
|
|
|
|
|
|
{
|
2018-05-24 13:10:10 +00:00
|
|
|
|
Messages = intentResponse?.Messages?.Select(x => {
|
2018-04-30 22:06:52 +00:00
|
|
|
|
if (x.Type == AIResponseMessageType.Custom)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (new
|
|
|
|
|
|
{
|
|
|
|
|
|
x.Type,
|
2018-05-23 05:11:15 +00:00
|
|
|
|
x.Payload
|
2018-04-30 22:06:52 +00:00
|
|
|
|
}) as Object;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return (new { x.Type, x.Speech }) as Object;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}).ToList()
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
HandleContext(dc, rasa, intentResponse, aiResponse);
|
|
|
|
|
|
|
2018-05-09 14:50:16 +00:00
|
|
|
|
Console.WriteLine(JsonConvert.SerializeObject(aiResponse.Result));
|
|
|
|
|
|
|
2018-04-30 22:06:52 +00:00
|
|
|
|
return aiResponse;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-07 00:48:10 +00:00
|
|
|
|
private static IntentResponse HandleIntentPerContextIn(RasaAi rasa, AIRequest request, RasaResponse response)
|
|
|
|
|
|
{
|
|
|
|
|
|
Database dc = rasa.dc;
|
|
|
|
|
|
|
|
|
|
|
|
// Merge input contexts
|
|
|
|
|
|
var contexts = dc.Table<ConversationContext>()
|
|
|
|
|
|
.Where(x => x.ConversationId == rasa.AiConfig.SessionId && x.Lifespan > 0)
|
|
|
|
|
|
.ToList()
|
|
|
|
|
|
.Select(x => new AIContext { Name = x.Context.ToLower(), Lifespan = x.Lifespan })
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
contexts.AddRange(request.Contexts.Select(x => new AIContext { Name = x.Name.ToLower(), Lifespan = x.Lifespan }));
|
|
|
|
|
|
contexts = contexts.OrderBy(x => x.Name).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
// search all potential intents which input context included in contexts
|
|
|
|
|
|
var intents = rasa.agent.Intents.Where(it =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (contexts.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return it.Contexts.Count() == 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return it.Contexts.Count() == 0 ||
|
|
|
|
|
|
it.Contexts.Count(x => contexts.Select(ctx => ctx.Name).Contains(x.Name.ToLower())) == it.Contexts.Count;
|
|
|
|
|
|
}
|
|
|
|
|
|
}).OrderByDescending(x => x.Contexts.Count).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
if (response.IntentRanking == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
response.IntentRanking = new List<RasaResponseIntent>
|
|
|
|
|
|
{
|
|
|
|
|
|
response.Intent
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2018-06-11 19:48:25 +00:00
|
|
|
|
response.IntentRanking = response.IntentRanking
|
|
|
|
|
|
.Where(x => x.Confidence > decimal.Parse("0.2") && intents.Select(i => i.Name).Contains(x.Name)).ToList();
|
2018-05-07 00:48:10 +00:00
|
|
|
|
|
2018-06-11 19:48:25 +00:00
|
|
|
|
// add Default Fallback Intent
|
2018-05-24 13:10:10 +00:00
|
|
|
|
if (response.IntentRanking.Count == 0)
|
|
|
|
|
|
{
|
2018-06-11 19:48:25 +00:00
|
|
|
|
var defaultFallbackIntent = rasa.agent.Intents.FirstOrDefault(x => x.Name == "Default Fallback Intent");
|
|
|
|
|
|
response.IntentRanking.Add(new RasaResponseIntent
|
|
|
|
|
|
{
|
|
|
|
|
|
Name = defaultFallbackIntent.Name,
|
|
|
|
|
|
Confidence = decimal.Parse("0.8")
|
|
|
|
|
|
});
|
2018-05-24 13:10:10 +00:00
|
|
|
|
}
|
2018-05-07 00:48:10 +00:00
|
|
|
|
|
2018-06-11 19:48:25 +00:00
|
|
|
|
response.Intent = response.IntentRanking.First();
|
2018-05-24 13:10:10 +00:00
|
|
|
|
|
2018-06-11 19:48:25 +00:00
|
|
|
|
var intent = (dc.Table<Intent>().Where(x => x.AgentId == rasa.agent.Id && x.Name == response.Intent.Name)
|
|
|
|
|
|
.Include(x => x.Responses).ThenInclude(x => x.Contexts)
|
|
|
|
|
|
.Include(x => x.Responses).ThenInclude(x => x.Parameters)
|
|
|
|
|
|
.Include(x => x.Responses).ThenInclude(x => x.Messages)).First();
|
|
|
|
|
|
|
|
|
|
|
|
var intentResponse = ArrayHelper.GetRandom(intent.Responses);
|
|
|
|
|
|
intentResponse.IntentName = intent.Name;
|
|
|
|
|
|
|
|
|
|
|
|
return intentResponse;
|
2018-05-07 00:48:10 +00:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-06-11 19:48:25 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
///
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="agent"></param>
|
|
|
|
|
|
/// <param name="intentResponse"></param>
|
|
|
|
|
|
/// <param name="response"></param>
|
|
|
|
|
|
/// <param name="request"></param>
|
|
|
|
|
|
/// <returns>Required field is missed</returns>
|
|
|
|
|
|
private static bool HandleParameter(Agent agent, IntentResponse intentResponse, RasaResponse response, AIRequest request)
|
2018-04-30 22:06:52 +00:00
|
|
|
|
{
|
2018-06-11 19:48:25 +00:00
|
|
|
|
if (intentResponse == null) return false;
|
2018-05-24 13:10:10 +00:00
|
|
|
|
|
2018-04-06 22:39:30 +00:00
|
|
|
|
intentResponse.Parameters.ForEach(p => {
|
|
|
|
|
|
string query = request.Query.First();
|
|
|
|
|
|
var entity = response.Entities.FirstOrDefault(x => x.Entity == p.Name);
|
2018-04-30 22:06:52 +00:00
|
|
|
|
if (entity != null)
|
2018-04-06 22:39:30 +00:00
|
|
|
|
{
|
|
|
|
|
|
p.Value = query.Substring(entity.Start, entity.End - entity.Start);
|
|
|
|
|
|
}
|
2018-04-30 22:06:52 +00:00
|
|
|
|
|
2018-05-09 14:50:16 +00:00
|
|
|
|
// convert to Standard entity value
|
|
|
|
|
|
if (!String.IsNullOrEmpty(p.Value) && !p.DataType.StartsWith("@sys."))
|
|
|
|
|
|
{
|
|
|
|
|
|
p.Value = agent.Entities.FirstOrDefault(x => x.Name == p.Name).Entries.FirstOrDefault((entry) => {
|
|
|
|
|
|
return entry.Value.ToLower() == p.Value.ToLower() ||
|
|
|
|
|
|
entry.Synonyms.Select(synonym => synonym.Synonym.ToLower()).Contains(p.Value.ToLower());
|
|
|
|
|
|
})?.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-04-06 22:39:30 +00:00
|
|
|
|
// fixed entity per request
|
2018-04-30 22:06:52 +00:00
|
|
|
|
if (request.Entities != null)
|
2018-04-06 22:39:30 +00:00
|
|
|
|
{
|
|
|
|
|
|
var fixedEntity = request.Entities.FirstOrDefault(x => x.Name == p.Name);
|
|
|
|
|
|
if (fixedEntity != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (query.ToLower().Contains(fixedEntity.Entries.First().Value.ToLower()))
|
|
|
|
|
|
{
|
|
|
|
|
|
p.Value = fixedEntity.Entries.First().Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2018-06-11 19:48:25 +00:00
|
|
|
|
|
|
|
|
|
|
return intentResponse.Parameters.Any(x => x.Required && String.IsNullOrEmpty(x.Value));
|
2018-04-30 22:06:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void HandleMessage(IntentResponse intentResponse)
|
|
|
|
|
|
{
|
2018-05-24 13:10:10 +00:00
|
|
|
|
if (intentResponse == null) return;
|
|
|
|
|
|
|
2018-04-06 22:39:30 +00:00
|
|
|
|
intentResponse.Messages = intentResponse.Messages.OrderBy(x => x.UpdatedTime).ToList();
|
|
|
|
|
|
intentResponse.Messages.ToList()
|
|
|
|
|
|
.ForEach(msg =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (msg.Type == AIResponseMessageType.Custom)
|
|
|
|
|
|
{
|
2018-04-30 22:06:52 +00:00
|
|
|
|
|
2018-04-06 22:39:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
msg.Speech = msg.Speech.StartsWith("[") ?
|
|
|
|
|
|
ArrayHelper.GetRandom(msg.Speech.Substring(2, msg.Speech.Length - 4).Split("\",\"").ToList()) :
|
|
|
|
|
|
msg.Speech;
|
2018-05-09 14:50:16 +00:00
|
|
|
|
|
|
|
|
|
|
msg.Speech = ReplaceParameters4Response(intentResponse.Parameters, msg.Speech);
|
2018-04-06 22:39:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2018-04-30 22:06:52 +00:00
|
|
|
|
}
|
2018-04-06 22:39:30 +00:00
|
|
|
|
|
2018-05-09 14:50:16 +00:00
|
|
|
|
private static string ReplaceParameters4Response(List<IntentResponseParameter> parameters, string text)
|
|
|
|
|
|
{
|
|
|
|
|
|
var reg = new Regex(@"\$\w+");
|
|
|
|
|
|
|
|
|
|
|
|
reg.Matches(text).ToList().ForEach(token => {
|
|
|
|
|
|
text = text.Replace(token.Value, parameters.FirstOrDefault(x => x.Name == token.Value.Substring(1))?.Value.ToString());
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return text;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-04-30 22:06:52 +00:00
|
|
|
|
private static void HandleContext(Database dc, RasaAi rasa, IntentResponse intentResponse, AIResponse aiResponse)
|
|
|
|
|
|
{
|
2018-05-24 13:10:10 +00:00
|
|
|
|
if (intentResponse == null) return;
|
|
|
|
|
|
|
2018-04-06 22:39:30 +00:00
|
|
|
|
// Merge context lifespan
|
|
|
|
|
|
// override if exists, otherwise add, delete if lifespan is zero
|
|
|
|
|
|
dc.DbTran(() =>
|
|
|
|
|
|
{
|
2018-05-06 17:41:36 +00:00
|
|
|
|
var sessionContexts = dc.Table<ConversationContext>().Where(x => x.ConversationId == rasa.AiConfig.SessionId).ToList();
|
2018-04-06 22:39:30 +00:00
|
|
|
|
|
|
|
|
|
|
// minus 1 round
|
|
|
|
|
|
sessionContexts.Where(x => !intentResponse.Contexts.Select(ctx => ctx.Name).Contains(x.Context))
|
|
|
|
|
|
.ToList()
|
|
|
|
|
|
.ForEach(ctx => ctx.Lifespan = ctx.Lifespan - 1);
|
|
|
|
|
|
|
|
|
|
|
|
intentResponse.Contexts.ForEach(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var session1 = sessionContexts.FirstOrDefault(x => x.Context == ctx.Name);
|
|
|
|
|
|
|
|
|
|
|
|
if (session1 != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ctx.Lifespan == 0)
|
|
|
|
|
|
{
|
2018-05-06 17:41:36 +00:00
|
|
|
|
dc.Table<ConversationContext>().Remove(session1);
|
2018-04-06 22:39:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
session1.Lifespan = ctx.Lifespan;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2018-05-06 17:41:36 +00:00
|
|
|
|
dc.Table<ConversationContext>().Add(new ConversationContext
|
2018-04-06 22:39:30 +00:00
|
|
|
|
{
|
2018-05-06 17:41:36 +00:00
|
|
|
|
ConversationId = rasa.AiConfig.SessionId,
|
2018-04-06 22:39:30 +00:00
|
|
|
|
Context = ctx.Name,
|
|
|
|
|
|
Lifespan = ctx.Lifespan
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2018-05-06 17:41:36 +00:00
|
|
|
|
aiResponse.Result.Contexts = dc.Table<ConversationContext>()
|
2018-05-07 00:48:10 +00:00
|
|
|
|
.Where(x => x.Lifespan > 0 && x.ConversationId == rasa.AiConfig.SessionId)
|
2018-04-06 22:39:30 +00:00
|
|
|
|
.Select(x => new AIContext { Name = x.Context.ToLower(), Lifespan = x.Lifespan })
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static IRestResponse<RasaResponse> CallRasa(string projectId, string text, string model)
|
|
|
|
|
|
{
|
2018-05-06 17:41:36 +00:00
|
|
|
|
var client = new RestClient($"{Database.Configuration.GetSection("Rasa:Nlu").Value}");
|
2018-04-06 22:39:30 +00:00
|
|
|
|
|
|
|
|
|
|
var rest = new RestRequest("parse", Method.POST);
|
|
|
|
|
|
string json = JsonConvert.SerializeObject(new { Project = projectId, Q = text, Model = model },
|
|
|
|
|
|
new JsonSerializerSettings
|
|
|
|
|
|
{
|
|
|
|
|
|
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
|
|
|
|
|
});
|
|
|
|
|
|
rest.AddParameter("application/json", json, ParameterType.RequestBody);
|
|
|
|
|
|
|
|
|
|
|
|
return client.Execute<RasaResponse>(rest);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static AIResponse TextRequestPerContexts(this RasaAi rasa, AIRequest request)
|
|
|
|
|
|
{
|
|
|
|
|
|
AIResponse aiResponse = new AIResponse();
|
|
|
|
|
|
RasaResponse response = null;
|
|
|
|
|
|
Database dc = rasa.dc;
|
|
|
|
|
|
|
|
|
|
|
|
// Merge input contexts
|
2018-05-06 17:41:36 +00:00
|
|
|
|
var contexts = dc.Table<ConversationContext>()
|
|
|
|
|
|
.Where(x => x.ConversationId == rasa.AiConfig.SessionId && x.Lifespan > 0)
|
2018-04-06 22:39:30 +00:00
|
|
|
|
.ToList()
|
|
|
|
|
|
.Select(x => new AIContext { Name = x.Context.ToLower(), Lifespan = x.Lifespan })
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
contexts.AddRange(request.Contexts.Select(x => new AIContext { Name = x.Name.ToLower(), Lifespan = x.Lifespan }));
|
|
|
|
|
|
contexts = contexts.OrderBy(x => x.Name).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
// search all potential intents which input context included in contexts
|
|
|
|
|
|
var intents = rasa.agent.Intents.Where(it =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (contexts.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return it.Contexts.Count() == 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return it.Contexts.Count() > 0 &&
|
|
|
|
|
|
it.Contexts.Count(x => contexts.Select(ctx => ctx.Name).Contains(x.Name.ToLower())) == it.Contexts.Count;
|
|
|
|
|
|
}
|
|
|
|
|
|
}).OrderByDescending(x => x.Contexts.Count).ToList();
|
|
|
|
|
|
|
2018-03-28 22:08:49 +00:00
|
|
|
|
// training per request contexts
|
2018-03-21 21:07:43 +00:00
|
|
|
|
{
|
2018-03-28 22:08:49 +00:00
|
|
|
|
string contextId = $"{String.Join(',', contexts.Select(x => x.Name))}".GetMd5Hash();
|
2018-03-21 21:07:43 +00:00
|
|
|
|
string modelName = dc.Table<ContextModelMapping>().FirstOrDefault(x => x.ContextId == contextId)?.ModelName;
|
|
|
|
|
|
// need training
|
|
|
|
|
|
if (String.IsNullOrEmpty(modelName))
|
|
|
|
|
|
{
|
2018-03-28 22:08:49 +00:00
|
|
|
|
request.Contexts = contexts.Select(x => new AIContext { Name = x.Name.ToLower() })
|
|
|
|
|
|
.OrderBy(x => x.Name)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
2018-03-21 21:07:43 +00:00
|
|
|
|
dc.DbTran(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
modelName = TrainWithContexts(rasa, dc, request, contextId);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-28 22:08:49 +00:00
|
|
|
|
var result = CallRasa(rasa.agent.Id, request.Query.First(), modelName);
|
2018-03-21 21:07:43 +00:00
|
|
|
|
|
2018-03-28 22:08:49 +00:00
|
|
|
|
if (result.Data.Intent != null)
|
2018-03-21 21:07:43 +00:00
|
|
|
|
{
|
|
|
|
|
|
response = result.Data;
|
|
|
|
|
|
}
|
2018-03-28 22:08:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Max contexts match
|
|
|
|
|
|
if (response == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var it in intents)
|
|
|
|
|
|
{
|
|
|
|
|
|
request.Contexts = it.Contexts.Select(x => new AIContext { Name = x.Name.ToLower() })
|
|
|
|
|
|
.OrderBy(x => x.Name)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
string contextId = $"{String.Join(',', request.Contexts.Select(x => x.Name))}".GetMd5Hash();
|
|
|
|
|
|
|
|
|
|
|
|
string modelName = dc.Table<ContextModelMapping>().FirstOrDefault(x => x.ContextId == contextId)?.ModelName;
|
|
|
|
|
|
|
|
|
|
|
|
// need training
|
|
|
|
|
|
if (String.IsNullOrEmpty(modelName))
|
|
|
|
|
|
{
|
|
|
|
|
|
dc.DbTran(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
modelName = TrainWithContexts(rasa, dc, request, contextId);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var result = CallRasa(rasa.agent.Id, request.Query.First(), modelName);
|
|
|
|
|
|
|
|
|
|
|
|
if (result.Data.Intent != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
response = result.Data;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2018-03-21 21:07:43 +00:00
|
|
|
|
|
|
|
|
|
|
var intent = (dc.Table<Intent>().Where(x => x.Name == response.Intent.Name)
|
|
|
|
|
|
.Include(x => x.Responses).ThenInclude(x => x.Contexts)
|
|
|
|
|
|
.Include(x => x.Responses).ThenInclude(x => x.Parameters)
|
|
|
|
|
|
.Include(x => x.Responses).ThenInclude(x => x.Messages)).First();
|
|
|
|
|
|
|
|
|
|
|
|
var intentResponse = ArrayHelper.GetRandom(intent.Responses);
|
|
|
|
|
|
aiResponse.Id = Guid.NewGuid().ToString();
|
|
|
|
|
|
aiResponse.Lang = rasa.agent.Language;
|
|
|
|
|
|
aiResponse.Status = new AIResponseStatus { };
|
2018-03-28 22:08:49 +00:00
|
|
|
|
aiResponse.SessionId = rasa.AiConfig.SessionId;
|
2018-03-21 21:07:43 +00:00
|
|
|
|
aiResponse.Timestamp = DateTime.UtcNow;
|
2018-03-28 22:08:49 +00:00
|
|
|
|
intentResponse.Messages = intentResponse.Messages.OrderBy(x => x.UpdatedTime).ToList();
|
|
|
|
|
|
intentResponse.Messages.ToList()
|
2018-03-21 21:07:43 +00:00
|
|
|
|
.ForEach(msg =>
|
|
|
|
|
|
{
|
2018-03-28 22:08:49 +00:00
|
|
|
|
if (msg.Type == AIResponseMessageType.Custom)
|
|
|
|
|
|
{
|
2018-04-06 22:39:30 +00:00
|
|
|
|
|
2018-03-28 22:08:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
msg.Speech = msg.Speech.StartsWith("[") ?
|
|
|
|
|
|
ArrayHelper.GetRandom(msg.Speech.Substring(2, msg.Speech.Length - 4).Split("\",\"").ToList()) :
|
|
|
|
|
|
msg.Speech;
|
|
|
|
|
|
}
|
2017-12-30 20:26:35 +00:00
|
|
|
|
});
|
2017-12-18 05:30:20 +00:00
|
|
|
|
|
2018-03-21 21:07:43 +00:00
|
|
|
|
aiResponse.Result = new AIResponseResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Source = "agent",
|
|
|
|
|
|
ResolvedQuery = request.Query.First(),
|
|
|
|
|
|
Action = intentResponse.Action,
|
2018-04-06 22:39:30 +00:00
|
|
|
|
Parameters = new Dictionary<string, string>(),
|
2018-03-21 21:07:43 +00:00
|
|
|
|
Score = response.Intent.Confidence,
|
|
|
|
|
|
Metadata = new AIResponseMetadata { IntentId = intent.Id, IntentName = intent.Name },
|
|
|
|
|
|
Fulfillment = new AIResponseFulfillment
|
|
|
|
|
|
{
|
2018-03-28 22:08:49 +00:00
|
|
|
|
Messages = intentResponse.Messages.Select(x => {
|
|
|
|
|
|
if (x.Type == AIResponseMessageType.Custom)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (new
|
|
|
|
|
|
{
|
|
|
|
|
|
x.Type,
|
2018-05-23 05:11:15 +00:00
|
|
|
|
x.Payload
|
2018-03-28 22:08:49 +00:00
|
|
|
|
}) as Object;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return (new { x.Type, x.Speech }) as Object;
|
|
|
|
|
|
}
|
2018-04-06 22:39:30 +00:00
|
|
|
|
|
2018-03-28 22:08:49 +00:00
|
|
|
|
}).ToList()
|
2018-03-21 21:07:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Merge context lifespan
|
|
|
|
|
|
// override if exists, otherwise add, delete if lifespan is zero
|
|
|
|
|
|
dc.DbTran(() =>
|
|
|
|
|
|
{
|
2018-05-06 17:41:36 +00:00
|
|
|
|
var sessionContexts = dc.Table<ConversationContext>().Where(x => x.ConversationId == rasa.AiConfig.SessionId).ToList();
|
2018-03-21 21:07:43 +00:00
|
|
|
|
|
|
|
|
|
|
// minus 1 round
|
|
|
|
|
|
sessionContexts.Where(x => !intentResponse.Contexts.Select(ctx => ctx.Name).Contains(x.Context))
|
|
|
|
|
|
.ToList()
|
|
|
|
|
|
.ForEach(ctx => ctx.Lifespan = ctx.Lifespan - 1);
|
|
|
|
|
|
|
|
|
|
|
|
intentResponse.Contexts.ForEach(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var session1 = sessionContexts.FirstOrDefault(x => x.Context == ctx.Name);
|
|
|
|
|
|
|
|
|
|
|
|
if (session1 != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ctx.Lifespan == 0)
|
|
|
|
|
|
{
|
2018-05-06 17:41:36 +00:00
|
|
|
|
dc.Table<ConversationContext>().Remove(session1);
|
2018-03-21 21:07:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
session1.Lifespan = ctx.Lifespan;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2018-05-06 17:41:36 +00:00
|
|
|
|
dc.Table<ConversationContext>().Add(new ConversationContext
|
2018-03-21 21:07:43 +00:00
|
|
|
|
{
|
2018-05-06 17:41:36 +00:00
|
|
|
|
ConversationId = rasa.AiConfig.SessionId,
|
2018-03-21 21:07:43 +00:00
|
|
|
|
Context = ctx.Name,
|
|
|
|
|
|
Lifespan = ctx.Lifespan
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2018-05-06 17:41:36 +00:00
|
|
|
|
aiResponse.Result.Contexts = dc.Table<ConversationContext>()
|
|
|
|
|
|
.Where(x => x.ConversationId == rasa.AiConfig.SessionId)
|
2018-03-28 22:08:49 +00:00
|
|
|
|
.Select(x => new AIContext { Name = x.Context.ToLower(), Lifespan = x.Lifespan })
|
2018-03-21 21:07:43 +00:00
|
|
|
|
.ToArray();
|
2017-12-18 05:30:20 +00:00
|
|
|
|
|
2018-03-21 21:07:43 +00:00
|
|
|
|
return aiResponse;
|
2017-12-18 05:30:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-30 20:26:35 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Need two categories at least
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="console"></param>
|
|
|
|
|
|
/// <param name="dc"></param>
|
2018-03-21 21:07:43 +00:00
|
|
|
|
/// <param name="request"></param>
|
|
|
|
|
|
/// <param name="contextId"></param>
|
2017-12-30 20:26:35 +00:00
|
|
|
|
/// <returns></returns>
|
2018-03-21 21:07:43 +00:00
|
|
|
|
public static string TrainWithContexts(this RasaAi console, Database dc, AIRequest request, String contextId)
|
2017-12-18 05:30:20 +00:00
|
|
|
|
{
|
2018-04-06 22:39:30 +00:00
|
|
|
|
var corpus = console.agent.GrabCorpusPerContexts(dc, request.Contexts);
|
2018-03-21 21:07:43 +00:00
|
|
|
|
|
2018-03-28 22:08:49 +00:00
|
|
|
|
corpus.UserSays.Add(new RasaIntentExpression
|
2018-03-21 21:07:43 +00:00
|
|
|
|
{
|
|
|
|
|
|
Intent = "Welcome",
|
|
|
|
|
|
Text = "Hi"
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2018-03-28 22:08:49 +00:00
|
|
|
|
corpus.UserSays.Add(new RasaIntentExpression
|
2018-03-21 21:07:43 +00:00
|
|
|
|
{
|
|
|
|
|
|
Intent = "Welcome",
|
|
|
|
|
|
Text = "Hey"
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2018-03-28 22:08:49 +00:00
|
|
|
|
corpus.UserSays.Add(new RasaIntentExpression
|
2018-03-21 21:07:43 +00:00
|
|
|
|
{
|
|
|
|
|
|
Intent = "Welcome",
|
|
|
|
|
|
Text = "Hello"
|
|
|
|
|
|
});
|
2017-12-18 05:30:20 +00:00
|
|
|
|
|
2017-12-18 13:31:15 +00:00
|
|
|
|
string json = JsonConvert.SerializeObject(new { rasa_nlu_data = corpus },
|
|
|
|
|
|
new JsonSerializerSettings
|
2017-12-18 05:30:20 +00:00
|
|
|
|
{
|
2018-04-02 22:42:11 +00:00
|
|
|
|
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
|
|
|
|
|
NullValueHandling = NullValueHandling.Ignore
|
2017-12-18 13:31:15 +00:00
|
|
|
|
});
|
2017-12-18 05:30:20 +00:00
|
|
|
|
|
2018-03-28 22:08:49 +00:00
|
|
|
|
var client = new RestClient($"{Database.Configuration.GetSection("Rasa:Host").Value}");
|
2018-03-21 21:07:43 +00:00
|
|
|
|
var rest = new RestRequest("train", Method.POST);
|
|
|
|
|
|
rest.AddQueryParameter("project", console.agent.Id);
|
|
|
|
|
|
rest.AddParameter("application/json", json, ParameterType.RequestBody);
|
|
|
|
|
|
|
|
|
|
|
|
var response = client.Execute(rest);
|
|
|
|
|
|
|
|
|
|
|
|
if (response.IsSuccessful)
|
|
|
|
|
|
{
|
2018-04-02 22:42:11 +00:00
|
|
|
|
var result = JObject.Parse(response.Content);
|
|
|
|
|
|
|
2018-03-21 21:07:43 +00:00
|
|
|
|
string modelName = result["info"].Value<String>().Split(": ")[1];
|
|
|
|
|
|
|
|
|
|
|
|
dc.Table<ContextModelMapping>().Add(new ContextModelMapping
|
|
|
|
|
|
{
|
|
|
|
|
|
AgentId = console.agent.Id,
|
|
|
|
|
|
ModelName = modelName,
|
|
|
|
|
|
ContextId = contextId
|
|
|
|
|
|
});
|
2017-12-18 05:30:20 +00:00
|
|
|
|
|
2018-03-21 21:07:43 +00:00
|
|
|
|
return modelName;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2018-04-02 22:42:11 +00:00
|
|
|
|
var result = JObject.Parse(response.Content);
|
|
|
|
|
|
|
2018-03-21 21:07:43 +00:00
|
|
|
|
Console.WriteLine(result["error"]);
|
2017-12-18 05:30:20 +00:00
|
|
|
|
|
2018-03-21 21:07:43 +00:00
|
|
|
|
return String.Empty;
|
|
|
|
|
|
}
|
2017-12-18 05:30:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|