Skip to content
36 changes: 36 additions & 0 deletions firebase-ai/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ package com.google.firebase.ai {
method @com.google.firebase.ai.type.PublicPreviewAPI public com.google.firebase.ai.LiveGenerativeModel liveModel(String modelName, com.google.firebase.ai.type.LiveGenerationConfig? generationConfig = null, java.util.List<com.google.firebase.ai.type.Tool>? tools = null);
method @com.google.firebase.ai.type.PublicPreviewAPI public com.google.firebase.ai.LiveGenerativeModel liveModel(String modelName, com.google.firebase.ai.type.LiveGenerationConfig? generationConfig = null, java.util.List<com.google.firebase.ai.type.Tool>? tools = null, com.google.firebase.ai.type.Content? systemInstruction = null);
method @com.google.firebase.ai.type.PublicPreviewAPI public com.google.firebase.ai.LiveGenerativeModel liveModel(String modelName, com.google.firebase.ai.type.LiveGenerationConfig? generationConfig = null, java.util.List<com.google.firebase.ai.type.Tool>? tools = null, com.google.firebase.ai.type.Content? systemInstruction = null, com.google.firebase.ai.type.RequestOptions requestOptions = com.google.firebase.ai.type.RequestOptions());
method @com.google.firebase.ai.type.PublicPreviewAPI public com.google.firebase.ai.TemplateGenerativeModel templateGenerativeModel();
method @com.google.firebase.ai.type.PublicPreviewAPI public com.google.firebase.ai.TemplateGenerativeModel templateGenerativeModel(com.google.firebase.ai.type.RequestOptions requestOptions = com.google.firebase.ai.type.RequestOptions());
method @com.google.firebase.ai.type.PublicPreviewAPI public com.google.firebase.ai.TemplateImagenModel templateImagenModel();
method @com.google.firebase.ai.type.PublicPreviewAPI public com.google.firebase.ai.TemplateImagenModel templateImagenModel(com.google.firebase.ai.type.RequestOptions requestOptions = com.google.firebase.ai.type.RequestOptions());
property public static final com.google.firebase.ai.FirebaseAI instance;
field public static final com.google.firebase.ai.FirebaseAI.Companion Companion;
}
Expand Down Expand Up @@ -83,6 +87,15 @@ package com.google.firebase.ai {
method public suspend Object? connect(kotlin.coroutines.Continuation<? super com.google.firebase.ai.type.LiveSession>);
}

@com.google.firebase.ai.type.PublicPreviewAPI public final class TemplateGenerativeModel {
method public suspend Object? generateContent(String templateId, java.util.Map<java.lang.String,?> inputs, kotlin.coroutines.Continuation<? super com.google.firebase.ai.type.GenerateContentResponse>);
method public kotlinx.coroutines.flow.Flow<com.google.firebase.ai.type.GenerateContentResponse> generateContentStream(String templateId, java.util.Map<java.lang.String,?> inputs);
}

@com.google.firebase.ai.type.PublicPreviewAPI public final class TemplateImagenModel {
method public suspend Object? generateImages(String templateId, java.util.Map<java.lang.String,?> inputs, kotlin.coroutines.Continuation<? super com.google.firebase.ai.type.ImagenGenerationResponse<com.google.firebase.ai.type.ImagenInlineImage>>);
}

}

package com.google.firebase.ai.java {
Expand Down Expand Up @@ -166,6 +179,29 @@ package com.google.firebase.ai.java {
method public com.google.firebase.ai.java.LiveSessionFutures from(com.google.firebase.ai.type.LiveSession session);
}

public abstract class TemplateGenerativeModelFutures {
method public static final com.google.firebase.ai.java.TemplateGenerativeModelFutures from(com.google.firebase.ai.TemplateGenerativeModel model);
method public abstract com.google.common.util.concurrent.ListenableFuture<com.google.firebase.ai.type.GenerateContentResponse> generateContent(String templateId, java.util.Map<java.lang.String,?> inputs);
method public abstract org.reactivestreams.Publisher<com.google.firebase.ai.type.GenerateContentResponse> generateContentStream(String templateId, java.util.Map<java.lang.String,?> inputs);
method public abstract com.google.firebase.ai.TemplateGenerativeModel getGenerativeModel();
field public static final com.google.firebase.ai.java.TemplateGenerativeModelFutures.Companion Companion;
}

public static final class TemplateGenerativeModelFutures.Companion {
method public com.google.firebase.ai.java.TemplateGenerativeModelFutures from(com.google.firebase.ai.TemplateGenerativeModel model);
}

public abstract class TemplateImagenModelFutures {
method public static final com.google.firebase.ai.java.TemplateImagenModelFutures from(com.google.firebase.ai.TemplateImagenModel model);
method public abstract com.google.common.util.concurrent.ListenableFuture<com.google.firebase.ai.type.ImagenGenerationResponse<com.google.firebase.ai.type.ImagenInlineImage>> generateImages(String templateId, java.util.Map<java.lang.String,?> inputs);
method public abstract com.google.firebase.ai.TemplateImagenModel getImageModel();
field public static final com.google.firebase.ai.java.TemplateImagenModelFutures.Companion Companion;
}

public static final class TemplateImagenModelFutures.Companion {
method public com.google.firebase.ai.java.TemplateImagenModelFutures from(com.google.firebase.ai.TemplateImagenModel model);
}

}

