Add onFunctionExecuted to reflect UI progress.

This commit is contained in:
hchen2020 2023-08-20 14:02:59 -05:00
parent 883ef65d2a
commit 0da5bd36f7
15 changed files with 103 additions and 26 deletions

View file

@ -2,5 +2,6 @@ namespace BotSharp.Abstraction.Agents;
public interface IAgentRouting
{
Task<Agent> LoadRouter();
Task<Agent> LoadCurrentAgent();
}

View file

@ -0,0 +1,9 @@
using System.Text.Json.Serialization;
namespace BotSharp.Abstraction.Agents.Models;
public class AgentRoutingArgs
{
[JsonPropertyName("agent_id")]
public string AgentId { get; set; }
}

View file

@ -20,12 +20,14 @@ public interface IConversationService
/// <param name="lastDalog"></param>
/// <param name="onMessageReceived"></param>
/// <param name="onFunctionExecuting">This delegate is useful when you want to report progress on UI</param>
/// <param name="onFunctionExecuted">This delegate is useful when you want to report progress on UI</param>
/// <returns></returns>
Task<bool> SendMessage(string agentId,
string conversationId,
RoleDialogModel lastDalog,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting);
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted);
List<RoleDialogModel> GetDialogHistory(string conversationId, int lastCount = 20);
Task CleanHistory(string agentId);

View file

@ -1,3 +1,4 @@
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Agents.Models;
namespace BotSharp.Core.Agents.Services;
@ -17,11 +18,18 @@ public class AgentRouter : IAgentRouting
_settings = settings;
}
public async Task<Agent> LoadRouter()
{
var agentService = _services.GetRequiredService<IAgentService>();
var agent = await agentService.LoadAgent(_settings.RouterId);
return agent;
}
public async Task<Agent> LoadCurrentAgent()
{
// Load current agent from state
var state = _services.GetRequiredService<IConversationStateService>();
var currentAgentId = state.GetState("agentId");
var currentAgentId = state.GetState("agent_id");
if (string.IsNullOrEmpty(currentAgentId))
{
currentAgentId = _settings.RouterId;
@ -30,7 +38,7 @@ public class AgentRouter : IAgentRouting
var agent = await agentService.LoadAgent(currentAgentId);
// Set agent and trigger state changed
state.SetState("agentId", currentAgentId);
state.SetState("agent_id", currentAgentId);
return agent;
}

View file

@ -73,7 +73,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Colorful.Console" Version="1.2.15" />
<PackageReference Include="EntityFrameworkCore.BootKit" Version="6.2.1" />
<PackageReference Include="Fluid.Core" Version="2.4.0" />
<PackageReference Include="TensorFlow.Keras" Version="0.11.2" />

View file

@ -38,6 +38,7 @@ public static class BotSharpServiceCollectionExtensions
services.AddScoped<IAgentRouting, AgentRouter>();
services.AddScoped<IFunctionCallback, GoToRouterFn>();
services.AddScoped<IFunctionCallback, RouteToAgentFn>();
return services;
}

View file

@ -15,7 +15,8 @@ public partial class ConversationService
Agent agent,
List<RoleDialogModel> wholeDialogs,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting)
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted)
{
currentRecursiveDepth++;
if (currentRecursiveDepth > maxRecursiveDepth)
@ -38,7 +39,7 @@ public partial class ConversationService
{
var preAgentId = agent.Id;
await HandleFunctionMessage(fn, onFunctionExecuting);
await HandleFunctionMessage(fn, onFunctionExecuting, onFunctionExecuted);
// Function executed has exception
if (fn.ExecutionResult == null)
@ -58,10 +59,6 @@ public partial class ConversationService
var agentSettings = _services.GetRequiredService<AgentSettings>();
var agentService = _services.GetRequiredService<IAgentService>();
agent = await agentService.LoadAgent(fn.CurrentAgentId);
// Set state to make next conversation will go to this agent directly
// var state = _services.GetRequiredService<IConversationStateService>();
// state.SetState("agentId", fn.CurrentAgentId);
}
// Add to dialog history
@ -70,7 +67,13 @@ public partial class ConversationService
// After function is executed, pass the result to LLM to get a natural response
wholeDialogs.Add(fn);
await GetChatCompletionsAsyncRecursively(chatCompletion, conversationId, agent, wholeDialogs, onMessageReceived, onFunctionExecuting);
await GetChatCompletionsAsyncRecursively(chatCompletion,
conversationId,
agent,
wholeDialogs,
onMessageReceived,
onFunctionExecuting,
onFunctionExecuted);
});
return result;
@ -89,7 +92,9 @@ public partial class ConversationService
await onMessageReceived(msg);
}
private async Task HandleFunctionMessage(RoleDialogModel msg, Func<RoleDialogModel, Task> onFunctionExecuting)
private async Task HandleFunctionMessage(RoleDialogModel msg,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted)
{
// Save states
SaveStateByArgs(msg.FunctionArgs);
@ -97,5 +102,6 @@ public partial class ConversationService
// Call functions
await onFunctionExecuting(msg);
await CallFunctions(msg);
await onFunctionExecuted(msg);
}
}

