From 99e949032aeb3fccf63be7cd96fa504f927a0845 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Sun, 22 Dec 2024 20:12:35 -0600
Subject: [PATCH 1/4] add gemini
---
.../Agents/Enums/AgentRole.cs | 1 +
.../BotSharp.Plugin.GoogleAI.csproj | 1 +
.../GoogleAiPlugin.cs | 12 +-
.../Chat/GeminiChatCompletionProvider.cs | 194 ++++++++++++++++++
.../PalmChatCompletionProvider.cs} | 43 ++--
.../Providers/ProviderHelper.cs | 21 ++
.../Text/GeminiTextCompletionProvider.cs | 84 ++++++++
.../Text/PalmTextCompletionProvider.cs | 66 ++++++
.../Providers/TextCompletionProvider.cs | 68 ------
.../Settings/GoogleAiSettings.cs | 18 +-
.../Settings/PaLMSetting.cs | 7 -
src/Plugins/BotSharp.Plugin.GoogleAI/Using.cs | 3 +-
src/WebStarter/appsettings.json | 5 +
13 files changed, 421 insertions(+), 102 deletions(-)
create mode 100644 src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs
rename src/Plugins/BotSharp.Plugin.GoogleAI/Providers/{ChatCompletionProvider.cs => Chat/PalmChatCompletionProvider.cs} (85%)
create mode 100644 src/Plugins/BotSharp.Plugin.GoogleAI/Providers/ProviderHelper.cs
create mode 100644 src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Text/GeminiTextCompletionProvider.cs
create mode 100644 src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Text/PalmTextCompletionProvider.cs
delete mode 100644 src/Plugins/BotSharp.Plugin.GoogleAI/Providers/TextCompletionProvider.cs
delete mode 100644 src/Plugins/BotSharp.Plugin.GoogleAI/Settings/PaLMSetting.cs
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs
index f9547d5a..226313e0 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs
@@ -6,4 +6,5 @@ public class AgentRole
public const string Assistant = "assistant";
public const string User = "user";
public const string Function = "function";
+ public const string Model = "model";
}
diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/BotSharp.Plugin.GoogleAI.csproj b/src/Plugins/BotSharp.Plugin.GoogleAI/BotSharp.Plugin.GoogleAI.csproj
index 93c0f9df..b6e0b24e 100644
--- a/src/Plugins/BotSharp.Plugin.GoogleAI/BotSharp.Plugin.GoogleAI.csproj
+++ b/src/Plugins/BotSharp.Plugin.GoogleAI/BotSharp.Plugin.GoogleAI.csproj
@@ -12,6 +12,7 @@
+
diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/GoogleAiPlugin.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/GoogleAiPlugin.cs
index aac88308..58a384a7 100644
--- a/src/Plugins/BotSharp.Plugin.GoogleAI/GoogleAiPlugin.cs
+++ b/src/Plugins/BotSharp.Plugin.GoogleAI/GoogleAiPlugin.cs
@@ -1,9 +1,9 @@
using BotSharp.Abstraction.Plugins;
using BotSharp.Abstraction.Settings;
-using BotSharp.Plugin.GoogleAI.Providers;
-using BotSharp.Plugin.GoogleAI.Settings;
+using BotSharp.Plugin.GoogleAi.Providers.Chat;
+using BotSharp.Plugin.GoogleAi.Providers.Text;
-namespace BotSharp.Plugin.GoogleAI;
+namespace BotSharp.Plugin.GoogleAi;
public class GoogleAiPlugin : IBotSharpPlugin
{
@@ -19,7 +19,9 @@ public class GoogleAiPlugin : IBotSharpPlugin
return settingService.Bind("GoogleAi");
});
- services.AddScoped();
- services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
}
}
diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs
new file mode 100644
index 00000000..b94dd534
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs
@@ -0,0 +1,194 @@
+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