Skip to content

Commit 3dfb921

Browse files
authored
Merge pull request #1060 from iceljc/test/google-realtime
add session reconnect
2 parents 2ff2c30 + ba0b486 commit 3dfb921

File tree

52 files changed

+374
-194
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+374
-194
lines changed

src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public enum AgentField
88
IsPublic,
99
Disabled,
1010
Type,
11-
Mode,
11+
RoutingMode,
1212
InheritAgentId,
1313
Profile,
1414
Label,

src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class Agent
99
public string Id { get; set; } = string.Empty;
1010
public string Name { get; set; } = string.Empty;
1111
public string Description { get; set; } = string.Empty;
12+
1213
/// <summary>
1314
/// Agent Type
1415
/// </summary>
@@ -17,7 +18,8 @@ public class Agent
1718
/// <summary>
1819
/// Routing Mode: lazy or eager
1920
/// </summary>
20-
public string Mode { get; set; } = "eager";
21+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
22+
public string? Mode { get; set; }
2123

2224
public DateTime CreatedDateTime { get; set; }
2325
public DateTime UpdatedDateTime { get; set; }
@@ -277,18 +279,18 @@ public Agent SetMergeUtility(bool merge)
277279
return this;
278280
}
279281

280-
public Agent SetAgentType(string type)
282+
public Agent SetType(string type)
281283
{
282284
Type = type;
283285
return this;
284286
}
285287

