optimize conversation Progeess, hotfix invoke function progress question

This commit is contained in:
LAPTOP-3CFGGVOS\rabbit 2024-09-12 18:49:45 +08:00
parent 793905436b
commit 3f8e506ec7
18 changed files with 89 additions and 69 deletions

View file

@ -0,0 +1,10 @@
namespace BotSharp.Abstraction.Conversations;
public delegate Task FunctionExecuting(RoleDialogModel msg);
public delegate Task FunctionExecuted(RoleDialogModel msg);
public interface IConversationProgressService
{
FunctionExecuted OnFunctionExecuted { get; set; }
FunctionExecuting OnFunctionExecuting { get; set; }
}

View file

@ -39,9 +39,7 @@ public interface IConversationService
Task<bool> SendMessage(string agentId,
RoleDialogModel lastDalog,
PostbackMessageModel? replyMessage,
Func<RoleDialogModel, Task> onResponseReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted);
Func<RoleDialogModel, Task> onResponseReceived);
List<RoleDialogModel> GetDialogHistory(int lastCount = 100, bool fromBreakpoint = true);
Task CleanHistory(string agentId);

View file

@ -16,5 +16,5 @@ public interface IRoutingHandler
void SetDialogs(List<RoleDialogModel> dialogs);
Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message, Func<RoleDialogModel, Task> onFunctionExecuting);
Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message);
}

View file

@ -28,9 +28,9 @@ public interface IRoutingService
List<RoutingHandlerDef> GetHandlers(Agent router);
void ResetRecursiveCounter();
Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs, Func<RoleDialogModel, Task> onFunctionExecuting);
Task<bool> InvokeFunction(string name, RoleDialogModel messages, Func<RoleDialogModel, Task>? onFunctionExecuting = null);
Task<RoleDialogModel> InstructLoop(RoleDialogModel message, List<RoleDialogModel> dialogs, Func<RoleDialogModel, Task> onFunctionExecuting);
Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs);
Task<bool> InvokeFunction(string name, RoleDialogModel messages);
Task<RoleDialogModel> InstructLoop(RoleDialogModel message, List<RoleDialogModel> dialogs);
/// <summary>
/// Talk to a specific Agent directly, bypassing the Router

View file

@ -7,6 +7,5 @@ public interface IExecutor
Task<RoleDialogModel> Execute(IRoutingService routing,
FunctionCallFromLlm inst,
RoleDialogModel message,
List<RoleDialogModel> dialogs,
Func<RoleDialogModel, Task> onFunctionExecuting);
List<RoleDialogModel> dialogs);
}

View file

@ -41,6 +41,7 @@ public class ConversationPlugin : IBotSharpPlugin
services.AddScoped<IConversationStorage, ConversationStorage>();
services.AddScoped<IConversationService, ConversationService>();
services.AddScoped<IConversationProgressService, ConversationProgressService>();
services.AddScoped<IConversationStateService, ConversationStateService>();
services.AddScoped<ITranslationService, TranslationService>();

View file

@ -0,0 +1,11 @@
namespace BotSharp.Core.Conversations.Services
{
public class ConversationProgressService : IConversationProgressService
{
public FunctionExecuting OnFunctionExecuting { get; set; }
public FunctionExecuted OnFunctionExecuted { get; set; }
}
}

View file

@ -10,9 +10,7 @@ public partial class ConversationService
public async Task<bool> SendMessage(string agentId,
RoleDialogModel message,
PostbackMessageModel? replyMessage,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted)
Func<RoleDialogModel, Task> onMessageReceived)
{
var conversation = await GetConversationRecordOrCreateNew(agentId);
var agentService = _services.GetRequiredService<IAgentService>();
@ -78,7 +76,7 @@ public partial class ConversationService
if (agent.Type == AgentType.Routing)
{
response = await routing.InstructLoop(message, dialogs, onFunctionExecuting);
response = await routing.InstructLoop(message, dialogs);
}
else
{

View file

@ -103,9 +103,7 @@ public class EvaluatingService : IEvaluatingService
await conv.SendMessage(agentId,
inputMsg,
replyMessage: null,
async msg => response = msg,
_ => Task.CompletedTask,
_ => Task.CompletedTask);
async msg => response = msg);
return response;
}

View file

@ -34,7 +34,7 @@ public class FallbackToRouterFn : IFunctionCallback
routing.Context.Replace(targetAgent.Id);
message.CurrentAgentId = targetAgent.Id;
var response = await routing.InstructLoop(message, dialogs, null);
var response = await routing.InstructLoop(message, dialogs);
message.Content = response.Content;
message.StopCompletion = true;

View file

