Format LLM functions.

This commit is contained in:
hchen 2023-09-27 15:49:44 -05:00
parent 2109a68851
commit 6ce664d618
20 changed files with 121 additions and 77 deletions

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Settings;
using BotSharp.Abstraction.Functions.Models;
namespace BotSharp.Abstraction.Agents;
@ -34,7 +35,7 @@ public abstract class AgentHookBase : IAgentHook
return true;
}
public virtual bool OnFunctionsLoaded(ref List<string> functions)
public virtual bool OnFunctionsLoaded(ref List<FunctionDef> functions)
{
_agent.Functions = functions;
return true;

View file

@ -1,3 +1,5 @@
using BotSharp.Abstraction.Functions.Models;
namespace BotSharp.Abstraction.Agents;
public interface IAgentHook
@ -15,7 +17,7 @@ public interface IAgentHook
bool OnInstructionLoaded(string template, Dictionary<string, object> dict);
bool OnFunctionsLoaded(ref List<string> functions);
bool OnFunctionsLoaded(ref List<FunctionDef> functions);
bool OnSamplesLoaded(ref string samples);

View file

@ -1,3 +1,4 @@
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.Abstraction.Agents.Models;
@ -32,7 +33,7 @@ public class Agent
/// Functions
/// </summary>
[JsonIgnore]
public List<string> Functions { get; set; }
public List<FunctionDef> Functions { get; set; } = new List<FunctionDef>();
/// <summary>
/// Responses
@ -102,9 +103,9 @@ public class Agent
return this;
}
public Agent SetFunctions(List<string> functions)
public Agent SetFunctions(List<FunctionDef> functions)
{
Functions = functions ?? new List<string>();
Functions = functions ?? new List<FunctionDef>();
return this;
}

View file

@ -1,12 +1,10 @@
using System.Text.Json;
namespace BotSharp.Abstraction.Functions.Models;
public class FunctionDef
{
public string Name { get; set; }
public string Description { get; set; }
public JsonDocument Parameters { get; set; }
public FunctionParametersDef Parameters { get; set; }
public override string ToString()
{

View file

@ -0,0 +1,21 @@
using System.Text.Json;
namespace BotSharp.Abstraction.Functions.Models;
public class FunctionParametersDef
{
[JsonPropertyName("type")]
public string Type { get; set; } = "object";
/// <summary>
/// ParameterPropertyDef
/// {
/// "field_name": {}
/// }
/// </summary>
[JsonPropertyName("properties")]
public JsonDocument Properties { get; set; } = JsonSerializer.Deserialize<JsonDocument>("{}");
[JsonPropertyName("required")]
public List<string> Required { get; set; } = new List<string>();
}

View file

@ -0,0 +1,7 @@
namespace BotSharp.Abstraction.Functions.Models;
public class ParameterPropertyDef
{
public string Type { get; set; } = "string";
public string Description { get; set; }
}

View file

@ -3,6 +3,10 @@ using BotSharp.Abstraction.Models;
namespace BotSharp.Abstraction.Routing;
/// <summary>
/// The routing handler will be injected to Router's FUNCTIONS section of the system prompt
/// So the handler will be invoked by LLM autonomously.
/// </summary>
public interface IRoutingHandler
{
string Name { get; }

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Repositories;
using System.IO;
@ -114,14 +115,13 @@ public partial class AgentService
return templates;
}
private List<string> FetchFunctionsFromFile(string fileDir)
private List<FunctionDef> FetchFunctionsFromFile(string fileDir)
{
var file = Path.Combine(fileDir, "functions.json");
if (!File.Exists(file)) return new List<string>();
if (!File.Exists(file)) return new List<FunctionDef>();
var functionsJson = File.ReadAllText(file);
var functionDefs = JsonSerializer.Deserialize<List<Abstraction.Functions.Models.FunctionDef>>(functionsJson, _options);
var functions = functionDefs.Select(x => JsonSerializer.Serialize(x, _options)).ToList();
var functions = JsonSerializer.Deserialize<List<FunctionDef>>(functionsJson, _options);
return functions;
}

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Routing.Models;
using System.IO;
@ -22,7 +23,7 @@ public partial class AgentService
record.Profiles = agent.Profiles ?? new List<string>();
record.RoutingRules = agent.RoutingRules ?? new List<RoutingRule>();
record.Instruction = agent.Instruction ?? string.Empty;
record.Functions = agent.Functions ?? new List<string>();
record.Functions = agent.Functions ?? new List<FunctionDef>();
record.Templates = agent.Templates ?? new List<AgentTemplate>();
record.Responses = agent.Responses ?? new List<AgentResponse>();

View file

@ -27,7 +27,8 @@ public partial class AgentService : IAgentService
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
WriteIndented = true,
AllowTrailingCommas = true
};
}