286288
/// <summary>
287-
/// Set agent mode: lazy or eager
289+
/// Set agent routing mode: lazy or eager
288290
/// </summary>
289291
/// <param name="mode"></param>
290292
/// <returns></returns>
291-
public Agent SetAgentMode(string mode)
293+
public Agent SetRoutingMode(string? mode)
292294
{
293295
Mode = mode;
294296
return this;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace BotSharp.Abstraction.Crontab.Settings;
2+
3+
public class CrontabSettings
4+
{
5+
public CrontabBaseSetting EventSubscriber { get; set; } = new();
6+
public CrontabBaseSetting Watcher { get; set; } = new();
7+
}
8+
9+
public class CrontabBaseSetting
10+
{
11+
public bool Enabled { get; set; } = true;
12+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using BotSharp.Abstraction.Users;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.AspNetCore.Mvc.Filters;
4+
using Microsoft.Extensions.DependencyInjection;
5+
6+
namespace BotSharp.Abstraction.Infrastructures.Attributes;
7+
8+
/// <summary>
9+
/// BotSharp authorization: check whether the request user is admin or root role.
10+
/// </summary>
11+
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
12+
public class BotSharpAuthAttribute : Attribute, IAsyncAuthorizationFilter
13+
{
14+
public BotSharpAuthAttribute()
15+
{
16+
17+
}
18+
19+
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
20+
{
21+
var services = context.HttpContext.RequestServices;
22+
23+
var userIdentity = services.GetRequiredService<IUserIdentity>();
24+
var userService = services.GetRequiredService<IUserService>();
25+
26+
var (isAdmin, user) = await userService.IsAdminUser(userIdentity.Id);
27+
if (!isAdmin || user == null)
28+
{
29+
context.Result = new UnauthorizedResult();
30+
}
31+
}
32+
}

src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Task Connect(
1919
Func<RoleDialogModel, Task> onInputAudioTranscriptionDone,
2020
Func<Task> onInterruptionDetected);
2121

22+
Task Reconnect(RealtimeHubConnection conn);
23+
2224
Task AppenAudioBuffer(string message);
2325
Task AppenAudioBuffer(ArraySegment<byte> data, int length);
2426

src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using BotSharp.Abstraction.Hooks;
22
using BotSharp.Abstraction.MLTasks;
3+
using BotSharp.Abstraction.Realtime.Models;
34

45
namespace BotSharp.Abstraction.Realtime;
56

@@ -8,4 +9,5 @@ public interface IRealtimeHook : IHookBase
89
Task OnModelReady(Agent agent, IRealTimeCompletion completer);
910
string[] OnModelTranscriptPrompt(Agent agent);
1011
Task OnTranscribeCompleted(RoleDialogModel message, TranscriptionData data);
12+
Task<bool> ShouldReconnect(RealtimeHubConnection conn) => Task.FromResult(false);
1113
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
using Microsoft.Extensions.Logging;
12
using System.Text.Json;
23

34
namespace BotSharp.Abstraction.Realtime.Models.Session;
45

56
public class ChatSessionOptions
67
{
8+
public string Provider { get; set; }
79
public int? BufferSize { get; set; }
810
public JsonSerializerOptions? JsonOptions { get; set; }
11+
public ILogger? Logger { get; set; }
912
}

src/Infrastructure/BotSharp.Abstraction/Repositories/BotSharpDatabaseSettings.cs renamed to src/Infrastructure/BotSharp.Abstraction/Repositories/Settings/BotSharpDatabaseSettings.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace BotSharp.Abstraction.Repositories;
1+
namespace BotSharp.Abstraction.Repositories.Settings;
22

33
public class BotSharpDatabaseSettings : DatabaseBasicSettings
44
{
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace BotSharp.Abstraction.Routing.Enums;
2+
3+
public class RoutingMode
4+
{
5+
public const string Eager = "eager";
6+
public const string Lazy = "lazy";
7+
}

src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace BotSharp.Abstraction.Utilities;
55

66
public static class StringExtensions
77
{
8-
public static string IfNullOrEmptyAs(this string str, string defaultValue)
8+
public static string IfNullOrEmptyAs(this string? str, string defaultValue)
99
=> string.IsNullOrEmpty(str) ? defaultValue : str;
1010

1111
public static string SubstringMax(this string str, int maxLength)

src/Infrastructure/BotSharp.Core.Crontab/CrontabPlugin.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,22 @@ public class CrontabPlugin : IBotSharpPlugin
3232

3333
public void RegisterDI(IServiceCollection services, IConfiguration config)
3434
{
35+
var settings = new CrontabSettings();
36+
config.Bind("Crontab", settings);
37+
services.AddSingleton(settings);
38+
3539
services.AddScoped<IAgentUtilityHook, CrontabUtilityHook>();
3640
services.AddScoped<ICrontabService, CrontabService>();
3741
services.AddScoped<ITaskFeeder, CrontabService>();
3842

39-
services.AddHostedService<CrontabWatcher>();
40-
services.AddHostedService<CrontabEventSubscription>();
43+
if (settings.Watcher?.Enabled == true)
44+
{
45+
services.AddHostedService<CrontabWatcher>();
46+
}
47+
48+
if (settings.EventSubscriber?.Enabled == true)
49+
{
50+
services.AddHostedService<CrontabEventSubscription>();
51+
}
4152
}
4253
}

src/Infrastructure/BotSharp.Core.Crontab/Using.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
global using BotSharp.Abstraction.Agents.Enums;
77
global using BotSharp.Abstraction.Crontab;
88
global using BotSharp.Abstraction.Crontab.Models;
9+
global using BotSharp.Abstraction.Crontab.Settings;
910
global using BotSharp.Abstraction.Agents;
1011
global using BotSharp.Abstraction.Plugins;
1112
global using BotSharp.Abstraction.Conversations.Models;

src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,15 @@ await HookEmitter.Emit<IRoutingHook>(_services, async hook => await hook.OnRouti
9898
}
9999

100100
await routing.InvokeFunction(message.FunctionName, message);
101-
dialogs.Add(message);
102-
storage.Append(_conn.ConversationId, message);
103101
}
104102
else
105103
{
106104
// append output audio transcript to conversation
107105
dialogs.Add(message);
108106
storage.Append(_conn.ConversationId, message);
109107

110-
var hooks = _services.GetHooksOrderByPriority<IConversationHook>(_conn.CurrentAgentId);
111-
foreach (var hook in hooks)
108+
var convHooks = _services.GetHooksOrderByPriority<IConversationHook>(_conn.CurrentAgentId);
109+
foreach (var hook in convHooks)
112110
{
113111
hook.SetAgent(agent)
114112
.SetConversation(conversation);
@@ -117,6 +115,19 @@ await HookEmitter.Emit<IRoutingHook>(_services, async hook => await hook.OnRouti
117115
}
118116
}
119117
}
118+
119+
var isReconnect = false;
120+
var realtimeHooks = _services.GetHooks<IRealtimeHook>(_conn.CurrentAgentId);
121+
foreach (var hook in realtimeHooks)
122+
{
123+
isReconnect = await hook.ShouldReconnect(_conn);
124+
if (isReconnect) break;
125+
}
126+
127+
if (isReconnect)
128+
{
129+
await _completer.Reconnect(_conn);
130+
}
120131
},
121132
onConversationItemCreated: async response =>
122133
{

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using BotSharp.Abstraction.Repositories.Settings;
12
using BotSharp.Abstraction.Tasks.Models;
23
using BotSharp.Abstraction.Users.Enums;
34
using System.IO;

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using BotSharp.Abstraction.Repositories.Enums;
2+
using BotSharp.Abstraction.Repositories.Settings;
23
using System.IO;
34

45
namespace BotSharp.Core.Agents.Services;
@@ -16,14 +17,6 @@ public async Task<string> RefreshAgents()
1617
return refreshResult;
1718
}
1819

19-
var userIdentity = _services.GetRequiredService<IUserIdentity>();
20-
var userService = _services.GetRequiredService<IUserService>();
21-
var (isValid, _) = await userService.IsAdminUser(userIdentity.Id);
22-
if (!isValid)
23-
{
24-
return "Unauthorized user.";
25-
}
26-
2720
var agentDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
2821
dbSettings.FileRepository,
2922
_agentSettings.DataDir);
@@ -74,7 +67,7 @@ public async Task<string> RefreshAgents()
7467
}
7568
catch (Exception ex)
7669
{
77-
_logger.LogError($"Failed to migrate agent in file directory: {dir}\r\nError: {ex.Message}");
70+
_logger.LogError(ex, $"Failed to migrate agent in file directory: {dir}\r\nError: {ex.Message}");
7871
}
7972
}
8073

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using BotSharp.Abstraction.Repositories.Enums;
2+
using BotSharp.Abstraction.Repositories.Settings;
23
using BotSharp.Abstraction.Users.Enums;
34
using BotSharp.Abstraction.Users.Models;
45
using System.IO;
@@ -93,12 +94,12 @@ public async Task<string> UpdateAgentFromFile(string id)
9394
{
9495
clonedAgent.SetId(foundAgent.Id)
9596
.SetName(foundAgent.Name)
96-
.SetDescription(foundAgent.Description)
97+
.SetType(foundAgent.Type)
98+
.SetRoutingMode(foundAgent.Mode)
9799
.SetIsPublic(foundAgent.IsPublic)
98100
.SetDisabled(foundAgent.Disabled)
101+
.SetDescription(foundAgent.Description)
99102
.SetMergeUtility(foundAgent.MergeUtility)
100-
.SetAgentType(foundAgent.Type)
101-
.SetAgentMode(foundAgent.Mode)
102103
.SetProfiles(foundAgent.Profiles)
103104
.SetLabels(foundAgent.Labels)
104105
.SetRoutingRules(foundAgent.RoutingRules)

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using BotSharp.Abstraction.Repositories.Settings;
12
using System.IO;
23
using System.Reflection;
34

src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using BotSharp.Core.Templating;
1717
using BotSharp.Abstraction.Infrastructures.Enums;
1818
using BotSharp.Abstraction.Realtime;
19+
using BotSharp.Abstraction.Repositories.Settings;
1920

2021
namespace BotSharp.Core;
2122

@@ -71,17 +72,6 @@ public static IServiceCollection UsingSqlServer(this IServiceCollection services
7172
return services;
7273
}
7374

74-
//public static IServiceCollection UsingFileRepository(this IServiceCollection services, IConfiguration config)
75-
//{
76-
// services.AddScoped<IBotSharpRepository>(sp =>
77-
// {
78-
// var myDatabaseSettings = sp.GetRequiredService<BotSharpDatabaseSettings>();
79-
// return new FileRepository(myDatabaseSettings, sp);
80-
// });
81-
82-
// return services;
83-
//}
84-
8575
public static IApplicationBuilder UseBotSharp(this IApplicationBuilder app)
8676
{
8777
if (app == null)

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using BotSharp.Abstraction.Infrastructures.Enums;
33
using BotSharp.Abstraction.Messaging;
44
using BotSharp.Abstraction.Messaging.Models.RichContent;
5+
using BotSharp.Abstraction.Routing.Enums;
56
using BotSharp.Abstraction.Routing.Settings;
67

78
namespace BotSharp.Core.Conversations.Services;
@@ -83,10 +84,10 @@ public async Task<bool> SendMessage(string agentId,
8384
{
8485
// Check the routing mode
8586
var states = _services.GetRequiredService<IConversationStateService>();
86-
var routingMode = states.GetState(StateConst.ROUTING_MODE, "eager");
87+
var routingMode = states.GetState(StateConst.ROUTING_MODE, RoutingMode.Eager);
8788
routing.Context.Push(agent.Id, reason: "request started", updateLazyRouting: false);
8889

89-
if (routingMode == "lazy")
90+
if (routingMode == RoutingMode.Lazy)
9091
{
9192
message.CurrentAgentId = states.GetState(StateConst.LAZY_ROUTING_AGENT_ID, message.CurrentAgentId);
9293
routing.Context.Push(message.CurrentAgentId, reason: "lazy routing", updateLazyRouting: false);

src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using BotSharp.Abstraction.Repositories.Settings;
12
using System.IO;
23

34
namespace BotSharp.Core.Files.Services;

src/Infrastructure/BotSharp.Core/Infrastructures/DistributedLocker.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ public async Task<bool> LockAsync(string resource, Func<Task> action, int timeou
2424
var redis = _services.GetService<IConnectionMultiplexer>();
2525
if (redis == null)
2626
{
27+
#if !DEBUG
2728
_logger.LogInformation($"The Redis server is experiencing issues and is not functioning as expected.");
29+
#endif
2830
await action();
2931
return true;
3032
}
@@ -50,7 +52,9 @@ public bool Lock(string resource, Action action, int timeoutInSeconds = 30)
5052
var redis = _services.GetRequiredService<IConnectionMultiplexer>();
5153
if (redis == null)
5254
{
55+
#if !DEBUG
5356
_logger.LogWarning($"The Redis server is experiencing issues and is not functioning as expected.");
57+
#endif
5458
action();
5559
return false;
5660
}

src/Infrastructure/BotSharp.Core/Infrastructures/Websocket/AsyncWebsocketDataCollectionResult.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using BotSharp.Abstraction.Realtime.Models.Session;
21
using System.ClientModel;
32
using System.Net.WebSockets;
43

@@ -7,16 +6,16 @@ namespace BotSharp.Core.Infrastructures.Websocket;
76
internal class AsyncWebsocketDataCollectionResult : AsyncCollectionResult<ClientResult>
87
{
98
private readonly WebSocket _webSocket;
10-
private readonly ChatSessionOptions? _sessionOptions;
9+
private readonly ChatSessionOptions? _options;
1110
private readonly CancellationToken _cancellationToken;
1211

1312
public AsyncWebsocketDataCollectionResult(
1413
WebSocket webSocket,
15-
ChatSessionOptions? sessionOptions,
14+
ChatSessionOptions? options,
1615
CancellationToken cancellationToken)
1716
{
1817
_webSocket = webSocket;
19-
_sessionOptions = sessionOptions;
18+
_options = options;
2019
_cancellationToken = cancellationToken;
2120
}
2221

@@ -27,7 +26,7 @@ public AsyncWebsocketDataCollectionResult(
2726

2827
public override async IAsyncEnumerable<ClientResult> GetRawPagesAsync()
2928
{
30-
await using var enumerator = new AsyncWebsocketDataResultEnumerator(_webSocket, _sessionOptions, _cancellationToken);
29+
await using var enumerator = new AsyncWebsocketDataResultEnumerator(_webSocket, _options, _cancellationToken);
3130
while (await enumerator.MoveNextAsync().ConfigureAwait(false))
3231
{
3332
yield return enumerator.Current;

0 commit comments

Comments
 (0)