add active rounds to state

This commit is contained in:
Jicheng Lu 2024-03-27 13:50:27 -05:00
parent de2f4ee5b1
commit 55480a97ea
15 changed files with 112 additions and 27 deletions

View file

@ -8,7 +8,7 @@ public interface IConversationService
IConversationStateService States { get; } IConversationStateService States { get; }
string ConversationId { get; } string ConversationId { get; }
Task<Conversation> NewConversation(Conversation conversation); Task<Conversation> NewConversation(Conversation conversation);
void SetConversationId(string conversationId, List<string> states); void SetConversationId(string conversationId, List<MessageState> states);
Task<Conversation> GetConversation(string id); Task<Conversation> GetConversation(string id);
Task<PagedItems<Conversation>> GetConversations(ConversationFilter filter); Task<PagedItems<Conversation>> GetConversations(ConversationFilter filter);
Task<Conversation> UpdateConversationTitle(string id, string title); Task<Conversation> UpdateConversationTitle(string id, string title);

View file

@ -12,7 +12,7 @@ public interface IConversationStateService
string GetState(string name, string defaultValue = ""); string GetState(string name, string defaultValue = "");
bool ContainsState(string name); bool ContainsState(string name);
Dictionary<string, string> GetStates(); Dictionary<string, string> GetStates();
IConversationStateService SetState<T>(string name, T value, bool isNeedVersion = true); IConversationStateService SetState<T>(string name, T value, bool isNeedVersion = true, int activeRounds = -1);
void SaveStateByArgs(JsonDocument args); void SaveStateByArgs(JsonDocument args);
void CleanStates(); void CleanStates();
void Save(); void Save();

View file

@ -23,10 +23,13 @@ public class StateValue
public string Data { get; set; } public string Data { get; set; }
[JsonPropertyName("message_id")] [JsonPropertyName("message_id")]
public string MessageId { get; set; } public string? MessageId { get; set; }
public bool Active { get; set; } public bool Active { get; set; }
[JsonPropertyName("active_rounds")]
public int ActiveRounds { get; set; }
[JsonPropertyName("update_time")] [JsonPropertyName("update_time")]
public DateTime UpdateTime { get; set; } public DateTime UpdateTime { get; set; }

View file

@ -34,7 +34,7 @@ public class MessageConfig : TruncateMessageRequest
/// <summary> /// <summary>
/// Conversation states from input /// Conversation states from input
/// </summary> /// </summary>
public List<string> States { get; set; } = new List<string>(); public List<MessageState> States { get; set; } = new List<MessageState>();
/// <summary> /// <summary>
/// Agent task id /// Agent task id

View file

@ -0,0 +1,22 @@
namespace BotSharp.Abstraction.Models;
public class MessageState
{
public string Key { get; set; }
public string Value { get; set; }
[JsonPropertyName("active_rounds")]
public int ActiveRounds { get; set; } = -1;
public MessageState()
{
}
public MessageState(string key, string value, int activeRounds = -1)
{
Key = key;
Value = value;
ActiveRounds = activeRounds;
}
}

View file

@ -1,3 +1,5 @@
using BotSharp.Abstraction.Models;
namespace BotSharp.Core.Conversations.Services; namespace BotSharp.Core.Conversations.Services;
public partial class ConversationService : IConversationService public partial class ConversationService : IConversationService
@ -120,10 +122,10 @@ public partial class ConversationService : IConversationService
.ToList(); .ToList();
} }
public void SetConversationId(string conversationId, List<string> states) public void SetConversationId(string conversationId, List<MessageState> states)
{ {
_conversationId = conversationId; _conversationId = conversationId;
_state.Load(_conversationId); _state.Load(_conversationId);
states.ForEach(x => _state.SetState(x.Split('=')[0], x.Split('=')[1])); states.ForEach(x => _state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds));
} }
} }

View file

