Skip to content

[Firebase AI] Add logic for Developer Live API #1288

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
4 changes: 4 additions & 0 deletions docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 0 additions & 6 deletions firebaseai/src/FirebaseAI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
/// </summary>
/// <param name="modelName">The name of the model to use, for example `"gemini-2.0-flash-live-preview-04-09"`; see
/// [available model names
Expand All @@ -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);
Expand Down
28 changes: 24 additions & 4 deletions firebaseai/src/LiveGenerativeModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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}");
}
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion firebaseai/src/LiveSessionResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public IReadOnlyList<byte[]> Audio {
if (Message is LiveSessionContent content) {
return content.Content?.Parts
.OfType<ModelContent.InlineDataPart>()
.Where(part => part.MimeType == "audio/pcm")
.Where(part => part.MimeType.StartsWith("audio/pcm"))
.Select(part => part.Data.ToArray())
.ToList();
}
Expand Down
4 changes: 2 additions & 2 deletions firebaseai/src/ModelContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,9 @@ internal Dictionary<string, object> ToJson() {
/// This method is used for deserializing JSON responses and should not be called directly.
/// </summary>
internal static ModelContent FromJson(Dictionary<string, object> jsonDict) {
// Both role and parts are required keys
return new ModelContent(
jsonDict.ParseValue<string>("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));
}
Expand Down