Gemini models return responses as unstructured text by default. However, some use cases require structured text (like JSON or enums). For example, you might be using the response for other downstream tasks that require an established data schema.
To ensure that the model's generated output always adheres to a specific schema, you can define a schema, which works like a blueprint for model responses. You can then directly extract data from the model's output with less post-processing.
Here are some example use cases:
Ensure that a model's response produces valid JSON and conforms to your provided schema.
For example, the model can generate structured entries for recipes that always include the recipe name, list of ingredients, and steps. You can then more easily parse and display this information in the UI of your app.Constrain how a model can respond during classification tasks.
For example, you can have the model annotate text with a specific set of labels (for instance, a specific set of enums likepositiveandnegative), rather than labels that the model produces (which could have a degree of variability likegood,positive,negative, orbad).
This page describes how to generate structured output (like JSON and enums) in your hybrid experiences for Android apps.
Jump to JSON output Jump to enum output
Configuration for structured output
Generating structured output (like JSON and enums) is supported for both on-device and cloud-hosted inference.
To generate structured output, pass your schema directly to
generateObject().
Schema requirements depend on your configured inference mode:
For on-device and hybrid inference (
ONLY_ON_DEVICE,PREFER_ON_DEVICE, andPREFER_IN_CLOUD):- Requires using the
@Generableannotation on a Kotlindata classwith the KSP processor; manual schemas and directenum classannotations are not supported. - When inference runs on-device, the SDK automatically translates the schema into constraints for the on-device model (using the ML Kit Prompt API).
- If a hybrid request falls back to cloud inference, the SDK automatically
sets the
responseMimeTypetoapplication/jsonand passes the schema to the cloud-hosted Gemini model.
- Requires using the
For cloud-only inference (
ONLY_IN_CLOUD):- Supports both
@Generableannotations (recommended) and manual schemas (built usingJsonSchemahelper methods). - The SDK automatically sets the
responseMimeTypetoapplication/jsonand passes the schema to the cloud-hosted Gemini model.
- Supports both
Before you begin
|
Click your Gemini API provider to view provider-specific content and code on this page. |
Before generating structured output, make sure that you've completed the following setup:
Complete the getting started guide for building hybrid experiences, which covers setting up your Firebase project, downloading the on-device model, and configuring App Check.
Configure the Kotlin Symbol Processing (KSP) plugin and add the Firebase AI KSP dependency to your app.
In your module (app-level) Gradle file (like
<project>/<app-module>/build.gradle.kts), add the KSP plugin, the Kotlin Serialization plugin, and the required dependencies:plugins { // ... other plugins id("com.google.gms.google-services") id("com.google.devtools.ksp") version "LATEST_VERSION" id("org.jetbrains.kotlin.plugin.serialization") version "LATEST_VERSION" } dependencies { // ... other androidx dependencies // Add the dependencies for the Firebase AI Logic and App Check libraries. implementation("com.google.firebase:firebase-ai:17.17.0") implementation("com.google.firebase:firebase-ai-ondevice:16.0.0-beta05") implementation("com.google.firebase:firebase-appcheck-debug:19.4.1") // Add the Firebase AI KSP processor for schema generation. ksp("com.google.firebase:firebase-ai-ksp-processor:16.0.2") // (Optional) Add kotlinx.serialization JSON library for object decoding. implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:LATEST_VERSION") }
Jump to JSON output Jump to enum output
JSON output
The following examples adapt the
general JSON output example
to accommodate hybrid inference (for example, PREFER_ON_DEVICE).
In the scenario for these examples, the model generates a list of character profiles for a fantasy story, with structured attributes like name, age, species, and optional accessories.
You can define your response schemas using either of the following approaches:
KSP annotations (
@Generableand@Guide)- Supported for all inference modes, and required for on-device and hybrid
inference (specifically,
ONLY_ON_DEVICE,PREFER_ON_DEVICE, andPREFER_IN_CLOUD). - Define Kotlin data classes to automatically generate schemas at compile time
and deserialize responses directly into strongly-typed objects using
getObject().
- Supported for all inference modes, and required for on-device and hybrid
inference (specifically,
Manual
JsonSchemahelper methods- Supported only for cloud-based inference (specifically,
ONLY_IN_CLOUD). - Manually construct a
JsonSchemain code without using the KSP processor, and read the raw JSON string fromresponse.response.text.
- Supported only for cloud-based inference (specifically,
Example 1: Using @Generable and @Guide annotations with KSP
Define a Kotlin data class annotated with @Serializable and @Generable.
Use @Guide annotations on properties to provide the model with descriptions,
value bounds, or item constraints.
This approach is supported for all inference modes, and it's required for
on-device and hybrid experiences (specifically, ONLY_ON_DEVICE,
PREFER_ON_DEVICE, and PREFER_IN_CLOUD).
|
Before trying this sample, complete the
Before you begin section of this guide
to set up your project and app. In that section, you'll also click a button for your chosen Gemini API provider so that you see provider-specific content on this page. |
import com.google.firebase.Firebase
import com.google.firebase.ai.type.GenerativeBackend
import com.google.firebase.ai.InferenceMode
import com.google.firebase.ai.OnDeviceConfig
import com.google.firebase.ai.annotations.Generable
import com.google.firebase.ai.annotations.Guide
import com.google.firebase.ai.ai
import kotlinx.serialization.Serializable
// Define data classes representing the schema, annotated with @Serializable and @Generable.
// You can provide descriptions on @Generable and @Guide to guide the model's output.
@Serializable
@Generable(description = "A character profile for a fantasy story")
data class Character(
val name: String,
val age: Int,
val species: String,
// Use @Guide to add property descriptions, value bounds (minimum/maximum), or formats.
// Properties with default values or nullable types are treated as optional in the schema.
@Guide(description = "An accessory the character wears or carries")
val accessory: String? = null
) {
// An empty companion object is required for KSP to generate the firebaseAISchema() extension.
companion object
}
@Serializable
@Generable(description = "A list of character profiles")
data class CharacterList(
// Use minItems or maxItems to specify collection size bounds for the model.
@Guide(description = "List of characters", minItems = 1)
val characters: List<Character>
) {
// An empty companion object is required for KSP to generate the firebaseAISchema() extension.
companion object
}
// Initialize the Gemini Developer API backend service.
// Create a GenerativeModel instance configured for hybrid inference (like PREFER_ON_DEVICE).
val model = Firebase.ai(backend = GenerativeBackend.googleAI())
.generativeModel(
modelName = "CLOUD_MODEL_NAME",
onDeviceConfig = OnDeviceConfig(mode = InferenceMode.INFERENCE_MODE)
)
// Obtain the schema generated by KSP via the firebaseAISchema() extension.
val schema = CharacterList.firebaseAISchema()
val prompt = "Create profiles for some characters for a fantasy story."
// Generate the structured object (the SDK applies the schema to on-device or cloud models).
val response = model.generateObject(schema, prompt)
// Access the strongly-typed deserialized object directly via getObject().
val characterList: CharacterList? = response.getObject()
characterList?.characters?.forEach { character ->
println("Name: ${character.name}, Species: ${character.species}, Age: ${character.age}")
println("Accessory: ${character.accessory ?: "None"}")
}
Example 2: Using manual JsonSchema helper methods
If your app is only using cloud-based inference (specifically,
ONLY_IN_CLOUD), then you can manually build a
JsonSchema
using helper methods provided by the Firebase AI Logic SDK.
|
Before trying this sample, complete the
Before you begin section of this guide
to set up your project and app. In that section, you'll also click a button for your chosen Gemini API provider so that you see provider-specific content on this page. |
import com.google.firebase.Firebase
import com.google.firebase.ai.type.GenerativeBackend
import com.google.firebase.ai.InferenceMode
import com.google.firebase.ai.OnDeviceConfig
import com.google.firebase.ai.ai
import com.google.firebase.ai.type.JsonSchema
// Define the schema manually using JsonSchema helper methods.
// Properties are required by default unless specified in optionalProperties.
val jsonSchema = JsonSchema.obj(
properties = mapOf(
"characters" to JsonSchema.array(
items = JsonSchema.obj(
properties = mapOf(
"name" to JsonSchema.string(),
"accessory" to JsonSchema.string(),
"age" to JsonSchema.integer(),
"species" to JsonSchema.string()
),
optionalProperties = listOf("accessory")
)
)
)
)
// Initialize the Gemini Developer API backend service.
// Manual schemas are only supported for cloud-based inference (specifically, ONLY_IN_CLOUD).
val model = Firebase.ai(backend = GenerativeBackend.googleAI())
.generativeModel(
modelName = "CLOUD_MODEL_NAME",
onDeviceConfig = OnDeviceConfig(mode = InferenceMode.ONLY_IN_CLOUD)
)
// Call generateObject() with the manual schema and prompt.
val response = model.generateObject(
jsonSchema,
"Create profiles for some characters for a fantasy story."
)
// Access the generated JSON string conforming to the schema from response.response.text.
println(response.response.text)
Enum output
The following examples adapt the
general enum output example
to accommodate hybrid inference (for example, PREFER_ON_DEVICE).
In the scenario for these examples, the model classifies a film description by
selecting a single genre from a predefined list of allowed options
(drama, comedy, or documentary).
You can define your response schemas using either of the following approaches:
-
- Supported for all inference modes, and required for on-device and hybrid
inference (specifically,
ONLY_ON_DEVICE,PREFER_ON_DEVICE, andPREFER_IN_CLOUD). - Define an enum wrapped in a Kotlin
data classto automatically generate the schema and deserialize responses directly into strongly-typed objects usinggetObject().
- Supported for all inference modes, and required for on-device and hybrid
inference (specifically,
Manual
JsonSchemahelper methods- Supported only for cloud-based inference (specifically,
ONLY_IN_CLOUD). - Manually build an enum
JsonSchemausingJsonSchema.enumeration()without using the KSP processor, and read the selected string fromresponse.response.text.
- Supported only for cloud-based inference (specifically,
Example 1: Using @Generable annotations with KSP
Define an enum class representing the allowed values, and wrap it as a
property inside a Kotlin data class annotated with @Serializable and
@Generable.
This approach is supported for all inference modes, and it's required for
on-device and hybrid experiences (specifically, ONLY_ON_DEVICE,
PREFER_ON_DEVICE, and PREFER_IN_CLOUD).
|
Before trying this sample, complete the
Before you begin section of this guide
to set up your project and app. In that section, you'll also click a button for your chosen Gemini API provider so that you see provider-specific content on this page. |
import com.google.firebase.Firebase
import com.google.firebase.ai.type.GenerativeBackend
import com.google.firebase.ai.InferenceMode
import com.google.firebase.ai.OnDeviceConfig
import com.google.firebase.ai.annotations.Generable
import com.google.firebase.ai.annotations.Guide
import com.google.firebase.ai.ai
import kotlinx.serialization.Serializable
// Define an enum class representing the allowed options.
@Serializable
enum class FilmGenre {
DRAMA,
COMEDY,
DOCUMENTARY
}
// Wrap the enum in a data class annotated with @Serializable and @Generable.
// On-device inference requires an @Generable data class.
// Direct enum annotations are not supported on-device.
@Serializable
@Generable(description = "The classification result for the film")
data class FilmClassification(
@Guide(description = "The genre of the film")
val genre: FilmGenre
) {
// An empty companion object is required for KSP to generate the firebaseAISchema() extension.
companion object
}
// Initialize the Gemini Developer API backend service.
// Create a GenerativeModel instance configured for hybrid inference (like PREFER_ON_DEVICE).
val model = Firebase.ai(backend = GenerativeBackend.googleAI())
.generativeModel(
modelName = "CLOUD_MODEL_NAME",
onDeviceConfig = OnDeviceConfig(mode = InferenceMode.INFERENCE_MODE)
)
// Obtain the schema generated by KSP via the firebaseAISchema() extension.
val schema = FilmClassification.firebaseAISchema()
val prompt = """
The film aims to educate and inform viewers about real-life subjects, events, or people.
It offers a factual record of a particular topic by combining interviews, historical footage,
and narration. The primary purpose of a film is to present information and provide insights
into various aspects of reality.
"""
// Generate the structured object (the SDK applies the schema to on-device or cloud models).
val response = model.generateObject(schema, prompt)
// Access the strongly-typed deserialized object and enum value directly via getObject().
val classification: FilmClassification? = response.getObject()
val genre: FilmGenre? = classification?.genre
println("Selected genre: $genre")
Example 2: Using manual JsonSchema helper methods
If your app is only using cloud-based inference (specifically,
ONLY_IN_CLOUD), then you can manually build an enum
JsonSchema
using helper methods provided by the Firebase AI Logic SDK.
|
Before trying this sample, complete the
Before you begin section of this guide
to set up your project and app. In that section, you'll also click a button for your chosen Gemini API provider so that you see provider-specific content on this page. |
import com.google.firebase.Firebase
import com.google.firebase.ai.type.GenerativeBackend
import com.google.firebase.ai.InferenceMode
import com.google.firebase.ai.OnDeviceConfig
import com.google.firebase.ai.ai
import com.google.firebase.ai.type.JsonSchema
// Define an enum schema with allowed string values and a description.
val enumSchema = JsonSchema.enumeration(
values = listOf("drama", "comedy", "documentary"),
description = "The genre of the film"
)
// Initialize the Gemini Developer API backend service.
// Manual schemas are only supported for cloud-based inference (specifically, ONLY_IN_CLOUD).
val model = Firebase.ai(backend = GenerativeBackend.googleAI())
.generativeModel(
modelName = "CLOUD_MODEL_NAME",
onDeviceConfig = OnDeviceConfig(mode = InferenceMode.ONLY_IN_CLOUD)
)
val prompt = """
The film aims to educate and inform viewers about real-life subjects, events, or people.
It offers a factual record of a particular topic by combining interviews, historical footage,
and narration. The primary purpose of a film is to present information and provide insights
into various aspects of reality.
"""
// Call generateObject() with the enum schema and prompt.
val response = model.generateObject(enumSchema, prompt)
// Access the selected enum value string from response.response.text.
println(response.response.text)
Give feedback about your experience with Firebase AI Logic