BotSharp/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs

691 lines
23 KiB
C#
Raw Normal View History

2025-05-13 16:45:19 +00:00
using BotSharp.Abstraction.Options;
using BotSharp.Abstraction.Realtime.Models.Session;
using BotSharp.Core.Session;
2025-05-13 20:43:08 +00:00
using BotSharp.Plugin.GoogleAI.Models.Realtime;
using GenerativeAI;
using GenerativeAI.Core;
using GenerativeAI.Live;
using GenerativeAI.Live.Extensions;
using GenerativeAI.Types;
2025-05-13 16:45:19 +00:00
using GenerativeAI.Types.Converters;
using Google.Ai.Generativelanguage.V1Beta2;
using Google.Api;
2025-05-13 20:43:08 +00:00
using System;
2025-05-13 16:45:19 +00:00
using System.Threading;
2025-04-05 20:22:06 +00:00
namespace BotSharp.Plugin.GoogleAi.Providers.Realtime;
public class GoogleRealTimeProvider : IRealTimeCompletion
{
2025-04-05 20:22:06 +00:00
public string Provider => "google-ai";
public string Model => _model;
2025-04-25 14:58:04 +00:00
private string _model = GoogleAIModels.Gemini2FlashExp;
2025-04-05 20:22:06 +00:00
private MultiModalLiveClient _client;
private GenerativeModel _chatClient;
private readonly IServiceProvider _services;
private readonly ILogger _logger;
private List<string> renderedInstructions = [];
2025-05-13 16:45:19 +00:00
private LlmRealtimeSession _session;
private readonly BotSharpOptions _botsharpOptions;
2025-04-05 20:22:06 +00:00
private readonly GoogleAiSettings _settings;
2025-05-13 20:43:08 +00:00
private const string DEFAULT_MIME_TYPE = "audio/pcm;rate=16000";
private readonly JsonSerializerOptions _jsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
Converters = { new JsonStringEnumConverter(), new DateOnlyJsonConverter(), new TimeOnlyJsonConverter() },
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
UnknownTypeHandling = JsonUnknownTypeHandling.JsonElement
};
2025-04-05 20:22:06 +00:00
public GoogleRealTimeProvider(
IServiceProvider services,
GoogleAiSettings settings,
2025-05-13 16:45:19 +00:00
BotSharpOptions botSharpOptions,
2025-04-05 20:22:06 +00:00
ILogger<GoogleRealTimeProvider> logger)
{
2025-04-05 20:22:06 +00:00
_settings = settings;
2025-05-13 16:45:19 +00:00
_botsharpOptions = botSharpOptions;
2025-04-05 20:22:06 +00:00
_services = services;
_logger = logger;
}
2025-04-05 20:22:06 +00:00
public void SetModelName(string model)
{
_model = model;
}
2025-04-25 14:58:04 +00:00
private RealtimeHubConnection _conn;
2025-05-13 20:43:08 +00:00
private Func<Task> _onModelReady;
private Func<string, string, Task> _onModelAudioDeltaReceived;
private Func<Task> _onModelAudioResponseDone;
private Func<string, Task> _onModelAudioTranscriptDone;
private Func<List<RoleDialogModel>, Task> _onModelResponseDone;
private Func<string, Task> _onConversationItemCreated;
private Func<RoleDialogModel, Task> _onInputAudioTranscriptionDone;
private Func<Task> _onUserInterrupted;
2025-04-25 14:58:04 +00:00
2025-04-05 20:22:06 +00:00
2025-05-13 20:43:08 +00:00
public async Task Connect(
RealtimeHubConnection conn,
Func<Task> onModelReady,
Func<string, string, Task> onModelAudioDeltaReceived,
Func<Task> onModelAudioResponseDone,
Func<string, Task> onModelAudioTranscriptDone,
Func<List<RoleDialogModel>, Task> onModelResponseDone,
Func<string, Task> onConversationItemCreated,
Func<RoleDialogModel, Task> onInputAudioTranscriptionDone,
Func<Task> onInterruptionDetected)
2025-04-05 20:22:06 +00:00
{
2025-04-25 14:58:04 +00:00
_conn = conn;
_onModelReady = onModelReady;
_onModelAudioDeltaReceived = onModelAudioDeltaReceived;
_onModelAudioResponseDone = onModelAudioResponseDone;
_onModelAudioTranscriptDone = onModelAudioTranscriptDone;
_onModelResponseDone = onModelResponseDone;
_onConversationItemCreated = onConversationItemCreated;
2025-05-13 20:43:08 +00:00
_onInputAudioTranscriptionDone = onInputAudioTranscriptionDone;
_onUserInterrupted = onInterruptionDetected;
2025-04-05 20:22:06 +00:00
2025-05-13 16:45:19 +00:00
var settingsService = _services.GetRequiredService<ILlmProviderService>();
var realtimeModelSettings = _services.GetRequiredService<RealtimeModelSettings>();
2025-05-13 16:45:19 +00:00
_model = realtimeModelSettings.Model;
2025-05-13 16:45:19 +00:00
var modelSettings = settingsService.GetSetting(Provider, _model);
2025-05-13 20:43:08 +00:00
if (_session != null)
{
_session.Dispose();
}
2025-05-13 16:45:19 +00:00
2025-05-13 20:43:08 +00:00
_session = new LlmRealtimeSession(_services, new ChatSessionOptions
{
JsonOptions = _jsonOptions
});
2025-05-13 16:45:19 +00:00
2025-05-13 20:43:08 +00:00
await _session.ConnectAsync(
uri: new Uri($"wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent?key={modelSettings.ApiKey}"),
cancellationToken: CancellationToken.None);
2025-05-13 16:45:19 +00:00
2025-05-13 20:43:08 +00:00
await onModelReady();
2025-05-13 16:45:19 +00:00
2025-05-13 20:43:08 +00:00
_ = ReceiveMessage(
conn,
onModelReady,
onModelAudioDeltaReceived,
onModelAudioResponseDone,
onModelAudioTranscriptDone,
onModelResponseDone,
onConversationItemCreated,
onInputAudioTranscriptionDone,
onInterruptionDetected);
2025-05-13 16:45:19 +00:00
2025-04-05 20:22:06 +00:00
2025-05-13 20:43:08 +00:00
//var client = ProviderHelper.GetGeminiClient(Provider, _model, _services);
//_chatClient = client.CreateGenerativeModel(_model);
//_client = _chatClient.CreateMultiModalLiveClient(
// config: new GenerationConfig
// {
// ResponseModalities = [Modality.AUDIO],
// },
// systemInstruction: "You are a helpful assistant.",
// logger: _logger);
2025-05-13 20:43:08 +00:00
//await AttachEvents(_client);
2025-05-13 20:43:08 +00:00
//await _client.ConnectAsync(false);
2025-04-05 20:22:06 +00:00
}
2025-05-13 16:45:19 +00:00
private async Task ReceiveMessage(
RealtimeHubConnection conn,
2025-05-13 20:43:08 +00:00
Func<Task> onModelReady,
Func<string, string, Task> onModelAudioDeltaReceived,
Func<Task> onModelAudioResponseDone,
Func<string, Task> onModelAudioTranscriptDone,
Func<List<RoleDialogModel>, Task> onModelResponseDone,
Func<string, Task> onConversationItemCreated,
Func<RoleDialogModel, Task> onInputAudioTranscriptionCompleted,
Func<Task> onInterruptionDetected)
2025-05-13 16:45:19 +00:00
{
await foreach (ChatSessionUpdate update in _session.ReceiveUpdatesAsync(CancellationToken.None))
{
var receivedText = update?.RawResponse;
if (string.IsNullOrEmpty(receivedText))
{
continue;
}
2025-05-13 20:43:08 +00:00
Console.WriteLine($"Received text: {receivedText}");
try
{
var response = JsonSerializer.Deserialize<RealtimeServerResponse>(receivedText, _jsonOptions);
if (response == null)
{
continue;
}
if (response.SetupComplete != null)
{
_logger.LogInformation($"Session setup completed.");
}
else if (response.ServerContent != null)
{
if (response.ServerContent.ModelTurn != null)
{
_logger.LogInformation($"Model audio delta received.");
var parts = response.ServerContent.ModelTurn.Parts;
if (!parts.IsNullOrEmpty())
{
foreach (var part in parts)
{
if (!string.IsNullOrEmpty(part.InlineData?.Data))
{
await onModelAudioDeltaReceived(part.InlineData.Data, string.Empty);
}
}
}
}
else if (response.ServerContent.GenerationComplete == true)
{
_logger.LogInformation($"Model generation completed.");
}
else if (response.ServerContent.TurnComplete == true)
{
_logger.LogInformation($"Model turn completed.");
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error when deserializing server response.");
continue;
}
2025-05-13 16:45:19 +00:00
}
_session.Dispose();
}
2025-04-05 20:22:06 +00:00
public async Task Disconnect()
{
2025-05-13 20:43:08 +00:00
if (_session != null)
2025-05-13 16:45:19 +00:00
{
2025-05-13 20:43:08 +00:00
await _session.Disconnect();
2025-05-13 16:45:19 +00:00
}
2025-05-13 20:43:08 +00:00
//if (_client != null)
//{
// await _client.DisconnectAsync();
//}
2025-04-05 20:22:06 +00:00
}
2025-04-05 20:22:06 +00:00
public async Task AppenAudioBuffer(string message)
{
2025-05-13 20:43:08 +00:00
//await _client.SendAudioAsync(Convert.FromBase64String(message));
2025-05-13 16:45:19 +00:00
2025-05-13 20:43:08 +00:00
await SendEventToModel(new BidiClientPayload
{
RealtimeInput = new()
{
MediaChunks = [new() { Data = message, MimeType = DEFAULT_MIME_TYPE }]
}
});
2025-04-05 20:22:06 +00:00
}
2025-04-07 04:15:27 +00:00
public async Task AppenAudioBuffer(ArraySegment<byte> data, int length)
{
var buffer = data.AsSpan(0, length).ToArray();
2025-05-13 20:43:08 +00:00
//await _client.SendAudioAsync(buffer, "audio/pcm;rate=16000");
2025-05-13 16:45:19 +00:00
2025-05-13 20:43:08 +00:00
await SendEventToModel(new BidiClientPayload
{
RealtimeInput = new()
{
MediaChunks = [new() { Data = Convert.ToBase64String(buffer), MimeType = DEFAULT_MIME_TYPE }]
}
});
2025-04-07 04:15:27 +00:00
}
2025-04-05 20:22:06 +00:00
public async Task TriggerModelInference(string? instructions = null)
{
2025-05-13 16:45:19 +00:00
var content = !string.IsNullOrWhiteSpace(instructions)
? new Content(instructions, AgentRole.User)
: null;
2025-05-13 20:43:08 +00:00
//await _client.SendClientContentAsync(new BidiGenerateContentClientContent()
2025-05-13 16:45:19 +00:00
//{
2025-05-13 20:43:08 +00:00
// Turns = content != null ? [content] : null,
// TurnComplete = true,
2025-05-13 16:45:19 +00:00
//});
2025-05-13 20:43:08 +00:00
await SendEventToModel(new BidiClientPayload
{
ClientContent = new()
{
Turns = content != null ? [content] : null,
TurnComplete = true
}
});
2025-04-05 20:22:06 +00:00
}
2025-04-05 20:22:06 +00:00
public async Task CancelModelResponse()
{
2025-05-13 16:45:19 +00:00
2025-04-05 20:22:06 +00:00
}
2025-04-05 20:22:06 +00:00
public async Task RemoveConversationItem(string itemId)
{
2025-05-13 16:45:19 +00:00
2025-04-05 20:22:06 +00:00
}
2025-04-05 20:22:06 +00:00
private Task AttachEvents(MultiModalLiveClient client)
{
client.Connected += (sender, e) =>
{
2025-04-14 16:15:14 +00:00
_logger.LogInformation("Google Realtime Client connected.");
2025-04-25 14:58:04 +00:00
_onModelReady();
2025-04-05 20:22:06 +00:00
};
2025-04-05 20:22:06 +00:00
client.Disconnected += (sender, e) =>
{
2025-04-14 16:15:14 +00:00
_logger.LogInformation("Google Realtime Client disconnected.");
2025-04-05 20:22:06 +00:00
};
2025-04-05 20:22:06 +00:00
client.MessageReceived += async (sender, e) =>
{
2025-04-14 16:15:14 +00:00
_logger.LogInformation("User message received.");
2025-04-05 20:22:06 +00:00
if (e.Payload.SetupComplete != null)
{
2025-04-25 14:58:04 +00:00
_onConversationItemCreated(_client.ConnectionId.ToString());
}
2025-04-05 20:22:06 +00:00
if (e.Payload.ServerContent != null)
{
2025-04-05 20:22:06 +00:00
if (e.Payload.ServerContent.TurnComplete == true)
{
2025-04-25 14:58:04 +00:00
var responseDone = await ResponseDone(_conn, e.Payload.ServerContent);
_onModelResponseDone(responseDone);
2025-04-05 20:22:06 +00:00
}
}
2025-04-05 20:22:06 +00:00
};
2025-04-05 20:22:06 +00:00
client.AudioChunkReceived += (sender, e) =>
{
2025-04-25 14:58:04 +00:00
_onModelAudioDeltaReceived(Convert.ToBase64String(e.Buffer), Guid.NewGuid().ToString());
2025-04-05 20:22:06 +00:00
};
2025-04-05 20:22:06 +00:00
client.TextChunkReceived += (sender, e) =>
{
2025-05-13 20:43:08 +00:00
_onInputAudioTranscriptionDone(new RoleDialogModel(AgentRole.Assistant, e.Text));
2025-04-05 20:22:06 +00:00
};
2025-04-05 20:22:06 +00:00
client.GenerationInterrupted += (sender, e) =>
2025-04-14 16:15:14 +00:00
{
_logger.LogInformation("Audio generation interrupted.");
2025-04-25 14:58:04 +00:00
_onUserInterrupted();
2025-04-05 20:22:06 +00:00
};
2025-04-05 20:22:06 +00:00
client.AudioReceiveCompleted += (sender, e) =>
2025-04-14 16:15:14 +00:00
{
_logger.LogInformation("Audio receive completed.");
2025-04-25 14:58:04 +00:00
_onModelAudioResponseDone();
2025-04-05 20:22:06 +00:00
};
2025-04-05 20:22:06 +00:00
client.ErrorOccurred += (sender, e) =>
{
var ex = e.GetException();
_logger.LogError(ex, "Error occurred in Google Realtime Client");
};
return Task.CompletedTask;
}
2025-04-05 20:22:06 +00:00
private async Task<List<RoleDialogModel>> ResponseDone(RealtimeHubConnection conn,
BidiGenerateContentServerContent serverContent)
{
var outputs = new List<RoleDialogModel>();
2025-04-05 20:22:06 +00:00
var parts = serverContent.ModelTurn?.Parts;
if (parts != null)
{
foreach (var part in parts)
{
2025-04-05 20:22:06 +00:00
var call = part.FunctionCall;
if (call != null)
{
var item = new RoleDialogModel(AgentRole.Assistant, part.Text)
{
CurrentAgentId = conn.CurrentAgentId,
MessageId = call.Id ?? String.Empty,
MessageType = MessageTypeName.FunctionCall
};
outputs.Add(item);
}
else
{
2025-04-05 20:22:06 +00:00
var item = new RoleDialogModel(AgentRole.Assistant, call.Args?.ToJsonString() ?? string.Empty)
{
CurrentAgentId = conn.CurrentAgentId,
FunctionName = call.Name,
FunctionArgs = call.Args?.ToJsonString() ?? string.Empty,
ToolCallId = call.Id ?? String.Empty,
MessageId = call.Id ?? String.Empty,
MessageType = MessageTypeName.FunctionCall
};
outputs.Add(item);
}
}
2025-04-05 20:22:06 +00:00
}
2025-04-05 20:22:06 +00:00
var contentHooks = _services.GetServices<IContentGeneratingHook>().ToList();
// After chat completion hook
foreach (var hook in contentHooks)
{
await hook.AfterGenerated(new RoleDialogModel(AgentRole.Assistant, "response.done")
2025-04-04 02:20:45 +00:00
{
2025-04-05 20:22:06 +00:00
CurrentAgentId = conn.CurrentAgentId
}, new TokenStatsModel
{
2025-04-05 20:22:06 +00:00
Provider = Provider,
Model = _model,
2025-04-05 20:22:06 +00:00
});
}
2025-04-05 20:22:06 +00:00
return outputs;
}
2025-04-05 20:22:06 +00:00
public async Task SendEventToModel(object message)
{
//todo Send Audio Chunks to Model, Botsharp RealTime Implementation seems to be incomplete
2025-05-13 16:45:19 +00:00
2025-05-13 20:43:08 +00:00
if (_session == null) return;
2025-05-13 16:45:19 +00:00
2025-05-13 20:43:08 +00:00
await _session.SendEventToModel(message);
2025-04-05 20:22:06 +00:00
}
2025-04-28 22:23:25 +00:00
public async Task<string> UpdateSession(RealtimeHubConnection conn, bool isInit = false)
2025-04-05 20:22:06 +00:00
{
var convService = _services.GetRequiredService<IConversationService>();
var conv = await convService.GetConversation(conn.ConversationId);
2025-04-05 20:22:06 +00:00
var agentService = _services.GetRequiredService<IAgentService>();
var agent = await agentService.LoadAgent(conn.CurrentAgentId);
2025-05-13 16:45:19 +00:00
var (prompt, request) = PrepareOptions(agent, []);
2025-04-05 20:22:06 +00:00
var config = request.GenerationConfig;
//Output Modality can either be text or audio
if (config != null)
{
2025-05-13 16:45:19 +00:00
config.ResponseModalities = [Modality.AUDIO];
2025-04-05 20:22:06 +00:00
var words = new List<string>();
HookEmitter.Emit<IRealtimeHook>(_services, hook => words.AddRange(hook.OnModelTranscriptPrompt(agent)));
2025-04-05 20:22:06 +00:00
var realtimeModelSettings = _services.GetRequiredService<RealtimeModelSettings>();
config.Temperature = Math.Max(realtimeModelSettings.Temperature, 0.6f);
config.MaxOutputTokens = realtimeModelSettings.MaxResponseOutputTokens;
}
2025-04-05 20:22:06 +00:00
2025-04-05 20:22:06 +00:00
var functions = request.Tools?.SelectMany(s => s.FunctionDeclarations).Select(x =>
{
2025-04-05 20:22:06 +00:00
var fn = new FunctionDef
{
2025-04-05 20:22:06 +00:00
Name = x.Name ?? string.Empty,
Description = x.Description ?? string.Empty,
2025-05-13 16:45:19 +00:00
Parameters = x.Parameters != null
? JsonSerializer.Deserialize<FunctionParametersDef>(JsonSerializer.Serialize(x.Parameters))
: null
2025-04-05 20:22:06 +00:00
};
return fn;
}).ToArray();
await HookEmitter.Emit<IContentGeneratingHook>(_services,
2025-04-28 22:23:25 +00:00
async hook => { await hook.OnSessionUpdated(agent, prompt, functions, isInit); });
2025-04-05 20:22:06 +00:00
if (_settings.Gemini.UseGoogleSearch)
{
2025-05-13 16:45:19 +00:00
request.Tools ??= [];
2025-04-05 20:22:06 +00:00
request.Tools.Add(new Tool()
{
2025-04-05 20:22:06 +00:00
GoogleSearch = new GoogleSearchTool()
});
}
2025-05-13 20:43:08 +00:00
//await _client.SendSetupAsync(new BidiGenerateContentSetup()
2025-05-13 16:45:19 +00:00
//{
2025-05-13 20:43:08 +00:00
// GenerationConfig = config,
// Model = Model.ToModelId(),
// SystemInstruction = request.SystemInstruction,
// //Tools = request.Tools?.ToArray(),
2025-05-13 16:45:19 +00:00
//});
2025-05-13 20:43:08 +00:00
await SendEventToModel(new BidiClientPayload
{
Setup = new BidiGenerateContentSetup()
{
GenerationConfig = config,
Model = Model.ToModelId(),
SystemInstruction = request.SystemInstruction,
Tools = []
}
});
2025-04-05 20:22:06 +00:00
return prompt;
}
public async Task InsertConversationItem(RoleDialogModel message)
{
2025-05-13 16:45:19 +00:00
//if (_client == null)
// throw new Exception("Client is not initialized");
2025-04-05 20:22:06 +00:00
if (message.Role == AgentRole.Function)
{
var function = new FunctionResponse()
{
2025-04-05 20:22:06 +00:00
Name = message.FunctionName ?? string.Empty,
Response = JsonNode.Parse(message.Content ?? "{}")
};
2025-05-13 20:43:08 +00:00
//await _client.SendToolResponseAsync(new BidiGenerateContentToolResponse()
2025-05-13 16:45:19 +00:00
//{
2025-05-13 20:43:08 +00:00
// FunctionResponses = [function]
2025-05-13 16:45:19 +00:00
//});
2025-05-13 20:43:08 +00:00
await SendEventToModel(new BidiClientPayload
{
ToolResponse = new()
{
FunctionResponses = [function]
}
});
}
2025-04-05 20:22:06 +00:00
else if (message.Role == AgentRole.Assistant)
{
2025-05-13 20:43:08 +00:00
await SendEventToModel(new BidiClientPayload
{
ClientContent = new()
{
Turns = [new Content(message.Content, AgentRole.Model)],
TurnComplete = true
}
});
2025-04-05 20:22:06 +00:00
}
else if (message.Role == AgentRole.User)
{
2025-05-13 20:43:08 +00:00
//await _client.SentTextAsync(message.Content);
2025-05-13 16:45:19 +00:00
2025-05-13 20:43:08 +00:00
await SendEventToModel(new BidiClientPayload
{
ClientContent = new()
{
Turns = [new Content(message.Content, AgentRole.User)],
TurnComplete = true
}
});
2025-04-05 20:22:06 +00:00
}
else
{
throw new NotImplementedException("");
}
2025-04-05 20:22:06 +00:00
}
2025-05-13 16:45:19 +00:00
public async Task<List<RoleDialogModel>> OnResponsedDone(RealtimeHubConnection conn, string response)
2025-04-05 20:22:06 +00:00
{
2025-05-13 16:45:19 +00:00
return [];
2025-04-05 20:22:06 +00:00
}
2025-05-13 16:45:19 +00:00
public async Task<RoleDialogModel> OnConversationItemCreated(RealtimeHubConnection conn, string response)
2025-04-05 20:22:06 +00:00
{
2025-05-13 16:45:19 +00:00
return await Task.FromResult(new RoleDialogModel(AgentRole.User, response));
2025-04-05 20:22:06 +00:00
}
2025-05-13 16:45:19 +00:00
private (string, GenerateContentRequest) PrepareOptions(Agent agent,
2025-04-05 20:22:06 +00:00
List<RoleDialogModel> conversations)
{
var agentService = _services.GetRequiredService<IAgentService>();
var googleSettings = _settings;
renderedInstructions = [];
2025-04-05 20:22:06 +00:00
// Assembly messages
var contents = new List<Content>();
var tools = new List<Tool>();
var funcDeclarations = new List<FunctionDeclaration>();
2025-04-05 20:22:06 +00:00
var systemPrompts = new List<string>();
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
{
var instruction = agentService.RenderedInstruction(agent);
renderedInstructions.Add(instruction);
systemPrompts.Add(instruction);
}
2025-04-05 20:22:06 +00:00
var funcPrompts = new List<string>();
var functions = agent.Functions.Concat(agent.SecondaryFunctions ?? []);
foreach (var function in functions)
{
if (!agentService.RenderFunction(agent, function)) continue;
2025-04-05 20:22:06 +00:00
var def = agentService.RenderFunctionProperty(agent, function);
var props = JsonSerializer.Serialize(def?.Properties);
var parameters = !string.IsNullOrWhiteSpace(props) && props != "{}"
? new Schema()
{
2025-04-05 20:22:06 +00:00
Type = "object",
Properties = JsonSerializer.Deserialize<Dictionary<string, Schema>>(props),
Required = def?.Required ?? []
}
: null;
2025-04-05 20:22:06 +00:00
funcDeclarations.Add(new FunctionDeclaration
{
2025-04-05 20:22:06 +00:00
Name = function.Name,
Description = function.Description,
Parameters = parameters
});
funcPrompts.Add($"{function.Name}: {function.Description} {def}");
}
if (!funcDeclarations.IsNullOrEmpty())
{
tools.Add(new Tool { FunctionDeclarations = funcDeclarations });
}
2025-04-05 20:22:06 +00:00
var convPrompts = new List<string>();
foreach (var message in conversations)
{
if (message.Role == AgentRole.Function)
{
2025-04-05 20:22:06 +00:00
contents.Add(new Content([
new Part()
{
FunctionCall = new FunctionCall
{
2025-04-05 20:22:06 +00:00
Name = message.FunctionName,
Args = JsonNode.Parse(message.FunctionArgs ?? "{}")
}
2025-04-05 20:22:06 +00:00
}
], AgentRole.Model));
2025-04-05 20:22:06 +00:00
contents.Add(new Content([
new Part()
{
FunctionResponse = new FunctionResponse
{
2025-04-05 20:22:06 +00:00
Name = message.FunctionName ?? string.Empty,
Response = new JsonObject()
{
2025-04-05 20:22:06 +00:00
["result"] = message.Content ?? string.Empty
}
}
2025-04-05 20:22:06 +00:00
}
], AgentRole.Function));
2025-04-05 20:22:06 +00:00
convPrompts.Add(
$"{AgentRole.Assistant}: Call function {message.FunctionName}({message.FunctionArgs}) => {message.Content}");
}
2025-04-05 20:22:06 +00:00
else if (message.Role == AgentRole.User)
{
2025-04-05 20:22:06 +00:00
var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content;
contents.Add(new Content(text, AgentRole.User));
convPrompts.Add($"{AgentRole.User}: {text}");
}
else if (message.Role == AgentRole.Assistant)
{
contents.Add(new Content(message.Content, AgentRole.Model));
convPrompts.Add($"{AgentRole.Assistant}: {message.Content}");
}
}
2025-04-05 20:22:06 +00:00
var state = _services.GetRequiredService<IConversationStateService>();
var temperature = float.Parse(state.GetState("temperature", "0.0"));
var maxTokens = int.TryParse(state.GetState("max_tokens"), out var tokens)
? tokens
: agent.LlmConfig?.MaxOutputTokens ?? LlmConstant.DEFAULT_MAX_OUTPUT_TOKEN;
2025-05-13 16:45:19 +00:00
2025-04-05 20:22:06 +00:00
var request = new GenerateContentRequest
{
2025-04-05 20:22:06 +00:00
SystemInstruction = !systemPrompts.IsNullOrEmpty()
? new Content(systemPrompts[0], AgentRole.System)
: null,
Contents = contents,
Tools = tools,
GenerationConfig = new()
{
2025-04-05 20:22:06 +00:00
Temperature = temperature,
MaxOutputTokens = maxTokens
}
2025-04-05 20:22:06 +00:00
};
2025-04-05 20:22:06 +00:00
var prompt = GetPrompt(systemPrompts, funcPrompts, convPrompts);
return (prompt, request);
}
private string GetPrompt(IEnumerable<string> systemPrompts, IEnumerable<string> funcPrompts,
IEnumerable<string> convPrompts)
{
string prompt = string.Join("\r\n\r\n", systemPrompts);
if (!funcPrompts.IsNullOrEmpty())
{
prompt += "\r\n\r\n[FUNCTIONS]\r\n";
prompt += string.Join("\r\n", funcPrompts);
}
2025-04-05 20:22:06 +00:00
if (!convPrompts.IsNullOrEmpty())
{
prompt += "\r\n\r\n[CONVERSATION]\r\n";
prompt += string.Join("\r\n", convPrompts);
}
2025-04-05 20:22:06 +00:00
return prompt;
}
}