@@ -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
)
@@ -483,6 +489,73 @@ struct GenerateContentIntegrationTests {
483
489
#expect( response == expectedResponse)
484
490
}
485
491
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
+
486
559
// MARK: - App Check Tests
487
560
488
561
@Test ( arguments: InstanceConfig . appCheckNotConfiguredConfigs)
0 commit comments