refactor IBotPlatform structure

This commit is contained in:
Oceania2018 2018-07-12 10:07:05 -05:00
parent 9e12cdd06a
commit 1c5b714603
23 changed files with 664 additions and 158 deletions

View file

@ -1,4 +1,5 @@
using System;
using BotSharp.Core.Models;
using System;
using System.Collections.Generic;
using System.Text;
@ -6,5 +7,7 @@ namespace BotSharp.Core.Engines
{
public interface IBotPlatform
{
AIResponse TextRequest(AIRequest request);
void Train();
}
}

View file

@ -67,5 +67,10 @@ namespace BotSharp.Core.Agents
[Required]
public DateTime CreatedDate { get; set; }
public Boolean IsSkillSet { get; set; }
[ForeignKey("AgentId")]
public AgentMlConfig MlConfig { get; set; }
}
}

View file

@ -31,6 +31,7 @@ namespace BotSharp.Core.Agents
return dc.Table<Agent>()
.Include(x => x.Intents).ThenInclude(x => x.Contexts)
.Include(x => x.Entities).ThenInclude(x => x.Entries).ThenInclude(x => x.Synonyms)
.Include(x => x.MlConfig)
.FirstOrDefault(x => x.ClientAccessToken == aiConfig.ClientAccessToken || x.DeveloperAccessToken == aiConfig.ClientAccessToken);
}

View file

@ -7,15 +7,21 @@ using System.Text;
namespace BotSharp.Core.Agents
{
[Table("Bot_AgentTrainConfig")]
public class AgentTrainConfig : DbRecord, IDbRecord
[Table("Bot_AgentMlConfig")]
public class AgentMlConfig : DbRecord, IDbRecord
{
[Required]
[StringLength(36)]
public String AgentId { get; set; }
public decimal ClassificationThreshould { get; set; }
[Required]
public decimal MinConfidence { get; set; }
[Required]
[MaxLength(64)]
public string CustomClassifierMode { get; set; }
[MaxLength(64)]
public String Pipeline { get; set; }
}
}

View file

@ -32,11 +32,15 @@
<DefineConstants>TRACE;MODEL_PER_CONTEXTS</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Engines\Dialogflow\ApiAi.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DotNetToolkit" Version="1.4.0" />
<PackageReference Include="EntityFrameworkCore.BootKit" Version="1.6.0" />
<PackageReference Include="EntityFrameworkCore.BootKit" Version="1.6.3" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="RestSharp" Version="106.3.0" />
<PackageReference Include="RestSharp" Version="106.3.1" />
</ItemGroup>
<ItemGroup>

View file

@ -14,7 +14,6 @@ namespace BotSharp.Core.Conversations
[StringLength(36)]
public String AgentId { get; set; }
[Required]
[StringLength(36)]
public String UserId { get; set; }

View file