package com.google.firebase.ai.type {
Expand Down
53 changes: 53 additions & 0 deletions firebase-ai/src/main/kotlin/com/google/firebase/ai/FirebaseAI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,29 @@ internal constructor(
)
}

/**
* Instantiates a new [TemplateGenerativeModel] given the provided parameters.
*
* @param requestOptions Configuration options for sending requests to the backend.
* @return The initialized [TemplateGenerativeModel] instance.
*/
@JvmOverloads
@PublicPreviewAPI
public fun templateGenerativeModel(
requestOptions: RequestOptions = RequestOptions(),
): TemplateGenerativeModel {
val templateUri = getTemplateUri(backend)
return TemplateGenerativeModel(
templateUri,
firebaseApp.options.apiKey,
firebaseApp,
useLimitedUseAppCheckTokens,
requestOptions,
appCheckProvider.get(),
internalAuthProvider.get(),
)
}

/**
* Instantiates a new [LiveGenerationConfig] given the provided parameters.
*
Expand Down Expand Up @@ -205,6 +228,29 @@ internal constructor(
)
}

/**
* Instantiates a new [TemplateImagenModel] given the provided parameters.
*
* @param requestOptions Configuration options for sending requests to the backend.
* @return The initialized [TemplateImagenModel] instance.
*/
@JvmOverloads
@PublicPreviewAPI
public fun templateImagenModel(
requestOptions: RequestOptions = RequestOptions(),
): TemplateImagenModel {
val templateUri = getTemplateUri(backend)
return TemplateImagenModel(
templateUri,
firebaseApp.options.apiKey,
firebaseApp,
useLimitedUseAppCheckTokens,
requestOptions,
appCheckProvider.get(),
internalAuthProvider.get(),
)
}

public companion object {
/** The [FirebaseAI] instance for the default [FirebaseApp] using the Google AI Backend. */
@JvmStatic
Expand Down Expand Up @@ -258,6 +304,13 @@ internal constructor(

private val TAG = FirebaseAI::class.java.simpleName
}

private fun getTemplateUri(backend: GenerativeBackend): String =
when (backend.backend) {
GenerativeBackendEnum.VERTEX_AI ->
"projects/${firebaseApp.options.projectId}/locations/${backend.location}/templates/"
GenerativeBackendEnum.GOOGLE_AI -> "projects/${firebaseApp.options.projectId}/templates/"
}
}

