Skip to content

util-twilio-leave_voicemail #955

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 3 additions & 12 deletions src/Plugins/BotSharp.Plugin.Twilio/BotSharp.Plugin.Twilio.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\util-twilio-hangup_phone_call.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\util-twilio-text_message.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\util-twilio-outbound_phone_call.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-twilio-hangup_phone_call.fn.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-twilio-outbound_phone_call.fn.liquid" />
</ItemGroup>

<ItemGroup>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\util-twilio-leave_voicemail.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\util-twilio-transfer_phone_call.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Expand All @@ -31,12 +32,6 @@
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\util-twilio-outbound_phone_call.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-twilio-hangup_phone_call.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-twilio-outbound_phone_call.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
Expand All @@ -51,8 +46,4 @@
<ProjectReference Include="..\..\Infrastructure\BotSharp.Core\BotSharp.Core.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,6 @@ public async Task<TwiMLResult> InitiateStreamConversation(ConversationalVoiceReq
var twilio = _services.GetRequiredService<TwilioService>();
VoiceResponse response = default!;

if (request.AnsweredBy == "machine_start" &&
request.Direction == "outbound-api" &&
request.InitAudioFile != null)
{
response = new VoiceResponse();
var url = twilio.GetSpeechPath(request.ConversationId, request.InitAudioFile);
response.Play(new Uri(url));
return TwiML(response);
}

var instruction = new ConversationalVoiceResponse
{
ConversationId = request.ConversationId,
Expand All @@ -70,7 +60,24 @@ await HookEmitter.Emit<ITwilioSessionHook>(_services, async hook =>

request.ConversationId = await InitConversation(request);
instruction.ConversationId = request.ConversationId;
response = twilio.ReturnBidirectionalMediaStreamsInstructions(instruction);

if (request.AnsweredBy == "machine_start" &&
request.Direction == "outbound-api")
{
response = new VoiceResponse();

await HookEmitter.Emit<ITwilioCallStatusHook>(_services, async hook =>
{
await hook.OnVoicemailStarting(request);
});

var url = twilio.GetSpeechPath(request.ConversationId, "voicemail.mp3");
response.Play(new Uri(url));
}
else
{
response = twilio.ReturnBidirectionalMediaStreamsInstructions(instruction);
}

await HookEmitter.Emit<ITwilioSessionHook>(_services, async hook =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,8 +475,7 @@ public async Task<ActionResult> PhoneCallStatus(ConversationalVoiceRequest reque
if (request.CallStatus == "completed")
{
if (request.AnsweredBy == "machine_start" &&
request.Direction == "outbound-api" &&
request.InitAudioFile != null)
request.Direction == "outbound-api")
{
// voicemail
await HookEmitter.Emit<ITwilioCallStatusHook>(_services, async hook =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public interface ITwilioCallStatusHook
Task OnVoicemailLeft(ConversationalVoiceRequest request);
Task OnUserDisconnected(ConversationalVoiceRequest request);
Task OnRecordingCompleted(ConversationalVoiceRequest request);
Task OnVoicemailStarting(ConversationalVoiceRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using BotSharp.Abstraction.Files;
using BotSharp.Abstraction.Routing;
using BotSharp.Core.Infrastructures;
using BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.LlmContexts;

namespace BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.Functions;

public class LeaveVoicemailFn : IFunctionCallback
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
private readonly TwilioSetting _setting;

public string Name => "util-twilio-leave_voicemail";
public string Indication => "leaving a voicemail";

public LeaveVoicemailFn(
IServiceProvider services,
ILogger<LeaveVoicemailFn> logger,
TwilioSetting setting)
{
_services = services;
_logger = logger;
_setting = setting;
}

public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<LeaveVoicemailArgs>(message.FunctionArgs);

var fileStorage = _services.GetRequiredService<IFileStorageService>();
var routing = _services.GetRequiredService<IRoutingService>();
var conversationId = routing.Context.ConversationId;
var states = _services.GetRequiredService<IConversationStateService>();
var callSid = states.GetState("twilio_call_sid");

if (string.IsNullOrEmpty(callSid))
{
message.Content = "The call has not been initiated.";
_logger.LogError(message.Content);
return false;
}

// Generate voice message audio
string initAudioFile = null;
if (!string.IsNullOrEmpty(args.VoicemailMessage))
{
var completion = CompletionProvider.GetAudioCompletion(_services, "openai", "tts-1");
var data = await completion.GenerateAudioFromTextAsync(args.VoicemailMessage);
initAudioFile = "voicemail.mp3";
fileStorage.SaveSpeechFile(conversationId, initAudioFile, data);
}

message.Content = args.VoicemailMessage;
message.StopCompletion = true;

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class OutboundPhoneCallHandlerUtilityHook : IAgentUtilityHook
private static string TRANSFER_PHONE_CALL_FN = $"{PREFIX}transfer_phone_call";
private static string HANGUP_PHONE_CALL_FN = $"{PREFIX}hangup_phone_call";
private static string TEXT_MESSAGE_FN = $"{PREFIX}text_message";
private static string LEAVE_VOICEMAIL_FN = $"{PREFIX}leave_voicemail";

public void AddUtilities(List<AgentUtility> utilities)
{
Expand All @@ -21,12 +22,11 @@ public void AddUtilities(List<AgentUtility> utilities)
new($"{OUTBOUND_PHONE_CALL_FN}"),
new($"{TRANSFER_PHONE_CALL_FN}"),
new($"{HANGUP_PHONE_CALL_FN}"),
new($"{TEXT_MESSAGE_FN}")
new($"{TEXT_MESSAGE_FN}"),
new($"{LEAVE_VOICEMAIL_FN}")
],
Templates =
[
new($"{OUTBOUND_PHONE_CALL_FN}.fn"),
new($"{HANGUP_PHONE_CALL_FN}.fn")
]
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;

namespace BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.LlmContexts;

public class LeaveVoicemailArgs
{
[JsonPropertyName("phone_number")]
public string PhoneNumber { get; set; } = null!;

[JsonPropertyName("voicemail_message")]
public string VoicemailMessage { get; set; } = null!;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "util-twilio-leave_voicemail",
"description": "If the user wants you to leave a voicemail.",
"visibility_expression": "{% if states.channel == 'phone' %}visible{% endif %}",
"parameters": {
"type": "object",
"properties": {
"voicemail_message": {
"type": "string",
"description": "User voicemail with details."
},
"phone_number": {
"type": "string",
"description": "Phone number to callback."
}
},
"required": [ "voicemail_message", "phone_number" ]
}
}

This file was deleted.

This file was deleted.

Loading