@ -1,4 +1,4 @@
using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.Users.Enums;
namespace BotSharp.Core.Conversations.Services; namespace BotSharp.Core.Conversations.Services;
@ -33,7 +33,7 @@ public class ConversationStateService : IConversationStateService, IDisposable
/// <param name="value"></param> /// <param name="value"></param>
/// <param name="isNeedVersion">whether the state is related to message or not</param> /// <param name="isNeedVersion">whether the state is related to message or not</param>
/// <returns></returns> /// <returns></returns>
public IConversationStateService SetState<T>(string name, T value, bool isNeedVersion = true) public IConversationStateService SetState<T>(string name, T value, bool isNeedVersion = true, int activeRounds = -1)
{ {
if (value == null) if (value == null)
{ {
@ -69,6 +69,7 @@ public class ConversationStateService : IConversationStateService, IDisposable
Data = currentValue, Data = currentValue,
MessageId = routingCtx.MessageId, MessageId = routingCtx.MessageId,
Active = true, Active = true,
ActiveRounds = activeRounds > 0 ? activeRounds : -1,
UpdateTime = DateTime.UtcNow, UpdateTime = DateTime.UtcNow,
}; };
@ -90,7 +91,16 @@ public class ConversationStateService : IConversationStateService, IDisposable
{ {
_conversationId = conversationId; _conversationId = conversationId;
var routingCtx = _services.GetRequiredService<IRoutingContext>();
var curMsgId = routingCtx.MessageId;
_states = _db.GetConversationStates(_conversationId); _states = _db.GetConversationStates(_conversationId);
var dialogs = _db.GetConversationDialogs(_conversationId);
var userDialogs = dialogs.Where(x => x.MetaData?.Role == AgentRole.User || x.MetaData?.Role == UserRole.Client)
.OrderBy(x => x.MetaData?.CreateTime)
.ToList();
var curMsgIndex = userDialogs.FindIndex(x => !string.IsNullOrEmpty(curMsgId) && x.MetaData?.MessageId == curMsgId);
curMsgIndex = curMsgIndex < 0 ? userDialogs.Count() : curMsgIndex;
var curStates = new Dictionary<string, string>(); var curStates = new Dictionary<string, string>();
if (!_states.IsNullOrEmpty()) if (!_states.IsNullOrEmpty())
@ -100,6 +110,23 @@ public class ConversationStateService : IConversationStateService, IDisposable
var value = state.Value?.Values?.LastOrDefault(); var value = state.Value?.Values?.LastOrDefault();
if (value == null || !value.Active) continue; if (value == null || !value.Active) continue;
if (value.ActiveRounds > 0)
{
var stateMsgIndex = userDialogs.FindIndex(x => !string.IsNullOrEmpty(x.MetaData?.MessageId) && x.MetaData.MessageId == value.MessageId);
if (stateMsgIndex >= 0 && curMsgIndex - stateMsgIndex >= value.ActiveRounds)
{
state.Value.Values.Add(new StateValue
{
Data = value.Data,
MessageId = value.MessageId,
Active = false,
ActiveRounds = value.ActiveRounds,
UpdateTime = DateTime.UtcNow
});
continue;
}
}
var data = value.Data ?? string.Empty; var data = value.Data ?? string.Empty;
curStates[state.Key] = data; curStates[state.Key] = data;
_logger.LogInformation($"[STATE] {state.Key} : {data}"); _logger.LogInformation($"[STATE] {state.Key} : {data}");
@ -150,6 +177,7 @@ public class ConversationStateService : IConversationStateService, IDisposable
Data = lastValue.Data, Data = lastValue.Data,
MessageId = lastValue.MessageId, MessageId = lastValue.MessageId,
Active = false, Active = false,
ActiveRounds = lastValue.ActiveRounds,
UpdateTime = utcNow UpdateTime = utcNow
}); });
} }

View file