@ -6,10 +6,10 @@ namespace BotSharp.Core.Models
{
public class AIConfiguration
{
private const string SERVICE_PROD_URL = "";
private const string SERVICE_DEV_URL = "";
private const string SERVICE_PROD_URL = "https://api.api.ai/v1/";
private const string SERVICE_DEV_URL = "https://dev.api.ai/api/";
private const string CURRENT_PROTOCOL_VERSION = "20190322";
private const string CURRENT_PROTOCOL_VERSION = "20150910";
public string ClientAccessToken { get; private set; }

View file

@ -0,0 +1,179 @@
using BotSharp.Core.Engines.Dialogflow.Http;
using BotSharp.Core.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
namespace BotSharp.Core.Engines.Dialogflow
{
public class AIDataService
{
private readonly AIConfiguration config;
public string SessionId { get; }
public AIDataService(AIConfiguration config)
{
this.config = config;
if (string.IsNullOrEmpty(config.SessionId))
{
SessionId = Guid.NewGuid().ToString();
}
else
{
SessionId = config.SessionId;
}
}
public AIResponse Request(AIRequest request)
{
request.Language = config.Language.code;
request.Timezone = TimeZone.CurrentTimeZone.StandardName;
request.SessionId = SessionId;
try
{
var httpRequest = (HttpWebRequest)WebRequest.Create(config.RequestUrl);
httpRequest.Method = "POST";
httpRequest.ContentType = "application/json; charset=utf-8";
httpRequest.Accept = "application/json";
httpRequest.Headers.Add("Authorization", "Bearer " + config.ClientAccessToken);
var jsonSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
var jsonRequest = JsonConvert.SerializeObject(request, Formatting.None, jsonSettings);
if (config.DebugLog)
{
Debug.WriteLine("Request: " + jsonRequest);
}
using (var streamWriter = new StreamWriter(httpRequest.GetRequestStream()))
{
streamWriter.Write(jsonRequest);
streamWriter.Close();
}
var httpResponse = httpRequest.GetResponse() as HttpWebResponse;
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
if (config.DebugLog)
{
Debug.WriteLine("Response: " + result);
}
var aiResponse = JsonConvert.DeserializeObject<AIResponse>(result);
CheckForErrors(aiResponse);
return aiResponse;
}
}
catch (Exception e)
{
throw new AIServiceException(e);
}
}
public AIResponse VoiceRequest(Stream voiceStream, RequestExtras requestExtras = null)
{
var request = new AIRequest();
request.Language = config.Language.code;
request.Timezone = TimeZone.CurrentTimeZone.StandardName;
request.SessionId = SessionId;
if (requestExtras != null)
{
requestExtras.CopyTo(request);
}
try
{
var httpRequest = (HttpWebRequest)WebRequest.Create(config.RequestUrl);
httpRequest.Method = "POST";
httpRequest.Accept = "application/json";
httpRequest.Headers.Add("Authorization", "Bearer " + config.ClientAccessToken);
var jsonSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
var jsonRequest = JsonConvert.SerializeObject(request, Formatting.None, jsonSettings);
if (config.DebugLog)
{
Debug.WriteLine("Request: " + jsonRequest);
}
var multipartClient = new MultipartHttpClient(httpRequest);
multipartClient.connect();
multipartClient.addStringPart("request", jsonRequest);
multipartClient.addFilePart("voiceData", "voice.wav", voiceStream);
multipartClient.finish();
var responseJsonString = multipartClient.getResponse();
if (config.DebugLog)
{
Debug.WriteLine("Response: " + responseJsonString);
}
var aiResponse = JsonConvert.DeserializeObject<AIResponse>(responseJsonString);
CheckForErrors(aiResponse);
return aiResponse;
}
catch (Exception e)
{
throw new AIServiceException(e);
}
}
public bool ResetContexts()
{
var cleanRequest = new AIRequest("empty_query_for_resetting_contexts");
cleanRequest.ResetContexts = true;
try
{
var response = Request(cleanRequest);
return !response.IsError;
}
catch (AIServiceException e)
{
Debug.WriteLine("Exception while contexts clean." + e);
return false;
}
}
static void CheckForErrors(AIResponse aiResponse)
{
if (aiResponse == null)
{
throw new AIServiceException("API.AI response parsed as null. Check debug log for details.");
}
if (aiResponse.IsError)
{
throw new AIServiceException(aiResponse);
}
}
}
}

View file

@ -7,6 +7,7 @@ namespace BotSharp.Core.Models
public enum AIResponseMessageType
{
Text = 0,
Card = 1,
Custom = 4
}
}

View file

@ -0,0 +1,49 @@
using BotSharp.Core.Models;
using System;
using System.Collections.Generic;
using System.Text;
namespace BotSharp.Core.Engines.Dialogflow
{
public class AIServiceException : Exception
{
public AIResponse Response { get; set; }
public AIServiceException()
{
}
public AIServiceException(string message) : base(message)
{
}
public AIServiceException(string message, Exception innerException) : base(message, innerException)
{
}
public AIServiceException(Exception e) : base(e.Message, e)
{
}
public AIServiceException(AIResponse response)
{
Response = response;
}
public override string Message
{
get
{
if (Response != null && Response.IsError)
{
if (!string.IsNullOrEmpty(Response.Status.ErrorDetails))
{
return Response.Status.ErrorDetails;
}
}
return base.Message;
}
}
}
}

View file

