BotSharp/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/ChatCompletionProvider.cs

153 lines
5.3 KiB
C#
Raw Normal View History

2023-10-28 20:59:26 +00:00
using BotSharp.Abstraction.Agents;
2023-10-08 20:46:42 +00:00
using BotSharp.Abstraction.Agents.Enums;
2023-11-29 23:23:14 +00:00
using BotSharp.Abstraction.Loggers;
2023-11-30 02:56:23 +00:00
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Routing;
2023-10-08 20:46:42 +00:00
using BotSharp.Plugin.GoogleAI.Settings;
using LLMSharp.Google.Palm;
using Microsoft.Extensions.Logging;
2023-12-01 03:59:05 +00:00
using LLMSharp.Google.Palm.DiscussService;
2023-10-08 20:46:42 +00:00
namespace BotSharp.Plugin.GoogleAI.Providers;
public class ChatCompletionProvider : IChatCompletion
{
public string Provider => "google-ai";
private readonly IServiceProvider _services;
private readonly GoogleAiSettings _settings;
private readonly ILogger _logger;
private string _model;
public ChatCompletionProvider(IServiceProvider services,
GoogleAiSettings settings,
2023-10-16 20:07:07 +00:00
ILogger<ChatCompletionProvider> logger)
2023-10-08 20:46:42 +00:00
{
_services = services;
_settings = settings;
_logger = logger;
}
2024-01-14 04:48:26 +00:00
public async Task<RoleDialogModel> GetChatCompletions(Agent agent, List<RoleDialogModel> conversations)
2023-10-08 20:46:42 +00:00
{
2023-10-16 20:07:07 +00:00
var hooks = _services.GetServices<IContentGeneratingHook>().ToList();
// Before chat completion hook
Task.WaitAll(hooks.Select(hook =>
hook.BeforeGenerating(agent, conversations)).ToArray());
2023-10-08 20:46:42 +00:00
var client = new GooglePalmClient(apiKey: _settings.PaLM.ApiKey);
2023-10-09 22:28:17 +00:00
2023-12-01 03:59:05 +00:00
var (prompt, messages, hasFunctions) = PrepareOptions(agent, conversations);
2023-11-30 02:56:23 +00:00
RoleDialogModel msg;
2023-12-01 03:59:05 +00:00
if (hasFunctions)
2023-11-30 02:56:23 +00:00
{
// use text completion
2023-12-01 03:59:05 +00:00
// var response = client.GenerateTextAsync(prompt, null).Result;
2024-01-14 04:48:26 +00:00
var response = await client.ChatAsync(new PalmChatCompletionRequest
2023-12-01 03:59:05 +00:00
{
Context = prompt,
Messages = messages,
Temperature = 0.1f
2024-01-14 04:48:26 +00:00
});
2023-11-30 02:56:23 +00:00
var message = response.Candidates.First();
2023-10-08 20:46:42 +00:00
2023-11-30 02:56:23 +00:00
// check if returns function calling
2023-12-01 03:59:05 +00:00
var llmResponse = message.Content.JsonContent<FunctionCallingResponse>();
2023-11-30 02:56:23 +00:00
msg = new RoleDialogModel(llmResponse.Role, llmResponse.Content)
{
CurrentAgentId = agent.Id,
FunctionName = llmResponse.FunctionName,
FunctionArgs = JsonSerializer.Serialize(llmResponse.Args)
};
}
else
2023-10-08 20:46:42 +00:00
{
2024-01-14 04:48:26 +00:00
var response = await client.ChatAsync(messages, context: prompt, examples: null, options: null);
2023-11-30 02:56:23 +00:00
var message = response.Candidates.First();
// check if returns function calling
var llmResponse = message.Content.JsonContent<FunctionCallingResponse>();
msg = new RoleDialogModel(llmResponse.Role, llmResponse.Content ?? message.Content)
{
CurrentAgentId = agent.Id
};
}
2023-10-08 20:46:42 +00:00
2023-10-16 20:07:07 +00:00
// After chat completion hook
Task.WaitAll(hooks.Select(hook =>
hook.AfterGenerated(msg, new TokenStatsModel
{
2023-12-01 03:59:05 +00:00
Prompt = prompt,
2023-10-16 20:07:07 +00:00
Model = _model
})).ToArray());
2023-10-08 20:46:42 +00:00
return msg;
}
2023-12-01 03:59:05 +00:00
private (string, List<PalmChatMessage>, bool) PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
2023-11-30 02:56:23 +00:00
{
var prompt = "";
var agentService = _services.GetRequiredService<IAgentService>();
if (!string.IsNullOrEmpty(agent.Instruction))
{
prompt += agentService.RenderedInstruction(agent);
}
var routing = _services.GetRequiredService<IRoutingService>();
var router = routing.Router;
2023-12-01 03:59:05 +00:00
var messages = conversations.Select(c => new PalmChatMessage(c.Content, c.Role == AgentRole.User ? "user" : "AI"))
.ToList();
2023-11-30 02:56:23 +00:00
if (agent.Functions != null && agent.Functions.Count > 0)
{
prompt += "\r\n\r\n[Functions] defined in JSON Schema:\r\n";
prompt += JsonSerializer.Serialize(agent.Functions, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
});
prompt += "\r\n\r\n[Conversations]\r\n";
foreach (var dialog in conversations)
{
prompt += dialog.Role == AgentRole.Function ?
$"{dialog.Role}: {dialog.FunctionName} => {dialog.Content}\r\n" :
$"{dialog.Role}: {dialog.Content}\r\n";
}
prompt += "\r\n\r\n" + router.Templates.FirstOrDefault(x => x.Name == "response_with_function").Content;
2023-12-01 03:59:05 +00:00
return (prompt, new List<PalmChatMessage>
{
new PalmChatMessage("Which function should be used for the next step based on latest user or function response, output your response in JSON:", AgentRole.User),
}, true);
2023-11-30 02:56:23 +00:00
}
2023-12-01 03:59:05 +00:00
return (prompt, messages, false);
2023-11-30 02:56:23 +00:00
}
2023-10-08 20:46:42 +00:00
public Task<bool> GetChatCompletionsAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived, Func<RoleDialogModel, Task> onFunctionExecuting)
{
throw new NotImplementedException();
}
public Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
{
throw new NotImplementedException();
}
public void SetModelName(string model)
{
_model = model;
}
}