From a471531528110636bc7aab35f55e9c470bed75c5 Mon Sep 17 00:00:00 2001 From: a-maurice Date: Thu, 10 Jul 2025 13:36:06 -0700 Subject: [PATCH 1/3] [Firebase AI] Add logic for Developer Live API --- firebaseai/src/FirebaseAI.cs | 6 ------ firebaseai/src/LiveGenerativeModel.cs | 28 +++++++++++++++++++++++---- firebaseai/src/LiveSessionResponse.cs | 2 +- firebaseai/src/ModelContent.cs | 4 ++-- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/firebaseai/src/FirebaseAI.cs b/firebaseai/src/FirebaseAI.cs index 09ea4258..5c1cead5 100644 --- a/firebaseai/src/FirebaseAI.cs +++ b/firebaseai/src/FirebaseAI.cs @@ -165,8 +165,6 @@ public GenerativeModel GetGenerativeModel( /// /// - Note: Refer to [Gemini models](https://firebase.google.com/docs/vertex-ai/gemini-models) for /// guidance on choosing an appropriate model for your use case. - /// - /// - Note: Currently only supports the VertexAI backend. /// /// The name of the model to use, for example `"gemini-2.0-flash-live-preview-04-09"`; see /// [available model names @@ -183,10 +181,6 @@ public LiveGenerativeModel GetLiveModel( Tool[] tools = null, ModelContent? systemInstruction = null, RequestOptions? requestOptions = null) { - if (_backend.Provider != Backend.InternalProvider.VertexAI) { - throw new NotSupportedException("LiveGenerativeModel is currently only supported with the VertexAI backend."); - } - return new LiveGenerativeModel(_firebaseApp, _backend, modelName, liveGenerationConfig, tools, systemInstruction, requestOptions); diff --git a/firebaseai/src/LiveGenerativeModel.cs b/firebaseai/src/LiveGenerativeModel.cs index 2035ff77..f5f31826 100644 --- a/firebaseai/src/LiveGenerativeModel.cs +++ b/firebaseai/src/LiveGenerativeModel.cs @@ -70,10 +70,30 @@ internal LiveGenerativeModel(FirebaseApp firebaseApp, } private string GetURL() { - return "wss://firebasevertexai.googleapis.com/ws" + - "/google.firebase.vertexai.v1beta.LlmBidiService/BidiGenerateContent" + - $"/locations/{_backend.Location}" + - $"?key={_firebaseApp.Options.ApiKey}"; + if (_backend.Provider == FirebaseAI.Backend.InternalProvider.VertexAI) { + return "wss://firebasevertexai.googleapis.com/ws" + + "/google.firebase.vertexai.v1beta.LlmBidiService/BidiGenerateContent" + + $"/locations/{_backend.Location}" + + $"?key={_firebaseApp.Options.ApiKey}"; + } else if (_backend.Provider == FirebaseAI.Backend.InternalProvider.GoogleAI) { + return "wss://firebasevertexai.googleapis.com/ws" + + "/google.firebase.vertexai.v1beta.GenerativeService/BidiGenerateContent" + + $"?key={_firebaseApp.Options.ApiKey}"; + } else { + throw new NotSupportedException($"Missing support for backend: {_backend.Provider}"); + } + } + + private string GetModelName() { + if (_backend.Provider == FirebaseAI.Backend.InternalProvider.VertexAI) { + return $"projects/{_firebaseApp.Options.ProjectId}/locations/{_backend.Location}" + + $"/publishers/google/models/{_modelName}"; + } else if (_backend.Provider == FirebaseAI.Backend.InternalProvider.GoogleAI) { + return $"projects/{_firebaseApp.Options.ProjectId}" + + $"/models/{_modelName}"; + } else { + throw new NotSupportedException($"Missing support for backend: {_backend.Provider}"); + } } /// diff --git a/firebaseai/src/LiveSessionResponse.cs b/firebaseai/src/LiveSessionResponse.cs index 24ce1fdc..3680be86 100644 --- a/firebaseai/src/LiveSessionResponse.cs +++ b/firebaseai/src/LiveSessionResponse.cs @@ -59,7 +59,7 @@ public IEnumerable Audio { if (Message is LiveSessionContent content) { return content.Content?.Parts .OfType() - .Where(part => part.MimeType == "audio/pcm") + .Where(part => part.MimeType.StartsWith("audio/pcm")) .Select(part => part.Data.ToArray()); } return null; diff --git a/firebaseai/src/ModelContent.cs b/firebaseai/src/ModelContent.cs index 648e7543..26525fc1 100644 --- a/firebaseai/src/ModelContent.cs +++ b/firebaseai/src/ModelContent.cs @@ -313,9 +313,9 @@ internal Dictionary ToJson() { /// This method is used for deserializing JSON responses and should not be called directly. /// internal static ModelContent FromJson(Dictionary jsonDict) { - // Both role and parts are required keys return new ModelContent( - jsonDict.ParseValue("role", JsonParseOptions.ThrowEverything), + // If the role is missing, default to model since this is likely coming from the backend. + jsonDict.ParseValue("role", defaultValue: "model"), // Unknown parts are converted to null, which we then want to filter out here jsonDict.ParseObjectList("parts", PartFromJson, JsonParseOptions.ThrowEverything).Where(p => p is not null)); } From 16df0a788147e8376f62bb9284bc5975e7379da0 Mon Sep 17 00:00:00 2001 From: a-maurice Date: Tue, 22 Jul 2025 13:08:21 -0700 Subject: [PATCH 2/3] Update LiveSessionResponse.cs --- firebaseai/src/LiveSessionResponse.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebaseai/src/LiveSessionResponse.cs b/firebaseai/src/LiveSessionResponse.cs index bb99b9a6..43903912 100644 --- a/firebaseai/src/LiveSessionResponse.cs +++ b/firebaseai/src/LiveSessionResponse.cs @@ -58,7 +58,7 @@ public IReadOnlyList Audio { if (Message is LiveSessionContent content) { return content.Content?.Parts .OfType() - .Where(part => part.MimeType == "audio/pcm") + .Where(part => part.MimeType.StartsWith("audio/pcm")) .Select(part => part.Data.ToArray()) .ToList(); } From 8c856b25f493382a5f2628ec18812566fa212749 Mon Sep 17 00:00:00 2001 From: a-maurice Date: Thu, 24 Jul 2025 15:46:10 -0700 Subject: [PATCH 3/3] Update readme.md --- docs/readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/readme.md b/docs/readme.md index 17529408..1b6800a4 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -109,6 +109,10 @@ Support Release Notes ------------- +### Upcoming +- Changes + - Firebase AI: Add support for Developer API backend to LiveSessions. + ### 13.0.0 - Changes - General: Update to Firebase C++ SDK version 13.0.0.