@ -33,7 +33,12 @@ namespace BotSharp.Core.Engines
agent.Id = Guid.NewGuid().ToString();
agent.Name = agentId;
return agent.ToObject<Agent>();
var result = agent.ToObject<Agent>();
result.MlConfig = agent.ToObject<AgentMlConfig>();
result.MlConfig.MinConfidence = agent.MlMinConfidence;
result.MlConfig.AgentId = agent.Id;
return result;
}
public void LoadCustomEntities(Agent agent, string agentDir)
@ -161,6 +166,8 @@ namespace BotSharp.Core.Engines
Lifespan = x.Lifespan
}).ToList();
int millSeconds = 0;
newResponse.Messages = res.MessageList.Where(x => x.Speech != null || x.Payload != null)
.Select(x =>
{
@ -170,7 +177,8 @@ namespace BotSharp.Core.Engines
{
Payload = JObject.FromObject(x.Payload),
PayloadJson = JsonConvert.SerializeObject(x.Payload),
Type = x.Type
Type = x.Type,
UpdatedTime = DateTime.UtcNow.AddMilliseconds(millSeconds++)
};
}
else
@ -182,7 +190,8 @@ namespace BotSharp.Core.Engines
return new IntentResponseMessage
{
Speech = speech,
Type = x.Type
Type = x.Type,
UpdatedTime = DateTime.UtcNow.AddMilliseconds(millSeconds++)
};
}

View file

@ -0,0 +1,61 @@
using BotSharp.Core.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace BotSharp.Core.Engines.Dialogflow
{
public class ApiAi : ApiAiBase, IBotPlatform
{
private readonly AIConfiguration config;
private readonly AIDataService dataService;
public ApiAi(AIConfiguration config)
{
this.config = config;
dataService = new AIDataService(this.config);
}
public AIResponse TextRequest(string text)
{
if (string.IsNullOrEmpty(text))
{
throw new ArgumentNullException("text");
}
return TextRequest(new AIRequest(text));
}
public AIResponse TextRequest(AIRequest request)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
return dataService.Request(request);
}
public AIResponse TextRequest(string text, RequestExtras requestExtras)
{
if (string.IsNullOrEmpty(text))
{
throw new ArgumentNullException("text");
}
return TextRequest(new AIRequest(text, requestExtras));
}
public AIResponse VoiceRequest(Stream voiceStream, RequestExtras requestExtras = null)
{
if (config.Language == SupportedLanguage.Italian)
{
throw new AIServiceException("Sorry, but Italian language now is not supported in Speaktoit recognition. Please use some another speech recognition engine.");
}
return dataService.VoiceRequest(voiceStream, requestExtras);
}
}
}

View file

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BotSharp.Core.Engines.Dialogflow
{
public class ApiAiBase
{
protected float[] TrimSilence(float[] samples)
{
if (samples == null)
{
return null;
}
const float min = 0.000001f;
var startIndex = 0;
var endIndex = samples.Length;
for (var i = 0; i < samples.Length; i++)
{
if (Math.Abs(samples[i]) > min)
{
startIndex = i;
break;
}
}
for (var i = samples.Length - 1; i > 0; i--)
{
if (Math.Abs(samples[i]) > min)
{
endIndex = i;
break;
}
}
if (endIndex <= startIndex)
{
return null;
}
var result = new float[endIndex - startIndex];
Array.Copy(samples, startIndex, result, 0, endIndex - startIndex);
return result;
}
protected static byte[] ConvertArrayShortToBytes(short[] array)
{
var numArray = new byte[array.Length * 2];
Buffer.BlockCopy(array, 0, numArray, 0, numArray.Length);
return numArray;
}
protected static short[] ConvertIeeeToPcm16(float[] source)
{
var resultBuffer = new short[source.Length];
for (var i = 0; i < source.Length; i++)
{
var f = source[i] * 32768f;
if (f > (double)short.MaxValue)
f = short.MaxValue;
else if (f < (double)short.MinValue)
f = short.MinValue;
resultBuffer[i] = Convert.ToInt16(f);
}
return resultBuffer;
}
}
}

View file

@ -11,6 +11,10 @@ namespace BotSharp.Core.Adapters.Dialogflow
public String Description { get; set; }
public Boolean Published { get; set; }
public String DefaultTimezone { get; set; }
public String Language { get; set; }
public decimal MlMinConfidence { get; set; }
public string CustomClassifierMode { get; set; }
}
}

View file