/** The [FirebaseAI] instance for the default [FirebaseApp] using the Google AI Backend. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ internal constructor(
}

@OptIn(PublicPreviewAPI::class)
private fun ImagenGenerationResponse.Internal.validate(): ImagenGenerationResponse.Internal {
internal fun ImagenGenerationResponse.Internal.validate(): ImagenGenerationResponse.Internal {
if (predictions.none { it.mimeType != null }) {
throw ContentBlockedException(
message = predictions.first { it.raiFilteredReason != null }.raiFilteredReason
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.firebase.ai

import com.google.firebase.FirebaseApp
import com.google.firebase.ai.common.APIController
import com.google.firebase.ai.common.AppCheckHeaderProvider
import com.google.firebase.ai.common.TemplateGenerateContentRequest
import com.google.firebase.ai.type.FinishReason
import com.google.firebase.ai.type.FirebaseAIException
import com.google.firebase.ai.type.GenerateContentResponse
import com.google.firebase.ai.type.PromptBlockedException
import com.google.firebase.ai.type.PublicPreviewAPI
import com.google.firebase.ai.type.RequestOptions
import com.google.firebase.ai.type.ResponseStoppedException
import com.google.firebase.ai.type.SerializationException
import com.google.firebase.appcheck.interop.InteropAppCheckTokenProvider
import com.google.firebase.auth.internal.InternalAuthProvider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map

/**
* Represents a multimodal model (like Gemini), capable of generating content based on various
* templated input types.
*/
@PublicPreviewAPI
public class TemplateGenerativeModel
internal constructor(
private val templateUri: String,
private val controller: APIController,
) {

internal constructor(
templateUri: String,
apiKey: String,
firebaseApp: FirebaseApp,
useLimitedUseAppCheckTokens: Boolean,
requestOptions: RequestOptions = RequestOptions(),
appCheckTokenProvider: InteropAppCheckTokenProvider? = null,
internalAuthProvider: InternalAuthProvider? = null
) : this(
templateUri,
APIController(
apiKey,
"",
requestOptions,
"gl-kotlin/${KotlinVersion.CURRENT}-ai fire/${BuildConfig.VERSION_NAME}",
firebaseApp,
AppCheckHeaderProvider(
TAG,
useLimitedUseAppCheckTokens,
appCheckTokenProvider,
internalAuthProvider
),
),
)

/**
* Generates new content using the given templateId with the given inputs.
*
* @param templateId The ID of server prompt template.
* @param inputs the inputs needed to fill in the prompt
* @return The content generated by the model.
* @throws [FirebaseAIException] if the request failed.
* @see [FirebaseAIException] for types of errors.
*/
public suspend fun generateContent(
templateId: String,
inputs: Map<String, Any>
): GenerateContentResponse =
try {
controller
.templateGenerateContent("$templateUri$templateId", constructRequest(inputs))
.toPublic()
.validate()
} catch (e: Throwable) {
throw FirebaseAIException.from(e)
}

/**
* Generates new content as a stream using the given templateId with the given inputs.
*
* @param templateId The ID of server prompt template.
* @param inputs the inputs needed to fill in the prompt
* @return A [Flow] which will emit responses as they are returned by the model.
* @throws [FirebaseAIException] if the request failed.
* @see [FirebaseAIException] for types of errors.
*/
public fun generateContentStream(
templateId: String,
inputs: Map<String, Any>
): Flow<GenerateContentResponse> =
controller
.templateGenerateContentStream("$templateUri$templateId", constructRequest(inputs))
.catch { throw FirebaseAIException.from(it) }
.map { it.toPublic().validate() }

internal fun constructRequest(inputs: Map<String, Any>): TemplateGenerateContentRequest {
return TemplateGenerateContentRequest(inputs)
}

private fun GenerateContentResponse.validate() = apply {
if (candidates.isEmpty() && promptFeedback == null) {
throw SerializationException("Error deserializing response, found no valid fields")
}
promptFeedback?.blockReason?.let { throw PromptBlockedException(this) }
candidates
.mapNotNull { it.finishReason }
.firstOrNull { it != FinishReason.STOP }
?.let { throw ResponseStoppedException(this) }
}

private companion object {
private val TAG = TemplateGenerativeModel::class.java.simpleName
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.firebase.ai

import com.google.firebase.FirebaseApp
import com.google.firebase.ai.common.APIController
import com.google.firebase.ai.common.AppCheckHeaderProvider
import com.google.firebase.ai.common.TemplateGenerateImageRequest
import com.google.firebase.ai.type.FirebaseAIException
import com.google.firebase.ai.type.ImagenGenerationResponse
import com.google.firebase.ai.type.ImagenInlineImage
import com.google.firebase.ai.type.PublicPreviewAPI
import com.google.firebase.ai.type.RequestOptions
import com.google.firebase.appcheck.interop.InteropAppCheckTokenProvider
import com.google.firebase.auth.internal.InternalAuthProvider

/**
* Represents a generative model (like Imagen), capable of generating images based a template.
*
* See the documentation for a list of
* [supported models](https://firebase.google.com/docs/ai-logic/models).
*/
@PublicPreviewAPI
public class TemplateImagenModel
internal constructor(
private val templateUri: String,
private val controller: APIController,
) {

@JvmOverloads
internal constructor(
templateUri: String,
apiKey: String,
firebaseApp: FirebaseApp,
useLimitedUseAppCheckTokens: Boolean,
requestOptions: RequestOptions = RequestOptions(),
appCheckTokenProvider: InteropAppCheckTokenProvider? = null,
internalAuthProvider: InternalAuthProvider? = null,
) : this(
templateUri,
APIController(
apiKey,
"",
requestOptions,
"gl-kotlin/${KotlinVersion.CURRENT}-ai fire/${BuildConfig.VERSION_NAME}",
firebaseApp,
AppCheckHeaderProvider(
TAG,
useLimitedUseAppCheckTokens,
appCheckTokenProvider,
internalAuthProvider
),
),
)

/**
* Generates an image, returning the result directly to the caller.
*
* @param templateId The ID of server prompt template.
* @param inputs the inputs needed to fill in the prompt
*/
public suspend fun generateImages(
templateId: String,
inputs: Map<String, Any>
): ImagenGenerationResponse<ImagenInlineImage> =
try {
controller
.templateGenerateImage(
"$templateUri$templateId",
constructTemplateGenerateImageRequest(inputs)
)
.validate()
.toPublicInline()
} catch (e: Throwable) {
throw FirebaseAIException.from(e)
}

private fun constructTemplateGenerateImageRequest(
inputs: Map<String, Any>
): TemplateGenerateImageRequest {
return TemplateGenerateImageRequest(inputs)
}

internal companion object {
private val TAG = TemplateImagenModel::class.java.simpleName
internal const val DEFAULT_FILTERED_ERROR =
"Unable to show generated images. All images were filtered out because they violated Vertex AI's usage guidelines. You will not be charged for blocked images. Try rephrasing the prompt. If you think this was an error, send feedback."
}
}
Loading
Loading