Reputation: 87
I am working on a bot with human handover features (Human-2-Human chat), where bot is in charge of whole communication. The user can start the communication with bot and if he is not satisfied by bot's responses, he can ask for further assistance from Human.
Bot is able to connect the user to a live Agent using a Third Party System. Bot forwards the message from a dialog to an API endpoint of this system along with a callback url. This third party system uses a callback mechanism to pass the message written by agent on this specified url.
I have created an API Controller endpoint and pass to this system as callback url. When agent sends a message the system notifies on this endpoint. It is simple Web API controller with no direct affiliation to Bot Framework.
Although I maintain a Conversation and User State of the bot in Cosmos DB and it has certain properties that contain status of chat connection like (ChatConnected, ChatClosed etc). Now to pass these message notifications to bot I maintain two concurrent dictionary one for Conversation Reference and second for TurnContext.
Conversation Reference helps to pass the agent message from bot to user using ContinueConversationAsync.
TurnContext helps to manage and update the state of these properties when session closed etc. And also use it to send message after certain period of inactivity since last turn has activity timestamp.
Now both of these are in-memory, which means they are added and removed as new chat sessions are created and as more messages are exchanged. I now want to move this out from in-memory to a shared cache or low latency cosmos. So that I can also use the possibility of auto-scaling new instances of bot service when required. I am using appservices currenly. But due to this coupling new instances don't have access to in-memory data, and therefore can not service. I don't think that enabling AffinityCookie for Bot Scenarios actually works.
I am able to serialize the ConversationReference object (through NewtonSoft), but serializing TurnContext throws JSON serialization exception due to internal loop in object. I tried to mitigate that with SerilizationSettings to ignore loop but it does not even works in VS during debugging throws VS stack overflow exception.
So how can I move this code to become independent of singleton ConcurrentDictionary on an instance-
private readonly ConcurrentDictionary<string, ITurnContext> TurnContextReferences;
private void AddTurnContext(ITurnContext turnContext, string sessionId)
{
if (turnContext != null && !string.IsNullOrWhiteSpace(sessionId))
{
//Add the Session Id and TurnContext to dictionary
TurnContextReferences.AddOrUpdate(sessionId, turnContext, (key, newValue) => turnContext);
}
}
//Using above method inside a function
//Trim the incoming message
var userMessage = messageActivity.Text.Trim();
if (!string.IsNullOrWhiteSpace(userMessage))
{
//send the incoming message from client to Agent
await TPSystem.SendMessageAsync(messageActivity.Conversation.Id, conversationData.SessionId, messageActivity.Text.Trim());
}
//Add to Turn context Dictionary
AddTurnContext(stepContext.Context, conversationData.SessionId);
//Inside API Controller
//Get the TurnContext from the Dictionary
TurnContextReferences.TryGetValue(sessionStateChangedEventData.SessionId, out ITurnContext turnContext);
if (turnContext != null)
{
var conversationData = await BotStateAccessors.ConversationStateAccessor.GetAsync(turnContext, () => new ConversationStateDataModel());
if (!conversationData.LiveAgentChatClosed)
{
conversationData.LiveAgentChatClosed = true;
await BotStateAccessors.ConversationStateAccessor.SetAsync(turnContext, conversationData);
await BotConversationState.SaveChangesAsync(turnContext);
}
}
Any ideas to think through would be appreciated.
Upvotes: 3
Views: 327
Reputation: 12284
Conversation references contain a subset of the information in activities, and an activity is just one property of a turn context, so a conversation reference contains a subset of the information in a turn context. It's redundant to be saving both conversation references and turn contexts because if you save turn contexts then you'll already have all the information from the conversation references.
That said, it's a very bad idea to try to save turn contexts. If you need some pieces of information that aren't in the conversation references then just save that specific information. For example, you can create your own class that contains a conversation reference and a timestamp that signifies the time of the last message from that conversation.
public class ConversationInfo
{
[JsonProperty(PropertyName = "conversationReference")]
public ConversationReference ConversationReference { get; set; }
[JsonProperty(PropertyName = "timestamp")]
public DateTimeOffset Timestamp { get; set; }
}
Upvotes: 1