@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
namespace BotSharp.Core.Engines.Dialogflow.Http
{
public class MultipartHttpClient
{
private const string delimiter = "--";
private string boundary = "SwA" + DateTime.UtcNow.Ticks.ToString("x") + "SwA";
private HttpWebRequest request;
private BinaryWriter os;
public MultipartHttpClient(HttpWebRequest request)
{
this.request = request;
}
public void connect()
{
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.SendChunked = true;
request.KeepAlive = true;
os = new BinaryWriter(request.GetRequestStream(), Encoding.UTF8);
}
public void addStringPart(string paramName, string data)
{
WriteString(delimiter + boundary + "\r\n");
WriteString("Content-Type: application/json\r\n");
WriteString("Content-Disposition: form-data; name=\"" + paramName + "\"\r\n");
WriteString("\r\n" + data + "\r\n");
}
public void addFilePart(string paramName, string fileName, Stream data)
{
WriteString(delimiter + boundary + "\r\n");
WriteString("Content-Disposition: form-data; name=\"" + paramName + "\"; filename=\"" + fileName + "\"\r\n");
WriteString("Content-Type: audio/wav\r\n");
WriteString("\r\n");
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesActuallyRead;
bytesActuallyRead = data.Read(buffer, 0, bufferSize);
while (bytesActuallyRead > 0)
{
os.Write(buffer, 0, bytesActuallyRead);
bytesActuallyRead = data.Read(buffer, 0, bufferSize);
}
WriteString("\r\n");
}
public void finish()
{
WriteString(delimiter + boundary + delimiter + "\r\n");
os.Close();
}
private void WriteString(string str)
{
os.Write(Encoding.UTF8.GetBytes(str));
}
public string getResponse()
{
try
{
var httpResponse = request.GetResponse() as HttpWebResponse;
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
return result;
}
}
catch (WebException we)
{
using (var stream = we.Response.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
}
}
}

View file

@ -42,47 +42,82 @@ namespace BotSharp.Core.Engines
aiConfig.DevMode = agent.DeveloperAccessToken == aiConfig.ClientAccessToken;
}
public string Train()
public AIResponse TextRequest(AIRequest request)
{
var client = new RestClient($"{Database.Configuration.GetSection("Rasa:Nlu").Value}");
var rest = new RestRequest("train", Method.POST);
rest.AddQueryParameter("project", agent.Id);
AIResponse aiResponse = new AIResponse();
var corpus = agent.GrabCorpus(dc);
#if MODEL_PER_CONTEXTS
string model = RasaRequestExtension.GetModelPerContexts(agent, AiConfig, request, dc);
var result = CallRasa(agent.Id, request.Query.First(), model);
#else
var result = CallRasa(rasa.agent.Id, request.Query.First(), rasa.agent.Id);
#endif
result.Content.Log();
string json = JsonConvert.SerializeObject(new { rasa_nlu_data = corpus },
new JsonSerializerSettings
RasaResponse response = result.Data;
aiResponse.Id = Guid.NewGuid().ToString();
aiResponse.Lang = agent.Language;
aiResponse.Status = new AIResponseStatus { };
aiResponse.SessionId = AiConfig.SessionId;
aiResponse.Timestamp = DateTime.UtcNow;
var intentResponse = RasaRequestExtension.HandleIntentPerContextIn(agent, AiConfig, request, result.Data, dc);
RasaRequestExtension.HandleParameter(agent, intentResponse, response, request);
RasaRequestExtension.HandleMessage(intentResponse);
aiResponse.Result = new AIResponseResult
{
Source = "agent",
ResolvedQuery = request.Query.First(),
Action = intentResponse?.Action,
Parameters = intentResponse?.Parameters?.ToDictionary(x => x.Name, x => x.Value),
Score = response.Intent.Confidence,
Metadata = new AIResponseMetadata { IntentId = intentResponse?.IntentId, IntentName = intentResponse?.IntentName },
Fulfillment = new AIResponseFulfillment
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore
});
Messages = intentResponse?.Messages?.Select(x => {
if (x.Type == AIResponseMessageType.Custom)
{
return (new
{
x.Type,
Payload = JsonConvert.DeserializeObject(x.PayloadJson)
}) as Object;
}
else
{
return (new { x.Type, x.Speech }) as Object;
}
string trainingConfig = agent.Language == "zh" ? "config_jieba_mitie_sklearn.yml" : "config_spacy.yml";
string body = File.ReadAllText($"{Database.ContentRootPath}{Path.DirectorySeparatorChar}Settings{Path.DirectorySeparatorChar}{trainingConfig}");
body = $"{body}\r\ndata: {json}";
rest.AddParameter("application/x-yml", body, ParameterType.RequestBody);
}).ToList()
}
};
var response = client.Execute(rest);
RasaRequestExtension.HandleContext(dc, AiConfig, intentResponse, aiResponse);
if (response.IsSuccessful)
{
var result = JObject.Parse(response.Content);
Console.WriteLine(JsonConvert.SerializeObject(aiResponse.Result));
string modelName = result["info"].Value<String>().Split(": ")[1];
return modelName;
}
else
{
var result = JObject.Parse(response.Content);
Console.WriteLine(result["error"]);
return String.Empty;
}
return aiResponse;
}
public void TrainWithContexts()
private IRestResponse<RasaResponse> CallRasa(string projectId, string text, string model)
{
var client = new RestClient($"{Database.Configuration.GetSection("Rasa:Nlu").Value}");
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 void Train()
{
var corpus = agent.GrabCorpus(dc);
var client = new RestClient($"{Database.Configuration.GetSection("Rasa:Nlu").Value}");
@ -174,7 +209,7 @@ namespace BotSharp.Core.Engines
var rest = new RestRequest("train", Method.POST);
rest.AddQueryParameter("project", agent.Id);
rest.AddQueryParameter("model", ctx);
string trainingConfig = agent.Language == "zh" ? "config_jieba_mitie_sklearn.yml" : "config_spacy.yml";
string trainingConfig = agent.Language == "zh" ? "config_jieba_mitie_sklearn.yml" : "config_mitie_sklearn.yml";
string body = File.ReadAllText($"{Database.ContentRootPath}{Path.DirectorySeparatorChar}Settings{Path.DirectorySeparatorChar}{trainingConfig}");
body = $"{body}\r\ndata: {json}";
rest.AddParameter("application/x-yml", body, ParameterType.RequestBody);
@ -196,5 +231,47 @@ namespace BotSharp.Core.Engines
});
}
[Obsolete]
public string TrainWithoutContext()
{
var client = new RestClient($"{Database.Configuration.GetSection("Rasa:Nlu").Value}");
var rest = new RestRequest("train", Method.POST);
rest.AddQueryParameter("project", agent.Id);
var corpus = agent.GrabCorpus(dc);
string json = JsonConvert.SerializeObject(new { rasa_nlu_data = corpus },
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore
});
string trainingConfig = agent.Language == "zh" ? "config_jieba_mitie_sklearn.yml" : "config_spacy.yml";
string body = File.ReadAllText($"{Database.ContentRootPath}{Path.DirectorySeparatorChar}Settings{Path.DirectorySeparatorChar}{trainingConfig}");
body = $"{body}\r\ndata: {json}";
rest.AddParameter("application/x-yml", body, ParameterType.RequestBody);
var response = client.Execute(rest);
if (response.IsSuccessful)
{
var result = JObject.Parse(response.Content);
string modelName = result["info"].Value<String>().Split(": ")[1];
return modelName;
}
else
{
var result = JObject.Parse(response.Content);
Console.WriteLine(result["error"]);
return String.Empty;
}
}
}
}

