diff --git a/packages/firebase_ai/firebase_ai/example/lib/main.dart b/packages/firebase_ai/firebase_ai/example/lib/main.dart index 133f2c6ec257..69f37490f6ba 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/main.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/main.dart @@ -26,7 +26,6 @@ import 'pages/chat_page.dart'; import 'pages/document.dart'; import 'pages/function_calling_page.dart'; import 'pages/image_prompt_page.dart'; -import 'pages/imagen_page.dart'; import 'pages/json_schema_page.dart'; import 'pages/schema_page.dart'; import 'pages/token_count_page.dart'; @@ -53,7 +52,6 @@ class GenerativeAISample extends StatefulWidget { class _GenerativeAISampleState extends State { bool _useVertexBackend = false; late GenerativeModel _currentModel; - late ImagenModel _currentImagenModel; static final ThemeData _darkTheme = ThemeData( colorScheme: ColorScheme.fromSeed( @@ -74,30 +72,12 @@ class _GenerativeAISampleState extends State { if (useVertexBackend) { final vertexInstance = FirebaseAI.vertexAI(auth: FirebaseAuth.instance); _currentModel = vertexInstance.generativeModel(model: 'gemini-2.5-flash'); - _currentImagenModel = _initializeImagenModel(vertexInstance); } else { final googleAI = FirebaseAI.googleAI(auth: FirebaseAuth.instance); _currentModel = googleAI.generativeModel(model: 'gemini-2.5-flash'); - _currentImagenModel = _initializeImagenModel(googleAI); } } - ImagenModel _initializeImagenModel(FirebaseAI instance) { - var generationConfig = ImagenGenerationConfig( - numberOfImages: 1, - aspectRatio: ImagenAspectRatio.square1x1, - imageFormat: ImagenFormat.jpeg(compressionQuality: 75), - ); - return instance.imagenModel( - model: 'imagen-3.0-capability-001', - generationConfig: generationConfig, - safetySettings: ImagenSafetySettings( - ImagenSafetyFilterLevel.blockLowAndAbove, - ImagenPersonFilterLevel.allowAdult, - ), - ); - } - void _toggleBackend(bool value) { setState(() { _useVertexBackend = value; @@ -117,7 +97,6 @@ class _GenerativeAISampleState extends State { '${_useVertexBackend}_${_currentModel.hashCode}', ), model: _currentModel, - imagenModel: _currentImagenModel, useVertexBackend: _useVertexBackend, onBackendChanged: _toggleBackend, ), @@ -127,14 +106,12 @@ class _GenerativeAISampleState extends State { class HomeScreen extends StatefulWidget { final GenerativeModel model; - final ImagenModel imagenModel; final bool useVertexBackend; final ValueChanged onBackendChanged; const HomeScreen({ super.key, required this.model, - required this.imagenModel, required this.useVertexBackend, required this.onBackendChanged, }); @@ -156,7 +133,6 @@ class _HomeScreenState extends State { Widget _buildSelectedPage( int index, GenerativeModel currentModel, - ImagenModel currentImagenModel, bool useVertexBackend, ) { switch (index) { @@ -178,22 +154,20 @@ class _HomeScreenState extends State { case 4: return ImagePromptPage(title: 'Image Prompt', model: currentModel); case 5: - return ImagenPage(title: 'Imagen Model', model: currentImagenModel); - case 6: return SchemaPromptPage(title: 'Schema Prompt', model: currentModel); - case 7: + case 6: return JsonSchemaPage(title: 'JSON Schema', model: currentModel); - case 8: + case 7: return DocumentPage(title: 'Document Prompt', model: currentModel); - case 9: + case 8: return VideoPage(title: 'Video Prompt', model: currentModel); - case 10: + case 9: return BidiPage( title: 'Live Stream', model: currentModel, useVertexBackend: useVertexBackend, ); - case 11: + case 10: return ServerTemplatePage( title: 'Server Template', useVertexBackend: useVertexBackend, @@ -259,7 +233,6 @@ class _HomeScreenState extends State { child: _buildSelectedPage( _selectedIndex, widget.model, - widget.imagenModel, widget.useVertexBackend, ), ), @@ -297,11 +270,6 @@ class _HomeScreenState extends State { label: 'Image', tooltip: 'Image Prompt', ), - BottomNavigationBarItem( - icon: Icon(Icons.image_search), - label: 'Imagen', - tooltip: 'Imagen Model', - ), BottomNavigationBarItem( icon: Icon(Icons.schema), label: 'Schema', diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/imagen_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/imagen_page.dart deleted file mode 100644 index 390279c1ffd2..000000000000 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/imagen_page.dart +++ /dev/null @@ -1,544 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:typed_data'; - -import 'package:image_picker/image_picker.dart'; -import 'package:firebase_ai/firebase_ai.dart'; - -import 'package:flutter/material.dart'; -//import 'package:firebase_storage/firebase_storage.dart'; -import '../widgets/message_widget.dart'; -import '../utils/image_utils.dart'; - -class ImagenPage extends StatefulWidget { - const ImagenPage({ - super.key, - required this.title, - required this.model, - }); - - final String title; - final ImagenModel model; - - @override - State createState() => _ImagenPageState(); -} - -class _ImagenPageState extends State { - final ScrollController _scrollController = ScrollController(); - final TextEditingController _textController = TextEditingController(); - final FocusNode _textFieldFocus = FocusNode(); - final List _generatedContent = []; - bool _loading = false; - - // For image picking - ImagenInlineImage? _sourceImage; - ImagenInlineImage? _maskImageForEditing; - - void _scrollDown() { - WidgetsBinding.instance.addPostFrameCallback( - (_) => _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: const Duration( - milliseconds: 750, - ), - curve: Curves.easeOutCirc, - ), - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Padding( - padding: const EdgeInsets.all(8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: ListView.builder( - controller: _scrollController, - itemBuilder: (context, idx) { - return MessageWidget( - text: _generatedContent[idx].text, - image: Image.memory( - _generatedContent[idx].imageBytes!, - cacheWidth: 400, - cacheHeight: 400, - ), - isFromUser: _generatedContent[idx].fromUser ?? false, - ); - }, - itemCount: _generatedContent.length, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, - horizontal: 15, - ), - child: Column( - children: [ - // Generate Image Row - Row( - children: [ - Expanded( - child: TextField( - autofocus: true, - focusNode: _textFieldFocus, - decoration: const InputDecoration( - hintText: 'Enter a prompt...', - ), - controller: _textController, - ), - ), - const SizedBox.square(dimension: 15), - IconButton( - onPressed: () async { - await _pickSourceImage(); - }, - icon: Icon( - Icons.add_a_photo, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Pick Source Image', - ), - IconButton( - onPressed: () async { - await _pickMaskImage(); - }, - icon: Icon( - Icons.add_to_photos, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Pick mask', - ), - IconButton( - onPressed: () async { - await _editWithMask(); - }, - icon: Icon( - Icons.brush, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Edit with Mask', - ), - IconButton( - onPressed: () async { - await _editWithStyle(); - }, - icon: Icon( - Icons.edit, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Edit with Style', - ), - IconButton( - onPressed: () async { - await _outpaintImage(); - }, - icon: Icon( - Icons.masks, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Outpaint', - ), - IconButton( - onPressed: () async { - await _inpaintImageHappyPath(); - }, - icon: Icon( - Icons.plus_one, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Inpaint', - ), - if (!_loading) - IconButton( - onPressed: () async { - await _generateImageFromPrompt( - _textController.text, - ); - }, - icon: Icon( - Icons.image_search, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Generate Image', - ) - else - const CircularProgressIndicator(), - ], - ), - ], - ), - ), - ], - ), - ), - ); - } - - Future _pickImage() async { - final ImagePicker picker = ImagePicker(); - try { - final XFile? imageFile = - await picker.pickImage(source: ImageSource.gallery); - if (imageFile != null) { - // Attempt to get mimeType, default if null. - // Note: imageFile.mimeType might be null on some platforms or for some files. - final String mimeType = imageFile.mimeType ?? 'image/jpeg'; - final Uint8List imageBytes = await imageFile.readAsBytes(); - return ImagenInlineImage( - bytesBase64Encoded: imageBytes, - mimeType: mimeType, - ); - } - } catch (e) { - _showError('Error picking image: $e'); - } - return null; - } - - Future _pickSourceImage() async { - final pickedImage = await _pickImage(); - if (pickedImage != null) { - setState(() { - _sourceImage = pickedImage; - }); - } - } - - Future _pickMaskImage() async { - final pickedImage = await _pickImage(); - if (pickedImage != null) { - setState(() { - _maskImageForEditing = pickedImage; - }); - } - } - - Future _inpaintImageHappyPath() async { - if (_sourceImage == null) { - _showError('Please pick a source image for inpaint insertion.'); - return; - } - setState(() { - _loading = true; - }); - - final String prompt = _textController.text; - final promptMessage = MessageData( - imageBytes: _sourceImage!.bytesBase64Encoded, - text: 'Try to inpaint image with prompt: $prompt', - fromUser: true, - ); - - MessageData? resultMessage; - - try { - // ignore: experimental_member_use - final response = await widget.model.inpaintImage( - _sourceImage!, - prompt, - // ignore: experimental_member_use - ImagenBackgroundMask(), - // ignore: experimental_member_use - config: ImagenEditingConfig(editMode: ImagenEditMode.inpaintInsertion), - ); - if (response.images.isNotEmpty) { - final inpaintImage = response.images[0]; - resultMessage = MessageData( - imageBytes: inpaintImage.bytesBase64Encoded, - text: 'Inpaint image result with prompt: $prompt', - fromUser: false, - ); - } else { - _showError('No image was returned from inpaint.'); - } - } catch (e) { - _showError('Error inpaint image: $e'); - } - - setState(() { - _generatedContent.add(promptMessage); - if (resultMessage != null) { - _generatedContent.add(resultMessage); - } - _loading = false; - _scrollDown(); - }); - } - - Future _editWithMask() async { - if (_sourceImage == null) { - _showError('Please pick a source image for editing.'); - return; - } - if (_maskImageForEditing == null) { - _showError('Please pick a mask image for editing.'); - return; - } - - setState(() { - _loading = true; - }); - - final String prompt = _textController.text; - // Create a message to show what we are doing - final promptMessage = MessageData( - imageBytes: _sourceImage!.bytesBase64Encoded, - text: 'Editing image with mask and prompt: $prompt', - fromUser: true, - ); - - MessageData? resultMessage; - - try { - // ignore: experimental_member_use - final response = await widget.model.editImage( - [ - // ignore: experimental_member_use - ImagenRawImage(image: _sourceImage!), - // ignore: experimental_member_use - ImagenRawMask(mask: _maskImageForEditing!), - ], - prompt, - ); - - if (response.images.isNotEmpty) { - final editedImage = response.images[0]; - resultMessage = MessageData( - imageBytes: editedImage.bytesBase64Encoded, - text: 'Edited image result with prompt: $prompt', - fromUser: false, - ); - } else { - _showError('No image was returned from editing with mask.'); - } - } catch (e) { - _showError('Error editing image with mask: $e'); - } - - setState(() { - _generatedContent.add(promptMessage); - if (resultMessage != null) { - _generatedContent.add(resultMessage); - } - _loading = false; - _scrollDown(); - }); - } - - Future _outpaintImage() async { - if (_sourceImage == null) { - _showError('Please pick a source image for outpainting.'); - return; - } - setState(() { - _loading = true; - }); - - final promptMessage = MessageData( - imageBytes: _sourceImage!.bytesBase64Encoded, - text: 'Outpaint the picture to 1400*1400', - fromUser: true, - ); - - MessageData? resultMessage; - try { - final referenceImages = await generateMaskAndPadForOutpainting( - image: _sourceImage!, - // ignore: experimental_member_use - newDimensions: ImagenDimensions(width: 1400, height: 1400), - ); - // ignore: experimental_member_use - final response = await widget.model.editImage( - referenceImages, - '', - // ignore: experimental_member_use - config: ImagenEditingConfig(editMode: ImagenEditMode.outpaint), - ); - if (response.images.isNotEmpty) { - final editedImage = response.images[0]; - resultMessage = MessageData( - imageBytes: editedImage.bytesBase64Encoded, - text: 'Edited image Outpaint 1400*1400', - fromUser: false, - ); - } else { - _showError('No image was returned from editing.'); - } - } catch (e) { - _showError('Error editing image: $e'); - } - - setState(() { - _generatedContent.add(promptMessage); - if (resultMessage != null) { - _generatedContent.add(resultMessage); - } - _loading = false; - _scrollDown(); - }); - } - - Future _editWithStyle() async { - if (_sourceImage == null) { - _showError('Please pick a source image for style editing.'); - return; - } - setState(() { - _loading = true; - }); - - final String prompt = _textController.text; - final promptMessage = MessageData( - imageBytes: _sourceImage!.bytesBase64Encoded, - text: prompt, - fromUser: true, - ); - MessageData? resultMessage; - try { - // ignore: experimental_member_use - final response = await widget.model.editImage( - [ - // ignore: experimental_member_use - ImagenStyleReference( - image: _sourceImage!, - description: 'van goh style', - referenceId: 1, - ), - ], - prompt, - // ignore: experimental_member_use - config: ImagenEditingConfig(editSteps: 50), - ); - if (response.images.isNotEmpty) { - final editedImage = response.images[0]; - - resultMessage = MessageData( - imageBytes: editedImage.bytesBase64Encoded, - text: 'Edited image with style: $prompt', - fromUser: false, - ); - } else { - _showError('No image was returned from style editing.'); - } - } catch (e) { - _showError('Error performing style edit: $e'); - } - - setState(() { - _generatedContent.add(promptMessage); - if (resultMessage != null) { - _generatedContent.add(resultMessage); - } - - _loading = false; - _scrollDown(); - }); - } - - Future _generateImageFromPrompt(String prompt) async { - setState(() { - _loading = true; - }); - MessageData? resultMessage; - try { - var response = await widget.model.generateImages(prompt); - - if (response.images.isNotEmpty) { - var imagenImage = response.images[0]; - - resultMessage = MessageData( - imageBytes: imagenImage.bytesBase64Encoded, - text: prompt, - fromUser: false, - ); - } else { - // Handle the case where no images were generated - _showError('Error: No images were generated.'); - } - } catch (e) { - _showError(e.toString()); - } - - setState(() { - if (resultMessage != null) { - _generatedContent.add(resultMessage); - } - - _loading = false; - _scrollDown(); - }); - } - // NOTE: Keep this API private until future release. - // Future _testImagenGCS(String prompt) async { - // setState(() { - // _loading = true; - // }); - // var gcsUrl = 'gs://vertex-ai-example-ef5a2.appspot.com/imagen'; - - // var response = await widget.model.generateImagesGCS(prompt, gcsUrl); - - // if (response.images.isNotEmpty) { - // var imagenImage = response.images[0]; - // final returnImageUri = imagenImage.gcsUri; - // final reference = FirebaseStorage.instance.refFromURL(returnImageUri); - // final downloadUrl = await reference.getDownloadURL(); - // // Process the image - // _generatedContent.add( - // MessageData( - // image: Image(image: NetworkImage(downloadUrl)), - // text: prompt, - // fromUser: false, - // ), - // ); - // } else { - // // Handle the case where no images were generated - // _showError('Error: No images were generated.'); - // } - // setState(() { - // _loading = false; - // }); - // } - - void _showError(String message) { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('Something went wrong'), - content: SingleChildScrollView( - child: SelectableText(message), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text('OK'), - ), - ], - ); - }, - ); - } -} diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/server_template_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/server_template_page.dart index 1be4e6d11ffe..10386f3e4d7b 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/server_template_page.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/server_template_page.dart @@ -40,8 +40,6 @@ class _ServerTemplatePageState extends State { // ignore: experimental_member_use TemplateGenerativeModel? _templateGenerativeModel; - // ignore: experimental_member_use - TemplateImagenModel? _templateImagenModel; @override void initState() { @@ -54,16 +52,10 @@ class _ServerTemplatePageState extends State { _templateGenerativeModel = // ignore: experimental_member_use FirebaseAI.vertexAI(location: 'global').templateGenerativeModel(); - _templateImagenModel = - // ignore: experimental_member_use - FirebaseAI.vertexAI(location: 'global').templateImagenModel(); } else { _templateGenerativeModel = // ignore: experimental_member_use FirebaseAI.googleAI().templateGenerativeModel(); - _templateImagenModel = - // ignore: experimental_member_use - FirebaseAI.googleAI().templateImagenModel(); } } @@ -130,16 +122,6 @@ class _ServerTemplatePageState extends State { dimension: 15, ), if (!_loading) ...[ - IconButton( - onPressed: () async { - await _serverTemplateImagen(_textController.text); - }, - icon: Icon( - Icons.image_search, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Imagen', - ), IconButton( onPressed: () async { await _serverTemplateImageInput(_textController.text); @@ -254,55 +236,6 @@ class _ServerTemplatePageState extends State { } } - Future _serverTemplateImagen(String message) async { - setState(() { - _loading = true; - }); - MessageData? resultMessage; - try { - _messages.add(MessageData(text: message, fromUser: true)); - // ignore: experimental_member_use - var response = await _templateImagenModel?.generateImages( - 'portrait-googleai', - inputs: { - 'animal': message, - }, - ); - - if (response!.images.isNotEmpty) { - var imagenImage = response.images[0]; - - resultMessage = MessageData( - imageBytes: imagenImage.bytesBase64Encoded, - text: message, - fromUser: false, - ); - } else { - // Handle the case where no images were generated - _showError('Error: No images were generated.'); - } - - setState(() { - if (resultMessage != null) { - _messages.add(resultMessage); - } - _loading = false; - _scrollDown(); - }); - } catch (e) { - _showError(e.toString()); - setState(() { - _loading = false; - }); - } finally { - _textController.clear(); - setState(() { - _loading = false; - }); - _textFieldFocus.requestFocus(); - } - } - Future _serverTemplateImageInput(String message) async { setState(() { _loading = true; diff --git a/packages/firebase_ai/firebase_ai/example/lib/utils/image_utils.dart b/packages/firebase_ai/firebase_ai/example/lib/utils/image_utils.dart deleted file mode 100644 index f297c9b25e66..000000000000 --- a/packages/firebase_ai/firebase_ai/example/lib/utils/image_utils.dart +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import 'package:firebase_ai/firebase_ai.dart'; -import 'package:image/image.dart' as img; -import 'package:flutter/foundation.dart'; - -/// The parameters passed to the isolate -class _IsolateParams { - final Uint8List imageBytes; - // ignore: experimental_member_use - final ImagenDimensions newDimensions; - // ignore: experimental_member_use - final ImagenImagePlacement newPosition; - - _IsolateParams({ - required Uint8List imageBytes, - required this.newDimensions, - required this.newPosition, - }) : imageBytes = Uint8List.fromList(imageBytes); -} - -/// The results returned from the isolate -class _IsolateResult { - final Uint8List paddedImageBytes; - final Uint8List maskBytes; - - _IsolateResult({ - required this.paddedImageBytes, - required this.maskBytes, - }); -} - -/// Processes the image request. -/// -/// This is the top-level function that will run in the background isolate. -/// It uses the 'image' package for all manipulations. -Future<_IsolateResult> _generateMaskAndPadInIsolate( - _IsolateParams params, -) async { - // 1. Decode the original image - final originalImage = img.decodeImage(params.imageBytes); - if (originalImage == null) { - throw StateError('Failed to decode image in isolate.'); - } - // Validate dimensions - if (originalImage.width >= params.newDimensions.width || - originalImage.height >= params.newDimensions.height) { - throw ArgumentError( - 'New Dimensions must be strictly larger than original image dimensions.', - ); - } - // 2. Calculate the position - // ignore: experimental_member_use - final originalDimensions = ImagenDimensions( - width: originalImage.width, - height: originalImage.height, - ); - final normalizedPosition = params.newPosition.normalizeToDimensions( - originalDimensions, - params.newDimensions, - ); - final x = normalizedPosition.x ?? 0; - final y = normalizedPosition.y ?? 0; - // 3. Create the mask image - final mask = img.Image( - width: params.newDimensions.width, - height: params.newDimensions.height, - ); - // Fill with white and draw a black rectangle for the original image area - img.fill(mask, color: img.ColorRgb8(255, 255, 255)); - img.fillRect( - mask, - x1: x, - y1: y, - x2: x + originalImage.width, - y2: y + originalImage.height, - color: img.ColorRgb8(0, 0, 0), - ); - // 4. Create the padded image - final paddedImage = img.Image( - width: params.newDimensions.width, - height: params.newDimensions.height, - ); - // Fill with black and draw the original image on top - img.fill(paddedImage, color: img.ColorRgb8(0, 0, 0)); - img.compositeImage( - paddedImage, - originalImage, - dstX: x, - dstY: y, - ); - // 5. Encode both images to PNG format (which is lossless) - final maskBytes = img.encodePng(mask); - final paddedBytes = img.encodePng(paddedImage); - return _IsolateResult( - paddedImageBytes: Uint8List.fromList(paddedBytes), - maskBytes: Uint8List.fromList(maskBytes), - ); -} - -/// Generates a mask and pads the image for outpainting. -// ignore: experimental_member_use -Future> generateMaskAndPadForOutpainting({ - required ImagenInlineImage image, - // ignore: experimental_member_use - required ImagenDimensions newDimensions, - // ignore: experimental_member_use - ImagenImagePlacement newPosition = ImagenImagePlacement.center, -}) async { - // Prepare the parameters for the isolate - // Note: We are assuming `image` has a way to get its raw bytes, - // which seems to be the case from `bytesBase64Encoded` in your example. - // If not, you'd need to convert the `ui.Image` to bytes here first. - final params = _IsolateParams( - imageBytes: image.bytesBase64Encoded, // Assuming this is Uint8List - newDimensions: newDimensions, - newPosition: newPosition, - ); - // Execute the image processing in a separate isolate and wait for the result - final result = await compute(_generateMaskAndPadInIsolate, params); - - // Use the resulting bytes to create your final objects - return [ - // ignore: experimental_member_use - ImagenRawImage( - image: ImagenInlineImage( - bytesBase64Encoded: result.paddedImageBytes, - mimeType: 'image/png', // The isolate always returns PNG - ), - ), - // ignore: experimental_member_use - ImagenRawMask( - mask: ImagenInlineImage( - bytesBase64Encoded: result.maskBytes, - mimeType: 'image/png', // The isolate always returns PNG - ), - ), - ]; -} diff --git a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_api.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_api.dart index 20810f86690e..8bf9ef018b9a 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_api.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_api.dart @@ -16,6 +16,11 @@ import 'dart:developer'; /// Specifies the level of safety filtering for image generation. /// /// If not specified, default will be "block_medium_and_above". +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenSafetyFilterLevel { /// Strongest filtering level, most strict blocking. blockLowAndAbove('block_low_and_above'), @@ -57,6 +62,11 @@ enum ImagenSafetyFilterLevel { /// Allow generation of people by the model. /// /// If not specified, the default value is "allow_adult". +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenPersonFilterLevel { /// Disallow the inclusion of people or faces in images. blockAll('dont_allow'), @@ -92,6 +102,11 @@ enum ImagenPersonFilterLevel { /// A class representing safety settings for image generation. /// /// It includes a safety filter level and a person filter level. +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenSafetySettings { // ignore: public_member_api_docs ImagenSafetySettings(this.safetyFilterLevel, this.personFilterLevel); @@ -114,6 +129,11 @@ final class ImagenSafetySettings { /// The aspect ratio for the image. /// /// The default value is "1:1". +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenAspectRatio { /// Square (1:1). square1x1('1:1'), @@ -155,6 +175,11 @@ enum ImagenAspectRatio { } /// Configuration options for image generation. +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenGenerationConfig { // ignore: public_member_api_docs ImagenGenerationConfig( @@ -195,6 +220,11 @@ final class ImagenGenerationConfig { } /// Represents the image format and compression quality. +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenFormat { // ignore: public_member_api_docs ImagenFormat(this.mimeType, this.compressionQuality); diff --git a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_content.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_content.dart index ef8be1bb31f6..01af99045211 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_content.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_content.dart @@ -28,6 +28,11 @@ sealed class ImagenImage { } /// Represents an image stored as a base64-encoded string. +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenInlineImage implements ImagenImage { // ignore: public_member_api_docs ImagenInlineImage({ diff --git a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_edit.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_edit.dart index 5cff20d08bdb..99c8c1064c6a 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_edit.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_edit.dart @@ -18,6 +18,11 @@ import 'package:meta/meta.dart'; /// The desired outcome of the image editing. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenEditMode { /// The result of the editing will be an insertion of the prompt in the masked /// region. @@ -37,6 +42,11 @@ enum ImagenEditMode { /// The type of the subject in the image. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenSubjectReferenceType { /// The subject is a person. person('SUBJECT_TYPE_PERSON'), @@ -56,6 +66,11 @@ enum ImagenSubjectReferenceType { /// The type of control image. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenControlType { /// Use edge detection to ensure the new image follow the same outlines. canny('CONTROL_TYPE_CANNY'), @@ -81,6 +96,11 @@ enum ImagenControlType { /// The mode of the mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenMaskMode { /// The mask is user provided. userProvided('MASK_MODE_USER_PROVIDED'), @@ -108,6 +128,11 @@ sealed class ImagenReferenceConfig { /// The configuration for the mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenMaskConfig extends ImagenReferenceConfig { // ignore: public_member_api_docs ImagenMaskConfig({ @@ -137,6 +162,11 @@ final class ImagenMaskConfig extends ImagenReferenceConfig { /// The configuration for the subject. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenSubjectConfig extends ImagenReferenceConfig { // ignore: public_member_api_docs ImagenSubjectConfig({ @@ -161,6 +191,11 @@ final class ImagenSubjectConfig extends ImagenReferenceConfig { /// The configuration for the style. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenStyleConfig extends ImagenReferenceConfig { // ignore: public_member_api_docs ImagenStyleConfig({ @@ -179,6 +214,11 @@ final class ImagenStyleConfig extends ImagenReferenceConfig { /// The configuration for the control. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenControlConfig extends ImagenReferenceConfig { // ignore: public_member_api_docs ImagenControlConfig({ @@ -214,6 +254,11 @@ final class ImagenControlConfig extends ImagenReferenceConfig { /// The configuration for image editing. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenEditingConfig { // ignore: public_member_api_docs ImagenEditingConfig({ @@ -230,6 +275,11 @@ final class ImagenEditingConfig { /// The dimensions of an image. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenDimensions { // ignore: public_member_api_docs ImagenDimensions({ @@ -246,6 +296,11 @@ final class ImagenDimensions { /// The placement of an image. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenImagePlacement { const ImagenImagePlacement._(this.x, this.y); diff --git a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_model.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_model.dart index 54789dcfadcc..197ed5714866 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_model.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_model.dart @@ -24,6 +24,11 @@ part of '../base_model.dart'; /// > Warning: For Vertex AI in Firebase, image generation using Imagen 3 models /// is in Public Preview, which means that the feature is not subject to any SLA /// or deprecation policy and could change in backwards-incompatible ways. +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenModel extends BaseApiClientModel { ImagenModel._( {required FirebaseApp app, diff --git a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_reference.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_reference.dart index c05785168424..f8cb307bb456 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_reference.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_reference.dart @@ -30,6 +30,11 @@ enum _ReferenceType { /// A reference image for image editing. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) sealed class ImagenReferenceImage { ImagenReferenceImage._({ this.referenceConfig, @@ -72,6 +77,11 @@ sealed class ImagenReferenceImage { /// A reference image that is a mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) sealed class ImagenMaskReference extends ImagenReferenceImage { // ignore: public_member_api_docs ImagenMaskReference({ @@ -86,6 +96,11 @@ sealed class ImagenMaskReference extends ImagenReferenceImage { /// A raw image. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenRawImage extends ImagenReferenceImage { // ignore: public_member_api_docs ImagenRawImage({ @@ -96,6 +111,11 @@ final class ImagenRawImage extends ImagenReferenceImage { /// A raw mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenRawMask extends ImagenMaskReference { // ignore: public_member_api_docs ImagenRawMask({ @@ -113,6 +133,11 @@ final class ImagenRawMask extends ImagenMaskReference { /// A semantic mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenSemanticMask extends ImagenMaskReference { // ignore: public_member_api_docs ImagenSemanticMask({ @@ -130,6 +155,11 @@ final class ImagenSemanticMask extends ImagenMaskReference { /// A background mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenBackgroundMask extends ImagenMaskReference { // ignore: public_member_api_docs ImagenBackgroundMask({ @@ -145,6 +175,11 @@ final class ImagenBackgroundMask extends ImagenMaskReference { /// A foreground mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenForegroundMask extends ImagenMaskReference { // ignore: public_member_api_docs ImagenForegroundMask({ @@ -160,6 +195,11 @@ final class ImagenForegroundMask extends ImagenMaskReference { /// A subject reference. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenSubjectReference extends ImagenReferenceImage { // ignore: public_member_api_docs ImagenSubjectReference({ @@ -179,6 +219,11 @@ final class ImagenSubjectReference extends ImagenReferenceImage { /// A style reference. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenStyleReference extends ImagenReferenceImage { // ignore: public_member_api_docs ImagenStyleReference({ @@ -196,6 +241,11 @@ final class ImagenStyleReference extends ImagenReferenceImage { /// A control reference. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenControlReference extends ImagenReferenceImage { // ignore: public_member_api_docs ImagenControlReference({ diff --git a/packages/firebase_ai/firebase_ai/lib/src/server_template/template_imagen_model.dart b/packages/firebase_ai/firebase_ai/lib/src/server_template/template_imagen_model.dart index e27dd5eaaa9d..0bfe90caebdc 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/server_template/template_imagen_model.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/server_template/template_imagen_model.dart @@ -16,6 +16,11 @@ part of '../base_model.dart'; /// An image model that connects to a remote server template. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class TemplateImagenModel extends BaseTemplateApiClientModel { TemplateImagenModel._testModel( {required FirebaseApp app,