Merge pull request #605 from hchen2020/master

AddTwilioRequestValidation
This commit is contained in:
Haiping 2024-08-23 10:18:12 -05:00 committed by GitHub
commit 8a63815f7a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 84 additions and 44 deletions

View file

@ -0,0 +1,11 @@
namespace BotSharp.Abstraction.Interpreters.Settings;
public class InterpreterSettings
{
public PythonInterpreterSetting Python { get; set; }
}
public class PythonInterpreterSetting
{
public string PythonDLL { get; set; }
}

View file

@ -3,7 +3,7 @@ namespace BotSharp.Abstraction.Routing.Models;
public class RoutingArgs
{
[JsonPropertyName("function")]
public string Function { get; set; } = string.Empty;
public string Function { get; set; } = "route_to_agent";
/// <summary>
/// The reason why you select this function or agent

View file

@ -6,6 +6,7 @@ using BotSharp.Abstraction.Settings;
using BotSharp.Abstraction.Options;
using BotSharp.Abstraction.Messaging.JsonConverters;
using BotSharp.Abstraction.Users.Settings;
using BotSharp.Abstraction.Interpreters.Settings;
namespace BotSharp.Core;
@ -13,6 +14,12 @@ public static class BotSharpCoreExtensions
{
public static IServiceCollection AddBotSharpCore(this IServiceCollection services, IConfiguration config, Action<BotSharpOptions>? configOptions = null)
{
var interpreterSettings = new InterpreterSettings();
config.Bind("Interpreter", interpreterSettings);
services.AddSingleton(x => interpreterSettings);
services.AddSingleton<DistributedLocker>();
services.AddScoped<ISettingService, SettingService>();
services.AddScoped<IUserService, UserService>();
services.AddSingleton<DistributedLocker>();

View file

@ -11,20 +11,20 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
public List<ParameterPropertyDef> Parameters => new List<ParameterPropertyDef>
{
new ParameterPropertyDef("next_action_reason",
/*new ParameterPropertyDef("next_action_reason",
"the reason why route to this virtual agent.",
required: true),
required: true),*/
new ParameterPropertyDef("next_action_agent",
"agent for next action based on user latest response, if user is replying last agent's question, you must route to this agent.",
required: true),
new ParameterPropertyDef("args",
"useful parameters of next action agent, format: { }",
type: "object"),
new ParameterPropertyDef("user_goal_description",
/*new ParameterPropertyDef("user_goal_description",
"user goal based on user initial task.",
required: true),
required: true),*/
new ParameterPropertyDef("user_goal_agent",
"agent who can acheive user initial task, must align with user_goal_description.",
"agent who can acheive user initial task.",
required: true),
new ParameterPropertyDef("conversation_end",
"user is ending the conversation.",

View file

@ -19,7 +19,6 @@ Follow these steps to handle user request:
# {{ handler.description}}
{% if handler.parameters and handler.parameters != empty -%}
Parameters:
- function: {{ handler.name }}
{% for p in handler.parameters -%}
- {{ p.name }} {% if p.required -%}(required){%- endif %}: {{ p.description }}{{ "\r\n " }}
{%- endfor %}

View file

@ -1,8 +1,12 @@
using BotSharp.Abstraction.Interpreters.Settings;
using BotSharp.Plugin.PythonInterpreter.Hooks;
using Microsoft.AspNetCore.Builder;
using Python.Runtime;
using System.IO;
namespace BotSharp.Plugin.PythonInterpreter;
public class InterpreterPlugin : IBotSharpPlugin
public class InterpreterPlugin : IBotSharpAppPlugin
{
public string Id => "23174e08-e866-4173-824a-cf1d97afa8d0";
public string Name => "Python Interpreter";
@ -14,4 +18,24 @@ public class InterpreterPlugin : IBotSharpPlugin
services.AddScoped<IAgentHook, InterpreterAgentHook>();
services.AddScoped<IAgentUtilityHook, InterpreterUtilityHook>();
}
public void Configure(IApplicationBuilder app)
{
var settings = app.ApplicationServices.GetRequiredService<InterpreterSettings>();
// For Python interpreter plugin
if (File.Exists(settings.Python.PythonDLL))
{
Runtime.PythonDLL = settings.Python.PythonDLL;
PythonEngine.Initialize();
PythonEngine.BeginAllowThreads();
}
else
{
Serilog.Log.Error("Python DLL found at {PythonDLL}", settings.Python.PythonDLL);
}
// Shut down the Python engine
// PythonEngine.Shutdown();
}
}

View file

@ -2,42 +2,33 @@ using BotSharp.Abstraction.Files;
using BotSharp.Core.Infrastructures;
using BotSharp.Plugin.Twilio.Models;
using BotSharp.Plugin.Twilio.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.IdentityModel.Tokens.Jwt;
namespace BotSharp.Plugin.Twilio.Controllers;
[AllowAnonymous]
[Route("twilio/voice")]
public class TwilioVoiceController : TwilioController
{
private readonly TwilioSetting _settings;
private readonly IServiceProvider _services;
private readonly IHttpContextAccessor _context;
public TwilioVoiceController(TwilioSetting settings, IServiceProvider services)
public TwilioVoiceController(TwilioSetting settings, IServiceProvider services, IHttpContextAccessor context)
{
_settings = settings;
_services = services;
_context = context;
}
[Authorize]
[HttpGet("/twilio/token")]
public Token GetAccessToken()
{
var twilio = _services.GetRequiredService<TwilioService>();
var accessToken = twilio.GetAccessToken();
var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessToken);
return new Token
{
AccessToken = accessToken,
ExpireTime = jwt.Payload.Exp.Value,
TokenType = "Bearer",
Scope = "api"
};
}
[HttpPost("welcome")]
/// <summary>
/// https://github.com/twilio-labs/twilio-aspnet?tab=readme-ov-file#validate-twilio-http-requests
/// </summary>
/// <param name="request"></param>
/// <param name="states"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
[ValidateRequest]
[HttpPost("twilio/voice/welcome")]
public TwiMLResult InitiateConversation(VoiceRequest request, [FromQuery] string states)
{
if (request?.CallSid == null) throw new ArgumentNullException(nameof(VoiceRequest.CallSid));
@ -48,7 +39,8 @@ public class TwilioVoiceController : TwilioController
return TwiML(response);
}
[HttpPost("{conversationId}/receive/{seqNum}")]
[ValidateRequest]
[HttpPost("twilio/voice/{conversationId}/receive/{seqNum}")]
public async Task<TwiMLResult> ReceiveCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum, [FromQuery] string states, VoiceRequest request)
{
var twilio = _services.GetRequiredService<TwilioService>();
@ -88,12 +80,15 @@ public class TwilioVoiceController : TwilioController
}
}
await messageQueue.EnqueueAsync(callerMessage);
response = twilio.ReturnInstructions(null, $"twilio/voice/{conversationId}/reply/{seqNum}?states={states}", true, 1);
int audioIndex = Random.Shared.Next(1, 5);
response = twilio.ReturnInstructions($"twilio/hold-on-{audioIndex}.mp3", $"twilio/voice/{conversationId}/reply/{seqNum}?states={states}", true, 1);
}
return TwiML(response);
}
[HttpPost("{conversationId}/reply/{seqNum}")]
[ValidateRequest]
[HttpPost("twilio/voice/{conversationId}/reply/{seqNum}")]
public async Task<TwiMLResult> ReplyCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum, [FromQuery] string states, VoiceRequest request)
{
var nextSeqNum = seqNum + 1;
@ -151,7 +146,8 @@ public class TwilioVoiceController : TwilioController
return TwiML(response);
}
[HttpGet("speeches/{conversationId}/{fileName}")]
[ValidateRequest]
[HttpGet("twilio/voice/speeches/{conversationId}/{fileName}")]
public async Task<FileContentResult> RetrieveSpeechFile([FromRoute] string conversationId, [FromRoute] string fileName)
{
var fileService = _services.GetRequiredService<IFileStorageService>();

View file

@ -81,6 +81,11 @@ public class TwilioService
if (!string.IsNullOrEmpty(speechPath))
{
gather.Play(new Uri($"{_settings.CallbackHost}/{speechPath}"));
if (speechPath.Contains("hold-on-"))
{
int audioIndex = Random.Shared.Next(1, 4);
gather.Play(new Uri($"{_settings.CallbackHost}/twilio/typing-{audioIndex}.mp3"));
}
}
response.Append(gather);
return response;

View file

@ -23,6 +23,6 @@ public class TwilioPlugin : IBotSharpPlugin
services.AddSingleton<ITwilioSessionManager>(sessionManager);
services.AddSingleton<TwilioMessageQueue>();
services.AddHostedService<TwilioMessageQueueService>();
services.AddTwilioRequestValidation();
}
}

View file

@ -4,7 +4,6 @@ using BotSharp.Logger;
using BotSharp.Plugin.ChatHub;
using Serilog;
using BotSharp.Abstraction.Messaging.JsonConverters;
using Python.Runtime;
var builder = WebApplication.CreateBuilder(args);
@ -42,11 +41,4 @@ app.UseBotSharp()
.UseBotSharpOpenAPI(app.Environment)
.UseBotSharpUI();
Runtime.PythonDLL = @"C:\Users\xxx\AppData\Local\Programs\Python\Python311\python311.dll";
PythonEngine.Initialize();
PythonEngine.BeginAllowThreads();
app.Run();
// Shut down the Python engine
PythonEngine.Shutdown();
app.Run();

View file

@ -299,6 +299,12 @@
}
},
"Interpreter": {
"Python": {
"PythonDLL": "C:/Users/xxx/AppData/Local/Programs/Python/Python311/python311.dll"
}
},
"PluginLoader": {
"Assemblies": [
"BotSharp.Core",