Repository: firebase/firebase-ios-sdk
File: FirebaseAI/Tests/TestApp/Tests/Integration/GenerativeModelSessionTests.swift (Swift)

Code before:

      @Guide(description: "The difficulty of the recipe")
      var difficulty: Difficulty
    }

    @Generable(description: "A list of recipes")
    @available(iOS 26.0, macOS 26.0, *)
    @available(tvOS, unavailable)
    @available(watchOS, unavailable)
    struct RecipeList {
      @Guide(description: "A list of recipes for a three-course meal.")
      var recipes: [Recipe]
    }

    @Test(arguments: [InstanceConfig.vertexAI_v1beta_global])
    @available(iOS 26.0, macOS 26.0, *)
    @available(tvOS, unavailable)
    @available(watchOS, unavailable)
    func respondGenerableRecipe(_ config: InstanceConfig) async throws {
      let model = FirebaseAI.componentInstance(config).generativeModel(
        modelName: ModelNames.gemini2_5_FlashLite,
      )
      let session = GenerativeModelSession(model: model)
      let prompt = "Generate a recipe for a pasta dish with meat."

      let response = try await session.respond(to: prompt, generating: Recipe.self)

      let recipe = response.content
      #expect(!recipe.name.isEmpty)
      #expect(recipe.preparationTime >= 5)
      #expect(recipe.preparationTime <= 120)
      #expect(!recipe.isVegetarian)
      #expect(recipe.rating >= 1.0)
      #expect(recipe.rating <= 5.0)
      #expect(!recipe.ingredients.isEmpty)
    }

    @Test(arguments: [InstanceConfig.vertexAI_v1beta_global])
    @available(iOS 26.0, macOS 26.0, *)
    @available(tvOS, unavailable)
    @available(watchOS, unavailable)
    func respondGenerableRecipeList(_ config: InstanceConfig) async throws {
      let model = FirebaseAI.componentInstance(config).generativeModel(
        modelName: ModelNames.gemini2_5_FlashLite,
      )
      let session = GenerativeModelSession(model: model)
      let prompt =
        "Generate three recipes for a full-course vegetarian meal (appetizer, main, dessert)."

      let response = try await session.respond(to: prompt, generating: RecipeList.self)


Review comment (suggestion):
Totally optional but it could be interesting to add a guide for the count in `RecipeList` in a follow-up PR:
```suggestion
    struct RecipeList {
      @Guide(description: "A list of recipes for a three-course meal.", .count(3))
      var recipes: [Recipe]
    }
```
and/or an optional `enum SuggestedCourse { case appetizer, main, dessert }` in `Recipe`. In `respondGenerableRecipeList` we could check that it picks a non-nil value and has one of each of appetizer, main and dessert.

Code after:
      var preparationTime: Int

      @Guide(description: "Whether the dish is vegetarian")
      var isVegetarian: Bool

      @Guide(description: "The rating of the dish from 1.0 to 5.0", .range(1.0 ... 5.0))
      var rating: Double

      @Guide(description: "A list of ingredients")
      var ingredients: [String]

      @Guide(description: "The difficulty of the recipe")
      var difficulty: Difficulty

      @Guide(description: "The course of the dish, such as appetizer, main, or dessert.")
      var course: SuggestedCourse
    }

    @Generable(description: "A list of recipes")
    @available(iOS 26.0, macOS 26.0, *)
    @available(tvOS, unavailable)
    @available(watchOS, unavailable)
    struct RecipeList {
      @Guide(description: "A list of recipes for a three-course meal.", .count(3))
      var recipes: [Recipe]
    }

    @Test(arguments: [InstanceConfig.vertexAI_v1beta_global])
    @available(iOS 26.0, macOS 26.0, *)
    @available(tvOS, unavailable)
    @available(watchOS, unavailable)
    func respondGenerableRecipe(_ config: InstanceConfig) async throws {
      let model = FirebaseAI.componentInstance(config).generativeModel(
        modelName: ModelNames.gemini2_5_FlashLite,
      )
      let session = GenerativeModelSession(model: model)
      let prompt = "Generate a recipe for a pasta dish with meat."

      let response = try await session.respond(to: prompt, generating: Recipe.self)

      let recipe = response.content
      #expect(!recipe.name.isEmpty)
      #expect(recipe.preparationTime >= 5)
      #expect(recipe.preparationTime <= 120)
      #expect(!recipe.isVegetarian)
      #expect(recipe.rating >= 1.0)
      #expect(recipe.rating <= 5.0)
      #expect(!recipe.ingredients.isEmpty)
      #expect([.appetizer, .main, .dessert].contains(recipe.course))
    }