You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Switching to a local 'using var session' removes the previous field-backed session disposal in the catch and after loop; ensure no code paths can leak the websocket or leave the session undisposed, especially when exceptions occur before 'using' scope exits or during AcceptWebSocketAsync failures.
usingvarwebSocket=awaithttpContext.WebSockets.AcceptWebSocketAsync();awaitHandleWebSocket(services,agentId,conversationId,webSocket);}catch(Exceptionex){_logger.LogError(ex,$"Error when connecting Chat stream. ({ex.Message})");}return;}}await_next(httpContext);}privateasyncTaskHandleWebSocket(IServiceProviderservices,stringagentId,stringconversationId,WebSocketwebSocket){usingvarsession=newBotSharpRealtimeSession(services,webSocket,newChatSessionOptions{
Closing with a formatted message that includes provider can exceed typical close message size or leak internal details to clients; verify the reason string length and whether including provider info is acceptable for clients.
publicasyncTaskDisconnectAsync(){if(_disposed||_websocket.State!=WebSocketState.Open){return;}await_websocket.CloseAsync(WebSocketCloseStatus.NormalClosure,$"Normal Closure from {nameof(BotSharpRealtimeSession)}-{_sessionOptions?.Provider}",CancellationToken.None);}
In 'ConnectToModel', 'session' is non-null by construction, so the null check is redundant; consider removing to simplify and avoid masking potential lifetime issues.
privateasyncTaskConnectToModel(IRealtimeHubhub,BotSharpRealtimeSessionsession,List<MessageState>?states=null){awaithub.ConnectToModel(responseToUser:async data =>{if(session!=null){awaitsession.SendEventAsync(data);}},initStates:states);
Wrap the logic in HandleWebSocket with a try...finally block to ensure convService.SaveStates() and session.DisconnectAsync() are always called for proper cleanup and state persistence, even if an exception occurs.
private async Task HandleWebSocket(IServiceProvider services, string agentId, string conversationId, WebSocket webSocket)
{
using var session = new BotSharpRealtimeSession(services, webSocket, new ChatSessionOptions
{
Provider = "BotSharp Chat Stream",
BufferSize = 1024 * 32,
JsonOptions = BotSharpOptions.defaultJsonOptions,
Logger = _logger
});
- var hub = services.GetRequiredService<IRealtimeHub>();- var conn = hub.SetHubConnection(conversationId);- conn.CurrentAgentId = agentId;- InitEvents(conn);+ var convService = services.GetRequiredService<IConversationService>();+ try+ {+ var hub = services.GetRequiredService<IRealtimeHub>();+ var conn = hub.SetHubConnection(conversationId);+ conn.CurrentAgentId = agentId;+ InitEvents(conn);- // load conversation and state- var convService = services.GetRequiredService<IConversationService>();- var state = services.GetRequiredService<IConversationStateService>();- convService.SetConversationId(conversationId, []);- await convService.GetConversationRecordOrCreateNew(agentId);+ // load conversation and state+ var state = services.GetRequiredService<IConversationStateService>();+ convService.SetConversationId(conversationId, []);+ await convService.GetConversationRecordOrCreateNew(agentId);- await foreach (ChatSessionUpdate update in session.ReceiveUpdatesAsync(CancellationToken.None))- {- var receivedText = update?.RawResponse;- if (string.IsNullOrEmpty(receivedText))+ await foreach (ChatSessionUpdate update in session.ReceiveUpdatesAsync(CancellationToken.None))
{
- continue;- }+ var receivedText = update?.RawResponse;+ if (string.IsNullOrEmpty(receivedText))+ {+ continue;+ }- var (eventType, data) = MapEvents(conn, receivedText, conversationId);- if (eventType == "start")- {-#if DEBUG- _logger.LogCritical($"Start chat stream connection for conversation ({conversationId})");-#endif- var request = InitRequest(data, conversationId);- await ConnectToModel(hub, session, request?.States);- }- else if (eventType == "media")- {- if (!string.IsNullOrEmpty(data))+ var (eventType, data) = MapEvents(conn, receivedText, conversationId);+ if (eventType == "start")
{
- await hub.Completer.AppenAudioBuffer(data);+ #if DEBUG+ _logger.LogCritical($"Start chat stream connection for conversation ({conversationId})");+ #endif+ var request = InitRequest(data, conversationId);+ await ConnectToModel(hub, session, request?.States);+ }+ else if (eventType == "media")+ {+ if (!string.IsNullOrEmpty(data))+ {+ await hub.Completer.AppenAudioBuffer(data);+ }+ }+ else if (eventType == "disconnect")+ {+ #if DEBUG+ _logger.LogCritical($"Disconnecting chat stream connection for conversation ({conversationId})");+ #endif+ await hub.Completer.Disconnect();+ break;
}
}
- else if (eventType == "disconnect")- {-#if DEBUG- _logger.LogCritical($"Disconnecting chat stream connection for conversation ({conversationId})");-#endif- await hub.Completer.Disconnect();- break;- }
}
-- convService.SaveStates();- await session.DisconnectAsync();+ finally+ {+ convService.SaveStates();+ await session.DisconnectAsync();+ }
}
Apply / Chat
Suggestion importance[1-10]: 8
__
Why: The suggestion correctly identifies that an exception within HandleWebSocket would skip crucial cleanup logic, leading to unsaved state and improperly closed connections, and proposes a robust try...finally solution.
Medium
Learned best practice
Safely close WebSocket with fallback
Use a non-null close description to avoid null propagation and include a fallback when provider is missing. Also guard for CloseSent state to prevent invalid operations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR Type
Bug fix, Enhancement
Description
Fix WebSocket close status and messages
Refactor parameter validation with new utility
Improve session management in chat stream
Remove duplicate code across providers
Diagram Walkthrough
File Walkthrough
7 files
Add parameter validation utility methodImprove session management with using statementReplace validation with LlmUtility methodReplace validation with LlmUtility methodReplace validation with LlmUtility methodReplace validation with LlmUtility methodRemove duplicate parameter validation method2 files
Fix WebSocket close status and messageFix WebSocket close status and message1 files
Add LlmUtility namespace import