Function templating response.

This commit is contained in:
Haiping Chen 2023-08-30 18:29:01 -05:00
parent 9393508a46
commit 4dcb00ca83
10 changed files with 140 additions and 27 deletions

View file

@ -0,0 +1,6 @@
namespace BotSharp.Abstraction.Templating;
public interface IResponseTemplateService
{
Task<string> RenderFunctionResponse(string agentId, RoleDialogModel fn);
}

View file

@ -2,5 +2,5 @@ namespace BotSharp.Abstraction.Templating;
public interface ITemplateRender
{
bool Render(Agent agent, Dictionary<string, object> dict);
string Render(string template, Dictionary<string, object> dict);
}

View file

@ -3,4 +3,5 @@ global using System.Collections.Generic;
global using System.Text;
global using System.Threading.Tasks;
global using System.ComponentModel.DataAnnotations;
global using BotSharp.Abstraction.Agents.Models;
global using BotSharp.Abstraction.Agents.Models;
global using BotSharp.Abstraction.Conversations.Models;

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Templating;
using BotSharp.Core.Templating;
namespace BotSharp.Core.Agents.Services;
@ -45,8 +46,8 @@ public partial class AgentService
}
// render liquid template
var render = _services.GetRequiredService<TemplateRender>();
render.Render(agent, templateDict);
var render = _services.GetRequiredService<ITemplateRender>();
agent.Instruction = render.Render(agent.Instruction, templateDict);
_logger.LogInformation($"Loaded agent {agent}.");

View file

@ -8,6 +8,7 @@ using BotSharp.Core.Plugins.Knowledges.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core;
@ -42,7 +43,8 @@ public static class BotSharpServiceCollectionExtensions
RegisterPlugins(services, config);
// Register template render
services.AddSingleton<TemplateRender>();
services.AddSingleton<ITemplateRender, TemplateRender>();
services.AddScoped<IResponseTemplateService, ResponseTemplateService>();
// Register router
var routingSettings = new RoutingSettings();

View file

@ -2,6 +2,11 @@ using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Templating;
using BotSharp.Core.Templating;
using System.IO;
using Tensorflow.Keras.Layers.Rnn;
using static System.Net.Mime.MediaTypeNames;
namespace BotSharp.Core.Conversations.Services;
@ -68,22 +73,48 @@ public partial class ConversationService
{
var agentService = _services.GetRequiredService<IAgentService>();
agent = await agentService.LoadAgent(fn.CurrentAgentId);
wholeDialogs.Add(fn);
await GetChatCompletionsAsyncRecursively(chatCompletion,
conversationId,
agent,
wholeDialogs,
onMessageReceived,
onFunctionExecuting,
onFunctionExecuted);
}
else
{
// Find response template
var templateService = _services.GetRequiredService<IResponseTemplateService>();
var response = await templateService.RenderFunctionResponse(agent.Id, fn);
if (!string.IsNullOrEmpty(response))
{
await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, response)
{
CurrentAgentId = agent.Id,
Channel = wholeDialogs.Last().Channel
}, onMessageReceived);
// Add to dialog history
// The server had an error processing your request. Sorry about that!
// _storage.Append(conversationId, preAgentId, fn);
return;
}
// Add to dialog history
// The server had an error processing your request. Sorry about that!
// _storage.Append(conversationId, preAgentId, fn);
// After function is executed, pass the result to LLM to get a natural response
wholeDialogs.Add(fn);
// 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,
onFunctionExecuted);
await GetChatCompletionsAsyncRecursively(chatCompletion,
conversationId,
agent,
wholeDialogs,
onMessageReceived,
onFunctionExecuting,
onFunctionExecuted);
}
});
return result;

View file

