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.
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 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();
}
diff --git a/firebaseai/src/ModelContent.cs b/firebaseai/src/ModelContent.cs
index 29ad07a9..cee6cdbd 100644
--- a/firebaseai/src/ModelContent.cs
+++ b/firebaseai/src/ModelContent.cs
@@ -312,9 +312,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));
}