using BotSharp.Abstraction.Agents; using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Loggers; using Microsoft.Extensions.Logging; using Mscc.GenerativeAI; namespace BotSharp.Plugin.GoogleAi.Providers.Chat; public class GeminiChatCompletionProvider : IChatCompletion { private readonly IServiceProvider _services; private readonly ILogger _logger; private string _model; public string Provider => "google-gemini"; public GeminiChatCompletionProvider( IServiceProvider services, ILogger logger) { _services = services; _logger = logger; } public async Task GetChatCompletions(Agent agent, List conversations) { var contentHooks = _services.GetServices().ToList(); // Before chat completion hook foreach (var hook in contentHooks) { await hook.BeforeGenerating(agent, conversations); } var client = ProviderHelper.GetGeminiClient(_services); var aiModel = client.GenerativeModel(_model); var (prompt, request) = PrepareOptions(aiModel, agent, conversations); var response = await aiModel.GenerateContent(request); var candidate = response.Candidates.First(); var part = candidate.Content?.Parts?.FirstOrDefault(); var text = part?.Text ?? string.Empty; RoleDialogModel responseMessage; if (part?.FunctionCall != null) { responseMessage = new RoleDialogModel(AgentRole.Function, text) { CurrentAgentId = agent.Id, MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty, ToolCallId = part.FunctionCall.Name, FunctionName = part.FunctionCall.Name, FunctionArgs = part.FunctionCall.Args?.ToString() }; } else { responseMessage = new RoleDialogModel(AgentRole.Assistant, text) { CurrentAgentId = agent.Id, MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty, }; } // After chat completion hook foreach (var hook in contentHooks) { await hook.AfterGenerated(responseMessage, new TokenStatsModel { Prompt = prompt, Provider = Provider, Model = _model }); } return responseMessage; } public Task GetChatCompletionsAsync(Agent agent, List conversations, Func onMessageReceived, Func onFunctionExecuting) { throw new NotImplementedException(); } public Task GetChatCompletionsStreamingAsync(Agent agent, List conversations, Func onMessageReceived) { throw new NotImplementedException(); } public void SetModelName(string model) { _model = model; } private (string, GenerateContentRequest) PrepareOptions(GenerativeModel aiModel, Agent agent, List conversations) { var agentService = _services.GetRequiredService(); var googleSettings = _services.GetRequiredService(); // Add settings aiModel.UseGoogleSearch = googleSettings.Gemini.UseGoogleSearch; aiModel.UseGrounding = googleSettings.Gemini.UseGrounding; // Assembly messages var prompt = string.Empty; var contents = new List(); var tools = new List(); var funcDeclarations = new List(); if (!string.IsNullOrEmpty(agent.Instruction)) { var instruction = agentService.RenderedInstruction(agent); contents.Add(new Content(instruction) { Role = AgentRole.User }); prompt += $"{instruction}\r\n"; } prompt += "\r\n[FUNCTIONS]\r\n"; foreach (var function in agent.Functions) { if (!agentService.RenderFunction(agent, function)) continue; var def = agentService.RenderFunctionProperty(agent, function); funcDeclarations.Add(new FunctionDeclaration { Name = function.Name, Description = function.Description, Parameters = new() { Type = ParameterType.Object, Properties = def.Properties, Required = def.Required } }); prompt += $"{function.Name}: {function.Description} {def}\r\n\r\n"; } if (!funcDeclarations.IsNullOrEmpty()) { tools.Add(new Tool { FunctionDeclarations = funcDeclarations }); } prompt += "\r\n[CONVERSATIONS]\r\n"; foreach (var message in conversations) { if (message.Role == AgentRole.Function) { contents.Add(new Content(message.Content) { Role = AgentRole.Function, Parts = new() { new FunctionCall { Name = message.FunctionName, Args = JsonSerializer.Deserialize(message.FunctionArgs ?? "{}") } } }); prompt += $"{AgentRole.Assistant}: Call function {message.FunctionName}({message.FunctionArgs})\r\n"; } else if (message.Role == AgentRole.User) { var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; contents.Add(new Content(text) { Role = AgentRole.User }); prompt += $"{AgentRole.User}: {text}\r\n"; } else if (message.Role == AgentRole.Assistant) { contents.Add(new Content(message.Content) { Role = AgentRole.Model }); prompt += $"{AgentRole.Assistant}: {message.Content}\r\n"; } } var request = new GenerateContentRequest { Contents = contents, Tools = tools }; return (prompt, request); } }