@ -0,0 +1,69 @@
using BotSharp.Abstraction.Templating;
using System.IO;
using System.Reflection;
namespace BotSharp.Core.Templating;
public class ResponseTemplateService : IResponseTemplateService
{
private readonly IServiceProvider _services;
public ResponseTemplateService(IServiceProvider services)
{
_services = services;
}
public async Task<string> RenderFunctionResponse(string agentId, RoleDialogModel fn)
{
// Find response template
var agentService = _services.GetRequiredService<IAgentService>();
var dir = Path.Combine(agentService.GetAgentDataDir(agentId), "responses");
var responses = Directory.GetFiles(dir)
.Where(f => f.Split(Path.DirectorySeparatorChar).Last().Split('.')[1] == fn.FunctionName)
.ToList();
if (responses.Count == 0)
{
return string.Empty;
}
var randomIndex = new Random().Next(0, responses.Count);
var template = File.ReadAllText(responses[randomIndex]);
var render = _services.GetRequiredService<ITemplateRender>();
// Convert args and execute data to dictionary
var dict = new Dictionary<string, object>();
ExtractArgs(JsonSerializer.Deserialize<JsonDocument>(fn.FunctionArgs), dict);
ExtractExecuteData(fn.ExecutionData, dict);
var text = render.Render(template, dict);
return text;
}
private void ExtractArgs(JsonDocument args, Dictionary<string, object> dict)
{
if (args.RootElement is JsonElement root)
{
foreach (JsonProperty property in root.EnumerateObject())
{
if (!string.IsNullOrEmpty(property.Value.ToString()))
{
dict[property.Name] = property.Value.ToString();
}
}
}
}
private void ExtractExecuteData(object data, Dictionary<string, object> dict)
{
foreach (PropertyInfo property in data.GetType().GetProperties())
{
var value = property.GetValue(data, null);
if (value != null)
{
dict[property.Name] = value;
}
}
}
}

View file

@ -1,8 +1,6 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Templating;
using Fluid;
using Microsoft.Extensions.Options;
namespace BotSharp.Core.Templating;
@ -22,19 +20,18 @@ public class TemplateRender : ITemplateRender
_options.MemberAccessStrategy.Register<RoutingRecord>();
}
public bool Render(Agent agent, Dictionary<string, object> dict)
public string Render(string template, Dictionary<string, object> dict)
{
var template = agent.Instruction;
if (_parser.TryParse(template, out var t, out var error))
{
var context = new TemplateContext(dict, _options);
agent.Instruction = t.Render(context);
return true;
template = t.Render(context);
return template;
}
else
{
return false;
return template;
}
}
}

View file

@ -13,6 +13,9 @@ global using BotSharp.Abstraction.Conversations;
global using BotSharp.Abstraction.Knowledges;
global using BotSharp.Abstraction.Users;
global using BotSharp.Abstraction.Utilities;
global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Agents.Settings;
global using BotSharp.Abstraction.Conversations.Settings;
global using BotSharp.Core.Repository;
global using BotSharp.Core.Repository.Abstraction;
global using BotSharp.Core.Repository.DbTables;
@ -20,6 +23,4 @@ global using BotSharp.Core.Agents.Services;
global using BotSharp.Core.Conversations.Services;
global using BotSharp.Core.Infrastructures;
global using BotSharp.Core.Plugins;
global using BotSharp.Core.Users.Services;
global using BotSharp.Abstraction.Agents.Settings;
global using BotSharp.Abstraction.Conversations.Settings;
global using BotSharp.Core.Users.Services;

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Conversations.Models;
using System.Text.Json;
namespace BotSharp.Plugin.PizzaBot.Functions;
@ -8,6 +9,10 @@ public class GetPizzaPricesFn : IFunctionCallback
public async Task<bool> Execute(RoleDialogModel message)
{
message.ExecutionData = new
{
cheese = "3.5"
};
message.ExecutionResult = "Pepperoni Pizza: $3.5/slice, Cheese Pizza: $2.5/slice, Margherita Pizza: $3.0/slice";
return true;
}