Skip to content

Commit ec8a8c2

Browse files
author
David Motsonashvili
committed
Server Templates
1 parent 89c3e53 commit ec8a8c2

File tree

7 files changed

+359
-1
lines changed

7 files changed

+359
-1
lines changed

firebase-ai/api.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ package com.google.firebase.ai {
3636
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);
3737
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);
3838
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());
39+
method public com.google.firebase.ai.TemplateGenerativeModel templateGenerativeModel();
40+
method public com.google.firebase.ai.TemplateGenerativeModel templateGenerativeModel(com.google.firebase.ai.type.RequestOptions requestOptions = com.google.firebase.ai.type.RequestOptions());
41+
method public com.google.firebase.ai.TemplateImagenModel templateImagenModel();
42+
method public com.google.firebase.ai.TemplateImagenModel templateImagenModel(com.google.firebase.ai.type.RequestOptions requestOptions = com.google.firebase.ai.type.RequestOptions());
3943
property public static final com.google.firebase.ai.FirebaseAI instance;
4044
field public static final com.google.firebase.ai.FirebaseAI.Companion Companion;
4145
}
@@ -83,6 +87,15 @@ package com.google.firebase.ai {
8387
method public suspend Object? connect(kotlin.coroutines.Continuation<? super com.google.firebase.ai.type.LiveSession>);
8488
}
8589

90+
public final class TemplateGenerativeModel {
91+
method public suspend Object? generateContent(String templateId, java.util.Map<java.lang.String,?> inputs, kotlin.coroutines.Continuation<? super com.google.firebase.ai.type.GenerateContentResponse>);
92+
method public kotlinx.coroutines.flow.Flow<com.google.firebase.ai.type.GenerateContentResponse> generateContentStream(String templateId, java.util.Map<java.lang.String,?> inputs);
93+
}
94+
95+
public final class TemplateImagenModel {
96+
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>>);
97+
}
98+
8699
}
87100

88101
package com.google.firebase.ai.java {

firebase-ai/src/main/kotlin/com/google/firebase/ai/FirebaseAI.kt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,33 @@ internal constructor(
106106
)
107107
}
108108

