2025-05-13 16:45:19 +00:00
using BotSharp.Abstraction.Options ;
using BotSharp.Abstraction.Realtime.Models.Session ;
using BotSharp.Core.Session ;
2025-04-03 23:02:34 +00:00
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 ;
using System.Threading ;
2025-04-03 23:02:34 +00:00
2025-04-05 20:22:06 +00:00
namespace BotSharp.Plugin.GoogleAi.Providers.Realtime ;
public class GoogleRealTimeProvider : IRealTimeCompletion
2025-04-03 23:02:34 +00:00
{
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 ;
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-03 23:02:34 +00:00
{
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-03 23:02:34 +00:00
2025-04-05 20:22:06 +00:00
public void SetModelName ( string model )
{
_model = model ;
}
2025-04-03 23:02:34 +00:00
2025-04-25 14:58:04 +00:00
private RealtimeHubConnection _conn ;
private Action _onModelReady ;
private Action < string , string > _onModelAudioDeltaReceived ;
private Action _onModelAudioResponseDone ;
private Action < string > _onModelAudioTranscriptDone ;
private Action < List < RoleDialogModel > > _onModelResponseDone ;
private Action < string > _onConversationItemCreated ;
private Action < RoleDialogModel > _onInputAudioTranscriptionCompleted ;
private Action _onUserInterrupted ;
2025-04-05 20:22:06 +00:00
public async Task Connect ( RealtimeHubConnection conn ,
Action onModelReady ,
Action < string , string > onModelAudioDeltaReceived ,
Action onModelAudioResponseDone ,
Action < string > onModelAudioTranscriptDone ,
Action < List < RoleDialogModel > > onModelResponseDone ,
Action < string > onConversationItemCreated ,
Action < RoleDialogModel > onInputAudioTranscriptionCompleted ,
Action onUserInterrupted )
{
2025-04-25 14:58:04 +00:00
_conn = conn ;
_onModelReady = onModelReady ;
_onModelAudioDeltaReceived = onModelAudioDeltaReceived ;
_onModelAudioResponseDone = onModelAudioResponseDone ;
_onModelAudioTranscriptDone = onModelAudioTranscriptDone ;
_onModelResponseDone = onModelResponseDone ;
_onConversationItemCreated = onConversationItemCreated ;
_onInputAudioTranscriptionCompleted = onInputAudioTranscriptionCompleted ;
_onUserInterrupted = onUserInterrupted ;
2025-04-05 20:22:06 +00:00
2025-05-13 16:45:19 +00:00
var settingsService = _services . GetRequiredService < ILlmProviderService > ( ) ;
2025-04-08 01:16:47 +00:00
var realtimeModelSettings = _services . GetRequiredService < RealtimeModelSettings > ( ) ;
2025-05-13 16:45:19 +00:00
2025-04-08 01:16:47 +00:00
_model = realtimeModelSettings . Model ;
2025-05-13 16:45:19 +00:00
var modelSettings = settingsService . GetSetting ( Provider , _model ) ;
//if (_session != null)
//{
// _session.Dispose();
//}
//_session = new LlmRealtimeSession(_services, new ChatSessionOptions
//{
// JsonOptions = new JsonSerializerOptions
// {
// PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
// PropertyNameCaseInsensitive = true,
// Converters = { new JsonStringEnumConverter(), new DateOnlyJsonConverter(), new TimeOnlyJsonConverter() },
// DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
// TypeInfoResolver = TypesSerializerContext.Default,
// UnknownTypeHandling = JsonUnknownTypeHandling.JsonElement,
// }
//});
//await _session.ConnectAsync(
// uri: new Uri($"wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent?key={modelSettings.ApiKey}"),
// cancellationToken: CancellationToken.None);
////await UpdateSession(conn, true);
//_ = ReceiveMessage(
// conn,
// onModelReady,
// onModelAudioDeltaReceived,
// onModelAudioResponseDone,
// onModelAudioTranscriptDone,
// onModelResponseDone,
// onConversationItemCreated,
// onInputAudioTranscriptionCompleted,
// onUserInterrupted);
2025-04-05 20:22:06 +00:00
var client = ProviderHelper . GetGeminiClient ( Provider , _model , _services ) ;
_chatClient = client . CreateGenerativeModel ( _model ) ;
_client = _chatClient . CreateMultiModalLiveClient (
config : new GenerationConfig
{
ResponseModalities = [ Modality . AUDIO ] ,
2025-05-13 16:45:19 +00:00
} ,
2025-04-05 20:22:06 +00:00
systemInstruction : "You are a helpful assistant." ,
logger : _logger ) ;
2025-04-03 23:02:34 +00:00
2025-04-05 20:22:06 +00:00
await AttachEvents ( _client ) ;
2025-04-03 23:02:34 +00:00
2025-04-14 05:24:05 +00:00
await _client . ConnectAsync ( false ) ;
2025-04-05 20:22:06 +00:00
}
2025-04-03 23:02:34 +00:00
2025-05-13 16:45:19 +00:00
private async Task ReceiveMessage (
RealtimeHubConnection conn ,
Action onModelReady ,
Action < string , string > onModelAudioDeltaReceived ,
Action onModelAudioResponseDone ,
Action < string > onModelAudioTranscriptDone ,
Action < List < RoleDialogModel > > onModelResponseDone ,
Action < string > onConversationItemCreated ,
Action < RoleDialogModel > onUserAudioTranscriptionCompleted ,
Action onInterruptionDetected )
{
await foreach ( ChatSessionUpdate update in _session . ReceiveUpdatesAsync ( CancellationToken . None ) )
{
var receivedText = update ? . RawResponse ;
Console . WriteLine ( $"Received text: {receivedText}" ) ;
if ( string . IsNullOrEmpty ( receivedText ) )
{
continue ;
}
}
_session . Dispose ( ) ;
}
2025-04-05 20:22:06 +00:00
public async Task Disconnect ( )
{
2025-05-13 16:45:19 +00:00
//if (_session != null)
//{
// await _session.Disconnect();
//}
2025-04-05 20:22:06 +00:00
if ( _client ! = null )
2025-05-13 16:45:19 +00:00
{
2025-04-05 20:22:06 +00:00
await _client . DisconnectAsync ( ) ;
2025-05-13 16:45:19 +00:00
}
2025-04-05 20:22:06 +00:00
}
2025-04-03 23:02:34 +00:00
2025-04-05 20:22:06 +00:00
public async Task AppenAudioBuffer ( string message )
{
await _client . SendAudioAsync ( Convert . FromBase64String ( message ) ) ;
2025-05-13 16:45:19 +00:00
//await SendEventToModel(new BidiClientPayload
//{
// RealtimeInput = new()
// {
// MediaChunks = [ new() { Data = message, MimeType = "audio/pcm; rate=16000" } ]
// }
//});
2025-04-05 20:22:06 +00:00
}
2025-04-03 23:02:34 +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 16:45:19 +00:00
await _client . SendAudioAsync ( buffer , "audio/pcm; rate=16000" ) ;
//await SendEventToModel(new BidiClientPayload
//{
// RealtimeInput = new()
// {
// MediaChunks = [new() { Data = Convert.ToBase64String(buffer), MimeType = "audio/pcm; rate=16000" }]
// }
//});
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-04-05 20:22:06 +00:00
await _client . SendClientContentAsync ( new BidiGenerateContentClientContent ( )
2025-04-03 23:02:34 +00:00
{
2025-05-13 16:45:19 +00:00
Turns = content ! = null ? [ content ] : null ,
2025-04-05 20:22:06 +00:00
TurnComplete = true ,
} ) ;
2025-05-13 16:45:19 +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-03 23:02:34 +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-03 23:02:34 +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-03 23:02:34 +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-03 23:02:34 +00:00
2025-04-05 20:22:06 +00:00
client . Disconnected + = ( sender , e ) = >
2025-04-03 23:02:34 +00:00
{
2025-04-14 16:15:14 +00:00
_logger . LogInformation ( "Google Realtime Client disconnected." ) ;
2025-04-05 20:22:06 +00:00
} ;
2025-04-04 01:18:14 +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-03 23:02:34 +00:00
{
2025-04-25 14:58:04 +00:00
_onConversationItemCreated ( _client . ConnectionId . ToString ( ) ) ;
2025-04-03 23:02:34 +00:00
}
2025-04-05 20:22:06 +00:00
if ( e . Payload . ServerContent ! = null )
2025-04-03 23:02:34 +00:00
{
2025-04-05 20:22:06 +00:00
if ( e . Payload . ServerContent . TurnComplete = = true )
2025-04-03 23:02:34 +00:00
{
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-03 23:02:34 +00:00
}
2025-04-05 20:22:06 +00:00
} ;
2025-04-04 01:18:14 +00:00
2025-04-05 20:22:06 +00:00
client . AudioChunkReceived + = ( sender , e ) = >
2025-04-03 23:02:34 +00:00
{
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-03 23:02:34 +00:00
2025-04-05 20:22:06 +00:00
client . TextChunkReceived + = ( sender , e ) = >
2025-04-03 23:02:34 +00:00
{
2025-04-25 14:58:04 +00:00
_onInputAudioTranscriptionCompleted ( new RoleDialogModel ( AgentRole . Assistant , e . Text ) ) ;
2025-04-05 20:22:06 +00:00
} ;
2025-04-03 23:02:34 +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-04 01:18:14 +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-03 23:02:34 +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-03 23:02:34 +00:00
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-04 01:18:14 +00:00
2025-04-05 20:22:06 +00:00
var parts = serverContent . ModelTurn ? . Parts ;
if ( parts ! = null )
{
foreach ( var part in parts )
2025-04-03 23:02:34 +00:00
{
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-03 23:02:34 +00:00
{
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-03 23:02:34 +00:00
}
2025-04-05 20:22:06 +00:00
}
2025-04-03 23:02:34 +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-03 23:02:34 +00:00
{
2025-04-05 20:22:06 +00:00
Provider = Provider ,
2025-04-03 23:02:34 +00:00
Model = _model ,
2025-04-05 20:22:06 +00:00
} ) ;
2025-04-03 23:02:34 +00:00
}
2025-04-05 20:22:06 +00:00
return outputs ;
}
2025-04-03 23:02:34 +00:00
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
//if (_session == null) return;
//await _session.SendEventToModel(message);
2025-04-05 20:22:06 +00:00
}
2025-04-03 23:02:34 +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-03 23:02:34 +00:00
2025-04-05 20:22:06 +00:00
var agentService = _services . GetRequiredService < IAgentService > ( ) ;
var agent = await agentService . LoadAgent ( conn . CurrentAgentId ) ;
2025-04-03 23:02:34 +00:00
2025-05-13 16:45:19 +00:00
var ( prompt , request ) = PrepareOptions ( agent , [ ] ) ;
2025-04-03 23:02:34 +00:00
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-04 01:18:14 +00:00
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-03 23:02:34 +00:00
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-03 23:02:34 +00:00
}
2025-04-05 20:22:06 +00:00
2025-04-03 23:02:34 +00:00
2025-04-05 20:22:06 +00:00
var functions = request . Tools ? . SelectMany ( s = > s . FunctionDeclarations ) . Select ( x = >
2025-04-03 23:02:34 +00:00
{
2025-04-05 20:22:06 +00:00
var fn = new FunctionDef
2025-04-03 23:02:34 +00:00
{
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-03 23:02:34 +00:00
{
2025-04-05 20:22:06 +00:00
GoogleSearch = new GoogleSearchTool ( )
} ) ;
}
await _client . SendSetupAsync ( new BidiGenerateContentSetup ( )
{
GenerationConfig = config ,
2025-04-14 05:24:05 +00:00
Model = Model . ToModelId ( ) ,
2025-04-05 20:22:06 +00:00
SystemInstruction = request . SystemInstruction ,
2025-05-13 16:45:19 +00:00
//Tools = request.Tools?.ToArray(),
2025-04-05 20:22:06 +00:00
} ) ;
2025-05-13 16:45:19 +00:00
//await SendEventToModel(new BidiClientPayload
//{
// Setup = new BidiGenerateContentSetup()
// {
// GenerationConfig = config,
// Model = $"models/{_model}",
// SystemInstruction = new Content(agent.Instruction, AgentRole.System),
// //Tools = request.Tools?.ToArray(),
// }
//});
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-03 23:02:34 +00:00
{
2025-04-05 20:22:06 +00:00
Name = message . FunctionName ? ? string . Empty ,
Response = JsonNode . Parse ( message . Content ? ? "{}" )
} ;
await _client . SendToolResponseAsync ( new BidiGenerateContentToolResponse ( )
2025-04-03 23:02:34 +00:00
{
2025-04-05 20:22:06 +00:00
FunctionResponses = [ function ]
} ) ;
2025-05-13 16:45:19 +00:00
//await SendEventToModel(new BidiClientPayload
//{
// ToolResponse = new()
// {
// FunctionResponses = [function]
// }
//});
2025-04-03 23:02:34 +00:00
}
2025-04-05 20:22:06 +00:00
else if ( message . Role = = AgentRole . Assistant )
{
2025-05-13 16:45:19 +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 )
{
await _client . SentTextAsync ( message . Content ) ;
2025-05-13 16:45:19 +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
2025-04-03 23:02:34 +00:00
{
throw new NotImplementedException ( "" ) ;
}
2025-04-05 20:22:06 +00:00
}
2025-04-03 23:02:34 +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-04-03 23:02:34 +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-04-03 23:02:34 +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-03 23:02:34 +00:00
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-03 23:02:34 +00:00
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-03 23:02:34 +00:00
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-03 23:02:34 +00:00
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-03 23:02:34 +00:00
{
2025-04-05 20:22:06 +00:00
Type = "object" ,
Properties = JsonSerializer . Deserialize < Dictionary < string , Schema > > ( props ) ,
Required = def ? . Required ? ? [ ]
}
: null ;
2025-04-03 23:02:34 +00:00
2025-04-05 20:22:06 +00:00
funcDeclarations . Add ( new FunctionDeclaration
2025-04-03 23:02:34 +00:00
{
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-03 23:02:34 +00:00
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-03 23:02:34 +00:00
{
2025-04-05 20:22:06 +00:00
contents . Add ( new Content ( [
new Part ( )
{
FunctionCall = new FunctionCall
2025-04-03 23:02:34 +00:00
{
2025-04-05 20:22:06 +00:00
Name = message . FunctionName ,
Args = JsonNode . Parse ( message . FunctionArgs ? ? "{}" )
2025-04-03 23:02:34 +00:00
}
2025-04-05 20:22:06 +00:00
}
] , AgentRole . Model ) ) ;
2025-04-03 23:02:34 +00:00
2025-04-05 20:22:06 +00:00
contents . Add ( new Content ( [
new Part ( )
{
FunctionResponse = new FunctionResponse
2025-04-03 23:02:34 +00:00
{
2025-04-05 20:22:06 +00:00
Name = message . FunctionName ? ? string . Empty ,
Response = new JsonObject ( )
2025-04-03 23:02:34 +00:00
{
2025-04-05 20:22:06 +00:00
["result"] = message . Content ? ? string . Empty
2025-04-03 23:02:34 +00:00
}
}
2025-04-05 20:22:06 +00:00
}
] , AgentRole . Function ) ) ;
2025-04-03 23:02:34 +00:00
2025-04-05 20:22:06 +00:00
convPrompts . Add (
$"{AgentRole.Assistant}: Call function {message.FunctionName}({message.FunctionArgs}) => {message.Content}" ) ;
2025-04-03 23:02:34 +00:00
}
2025-04-05 20:22:06 +00:00
else if ( message . Role = = AgentRole . User )
2025-04-03 23:02:34 +00:00
{
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-03 23:02:34 +00:00
}
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-03 23:02:34 +00:00
{
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-03 23:02:34 +00:00
{
2025-04-05 20:22:06 +00:00
Temperature = temperature ,
MaxOutputTokens = maxTokens
2025-04-03 23:02:34 +00:00
}
2025-04-05 20:22:06 +00:00
} ;
2025-04-03 23:02:34 +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-03 23:02:34 +00:00
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-03 23:02:34 +00:00
}
2025-04-05 20:22:06 +00:00
return prompt ;
2025-04-03 23:02:34 +00:00
}
}