Skip to content

Commit 99e9490

Browse files
author
Jicheng Lu
committed
add gemini
1 parent db08be2 commit 99e9490

File tree

13 files changed

+421
-102
lines changed

13 files changed

+421
-102
lines changed

src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ public class AgentRole
66
public const string Assistant = "assistant";
77
public const string User = "user";
88
public const string Function = "function";
9+
public const string Model = "model";
910
}

src/Plugins/BotSharp.Plugin.GoogleAI/BotSharp.Plugin.GoogleAI.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
<ItemGroup>
1414
<PackageReference Include="LLMSharp.Google.Palm" Version="1.0.2" />
15+
<PackageReference Include="Mscc.GenerativeAI" Version="2.0.1" />
1516
</ItemGroup>
1617

1718
<ItemGroup>

src/Plugins/BotSharp.Plugin.GoogleAI/GoogleAiPlugin.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
using BotSharp.Abstraction.Plugins;
22
using BotSharp.Abstraction.Settings;
3-
using BotSharp.Plugin.GoogleAI.Providers;
4-
using BotSharp.Plugin.GoogleAI.Settings;
3+
using BotSharp.Plugin.GoogleAi.Providers.Chat;
4+
using BotSharp.Plugin.GoogleAi.Providers.Text;
55

6-
namespace BotSharp.Plugin.GoogleAI;
6+
namespace BotSharp.Plugin.GoogleAi;
77

88
public class GoogleAiPlugin : IBotSharpPlugin
99
{
@@ -19,7 +19,9 @@ public void RegisterDI(IServiceCollection services, IConfiguration config)
1919
return settingService.Bind<GoogleAiSettings>("GoogleAi");
2020
});
2121

