From 82b831d4068e2b66d7a281841bfdb5be396be0c8 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Mon, 19 May 2025 14:54:40 -0500
Subject: [PATCH 01/11] add session reconnect
---
.../Attributes/BotSharpAuthAttribute.cs | 32 +++++
.../MLTasks/IRealTimeCompletion.cs | 4 +-
.../Realtime/IRealtimeHook.cs | 2 +
.../Utilities/StringExtensions.cs | 2 +-
.../Services/RealtimeHub.cs | 16 ++-
.../Services/AgentService.RefreshAgents.cs | 8 --
.../Session/BotSharpRealtimeSession.cs | 18 ++-
.../Session/LlmRealtimeSession.cs | 33 +++--
.../Controllers/AgentController.cs | 2 +
.../Controllers/PluginController.cs | 17 +--
.../Controllers/RoleController.cs | 29 +----
.../Controllers/UserController.cs | 22 +---
.../Providers/Chat/ChatCompletionProvider.cs | 2 +-
.../Realtime/RealtimeTranscriptionResponse.cs | 19 ++-
.../Realtime/RealTimeCompletionProvider.cs | 69 ++++++++---
.../Providers/Chat/ChatCompletionProvider.cs | 2 +-
.../Realtime/RealTimeCompletionProvider.cs | 114 ++++++++++++------
.../BotSharp.LLM.Tests/GoogleRealTimeTests.cs | 2 +-
18 files changed, 251 insertions(+), 142 deletions(-)
create mode 100644 src/Infrastructure/BotSharp.Abstraction/Infrastructures/Attributes/BotSharpAuthAttribute.cs
diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Attributes/BotSharpAuthAttribute.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Attributes/BotSharpAuthAttribute.cs
new file mode 100644
index 00000000..25193cb3
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Attributes/BotSharpAuthAttribute.cs
@@ -0,0 +1,32 @@
+using BotSharp.Abstraction.Users;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace BotSharp.Abstraction.Infrastructures.Attributes;
+
+///
+/// BotSharp authorization: check whether the request user is admin or root role.
+///
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
+public class BotSharpAuthAttribute : Attribute, IAsyncAuthorizationFilter
+{
+ public BotSharpAuthAttribute()
+ {
+
+ }
+
+ public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
+ {
+ var services = context.HttpContext.RequestServices;
+
+ var userIdentity = services.GetRequiredService();
+ var userService = services.GetRequiredService();
+
+ var (isAdmin, user) = await userService.IsAdminUser(userIdentity.Id);
+ if (!isAdmin || user == null)
+ {
+ context.Result = new BadRequestResult();
+ }
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs
index d6057859..733eef92 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs
@@ -14,11 +14,13 @@ public interface IRealTimeCompletion
Func onModelAudioDeltaReceived,
Func onModelAudioResponseDone,
Func onModelAudioTranscriptDone,
- Func, Task> onModelResponseDone,
+ Func, Task> onModelResponseDone,
Func onConversationItemCreated,
Func onInputAudioTranscriptionDone,
Func onInterruptionDetected);
+ Task Reconnect(RealtimeHubConnection conn);
+
Task AppenAudioBuffer(string message);
Task AppenAudioBuffer(ArraySegment data, int length);
diff --git a/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs b/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs
index bd131b93..51d7383a 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs
@@ -1,5 +1,6 @@
using BotSharp.Abstraction.Hooks;
using BotSharp.Abstraction.MLTasks;
+using BotSharp.Abstraction.Realtime.Models;
namespace BotSharp.Abstraction.Realtime;
@@ -8,4 +9,5 @@ public interface IRealtimeHook : IHookBase
Task OnModelReady(Agent agent, IRealTimeCompletion completer);
string[] OnModelTranscriptPrompt(Agent agent);
Task OnTranscribeCompleted(RoleDialogModel message, TranscriptionData data);
+ Task ShouldReconnect(RealtimeHubConnection conn) => Task.FromResult(false);
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs
index 94bda0cd..ea6bb8ec 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs
@@ -5,7 +5,7 @@ namespace BotSharp.Abstraction.Utilities;
public static class StringExtensions
{
- public static string IfNullOrEmptyAs(this string str, string defaultValue)
+ public static string IfNullOrEmptyAs(this string? str, string defaultValue)
=> string.IsNullOrEmpty(str) ? defaultValue : str;
public static string SubstringMax(this string str, int maxLength)
diff --git a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs
index 070bcf26..53318a5f 100644
--- a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs
+++ b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs
@@ -98,8 +98,6 @@ public class RealtimeHub : IRealtimeHub
}
await routing.InvokeFunction(message.FunctionName, message);
- dialogs.Add(message);
- storage.Append(_conn.ConversationId, message);
}
else
{
@@ -107,8 +105,8 @@ public class RealtimeHub : IRealtimeHub
dialogs.Add(message);
storage.Append(_conn.ConversationId, message);
- var hooks = _services.GetHooksOrderByPriority(_conn.CurrentAgentId);
- foreach (var hook in hooks)
+ var convHooks = _services.GetHooksOrderByPriority(_conn.CurrentAgentId);
+ foreach (var hook in convHooks)
{
hook.SetAgent(agent)
.SetConversation(conversation);
@@ -117,6 +115,16 @@ public class RealtimeHub : IRealtimeHub
}
}
}
+
+ var isReconnect = false;
+ var realtimeHooks = _services.GetHooks(_conn.CurrentAgentId);
+ foreach (var hook in realtimeHooks)
+ {
+ isReconnect = await hook.ShouldReconnect(_conn);
+ if (isReconnect) break;
+ }
+
+ return isReconnect;
},
onConversationItemCreated: async response =>
{
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs
index c9b3c90c..452f54c9 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs
@@ -16,14 +16,6 @@ public partial class AgentService
return refreshResult;
}
- var userIdentity = _services.GetRequiredService();
- var userService = _services.GetRequiredService();
- var (isValid, _) = await userService.IsAdminUser(userIdentity.Id);
- if (!isValid)
- {
- return "Unauthorized user.";
- }
-
var agentDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
dbSettings.FileRepository,
_agentSettings.DataDir);
diff --git a/src/Infrastructure/BotSharp.Core/Session/BotSharpRealtimeSession.cs b/src/Infrastructure/BotSharp.Core/Session/BotSharpRealtimeSession.cs
index 7f5f6c15..8dc5b08d 100644
--- a/src/Infrastructure/BotSharp.Core/Session/BotSharpRealtimeSession.cs
+++ b/src/Infrastructure/BotSharp.Core/Session/BotSharpRealtimeSession.cs
@@ -11,6 +11,7 @@ public class BotSharpRealtimeSession : IDisposable
private readonly ChatSessionOptions? _sessionOptions;
private readonly object _singleReceiveLock = new();
private AsyncWebsocketDataCollectionResult _receivedCollectionResult;
+ private bool _disposed = false;
public BotSharpRealtimeSession(
IServiceProvider services,
@@ -57,23 +58,30 @@ public class BotSharpRealtimeSession : IDisposable
public async Task SendEventAsync(string message)
{
- if (_websocket.State == WebSocketState.Open)
+ if (_disposed || _websocket.State != WebSocketState.Open)
{
- var buffer = Encoding.UTF8.GetBytes(message);
- await _websocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
+ return;
}
+
+ var buffer = Encoding.UTF8.GetBytes(message);
+ await _websocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
}
public async Task DisconnectAsync()
{
- if (_websocket.State == WebSocketState.Open)
+ if (_disposed || _websocket.State != WebSocketState.Open)
{
- await _websocket.CloseAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None);
+ return;
}
+
+ await _websocket.CloseAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None);
}
public void Dispose()
{
+ if (_disposed) return;
+
+ _disposed = true;
_websocket.Dispose();
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs b/src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs
index 60ecee04..911338b5 100644
--- a/src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs
+++ b/src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs
@@ -13,6 +13,7 @@ public class LlmRealtimeSession : IDisposable
private readonly object _singleReceiveLock = new();
private readonly SemaphoreSlim _clientEventSemaphore = new(initialCount: 1, maxCount: 1);
private AsyncWebsocketDataCollectionResult _receivedCollectionResult;
+ private bool _disposed = false;
public LlmRealtimeSession(
IServiceProvider services,
@@ -24,6 +25,7 @@ public class LlmRealtimeSession : IDisposable
public async Task ConnectAsync(Uri uri, Dictionary? headers = null, CancellationToken cancellationToken = default)
{
+ _disposed = false;
_webSocket?.Dispose();
_webSocket = new ClientWebSocket();
@@ -73,31 +75,43 @@ public class LlmRealtimeSession : IDisposable
public async Task SendEventToModelAsync(object message)
{
- if (_webSocket.State != WebSocketState.Open)
- {
- return;
- }
-
- await _clientEventSemaphore.WaitAsync();
-
try
{
+ if (_disposed)
+ {
+ return;
+ }
+
+ await _clientEventSemaphore.WaitAsync();
+
+ if (_webSocket.State != WebSocketState.Open)
+ {
+ return;
+ }
+
if (message is not string data)
{
data = JsonSerializer.Serialize(message, _sessionOptions?.JsonOptions);
}
+ //Console.WriteLine($"Sending event to model {data.Substring(0, 20)}");
+
var buffer = Encoding.UTF8.GetBytes(data);
await _webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
}
finally
{
- _clientEventSemaphore.Release();
+ if (!_disposed)
+ {
+ _clientEventSemaphore.Release();
+ }
}
}
public async Task DisconnectAsync()
{
+ if (_disposed) return;
+
if (_webSocket.State == WebSocketState.Open)
{
await _webSocket.CloseAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None);
@@ -106,6 +120,9 @@ public class LlmRealtimeSession : IDisposable
public void Dispose()
{
+ if (_disposed) return;
+
+ _disposed = true;
_clientEventSemaphore?.Dispose();
_webSocket?.Dispose();
}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs
index 401a693f..52983506 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
+using BotSharp.Abstraction.Infrastructures.Attributes;
namespace BotSharp.OpenAPI.Controllers;
@@ -108,6 +109,7 @@ public class AgentController : ControllerBase
return AgentViewModel.FromAgent(createdAgent);
}
+ [BotSharpAuth]
[HttpPost("/refresh-agents")]
public async Task RefreshAgents()
{
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs
index 4f6d3f2b..b74cc68a 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs
@@ -1,3 +1,4 @@
+using BotSharp.Abstraction.Infrastructures.Attributes;
using BotSharp.Abstraction.Plugins.Models;
using BotSharp.Abstraction.Users.Enums;
using BotSharp.Core.Plugins;
@@ -10,15 +11,10 @@ public class PluginController(IServiceProvider services, IUserIdentity user, Plu
{
private readonly IUserIdentity _user = user;
+ [BotSharpAuth]
[HttpGet("/plugins")]
public async Task> GetPlugins([FromQuery] PluginFilter filter)
{
- var isValid = await IsValidUser();
- if (!isValid)
- {
- return new PagedItems();
- }
-
var loader = services.GetRequiredService();
return loader.GetPagedPlugins(services, filter);
}
@@ -72,6 +68,7 @@ public class PluginController(IServiceProvider services, IUserIdentity user, Plu
return menu;
}
+ [BotSharpAuth]
[HttpPost("/plugin/{id}/install")]
public PluginDef InstallPlugin([FromRoute] string id)
{
@@ -79,17 +76,11 @@ public class PluginController(IServiceProvider services, IUserIdentity user, Plu
return loader.UpdatePluginStatus(services, id, true);
}
+ [BotSharpAuth]
[HttpPost("/plugin/{id}/remove")]
public PluginDef RemovePluginStats([FromRoute] string id)
{
var loader = services.GetRequiredService();
return loader.UpdatePluginStatus(services, id, false);
}
-
- private async Task IsValidUser()
- {
- var userService = services.GetRequiredService();
- var (isAdmin, _) = await userService.IsAdminUser(_user.Id);
- return isAdmin;
- }
}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/RoleController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/RoleController.cs
index ee3ac863..7b356f93 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/RoleController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/RoleController.cs
@@ -1,3 +1,4 @@
+using BotSharp.Abstraction.Infrastructures.Attributes;
using BotSharp.Abstraction.Roles;
namespace BotSharp.OpenAPI.Controllers;
@@ -20,15 +21,10 @@ public class RoleController : ControllerBase
_user = user;
}
+ [BotSharpAuth]
[HttpPost("/role/refresh")]
public async Task RefreshRoles()
{
- var isValid = await IsValidUser();
- if (!isValid)
- {
- return false;
- }
-
return await _roleService.RefreshRoles();
}
@@ -39,6 +35,7 @@ public class RoleController : ControllerBase
return await _roleService.GetRoleOptions();
}
+ [BotSharpAuth]
[HttpPost("/roles")]
public async Task> GetRoles([FromBody] RoleFilter? filter = null)
{
@@ -47,12 +44,6 @@ public class RoleController : ControllerBase
filter = RoleFilter.Empty();
}
- var isValid = await IsValidUser();
- if (!isValid)
- {
- return Enumerable.Empty();
- }
-
var roles = await _roleService.GetRoles(filter);
return roles.Select(x => RoleViewModel.FromRole(x)).ToList();
}
@@ -64,25 +55,13 @@ public class RoleController : ControllerBase
return RoleViewModel.FromRole(role);
}
+ [BotSharpAuth]
[HttpPut("/role")]
public async Task UpdateRole([FromBody] RoleUpdateModel model)
{
if (model == null) return false;
- var isValid = await IsValidUser();
- if (!isValid)
- {
- return false;
- }
-
var role = RoleUpdateModel.ToRole(model);
return await _roleService.UpdateRole(role, isUpdateRoleAgents: true);
}
-
- private async Task IsValidUser()
- {
- var userService = _services.GetRequiredService();
- var (isAdmin, _) = await userService.IsAdminUser(_user.Id);
- return isAdmin;
- }
}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs
index 7cb1aa2b..88a10838 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs
@@ -1,3 +1,4 @@
+using BotSharp.Abstraction.Infrastructures.Attributes;
using BotSharp.Abstraction.Users.Settings;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
@@ -181,12 +182,6 @@ public class UserController : ControllerBase
public async Task> GetUsers([FromBody] UserFilter filter)
{
var userService = _services.GetRequiredService();
- var isValid = await IsValidUser();
- if (!isValid)
- {
- return new PagedItems();
- }
-
var users = await userService.GetUsers(filter);
var views = users.Items.Select(x => UserViewModel.FromUser(x)).ToList();
@@ -197,6 +192,7 @@ public class UserController : ControllerBase
};
}
+ [BotSharpAuth]
[HttpGet("/user/{id}/details")]
public async Task GetUserDetails(string id)
{
@@ -205,17 +201,12 @@ public class UserController : ControllerBase
return UserViewModel.FromUser(user);
}
+ [BotSharpAuth]
[HttpPut("/user")]
public async Task UpdateUser([FromBody] UserUpdateModel model)
{
if (model == null) return false;
- var isValid = await IsValidUser();
- if (!isValid)
- {
- return false;
- }
-
var userService = _services.GetRequiredService();
var updated = await userService.UpdateUser(UserUpdateModel.ToUser(model), isUpdateUserAgents: true);
return updated;
@@ -251,13 +242,6 @@ public class UserController : ControllerBase
#region Private methods
- private async Task IsValidUser()
- {
- var userService = _services.GetRequiredService();
- var (isAdmin, _) = await userService.IsAdminUser(_user.Id);
- return isAdmin;
- }
-
private FileContentResult BuildFileResult(string file)
{
var fileStorage = _services.GetRequiredService();
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs
index e2d9a161..114ea69d 100644
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs
@@ -305,7 +305,7 @@ public class ChatCompletionProvider : IChatCompletion
{
messages.Add(new AssistantChatMessage(new List
{
- ChatToolCall.CreateFunctionToolCall(message.ToolCallId ?? message.FunctionName, message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}"))
+ ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}"))
}));
messages.Add(new ToolChatMessage(message.ToolCallId ?? message.FunctionName, message.Content));
diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Models/Realtime/RealtimeTranscriptionResponse.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Models/Realtime/RealtimeTranscriptionResponse.cs
index 189252fa..0a383c80 100644
--- a/src/Plugins/BotSharp.Plugin.GoogleAI/Models/Realtime/RealtimeTranscriptionResponse.cs
+++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Models/Realtime/RealtimeTranscriptionResponse.cs
@@ -9,6 +9,8 @@ internal class RealtimeTranscriptionResponse : IDisposable
}
+ private bool _disposed = false;
+
private MemoryStream _contentStream = new();
public Stream? ContentStream
{
@@ -20,6 +22,8 @@ internal class RealtimeTranscriptionResponse : IDisposable
public void Collect(string text)
{
+ if (_disposed) return;
+
var binary = BinaryData.FromString(text);
var bytes = binary.ToArray();
@@ -30,7 +34,7 @@ internal class RealtimeTranscriptionResponse : IDisposable
public string GetText()
{
- if (_contentStream.Length == 0)
+ if (_disposed || _contentStream.Length == 0)
{
return string.Empty;
}
@@ -42,12 +46,21 @@ internal class RealtimeTranscriptionResponse : IDisposable
public void Clear()
{
- _contentStream.Position = 0;
- _contentStream.SetLength(0);
+ try
+ {
+ if (_disposed) return;
+
+ _contentStream.Position = 0;
+ _contentStream.SetLength(0);
+ }
+ catch { }
}
public void Dispose()
{
+ if (_disposed) return;
+
+ _disposed = true;
_contentStream?.Dispose();
}
}
diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs
index 5df8ee13..6f3d9d26 100644
--- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs
@@ -14,7 +14,7 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
public string Provider => "google-ai";
public string Model => _model;
- private string _model = GoogleAIModels.Gemini2FlashExp;
+ private string _model = GoogleAIModels.Gemini2FlashLive001;
private readonly IServiceProvider _services;
private readonly ILogger _logger;
@@ -35,14 +35,14 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
private RealtimeTranscriptionResponse _inputStream = new();
private RealtimeTranscriptionResponse _outputStream = new();
-
+ private bool _isBlocking = false;
private RealtimeHubConnection _conn;
private Func _onModelReady;
private Func _onModelAudioDeltaReceived;
private Func _onModelAudioResponseDone;
private Func _onModelAudioTranscriptDone;
- private Func, Task> _onModelResponseDone;
+ private Func, Task> _onModelResponseDone;
private Func _onConversationItemCreated;
private Func _onInputAudioTranscriptionDone;
private Func _onInterruptionDetected;
@@ -68,7 +68,7 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
Func onModelAudioDeltaReceived,
Func onModelAudioResponseDone,
Func onModelAudioTranscriptDone,
- Func, Task> onModelResponseDone,
+ Func, Task> onModelResponseDone,
Func onConversationItemCreated,
Func onInputAudioTranscriptionDone,
Func onInterruptionDetected)
@@ -90,6 +90,7 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
var modelSettings = settingsService.GetSetting(Provider, _model);
Reset();
+ _isBlocking = true;
_inputStream = new();
_outputStream = new();
_session = new LlmRealtimeSession(_services, new ChatSessionOptions
@@ -105,6 +106,8 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
private async Task ReceiveMessage()
{
+ var isReconnect = false;
+
await foreach (ChatSessionUpdate update in _session.ReceiveUpdatesAsync(CancellationToken.None))
{
var receivedText = update?.RawResponse;
@@ -124,6 +127,7 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
if (response.SetupComplete != null)
{
_logger.LogInformation($"Session setup completed.");
+ _isBlocking = false;
}
else if (response.SessionResumptionUpdate != null)
{
@@ -138,7 +142,7 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
if (functionCall != null)
{
var messages = OnFunctionCall(_conn, functionCall);
- await _onModelResponseDone(messages);
+ isReconnect = await _onModelResponseDone(messages);
}
}
else if (response.ServerContent != null)
@@ -155,8 +159,6 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
if (response.ServerContent.ModelTurn != null)
{
- _logger.LogInformation($"Model audio delta received.");
-
// Handle input transcription
var inputTranscription = _inputStream.GetText();
if (!string.IsNullOrEmpty(inputTranscription))
@@ -188,31 +190,62 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
// Handle output transcription
var outputTranscription = _outputStream.GetText();
- if (!string.IsNullOrEmpty(outputTranscription))
- {
- var messages = await OnResponseDone(_conn, outputTranscription, response.UsageMetaData);
- await _onModelResponseDone(messages);
- }
+ var messages = await OnResponseDone(_conn, outputTranscription ?? string.Empty, response.UsageMetaData);
+ isReconnect = await _onModelResponseDone(messages);
_inputStream.Clear();
_outputStream.Clear();
}
}
+
+ if (isReconnect)
+ {
+ break;
+ }
}
catch (Exception ex)
{
- _logger.LogError(ex, $"Error when deserializing server response. {ex.Message}");
+ _logger.LogError(ex, $"Error when handling server response. {ex.Message}");
break;
}
}
- _inputStream.Dispose();
- _outputStream.Dispose();
- _session.Dispose();
+ if (isReconnect)
+ {
+ await Reconnect(_conn);
+ }
+ else
+ {
+ _inputStream.Dispose();
+ _outputStream.Dispose();
+ _session.Dispose();
+ }
}
+ public async Task Reconnect(RealtimeHubConnection conn)
+ {
+ _logger.LogInformation($"Reconnecting {Provider} realtime server...");
+
+ _isBlocking = true;
+ _conn = conn;
+ await Disconnect();
+ await Task.Delay(500);
+ await Connect(
+ _conn,
+ _onModelReady,
+ _onModelAudioDeltaReceived,
+ _onModelAudioResponseDone,
+ _onModelAudioTranscriptDone,
+ _onModelResponseDone,
+ _onConversationItemCreated,
+ _onInputAudioTranscriptionDone,
+ _onInterruptionDetected);
+ }
+
public async Task Disconnect()
{
+ _logger.LogInformation($"Disconnecting {Provider} realtime server...");
+
if (_session != null)
{
_inputStream?.Dispose();
@@ -224,6 +257,8 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
public async Task AppenAudioBuffer(string message)
{
+ if (_isBlocking) return;
+
await SendEventToModel(new RealtimeClientPayload
{
RealtimeInput = new()
@@ -235,6 +270,8 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
public async Task AppenAudioBuffer(ArraySegment data, int length)
{
+ if (_isBlocking) return;
+
var buffer = data.AsSpan(0, length).ToArray();
await SendEventToModel(new RealtimeClientPayload
{
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
index c0a9c0d4..074c9b48 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
@@ -272,7 +272,7 @@ public class ChatCompletionProvider : IChatCompletion
{
messages.Add(new AssistantChatMessage(new List
{
- ChatToolCall.CreateFunctionToolCall(message.ToolCallId ?? message.FunctionName, message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}"))
+ ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}"))
}));
messages.Add(new ToolChatMessage(message.ToolCallId ?? message.FunctionName, message.Content));
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
index 52f63447..a2a4dba3 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
@@ -16,8 +16,19 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
private readonly ILogger _logger;
private readonly BotSharpOptions _botsharpOptions;
- protected string _model = "gpt-4o-mini-realtime-preview";
+ private string _model = "gpt-4o-mini-realtime-preview";
private LlmRealtimeSession _session;
+ private bool _isBlocking = false;
+
+ private RealtimeHubConnection _conn;
+ private Func _onModelReady;
+ private Func _onModelAudioDeltaReceived;
+ private Func _onModelAudioResponseDone;
+ private Func _onModelAudioTranscriptDone;
+ private Func, Task> _onModelResponseDone;
+ private Func _onConversationItemCreated;
+ private Func _onInputAudioTranscriptionDone;
+ private Func _onInterruptionDetected;
public RealTimeCompletionProvider(
IServiceProvider services,
@@ -35,11 +46,21 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
Func onModelAudioDeltaReceived,
Func onModelAudioResponseDone,
Func onModelAudioTranscriptDone,
- Func, Task> onModelResponseDone,
+ Func, Task> onModelResponseDone,
Func onConversationItemCreated,
Func onInputAudioTranscriptionDone,
Func onInterruptionDetected)
{
+ _conn = conn;
+ _onModelReady = onModelReady;
+ _onModelAudioDeltaReceived = onModelAudioDeltaReceived;
+ _onModelAudioResponseDone = onModelAudioResponseDone;
+ _onModelAudioTranscriptDone = onModelAudioTranscriptDone;
+ _onModelResponseDone = onModelResponseDone;
+ _onConversationItemCreated = onConversationItemCreated;
+ _onInputAudioTranscriptionDone = onInputAudioTranscriptionDone;
+ _onInterruptionDetected = onInterruptionDetected;
+
var settingsService = _services.GetRequiredService();
var realtimeSettings = _services.GetRequiredService();
@@ -61,31 +82,12 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
},
cancellationToken: CancellationToken.None);
- _ = ReceiveMessage(
- realtimeSettings,
- conn,
- onModelReady,
- onModelAudioDeltaReceived,
- onModelAudioResponseDone,
- onModelAudioTranscriptDone,
- onModelResponseDone,
- onConversationItemCreated,
- onInputAudioTranscriptionDone,
- onInterruptionDetected);
+ _ = ReceiveMessage(realtimeSettings);
}
- private async Task ReceiveMessage(
- RealtimeModelSettings realtimeSettings,
- RealtimeHubConnection conn,
- Func onModelReady,
- Func onModelAudioDeltaReceived,
- Func onModelAudioResponseDone,
- Func onModelAudioTranscriptDone,
- Func, Task> onModelResponseDone,
- Func onConversationItemCreated,
- Func onInputAudioTranscriptionDone,
- Func onInterruptionDetected)
+ private async Task ReceiveMessage(RealtimeModelSettings realtimeSettings)
{
+ var isReconnect = false;
DateTime? startTime = null;
await foreach (ChatSessionUpdate update in _session.ReceiveUpdatesAsync(CancellationToken.None))
@@ -121,7 +123,8 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
else if (response.Type == "session.created")
{
_logger.LogInformation($"{response.Type}: {receivedText}");
- await onModelReady();
+ _isBlocking = false;
+ await _onModelReady();
}
else if (response.Type == "session.updated")
{
@@ -135,7 +138,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
{
_logger.LogInformation($"{response.Type}: {receivedText}");
var data = JsonSerializer.Deserialize(receivedText);
- await onModelAudioTranscriptDone(data.Transcript);
+ await _onModelAudioTranscriptDone(data.Transcript);
}
else if (response.Type == "response.audio.delta")
{
@@ -143,13 +146,13 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
if (audio?.Delta != null)
{
_logger.LogDebug($"{response.Type}: {receivedText}");
- await onModelAudioDeltaReceived(audio.Delta, audio.ItemId);
+ await _onModelAudioDeltaReceived(audio.Delta, audio.ItemId);
}
}
else if (response.Type == "response.audio.done")
{
_logger.LogInformation($"{response.Type}: {receivedText}");
- await onModelAudioResponseDone();
+ await _onModelAudioResponseDone();
}
else if (response.Type == "response.done")
{
@@ -159,14 +162,14 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
{
if (data.StatusDetails.Type == "incomplete" && data.StatusDetails.Reason == "max_output_tokens")
{
- await onInterruptionDetected();
+ await _onInterruptionDetected();
await TriggerModelInference("Response user concisely");
}
}
else
{
- var messages = await OnResponsedDone(conn, receivedText);
- await onModelResponseDone(messages);
+ var messages = await OnResponsedDone(_conn, receivedText);
+ isReconnect = await _onModelResponseDone(messages);
}
}
else if (response.Type == "conversation.item.created")
@@ -179,23 +182,23 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
startTime = DateTime.UtcNow;
}
- await onConversationItemCreated(receivedText);
+ await _onConversationItemCreated(receivedText);
}
else if (response.Type == "conversation.item.input_audio_transcription.completed")
{
_logger.LogInformation($"{response.Type}: {receivedText}");
- var message = await OnUserAudioTranscriptionCompleted(conn, receivedText);
+ var message = await OnUserAudioTranscriptionCompleted(_conn, receivedText);
if (!string.IsNullOrEmpty(message.Content))
{
- await onInputAudioTranscriptionDone(message);
+ await _onInputAudioTranscriptionDone(message);
}
}
else if (response.Type == "input_audio_buffer.speech_started")
{
_logger.LogInformation($"{response.Type}: {receivedText}");
// Handle user interuption
- await onInterruptionDetected();
+ await _onInterruptionDetected();
}
else if (response.Type == "input_audio_buffer.speech_stopped")
{
@@ -205,13 +208,48 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
{
_logger.LogInformation($"{response.Type}: {receivedText}");
}
+
+ if (isReconnect)
+ {
+ break;
+ }
}
- _session.Dispose();
+ if (isReconnect)
+ {
+ await Reconnect(_conn);
+ }
+ else
+ {
+ _session.Dispose();
+ }
+ }
+
+
+ public async Task Reconnect(RealtimeHubConnection conn)
+ {
+ _logger.LogInformation($"Reconnecting {Provider} realtime server...");
+
+ _isBlocking = true;
+ _conn = conn;
+ await Disconnect();
+ await Task.Delay(500);
+ await Connect(
+ _conn,
+ _onModelReady,
+ _onModelAudioDeltaReceived,
+ _onModelAudioResponseDone,
+ _onModelAudioTranscriptDone,
+ _onModelResponseDone,
+ _onConversationItemCreated,
+ _onInputAudioTranscriptionDone,
+ _onInterruptionDetected);
}
public async Task Disconnect()
{
+ _logger.LogInformation($"Disconnecting {Provider} realtime server...");
+
if (_session != null)
{
await _session.DisconnectAsync();
@@ -221,6 +259,8 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
public async Task AppenAudioBuffer(string message)
{
+ if (_isBlocking) return;
+
var audioAppend = new
{
type = "input_audio_buffer.append",
@@ -232,6 +272,8 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
public async Task AppenAudioBuffer(ArraySegment data, int length)
{
+ if (_isBlocking) return;
+
var message = Convert.ToBase64String(data.AsSpan(0, length).ToArray());
await AppenAudioBuffer(message);
}
diff --git a/tests/BotSharp.LLM.Tests/GoogleRealTimeTests.cs b/tests/BotSharp.LLM.Tests/GoogleRealTimeTests.cs
index 061f0545..8e4202a7 100644
--- a/tests/BotSharp.LLM.Tests/GoogleRealTimeTests.cs
+++ b/tests/BotSharp.LLM.Tests/GoogleRealTimeTests.cs
@@ -46,7 +46,7 @@ namespace BotSharp.Plugin.Google.Core
async (s, s1) => { Console.WriteLine(s); },
async () => { },
async (s) => { Console.WriteLine(s); },
- async list => { Console.WriteLine(list); },
+ async list => { Console.WriteLine(list); return false; },
async s => { Console.WriteLine(s); },
async model => { Console.WriteLine(model); },
async () => { Console.WriteLine("UserInterrupted"); });
From b0b6c4b651578616f9757f395429ae088e2c5b7e Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Mon, 19 May 2025 15:02:28 -0500
Subject: [PATCH 02/11] remove comments
---
src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs b/src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs
index 911338b5..fe2831bc 100644
--- a/src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs
+++ b/src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs
@@ -94,8 +94,6 @@ public class LlmRealtimeSession : IDisposable
data = JsonSerializer.Serialize(message, _sessionOptions?.JsonOptions);
}
- //Console.WriteLine($"Sending event to model {data.Substring(0, 20)}");
-
var buffer = Encoding.UTF8.GetBytes(data);
await _webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
}
From 73c2e9c72ea7d4c912ae0e192105a99db5df9f36 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Mon, 19 May 2025 15:44:20 -0500
Subject: [PATCH 03/11] minor refine
---
.../MLTasks/IRealTimeCompletion.cs | 2 +-
.../Services/RealtimeHub.cs | 5 ++-
.../Realtime/RealTimeCompletionProvider.cs | 43 ++++++++-----------
...rosoftExtensionsAITextEmbeddingProvider.cs | 2 +-
.../BotSharp.Plugin.OpenAI.csproj | 4 ++
.../Realtime/RealTimeCompletionProvider.cs | 23 +++-------
.../BotSharp.LLM.Tests/GoogleRealTimeTests.cs | 2 +-
7 files changed, 34 insertions(+), 47 deletions(-)
diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs
index 733eef92..bb3104d3 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs
@@ -14,7 +14,7 @@ public interface IRealTimeCompletion
Func onModelAudioDeltaReceived,
Func onModelAudioResponseDone,
Func onModelAudioTranscriptDone,
- Func, Task> onModelResponseDone,
+ Func, Task> onModelResponseDone,
Func onConversationItemCreated,
Func onInputAudioTranscriptionDone,
Func onInterruptionDetected);
diff --git a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs
index 53318a5f..baac131c 100644
--- a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs
+++ b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs
@@ -124,7 +124,10 @@ public class RealtimeHub : IRealtimeHub
if (isReconnect) break;
}
- return isReconnect;
+ if (isReconnect)
+ {
+ await _completer.Reconnect(_conn);
+ }
},
onConversationItemCreated: async response =>
{
diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs
index 6f3d9d26..f708092d 100644
--- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs
@@ -42,7 +42,7 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
private Func _onModelAudioDeltaReceived;
private Func _onModelAudioResponseDone;
private Func _onModelAudioTranscriptDone;
- private Func, Task> _onModelResponseDone;
+ private Func, Task> _onModelResponseDone;
private Func _onConversationItemCreated;
private Func _onInputAudioTranscriptionDone;
private Func _onInterruptionDetected;
@@ -68,11 +68,13 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
Func onModelAudioDeltaReceived,
Func onModelAudioResponseDone,
Func onModelAudioTranscriptDone,
- Func, Task> onModelResponseDone,
+ Func, Task> onModelResponseDone,
Func onConversationItemCreated,
Func onInputAudioTranscriptionDone,
Func onInterruptionDetected)
{
+ _logger.LogInformation($"Connecting {Provider} realtime server...");
+
_conn = conn;
_onModelReady = onModelReady;
_onModelAudioDeltaReceived = onModelAudioDeltaReceived;
@@ -106,8 +108,6 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
private async Task ReceiveMessage()
{
- var isReconnect = false;
-
await foreach (ChatSessionUpdate update in _session.ReceiveUpdatesAsync(CancellationToken.None))
{
var receivedText = update?.RawResponse;
@@ -142,7 +142,7 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
if (functionCall != null)
{
var messages = OnFunctionCall(_conn, functionCall);
- isReconnect = await _onModelResponseDone(messages);
+ await _onModelResponseDone(messages);
}
}
else if (response.ServerContent != null)
@@ -161,9 +161,9 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
{
// Handle input transcription
var inputTranscription = _inputStream.GetText();
- if (!string.IsNullOrEmpty(inputTranscription))
+ if (!string.IsNullOrWhiteSpace(inputTranscription))
{
- var message = OnUserAudioTranscriptionCompleted(_conn, inputTranscription);
+ var message = OnUserAudioTranscriptionCompleted(_conn, inputTranscription ?? string.Empty);
await _onInputAudioTranscriptionDone(message);
}
_inputStream.Clear();
@@ -190,17 +190,15 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
// Handle output transcription
var outputTranscription = _outputStream.GetText();
- var messages = await OnResponseDone(_conn, outputTranscription ?? string.Empty, response.UsageMetaData);
- isReconnect = await _onModelResponseDone(messages);
+ if (!string.IsNullOrWhiteSpace(outputTranscription))
+ {
+ var messages = await OnResponseDone(_conn, outputTranscription ?? string.Empty, response.UsageMetaData);
+ await _onModelResponseDone(messages);
+ }
_inputStream.Clear();
_outputStream.Clear();
}
}
-
- if (isReconnect)
- {
- break;
- }
}
catch (Exception ex)
{
@@ -209,16 +207,9 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
}
}
- if (isReconnect)
- {
- await Reconnect(_conn);
- }
- else
- {
- _inputStream.Dispose();
- _outputStream.Dispose();
- _session.Dispose();
- }
+ _inputStream.Dispose();
+ _outputStream.Dispose();
+ _session.Dispose();
}
@@ -369,8 +360,8 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
Model = Model.ToModelId(),
SystemInstruction = request.SystemInstruction,
Tools = request.Tools?.ToArray(),
- InputAudioTranscription = realtimeSetting.InputAudioTranscribe ? new() : null,
- OutputAudioTranscription = realtimeSetting.InputAudioTranscribe ? new() : null
+ InputAudioTranscription = new(),
+ OutputAudioTranscription = new()
}
};
diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAITextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAITextEmbeddingProvider.cs
index 1758468a..d7933a1f 100644
--- a/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAITextEmbeddingProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAITextEmbeddingProvider.cs
@@ -27,7 +27,7 @@ public sealed class MicrosoftExtensionsAITextEmbeddingProvider : ITextEmbedding
///
public async Task GetVectorAsync(string text) =>
- (await _generator.GenerateEmbeddingVectorAsync(text, CreateOptions())).ToArray();
+ (await _generator.GenerateVectorAsync(text, CreateOptions())).ToArray();
///
public async Task> GetVectorsAsync(List texts)
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj b/src/Plugins/BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj
index 2455e7b5..e54033b6 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj
@@ -10,6 +10,10 @@
$(SolutionDir)packages
+
+
+
+
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
index a2a4dba3..d008cc60 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
@@ -25,7 +25,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
private Func _onModelAudioDeltaReceived;
private Func _onModelAudioResponseDone;
private Func _onModelAudioTranscriptDone;
- private Func, Task> _onModelResponseDone;
+ private Func, Task> _onModelResponseDone;
private Func _onConversationItemCreated;
private Func _onInputAudioTranscriptionDone;
private Func _onInterruptionDetected;
@@ -46,11 +46,13 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
Func onModelAudioDeltaReceived,
Func onModelAudioResponseDone,
Func onModelAudioTranscriptDone,
- Func, Task> onModelResponseDone,
+ Func, Task> onModelResponseDone,
Func onConversationItemCreated,
Func onInputAudioTranscriptionDone,
Func onInterruptionDetected)
{
+ _logger.LogInformation($"Connecting {Provider} realtime server...");
+
_conn = conn;
_onModelReady = onModelReady;
_onModelAudioDeltaReceived = onModelAudioDeltaReceived;
@@ -87,7 +89,6 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
private async Task ReceiveMessage(RealtimeModelSettings realtimeSettings)
{
- var isReconnect = false;
DateTime? startTime = null;
await foreach (ChatSessionUpdate update in _session.ReceiveUpdatesAsync(CancellationToken.None))
@@ -169,7 +170,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
else
{
var messages = await OnResponsedDone(_conn, receivedText);
- isReconnect = await _onModelResponseDone(messages);
+ await _onModelResponseDone(messages);
}
}
else if (response.Type == "conversation.item.created")
@@ -208,21 +209,9 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
{
_logger.LogInformation($"{response.Type}: {receivedText}");
}
-
- if (isReconnect)
- {
- break;
- }
}
- if (isReconnect)
- {
- await Reconnect(_conn);
- }
- else
- {
- _session.Dispose();
- }
+ _session.Dispose();
}
diff --git a/tests/BotSharp.LLM.Tests/GoogleRealTimeTests.cs b/tests/BotSharp.LLM.Tests/GoogleRealTimeTests.cs
index 8e4202a7..061f0545 100644
--- a/tests/BotSharp.LLM.Tests/GoogleRealTimeTests.cs
+++ b/tests/BotSharp.LLM.Tests/GoogleRealTimeTests.cs
@@ -46,7 +46,7 @@ namespace BotSharp.Plugin.Google.Core
async (s, s1) => { Console.WriteLine(s); },
async () => { },
async (s) => { Console.WriteLine(s); },
- async list => { Console.WriteLine(list); return false; },
+ async list => { Console.WriteLine(list); },
async s => { Console.WriteLine(s); },
async model => { Console.WriteLine(model); },
async () => { Console.WriteLine("UserInterrupted"); });
From aab468f41005df6054155d158998037541825180 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Tue, 20 May 2025 10:59:42 -0500
Subject: [PATCH 04/11] add session logging and crontab settings
---
.../Crontab/Settings/CrontabSettings.cs | 12 ++++++++++++
.../Realtime/Models/Session/ChatSessionOptions.cs | 3 +++
.../{ => Settings}/BotSharpDatabaseSettings.cs | 2 +-
.../BotSharp.Core.Crontab/CrontabPlugin.cs | 15 +++++++++++++--
src/Infrastructure/BotSharp.Core.Crontab/Using.cs | 1 +
.../Agents/Services/AgentService.CreateAgent.cs | 1 +
.../Agents/Services/AgentService.RefreshAgents.cs | 1 +
.../Agents/Services/AgentService.UpdateAgent.cs | 1 +
.../BotSharp.Core/Agents/Services/AgentService.cs | 1 +
.../BotSharp.Core/BotSharpCoreExtensions.cs | 12 +-----------
.../Services/Storage/LocalFileStorageService.cs | 1 +
.../Infrastructures/DistributedLocker.cs | 4 ++++
.../AsyncWebsocketDataCollectionResult.cs | 9 ++++-----
.../AsyncWebsocketDataResultEnumerator.cs | 14 +++++++++-----
.../BotSharp.Core/Repository/DataContextHelper.cs | 1 +
.../Repository/FileRepository/FileRepository.cs | 1 +
.../BotSharp.Core/Repository/RepositoryPlugin.cs | 1 +
.../Provider/NativeWhisperProvider.cs | 1 +
.../ChatStreamMiddleware.cs | 1 +
.../Realtime/RealTimeCompletionProvider.cs | 4 +++-
.../MongoDbContext.cs | 2 ++
.../MongoStoragePlugin.cs | 1 +
.../Realtime/RealTimeCompletionProvider.cs | 4 +++-
src/WebStarter/appsettings.json | 11 +++++++++++
24 files changed, 78 insertions(+), 26 deletions(-)
create mode 100644 src/Infrastructure/BotSharp.Abstraction/Crontab/Settings/CrontabSettings.cs
rename src/Infrastructure/BotSharp.Abstraction/Repositories/{ => Settings}/BotSharpDatabaseSettings.cs (95%)
diff --git a/src/Infrastructure/BotSharp.Abstraction/Crontab/Settings/CrontabSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Crontab/Settings/CrontabSettings.cs
new file mode 100644
index 00000000..4bca1240
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Crontab/Settings/CrontabSettings.cs
@@ -0,0 +1,12 @@
+namespace BotSharp.Abstraction.Crontab.Settings;
+
+public class CrontabSettings
+{
+ public CrontabBaseSetting EventSubscriber { get; set; } = new();
+ public CrontabBaseSetting Watcher { get; set; } = new();
+}
+
+public class CrontabBaseSetting
+{
+ public bool Enabled { get; set; } = true;
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Realtime/Models/Session/ChatSessionOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Realtime/Models/Session/ChatSessionOptions.cs
index 3bcba8ba..f4318fbb 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Realtime/Models/Session/ChatSessionOptions.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Realtime/Models/Session/ChatSessionOptions.cs
@@ -1,9 +1,12 @@
+using Microsoft.Extensions.Logging;
using System.Text.Json;
namespace BotSharp.Abstraction.Realtime.Models.Session;
public class ChatSessionOptions
{
+ public string Provider { get; set; }
public int? BufferSize { get; set; }
public JsonSerializerOptions? JsonOptions { get; set; }
+ public ILogger? Logger { get; set; }
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/BotSharpDatabaseSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Settings/BotSharpDatabaseSettings.cs
similarity index 95%
rename from src/Infrastructure/BotSharp.Abstraction/Repositories/BotSharpDatabaseSettings.cs
rename to src/Infrastructure/BotSharp.Abstraction/Repositories/Settings/BotSharpDatabaseSettings.cs
index 19f5b141..76fdf455 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Repositories/BotSharpDatabaseSettings.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Settings/BotSharpDatabaseSettings.cs
@@ -1,4 +1,4 @@
-namespace BotSharp.Abstraction.Repositories;
+namespace BotSharp.Abstraction.Repositories.Settings;
public class BotSharpDatabaseSettings : DatabaseBasicSettings
{
diff --git a/src/Infrastructure/BotSharp.Core.Crontab/CrontabPlugin.cs b/src/Infrastructure/BotSharp.Core.Crontab/CrontabPlugin.cs
index feef2546..51701968 100644
--- a/src/Infrastructure/BotSharp.Core.Crontab/CrontabPlugin.cs
+++ b/src/Infrastructure/BotSharp.Core.Crontab/CrontabPlugin.cs
@@ -32,11 +32,22 @@ public class CrontabPlugin : IBotSharpPlugin
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
+ var settings = new CrontabSettings();
+ config.Bind("Crontab", settings);
+ services.AddSingleton(settings);
+
services.AddScoped();
services.AddScoped();
services.AddScoped();
- services.AddHostedService();
- services.AddHostedService();
+ if (settings.Watcher?.Enabled == true)
+ {
+ services.AddHostedService();
+ }
+
+ if (settings.EventSubscriber?.Enabled == true)
+ {
+ services.AddHostedService();
+ }
}
}
diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Using.cs b/src/Infrastructure/BotSharp.Core.Crontab/Using.cs
index f1b7a838..e1c1bca5 100644
--- a/src/Infrastructure/BotSharp.Core.Crontab/Using.cs
+++ b/src/Infrastructure/BotSharp.Core.Crontab/Using.cs
@@ -6,6 +6,7 @@ global using Microsoft.Extensions.DependencyInjection;
global using BotSharp.Abstraction.Agents.Enums;
global using BotSharp.Abstraction.Crontab;
global using BotSharp.Abstraction.Crontab.Models;
+global using BotSharp.Abstraction.Crontab.Settings;
global using BotSharp.Abstraction.Agents;
global using BotSharp.Abstraction.Plugins;
global using BotSharp.Abstraction.Conversations.Models;
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs
index 5becbec6..fb4d49c4 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs
@@ -1,3 +1,4 @@
+using BotSharp.Abstraction.Repositories.Settings;
using BotSharp.Abstraction.Tasks.Models;
using BotSharp.Abstraction.Users.Enums;
using System.IO;
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs
index 452f54c9..25f7d2d5 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Repositories.Enums;
+using BotSharp.Abstraction.Repositories.Settings;
using System.IO;
namespace BotSharp.Core.Agents.Services;
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
index 8365639b..f2fb4ff7 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Repositories.Enums;
+using BotSharp.Abstraction.Repositories.Settings;
using BotSharp.Abstraction.Users.Enums;
using BotSharp.Abstraction.Users.Models;
using System.IO;
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs
index c54c4fd8..ca025447 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs
@@ -1,3 +1,4 @@
+using BotSharp.Abstraction.Repositories.Settings;
using System.IO;
using System.Reflection;
diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
index 19b6df53..5c00de0d 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
+++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
@@ -16,6 +16,7 @@ using BotSharp.Abstraction.Templating;
using BotSharp.Core.Templating;
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Realtime;
+using BotSharp.Abstraction.Repositories.Settings;
namespace BotSharp.Core;
@@ -71,17 +72,6 @@ public static class BotSharpCoreExtensions
return services;
}
- //public static IServiceCollection UsingFileRepository(this IServiceCollection services, IConfiguration config)
- //{
- // services.AddScoped(sp =>
- // {
- // var myDatabaseSettings = sp.GetRequiredService();
- // return new FileRepository(myDatabaseSettings, sp);
- // });
-
- // return services;
- //}
-
public static IApplicationBuilder UseBotSharp(this IApplicationBuilder app)
{
if (app == null)
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.cs
index ea2a68ac..6833ffa2 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.cs
@@ -1,3 +1,4 @@
+using BotSharp.Abstraction.Repositories.Settings;
using System.IO;
namespace BotSharp.Core.Files.Services;
diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/DistributedLocker.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/DistributedLocker.cs
index a040525f..30a967e8 100644
--- a/src/Infrastructure/BotSharp.Core/Infrastructures/DistributedLocker.cs
+++ b/src/Infrastructure/BotSharp.Core/Infrastructures/DistributedLocker.cs
@@ -24,7 +24,9 @@ public class DistributedLocker : IDistributedLocker
var redis = _services.GetService();
if (redis == null)
{
+#if !DEBUG
_logger.LogInformation($"The Redis server is experiencing issues and is not functioning as expected.");
+#endif
await action();
return true;
}
@@ -50,7 +52,9 @@ public class DistributedLocker : IDistributedLocker
var redis = _services.GetRequiredService();
if (redis == null)
{
+#if !DEBUG
_logger.LogWarning($"The Redis server is experiencing issues and is not functioning as expected.");
+#endif
action();
return false;
}
diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/Websocket/AsyncWebsocketDataCollectionResult.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/Websocket/AsyncWebsocketDataCollectionResult.cs
index 4f16397e..7364401a 100644
--- a/src/Infrastructure/BotSharp.Core/Infrastructures/Websocket/AsyncWebsocketDataCollectionResult.cs
+++ b/src/Infrastructure/BotSharp.Core/Infrastructures/Websocket/AsyncWebsocketDataCollectionResult.cs
@@ -1,4 +1,3 @@
-using BotSharp.Abstraction.Realtime.Models.Session;
using System.ClientModel;
using System.Net.WebSockets;
@@ -7,16 +6,16 @@ namespace BotSharp.Core.Infrastructures.Websocket;
internal class AsyncWebsocketDataCollectionResult : AsyncCollectionResult
{
private readonly WebSocket _webSocket;
- private readonly ChatSessionOptions? _sessionOptions;
+ private readonly ChatSessionOptions? _options;
private readonly CancellationToken _cancellationToken;
public AsyncWebsocketDataCollectionResult(
WebSocket webSocket,
- ChatSessionOptions? sessionOptions,
+ ChatSessionOptions? options,
CancellationToken cancellationToken)
{
_webSocket = webSocket;
- _sessionOptions = sessionOptions;
+ _options = options;
_cancellationToken = cancellationToken;
}
@@ -27,7 +26,7 @@ internal class AsyncWebsocketDataCollectionResult : AsyncCollectionResult GetRawPagesAsync()
{
- await using var enumerator = new AsyncWebsocketDataResultEnumerator(_webSocket, _sessionOptions, _cancellationToken);
+ await using var enumerator = new AsyncWebsocketDataResultEnumerator(_webSocket, _options, _cancellationToken);
while (await enumerator.MoveNextAsync().ConfigureAwait(false))
{
yield return enumerator.Current;
diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/Websocket/AsyncWebsocketDataResultEnumerator.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/Websocket/AsyncWebsocketDataResultEnumerator.cs
index f89127e2..59eac990 100644
--- a/src/Infrastructure/BotSharp.Core/Infrastructures/Websocket/AsyncWebsocketDataResultEnumerator.cs
+++ b/src/Infrastructure/BotSharp.Core/Infrastructures/Websocket/AsyncWebsocketDataResultEnumerator.cs
@@ -1,3 +1,4 @@
+using Microsoft.AspNetCore.Builder;
using System.Buffers;
using System.ClientModel;
using System.Net.WebSockets;
@@ -7,7 +8,7 @@ namespace BotSharp.Core.Infrastructures.Websocket;
internal class AsyncWebsocketDataResultEnumerator : IAsyncEnumerator
{
private readonly WebSocket _webSocket;
- private readonly ChatSessionOptions? _sessionOptions;
+ private readonly ChatSessionOptions? _options;
private readonly CancellationToken _cancellationToken;
private readonly byte[] _buffer;
@@ -15,13 +16,13 @@ internal class AsyncWebsocketDataResultEnumerator : IAsyncEnumerator 0 ? sessionOptions.BufferSize.Value : DEFAULT_BUFFER_SIZE;
+ var bufferSize = options?.BufferSize > 0 ? options.BufferSize.Value : DEFAULT_BUFFER_SIZE;
_buffer = ArrayPool.Shared.Rent(bufferSize);
}
@@ -44,7 +45,10 @@ internal class AsyncWebsocketDataResultEnumerator : IAsyncEnumerator
Date: Tue, 20 May 2025 13:20:13 -0500
Subject: [PATCH 05/11] fix console log
---
src/WebStarter/Program.cs | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/WebStarter/Program.cs b/src/WebStarter/Program.cs
index 814aa7e1..2c9c073c 100644
--- a/src/WebStarter/Program.cs
+++ b/src/WebStarter/Program.cs
@@ -9,7 +9,12 @@ using StackExchange.Redis;
var builder = WebApplication.CreateBuilder(args);
-builder.Host.UseSerilog();
+Log.Logger = new LoggerConfiguration()
+ .MinimumLevel.Information()
+ .WriteTo.Console()
+ .CreateLogger();
+
+builder.Host.UseSerilog(Log.Logger);
string[] allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get() ?? new[]
{
@@ -18,8 +23,6 @@ string[] allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get
"https://chat.scisharpstack.org"
};
-
-
// Add BotSharp
builder.Services.AddBotSharpCore(builder.Configuration, options =>
{
From 069170a01cc8798d77db2d40d9b17d9c7a821834 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Wed, 21 May 2025 17:34:38 -0500
Subject: [PATCH 06/11] add agent mode
---
.../BotSharp.Abstraction/Agents/Enums/AgentMode.cs | 7 +++++++
.../BotSharp.Abstraction/Agents/Models/Agent.cs | 6 +++---
.../Agents/Services/AgentService.RefreshAgents.cs | 2 +-
.../Agents/Services/AgentService.UpdateAgent.cs | 6 +++---
.../Services/ConversationService.SendMessage.cs | 4 ++--
.../Repository/FileRepository/FileRepository.Agent.cs | 5 +++--
.../BotSharp.Core/Routing/RoutingContext.cs | 4 ++--
.../ViewModels/Agents/Request/AgentCreationModel.cs | 8 +++++---
.../ViewModels/Agents/Request/AgentUpdateModel.cs | 7 +++++--
9 files changed, 31 insertions(+), 18 deletions(-)
create mode 100644 src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentMode.cs
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentMode.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentMode.cs
new file mode 100644
index 00000000..4e2cd60c
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentMode.cs
@@ -0,0 +1,7 @@
+namespace BotSharp.Abstraction.Agents.Enums;
+
+public class AgentMode
+{
+ public const string Eager = "eager";
+ public const string Lazy = "lazy";
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
index 1ba644e8..4562f16a 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
@@ -17,7 +17,7 @@ public class Agent
///
/// Routing Mode: lazy or eager
///
- public string Mode { get; set; } = "eager";
+ public string Mode { get; set; } = AgentMode.Eager;
public DateTime CreatedDateTime { get; set; }
public DateTime UpdatedDateTime { get; set; }
@@ -277,7 +277,7 @@ public class Agent
return this;
}
- public Agent SetAgentType(string type)
+ public Agent SetType(string type)
{
Type = type;
return this;
@@ -288,7 +288,7 @@ public class Agent
///
///
///
- public Agent SetAgentMode(string mode)
+ public Agent SetMode(string mode)
{
Mode = mode;
return this;
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs
index 25f7d2d5..9603d1e5 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs
@@ -67,7 +67,7 @@ public partial class AgentService
}
catch (Exception ex)
{
- _logger.LogError($"Failed to migrate agent in file directory: {dir}\r\nError: {ex.Message}");
+ _logger.LogError(ex, $"Failed to migrate agent in file directory: {dir}\r\nError: {ex.Message}");
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
index f2fb4ff7..50dbcad4 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
@@ -94,12 +94,12 @@ public partial class AgentService
{
clonedAgent.SetId(foundAgent.Id)
.SetName(foundAgent.Name)
- .SetDescription(foundAgent.Description)
+ .SetType(foundAgent.Type)
+ .SetMode(foundAgent.Mode)
.SetIsPublic(foundAgent.IsPublic)
.SetDisabled(foundAgent.Disabled)
+ .SetDescription(foundAgent.Description)
.SetMergeUtility(foundAgent.MergeUtility)
- .SetAgentType(foundAgent.Type)
- .SetAgentMode(foundAgent.Mode)
.SetProfiles(foundAgent.Profiles)
.SetLabels(foundAgent.Labels)
.SetRoutingRules(foundAgent.RoutingRules)
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs
index 1227d3dc..42e0de2c 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs
@@ -83,10 +83,10 @@ public partial class ConversationService
{
// Check the routing mode
var states = _services.GetRequiredService();
- var routingMode = states.GetState(StateConst.ROUTING_MODE, "eager");
+ var routingMode = states.GetState(StateConst.ROUTING_MODE, AgentMode.Eager);
routing.Context.Push(agent.Id, reason: "request started", updateLazyRouting: false);
- if (routingMode == "lazy")
+ if (routingMode == AgentMode.Lazy)
{
message.CurrentAgentId = states.GetState(StateConst.LAZY_ROUTING_AGENT_ID, message.CurrentAgentId);
routing.Context.Push(message.CurrentAgentId, reason: "lazy routing", updateLazyRouting: false);
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
index 930eb13d..5494f972 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
@@ -383,11 +383,12 @@ namespace BotSharp.Core.Repository
if (agent == null) return;
agent.Name = inputAgent.Name;
- agent.Description = inputAgent.Description;
+ agent.Type = inputAgent.Type;
+ agent.Mode = inputAgent.Mode;
agent.IsPublic = inputAgent.IsPublic;
agent.Disabled = inputAgent.Disabled;
+ agent.Description = inputAgent.Description;
agent.MergeUtility = inputAgent.MergeUtility;
- agent.Type = inputAgent.Type;
agent.Profiles = inputAgent.Profiles;
agent.Labels = inputAgent.Labels;
agent.Utilities = inputAgent.Utilities;
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
index b422a423..5a5a02da 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
@@ -290,8 +290,8 @@ public class RoutingContext : IRoutingContext
// Set next handling agent for lazy routing mode
var states = _services.GetRequiredService();
- var routingMode = states.GetState(StateConst.ROUTING_MODE, "eager");
- if (routingMode == "lazy")
+ var routingMode = states.GetState(StateConst.ROUTING_MODE, AgentMode.Eager);
+ if (routingMode == AgentMode.Lazy)
{
var agentId = GetCurrentAgentId();
if (agentId != BuiltInAgentId.Fallback)
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs
index 8265ebe4..2fd2c429 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs
@@ -8,6 +8,7 @@ public class AgentCreationModel
public string Name { get; set; }
public string Description { get; set; }
public string Type { get; set; } = AgentType.Task;
+ public string Mode { get; set; } = AgentMode.Eager;
///
/// LLM default system instructions
@@ -66,6 +67,10 @@ public class AgentCreationModel
return new Agent
{
Name = Name,
+ Type = Type,
+ Mode = Mode,
+ Disabled = Disabled,
+ IsPublic = IsPublic,
Description = Description,
Instruction = Instruction,
ChannelInstructions = ChannelInstructions,
@@ -75,9 +80,6 @@ public class AgentCreationModel
Samples = Samples,
Utilities = Utilities,
McpTools = McpTools,
- IsPublic = IsPublic,
- Type = Type,
- Disabled = Disabled,
MergeUtility = MergeUtility,
MaxMessageCount = MaxMessageCount,
Profiles = Profiles,
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs
index b11a9db0..da0c8b3a 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs
@@ -9,6 +9,8 @@ public class AgentUpdateModel
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Type { get; set; } = AgentType.Task;
+ public string Mode { get; set; } = AgentMode.Eager;
+
///
/// Instruction
///
@@ -93,12 +95,13 @@ public class AgentUpdateModel
var agent = new Agent()
{
Name = Name ?? string.Empty,
- Description = Description ?? string.Empty,
+ Type = Type,
+ Mode = Mode,
IsPublic = IsPublic,
Disabled = Disabled,
+ Description = Description ?? string.Empty,
MergeUtility = MergeUtility,
MaxMessageCount = MaxMessageCount,
- Type = Type,
Profiles = Profiles ?? [],
Labels = Labels ?? [],
RoutingRules = RoutingRules?.Select(x => RoutingRuleUpdateModel.ToDomainElement(x))?.ToList() ?? [],
From 2bf15f50784f13ea8d362123ea90f9841051115c Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Thu, 22 May 2025 13:58:42 -0500
Subject: [PATCH 07/11] refine routing mode
---
.../BotSharp.Abstraction/Agents/Enums/AgentField.cs | 2 +-
.../BotSharp.Abstraction/Agents/Models/Agent.cs | 4 +++-
.../Enums/AgentMode.cs => Routing/Enums/RoutingMode.cs} | 4 ++--
.../Services/ConversationService.SendMessage.cs | 5 +++--
.../Repository/FileRepository/FileRepository.Agent.cs | 6 +++---
src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs | 5 +++--
.../ViewModels/Agents/Request/AgentCreationModel.cs | 7 ++++++-
.../ViewModels/Agents/Request/AgentUpdateModel.cs | 6 +++++-
.../ViewModels/Agents/View/AgentViewModel.cs | 4 +++-
.../Collections/AgentDocument.cs | 2 +-
.../Repository/MongoRepository.Agent.cs | 6 +++---
11 files changed, 33 insertions(+), 18 deletions(-)
rename src/Infrastructure/BotSharp.Abstraction/{Agents/Enums/AgentMode.cs => Routing/Enums/RoutingMode.cs} (54%)
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs
index 0cab0dfb..0617a9de 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs
@@ -8,7 +8,7 @@ public enum AgentField
IsPublic,
Disabled,
Type,
- Mode,
+ RoutingMode,
InheritAgentId,
Profile,
Label,
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
index 4562f16a..aa872566 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
@@ -1,5 +1,6 @@
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Plugins.Models;
+using BotSharp.Abstraction.Routing.Enums;
using BotSharp.Abstraction.Tasks.Models;
namespace BotSharp.Abstraction.Agents.Models;
@@ -17,7 +18,8 @@ public class Agent
///
/// Routing Mode: lazy or eager
///
- public string Mode { get; set; } = AgentMode.Eager;
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Mode { get; set; }
public DateTime CreatedDateTime { get; set; }
public DateTime UpdatedDateTime { get; set; }
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentMode.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RoutingMode.cs
similarity index 54%
rename from src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentMode.cs
rename to src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RoutingMode.cs
index 4e2cd60c..8f946d01 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentMode.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RoutingMode.cs
@@ -1,6 +1,6 @@
-namespace BotSharp.Abstraction.Agents.Enums;
+namespace BotSharp.Abstraction.Routing.Enums;
-public class AgentMode
+public class RoutingMode
{
public const string Eager = "eager";
public const string Lazy = "lazy";
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs
index 42e0de2c..a28ccbc7 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs
@@ -2,6 +2,7 @@ using BotSharp.Abstraction.Hooks;
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Messaging;
using BotSharp.Abstraction.Messaging.Models.RichContent;
+using BotSharp.Abstraction.Routing.Enums;
using BotSharp.Abstraction.Routing.Settings;
namespace BotSharp.Core.Conversations.Services;
@@ -83,10 +84,10 @@ public partial class ConversationService
{
// Check the routing mode
var states = _services.GetRequiredService();
- var routingMode = states.GetState(StateConst.ROUTING_MODE, AgentMode.Eager);
+ var routingMode = states.GetState(StateConst.ROUTING_MODE, RoutingMode.Eager);
routing.Context.Push(agent.Id, reason: "request started", updateLazyRouting: false);
- if (routingMode == AgentMode.Lazy)
+ if (routingMode == RoutingMode.Lazy)
{
message.CurrentAgentId = states.GetState(StateConst.LAZY_ROUTING_AGENT_ID, message.CurrentAgentId);
routing.Context.Push(message.CurrentAgentId, reason: "lazy routing", updateLazyRouting: false);
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
index 5494f972..f7e70ca7 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
@@ -27,8 +27,8 @@ namespace BotSharp.Core.Repository
case AgentField.Type:
UpdateAgentType(agent.Id, agent.Type);
break;
- case AgentField.Mode:
- UpdateAgentMode(agent.Id, agent.Mode);
+ case AgentField.RoutingMode:
+ UpdateAgentRoutingMode(agent.Id, agent.Mode);
break;
case AgentField.InheritAgentId:
UpdateAgentInheritAgentId(agent.Id, agent.InheritAgentId);
@@ -145,7 +145,7 @@ namespace BotSharp.Core.Repository
File.WriteAllText(agentFile, json);
}
- private void UpdateAgentMode(string agentId, string mode)
+ private void UpdateAgentRoutingMode(string agentId, string? mode)
{
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
index 5a5a02da..929289aa 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Infrastructures.Enums;
+using BotSharp.Abstraction.Routing.Enums;
using BotSharp.Abstraction.Routing.Settings;
namespace BotSharp.Core.Routing;
@@ -290,8 +291,8 @@ public class RoutingContext : IRoutingContext
// Set next handling agent for lazy routing mode
var states = _services.GetRequiredService();
- var routingMode = states.GetState(StateConst.ROUTING_MODE, AgentMode.Eager);
- if (routingMode == AgentMode.Lazy)
+ var routingMode = states.GetState(StateConst.ROUTING_MODE, RoutingMode.Eager);
+ if (routingMode == RoutingMode.Lazy)
{
var agentId = GetCurrentAgentId();
if (agentId != BuiltInAgentId.Fallback)
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs
index 2fd2c429..33553e48 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs
@@ -1,5 +1,6 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
+using BotSharp.Abstraction.Routing.Enums;
namespace BotSharp.OpenAPI.ViewModels.Agents;
@@ -8,7 +9,11 @@ public class AgentCreationModel
public string Name { get; set; }
public string Description { get; set; }
public string Type { get; set; } = AgentType.Task;
- public string Mode { get; set; } = AgentMode.Eager;
+
+ ///
+ /// Agent routing mode
+ ///
+ public string? Mode { get; set; }
///
/// LLM default system instructions
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs
index da0c8b3a..3b69055f 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs
@@ -9,7 +9,11 @@ public class AgentUpdateModel
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Type { get; set; } = AgentType.Task;
- public string Mode { get; set; } = AgentMode.Eager;
+
+ ///
+ /// Agent routing mode
+ ///
+ public string? Mode { get; set; }
///
/// Instruction
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentViewModel.cs
index 4fede545..728a73e6 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentViewModel.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentViewModel.cs
@@ -12,7 +12,9 @@ public class AgentViewModel
public string Name { get; set; }
public string Description { get; set; }
public string Type { get; set; } = AgentType.Task;
- public string Mode { get; set; } = null!;
+
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Mode { get; set; }
public string Instruction { get; set; }
[JsonPropertyName("channel_instructions")]
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs
index 7ebded25..767a054a 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs
@@ -5,7 +5,7 @@ public class AgentDocument : MongoBase
public string Name { get; set; } = default!;
public string Description { get; set; } = default!;
public string Type { get; set; } = default!;
- public string Mode { get; set; } = default!;
+ public string? Mode { get; set; }
public string? InheritAgentId { get; set; }
public string? IconUrl { get; set; }
public string Instruction { get; set; } = default!;
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs
index 6eda94ed..5e0e741a 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs
@@ -28,8 +28,8 @@ public partial class MongoRepository
case AgentField.Type:
UpdateAgentType(agent.Id, agent.Type);
break;
- case AgentField.Mode:
- UpdateAgentMode(agent.Id, agent.Mode);
+ case AgentField.RoutingMode:
+ UpdateAgentRoutingMode(agent.Id, agent.Mode);
break;
case AgentField.InheritAgentId:
UpdateAgentInheritAgentId(agent.Id, agent.InheritAgentId);
@@ -139,7 +139,7 @@ public partial class MongoRepository
_dc.Agents.UpdateOne(filter, update);
}
- private void UpdateAgentMode(string agentId, string mode)
+ private void UpdateAgentRoutingMode(string agentId, string? mode)
{
var filter = Builders.Filter.Eq(x => x.Id, agentId);
var update = Builders.Update
From 1cd8ab9b03f0be28ffc20bdb439fe9719cb1666c Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Thu, 22 May 2025 14:02:39 -0500
Subject: [PATCH 08/11] minor change
---
.../BotSharp.Abstraction/Agents/Models/Agent.cs | 6 +++---
.../Agents/Services/AgentService.UpdateAgent.cs | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
index aa872566..431a78c5 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
@@ -1,6 +1,5 @@
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Plugins.Models;
-using BotSharp.Abstraction.Routing.Enums;
using BotSharp.Abstraction.Tasks.Models;
namespace BotSharp.Abstraction.Agents.Models;
@@ -10,6 +9,7 @@ public class Agent
public string Id { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
+
///
/// Agent Type
///
@@ -286,11 +286,11 @@ public class Agent
}
///
- /// Set agent mode: lazy or eager
+ /// Set agent routing mode: lazy or eager
///
///
///
- public Agent SetMode(string mode)
+ public Agent SetRoutingMode(string? mode)
{
Mode = mode;
return this;
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
index 50dbcad4..41174886 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
@@ -95,7 +95,7 @@ public partial class AgentService
clonedAgent.SetId(foundAgent.Id)
.SetName(foundAgent.Name)
.SetType(foundAgent.Type)
- .SetMode(foundAgent.Mode)
+ .SetRoutingMode(foundAgent.Mode)
.SetIsPublic(foundAgent.IsPublic)
.SetDisabled(foundAgent.Disabled)
.SetDescription(foundAgent.Description)
From 6def0031415f53b6517f5ad7991b2842f0646af4 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Thu, 22 May 2025 14:59:35 -0500
Subject: [PATCH 09/11] trigger agent instruction logging in realtime
---
.../Providers/Realtime/RealTimeCompletionProvider.cs | 2 +-
.../Providers/Realtime/RealTimeCompletionProvider.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs
index d2084801..2e95cfa2 100644
--- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs
@@ -343,7 +343,7 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
}).ToArray();
await HookEmitter.Emit(_services,
- async hook => { await hook.OnSessionUpdated(agent, prompt, functions, isInit); }, agent.Id);
+ async hook => { await hook.OnSessionUpdated(agent, prompt, functions, isInit: false); }, agent.Id);
if (_settings.Gemini.UseGoogleSearch)
{
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
index 4e85be76..fd458744 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
@@ -381,7 +381,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
await HookEmitter.Emit(_services, async hook =>
{
- await hook.OnSessionUpdated(agent, instruction, functions, isInit);
+ await hook.OnSessionUpdated(agent, instruction, functions, isInit: false);
}, agent.Id);
await SendEventToModel(sessionUpdate);
From b52c2405937a695e67f0e8c6446c4e69b7e50653 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Thu, 22 May 2025 16:10:09 -0500
Subject: [PATCH 10/11] fix user update
---
.../Repository/FileRepository/FileRepository.User.cs | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs
index c8f1fbd6..7bd5a1a9 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs
@@ -263,8 +263,15 @@ public partial class FileRepository
}
var userFile = Path.Combine(dir, USER_FILE);
- user.UpdatedTime = DateTime.UtcNow;
- File.WriteAllText(userFile, JsonSerializer.Serialize(user, _options));
+ var userJson = File.ReadAllText(userFile);
+ var curUser = JsonSerializer.Deserialize(userJson, _options);
+ if (curUser == null) return false;
+
+ curUser.Type = user.Type;
+ curUser.Role = user.Role;
+ curUser.Permissions = user.Permissions;
+ curUser.UpdatedTime = DateTime.UtcNow;
+ File.WriteAllText(userFile, JsonSerializer.Serialize(curUser, _options));
if (updateUserAgents)
{
From ba0b486d5a4918baeac8db595a9171a086133947 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Thu, 22 May 2025 16:57:27 -0500
Subject: [PATCH 11/11] minor change
---
.../Infrastructures/Attributes/BotSharpAuthAttribute.cs | 2 +-
.../BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj | 4 ----
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Attributes/BotSharpAuthAttribute.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Attributes/BotSharpAuthAttribute.cs
index 25193cb3..46f28d6c 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Attributes/BotSharpAuthAttribute.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Attributes/BotSharpAuthAttribute.cs
@@ -26,7 +26,7 @@ public class BotSharpAuthAttribute : Attribute, IAsyncAuthorizationFilter
var (isAdmin, user) = await userService.IsAdminUser(userIdentity.Id);
if (!isAdmin || user == null)
{
- context.Result = new BadRequestResult();
+ context.Result = new UnauthorizedResult();
}
}
}
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj b/src/Plugins/BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj
index e54033b6..2455e7b5 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj
@@ -10,10 +10,6 @@
$(SolutionDir)packages
-
-
-
-