@ -34,7 +34,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase//, IRoutin
{
}
public async Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message, Func<RoleDialogModel, Task> onFunctionExecuting)
public async Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
{
var context = _services.GetRequiredService<IRoutingContext>();
var agentId = context.GetCurrentAgentId();
@ -47,7 +47,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase//, IRoutin
}
};
var ret = await routing.InvokeAgent(agentId, dialogs, onFunctionExecuting);
var ret = await routing.InvokeAgent(agentId, dialogs);
var response = dialogs.Last();
inst.Response = response.Content;

View file

@ -40,7 +40,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
{
}
public async Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message, Func<RoleDialogModel, Task> onFunctionExecuting)
public async Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
{
var states = _services.GetRequiredService<IConversationStateService>();
var goalAgent = states.GetState(StateConst.EXPECTED_GOAL_AGENT);
@ -88,7 +88,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
}
else
{
var ret = await routing.InvokeAgent(agentId, _dialogs, onFunctionExecuting);
var ret = await routing.InvokeAgent(agentId, _dialogs);
}
var response = _dialogs.Last();

View file

@ -16,8 +16,7 @@ public class InstructExecutor : IExecutor
public async Task<RoleDialogModel> Execute(IRoutingService routing,
FunctionCallFromLlm inst,
RoleDialogModel message,
List<RoleDialogModel> dialogs,
Func<RoleDialogModel, Task> onFunctionExecuting)
List<RoleDialogModel> dialogs)
{
message.Instruction = inst;
@ -25,7 +24,7 @@ public class InstructExecutor : IExecutor
var handler = handlers.FirstOrDefault(x => x.Name == inst.Function);
handler.SetDialogs(dialogs);
var handled = await handler.Handle(routing, inst, message, onFunctionExecuting);
var handled = await handler.Handle(routing, inst, message);
// For client display purpose
var response = dialogs.Last();

View file

@ -5,7 +5,7 @@ namespace BotSharp.Core.Routing;
public partial class RoutingService
{
private int _currentRecursionDepth = 0;
public async Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs, Func<RoleDialogModel, Task> onFunctionExecuting)
public async Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs)
{
var agentService = _services.GetRequiredService<IAgentService>();
var agent = await agentService.LoadAgent(agentId);
@ -47,7 +47,7 @@ public partial class RoutingService
message.FunctionArgs = response.FunctionArgs;
message.CurrentAgentId = agent.Id;
await InvokeFunction(message, dialogs, onFunctionExecuting);
await InvokeFunction(message, dialogs);
}
else
{
@ -67,7 +67,7 @@ public partial class RoutingService
return true;
}
private async Task<bool> InvokeFunction(RoleDialogModel message, List<RoleDialogModel> dialogs, Func<RoleDialogModel, Task>? onFunctionExecuting = null)
private async Task<bool> InvokeFunction(RoleDialogModel message, List<RoleDialogModel> dialogs)
{
// execute function
// Save states
@ -76,7 +76,7 @@ public partial class RoutingService
var routing = _services.GetRequiredService<IRoutingService>();
// Call functions
await routing.InvokeFunction(message.FunctionName, message, onFunctionExecuting);
await routing.InvokeFunction(message.FunctionName, message);
// Pass execution result to LLM to get response
if (!message.StopCompletion)
@ -101,7 +101,7 @@ public partial class RoutingService
// Send to Next LLM
var agentId = routing.Context.GetCurrentAgentId();
await InvokeAgent(agentId, dialogs, onFunctionExecuting);
await InvokeAgent(agentId, dialogs);
}
}
else

View file

