@@ -322,14 +322,20 @@ struct GenerateContentIntegrationTests {
322
322
}
323
323
324
324
@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) ,
328
330
// 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
+ // ),
331
337
] )
332
- func generateImage( _ config: InstanceConfig ) async throws {
338
+ func generateImage( _ config: InstanceConfig , modelName : String ) async throws {
333
339
let generationConfig = GenerationConfig (
334
340
temperature: 0.0 ,
335
341
topP: 0.0 ,
@@ -342,7 +348,7 @@ struct GenerateContentIntegrationTests {
342
348
$0. harmCategory != . civicIntegrity
343
349
}
344
350
let model = FirebaseAI . componentInstance ( config) . generativeModel (
345
- modelName: ModelNames . gemini2FlashPreviewImageGeneration ,
351
+ modelName: modelName ,
346
352
generationConfig: generationConfig,
347
353
safetySettings: safetySettings
348
354
)
@@ -511,6 +517,73 @@ struct GenerateContentIntegrationTests {
511
517
#expect( response == expectedResponse)
512
518
}
513
519
520
+ @Test ( arguments: [
521
+ ( InstanceConfig . vertexAI_v1beta, ModelNames . gemini2FlashPreviewImageGeneration) ,
522
+ ( InstanceConfig . vertexAI_v1beta_global, ModelNames . gemini2FlashPreviewImageGeneration) ,
523
+ ( InstanceConfig . vertexAI_v1beta_global, ModelNames . gemini2_5_FlashImagePreview) ,
524
+ ( InstanceConfig . googleAI_v1beta, ModelNames . gemini2FlashPreviewImageGeneration) ,
525
+ ( InstanceConfig . googleAI_v1beta, ModelNames . gemini2_5_FlashImagePreview) ,
526
+ // Note: The following configs are commented out for easy one-off manual testing.
527
+ // (InstanceConfig.googleAI_v1beta_staging, ModelNames.gemini2FlashPreviewImageGeneration)
528
+ // (InstanceConfig.googleAI_v1beta_freeTier, ModelNames.gemini2FlashPreviewImageGeneration),
529
+ // (
530
+ // InstanceConfig.googleAI_v1beta_freeTier_bypassProxy,
531
+ // ModelNames.gemini2FlashPreviewImageGeneration
532
+ // ),
533
+ ] )
534
+ func generateImageStreaming( _ config: InstanceConfig , modelName: String ) async throws {
535
+ let generationConfig = GenerationConfig (
536
+ temperature: 0.0 ,
537
+ topP: 0.0 ,
538
+ topK: 1 ,
539
+ responseModalities: [ . text, . image]
540
+ )
541
+ let safetySettings = safetySettings. filter {
542
+ // HARM_CATEGORY_CIVIC_INTEGRITY is deprecated in Vertex AI but only rejected when using the
543
+ // 'gemini-2.0-flash-preview-image-generation' model.
544
+ $0. harmCategory != . civicIntegrity
545
+ }
546
+ let model = FirebaseAI . componentInstance ( config) . generativeModel (
547
+ modelName: modelName,
548
+ generationConfig: generationConfig,
549
+ safetySettings: safetySettings
550
+ )
551
+ let prompt = " Generate an image of a cute cartoon kitten playing with a ball of yarn "
552
+
553
+ let stream = try model. generateContentStream ( prompt)
554
+
555
+ var inlineDataParts = [ InlineDataPart] ( )
556
+ for try await response in stream {
557
+ let candidate = try #require( response. candidates. first)
558
+ let inlineDataPart = candidate. content. parts. first { $0 is InlineDataPart } as? InlineDataPart
559
+ if let inlineDataPart {
560
+ inlineDataParts. append ( inlineDataPart)
561
+ let inlineDataPartsViaAccessor = response. inlineDataParts
562
+ #expect( inlineDataPartsViaAccessor. count == 1 )
563
+ #expect( inlineDataPartsViaAccessor == response. inlineDataParts)
564
+ }
565
+ let textPart = candidate. content. parts. first { $0 is TextPart } as? TextPart
566
+ #expect(
567
+ inlineDataPart != nil || textPart != nil || candidate. finishReason == . stop,
568
+ " No text or image found in the candidate "
569
+ )
570
+ }
571
+
572
+ #expect( inlineDataParts. count == 1 )
573
+ let inlineDataPart = try #require( inlineDataParts. first)
574
+ #expect( inlineDataPart. mimeType == " image/png " )
575
+ #expect( inlineDataPart. data. count > 0 )
576
+ #if canImport(UIKit)
577
+ let uiImage = try #require( UIImage ( data: inlineDataPart. data) )
578
+ // Gemini 2.0 Flash Experimental returns images sized to fit within a 1024x1024 pixel box but
579
+ // dimensions may vary depending on the aspect ratio.
580
+ #expect( uiImage. size. width <= 1024 )
581
+ #expect( uiImage. size. width >= 500 )
582
+ #expect( uiImage. size. height <= 1024 )
583
+ #expect( uiImage. size. height >= 500 )
584
+ #endif // canImport(UIKit)
585
+ }
586
+
514
587
// MARK: - App Check Tests
515
588
516
589
@Test ( arguments: InstanceConfig . appCheckNotConfiguredConfigs)
0 commit comments