22-
services.AddScoped<IChatCompletion, ChatCompletionProvider>();
23-
services.AddScoped<ITextCompletion, TextCompletionProvider>();
22+
services.AddScoped<ITextCompletion, PalmTextCompletionProvider>();
23+
services.AddScoped<ITextCompletion, GeminiTextCompletionProvider>();
24+
services.AddScoped<IChatCompletion, PalmChatCompletionProvider>();
25+
services.AddScoped<IChatCompletion, GeminiChatCompletionProvider>();
2426
}
2527
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
using BotSharp.Abstraction.Agents;
2+
using BotSharp.Abstraction.Agents.Enums;
3+
using BotSharp.Abstraction.Loggers;
4+
using Microsoft.Extensions.Logging;
5+
using Mscc.GenerativeAI;
6+
7+
namespace BotSharp.Plugin.GoogleAi.Providers.Chat;
8+
9+
public class GeminiChatCompletionProvider : IChatCompletion
10+
{
11+
private readonly IServiceProvider _services;
12+
private readonly ILogger<GeminiChatCompletionProvider> _logger;
13+
14+
private string _model;
15+
16+
public string Provider => "google-gemini";
17+
18+
public GeminiChatCompletionProvider(
19+
IServiceProvider services,
20+
ILogger<GeminiChatCompletionProvider> logger)
21+
{
22+
_services = services;
23+
_logger = logger;
24+
}
25+
26+
public async Task<RoleDialogModel> GetChatCompletions(Agent agent, List<RoleDialogModel> conversations)
27+
{
28+
var contentHooks = _services.GetServices<IContentGeneratingHook>().ToList();
29+
30+
// Before chat completion hook
31+
foreach (var hook in contentHooks)
32+
{
33+
await hook.BeforeGenerating(agent, conversations);
34+
}
35+
36+
var client = ProviderHelper.GetGeminiClient(_services);
37+
var aiModel = client.GenerativeModel(_model);
38+
var (prompt, request) = PrepareOptions(aiModel, agent, conversations);
39+
40+
var response = await aiModel.GenerateContent(request);
41+
var candidate = response.Candidates.First();
42+
var part = candidate.Content?.Parts?.FirstOrDefault();
43+
var text = part?.Text ?? string.Empty;
44+
45+
RoleDialogModel responseMessage;
46+
if (part?.FunctionCall != null)
47+
{
48+
responseMessage = new RoleDialogModel(AgentRole.Function, text)
49+
{
50+
CurrentAgentId = agent.Id,
51+
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
52+
ToolCallId = part.FunctionCall.Name,
53+
FunctionName = part.FunctionCall.Name,
54+
FunctionArgs = part.FunctionCall.Args?.ToString()
55+
};
56+
}
57+
else
58+
{
59+
responseMessage = new RoleDialogModel(AgentRole.Assistant, text)
60+
{
61+
CurrentAgentId = agent.Id,
62+
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
63+
};
64+
}
65+
66+
// After chat completion hook
67+
foreach (var hook in contentHooks)
68+
{
69+
await hook.AfterGenerated(responseMessage, new TokenStatsModel
70+
{
71+
Prompt = prompt,
72+
Provider = Provider,
73+
Model = _model
74+
});
75+
}
76+
77+
return responseMessage;
78+
}
79+
80+
public Task<bool> GetChatCompletionsAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived, Func<RoleDialogModel, Task> onFunctionExecuting)
81+
{
82+
throw new NotImplementedException();
83+
}
84+
85+
public Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
86+
{
87+
throw new NotImplementedException();
88+
}
89+
90+
public void SetModelName(string model)
91+
{
92+
_model = model;
93+
}
94+
95+
private (string, GenerateContentRequest) PrepareOptions(GenerativeModel aiModel, Agent agent, List<RoleDialogModel> conversations)
96+
{
97+
var agentService = _services.GetRequiredService<IAgentService>();
98+
var googleSettings = _services.GetRequiredService<GoogleAiSettings>();
99+
100+
// Add settings
101+
aiModel.UseGoogleSearch = googleSettings.Gemini.UseGoogleSearch;
102+
aiModel.UseGrounding = googleSettings.Gemini.UseGrounding;
103+
104+
// Assembly messages
105+
var prompt = string.Empty;
106+
var contents = new List<Content>();
107+
var tools = new List<Tool>();
108+
var funcDeclarations = new List<FunctionDeclaration>();
109+
110+
if (!string.IsNullOrEmpty(agent.Instruction))
111+
{
112+
var instruction = agentService.RenderedInstruction(agent);
113+
contents.Add(new Content(instruction)
114+
{
115+
Role = AgentRole.User
116+
});
117+
118+
prompt += $"{instruction}\r\n";
119+
}
120+
121+
prompt += "\r\n[FUNCTIONS]\r\n";
122+
foreach (var function in agent.Functions)
123+
{
124+
if (!agentService.RenderFunction(agent, function)) continue;
125+
126+
var def = agentService.RenderFunctionProperty(agent, function);
127+
128+
funcDeclarations.Add(new FunctionDeclaration
129+
{
130+
Name = function.Name,
131+
Description = function.Description,
132+
Parameters = new()
133+
{
134+
Type = ParameterType.Object,
135+
Properties = def.Properties,
136+
Required = def.Required
137+
}
138+
});
139+
140+
prompt += $"{function.Name}: {function.Description} {def}\r\n\r\n";
141+
}
142+
143+
if (!funcDeclarations.IsNullOrEmpty())
144+
{
145+
tools.Add(new Tool { FunctionDeclarations = funcDeclarations });
146+
}
147+
148+
prompt += "\r\n[CONVERSATIONS]\r\n";
149+
foreach (var message in conversations)
150+
{
151+
if (message.Role == AgentRole.Function)
152+
{
153+
contents.Add(new Content(message.Content)
154+
{
155+
Role = AgentRole.Function,
156+
Parts = new()
157+
{
158+
new FunctionCall
159+
{
160+
Name = message.FunctionName,
161+
Args = JsonSerializer.Deserialize<object>(message.FunctionArgs ?? "{}")
162+
}
163+
}
164+
});
165+
166+
prompt += $"{AgentRole.Assistant}: Call function {message.FunctionName}({message.FunctionArgs})\r\n";
167+
}
168+
else if (message.Role == AgentRole.User)
169+
{
170+
var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content;
171+
contents.Add(new Content(text)
172+
{
173+
Role = AgentRole.User
174+
});
175+
prompt += $"{AgentRole.User}: {text}\r\n";
176+
}
177+
else if (message.Role == AgentRole.Assistant)
178+
{
179+
contents.Add(new Content(message.Content)
180+
{
181+
Role = AgentRole.Model
182+
});
183+
prompt += $"{AgentRole.Assistant}: {message.Content}\r\n";
184+
}
185+
}
186+
187+
var request = new GenerateContentRequest
188+
{
189+
Contents = contents,
190+
Tools = tools
191+
};
192+
return (prompt, request);
193+
}
194+
}

