Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 55 additions & 13 deletions firebaseai/src/LiveSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,29 +141,71 @@ public async Task SendAsync(
}

/// <summary>
/// Send realtime input to the server.
/// Sends realtime input (media chunks) to the server.
/// </summary>
/// <param name="mediaChunks">A list of media chunks to send.</param>
/// <param name="cancellationToken">A token to cancel the send operation.</param>
public async Task SendMediaChunksAsync(
/// <param name="mediaChunks">The list of media chunks to send.</param>
[Obsolete("Use SendAudio, SendVideo, or SendText instead")]
public Task SendMediaChunksAsync(
List<ModelContent.InlineDataPart> mediaChunks,
CancellationToken cancellationToken = default) {
if (mediaChunks == null) return;
if (mediaChunks == null) return Task.CompletedTask;

return SendRealtimeInputAsync(cancellationToken: cancellationToken);
}

/// <summary>
/// Sends audio data to the server.
/// </summary>
/// <param name="audio">The audio data to send.</param>
public Task SendAudioAsync(ModelContent.InlineDataPart audio, CancellationToken cancellationToken = default) {
return SendRealtimeInputAsync(audio: audio, cancellationToken: cancellationToken);
}

/// <summary>
/// Sends video data to the server.
/// </summary>
/// <param name="video">The video data to send.</param>
public Task SendVideoAsync(ModelContent.InlineDataPart video, CancellationToken cancellationToken = default) {
return SendRealtimeInputAsync(video: video, cancellationToken: cancellationToken);
}

/// <summary>
/// Sends text data to the server.
/// </summary>
/// <param name="text">The text data to send.</param>
public Task SendTextAsync(string text, CancellationToken cancellationToken = default) {
return SendRealtimeInputAsync(text: text, cancellationToken: cancellationToken);
}

private Task SendRealtimeInputAsync(
ModelContent.InlineDataPart? audio = null,
ModelContent.InlineDataPart? video = null,
string text = null,
CancellationToken cancellationToken = default) {
// Prepare the message payload.
Dictionary<string, object> jsonDict = new() {
{
"realtimeInput", new Dictionary<string, object>() {
{
// InlineDataPart inherits from Part, so this conversion should be safe.
"mediaChunks", mediaChunks.Select(mc => (mc as ModelContent.Part).ToJson()["inlineData"]).ToList()
}
}
"realtimeInput", new Dictionary<string, object>()
}
};

var realtimeInputDict = (Dictionary<string, object>)jsonDict["realtimeInput"];

if (audio.HasValue) {
realtimeInputDict["audio"] = (audio.Value as ModelContent.Part).ToJson()["inlineData"];
}

if (video.HasValue) {
realtimeInputDict["video"] = (video.Value as ModelContent.Part).ToJson()["inlineData"];
}

if (!string.IsNullOrEmpty(text)) {
realtimeInputDict["text"] = text;
}

var byteArray = Encoding.UTF8.GetBytes(Json.Serialize(jsonDict));

await InternalSendBytesAsync(new ArraySegment<byte>(byteArray), cancellationToken);
return InternalSendBytesAsync(new ArraySegment<byte>(byteArray), cancellationToken);
}

private static byte[] ConvertTo16BitPCM(float[] samples) {
Expand All @@ -189,7 +231,7 @@ private static byte[] ConvertTo16BitPCM(float[] samples) {
/// <param name="cancellationToken">A token to cancel the send operation.</param>
public Task SendAudioAsync(float[] audioData, CancellationToken cancellationToken = default) {
ModelContent.InlineDataPart inlineDataPart = new("audio/pcm", ConvertTo16BitPCM(audioData));
return SendMediaChunksAsync(new List<ModelContent.InlineDataPart>(new []{inlineDataPart}), cancellationToken);
return SendAudioAsync(inlineDataPart, cancellationToken);
}

/// <summary>
Expand Down