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 ;
}
}