src/Plugins/BotSharp.Plugin.GoogleAI/Providers/ChatCompletionProvider.cs renamed to src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,44 @@
33
using BotSharp.Abstraction.Loggers;
44
using BotSharp.Abstraction.Functions.Models;
55
using BotSharp.Abstraction.Routing;
6-
using BotSharp.Plugin.GoogleAI.Settings;
76
using LLMSharp.Google.Palm;
8-
using Microsoft.Extensions.Logging;
97
using LLMSharp.Google.Palm.DiscussService;
8+
using Microsoft.Extensions.Logging;
109

11-
namespace BotSharp.Plugin.GoogleAI.Providers;
10+
namespace BotSharp.Plugin.GoogleAi.Providers.Chat;
1211

13-
public class ChatCompletionProvider : IChatCompletion
12+
public class PalmChatCompletionProvider : IChatCompletion
1413
{
15-
public string Provider => "google-ai";
1614
private readonly IServiceProvider _services;
17-
private readonly GoogleAiSettings _settings;
18-
private readonly ILogger _logger;
15+
private readonly ILogger<PalmChatCompletionProvider> _logger;
16+
1917
private string _model;
2018

21-
public ChatCompletionProvider(IServiceProvider services,
22-
GoogleAiSettings settings,
23-
ILogger<ChatCompletionProvider> logger)
19+
public string Provider => "google-ai";
20+
21+
public PalmChatCompletionProvider(
22+
IServiceProvider services,
23+
ILogger<PalmChatCompletionProvider> logger)
2424
{
2525
_services = services;
26-
_settings = settings;
2726
_logger = logger;
2827
}
2928

3029
public async Task<RoleDialogModel> GetChatCompletions(Agent agent, List<RoleDialogModel> conversations)
3130
{
32-
var hooks = _services.GetServices<IContentGeneratingHook>().ToList();
31+
var contentHooks = _services.GetServices<IContentGeneratingHook>().ToList();
3332

3433
// Before chat completion hook
35-
Task.WaitAll(hooks.Select(hook =>
36-
hook.BeforeGenerating(agent, conversations)).ToArray());
37-
38-
var client = new GooglePalmClient(apiKey: _settings.PaLM.ApiKey);
34+
foreach (var hook in contentHooks)
35+
{
36+
await hook.BeforeGenerating(agent, conversations);
37+
}
3938

39+
var client = ProviderHelper.GetPalmClient(_services);
4040
var (prompt, messages, hasFunctions) = PrepareOptions(agent, conversations);
4141

4242
RoleDialogModel msg;
43-
43+
4444
if (hasFunctions)
4545
{
4646
// use text completion
@@ -80,12 +80,15 @@ public async Task<RoleDialogModel> GetChatCompletions(Agent agent, List<RoleDial
8080
}
8181

8282
// After chat completion hook
83-
Task.WaitAll(hooks.Select(hook =>
84-
hook.AfterGenerated(msg, new TokenStatsModel
83+
foreach (var hook in contentHooks)
84+
{
85+
await hook.AfterGenerated(msg, new TokenStatsModel
8586
{
8687
Prompt = prompt,
88+
Provider = Provider,
8789
Model = _model
88-
})).ToArray());
90+
});
91+
}
8992

9093
return msg;
9194
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using LLMSharp.Google.Palm;
2+
using Mscc.GenerativeAI;
3+
4+
namespace BotSharp.Plugin.GoogleAi.Providers;
5+
6+
public static class ProviderHelper
7+
{
8+
public static GoogleAI GetGeminiClient(IServiceProvider services)
9+
{
10+
var settings = services.GetRequiredService<GoogleAiSettings>();
11+
var client = new GoogleAI(settings.Gemini.ApiKey);
12+
return client;
13+
}
14+
15+
public static GooglePalmClient GetPalmClient(IServiceProvider services)
16+
{
17+
var settings = services.GetRequiredService<GoogleAiSettings>();
18+
var client = new GooglePalmClient(settings.PaLM.ApiKey);
19+
return client;
20+
}
21+
}

0 commit comments

Comments
 (0)