@@ -322,14 +322,20 @@ struct GenerateContentIntegrationTests {
322322 }
323323
324324 @Test ( arguments: [
325- InstanceConfig . vertexAI_v1beta,
326- InstanceConfig . vertexAI_v1beta_global,
327- InstanceConfig . googleAI_v1beta,
325+ ( InstanceConfig . vertexAI_v1beta, ModelNames . gemini2FlashPreviewImageGeneration) ,
326+ ( InstanceConfig . vertexAI_v1beta_global, ModelNames . gemini2FlashPreviewImageGeneration) ,
327+ ( InstanceConfig . vertexAI_v1beta_global, ModelNames . gemini2_5_FlashImagePreview) ,
328+ ( InstanceConfig . googleAI_v1beta, ModelNames . gemini2FlashPreviewImageGeneration) ,
329+ ( InstanceConfig . googleAI_v1beta, ModelNames . gemini2_5_FlashImagePreview) ,
328330 // Note: The following configs are commented out for easy one-off manual testing.
329- // InstanceConfig.googleAI_v1beta_staging,
330- // InstanceConfig.googleAI_v1beta_freeTier_bypassProxy,
331+ // (InstanceConfig.googleAI_v1beta_staging, ModelNames.gemini2FlashPreviewImageGeneration)
332+ // (InstanceConfig.googleAI_v1beta_freeTier, ModelNames.gemini2FlashPreviewImageGeneration),
333+ // (
334+ // InstanceConfig.googleAI_v1beta_freeTier_bypassProxy,
335+ // ModelNames.gemini2FlashPreviewImageGeneration
336+ // ),
331337 ] )
332- func generateImage( _ config: InstanceConfig ) async throws {
338+ func generateImage( _ config: InstanceConfig , modelName : String ) async throws {
333339 let generationConfig = GenerationConfig (
334340 temperature: 0.0 ,
335341 topP: 0.0 ,
@@ -342,7 +348,7 @@ struct GenerateContentIntegrationTests {
342348 $0. harmCategory != . civicIntegrity
343349 }
344350 let model = FirebaseAI . componentInstance ( config) . generativeModel (
345- modelName: ModelNames . gemini2FlashPreviewImageGeneration ,
351+ modelName: modelName ,
346352 generationConfig: generationConfig,
347353 safetySettings: safetySettings
348354 )
@@ -483,6 +489,73 @@ struct GenerateContentIntegrationTests {
483489 #expect( response == expectedResponse)
484490 }
485491
492+ @Test ( arguments: [
493+ ( InstanceConfig . vertexAI_v1beta, ModelNames . gemini2FlashPreviewImageGeneration) ,
494+ ( InstanceConfig . vertexAI_v1beta_global, ModelNames . gemini2FlashPreviewImageGeneration) ,
495+ ( InstanceConfig . vertexAI_v1beta_global, ModelNames . gemini2_5_FlashImagePreview) ,
496+ ( InstanceConfig . googleAI_v1beta, ModelNames . gemini2FlashPreviewImageGeneration) ,
497+ ( InstanceConfig . googleAI_v1beta, ModelNames . gemini2_5_FlashImagePreview) ,
498+ // Note: The following configs are commented out for easy one-off manual testing.
499+ // (InstanceConfig.googleAI_v1beta_staging, ModelNames.gemini2FlashPreviewImageGeneration)
500+ // (InstanceConfig.googleAI_v1beta_freeTier, ModelNames.gemini2FlashPreviewImageGeneration),
501+ // (
502+ // InstanceConfig.googleAI_v1beta_freeTier_bypassProxy,
503+ // ModelNames.gemini2FlashPreviewImageGeneration
504+ // ),
505+ ] )
506+ func generateImageStreaming( _ config: InstanceConfig , modelName: String ) async throws {
507+ let generationConfig = GenerationConfig (
508+ temperature: 0.0 ,
509+ topP: 0.0 ,
510+ topK: 1 ,
511+ responseModalities: [ . text, . image]
512+ )
513+ let safetySettings = safetySettings. filter {
514+ // HARM_CATEGORY_CIVIC_INTEGRITY is deprecated in Vertex AI but only rejected when using the
515+ // 'gemini-2.0-flash-preview-image-generation' model.
516+ $0. harmCategory != . civicIntegrity
517+ }
518+ let model = FirebaseAI . componentInstance ( config) . generativeModel (
519+ modelName: modelName,
520+ generationConfig: generationConfig,
521+ safetySettings: safetySettings
522+ )
523+ let prompt = " Generate an image of a cute cartoon kitten playing with a ball of yarn "
524+
525+ let stream = try model. generateContentStream ( prompt)
526+
527+ var inlineDataParts = [ InlineDataPart] ( )
528+ for try await response in stream {
529+ let candidate = try #require( response. candidates. first)
530+ let inlineDataPart = candidate. content. parts. first { $0 is InlineDataPart } as? InlineDataPart
531+ if let inlineDataPart {
532+ inlineDataParts. append ( inlineDataPart)
533+ let inlineDataPartsViaAccessor = response. inlineDataParts
534+ #expect( inlineDataPartsViaAccessor. count == 1 )
535+ #expect( inlineDataPartsViaAccessor == response. inlineDataParts)
536+ }
537+ let textPart = candidate. content. parts. first { $0 is TextPart } as? TextPart
538+ #expect(
539+ inlineDataPart != nil || textPart != nil || candidate. finishReason == . stop,
540+ " No text or image found in the candidate "
541+ )
542+ }
543+
544+ #expect( inlineDataParts. count == 1 )
545+ let inlineDataPart = try #require( inlineDataParts. first)
546+ #expect( inlineDataPart. mimeType == " image/png " )
547+ #expect( inlineDataPart. data. count > 0 )
548+ #if canImport(UIKit)
549+ let uiImage = try #require( UIImage ( data: inlineDataPart. data) )
550+ // Gemini 2.0 Flash Experimental returns images sized to fit within a 1024x1024 pixel box but
551+ // dimensions may vary depending on the aspect ratio.
552+ #expect( uiImage. size. width <= 1024 )
553+ #expect( uiImage. size. width >= 500 )
554+ #expect( uiImage. size. height <= 1024 )
555+ #expect( uiImage. size. height >= 500 )
556+ #endif // canImport(UIKit)
557+ }
558+
486559 // MARK: - App Check Tests
487560
488561 @Test ( arguments: InstanceConfig . appCheckNotConfiguredConfigs)
0 commit comments