View file

@ -8,7 +8,8 @@ public partial class ConversationService
public async Task<bool> SendMessage(string agentId, string conversationId,
RoleDialogModel lastDialog,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting)
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted)
{
var converation = await GetConversation(conversationId);
@ -29,7 +30,7 @@ public partial class ConversationService
stateService.Load();
var router = _services.GetRequiredService<IAgentRouting>();
var agent = await router.LoadCurrentAgent();
var agent = await router.LoadRouter();
_logger.LogInformation($"[{agent.Name}] {lastDialog.Role}: {lastDialog.Content}");
@ -67,7 +68,8 @@ public partial class ConversationService
agent,
wholeDialogs,
onMessageReceived,
onFunctionExecuting);
onFunctionExecuting,
onFunctionExecuted);
return result;
}

View file

@ -0,0 +1,36 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions;
using BotSharp.Abstraction.Functions.Models;
namespace BotSharp.Core.Functions;
public class RouteToAgentFn : IFunctionCallback
{
public string Name => "route_to_agent";
private readonly IServiceProvider _services;
public RouteToAgentFn(IServiceProvider services)
{
_services = services;
}
public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<AgentRoutingArgs>(message.FunctionArgs);
if (string.IsNullOrEmpty(args.AgentId))
{
var result = new FunctionExecutionValidationResult("false", "agent_id can't be parsed.");
message.ExecutionResult = JsonSerializer.Serialize(result);
}
else
{
var result = new FunctionExecutionValidationResult("true");
message.ExecutionResult = JsonSerializer.Serialize(result);
message.CurrentAgentId = args.AgentId;
}
return true;
}
}

View file

@ -3,7 +3,6 @@ using Microsoft.Extensions.Configuration;
using System.Drawing;
using System.IO;
using System.Reflection;
using Console = Colorful.Console;
namespace BotSharp.Core.Plugins;

View file

@ -1,8 +1,6 @@
using BotSharp.Abstraction.ApiAdapters;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.OpenAPI.ViewModels.Conversations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace BotSharp.OpenAPI.Controllers;
@ -52,9 +50,17 @@ public class ConversationController : ControllerBase, IApiAdapter
await conv.SendMessage(agentId, conversationId,
new RoleDialogModel("user", input.Text),
async msg =>
stackMsg.Add(msg),
async fn
=> await Task.CompletedTask);
{
stackMsg.Add(msg);
},
async fnExecuting =>
{
},
async fnExecuted =>
{
response.Json = JsonSerializer.Deserialize<object>(fnExecuted.ExecutionResult);
});
response.Text = string.Join("\r\n", stackMsg.Select(x => x.Content));
return response;

View file

@ -3,4 +3,5 @@ namespace BotSharp.OpenAPI.ViewModels.Conversations;
public class MessageResponseModel
{
public string Text { get; set; }
public object Json { get; set; }
}

View file

@ -75,6 +75,8 @@ public class ChatbotUiController : ControllerBase, IApiAdapter
async msg =>
await OnChunkReceived(outputStream, msg),
async fn
=> await Task.CompletedTask,
async fn
=> await Task.CompletedTask);
await OnEventCompleted(outputStream);

View file

@ -105,7 +105,7 @@ public class WebhookController : ControllerBase
}
content = msg.Content;
}, async fn =>
}, async functionExecuting =>
{
/*await messenger.SendMessage(setting.ApiVersion, setting.PageId, new SendingMessageRequest
{
@ -113,13 +113,15 @@ public class WebhookController : ControllerBase
Recipient = JsonSerializer.Serialize(new { Id = sessionId }, jsonOpt),
Message = JsonSerializer.Serialize(new { Text = "I'm pulling the relevent information, please wait a second ..." }, jsonOpt)
});*/
await messenger.SendMessage(setting.ApiVersion, setting.PageId, new SendingMessageRequest
}, async functionExecuted =>
{
// Render structured data
/*await messenger.SendMessage(setting.ApiVersion, setting.PageId, new SendingMessageRequest
{
AccessToken = setting.PageAccessToken,
Recipient = JsonSerializer.Serialize(new { Id = senderId }, jsonOpt),
SenderAction = SenderActionEnum.TypingOn
});
});*/
});
// Response to user

View file

@ -59,7 +59,10 @@ namespace BotSharp.Plugin.WeChat
var result = await conversationService.SendMessage(AgentId, latestConversationId, new RoleDialogModel("user", message), async msg =>
{
await ReplyTextMessageAsync(openid, msg.Content);
}, async fn =>
}, async functionExecuting =>
{
}, async functionExecuted =>
{
});