Merge pull request #1093 from iceljc/master

Refine realtime and twilio
This commit is contained in:
iceljc 2025-07-09 11:41:43 -05:00 committed by GitHub
commit 5127c65c40
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 34 additions and 34 deletions

View file

@ -79,7 +79,7 @@ public class SideCarAttribute : AsyncMoAttribute
object? res = null; object? res = null;
var isHandled = false; var isHandled = false;
var enabled = instance != null && instance.IsEnabled() && method != null; var enabled = instance != null && instance.IsEnabled && method != null;
if (!enabled) if (!enabled)
{ {
return (isHandled, value); return (isHandled, value);
@ -112,7 +112,7 @@ public class SideCarAttribute : AsyncMoAttribute
object? value = null; object? value = null;
var isHandled = false; var isHandled = false;
var enabled = instance != null && instance.IsEnabled() && method != null; var enabled = instance != null && instance.IsEnabled && method != null;
if (!enabled) if (!enabled)
{ {
return (isHandled, value); return (isHandled, value);

View file

@ -5,8 +5,8 @@ namespace BotSharp.Abstraction.SideCar;
public interface IConversationSideCar public interface IConversationSideCar
{ {
string Provider { get; } string Provider { get; }
bool IsEnabled { get; }
bool IsEnabled();
void AppendConversationDialogs(string conversationId, List<DialogElement> messages); void AppendConversationDialogs(string conversationId, List<DialogElement> messages);
List<DialogElement> GetConversationDialogs(string conversationId); List<DialogElement> GetConversationDialogs(string conversationId);
void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint); void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint);

View file

@ -14,7 +14,6 @@
limitations under the License. limitations under the License.
******************************************************************************/ ******************************************************************************/
using BotSharp.Abstraction.SideCar.Models;
using BotSharp.Core.Infrastructures; using BotSharp.Core.Infrastructures;
namespace BotSharp.Core.SideCar.Services; namespace BotSharp.Core.SideCar.Services;
@ -31,6 +30,7 @@ public class BotSharpConversationSideCar : IConversationSideCar
private string _conversationId = string.Empty; private string _conversationId = string.Empty;
public string Provider => "botsharp"; public string Provider => "botsharp";
public bool IsEnabled => _enabled;
public BotSharpConversationSideCar( public BotSharpConversationSideCar(
IServiceProvider services, IServiceProvider services,
@ -40,11 +40,6 @@ public class BotSharpConversationSideCar : IConversationSideCar
_logger = logger; _logger = logger;
} }
public bool IsEnabled()
{
return _enabled;
}
public void AppendConversationDialogs(string conversationId, List<DialogElement> messages) public void AppendConversationDialogs(string conversationId, List<DialogElement> messages)
{ {
if (!IsValid(conversationId)) if (!IsValid(conversationId))
@ -99,17 +94,22 @@ public class BotSharpConversationSideCar : IConversationSideCar
top.State = new ConversationState(states); top.State = new ConversationState(states);
} }
public async Task<RoleDialogModel> SendMessage(string agentId, string text, public async Task<RoleDialogModel> SendMessage(
string agentId,
string text,
PostbackMessageModel? postback = null, PostbackMessageModel? postback = null,
List<MessageState>? states = null, List<MessageState>? states = null,
List<DialogElement>? dialogs = null, List<DialogElement>? dialogs = null,
SideCarOptions? options = null) SideCarOptions? options = null)
{ {
_sideCarOptions = options; _sideCarOptions = options;
_logger.LogInformation($"Entering side car conversation...");
BeforeExecute(dialogs); BeforeExecute(dialogs);
var response = await InnerExecute(agentId, text, postback, states); var response = await InnerExecute(agentId, text, postback, states);
AfterExecute(); AfterExecute();
_logger.LogInformation($"Existing side car conversation...");
return response; return response;
} }

View file

@ -159,7 +159,7 @@ public class ConversationStateService : IConversationStateService
Reset(); Reset();
var endNodes = new Dictionary<string, string>(); var endNodes = new Dictionary<string, string>();
if (_sidecar?.IsEnabled() == true) if (_sidecar?.IsEnabled == true)
{ {
return endNodes; return endNodes;
} }
@ -234,7 +234,7 @@ public class ConversationStateService : IConversationStateService
public void Save() public void Save()
{ {
if (_conversationId == null || _sidecar?.IsEnabled() == true) if (_conversationId == null || _sidecar?.IsEnabled == true)
{ {
return; return;
} }

View file

@ -62,6 +62,7 @@ public class ChatStreamMiddleware
var hub = services.GetRequiredService<IRealtimeHub>(); var hub = services.GetRequiredService<IRealtimeHub>();
var conn = hub.SetHubConnection(conversationId); var conn = hub.SetHubConnection(conversationId);
conn.CurrentAgentId = agentId; conn.CurrentAgentId = agentId;
InitEvents(conn);
// load conversation and state // load conversation and state
var convService = services.GetRequiredService<IConversationService>(); var convService = services.GetRequiredService<IConversationService>();
@ -128,6 +129,11 @@ public class ChatStreamMiddleware
break; break;
} }
return (response.Event, data);
}
private void InitEvents(RealtimeHubConnection conn)
{
conn.OnModelMessageReceived = message => conn.OnModelMessageReceived = message =>
JsonSerializer.Serialize(new JsonSerializer.Serialize(new
{ {
@ -147,7 +153,5 @@ public class ChatStreamMiddleware
{ {
@event = "clear" @event = "clear"
}); });
return (response.Event, data);
} }
} }

View file

@ -179,7 +179,7 @@ public class ChatHubConversationHook : ConversationHookBase
private bool AllowSendingMessage() private bool AllowSendingMessage()
{ {
var sidecar = _services.GetService<IConversationSideCar>(); var sidecar = _services.GetService<IConversationSideCar>();
return sidecar == null || !sidecar.IsEnabled(); return sidecar == null || !sidecar.IsEnabled;
} }
private async Task InitClientConversation(string conversationId, ConversationDto conversation) private async Task InitClientConversation(string conversationId, ConversationDto conversation)

View file

@ -329,17 +329,13 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
var words = new List<string>(); var words = new List<string>();
HookEmitter.Emit<IRealtimeHook>(_services, hook => words.AddRange(hook.OnModelTranscriptPrompt(agent)), agent.Id); HookEmitter.Emit<IRealtimeHook>(_services, hook => words.AddRange(hook.OnModelTranscriptPrompt(agent)), agent.Id);
var functions = request.Tools?.SelectMany(s => s.FunctionDeclarations).Select(x => var functions = request.Tools?.SelectMany(s => s.FunctionDeclarations).Select(x => new FunctionDef
{
var fn = new FunctionDef
{ {
Name = x.Name ?? string.Empty, Name = x.Name ?? string.Empty,
Description = x.Description ?? string.Empty, Description = x.Description ?? string.Empty,
Parameters = x.Parameters != null Parameters = x.Parameters != null
? JsonSerializer.Deserialize<FunctionParametersDef>(JsonSerializer.Serialize(x.Parameters)) ? JsonSerializer.Deserialize<FunctionParametersDef>(JsonSerializer.Serialize(x.Parameters))
: null : null
};
return fn;
}).ToArray(); }).ToArray();
await HookEmitter.Emit<IContentGeneratingHook>(_services, await HookEmitter.Emit<IContentGeneratingHook>(_services,

View file

@ -326,15 +326,11 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
var (prompt, messages, options) = PrepareOptions(agent, []); var (prompt, messages, options) = PrepareOptions(agent, []);
var instruction = messages.FirstOrDefault()?.Content.FirstOrDefault()?.Text ?? agent?.Description ?? string.Empty; var instruction = messages.FirstOrDefault()?.Content.FirstOrDefault()?.Text ?? agent?.Description ?? string.Empty;
var functions = options.Tools.Select(x => var functions = options.Tools.Select(x => new FunctionDef
{
var fn = new FunctionDef
{ {
Name = x.FunctionName, Name = x.FunctionName,
Description = x.FunctionDescription Description = x.FunctionDescription,
}; Parameters = JsonSerializer.Deserialize<FunctionParametersDef>(x.FunctionParameters)
fn.Parameters = JsonSerializer.Deserialize<FunctionParametersDef>(x.FunctionParameters);
return fn;
}).ToArray(); }).ToArray();
var realtimeModelSettings = _services.GetRequiredService<RealtimeModelSettings>(); var realtimeModelSettings = _services.GetRequiredService<RealtimeModelSettings>();

View file

@ -53,25 +53,29 @@ public class TwilioInboundController : TwilioController
instruction.SpeechPaths.Add(request.InitAudioFile); instruction.SpeechPaths.Add(request.InitAudioFile);
} }
// Before creating session
await HookEmitter.Emit<ITwilioSessionHook>(_services, async hook => await HookEmitter.Emit<ITwilioSessionHook>(_services, async hook =>
{ {
await hook.OnSessionCreating(request, instruction); await hook.OnSessionCreating(request, instruction);
}, request.AgentId); }, request.AgentId);
var (agent, conversationId) = await InitConversation(request); var (agent, conversationId) = await InitConversation(request);
request.ConversationId = conversationId.Id; request.ConversationId = conversationId.Id;
instruction.AgentId = request.AgentId; instruction.AgentId = request.AgentId;
instruction.ConversationId = request.ConversationId; instruction.ConversationId = request.ConversationId;
// After creating session
await HookEmitter.Emit<ITwilioSessionHook>(_services, async hook => await HookEmitter.Emit<ITwilioSessionHook>(_services, async hook =>
{ {
await hook.OnSessionCreated(request); await hook.OnSessionCreated(request);
}, request.AgentId); }, request.AgentId);
if (twilio.MachineDetected(request)) if (twilio.MachineDetected(request))
{ {
response = new VoiceResponse(); response = new VoiceResponse();
await HookEmitter.Emit<ITwilioCallStatusHook>(_services, await HookEmitter.Emit<ITwilioCallStatusHook>(_services,
async hook => await hook.OnVoicemailStarting(request), request.AgentId); async hook => await hook.OnVoicemailStarting(request), request.AgentId);
@ -204,7 +208,7 @@ public class TwilioInboundController : TwilioController
storage.Append(conversation.Id, new RoleDialogModel(AgentRole.User, request.Intent) storage.Append(conversation.Id, new RoleDialogModel(AgentRole.User, request.Intent)
{ {
CurrentAgentId = conversation.Id, CurrentAgentId = agent.Id,
CreatedAt = DateTime.UtcNow CreatedAt = DateTime.UtcNow
}); });
} }