View file

@ -10,6 +10,7 @@ using BotSharp.Core.Instructs;
using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Routing;
using System.Reflection;
using BotSharp.Core.Routing.Hooks;
namespace BotSharp.Core;
@ -63,6 +64,8 @@ public static class BotSharpServiceCollectionExtensions
services.AddScoped<IInstructService, InstructService>();
services.AddScoped<ITokenStatistics, TokenStatistics>();
services.AddScoped<IAgentHook, AgentHook>();
return services;
}

View file

@ -372,7 +372,7 @@ public class FileRepository : IBotSharpRepository
File.WriteAllText(instructionFile, instruction);
}
private void UpdateAgentFunctions(string agentId, List<string> inputFunctions)
private void UpdateAgentFunctions(string agentId, List<FunctionDef> inputFunctions)
{
if (inputFunctions.IsNullOrEmpty()) return;
@ -385,8 +385,7 @@ public class FileRepository : IBotSharpRepository
var functions = new List<string>();
foreach (var function in inputFunctions)
{
var functionDef = JsonSerializer.Deserialize<FunctionDef>(function, _options);
functions.Add(JsonSerializer.Serialize(functionDef, _options));
functions.Add(JsonSerializer.Serialize(function, _options));
}
var functionText = JsonSerializer.Serialize(functions, _options);
@ -730,14 +729,13 @@ public class FileRepository : IBotSharpRepository
return instruction;
}
private List<string> FetchFunctions(string fileDir)
private List<FunctionDef> FetchFunctions(string fileDir)
{
var file = Path.Combine(fileDir, "functions.json");
if (!File.Exists(file)) return new List<string>();
if (!File.Exists(file)) return new List<FunctionDef>();
var functionsJson = File.ReadAllText(file);
var functionDefs = JsonSerializer.Deserialize<List<FunctionDef>>(functionsJson, _options);
var functions = functionDefs.Select(x => JsonSerializer.Serialize(x, _options)).ToList();
var functions = JsonSerializer.Deserialize<List<FunctionDef>>(functionsJson, _options);
return functions;
}

View file

@ -0,0 +1,16 @@
using BotSharp.Abstraction.Functions.Models;
namespace BotSharp.Core.Routing.Hooks;
public class AgentHook : AgentHookBase
{
public AgentHook(IServiceProvider services, AgentSettings settings)
: base(services, settings)
{
}
public override bool OnFunctionsLoaded(ref List<FunctionDef> functions)
{
return base.OnFunctionsLoaded(ref functions);
}
}

View file