View file

@ -18,80 +18,18 @@ using System.Text.RegularExpressions;
namespace BotSharp.Core.Engines
{
public static class RequestExtension
public static class RasaRequestExtension
{
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)
public static IntentResponse HandleIntentPerContextIn(Agent agent, AIConfiguration aiConfig, AIRequest request, RasaResponse response, Database dc)
{
AIResponse aiResponse = new AIResponse();
Database dc = rasa.dc;
#if MODEL_PER_CONTEXTS
string model = GetModelPerContexts(rasa, request);
var result = CallRasa(rasa.agent.Id, request.Query.First(), model);
#else
var result = CallRasa(rasa.agent.Id, request.Query.First(), rasa.agent.Id);
#endif
result.Content.Log();
RasaResponse response = result.Data;
aiResponse.Id = Guid.NewGuid().ToString();
aiResponse.Lang = rasa.agent.Language;
aiResponse.Status = new AIResponseStatus { };
aiResponse.SessionId = rasa.AiConfig.SessionId;
aiResponse.Timestamp = DateTime.UtcNow;
var intentResponse = HandleIntentPerContextIn(rasa, request, result.Data);
HandleParameter(rasa.agent, intentResponse, response, request);
HandleMessage(intentResponse);
aiResponse.Result = new AIResponseResult
{
Source = "agent",
ResolvedQuery = request.Query.First(),
Action = intentResponse?.Action,
Parameters = intentResponse?.Parameters?.ToDictionary(x => x.Name, x=> x.Value),
Score = response.Intent.Confidence,
Metadata = new AIResponseMetadata { IntentId = intentResponse?.IntentId, IntentName = intentResponse?.IntentName },
Fulfillment = new AIResponseFulfillment
{
Messages = intentResponse?.Messages?.Select(x => {
if (x.Type == AIResponseMessageType.Custom)
{
return (new
{
x.Type,
Payload = JsonConvert.DeserializeObject(x.PayloadJson)
}) as Object;
}
else
{
return (new { x.Type, x.Speech }) as Object;
}
}).ToList()
}
};
HandleContext(dc, rasa, intentResponse, aiResponse);
Console.WriteLine(JsonConvert.SerializeObject(aiResponse.Result));
return aiResponse;
}
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)
.Where(x => x.ConversationId == aiConfig.SessionId && x.Lifespan > 0)
.ToList()
.Select(x => new AIContext { Name = x.Context.ToLower(), Lifespan = x.Lifespan })
.ToList();
@ -100,7 +38,7 @@ namespace BotSharp.Core.Engines
contexts = contexts.OrderBy(x => x.Name).ToList();
// search all potential intents which input context included in contexts
var intents = rasa.agent.Intents.Where(it =>
var intents = agent.Intents.Where(it =>
{
if (contexts.Count == 0)
{
@ -121,13 +59,13 @@ namespace BotSharp.Core.Engines
};
}
response.IntentRanking = response.IntentRanking.Where(x => x.Confidence > decimal.Parse("0.3")).ToList();
response.IntentRanking = response.IntentRanking.Where(x => x.Confidence > agent.MlConfig.MinConfidence).ToList();
response.IntentRanking = response.IntentRanking.Where(x => intents.Select(i => i.Name).Contains(x.Name)).ToList();
// add Default Fallback Intent
if (response.IntentRanking.Count == 0)
{
var defaultFallbackIntent = rasa.agent.Intents.FirstOrDefault(x => x.Name == "Default Fallback Intent");
var defaultFallbackIntent = agent.Intents.FirstOrDefault(x => x.Name == "Default Fallback Intent");
response.IntentRanking.Add(new RasaResponseIntent
{
Name = defaultFallbackIntent.Name,
@ -137,7 +75,7 @@ namespace BotSharp.Core.Engines
response.Intent = response.IntentRanking.First();
var intent = (dc.Table<Intent>().Where(x => x.AgentId == rasa.agent.Id && x.Name == response.Intent.Name)
var intent = (dc.Table<Intent>().Where(x => x.AgentId == agent.Id && x.Name == response.Intent.Name)
.Include(x => x.Responses).ThenInclude(x => x.Contexts)
.Include(x => x.Responses).ThenInclude(x => x.Parameters).ThenInclude(x => x.Prompts)
.Include(x => x.Responses).ThenInclude(x => x.Messages)).First();
@ -157,7 +95,7 @@ namespace BotSharp.Core.Engines
/// <param name="response"></param>
/// <param name="request"></param>
/// <returns>Required field is missed</returns>
private static void HandleParameter(Agent agent, IntentResponse intentResponse, RasaResponse response, AIRequest request)
public static void HandleParameter(Agent agent, IntentResponse intentResponse, RasaResponse response, AIRequest request)
{
if (intentResponse == null) return;
@ -197,7 +135,7 @@ namespace BotSharp.Core.Engines
});
}
private static void HandleMessage(IntentResponse intentResponse)
public static void HandleMessage(IntentResponse intentResponse)
{
if (intentResponse == null) return;
@ -254,7 +192,7 @@ namespace BotSharp.Core.Engines
return text;
}
private static void HandleContext(Database dc, RasaAi rasa, IntentResponse intentResponse, AIResponse aiResponse)
public static void HandleContext(Database dc, AIConfiguration AiConfig, IntentResponse intentResponse, AIResponse aiResponse)
{
if (intentResponse == null) return;
@ -262,7 +200,7 @@ namespace BotSharp.Core.Engines
// override if exists, otherwise add, delete if lifespan is zero
dc.DbTran(() =>
{
var sessionContexts = dc.Table<ConversationContext>().Where(x => x.ConversationId == rasa.AiConfig.SessionId).ToList();
var sessionContexts = dc.Table<ConversationContext>().Where(x => x.ConversationId == AiConfig.SessionId).ToList();
// minus 1 round
sessionContexts.Where(x => !intentResponse.Contexts.Select(ctx => ctx.Name).Contains(x.Context))
@ -288,7 +226,7 @@ namespace BotSharp.Core.Engines
{
dc.Table<ConversationContext>().Add(new ConversationContext
{
ConversationId = rasa.AiConfig.SessionId,
ConversationId = AiConfig.SessionId,
Context = ctx.Name,
Lifespan = ctx.Lifespan
});
@ -297,33 +235,16 @@ namespace BotSharp.Core.Engines
});
aiResponse.Result.Contexts = dc.Table<ConversationContext>()
.Where(x => x.Lifespan > 0 && x.ConversationId == rasa.AiConfig.SessionId)
.Where(x => x.Lifespan > 0 && x.ConversationId == AiConfig.SessionId)
.Select(x => new AIContext { Name = x.Context.ToLower(), Lifespan = x.Lifespan })
.ToArray();
}
private static IRestResponse<RasaResponse> CallRasa(string projectId, string text, string model)
public static string GetModelPerContexts(Agent agent, AIConfiguration aiConfig, AIRequest request, Database dc)
{
var client = new RestClient($"{Database.Configuration.GetSection("Rasa:Nlu").Value}");
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);
}
private static string GetModelPerContexts(RasaAi rasa, AIRequest request)
{
Database dc = rasa.dc;
// Merge input contexts
var contexts = dc.Table<ConversationContext>()
.Where(x => x.ConversationId == rasa.AiConfig.SessionId && x.Lifespan > 0)
.Where(x => x.ConversationId == aiConfig.SessionId && x.Lifespan > 0)
.ToList()
.Select(x => new AIContext { Name = x.Context.ToLower(), Lifespan = x.Lifespan })
.ToList();
@ -332,7 +253,7 @@ namespace BotSharp.Core.Engines
contexts = contexts.OrderBy(x => x.Name).ToList();
// search all potential intents which input context included in contexts
var intents = rasa.agent.Intents.Where(it =>
var intents = agent.Intents.Where(it =>
{
if (contexts.Count == 0)
{

View file

@ -28,6 +28,9 @@ namespace BotSharp.Core.Intents
[ForeignKey("IntentId")]
public List<IntentInputContext> Contexts { get; set; }
[ForeignKey("IntentId")]
public List<IntentEvent> Events { get; set; }
/// <summary>
/// Get input contexts hash
/// </summary>

View file

@ -0,0 +1,20 @@
using EntityFrameworkCore.BootKit;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;
namespace BotSharp.Core.Intents
{
[Table("Bot_IntentEvent")]
public class IntentEvent : DbRecord, IDbRecord
{
[Required]
[StringLength(36)]
public String IntentId { get; set; }
[MaxLength(64)]
public String Name { get; set; }
}
}

View file

@ -19,6 +19,11 @@ namespace BotSharp.Core.Intents
public AIResponseMessageType Type { get; set; }
/// <summary>
/// Platform like: facebook, slack
/// </summary>
public String Platform { get; set; }
/// <summary>
/// json list data
/// </summary>
@ -31,6 +36,8 @@ namespace BotSharp.Core.Intents
[MaxLength(1024)]
public String PayloadJson { get; set; }
public String CardJson { get; set; }
[NotMapped]
public JObject Payload { get; set; }
}

View file

@ -70,20 +70,7 @@ namespace BotSharp.UnitTest
var rasa = new RasaAi(dc, config);
string msg = rasa.Train();
Assert.IsTrue(!String.IsNullOrEmpty(msg));
}
[TestMethod]
public void TrainAgentPerContextTest()
{
var config = new AIConfiguration(BOT_CLIENT_TOKEN, SupportedLanguage.English);
config.SessionId = Guid.NewGuid().ToString();
var rasa = new RasaAi(dc, config);
rasa.TrainWithContexts();
rasa.Train();
}
}
}

View file

@ -31,8 +31,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
<PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
<PackageReference Include="MSTest.TestFramework" Version="1.3.2" />

View file

@ -1,6 +1,6 @@
{
"Rasa": {
"Nlu": "http://gtx.local:5000"
"Nlu": "http://localhost:5000"
},
"BotSharpAi": {