diff --git a/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart b/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart index b42c458d3075..ecc18105a03d 100644 --- a/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart +++ b/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart @@ -55,6 +55,7 @@ export 'src/error.dart' ServerException, UnsupportedUserLocation; export 'src/firebase_ai.dart' show FirebaseAI; +export 'src/image_config.dart' show ImageConfig, AspectRatio; export 'src/imagen/imagen_api.dart' show ImagenSafetySettings, diff --git a/packages/firebase_ai/firebase_ai/lib/src/api.dart b/packages/firebase_ai/firebase_ai/lib/src/api.dart index 4c38ec4b0f6a..f8d274fbe4b4 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/api.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/api.dart @@ -14,6 +14,7 @@ import 'content.dart'; import 'error.dart'; +import 'image_config.dart'; import 'schema.dart'; import 'tool.dart' show Tool, ToolConfig; @@ -752,7 +753,23 @@ enum FinishReason { recitation('RECITATION'), /// Unknown reason. - other('OTHER'); + other('OTHER'), + + /// Image generation stopped because generated images has safety issues. + imageSafety('IMAGE_SAFETY'), + + /// Image generation stopped because generated images has other prohibited + /// content. + imageProhibitedContent('IMAGE_PROHIBITED_CONTENT'), + + /// Image generation stopped due to recitation. + imageRecitation('IMAGE_RECITATION'), + + /// Image generation stopped because of other miscellaneous issue. + imageOther('IMAGE_OTHER'), + + /// The model was expected to generate an image, but none was generated. + noImage('NO_IMAGE'); const FinishReason(this._jsonString); @@ -770,6 +787,11 @@ enum FinishReason { 'SAFETY' => FinishReason.safety, 'RECITATION' => FinishReason.recitation, 'OTHER' => FinishReason.other, + 'IMAGE_SAFETY' => FinishReason.imageSafety, + 'IMAGE_PROHIBITED_CONTENT' => FinishReason.imageProhibitedContent, + 'IMAGE_RECITATION' => FinishReason.imageRecitation, + 'IMAGE_OTHER' => FinishReason.imageOther, + 'NO_IMAGE' => FinishReason.noImage, _ => throw FormatException('Unhandled FinishReason format', jsonObject), }; } @@ -1105,6 +1127,7 @@ final class GenerationConfig extends BaseGenerationConfig { this.responseSchema, this.responseJsonSchema, this.thinkingConfig, + this.imageConfig, }) : assert(responseSchema == null || responseJsonSchema == null, 'responseSchema and responseJsonSchema cannot both be set.'); @@ -1153,6 +1176,9 @@ final class GenerationConfig extends BaseGenerationConfig { /// support thinking. final ThinkingConfig? thinkingConfig; + /// Config for image generation. + final ImageConfig? imageConfig; + @override Map toJson() => { ...super.toJson(), @@ -1167,6 +1193,8 @@ final class GenerationConfig extends BaseGenerationConfig { 'responseJsonSchema': responseJsonSchema, if (thinkingConfig case final thinkingConfig?) 'thinkingConfig': thinkingConfig.toJson(), + if (imageConfig case final imageConfig?) + 'imageConfig': imageConfig.toJson(), }; } diff --git a/packages/firebase_ai/firebase_ai/lib/src/image_config.dart b/packages/firebase_ai/firebase_ai/lib/src/image_config.dart new file mode 100644 index 000000000000..9f6f3c2f43d3 --- /dev/null +++ b/packages/firebase_ai/firebase_ai/lib/src/image_config.dart @@ -0,0 +1,89 @@ +// Copyright 2024 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. + +/// The aspect ratio for the image. +/// +/// The default value is "1:1". +enum AspectRatio { + /// 1:1 aspect ratio. + ratio1x1('1:1'), + + /// 2:3 aspect ratio. + ratio2x3('2:3'), + + /// 3:2 aspect ratio. + ratio3x2('3:2'), + + /// 3:4 aspect ratio. + ratio3x4('3:4'), + + /// 4:3 aspect ratio. + ratio4x3('4:3'), + + /// 4:5 aspect ratio. + ratio4x5('4:5'), + + /// 5:4 aspect ratio. + ratio5x4('5:4'), + + /// 9:16 aspect ratio. + ratio9x16('9:16'), + + /// 16:9 aspect ratio. + ratio16x9('16:9'), + + /// 21:9 aspect ratio. + ratio21x9('21:9'); + + const AspectRatio(this._jsonString); + + final String _jsonString; + + /// Convert to json format. + String toJson() => _jsonString; + + /// Parse the json to [AspectRatio] object. + static AspectRatio parseValue(Object jsonObject) { + return switch (jsonObject) { + '1:1' => AspectRatio.ratio1x1, + '2:3' => AspectRatio.ratio2x3, + '3:2' => AspectRatio.ratio3x2, + '3:4' => AspectRatio.ratio3x4, + '4:3' => AspectRatio.ratio4x3, + '4:5' => AspectRatio.ratio4x5, + '5:4' => AspectRatio.ratio5x4, + '9:16' => AspectRatio.ratio9x16, + '16:9' => AspectRatio.ratio16x9, + '21:9' => AspectRatio.ratio21x9, + _ => throw FormatException('Unhandled AspectRatio format', jsonObject), + }; + } + + @override + String toString() => name; +} + +/// Configuration options for image generation. +final class ImageConfig { + // ignore: public_member_api_docs + ImageConfig({this.aspectRatio}); + + /// The aspect ratio for the image. The default value is "1:1". + final AspectRatio? aspectRatio; + + // ignore: public_member_api_docs + Map toJson() => { + if (aspectRatio != null) 'aspectRatio': aspectRatio!.toJson(), + }; +}