@ -18,51 +18,48 @@ public partial class RoutingService
var agent = await agentService.LoadAgent(agentId);
var chatCompletion = CompletionProvider.GetChatCompletion(_services);
RoleDialogModel response = chatCompletion.GetChatCompletions(agent, Dialogs);
RoleDialogModel response = null;
await chatCompletion.GetChatCompletionsAsync(agent, Dialogs,
async msg =>
if (response.Role == AgentRole.Function)
{
var fn = response;
// execute function
// Save states
SaveStateByArgs(JsonSerializer.Deserialize<JsonDocument>(fn.FunctionArgs));
var conversationService = _services.GetRequiredService<IConversationService>();
// Call functions
await conversationService.CallFunctions(fn);
if (string.IsNullOrEmpty(fn.Content))
{
response = msg;
}, async fn =>
fn.Content = fn.ExecutionResult;
}
Dialogs.Add(fn);
if (!fn.StopCompletion)
{
// execute function
// Save states
SaveStateByArgs(JsonSerializer.Deserialize<JsonDocument>(fn.FunctionArgs));
var conversationService = _services.GetRequiredService<IConversationService>();
// Call functions
await conversationService.CallFunctions(fn);
if (string.IsNullOrEmpty(fn.Content))
// Find response template
var templateService = _services.GetRequiredService<IResponseTemplateService>();
var quickResponse = await templateService.RenderFunctionResponse(agent.Id, fn);
if (!string.IsNullOrEmpty(quickResponse))
{
fn.Content = fn.ExecutionResult;
}
Dialogs.Add(fn);
if (!fn.StopCompletion)
{
// Find response template
var templateService = _services.GetRequiredService<IResponseTemplateService>();
var quickResponse = await templateService.RenderFunctionResponse(agent.Id, fn);
if (!string.IsNullOrEmpty(quickResponse))
response = new RoleDialogModel(AgentRole.Assistant, quickResponse)
{
response = new RoleDialogModel(AgentRole.Assistant, quickResponse)
{
CurrentAgentId = agent.Id
};
}
else
{
response = await InvokeAgent(fn.CurrentAgentId);
}
CurrentAgentId = agent.Id
};
}
else
{
response = fn;
response = await InvokeAgent(fn.CurrentAgentId);
}
});
}
else
{
response = fn;
}
}
return response;
}

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.OpenAPI.ViewModels.Agents;
@ -9,7 +10,7 @@ public class AgentCreationModel
public string Description { get; set; }
public string Instruction { get; set; }
public List<AgentTemplate> Templates { get; set; }
public List<string> Functions { get; set; }
public List<FunctionDef> Functions { get; set; }
public List<AgentResponse> Responses { get; set; }
public bool IsPublic { get; set; }
public bool AllowRouting { get; set; }

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.OpenAPI.ViewModels.Agents;
@ -26,7 +27,7 @@ public class AgentUpdateModel
/// <summary>
/// Functions
/// </summary>
public List<string>? Functions { get; set; }
public List<FunctionDef>? Functions { get; set; }
/// <summary>
/// Routes
@ -61,7 +62,7 @@ public class AgentUpdateModel
.ToList() ?? new List<RoutingRule>(),
Instruction = Instruction ?? string.Empty,
Templates = Templates ?? new List<AgentTemplate>(),
Functions = Functions ?? new List<string>(),
Functions = Functions ?? new List<FunctionDef>(),
Responses = Responses ?? new List<AgentResponse>()
};

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.OpenAPI.ViewModels.Agents;
@ -10,7 +11,7 @@ public class AgentViewModel
public string Description { get; set; }
public string Instruction { get; set; }
public List<AgentTemplate> Templates { get; set; }
public List<string> Functions { get; set; }
public List<FunctionDef> Functions { get; set; }
public List<AgentResponse> Responses { get; set; }
public bool IsPublic { get; set; }
public bool AllowRouting { get; set; }

View file

@ -84,17 +84,6 @@ public class ChatCompletionProvider : IChatCompletion
return samples;
}
public List<FunctionDef> GetFunctions(List<string> functionsJson)
{
var functions = functionsJson?.Select(x => JsonSerializer.Deserialize<FunctionDef>(x, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
AllowTrailingCommas = true
}))?.ToList() ?? new List<FunctionDef>();
return functions;
}
public RoleDialogModel GetChatCompletions(Agent agent, List<RoleDialogModel> conversations)
{
var (client, deploymentModel) = GetClient();
@ -263,8 +252,7 @@ public class ChatCompletionProvider : IChatCompletion
chatCompletionsOptions.Messages.Add(new ChatMessage(message.Role, message.Content));
}
var functions = GetFunctions(agent.Functions);
foreach (var function in functions)
foreach (var function in agent.Functions)
{
chatCompletionsOptions.Functions.Add(new FunctionDefinition
{
@ -301,10 +289,11 @@ public class ChatCompletionProvider : IChatCompletion
var convSetting = _services.GetRequiredService<ConversationSetting>();
if (convSetting.ShowVerboseLog)
{
_logger.LogInformation("VERBOSE COMPLETION MESSAGES");
var verbose = string.Join("\n", chatCompletionsOptions.Messages.Select(x =>
{
return x.Role == ChatRole.Function ?
$"{x.Role}: {x.Name} {x.Content}" :
$"{x.Role}: {x.Name} => {x.Content}" :
$"{x.Role}: {x.Content}";
}));

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Plugin.MongoStorage.Models;
namespace BotSharp.Plugin.MongoStorage.Collections;
@ -9,7 +10,7 @@ public class AgentCollection : MongoBase
public string Description { get; set; }
public string Instruction { get; set; }
public List<AgentTemplate> Templates { get; set; }
public List<string> Functions { get; set; }
public List<FunctionDef> Functions { get; set; }
public List<AgentResponse> Responses { get; set; }
public bool IsPublic { get; set; }
public bool AllowRouting { get; set; }

View file

@ -1,5 +1,6 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Users.Models;
using BotSharp.Plugin.MongoStorage.Collections;
@ -441,7 +442,7 @@ public class MongoRepository : IBotSharpRepository
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentFunctions(string agentId, List<string> functions)
private void UpdateAgentFunctions(string agentId, List<FunctionDef> functions)
{
if (functions.IsNullOrEmpty()) return;