> [!WARNING]
> **Experimental:** Using the Firebase AI Logic SDK to build hybrid experiences is an Experimental feature (and the Prompt API from ML Kit is in beta), which means that this feature isn't subject to any SLA or deprecation policy and could change in backwards-incompatible ways.

<br />

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 like `positive` and `negative`),
  rather than labels that the model produces (which could have a degree of
  variability like `good`, `positive`, `negative`, or `bad`).

This page describes how to generate structured output (like JSON and enums)
in your hybrid experiences for Android apps.

[Jump to JSON output](https://firebase.google.com/docs/ai-logic/hybrid/android/generate-structured-output.md.txt#json-output)
[Jump to enum output](https://firebase.google.com/docs/ai-logic/hybrid/android/generate-structured-output.md.txt#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()`](https://firebase.google.com/docs/reference/kotlin/com/google/firebase/ai/GenerativeModel.md.txt#generateObject(com.google.firebase.ai.type.JsonSchema,kotlin.String)).
Schema requirements depend on your configured inference mode:

- **For on-device and hybrid inference (`ONLY_ON_DEVICE`, `PREFER_ON_DEVICE`,
  and `PREFER_IN_CLOUD`)**:

  - Requires using the `@Generable` annotation on a Kotlin `data class` with the KSP processor; manual schemas and direct `enum class` annotations 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 `responseMimeType` to `application/json` and passes the schema to the cloud-hosted Gemini model.
- **For cloud-only inference (`ONLY_IN_CLOUD`)**:

  - Supports both `@Generable` annotations (recommended) and manual schemas (built using `JsonSchema` helper methods).
  - The SDK automatically sets the `responseMimeType` to `application/json` and passes the schema to the cloud-hosted Gemini model.

## Before you begin


|---|
| *Click your Gemini API provider to view provider-specific content and code on this page.* <button value="dev" default="">Gemini Developer API</button> <button value="vertex">Agent Platform Gemini API</button> |

<br />

Before generating structured output, make sure that you've completed the
following setup:

1. Complete the
   [getting started guide for building hybrid experiences](https://firebase.google.com/docs/ai-logic/hybrid/android/get-started.md.txt#get-started),
   which covers setting up your Firebase project, downloading the on-device
   model, and configuring App Check.

2. 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:

   ```groovy
   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:18.1.0")
      implementation("com.google.firebase:firebase-ai-ondevice:16.0.0-beta07")
      implementation("com.google.firebase:firebase-appcheck-debug:20.0.0")

      // Add the Firebase AI KSP processor for schema generation.
      ksp("com.google.firebase:firebase-ai-ksp-processor:17.0.0")

      // (Optional) Add kotlinx.serialization JSON library for object decoding.
      implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:LATEST_VERSION")
   }
   ```

   > [!NOTE]
   > **Note:** The KSP processor and strongly-typed `generateObject()` APIs on this page are designed for Kotlin.  
   > For Java apps, to generate structured output with on-device or hybrid inference, we recommend writing your AI model interactions in Kotlin. Alternatively, for cloud-only inference, you can use standard [`generateContent()`](https://firebase.google.com/docs/ai-logic/generate-structured-output.md.txt#generate-json-basic) with cloud-hosted models to generate JSON text and parse the response with your preferred JSON library (such as Gson or Moshi).

<br />

[Jump to JSON output](https://firebase.google.com/docs/ai-logic/hybrid/android/generate-structured-output.md.txt#json-output)
[Jump to enum output](https://firebase.google.com/docs/ai-logic/hybrid/android/generate-structured-output.md.txt#enum-output)

## JSON output

The following examples adapt the
[general JSON output example](https://firebase.google.com/docs/ai-logic/generate-structured-output.md.txt#generate-json-basic)
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 (`@Generable` and `@Guide`)**](https://firebase.google.com/docs/ai-logic/hybrid/android/generate-structured-output.md.txt#json-output-ksp)

  - Supported for *all* inference modes, and *required* for on-device and hybrid inference (specifically, `ONLY_ON_DEVICE`, `PREFER_ON_DEVICE`, and `PREFER_IN_CLOUD`).
  - Define Kotlin data classes to automatically generate schemas at compile time and deserialize responses directly into strongly-typed objects using `getObject()`.
- [**Manual `JsonSchema` helper methods**](https://firebase.google.com/docs/ai-logic/hybrid/android/generate-structured-output.md.txt#json-output-helpers)

  - Supported *only* for cloud-based inference (specifically, `ONLY_IN_CLOUD`).
  - Manually construct a `JsonSchema` in code without using the KSP processor, and read the raw JSON string from `response.response.text`.

### 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`).

> [!IMPORTANT]
> **Important:** Declare an empty `companion object` in the data class so that KSP can generate the `firebaseAISchema()` extension method.


|---|
| *Before trying this sample, complete the [Before you begin](https://firebase.google.com/docs/ai-logic/hybrid/android/generate-structured-output.md.txt#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**.* |

<br />

^*For Kotlin, the methods in this SDK are suspend functions and need to be called
from a [Coroutine scope](https://developer.android.com/kotlin/coroutines).*^

```kotlin
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`](https://firebase.google.com/docs/reference/kotlin/com/google/firebase/ai/type/JsonSchema.md.txt)
using helper methods provided by the Firebase AI Logic SDK.


|---|
| *Before trying this sample, complete the [Before you begin](https://firebase.google.com/docs/ai-logic/hybrid/android/generate-structured-output.md.txt#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**.* |

<br />

^*For Kotlin, the methods in this SDK are suspend functions and need to be called
from a [Coroutine scope](https://developer.android.com/kotlin/coroutines).*^

```kotlin
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](https://firebase.google.com/docs/ai-logic/generate-structured-output.md.txt#generate-enum-basic)
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:**

- [**KSP annotations (`@Generable`)**](https://firebase.google.com/docs/ai-logic/hybrid/android/generate-structured-output.md.txt#enum-output-ksp)

  - Supported for *all* inference modes, and *required* for on-device and hybrid inference (specifically, `ONLY_ON_DEVICE`, `PREFER_ON_DEVICE`, and `PREFER_IN_CLOUD`).
  - Define an enum wrapped in a Kotlin `data class` to automatically generate the schema and deserialize responses directly into strongly-typed objects using `getObject()`.
- [**Manual `JsonSchema` helper methods**](https://firebase.google.com/docs/ai-logic/hybrid/android/generate-structured-output.md.txt#enum-output-helpers)

  - Supported *only* for cloud-based inference (specifically, `ONLY_IN_CLOUD`).
  - Manually build an enum `JsonSchema` using `JsonSchema.enumeration()` without using the KSP processor, and read the selected string from `response.response.text`.

### 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`).

> [!IMPORTANT]
> **Important:** Declare an empty `companion object` in the data class so that KSP can generate the `firebaseAISchema()` extension method.


|---|
| *Before trying this sample, complete the [Before you begin](https://firebase.google.com/docs/ai-logic/hybrid/android/generate-structured-output.md.txt#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**.* |

<br />

^*For Kotlin, the methods in this SDK are suspend functions and need to be called
from a [Coroutine scope](https://developer.android.com/kotlin/coroutines).*^

```kotlin
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`](https://firebase.google.com/docs/reference/kotlin/com/google/firebase/ai/type/JsonSchema.md.txt)
using helper methods provided by the Firebase AI Logic SDK.

> [!NOTE]
> **Note:** On-device inference does *not* support manual schemas or root-level enum schemas, so you must use KSP annotations (`@Generable`) on a `data class` that wraps the enum.


|---|
| *Before trying this sample, complete the [Before you begin](https://firebase.google.com/docs/ai-logic/hybrid/android/generate-structured-output.md.txt#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**.* |

<br />

^*For Kotlin, the methods in this SDK are suspend functions and need to be called
from a [Coroutine scope](https://developer.android.com/kotlin/coroutines).*^

```kotlin
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)
```

<br />

[Give feedback
about your experience with Firebase AI Logic](https://firebase.google.com/docs/ai-logic/feedback)

<br />