@ -2,6 +2,7 @@ using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.Evaluations; using BotSharp.Abstraction.Evaluations;
using BotSharp.Abstraction.Evaluations.Models; using BotSharp.Abstraction.Evaluations.Models;
using BotSharp.Abstraction.Evaluations.Settings; using BotSharp.Abstraction.Evaluations.Settings;
using BotSharp.Abstraction.Models;
using BotSharp.Abstraction.Templating; using BotSharp.Abstraction.Templating;
using System.Drawing; using System.Drawing;
@ -88,15 +89,19 @@ public class EvaluatingService : IEvaluatingService
private async Task<RoleDialogModel> SendMessage(string agentId, string conversationId, string text) private async Task<RoleDialogModel> SendMessage(string agentId, string conversationId, string text)
{ {
var conv = _services.GetRequiredService<IConversationService>(); var conv = _services.GetRequiredService<IConversationService>();
conv.SetConversationId(conversationId, new List<string>
var inputMsg = new RoleDialogModel(AgentRole.User, text);
var routing = _services.GetRequiredService<IRoutingService>();
routing.Context.SetMessageId(conversationId, inputMsg.MessageId);
conv.SetConversationId(conversationId, new List<MessageState>
{ {
$"channel={ConversationChannel.OpenAPI}" new MessageState("channel", ConversationChannel.OpenAPI)
}); });
RoleDialogModel response = default; RoleDialogModel response = default;
await conv.SendMessage(agentId, await conv.SendMessage(agentId,
new RoleDialogModel(AgentRole.User, text), inputMsg,
replyMessage: null, replyMessage: null,
async msg => response = msg, async msg => response = msg,
_ => Task.CompletedTask, _ => Task.CompletedTask,

View file

@ -66,7 +66,7 @@ public class ConversationController : ControllerBase
public async Task<IEnumerable<ChatResponseModel>> GetDialogs([FromRoute] string conversationId) public async Task<IEnumerable<ChatResponseModel>> GetDialogs([FromRoute] string conversationId)
{ {
var conv = _services.GetRequiredService<IConversationService>(); var conv = _services.GetRequiredService<IConversationService>();
conv.SetConversationId(conversationId, new List<string>()); conv.SetConversationId(conversationId, new List<MessageState>());
var history = conv.GetDialogHistory(fromBreakpoint: false); var history = conv.GetDialogHistory(fromBreakpoint: false);
var userService = _services.GetRequiredService<IUserService>(); var userService = _services.GetRequiredService<IUserService>();

View file

@ -22,7 +22,7 @@ public class InstructModeController : ControllerBase
[FromBody] InstructMessageModel input) [FromBody] InstructMessageModel input)
{ {
var state = _services.GetRequiredService<IConversationStateService>(); var state = _services.GetRequiredService<IConversationStateService>();
input.States.ForEach(x => state.SetState(x.Split('=')[0], x.Split('=')[1])); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds));
state.SetState("provider", input.Provider) state.SetState("provider", input.Provider)
.SetState("model", input.Model) .SetState("model", input.Model)
.SetState("model_id", input.ModelId) .SetState("model_id", input.ModelId)
@ -44,7 +44,7 @@ public class InstructModeController : ControllerBase
public async Task<string> TextCompletion([FromBody] IncomingMessageModel input) public async Task<string> TextCompletion([FromBody] IncomingMessageModel input)
{ {
var state = _services.GetRequiredService<IConversationStateService>(); var state = _services.GetRequiredService<IConversationStateService>();
input.States.ForEach(x => state.SetState(x.Split('=')[0], x.Split('=')[1])); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds));
state.SetState("provider", input.Provider) state.SetState("provider", input.Provider)
.SetState("model", input.Model) .SetState("model", input.Model)
.SetState("model_id", input.ModelId); .SetState("model_id", input.ModelId);
@ -57,7 +57,7 @@ public class InstructModeController : ControllerBase
public async Task<string> ChatCompletion([FromBody] IncomingMessageModel input) public async Task<string> ChatCompletion([FromBody] IncomingMessageModel input)
{ {
var state = _services.GetRequiredService<IConversationStateService>(); var state = _services.GetRequiredService<IConversationStateService>();
input.States.ForEach(x => state.SetState(x.Split('=')[0], x.Split('=')[1])); input.States.ForEach(x => state.SetState(x.Key, x.Value, activeRounds: x.ActiveRounds));
state.SetState("provider", input.Provider) state.SetState("provider", input.Provider)
.SetState("model", input.Model) .SetState("model", input.Model)
.SetState("model_id", input.ModelId); .SetState("model_id", input.ModelId);

View file

@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Authorization;
using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.MLTasks.Settings; using BotSharp.Abstraction.MLTasks.Settings;
using BotSharp.Abstraction.Routing;
namespace BotSharp.Plugin.ChatbotUI.Controllers; namespace BotSharp.Plugin.ChatbotUI.Controllers;
@ -72,6 +73,9 @@ public class ChatbotUiController : ControllerBase
.Name; .Name;
var conv = _services.GetRequiredService<IConversationService>(); var conv = _services.GetRequiredService<IConversationService>();
var routing = _services.GetRequiredService<IRoutingService>();
routing.Context.SetMessageId(input.ConversationId, message.MessageId);
conv.SetConversationId(input.ConversationId, input.States); conv.SetConversationId(input.ConversationId, input.States);
conv.States.SetState("channel", input.Channel) conv.States.SetState("channel", input.Channel)
.SetState("provider", "azure-openai") .SetState("provider", "azure-openai")

View file

@ -1,7 +1,10 @@
using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Messaging.JsonConverters; using BotSharp.Abstraction.Messaging.JsonConverters;
using BotSharp.Abstraction.Messaging.Models.RichContent; using BotSharp.Abstraction.Messaging.Models.RichContent;
using BotSharp.Abstraction.Models;
using BotSharp.Abstraction.Routing;
using System.Text.Json.Serialization.Metadata; using System.Text.Json.Serialization.Metadata;
namespace BotSharp.Plugin.MetaMessenger.Services; namespace BotSharp.Plugin.MetaMessenger.Services;
@ -52,15 +55,18 @@ public class MessageHandleService
}); });
// Go to LLM // Go to LLM
var inputMsg = new RoleDialogModel(AgentRole.User, message);
var conv = _services.GetRequiredService<IConversationService>(); var conv = _services.GetRequiredService<IConversationService>();
conv.SetConversationId(sender, new List<string> var routing = _services.GetRequiredService<IRoutingService>();
routing.Context.SetMessageId(sender, inputMsg.MessageId);
conv.SetConversationId(sender, new List<MessageState>
{ {
$"channel={ConversationChannel.Messenger}" new MessageState("channel", ConversationChannel.Messenger)
}); });
var replies = new List<IRichMessage>(); var replies = new List<IRichMessage>();
var result = await conv.SendMessage(agentId, var result = await conv.SendMessage(agentId,
new RoleDialogModel(AgentRole.User, message), inputMsg,
replyMessage: null, replyMessage: null,
async msg => async msg =>
{ {

View file

@ -32,8 +32,9 @@ public class StateMongoElement
public class StateValueMongoElement public class StateValueMongoElement
{ {
public string Data { get; set; } public string Data { get; set; }
public string MessageId { get; set; } public string? MessageId { get; set; }
public bool Active { get; set; } public bool Active { get; set; }
public int ActiveRounds { get; set; }
public DateTime UpdateTime { get; set; } public DateTime UpdateTime { get; set; }
public static StateValueMongoElement ToMongoElement(StateValue element) public static StateValueMongoElement ToMongoElement(StateValue element)
@ -43,6 +44,7 @@ public class StateValueMongoElement
Data = element.Data, Data = element.Data,
MessageId = element.MessageId, MessageId = element.MessageId,
Active = element.Active, Active = element.Active,
ActiveRounds = element.ActiveRounds,
UpdateTime = element.UpdateTime UpdateTime = element.UpdateTime
}; };
} }
@ -54,6 +56,7 @@ public class StateValueMongoElement
Data = element.Data, Data = element.Data,
MessageId = element.MessageId, MessageId = element.MessageId,
Active = element.Active, Active = element.Active,
ActiveRounds = element.ActiveRounds,
UpdateTime = element.UpdateTime UpdateTime = element.UpdateTime
}; };
} }