109+
/**
110+
* Instantiates a new [TemplateGenerativeModel] given the provided parameters.
111+
*
112+
* @param requestOptions Configuration options for sending requests to the backend.
113+
* @return The initialized [TemplateGenerativeModel] instance.
114+
*/
115+
@JvmOverloads
116+
public fun templateGenerativeModel(
117+
requestOptions: RequestOptions = RequestOptions(),
118+
): TemplateGenerativeModel {
119+
val templateUri =
120+
when (backend.backend) {
121+
GenerativeBackendEnum.VERTEX_AI ->
122+
"projects/${firebaseApp.options.projectId}/locations/${backend.location}/templates/"
123+
GenerativeBackendEnum.GOOGLE_AI -> "projects/${firebaseApp.options.projectId}/templates/"
124+
}
125+
return TemplateGenerativeModel(
126+
templateUri,
127+
firebaseApp.options.apiKey,
128+
firebaseApp,
129+
useLimitedUseAppCheckTokens,
130+
requestOptions,
131+
appCheckProvider.get(),
132+
internalAuthProvider.get(),
133+
)
134+
}
135+
109136
/**
110137
* Instantiates a new [LiveGenerationConfig] given the provided parameters.
111138
*
@@ -205,6 +232,33 @@ internal constructor(
205232
)
206233
}
207234

235+
/**
236+
* Instantiates a new [ImagenModel] given the provided parameters.
237+
*
238+
* @param requestOptions Configuration options for sending requests to the backend.
239+
* @return The initialized [TemplateImagenModel] instance.
240+
*/
241+
@JvmOverloads
242+
public fun templateImagenModel(
243+
requestOptions: RequestOptions = RequestOptions(),
244+
): TemplateImagenModel {
245+
val templateUri =
246+
when (backend.backend) {
247+
GenerativeBackendEnum.VERTEX_AI ->
248+
"projects/${firebaseApp.options.projectId}/locations/${backend.location}/templates/"
249+
GenerativeBackendEnum.GOOGLE_AI -> "projects/${firebaseApp.options.projectId}/templates/"
250+
}
251+
return TemplateImagenModel(
252+
templateUri,
253+
firebaseApp.options.apiKey,
254+
firebaseApp,
255+
useLimitedUseAppCheckTokens,
256+
requestOptions,
257+
appCheckProvider.get(),
258+
internalAuthProvider.get(),
259+
)
260+
}
261+
208262
public companion object {
209263
/** The [FirebaseAI] instance for the default [FirebaseApp] using the Google AI Backend. */
210264
@JvmStatic

firebase-ai/src/main/kotlin/com/google/firebase/ai/ImagenModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ internal constructor(
233233
}
234234

235235
@OptIn(PublicPreviewAPI::class)
236-
private fun ImagenGenerationResponse.Internal.validate(): ImagenGenerationResponse.Internal {
236+
internal fun ImagenGenerationResponse.Internal.validate(): ImagenGenerationResponse.Internal {
237237
if (predictions.none { it.mimeType != null }) {
238238
throw ContentBlockedException(
239239
message = predictions.first { it.raiFilteredReason != null }.raiFilteredReason
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.ai
18+
19+
import com.google.firebase.FirebaseApp
20+
import com.google.firebase.ai.common.APIController
21+
import com.google.firebase.ai.common.AppCheckHeaderProvider
22+
import com.google.firebase.ai.common.TemplateGenerateContentRequest
23+
import com.google.firebase.ai.type.FinishReason
24+
import com.google.firebase.ai.type.FirebaseAIException
25+
import com.google.firebase.ai.type.GenerateContentResponse
26+
import com.google.firebase.ai.type.PromptBlockedException
27+
import com.google.firebase.ai.type.RequestOptions
28+
import com.google.firebase.ai.type.ResponseStoppedException
29+
import com.google.firebase.ai.type.SerializationException
30+
import com.google.firebase.appcheck.interop.InteropAppCheckTokenProvider
31+
import com.google.firebase.auth.internal.InternalAuthProvider
32+
import kotlinx.coroutines.flow.Flow
33+
import kotlinx.coroutines.flow.catch
34+
import kotlinx.coroutines.flow.map
35+
36+
/**
37+
* Represents a multimodal model (like Gemini), capable of generating content based on various
38+
* templated input types.
39+
*/
40+
public class TemplateGenerativeModel
41+
internal constructor(
42+
private val templateUri: String,
43+
private val controller: APIController,
44+
) {
45+
46+
internal constructor(
47+
templateUri: String,
48+
apiKey: String,
49+
firebaseApp: FirebaseApp,
50+
useLimitedUseAppCheckTokens: Boolean,
51+
requestOptions: RequestOptions = RequestOptions(),
52+
appCheckTokenProvider: InteropAppCheckTokenProvider? = null,
53+
internalAuthProvider: InternalAuthProvider? = null
54+
) : this(
55+
templateUri,
56+
APIController(
57+
apiKey,
58+
"",
59+
requestOptions,
60+
"gl-kotlin/${KotlinVersion.CURRENT}-ai fire/${BuildConfig.VERSION_NAME}",
61+
firebaseApp,
62+
AppCheckHeaderProvider(
63+
TAG,
64+
useLimitedUseAppCheckTokens,
65+
appCheckTokenProvider,
66+
internalAuthProvider
67+
),
68+
),
69+
)
70+
71+
/**
72+
* Generates new content using the given templateId with the given inputs.
73+
*
74+
* @param templateId The ID of server prompt template.
75+
* @param inputs the inputs needed to fill in the prompt
76+
* @return The content generated by the model.
77+
* @throws [FirebaseAIException] if the request failed.
78+
* @see [FirebaseAIException] for types of errors.
79+
*/
80+
public suspend fun generateContent(
81+
templateId: String,
82+
inputs: Map<String, Any>
83+
): GenerateContentResponse =
84+
try {
85+
controller
86+
.templateGenerateContent("$templateUri$templateId", constructRequest(inputs))
87+
.toPublic()
88+
.validate()
89+
} catch (e: Throwable) {
90+
throw FirebaseAIException.from(e)
91+
}
92+
93+
/**
94+
* Generates new content as a stream using the given templateId with the given inputs.
95+
*
96+
* @param templateId The ID of server prompt template.
97+
* @param inputs the inputs needed to fill in the prompt
98+
* @return A [Flow] which will emit responses as they are returned by the model.
99+
* @throws [FirebaseAIException] if the request failed.
100+
* @see [FirebaseAIException] for types of errors.
101+
*/
102+
public fun generateContentStream(
103+
templateId: String,
104+
inputs: Map<String, Any>
105+
): Flow<GenerateContentResponse> =
106+
controller
107+
.templateGenerateContentStream("$templateUri$templateId", constructRequest(inputs))
108+
.catch { throw FirebaseAIException.from(it) }
109+
.map { it.toPublic().validate() }
110+
111+
internal fun constructRequest(inputs: Map<String, Any>): TemplateGenerateContentRequest {
112+
return TemplateGenerateContentRequest(inputs)
113+
}
114+
115+
private fun GenerateContentResponse.validate() = apply {
116+
if (candidates.isEmpty() && promptFeedback == null) {
117+
throw SerializationException("Error deserializing response, found no valid fields")
118+
}
119+
promptFeedback?.blockReason?.let { throw PromptBlockedException(this) }
120+
candidates
121+
.mapNotNull { it.finishReason }
122+
.firstOrNull { it != FinishReason.STOP }
123+
?.let { throw ResponseStoppedException(this) }
124+
}
125+
126+
private companion object {
127+
private val TAG = TemplateGenerativeModel::class.java.simpleName
128+
}
129+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.ai
18+
19+
import com.google.firebase.FirebaseApp
20+
import com.google.firebase.ai.common.APIController
21+
import com.google.firebase.ai.common.AppCheckHeaderProvider
22+
import com.google.firebase.ai.common.TemplateGenerateImageRequest
23+
import com.google.firebase.ai.type.FirebaseAIException
24+
import com.google.firebase.ai.type.ImagenGenerationResponse
25+
import com.google.firebase.ai.type.ImagenInlineImage
26+
import com.google.firebase.ai.type.RequestOptions
27+
import com.google.firebase.appcheck.interop.InteropAppCheckTokenProvider
28+
import com.google.firebase.auth.internal.InternalAuthProvider
29+
30+
/**
31+
* Represents a generative model (like Imagen), capable of generating images based a template.
32+
*
33+
* See the documentation for a list of
34+
* [supported models](https://firebase.google.com/docs/ai-logic/models).
35+
*/
36+
public class TemplateImagenModel
37+
internal constructor(
38+
private val templateUri: String,
39+
private val controller: APIController,
40+
) {
41+
42+
@JvmOverloads
43+
internal constructor(
44+
templateUri: String,
45+
apiKey: String,
46+
firebaseApp: FirebaseApp,
47+
useLimitedUseAppCheckTokens: Boolean,
48+
requestOptions: RequestOptions = RequestOptions(),
49+
appCheckTokenProvider: InteropAppCheckTokenProvider? = null,
50+
internalAuthProvider: InternalAuthProvider? = null,
51+
) : this(
52+
templateUri,
53+
APIController(
54+
apiKey,
55+
"",
56+
requestOptions,
57+
"gl-kotlin/${KotlinVersion.CURRENT}-ai fire/${BuildConfig.VERSION_NAME}",
58+
firebaseApp,
59+
AppCheckHeaderProvider(
60+
TAG,
61+
useLimitedUseAppCheckTokens,
62+
appCheckTokenProvider,
63+
internalAuthProvider
64+
),
65+
),
66+
)
67+
68+
/**
69+
* Generates an image, returning the result directly to the caller.
70+
*
71+
* @param templateId The ID of server prompt template.
72+
* @param inputs the inputs needed to fill in the prompt
73+
*/
74+
public suspend fun generateImages(
75+
templateId: String,
76+
inputs: Map<String, Any>
77+
): ImagenGenerationResponse<ImagenInlineImage> =
78+
try {
79+
controller
80+
.templateGenerateImage(
81+
"$templateUri$templateId",
82+
constructTemplateGenerateImageRequest(inputs)
83+
)
84+
.validate()
85+
.toPublicInline()
86+
} catch (e: Throwable) {
87+
throw FirebaseAIException.from(e)
88+
}
89+
90+
private fun constructTemplateGenerateImageRequest(
91+
inputs: Map<String, Any>
92+
): TemplateGenerateImageRequest {
93+
return TemplateGenerateImageRequest(inputs)
94+
}
95+
96+
internal companion object {
97+
private val TAG = TemplateImagenModel::class.java.simpleName
98+
internal const val DEFAULT_FILTERED_ERROR =
99+
"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."
100+
}
101+
}

0 commit comments

Comments
 (0)