@ -1,10 +1,9 @@
using BotSharp.Abstraction.Functions;
namespace BotSharp.Core.Routing;
public partial class RoutingService
{
public async Task<bool> InvokeFunction(string name, RoleDialogModel message, Func<RoleDialogModel, Task>? onFunctionExecuting = null)
public async Task<bool> InvokeFunction(string name, RoleDialogModel message)
{
var function = _services.GetServices<IFunctionCallback>().FirstOrDefault(x => x.Name == name);
if (function == null)
@ -23,11 +22,13 @@ public partial class RoutingService
.OrderBy(x => x.Priority)
.ToList();
var progressService = _services.GetService<IConversationProgressService>();
// Before executing functions
clonedMessage.Indication = function.Indication;
if (onFunctionExecuting != null)
if (progressService?.OnFunctionExecuting != null)
{
await onFunctionExecuting(clonedMessage);
await progressService.OnFunctionExecuting(clonedMessage);
}
foreach (var hook in hooks)

View file

@ -55,7 +55,7 @@ public partial class RoutingService : IRoutingService
ExecutingDirectly = true
};
var result = await handler.Handle(this, inst, message, null);
var result = await handler.Handle(this, inst, message);
var response = dialogs.Last();
response.MessageId = message.MessageId;
@ -64,7 +64,7 @@ public partial class RoutingService : IRoutingService
return response;
}
public async Task<RoleDialogModel> InstructLoop(RoleDialogModel message, List<RoleDialogModel> dialogs, Func<RoleDialogModel, Task> onFunctionExecuting)
public async Task<RoleDialogModel> InstructLoop(RoleDialogModel message, List<RoleDialogModel> dialogs)
{
RoleDialogModel response = default;
@ -125,12 +125,12 @@ public partial class RoutingService : IRoutingService
if (inst.HandleDialogsByPlanner)
{
var dialogWithoutContext = planner.BeforeHandleContext(inst, message, dialogs);
response = await executor.Execute(this, inst, message, dialogWithoutContext, onFunctionExecuting);
response = await executor.Execute(this, inst, message, dialogWithoutContext);
planner.AfterHandleContext(dialogs, dialogWithoutContext);
}
else
{
response = await executor.Execute(this, inst, message, dialogs, onFunctionExecuting);
response = await executor.Execute(this, inst, message, dialogs);
}
await planner.AgentExecuted(_router, inst, response, dialogs);

View file

@ -281,9 +281,7 @@ public class ConversationController : ControllerBase
response.RichContent = msg.SecondaryRichContent ?? msg.RichContent;
response.Instruction = msg.Instruction;
response.Data = msg.Data;
},
_ => Task.CompletedTask,
_ => Task.CompletedTask);
});
var state = _services.GetRequiredService<IConversationStateService>();
response.States = state.GetStates();
@ -321,6 +319,7 @@ public class ConversationController : ControllerBase
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.ContentType, "text/event-stream");
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.CacheControl, "no-cache");
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.Connection, "keep-alive");
InitProgressService(conversationId);
await conv.SendMessage(agentId, inputMsg,
replyMessage: input.Postback,
@ -335,25 +334,6 @@ public class ConversationController : ControllerBase
response.States = state.GetStates();
await OnChunkReceived(Response, response);
},
// executing
async msg =>
{
var indicator = new ChatResponseModel
{
ConversationId = conversationId,
MessageId = msg.MessageId,
Text = msg.Indication,
Function = "indicating",
Instruction = msg.Instruction,
States = new Dictionary<string, string>()
};
await OnChunkReceived(Response, indicator);
},
// executed
async msg =>
{
});
response.States = state.GetStates();
@ -362,6 +342,25 @@ public class ConversationController : ControllerBase
// await OnEventCompleted(Response);
}
private void InitProgressService(string conversationId)
{
var progressService = _services.GetService<IConversationProgressService>();
progressService.OnFunctionExecuting = async msg =>
{
var indicator = new ChatResponseModel
{
ConversationId = conversationId,
MessageId = msg.MessageId,
Text = msg.Indication,
Function = "indicating",
Instruction = msg.Instruction,
States = new Dictionary<string, string>()
};
await OnChunkReceived(Response, indicator);
};
progressService.OnFunctionExecuted = async msg => { };
}
#endregion
#region Files and attachments

View file

@ -63,6 +63,9 @@ namespace BotSharp.Plugin.Twilio.Services
var conv = sp.GetRequiredService<IConversationService>();
var routing = sp.GetRequiredService<IRoutingService>();
var config = sp.GetRequiredService<TwilioSetting>();
var sessionManager = sp.GetRequiredService<ITwilioSessionManager>();
var progressService = sp.GetRequiredService<IConversationProgressService>();
InitProgressService(message, sessionManager, progressService);
routing.Context.SetMessageId(message.ConversationId, inputMsg.MessageId);
var states = new List<MessageState>
@ -77,7 +80,6 @@ namespace BotSharp.Plugin.Twilio.Services
}
conv.SetConversationId(message.ConversationId, states);
var sessionManager = sp.GetRequiredService<ITwilioSessionManager>();
var result = await conv.SendMessage(config.AgentId,
inputMsg,
replyMessage: null,
@ -90,15 +92,7 @@ namespace BotSharp.Plugin.Twilio.Services
Content = msg.Content,
MessageId = msg.MessageId
};
},
async msg =>
{
if (!string.IsNullOrEmpty(msg.Indication))
{
await sessionManager.SetReplyIndicationAsync(message.ConversationId, message.SeqNumber, msg.Indication);
}
},
async functionExecuted => { }
}
);
var completion = CompletionProvider.GetAudioCompletion(sp, "openai", "tts-1");
@ -130,5 +124,17 @@ namespace BotSharp.Plugin.Twilio.Services
reply.Content = null;
await sessionManager.SetAssistantReplyAsync(message.ConversationId, message.SeqNumber, reply);
}
private static void InitProgressService(CallerMessage message, ITwilioSessionManager sessionManager, IConversationProgressService progressService)
{
progressService.OnFunctionExecuting = async msg =>
{
if (!string.IsNullOrEmpty(msg.Indication))
{
await sessionManager.SetReplyIndicationAsync(message.ConversationId, message.SeqNumber, msg.Indication);
}
};
progressService.OnFunctionExecuted = async msg => { };
}
}
}