diff --git a/FirebaseAI/Tests/TestApp/Tests/Integration/GenerativeModelSessionTests.swift b/FirebaseAI/Tests/TestApp/Tests/Integration/GenerativeModelSessionTests.swift index 16c590e5f37..d583486b672 100644 --- a/FirebaseAI/Tests/TestApp/Tests/Integration/GenerativeModelSessionTests.swift +++ b/FirebaseAI/Tests/TestApp/Tests/Integration/GenerativeModelSessionTests.swift @@ -97,5 +97,98 @@ #expect(catProfile.age <= 20) #expect(!catProfile.profile.isEmpty) } + + @Generable + @available(iOS 26.0, macOS 26.0, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + enum Difficulty: String, Codable, CaseIterable { + case easy + case medium + case hard + } + + @Generable(description: "A recipe for a delicious dish") + @available(iOS 26.0, macOS 26.0, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + struct Recipe { + @Guide(description: "The name of the dish") + var name: String + + @Guide(description: "The time it takes to prepare the dish in minutes", .range(5 ... 120)) + 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 + } + + @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) + + let recipeList = response.content + #expect(recipeList.recipes.count == 3) + for recipe in recipeList.recipes { + #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) + } + } } #endif // compiler(>=6.2) && canImport(FoundationModels)