View file

@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using BotSharp.Plugin.Twilio.Services; using BotSharp.Plugin.Twilio.Services;
using BotSharp.Abstraction.Routing;
namespace BotSharp.Plugin.Twilio.Controllers; namespace BotSharp.Plugin.Twilio.Controllers;
@ -47,18 +48,22 @@ public class TwilioVoiceController : TwilioController
{ {
string sessionId = $"TwilioVoice_{input.CallSid}"; string sessionId = $"TwilioVoice_{input.CallSid}";
var inputMsg = new RoleDialogModel(AgentRole.User, input.SpeechResult);
var conv = _services.GetRequiredService<IConversationService>(); var conv = _services.GetRequiredService<IConversationService>();
conv.SetConversationId(sessionId, new List<string> var routing = _services.GetRequiredService<IRoutingService>();
routing.Context.SetMessageId(sessionId, inputMsg.MessageId);
conv.SetConversationId(sessionId, new List<MessageState>
{ {
$"channel={ConversationChannel.Phone}", new MessageState("channel", ConversationChannel.Phone),
$"calling_phone={input.DialCallSid}" new MessageState("calling_phone", input.DialCallSid)
}); });
var twilio = _services.GetRequiredService<TwilioService>(); var twilio = _services.GetRequiredService<TwilioService>();
VoiceResponse response = default; VoiceResponse response = default;
var result = await conv.SendMessage(agentId, var result = await conv.SendMessage(agentId,
new RoleDialogModel(AgentRole.User, input.SpeechResult), inputMsg,
replyMessage: null, replyMessage: null,
async msg => async msg =>
{ {

View file

@ -1,6 +1,9 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Conversations; using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Models;
using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Users.Models; using BotSharp.Abstraction.Users.Models;
using BotSharp.Plugin.WeChat.Users; using BotSharp.Plugin.WeChat.Users;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -50,9 +53,13 @@ namespace BotSharp.Plugin.WeChat
.OrderByDescending(_ => _.CreatedTime) .OrderByDescending(_ => _.CreatedTime)
.FirstOrDefault()?.Id; .FirstOrDefault()?.Id;
conversationService.SetConversationId(latestConversationId, new List<string> var inputMsg = new RoleDialogModel(AgentRole.User, message);
var routing = _service.GetRequiredService<IRoutingService>();
routing.Context.SetMessageId(latestConversationId, inputMsg.MessageId);
conversationService.SetConversationId(latestConversationId, new List<MessageState>
{ {
"channel=wechat" new MessageState("channel", "wechat")
}); });
latestConversationId ??= (await conversationService.NewConversation(new Conversation() latestConversationId ??= (await conversationService.NewConversation(new Conversation()
@ -62,7 +69,7 @@ namespace BotSharp.Plugin.WeChat
}))?.Id; }))?.Id;
var result = await conversationService.SendMessage(AgentId, var result = await conversationService.SendMessage(AgentId,
new RoleDialogModel("user", message), inputMsg,
replyMessage: null, replyMessage: null,
async msg => async msg =>
{ {