From 27c05b2383ffefe7ae0087cc283f0f5a2aec29c0 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 5 May 2025 20:00:08 +0000 Subject: [PATCH 01/13] Add a new package firebase_ai Copy all the code from firebase_vertexai and rename with "Vertex" removed or replaced with "Firebase". Make firebase_vertexai an export package, retaining only the FirebaseVertexAI implementation and adding `typedef` aliases for 2 renamed exception types. Replace the rest with exports from `package:firebase_ai`. Revert the examples changes to use googleAI - the example in the `firebase_ai` package will show both while the old example shows only the usage of vertex.. Import from src across the packages to keep existing tests as close to identical as possible despite doubling up test cases with the upstream package. --- .../firebase_ai/CHANGELOG.md | 3 + .../firebase_vertexai/firebase_ai/LICENSE | 202 ++++ .../firebase_vertexai/firebase_ai/README.md | 26 + .../firebase_ai/example/.gitignore | 51 + .../firebase_ai/example/.metadata | 30 + .../firebase_ai/example/README.md | 16 + .../firebase_ai/example/analysis_options.yaml | 10 + .../firebase_ai/example/android/.gitignore | 13 + .../example/android/app/build.gradle | 59 ++ .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 50 + .../com/example/example/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + .../firebase_ai/example/android/build.gradle | 18 + .../example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle | 28 + .../assets/documents/gemini_summary.pdf | Bin 0 -> 1689 bytes .../firebase_ai/example/assets/images/cat.jpg | Bin 0 -> 17858 bytes .../example/assets/images/scones.jpg | Bin 0 -> 217722 bytes .../example/assets/videos/landscape.mp4 | Bin 0 -> 89636 bytes .../firebase_ai/example/ios/.gitignore | 34 + .../ios/Flutter/AppFrameworkInfo.plist | 26 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../firebase_ai/example/ios/Podfile | 44 + .../ios/Runner.xcodeproj/project.pbxproj | 756 +++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 123 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 +++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../firebase_ai/example/ios/Runner/Info.plist | 51 + .../ios/Runner/Runner-Bridging-Header.h | 1 + .../example/ios/firebase_app_id_file.json | 7 + .../firebase_ai/example/lib/main.dart | 321 +++++++ .../example/lib/pages/audio_page.dart | 186 ++++ .../example/lib/pages/bidi_page.dart | 448 +++++++++ .../example/lib/pages/chat_page.dart | 176 ++++ .../example/lib/pages/document.dart | 117 +++ .../lib/pages/function_calling_page.dart | 200 ++++ .../example/lib/pages/image_prompt_page.dart | 243 +++++ .../example/lib/pages/imagen_page.dart | 214 +++++ .../example/lib/pages/schema_page.dart | 182 ++++ .../example/lib/pages/token_count_page.dart | 106 +++ .../example/lib/pages/video_page.dart | 116 +++ .../example/lib/utils/audio_player.dart | 143 +++ .../example/lib/utils/audio_recorder.dart | 245 +++++ .../example/lib/widgets/message_widget.dart | 68 ++ .../firebase_ai/example/macos/.gitignore | 7 + .../macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../firebase_ai/example/macos/Podfile | 43 + .../macos/Runner.xcodeproj/project.pbxproj | 805 ++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 105 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 68 ++ .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 2218 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 343 +++++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 18 + .../example/macos/Runner/Info.plist | 34 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 10 + .../example/macos/firebase_app_id_file.json | 7 + .../firebase_ai/example/pubspec.yaml | 51 + .../firebase_ai/example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../example/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes .../firebase_ai/example/web/index.html | 38 + .../firebase_ai/example/web/manifest.json | 35 + .../firebase_ai/lib/firebase_ai.dart | 82 ++ .../lib/src/api.dart | 8 +- .../lib/src/base_model.dart | 1 - .../lib/src/chat.dart | 0 .../lib/src/client.dart | 2 +- .../lib/src/content.dart | 0 .../lib/src/developer/api.dart | 0 .../lib/src/error.dart | 26 +- .../firebase_ai/lib/src/firebase_ai.dart | 187 ++++ .../lib/src/function_calling.dart | 0 .../lib/src/generative_model.dart | 2 +- .../lib/src/imagen_api.dart | 0 .../lib/src/imagen_content.dart | 0 .../lib/src/imagen_model.dart | 3 +- .../lib/src/live_api.dart | 8 +- .../lib/src/live_model.dart | 3 +- .../lib/src/live_session.dart | 2 +- .../lib/src/schema.dart | 0 .../lib/src/utils/mutex.dart | 0 .../lib/src/vertex_version.dart | 0 .../firebase_ai/pubspec.yaml | 39 + .../firebase_ai/test/base_model_test.dart | 141 +++ .../firebase_ai/test/chat_test.dart | 132 +++ .../firebase_ai/test/content_test.dart | 215 +++++ .../firebase_ai/test/error_test.dart | 165 ++++ .../test/firebase_vertexai_test.dart | 81 ++ .../test/google_ai_generative_model_test.dart | 731 +++++++++++++++ .../test/google_ai_response_parsing_test.dart | 770 +++++++++++++++ .../firebase_ai/test/imagen_test.dart | 241 +++++ .../firebase_ai/test/live_test.dart | 239 +++++ .../firebase_ai/test/mock.dart | 73 ++ .../firebase_ai/test/model_test.dart | 464 ++++++++++ .../test/response_parsing_test.dart | 875 ++++++++++++++++++ .../firebase_ai/test/schema_test.dart | 140 +++ .../firebase_ai/test/utils/matchers.dart | 100 ++ .../firebase_ai/test/utils/stub_client.dart | 93 ++ .../firebase_vertexai/example/.gitignore | 1 - .../example/android/settings.gradle | 3 - .../firebase_vertexai/example/lib/main.dart | 289 ++---- .../example/lib/pages/bidi_page.dart | 2 +- .../lib/pages/function_calling_page.dart | 34 +- .../example/lib/pages/imagen_page.dart | 26 +- .../lib/firebase_vertexai.dart | 58 +- .../lib/src/firebase_vertexai.dart | 39 +- .../firebase_vertexai/pubspec.yaml | 5 + .../test/base_model_test.dart | 6 +- .../firebase_vertexai/test/chat_test.dart | 3 +- .../firebase_vertexai/test/content_test.dart | 5 +- .../firebase_vertexai/test/error_test.dart | 6 +- .../test/google_ai_generative_model_test.dart | 2 +- .../test/google_ai_response_parsing_test.dart | 2 +- .../firebase_vertexai/test/imagen_test.dart | 4 +- .../firebase_vertexai/test/live_test.dart | 7 +- .../firebase_vertexai/test/model_test.dart | 3 +- .../test/response_parsing_test.dart | 4 +- .../firebase_vertexai/test/schema_test.dart | 2 +- .../test/utils/stub_client.dart | 2 +- 177 files changed, 11313 insertions(+), 345 deletions(-) create mode 100644 packages/firebase_vertexai/firebase_ai/CHANGELOG.md create mode 100644 packages/firebase_vertexai/firebase_ai/LICENSE create mode 100644 packages/firebase_vertexai/firebase_ai/README.md create mode 100644 packages/firebase_vertexai/firebase_ai/example/.gitignore create mode 100644 packages/firebase_vertexai/firebase_ai/example/.metadata create mode 100644 packages/firebase_vertexai/firebase_ai/example/README.md create mode 100644 packages/firebase_vertexai/firebase_ai/example/analysis_options.yaml create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/.gitignore create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/build.gradle create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/debug/AndroidManifest.xml create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/values-night/styles.xml create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/values/styles.xml create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/app/src/profile/AndroidManifest.xml create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/build.gradle create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/gradle.properties create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/firebase_vertexai/firebase_ai/example/android/settings.gradle create mode 100644 packages/firebase_vertexai/firebase_ai/example/assets/documents/gemini_summary.pdf create mode 100644 packages/firebase_vertexai/firebase_ai/example/assets/images/cat.jpg create mode 100644 packages/firebase_vertexai/firebase_ai/example/assets/images/scones.jpg create mode 100644 packages/firebase_vertexai/firebase_ai/example/assets/videos/landscape.mp4 create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/.gitignore create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Flutter/Release.xcconfig create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Podfile create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/AppDelegate.swift create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Info.plist create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/Runner/Runner-Bridging-Header.h create mode 100644 packages/firebase_vertexai/firebase_ai/example/ios/firebase_app_id_file.json create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/main.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/pages/audio_page.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/pages/bidi_page.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/pages/chat_page.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/pages/document.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/pages/function_calling_page.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/pages/image_prompt_page.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/pages/imagen_page.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/pages/schema_page.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/pages/token_count_page.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/pages/video_page.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/utils/audio_player.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/utils/audio_recorder.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/lib/widgets/message_widget.dart create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/.gitignore create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Podfile create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/AppDelegate.swift create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Info.plist create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/Runner/Release.entitlements create mode 100644 packages/firebase_vertexai/firebase_ai/example/macos/firebase_app_id_file.json create mode 100644 packages/firebase_vertexai/firebase_ai/example/pubspec.yaml create mode 100644 packages/firebase_vertexai/firebase_ai/example/web/favicon.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-192.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-512.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-maskable-192.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-maskable-512.png create mode 100644 packages/firebase_vertexai/firebase_ai/example/web/index.html create mode 100644 packages/firebase_vertexai/firebase_ai/example/web/manifest.json create mode 100644 packages/firebase_vertexai/firebase_ai/lib/firebase_ai.dart rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/api.dart (99%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/base_model.dart (99%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/chat.dart (100%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/client.dart (99%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/content.dart (100%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/developer/api.dart (100%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/error.dart (87%) create mode 100644 packages/firebase_vertexai/firebase_ai/lib/src/firebase_ai.dart rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/function_calling.dart (100%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/generative_model.dart (99%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/imagen_api.dart (100%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/imagen_content.dart (100%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/imagen_model.dart (99%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/live_api.dart (97%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/live_model.dart (99%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/live_session.dart (98%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/schema.dart (100%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/utils/mutex.dart (100%) rename packages/firebase_vertexai/{firebase_vertexai => firebase_ai}/lib/src/vertex_version.dart (100%) create mode 100644 packages/firebase_vertexai/firebase_ai/pubspec.yaml create mode 100644 packages/firebase_vertexai/firebase_ai/test/base_model_test.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/chat_test.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/content_test.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/error_test.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/firebase_vertexai_test.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/google_ai_generative_model_test.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/google_ai_response_parsing_test.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/imagen_test.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/live_test.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/mock.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/model_test.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/response_parsing_test.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/schema_test.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/utils/matchers.dart create mode 100644 packages/firebase_vertexai/firebase_ai/test/utils/stub_client.dart diff --git a/packages/firebase_vertexai/firebase_ai/CHANGELOG.md b/packages/firebase_vertexai/firebase_ai/CHANGELOG.md new file mode 100644 index 000000000000..6e551e77ef40 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.5.0 + +- Initial release following package rename. diff --git a/packages/firebase_vertexai/firebase_ai/LICENSE b/packages/firebase_vertexai/firebase_ai/LICENSE new file mode 100644 index 000000000000..e58143fccfb6 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/packages/firebase_vertexai/firebase_ai/README.md b/packages/firebase_vertexai/firebase_ai/README.md new file mode 100644 index 000000000000..f636778c1789 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/README.md @@ -0,0 +1,26 @@ +# Firebase AI in Firebase Flutter +[![pub package](https://img.shields.io/pub/v/firebase_ai.svg)](https://pub.dev/packages/firebase_ai) + +A Flutter plugin to use the [Firebase AI](https://firebase.google.com/docs/vertex-ai/). + +To learn more about Firebase AI, please visit the [website](https://cloud.google.com/vertex-ai) + +**Generally Available**: Firebase AI in Firebase is now Generally Available (GA) and can be used in production apps + +## Getting Started + +To get started with Firebase AI in Firebase Flutter, please [see the documentation](https://firebase.google.com/docs/vertex-ai/get-started?platform=flutter). + +## Usage + +To start use this plugin, please visit the [Text only prompt documentation](https://firebase.google.com/docs/vertex-ai/text-gen-from-text?platform=flutter) + +## Issues and feedback + +Please file FlutterFire specific issues, bugs, or feature requests in our [issue tracker](https://github.com/firebase/flutterfire/issues/new). + +Plugin issues that are not specific to FlutterFire can be filed in the [Flutter issue tracker](https://github.com/flutter/flutter/issues/new). + +To contribute a change to this plugin, +please review our [contribution guide](https://github.com/firebase/flutterfire/blob/main/CONTRIBUTING.md) +and open a [pull request](https://github.com/firebase/flutterfire/pulls). diff --git a/packages/firebase_vertexai/firebase_ai/example/.gitignore b/packages/firebase_vertexai/firebase_ai/example/.gitignore new file mode 100644 index 000000000000..53bed76d8faa --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/.gitignore @@ -0,0 +1,51 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release + +#firebase +firebase_options.dart +google-services.json +GoogleService-Info.plist +firebase.json diff --git a/packages/firebase_vertexai/firebase_ai/example/.metadata b/packages/firebase_vertexai/firebase_ai/example/.metadata new file mode 100644 index 000000000000..784ce1298249 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + - platform: web + create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/firebase_vertexai/firebase_ai/example/README.md b/packages/firebase_vertexai/firebase_ai/example/README.md new file mode 100644 index 000000000000..750e258b7bf8 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/README.md @@ -0,0 +1,16 @@ +# firebase_ai_example + +Sample app to show how to use Firebase AI in Firebase. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/firebase_vertexai/firebase_ai/example/analysis_options.yaml b/packages/firebase_vertexai/firebase_ai/example/analysis_options.yaml new file mode 100644 index 000000000000..b6cd704fb940 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/analysis_options.yaml @@ -0,0 +1,10 @@ +# Copyright 2021 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# in the LICENSE file. + +include: ../../../../analysis_options.yaml +linter: + rules: + avoid_print: false + depend_on_referenced_packages: false + library_private_types_in_public_api: false diff --git a/packages/firebase_vertexai/firebase_ai/example/android/.gitignore b/packages/firebase_vertexai/firebase_ai/example/android/.gitignore new file mode 100644 index 000000000000..6f568019d3c6 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/build.gradle b/packages/firebase_vertexai/firebase_ai/example/android/app/build.gradle new file mode 100644 index 000000000000..b5a45dbd1ff0 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/app/build.gradle @@ -0,0 +1,59 @@ +plugins { + id "com.android.application" + // START: FlutterFire Configuration + id 'com.google.gms.google-services' + // END: FlutterFire Configuration + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +android { + namespace "com.example.example" + + compileSdk 35 + + defaultConfig { + applicationId "com.example.example" + minSdk 23 + targetSdk 33 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildFeatures { + buildConfig true + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } + kotlinOptions { + jvmTarget = '1.8' // Or '11' + } +} + +flutter { + source '../..' +} diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/debug/AndroidManifest.xml b/packages/firebase_vertexai/firebase_ai/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..399f6981d5d3 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/AndroidManifest.xml b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..3401fcfb42b9 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt new file mode 100644 index 000000000000..70f8f08f2479 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000000..f74085f3f6a2 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/drawable/launch_background.xml b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000000..304732f88420 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/values-night/styles.xml b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000000..06952be745f9 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/values/styles.xml b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..cb1ef88056ed --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/profile/AndroidManifest.xml b/packages/firebase_vertexai/firebase_ai/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000000..399f6981d5d3 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/android/build.gradle b/packages/firebase_vertexai/firebase_ai/example/android/build.gradle new file mode 100644 index 000000000000..bc157bd1a12b --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/build.gradle @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/packages/firebase_vertexai/firebase_ai/example/android/gradle.properties b/packages/firebase_vertexai/firebase_ai/example/android/gradle.properties new file mode 100644 index 000000000000..598d13fee446 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/firebase_vertexai/firebase_ai/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/firebase_vertexai/firebase_ai/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..aa49780cd59e --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip diff --git a/packages/firebase_vertexai/firebase_ai/example/android/settings.gradle b/packages/firebase_vertexai/firebase_ai/example/android/settings.gradle new file mode 100644 index 000000000000..40cbd22bb13b --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/android/settings.gradle @@ -0,0 +1,28 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + // START: FlutterFire Configuration + id "com.google.gms.google-services" version "4.3.15" apply false + // END: FlutterFire Configuration + id "org.jetbrains.kotlin.android" version "1.9.22" apply false +} + +include ":app" diff --git a/packages/firebase_vertexai/firebase_ai/example/assets/documents/gemini_summary.pdf b/packages/firebase_vertexai/firebase_ai/example/assets/documents/gemini_summary.pdf new file mode 100644 index 0000000000000000000000000000000000000000..08881c7839eca5928ae84aea667eb9d0f291c376 GIT binary patch literal 1689 zcmbVNdrVVT7@rzYuY*W^%q`ZPOb|M-_eDz!6+>Gp1qH;Ksl+UO+?HF)y|umVijUwn zbpsV9Q>NQwHq$xO1#lI_tQmI3fCy1E1;Y@ZETYp1Q(44i=ORp$f0ms;?m6Fg&iTIc zJ$}C%m95q+#Kj~+0t~PQE22=KxWPu>@i zbm#je)g+etU7ar9><$e-b*A*tUBM99J6|?*b3yd10Wzdzqo_ST=71)c7ySqAFlN?G zKf1C>w3ib!HW+S{2?PnJe?1@rFK>*g#7-v36lV_xsYz2Rt8xng#6f61i{ z+H}Xwd)An$BQ*mD7Il3Zv2Ppa^>ives}&1G$M*Q1HcCT_-tB0PZPQjXG==^)r|kE2 z>#z8q*7qkE{nZ8AWwOWjd_!}RBb)11>b7XAcM!#YEy3otU-E1hmM!ahZS^OygLy;O zb`rmK*2I-QEZID@)HCx{Ati3(ha-yzxS5s(F4R&=(DYlP0foMuQmje&zsBD!rjyU9yp(AFPiY9arftZSC2I5 zNPqKq7>5=of@aVrC&=ZolmpfxsET#*;6c!3l!?|WSr5p?;GRS-5fd^Xi^s(>`C8-! zY$UIm)j)h9s8a8w_!U6YD4Uz&Xrq3ilC_x-bcM^n4WpIEEcBDgKzz}R1)%TIObX+q zM-^k7NdL2w|7V}4hUd9~^x z+f8P%g=Hqv6UUCyDWZIlE4OkfMq>nRupn!~aj;FAZds%6Y9;Hx%VS0|@;aKrK9@g#{vMWtqG GkbeMI#bOKq literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/assets/images/cat.jpg b/packages/firebase_vertexai/firebase_ai/example/assets/images/cat.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8d2069e6c979ef6fe4fb52e7873e1019f008d2e1 GIT binary patch literal 17858 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

$mA|9kk@Nym2WCk-Kkj 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

lHlqQhn-!3f=#Q-9-TfqS|2?PE!W;IdI`5u{ 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

_{?(lwOCg$jxWRAE>D`C=J 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/E8z8iTuxK;tA_@ZO)2q@ik|`HgB%?QDgXf z21&rL{pL+?*PkEJ1b+Bg%P-@V5myCLQTFl05Y4*S>Nk##PQ~qeV!E_d#m&|taN1N zh4PuWo9Z0`qGK%cbHrTm?AtNk>odAHp0LNE>z9G^MC;s(p}g1Bk!yYvu6$)JG(I0W zK!$1$H{!8gXH1b_GTy{*kER_V!;k~BKg*MaoDHss zcAm9bma(Y_@LW>MqW3+(zaihF;{5C`+H_@q3f2v_g%m4Ss1}mEa%{}(=0F+`!e&zJ z9&H4Rg8ezP0YN&b`E4^};-(k$8hC?92APfFQD6b&2yYY6+ZIv8VETE!X|Dt_QB3wl zy%4;J`Sy^tv_t^aUysbhVsy>(WM!0%YWqB^b*e{;o`$C@tTvcum}QaNn~G^rR}ppL zIrqU%>K9SjCE$1Ojd!5tqN(;C_2s&_ZRDgZ=&MNakIs{S4u{K$B79lI(mslT21iBx zvRFdk_E=|4^)-!12JWe zznO>VYrg?Dku2@n8|K&v__|_tjrb1)XA{Zzqmbl#rU|fZiqY?4S_Fx1#PkWnJeFU4 zy&}7`p!X}n*R(wk>LWZWbs~UCo-~1GkL0Q1CRE{aB~?Yyr!Dp9p4G{ulB;o|+f7r1t>#swrV;bub$Bn{N4e29x zEzGB}!!haI!|*jDhVk!J(goC|iG%U;=>-tV@k1qARhWpwf+T#71V^S`n2N4~g5{1F zKhMDH7R*Rdx|0(fn@h$pa((~&vLUW<$0-<{;Wrr5)l!u`5bxkT+TA7Mtygk95knw^ zFJK;tv)5-Wv}AUmztozK{AghsbKKxJ-q=P@MgHeVsb$4@ra!L4s>x)#ymy76sA9Lu z(R}m&0X;y%zipUxk}bDydzW%p;35D&^igW4I1&hBjP|+ zzfcm;;^;gzquco7hT7Li&=oNQMjJj0J7R#QzQnn8D}X6e4K@3j$h`J&b$MhF9XX+s z@`I#kjWi$v1{2Pz0qfQa^rmlf0}wflb=AgDT5?W!b^iy^%@EvULs(^>jF^rsonZFZ zopXnnh8p9$=W;l0@^*fMZl*{>FJs{@H;W4O%xqJEwRFL zog6pT=*=_+0l&|T>Jmx4OQt(Nw4p)2B{H^~OP%`O#J@E6TZS9VR} zPpOJn?P#bEP0hPK2O~3*jDw8+nam?eAO-y#ePv%tirpsx&^X}MVhZIZ5>#jtqcA0D z19#w{#38}e(=k=SvaRXA*GAEEX%m$CJmxYU>d+IWI9n3mRLr-LZf?_xA|Fbyf#zjM z

r*Fu@*tM+~sIJXNTs(AevlMSR_uI)|ltlq+sVufjv4Q@lPuch1oV4YadyS1rB7 zg;FSd#KY>a9TO(!nsmjU{2u=_IrFG{Cm&}P4$w(HX)~G(e)1xdIte;iol^)y3vfA( zeqkwm3uVCLx2_RACg3?``knl4j3&h7>Tm=VVJ*Ims$+h$>50^FUjq5a^#2+dMJPSD zQkvakSCd189xlW&e$k5OX%FVsA%g-{COotB<`%-`P?Sril@w0wS7T8s)2S9%4Cs=5 z#ckV%b&6rrK*&teXAI2W}VAYb>A5K^L78FECa=1gFJ6Sx+95$C>u{SqEcumj^{L^hq%C_Rnnx z9fP!Y-=A_qzI#61VAhuvh8(Ke-BQ?H%b~gjSbr$xUpuqWns-lq1z*eSILNVT@tegG&mNaBF2}Oc69R3paqm6@ zE(_QCVn6&3Ttbf?_5J4Qx%YFB6FpcorM)fCF!9%6n5n&F&9i1RQ~@1&>`0{e z_4%Z+$l#h!QC)^{m-B6sn5cS-m%+Vl;Cun(mdTN4(7%4yKJuG+!&QcC%`@>&FcFhZ z_46C%*kd@!Ef$&G5Qp)tk>sK>kfTsWr9$b=W|(p6Jy0S8evE1DH2A!CA_yJLtx2FM zca}kEDU=qM5?zo-;uHmC>0g=Qk<%mou*47ZzfGJd1^QW?3)xq6R%uwMezIS7(c6#X z7=ikovdDs(9~it0;5d&lLflc+Hl33_7o#gN9pb+VHOmEB&8?XtAbons9al^aZ;+>Z zDYAmqov?2gmUd8AKk=sy&F{u2xC$6-q?3-1xLQSR5 zX16&qn%UUkkR%TCh`ZfaAXSoax(13v{ zA5~UN`{+qL4Ot4bbNzNssI1B(XAgbaBp@KDJc+0j-sX|@22eQSqGDqzyMxN$+@6DK z05h4+9@PjF#n0umKrQT5L+|&kR?#PhCB5=aUl{g!*zvWu2C8I5h3UFd@B+&-iGVaB zC`A5>WzU4J_Y*Z3=?aYY#r>|rMxl+vut$C|PpUG~wx9X0th*z)x!fa1Cgl1I{aLT7 zIdKImuc{Ds>LrGq{aa2!fJg&`UOEuhm4xYY7%Dg(@y9!O^_DjwsuzX+WESP)*tw|L z!v6l=W45P7>(JsWzGGzeuWcbumN`qO5IcZsdN(Z>#j=y=!7%0_RT{VqlE$Nr2ujol zyJn|dSr+L7I(=RZ=S&j$+u8CFW(pzQpTQO>I-hF6SvmU6G~TNqJc&eu%z5RdxBnXo$W?Z55KQ~k@s#% z>u8tAS^X2uL)E=vB~A02MU=aq77f8#!Zx$)LJ-`sC35KwE*C84!Ylvb*MazU;$74F zJ97AMvLx!Mfc=|jKMCBYYY%8@M??kkAn6|vGPNJ=aXAmBnaYPkngwD(5q#ut?K;=S z@9xO7;d`K?6yTR3dzoLM;eZWI?O3@|&=~9XG3OY1l%?NPY#Sap7$p;(sR#Jy5ggbR zvjB1(;I+(0V+k1K!&2)0+gBFxy_=)L-rJg-+OmQ+c@mYM9lnlSx?}U=VO&{|3Uyxz4Xwvv%6v>8#|YRpilO2 z7f3QGB5#_UT(2KVZ~tlgAQi7rRp?6l3E<-;;ED#G44I4}H#d8D<2k&FyucSW7bDqe zGWlfnUbR#KyB}{QdKL@vQZ3oBezkRqR|LjXbNZ7MYaxWFReHX`%SH2-Wz=Gp>ex6O z98F?71y%WYPZv(>P;I5TT`QlFj9z32TyA`h^x8jEmWx+D9I^b|YwS7> zPc*MOwvX;e6z{dTjKscE_K)-s`r+qtr^tE+X}EHQu@M-$#5aD{9*og(-zZk&wQCp& z!Kh|sbCFLaDpKzN_vXHD6z3jKmi?;q2vAOeb9KWTr2k^1GKyCH+eyEEPEDr0O0>cM zrvDMI^H+EvLiaJb*J#0@AZm}_uJ}H~+zQD+ud0A0Bb!^kn!`z3iwvE+kxqgMl(;s$ zNljZMo?h`BNbV@m zr*lC){?Cx&6}#4Bm?H)ORyF1>?;ED1JK9gYhCni()&Sn zz8KxbZj?L>?G+cINOpO zKumdj#UmV!{#+qLeu#v1EoWKUSv%i3d~i?0f#t;LdE+0Mg9)@P#C#w!?b~;CCZjD-r+7 z6!maiiD08d-X$5be1JihM`_W%M0QGryEe(;ER55=L73^Na*bYAJYy3z-~WNg}Du&G-0lGr_^ zu_j&ZDSurZ8TTz4q|Xqte0V@qCSQ0~sK#wDMD~GqhVQ;5*C4FPh;V0oI@(`l!dj0p zb#KIw%dlV{)62BzH$%c1OkPVx?%F+nO3ci!62x#T5cKDry>pQqGYY5MM~YA#1KeV& zjJs9su@p<^+94vIBwbAafxrWu-g?x@whg4CaLQqNYETB zB7df-HJ60^T*2<{me%L2AReeg*Vy>xu`VCSR_&Vg_+n{*;(hFsM-N80XdO8MDZ3TK z3%dgcV_&3_ngpm6q18gIn;z%zC}#AZku2n^A|e4V4j9#h1EXr_&oaV!26EMFKa>rs zI1Dj^Qp;#w^3IHR^Z0kzP1Xhuk;6Cl7>c z4&z^V?iy%zvdiLeMDFF37o3ck9fL9etw*m>9XuN=yD2~TVC`VA|2R%KKB;?|W)^Ws z5sKx_6^DQ6QXy4Yi!3kA?rCz6-8GZ;x$w~89*-fnE9(U--6C0!*A}XnXk-6`T>|{r zv;^E{-W9TC?d&tilD&!PJUqZp?>D{})yK0f6N6saxO&Q5%e=ZUzq5N)yN@Qd8kYvj zErMQuQY*vgYNpGw^QjKRdc^%v6-MnNc>tseP0=9$FDrU?nUFbf`^)zcm1~Fy?PfJa z_zUZWE48_|;O#9{|BcLg&Q?pB^rWsR_vtKKsC`<%^0>~jin6G7UZ1=~_Jn?7zq81# zUV4RG#yZ3qvxrz{RTc(}+EMK9UpzmPnKvg4M#D*4kS=o=A=fs+o6!D>M&IQ;O_Kpl znj1e(5<>(v$&e^J9@Ax#+2M!jD4pTjDR;T>fV%*HlKR~8$T6+SAb);n2_887FmPGT zXWbbX4n2>s4nOv~Z1*0_SW3O%A6rv5esU`i(j&?i7rkfp?``t;k>O3~aM^x%z!)Y# z76e!3GQ(16jRU=;qAG1+BRmx@U$XrNy(k=!CJ}DrknimY)z^1lNhT#rG`HKs%Rj&mGj{QyCf?}Yc`8Vp?!NF+Xt{? ze1uZqkx7hMW?n`x?Y$PQe8QJ+^JQAsFGN!r`px9l60~3=K>1|0b}VrW$7oza#e5g^ zW>o}ovkJWkLFR)_H{$|){w`Ae1q?2&-dXV}2sD3@$M_{qSQ=Cv7X%ia42b~4HQ58v z2h_|RFPqGlnD19Ax%~U}-?uE3jDyM7$`EGqX0xg*7CEay&{G?O`M{P41+v1{ZW56f z6U&5-;ACUu$pPA$V5vHDmK)W|J(`EhdGW=00kydEx#=tCT)N}GRnh~9h(jl1oH-{{ zp8+;Sz{~7GgL`UnI8{2(3BwHK#AA~UzyS`W>|vi@qOxv6m5VEUvhdg;?%`#!!uWcz zm{);;NBQnt>s`-2J)%iv%0NmLRdSc~}zO^|T z3Dd-PXFuxK&yE=VOSI!~0W=%bU-pZ7gLub?W1TcO9Xed1BMZQYx#jQebb|_6YII(Z zPl(tbfH8YqTW%-?3?8D=2$AD|c*AX%j6UO=WVhk4{)&<>ocivlKdBl*5~kL^r%6g9T{{SB&pv?hS+nX9=`G1W}kt z-|h7tYWf~?ZQ)6WPVqsY%RLE1FZ8BFBelS@%g!c3J}9h-YrkzK)?H(nV)(WSM4E?o zK9YqLP{S(H8)lDHiu%xTyxEGYv+rh(^{ZC{ap?4|a#wFDv*(aB=u_0HdKN6N@T=dn zZPV4HZB~vLB_%uJ^n=5!BxOA@s?Y5p=H^)**?rUy<%BAVRKT-X5qy2d&V<03cacXH z%eD1{kPJ)fGW~i`2?m(EJb6m~U>(4GrKmmaFsG}zWu2Ws4GS3{;`q`F#4 z$v!K*Z+o<0{R$bASi0LePP*OJuxm!r6$X39%UVB={-X@T3*ByZv<=)-pT~3ZV^g zcsZ4B?+z$AxV_O8X0%ybx7F0S9iP_!nFqP1pgm){y)0vZ0)kfa$ZAJzZOeK9O5?3b z5oH&p_4DogY=5v?wCIdg#b`1Vg0jg{$X)xf#A{4DCmF)GA_-*jOBD7u$*TI}g|Yka zJzB7BFyz#09g-)@dR(K_p;G?Q^FL8xoyZoP;ZPJ)(Nu!e8InR#w`#ni(~3qPm?#^1 z>q9;*9Z<}0KQlv?ccb^*UfuNsz{qp&P}pOAcbx96$KxYd9bT%rTB&ZI1oO+ke#4r? zotzg8)J&* zFRb1}n*go>*u_amVAU!J-I%2L^M*6Bbm^^%jRzPQ0cxA|A@=S>F%8k%z%PZ>hyB?zPz(HMM=^S<|z&EERn1$w0UMgAA;Wj@85pAn!l!5w8 zwCEQ3Ni(cC)+{lLqnCYEBi_(7NjQdd?ZmovqQQ_RwqhQ%&2nCiOq|A-yP7iAKa>1} zgX(cbwjt*KVR2gSFWu~VzY}6gZ@TO3G0sg+EgU|+{w*=WvBt$A0t1vc5Yk@`H zP1KrqPcAVp_av7W3#_y$iSDT){SVpqQIdV+7g!RPqEv%uI<~~%fm-5A1tbNp%hUxb zz564j-Tlk)Z8n7nA)UK{awd-mRYOK44NGvA<5+DW_Lk*99qF%JKH>*PUyBEG7q;F- z%Q@VZOy~D5@MBZiK9hK-_NQJhYUG3vmP-4Ek`PLxLu6<>$T1#JcM<_Ha>cZo*F@>k zfgs8c)3V-W@Q!;0cFkD6f?5x<)o`>-Y97 zihf&`glNqbWk)}w)e1-kvPp4AoSo?+k6TQnSromG&(FJGjbqt;CJobjU@H|_$jOOYW|f8|4VxJ-(yf% z{kve(gF9jKzbE z4y^60OLk-9+OL{$t%5q|?9%#5d;<-MbGu&ZX7pzC6)PK}7UH++y{A?}jzkrbV7oa+ zcQPRMce$Or^n)NS?68>OSxA7vTBwhW4OK3U(+-UY75a!XKi1ew;5|4|gwX?+j#mUI z)kGl`Ez$x+Pi0NJbf0<0MycYN2%dvB>t=AQ9vv=fGXNOErw_W+(G5^xYK@k<=~k+r z@qheG5`1kKY`7eW8gp1u)*c`jXITd6En+Ob%qZSR&#_%5Q@W`gup*BE4279xT3{e2 zA5NZTZE4m-^+ReVyF{hV3P$VdgA@}0gSQhvgKX}T+l6Sh_C(|GprI-$9?0T+VDj#M z`JfJI=EvThrZm7z4*NgTKINz|C2!=ZB{3a!uldssqP?JoDD%%jOZ9A=)RJy#_@Dph zqLN^lXh5BIw*qrTOD${+JlyV4Bv&m{BORAr>GXiMgaa%U``~>?LQmX-(q&`=yhPc- z1LXA#icLg&jJ9-bj;m+z??oT~R4fbKh)#8-*! zX>fY>Q~8%S<|9YDdLpcEarH`JkckPxH=GBlBrTGo=&9LrkuBCgr$1KmCz)N-^V^P! z7iS5e`OhVx^2l}6?61kA`0mjJC;c?Y*FsFQWPANPr7EbXUZPn52b!#ai_j(yDeqmF!L;_>kU`UTQRG z!l_Cl!9r*$!5-Su&G5>QT8TGHO3E&U+BYR9gW$I&#muylrQD9f2?UzkiS?{#W;U15 zid%f+4-j6V*_*2t6|ykJ(tSiWJIwA0M={fbZNOkhZV)#JS$&g|+BxBt1*+in#{d8) zRY96YJRy@QguhkSWTxzi0>@MJDDI}Cwtfyk13L&j+ky@*c9%l&{GQ^~_ml)l$hXJu z<4R=cPpT9-$XnF`P1omZhr)NL{js^zp*q>uPyJ~jtD(WFj>u*n(GB`;JNIqpd%;y4 z(K;g|MDw*J>7S(oJ+!}+fV;h|U3BX9&_BH06b)ePK9;BE&+`km@Inl^-q5tIjoTxf zkw))UhqLa;XM?esr1H$XHMaZRewJzcCv%h{wq$n zPlZ9EiLYk^sIWh^Mb`Jp&nKqQ({F1zi26>iwV8D|SbJX+c2MuObmBjR&Y!R6)tUb2 z4fb=-g;q9I zsz#?Wk6grxh*5@qb%GU1hWhaieo#<1*^YfRNf_Wt1iu4RX0AhMT_6-pn%gU=a283# zBmon+`#hfklfVPXG&$yli9P#%L~g0#%CWz6LrTrONdlJ z`zC;x7$t&VGTmH0-C2=f1T*?;JpK@l2jdZ;CG;FfUW5>D_(R}TykHJ`e@86i+q%oslbUj8z=Uz2PUj!DDK&Ihwi^FfpnsYlC2`~ zF#qyueE{yRGaVBY`+-&F@Wj~?6#D>JgDW%9|H(GmvtuR$s{qR9!V?zSRiucgmE4!n z>ryoJxYPdN%6;E<-HspdcWvzgFu)=3U8CUSF+Dt>N5T9kE))V2lWbzuH~?&M$utl# zb z3X%8ySC-|q_6~%JC!~&$cc^O1cZy2v8blpl(KT}MCZv!^#J{DTfv6wjbr$!F5}O)92N9yNnkVg>RwKZKs*e#64p+!LXR;83 zeea#Z>)YJB=ptv3X(xr_?-@zWhx^{SN7@5?(>~d)_E`5<5WW))rkJ29&>zM?swfW- z&ALw(jI6u>u6USly`-0<;0(}B0$h4YTmbn%)C@?bmQNR>veN?EQGA%0=yl{)u5+>S zxExH4>?JN}3CdG=i{5}clWtt7cJX8?;10MY1Lyj1a2bj4psQ!|mX$KsNkrH%IgaeH z7-QdY0UQ9>fE^7@D=j+92d09EpIPQ=*jeBbj%VaMIUZK@x8?h$@u7&QBpem|pk^6f zJhFx)&qyQw+VZ4JBbkv5p!J3=VsX8nT*SJ&JF6`tU1Ip{W!(vxf3|A&gp2M*_oU9_ zSG1nQt}m*Fl7g=5cZd_bgG~h$BieZ^z`=XJE)G{{=p+C$vEd0n9weknKxh3%AvRsu zt4e5fXxh~+%ADl>eL14#?Q_hY$1+1f>O@o7;ba*QVA~^27C~fXO1Zs1dcJW0+OxJ9 z+djkh-cETCnw&}Z2z@VMwNoyTsN?h-fM>2=MxdBp*~?nO?Hn_6O6qn7VR5Q$Q}F?( zw{Tt&E=huzCQGe`Zc{ie%zaL=YYpOA6zlzZp+KqSH@fQRug>qZ(%ccL*4vJhI)NNn z>N#WoqFdXmH z@S51I&W+H&>KXGpd>d(_WlL+!Ejs>9sO?rem{?W5hOS@4bHm7(4K$w%75*>x5q8?9 zvV;q~c2LcZa1Rpj1Dg4rpN^4FU9%~=)7SgaQ_3i7=~Cu~QXz*}2TxkVmPD3fb1Joe z44g;AK6lI} z;ow!51L5k}mdQB7a>8rpO%fsvJyt{?``%hanq;~mGOBN_Uu=a5?1du_l+P3U1W{D< z6f^z~f>zf6t{9zY}eQ9kU-mr z&e}5s>4Wi2W|^@vt}XczCyjI|)gJZ4}4O!>_H7bbSF9L(@4B(5W&xM^-6Mu;f3^$t#x3sYod@Vd@JD5mQ zuZ_g;z4d5Cc?f+{C)f(iq-?URS0TfI$@)V)R1|Qr=u1nE}+dX?)uF~9Jv_W?cYRM;G z&(KmVd?gsl!AQ%cXuj+&y#@Bdk=qy)WO7KquEZdhu>npotUr$dp|Ii_SeMGg)dsZ* z`E(G=T=Fp|pi6iYpnd&?FP?l|CKCUyai8Eu9T>j|bmR0wri&@eTNhJh#{07G%uDL> zt&%D&Iu-9nZ&{L$y0|m!OW50d&vIPBND$sBmt8(Y9`bF+=|6FlF6>Pibs=U>KdDV< z;(*rI=tk=84jzULg1{<~00F(Qdi6}olT`AJjnJ`<&{x-rV=`*`@dKUehQoUKtBMx4 zN>E1xttpu~KfTrYfG99<<*dU*<^c^d<-Y;dDdMJC`f>#z(`n{FnqibQW2GZ9um0Kv zgXqpYkFnYSKZf*$6UW+#n*_h)IzrjBB zIZOsz_lh{)M4TqWVnU)!N4_Bysn8Titqm7cXSx88pIhV0d)!&vg1wP9xut9QcL99Z zqEXY3G*+U``cMO&(DN|Fp3&1j6N6|MMHrHn2-n#YF zJLV731Oz0)oCkAnw+T3}zgwa!(sZ!sy8^d3$|OgRcCKJVxtWgq(jS_ALAj?)STQ#=VCgkTCe3LF}( zc&V-2<`zIpO3_H|#SK^M9Uk~_^1$}*iUVIW!qK{|W+f7W$o>x+fH%8tWmOvRRQT_; zMHJCvdm~I$*c$XF>G~kusHpFj<%fG-L5{Dq4B1t^#7?#67=%OVi9JEyj>Vma_DK(k z6k=|d=XdWJcKVh%;t%*cAH$g%l&?3>vY|wWyZjF32g+;OV~C#Il&&C=p|(q{IRRZO z9szNNm8j;^$1-&~0h3v3@jP%lQBuztx0n^a1)li>tT6Hl(>nj8xZMY&1YI}LCHJq# zd(-%?960t{Kwxx%xT?hcqPuoJez}wv7){m?w|4v-B~!PS7d0#SV_;EPgYNWLKh&ol zFH0Awfy-nz)*!WuXAksrJh@tdN46lJh+%`C3hP?dJCm+Mb{jP5!|SF86!68? zGK^82G6O_NDaZcyj?pkxcw6kZ>dc2A1rOi8yqu$z!#!MId%QX#() z&8KG(F414+GiZ)y(a$9D{4WQEj}TH)<3b4KGJqfm?dp=#y5SSU=va8}u_xAos*vEo7JwCu95hprW}w%g0AGGg-3iZTFjBZ30dB7y=WGl?Z!?8JIZhISXfy z!AY%?w{nyL6#Tvl^l5-Pq17KF3onQp3Xc+R5d{_|xr1>Gpx1&)Ma_hq9CTEGes6D1 z`{_{-k`_oVTkIlDSe_7Vl|kF&UJ9lc)JRYL=Y3Sl9@%3{BMdUO$)VHsehf3&Mqn3E z#T@~+@xb#_Z^aF{6LVhO6KylHlc2a3BUm%G*lr)h+8Ssmqokt`8a73_;Xk%L*Ln?7 z2{kDY_4X`z!-iWx&l|iifVp%w;OU{!86(e&y7VpwQ|;nBiUZ;n$ks)iL^!?tzWbTq zelLe7$~|pifGp+lAOG-~m#&0yAkefy%WCnAU^F6O3;^?=8i)j(9iwrpVoFt`DFtcH zZf$*2Z?8B#?aFmuyOmSMj$leH(rCrdIhQ0n77d+rf0>#Edb~hw9*9a5Em*Jpx^=$9 z++o`nFOY$>#!3;GM4E%-JZ;K?IAA_jUp8=@&si5K;rE|bEL!YsvbvQ_CB1Au8=O|R zM1M`?X>z0RBm1c=%CArkcbh|$MNKch;J<84*KRh-sn2bvQ|!YZNCv?fjQOsBvhScG z{#7q|aqMckSvb=>iSh|D7%D9f@Q}aP!`G^MpY@iW+HSOU|7Lwmb?X_p+JvRBmo$;NC6vv(!Bg!{I*mW zv`;zlEDbt>V&{HFIxgqN8_@Dfqtw7R&y7rs2Rfl;XozG({IQX{A9vYQ-2Kva-`g|V zQbxB>-1vWnzF|SxneUhL#Nv(q)b`TD!1I_FaMLehA*W@aO@$VT0L}xUIBD2Og8If{ zS8Yn@dP>k!q$>QxknDu7Mzejwr%xh;g`4=p@P(SmgwjR_ ztuyb5wxm2P*g(kfDPY}hR7j%iyBO9-c&&b1?aQ=U*ygQ|&P=v!UG~fOZQ_ZOdf zp+_NMw>IJKKlW!b4NY$J^+F5j=r&vXV@E7Hd;wIiKQ-I?*UKYlXK9ScMKTON-D-w< zW;RQG+JYgh$>!ObB%dZREiNMm23r%^m*?_C5yqRxYWZ+=Si9;FY8GhCK^k&51p%8a zZ$u+b*{!It%;PL=sFL#{u30#0mG!@%*Y*K@qPk4LO&m*kW2I1rX+UqNWh2R+s9!8+?3A-zSX}Kb&Yq$ES}bUue|3?DGmh`st%}g zFAXLy69v$4VP(bd^f$6@A_du0)TFhu+$|Wcqlr^hU;QQinLA_%olm)bAz&bM_beFm zY^J_=n`7#P`ppXaGvf`WrqhfdN9P*#5FGda^d!`_zxWbvK?w6DD~vxcqI0yrHZbqx zQhw3_l20=_QoKFsdaq~=HTtv=z7roTZ)svEUaz_WJ;JH@9P7|arg&u;lW5y@sp-eR zt%7Rw`3EDQh4pX@{WhV-0E1G;;pkdE+MbbnQUM}boQ7QZJpAtt)}H%A|A0~CMv7_0 zlgJ(I%%Ql4r;Ex&QZYCy6a)8c+}OY}EUA+)P(Ep>`46bib6TyMyiZkt535PFNwhiu z-e8&l*B_!kCs1{d8AhE7Y>^&E^~QcL`|(YKUG(g|Z^U?g@EFPX3w{XMrVC%`nQewi zPbdGUf?~r0dk+l4pxp!`Iv>{dweL^Td1|MCR@>DQ&A1RTJdKwh2Hn2W0R1-U%nu@Zyxz$$LV%7}D@Is%)kC9M|lyNDz8t&S6GiDq3!o>td zW8C5M{$c-dX{^ks(mdhD7x0?CQInhv_e(aCatiJ{mt!|;2nK=VU&u_d z9<}k86HViGXf%s(M~~HtM|xO!_MlOA8Gcnj{$Zf?`Y&$(qYM5X)OP!opPgs&M0~l2 zuRMo8e%F6cZ9Uzy{oK_eWTkU7H(&>mYfGyJujmCjIf_$e4Xb%Um#M-HLBL;pX6lX1 z3=I*m7#URW_vMpQXAn&saE^cE*5;B{gy?V=-dg|-Y0TI#TLs1u2A|@D0gn#9`4o?f z-65=UnQd%Ix`!&$L~G8sC7{61R6D>UhKRd^F{h?-dr~uZ1m7X) zl1=R{Yi=!W=DnV8d{xmy-q*Ode3Ey|NoNft;YD7!#`4)uWIlzU5q=+t0;f8n{#Dy z@gp@4l4r&bv@qn*Gb?T-vIXE=I~YB)1}Q5vd}tuW1C`=))d0OxoDSsGgz0}sYNZ90 z*;*QlTWS*0{!mp$T{9!@jdpw<7A=zGw#}?=^4m@AMM%$RcIwC2WDaZdTcODQ(A{>i z(B7Bm>0Dq5LE};jbzHpjIzZH+7p>-?6d>9r_g#1EYb8@J42Qlb8OAlzl z(X)7Nvpk58NDf*P$B!*Er|^~hzJH<0^$4>sg8QWjz#?N5>Ve)#U@tKjIfv!qH=omq zXs4)AYbu$FQ5klr*qQ?g+IAxH)&*gJcbrybo$gH;JP0P=a2{>>9SIz|0$^H-ad@Sh z6Mf`pW7q*q{_dRIwm zWn|QXLpMbydkADQQY(F(b(C5{L-Ib#Kv#ucUxpP zo#=>p;WQUNbLiPQ!omCb{5@l%363Vo{Yc}J8fwi;M~dH#R)faysFdYF#o}ssw&c_b z+8=LSa;4_rw7s_5;Z8p5x#5@5Ly0CwnI+#U7G95Cy*_UU!))j9@W zpp-HjS!wdJ$0a(57}6usM;#_VnMy)$CbMba#RRPCYh*3l+RHGur3M1QxiniU?vv~K z>uTj|dtu0tG6G*+|9jq^kR_y|oX*fF-?6hlZ;6bsD%cqXftzmAg6boaliVZR6q8EEtd41G7oSZDK#839|~YcKh}Q zrB-no3jjdV1Bd^_454hDMJ!uhg4P^+fE(yi|18)`?YvK|z9Q$jZJrE#9hf2PUx*P7 zJ7I<23@CVPP!JQAqVxjhkH*6)?W5?xAgM%@QmEiHY0@rxt?Ex2vl%GKL2E)iAn;&+ z-ed#SWd}l1-@VgjA_guVLq)-n@51h*oGBKyz*B-#3=upH0dRSSQEP})*M3OuIqnr= zm;jniX{LGgt9*aE07p8~%%A+*ZFxw0(Rco^cGg_lfKh+&F3j;|Ugh-c>F<@Q@p_L3 zK6Ty$C_@)3T4hdKHI;(cmPOi9*tY-V;f_GBhEf}HUA?-0{o|dXUELe%#a%!6+F5Y# zQf`fPuB6xYFWep`7upL+bv$${z!f^?onv;7rWcb*1X+%=}zesLO%c? z=?I9f3zl@Mbq)-~n%{HoKgo?1@QXgUJd9lfa zCtGwsS}5f?Uo^vo_q>FVWY&!mMCY#9-GdCK1pmqW45&q%zL<6V%rWo3sgh3zR$T(x zf(#S^1#KYAeH+Kox*xEKSPGZRoji+H*1il`zFEIw5-nJzK(3+E<-s|^^aclAZap}Y zwaMm4%2(=v`uQX*ITR?@hvu2@6A!pE*0IE9J>$CR@-B5|vV9z#w)VlRkMjm$;uX5viU>bK>Q>}9MVg&aIVoZ3SDKE#vxGTtaD`p`jhDW!7 zAHP5aBR4|aCR>s2w6A%z518G3mF_wxV9hq10g>9+0T`aMr z#ct%J!@=*fD$|!{i8Y$l46O0<-I2RETM`brDf~E?v0qof?;6>b#e4U=`0l=>tR@c# z1~HyfrIA0(LtQnn?P6{Mm~1?m<^e;F%6zSq8dZ zaqD}Mhg=LijhwHwC)Nw98(d&;n->&{7r?PMMlI%5d}&dhYuxOqV${1B`&|l-Vd~*0ZJlEa?2`3hS zse)_zF5**lyDAp0HE*|8~DY(Se%U^~6S^kOetnwaT$QNAcE@mb%eUt|aV zheah)mtfVfXV#WI+Ah)ww1x?SeI3H&{K7S7F~tr-{2Pi1(FM>i4f#H3{;jCgG#E2W zh+b-qk*;2CXtZ5T7tO~QKS#d*{&AhcqqB_hG)Yi&junQEDC*|A>Ie&ViRBUP!q?%B{b$uAlI-7U+bt8Je+Ye@#b!mb;kcRb?lGKiD?tAzQ z#J19P@rib*i+{h0uv(^ZlrLc(E+oS^XgokNpkD~(oXY9){kTOPuK2Vc*ob-d3aXL$ z@Q;W?x!Mpj`Fhg9Xg>Z&qhvM&YwQ5P69!5SH@22D#C6Z+T#xt^vvjWw$C%8(b{Pj# zEL?4~^#v-;yjZ}m?O$*hv|)ifxRFiEIxjnLUa$W>T{jk%#%Kf%c^KCHfi(FiKKk!p z)M~=hdicMC$78uLeT6E-GVJB1IpwtpeSvKM7FAxdRR?M#4m$aH6yHF9q__x)ARH+j zjl4JDjt3g_L4b`C0(HtMze1JL16{kjcj2GNZ`zo#hj4#N-pL=@^C-|$f0CO!L@F!@ z?}re+`G?ztfZ4oS*HcHK-O z+Iw=98@G|ez)`U~_aI_Q?f@=rZ`5UGf`TA)R3d@FDA;3S3h(S692UWU)l(?Iw5EOu z!?sCYkgo$OeM&()*NJvv6RNyLqP2DPK&_%LbP1*=NJFF`AEyX`=BFHIR<$kMrL^f> zexNQW>|@DLO9X^T)*cE?VX7&c3@OcMnoXBca3uvR$a|v1Ex5in)LIkLF3^i{#^*uu z%Jf1*5F-INQ9jlfq~5$ABzEPP6k667H2!{-2uCfKOXy^n@aJN(41p5I`_id9ZozvD zwrTYf$i_#MVyT|^=-qq}092{gEjNXRVEHeM+MVbWzvhTz@sy8K+Gsid6yF9f2JY-YDX`v(uuv#lt~x*< z;;BP+W-gn`)MUsPgJHA)3#7^gx)G>tl;oqOMzSS6NKh)Xcq-&4oB4S0U_S#!#Ztb4^D!V3Vw#`i>7zM zKA2GclGg*%+Ogp)EiukEcfI#=Wd2QqUKWi)){Y2Af0T z1qqWZphZIy^Zvx#FtWpXdaA@0ueGYf+%9K+v9rKSOT%^I>@s1<3^;Z0J$BCzg#Mv| zH4yuQfxvmh{J_Lvy@@K>dUn48OzD#tqegle`%- zRy^$Gy@{`Odgiary+n7!^Dg49!Wmw}7pgr*04y;ekwIk@Ist+>`&Slk%!xC};`w=f zLlW5!yN*3v%Vuo_+M!$)0z;^wb~b^_%3pJW-PkC8{Q$@zkD5tw7HURwb{GhqWawnP zEF+_rq1Gp!0NPwD{_zIXKiy$$5rb%j85;d5kq+X(BNF>}9~|69A`gH$`r*3<(mYuK zzC9N8E^!-RD5`s|1;@-DNX3A@d5R3ibMw(pt! zK_nuSrvdB6XL(Ger^x9!lp+#miZ?*NlMTf8x`M;o5i%^TxD}DLeQeGWOKqExpV1%J zlu1#qN-35VjcNW`XUY}gX=-QcrqW`y#(6l!%_dw&zEE>!&Fi6P92DwxZxn)BHEc2H zityI>eXuSwNiZ6!L!p465Cl5O-dv#er){1b>eg(yK>{+n<^(R8Rs(QOWVJr|2Xpsa2^*0xYXJ%0ndou+D z_lWAtYyIKS30~N>nc6JTG%iHTEm8Wc76^H$pFyUvuq(h^l#0=K-UQfZ3j6c2(@ubr zBSMJ;2>;4a56@HZbSy~Of8TlQQB?^pzLu3V0YN|p(wM3lWh~p?xxV&r@Ue78(q*vvhR3V9ax#BDl2_h z6ce-CfHoBz{Hf}>^8J-e!||bxb{qpeC%#ka@eAJJpSiC?f(Ri!3ZO&!QzrF*KSF|L z#6zc3Ai>SmH)1GY3NajzJjwq$2s4ZUw4rV&6LoVF9JthB2ce1ddeR?qSHtV-W6a0G zK0|W=e(4`c76lVL-RerdFga?eTE;(O9Ln@kNm`u+kyo*~w+D``^S;VDgVS6`x{Ce0; zHBxK=s4^L#$RX#$ei~h`Ua!;(J)Elek}FX=Ye6u~G)#voO>}*F38Lo~9k~+GLIc_Y z#pd)EF#xND<|28NXE=uWkb8aV(}a(WX03!}H@<5-0WipMi~?bh3p|)zvc^r1NV=xk zf)UGP2M9Gb|MYV!1nujm4~9iIyiS1T4JV_9b=}{Fy8lZaBtJan-*i_=wi|8f?7a@( zPQNm}@UN-gg+xv!VeTp{*)iI?Ow<}y9OYXaYK@rYX0mn95w(<~R3NSFn1uf1?U@74 zecJ$SVc8tO?=@daw5PgD6phBDY5rd$6TTO5ZJ9zeNey{~9q*340@ySzze2sb$6`aw z3#{u7SJHAA?Bcp>GuGoi#|z}tz)Km>zt=n88}=u1U5Z}NaHY;AQSR=ywCn1fZ9{`T zjYA7o-q&YdvI6l5(d6HoK?vsqPS@-M`x0$rB=YlIpDuUVr3fTf2IGclK-Z@m^wl*4 z)br`lFPyuu+YLxqH0X0f0^4Gz`%M!}ufXu#AEv2)*&v2r))V>x`0`n*P8G;%Aqzet zq>rlPOEH7zfNIFY_RYq5Zw*+QI=dR{L%YK3-neS1%yXMNIDB-9*h&wX6|~6`vYc&& zDH!QWE(4Q*xz11ro^sjAHyF~z9Iy>wu6iGNC3+-gM!w9bV#$RaKQc^;0OU+f*e%*@ zo^MJx8|EqF%j4tPPENT06D02UR=zB}C3?iAu9cSAIRo)_ApJ)|DIi8pkijj_(Yx&75^%L z)DB7(*C=}>z=r;~dbZ2t6K*_eYUB!aDG;fLB=Vyum41o5GxLI_ z!?y!-DZ|xt0kg$hLQU#ihG&~KY$%#wq)l|NBJ;%rshr`w-HnJ#gXr#WI*NGl_aMO= zbcJ4x{k*>?oYp=F7+%3)4JnN3Kr)-``<=@D(>&$l|=Ncf9B^kmyv#lz*k+sLdJV+SXdAnj=4O<0=u`a`|!F}=`H_=2CA z3Gm_QzOQ|;h@z{>@^a^GnEDZXO8w)J@(xfums<^e&4b7u>#nlE7G}rpeq#RHMt?Zl{bs>Rda7`V#P^sMUNAQl%!a_9;b;Fs@NR2jb=W2_sW<;R)Q8s^k?r?rby1&)F~)$O;lBS%yJ!#BB@9UzqN9eh(+$BodN~Eq zf1V!INugbRD(Rf$`;hHp21~ zF1(3KL0k z5qEq1mYNWG_xzUtaG=hwYA~TG@^b@JPNGuNPz$Xx+5Jxdy1Hsr zo;cv#6fCI~8A&;;lzE=ECjd^^Z>gq@ZIz{^G7?I2Eay;!WDV#4kymSE^$iE` z0q63Q(4J7~kPvd05tvc5H*tPD&L(u``F0on#WIbT1de@(rb-#t1IIfZf{t(b(y?%| z7;j)2VZm8$dI-J5mo=ANZ(dVzrVI8CV(%yeAUWJ#{j=lUFCEg5v6f2oNgQGIi$9Ip zNY``NFszPRB{EH82nw&$>lHhfCE8){gAaBM39kg-fzM;&9lYv7Hdw@aM54b7%Dg*+ zFIdxPc-d=GbQ6(@wh3~Mc2&W>rdF2|&x*F~bwZpYN@YiFr$kH38d{MX3@BfV)|7Yj z2`55c(F1^H;5bTQ#kNmeloCQ=$1Q_Ze7hja{3CKILYB=>W(cEX=J4FLA~h+tlqrU@ zqe}@tD^-Ublo>3U$6*~#RH@y_ya;{Yu7b(MXLcxxyi+Hh(&SMF%6zy@7g4|773PSA zFv|WGGrpq&Wb85mY@SlaCv2V#){NtbB2}NmF0)_DKG<<#u0~!lS&5`ZGHfslOlil= zT=^rg1QadV%jM6^=yxzzTl|03_u&~3jHH4@x#T~3VjiWrPXyV3Cc8tldPq%t`iuiq z2VRv4$>Xp_XVAp@hljJfRE~Y6qQ{x#7;cgIi|LT?9IaB%OOJ`KGOU|AbEdtn-#Gly z(hFyeqZ~PbC#fg?%VZ=XNC{eDpRo-Ap-8%JRKKG#h*950@WU5;XU!6i$S2|b<&;9> zSc4xW>dG3aog+~2dhGsSp%zf(CYa%$r0z0>StZS@S(g(t{~}XE8QsJ>+A=lYPcp5! zafbxm)RkXGw(o~t%z`-9QbscH~$!Pusa?8x9`#-y*HM!T#07%3|-Tf_XZ>kro)B^9R( zR$5y?EUk}*V|GVeTLVm?)4<&`v2ZI@n3v=h&AiYpeIa9U8k)lWXm%8_m)lVqV(zrk z@7FFR@-{ApE?Y-iVi=nG)9A4a+gfFy?ammMtQzeq+GkVR>DyuRX5IF{ZhdwwaM(^3 z!yaU#E{dOAvFse4dW=r8@tL!zE-5qz@nA#3j@pO_~rfoD%2-q?(l-y66 z;xH-f$%T&K2Rk=pb8Ox95l+9xcK-)^2G{7<0a>Re;$Bd>KT!@0ix!wE5vI5U5}?)0 z-yVV)dM_9)i#G;*bs4()45V4@g*(u>NWpNpm`p=xsmLi3{d%znrod)fO?)wYk2vb? zpNn~%fbqejNUS!s890*`A-U_L6l~}dTZBVPR*qH>SoApk#@2$C(<)5PE}(A+NS~?i^f*n^de}Ws-l*jHwV!d=CR8f}UC&c}O2+ z&w7rq&AJiJ*m^p^l4O1qa0F6~DmFC9Q$6`!A*dHe;CBQi`Zw>HN)x?)cUL#7a|@)l z6(L6PllQD$p*1yFyC$PBZuz-LLt(2|!Sn_3`|F@mqAiX+DPSKXCx^h-zyRL2<2Rm) zpDORu9|_b#tm6#s;JPbJKw9@*1n0T{9%Y>G(YndG(tE_LZWbDyM{6wk7%6nHW!}!|V5=DLoH(*EAHp2@%P)*mjM$6N_4PEEgQ($fg3VqS6b)fPreY>S zdgGY)DBB@~bl>KzWa7VJVTtC6qn@ z@jGPcT1UE!aDBvtFUnCwK`v6=sBMR4d$v7l- zp=rwW0EDz%0O%)LbBF|J@NeEdf$=4#&|Xc#m;t>KhuVx{L;7xJJ7_ss@npk`iLYYL zN}pw9XqU(uyzO~5{7VDu5RHSN^(U>RuGn*}3rBP*VA^PRmdV7P4n`4WkJ+Z5NzvjA z{g=S{u3P7t`zC3~xsMgBhb&X64zw7!rq^@cH2F3rw6g%or}PUAaQ~MVR*sNjWhJ)e zcheTZa{t~-`>=_*ciXs4K!JWZF_E&C$4~R`_-Higf9KGTmhp4nfJ5cp3B{)+qNjR1 zD}nDQ6P44gP=bD0=P57|uUq;F{XwF! zn?3p>=_Rx#`c6bL&z$7MGyr$W2I|Vf{>db?o3waSH_GUHXvkyE(^j&Qf)3FU(H2px z9>WdiFx=OH3SGC^CIU@kjn|<>!#``n0tL$61y8cT85d`sR!#(w=NmktqKcah!B+)w zO;qqOOq2sDDpjlg(WQ4rcuJ3l>!NoP4A>}ZkQ@~S^wi>jcZ##|hB=iai;*RVjBl5$ zTpExPA@fbZz+#Eg#;=_#u_#Ly-c!YUhU+NQ8(J|W*+W)my3QJf`-+xMUn1s1>be+^ z;DoCy_)v1cCQracR~$aMIoPP@k1R;@p5<;lg&6H6n}sJplAobFm-6Av4CGQ{!ar6r zmhxV@YX&_exu=#YE-HSBdddfaT7zasMq5kEt};O$-ie%)+`8{P$@= zkcdMD`mqFL3sYR2_}EJmW($Cj<}Izpwqs%y{w6~vYiac@WhWV4Cr=drm`_!+n1H3B zGLu4BHVH#Z)&9;zq<)EJSTR%i@o!V6TSh(-R-Sk1%Qj7*N6Hs|uxK^bv7>+jw*2W=D08eOfR!{NHEcbSP!>fCG1+{idYYMBPD}$=W z2Lpa-jKr=B^Vc_Nw%Ho|Rt~9DshCb^DQjE@ei!M~x2hP0fM5*l6S~+EhJ=q=w*ZK8 zGsUgxrWB)h2x%?bMyV^+*NN$LYg4QrtpNRK{lQc(eTScW7#}0!B-N8gx1J(MaCx!C z9;+IJB5z71g+Apf@z3^LL@M4yd3eV=SMYEV(5Be{00jmCo`ZBmf3a2?sgIzh((Vv6 z2H&a}`UXTZ(h{RPKuw!EpLwx-bwj)mLgQ4z)p4>`&1Jv6h~}n~NndWx6S?wiVijKv z+>hyr$;aHbi(>y88B&@xKS`%-v!xzWyn=V%{xw0#_%B_0(B>?pc(4%Leb8%gstcV> z_CQM*p&jq#OUP%XOyBBJZEmC%KH%-aM8&f8TKQjm zBX&aOnYOgneNegIm+MMv)D0`?K)nP?qJ|o?yE+Q0yrle11mlQyQ~9IiV2jL3w>A8Q z{maZ5KVC#*!*%DhQkv9#?^_#b*_0vay{891DOInQNbJhAqQeh?!P^ARWslY1a(`>` zd(|AxcaQU7wTNnK`T?L#d5J9{04>Wd{$>u?ng|f(0c+UDtFVc;O_3jwGnZ7n>U)Xs zKaW)Bp?2eq&th7-_UVGh_>%wU&?lofovmwMd#5^a2^pvtJU2dD z*+`|crU-G}I#XcMXc0^&^YJ!+spj?g==4h&qWS$xv9wT5_%%;*>@nEDjYmaK*Wf#5 ziUdm$n1jNv?A4Yn!n=X+ph@IBmy?0t8WO_3mX8j=4*i4LAwR-OPWd+UCcN$kBe)sW zBo&6889Nb5r3PMB@1j&ICO0rv={@l5Uj;+UEkOvS z_+0HdGk?^Rmz}c-UJPE-3$GwtcjZ#Oec#wCj~U)7IxUY^Bc_3ja|qS=Kk6vlDzMh& z*bP5KG0jpV>6-@?>HCO%PZ<51&GR;h zTu5{5U*@j;G>L)y3=+qCO6hwYHE;=*P^^fe9YKaN#2Q$edZ5qf=QFdh^%TFmV`f!l zAreK2H=<_2&n?g+J(8VOthJA0ojeev8d3e z0wGl%qB$F^V#RvzCn`N1?D^NbKPMprf~&D7+X#2k1CyXa|x`YwN}r)@UTgA)wu(vBvlt? zR%>S%?om&AplSs{TB@Uf4W#%IPep&nu%q$=sAu|R$uY1btjV?fdL|rdrMr@< zJGnyEM9TB>u0t1euy+NAu-m_F=-Mljcg1#6Mrgm+Sf)Wa@B_o*SUWw&n9X{^!m>~@ z8}qsfhVq6pEXC7RSDn6T%qf4R$&m>hCnIqZXH@ueki>>}@3X;%CL8v&e_=|W$o=X8 zU7Y7X-6a(K?Ea#VDAkGtm(Z``IJ4Sbvs$@;GAQnWU{=c%(@GWPNAjDoo02{_^lTlh z4RGNI=i2<`%{htWx-*?`p@>ldfdBvnQvsfZYD9nOAgk8PU^?>}b1ua4xH*Y@FK%HKOipG0gORscbVFYj_W_ zm%Zbl((6}<4XG|L$iu&;Hh=&>6GL^TT|`sk&>Vk0B4%+CFk3Vn-Q*N}^9`5~m;_H} zFs7=8HV9xNk}fe6nhQlI?*n0Y<$RPw3wiLNI88{T?pXHP#3(~ zU#bL|DC8~+JpG9L>l#;DEN^ZQqp)-3EYAD6qo%b|i`gS;QOiyhfBzM1Z09jC>`rBv z0$Oibb9o+nYfX`UL`7|3-WUVqX!)CK-^X2hoVR;DAOIoJ65h3&OxsK`^9%^w_6E~E zynaz&sQo6F|FBy>dm@FG>3VUpt_44l$1YR$bKWH!*I8#n9ZWN+Huf6i7Y@Rdo$0aN z#(nV~kAnp4-2bGUIs0XQ8-IjQBL6!|?@T~j_5Il_cu!l@UPcPF6`(4>SlVlHF0nWgJsgZ!gk4_p<(Cb!1h#w;mw@2eBD?ki;t#=T z-lcTMA?yr@3M%($_3B6iRVYc?m;nMD*doLsI^eqw%-<|U^q$S3P-bO;2g{-XMI=-OBB zkzZ)^W794m*vIhE(9RSM(;fhB*L`h^yF%uwmbDn&BCk1J;f&IuhSH0CR|tI3 zjYs4TOEo%EH2O~M+{`h5+mDy?3RsG2>$QF& z`Pqnf7o6@^_kfmn#TmiQTS2GUser-XL6X-#c~4R<=6U zisgP0!PWNV`ew%W8e6z-$Hz&hkKjU<0DV?*DK%>AUdUo~doiUV0IEGEjpB*98Fg80 zR5Kw}obZPS&WtC?t824eyJEuPu9XL_7k%>y^>AOlNa++zRxL%TdN^1t%U`ukX}Rn+ z84NFMzu{PGcf#}`kS)&@;F#ICPs8?|v#+>8_$}N40odLB!7gf82Rjy2NBKuf+T>|# zB$weq_c8osAJt@R(dJUbEjI4$YQ3obv$<-3oeMLGcC=0Fss-H*8r%>_7@FebHdq1{ zo3i6h#R|iNSD7{->HU6U{+#&vT$F+d?BX5?_Qa6f^iacahEy6yxGS}EkV4|k=NOyM zmXO+0Vk4T`ahnHnTEIJS@<&`zESER^j?=H#_yl%T@n{9Dsd#W~M8DD+d%zP>+RO7C zm!B~hv4J^oi7FTjyai@`WSqoU$>M0~6}Nl7qv5nDn_0T?0019~L7Iq3s6l9%Oce6} zyl{1@T|u}|#$%S&tCGKKbA_Qw!eyxK8tXBZUA+f=FP#jr(yvP>aTZm3Cw4=Sj|=&N z%BqNoN9Y1KxO^Di<~qg>ayD+W_Z$48bMjQ7_D3e+7U2G7fa~R<y!qaorg4K0C&&bafz-3MW6lpXe3Ui65)Nm70lGrZD(@WM4d*jrd`%I$PNDM=3 zp;ji1KR$cx>9p*IgQA~(n!{`mL`kiYn$=yHu<^uZlRr4Ko;G??&x^xwzGwstmA(IC zbO-)>zggMO!ytbCohh4}7%lU{h)%G`72w^zeI-gJRgjwK>v>KWO5}ji1rwqbV8@9g zu|3yWgU`j$?8Au=#3)aM8RzW75y~|c;;teIk$!H1NxHQav+rv|d1b3uK#;ixvE8Clz~@q#jqIudCm zzmUOIIil8w~664bf+1q_ZgB^(p6{_6|cfkxjfD*_?(&BlJ%$unLK!d~COZDAXY<#L@n5ejvGXtoC` zcrJv^inZxu5>+02FFt1m?zp@buM`sx6p{*vQ{0I@_$+8i^d~}nkq`L4jf|Y)T@*|7asDfE?uSYSa>N3?;zcs^S^D;Vt`LU4i`fbAuU;QgQF{ z&6Ab-yrRl_8~y~ z*hK}pux)*FOpGmVKmrs0p-a#PmtZAe0l}B!^rly6aaF7RBn^-xD&Tz&6kfMM`*HV4h$@ zl*f(m(Liq!vw|R1E2KO-V6!JO(%Qa#7qA)9#Zb0`uGwM(9t+`wnoI4YtBb@5!=jxV zU%P~>qFN~+J8w)&3orKiPgA;V!w;9T>nS%{-NHyHqPUM-+xAW!25+CAeV4kRMWdUF zHj4O32=|>v$jGlQBgAJ~8-+4rSMk_%0Im$~XEvNqRg0;lvVR&n8rF2EAjPRFgw z+yeJp6DJ|)T-KXa&q#2MD5kb=5ixG_=61FW2G`kt6zwGq*ce)pi7KOk{Kq7%-KJ}h zr@^Mo`{AU>J>tkoQXM&#PyUcZr+PU3p|JJIKz1_i)m(Fos+LVMC2yP+<5=@Og7UT# z&xz5KvWe`4URNGnX*^<+Hp@1;>VQVBkwIg0F9y~7XJTVfA)wDA(!oXG&e}7@=6)D*+o8GmMwJz(+o3N4uV`^?*v1ZfKRk^efb0Mk9*r>qTfG> zrm~X2m)I4}l@9BP>gW*ZAs>Dy+UnS4JLPiSZv7f67mWDCZzy_yd5fQyDC%O?Wjx{E z#k5#zV`TL8#?JFt)bzGeQF?jfKxuba7Q3C|>3Jj$EZW-vGcT|M=lV9F?kzhG2Z$SE z>26G-ed!nuw)js26KCwWb@w`^Fn8z|sxc7DgdWXlDY8Xq;26BNA8ME9%a61%RIQ2x zETTs=fk$xeB{$O>Oqu?+0F|5GM#EwaJ|)68y$|j(F{>!UWe)~Vd0d5xB$F@ia9v_CexH+%&z{re52~ynkSfe6z z9hm5e^!Hc(3!GUdZiV$Ale|(|7&+uOi~{t0(FSFbU}U2{fZ-l^CQDc4KzCW7w66Kk zxC2?Dj8M-P7gG#XTIUkD&0o*vylFCIu+X%xVEOq51PqNUZ@fk)O>X2q!`pFFt1z>n z4pqf=bZf;Y00dtYxvnNjEXz+Excr2pMJ=ZX7XPp7fvQlu zHZ(kw;W-9y}J4NtatR%;j`>Qsff!>eciqC-*yKFfoGPkm`KMY&{hy z$>-ncMMil&AA8*?6^PbD#u~u>TS+N_^Js`Am`XV-V=YJ$;*BhDsp%Z+X`=3wB+lR8 zgh7$+Czj?_)7k-8LlB=h3oQp3LTS!%^j%vN9!E>Fgro( za3D3|+jfd25aO41-_VG|w8PF#62!G&+v4m=;(o3GA=iyY*W%o8>N7x3l=`T@5Zzk* zGR{R@LWrD$pKtRwU>+Sb+uq~dUQ7)MMuX%H%x-6cwtVJ$3tIV`r-(8LJKvW(daUE2 z1Uaw@%xPqIt@LRIR&9>K7)wm``~{rq8LSOkFt&;A^q!KM3fUt^X)F%rk4wQ&k z91@03^^gSur2CV0N$NQH@Np)97OBSc5e1+pBeqYo8awZ>(YrDnJ#bWnZkow+&OpR- z==jdEy9WP6bEaIC0t;7-BQb^kHOC(CIwscoB&HDE!+y;{hQ(F| zxXK>tkG$}PCt(XF7;~)ZwU<^+vCrBz)pjqnfW2$eMB}R(fGI?Ka$X4scpFJ)LHu1^c#BPd@%e0FGlEg(Z-mJ}X%C7I8=fR}gaPWoOekLn0aG*mW{x^%D zRUH2*PWq+D9)tR=rOtRmN&5d z0N^KC46O$UoVt5$5r+P82L|dm<2;1A|7rXAu{+D^FrM{wIjVZfC0xWvZ51c=SO%j~ zI*{*1ZN&?r-}(oGM_Y;6Zs$!>MNro6HCpmj!#_y)dT=Z4$;04_zX)X#CxR2u2>7u; za}$!At;b6x$iUoy0xq@|f$KDa14Pue9hES1+uAv9kL8-9J&h| z%E3Z4FdrjRwA@m$P8GfF9pj<3C=7a#n))Wz_$@A1r89l`6;s z7ehbj(XZB5;&c-lM3A;{#juu|+E?MIGDHJ<-7=m*p&4Z=pO>CbPBCdbwN{bO;DA~7 z@`M*i($$ngtNAc@QDL4BDB_t)d~0Z>w)hod%oWLOM+kLJ^c@|%0IP@Hl>85sBFlt! zD~zF?m#q_Yy=;<5rWJdK#(BuM-vCWeht3CD+<#Uf)ca&}Gx@XO7@p8~Pp1ZE+Ad+~ zWy0?YVJ9?lTW1Y4sd@2bS&dFTDOTy;q9zEvJZ(m7L!W&-S*XyqxdQf;;{$Qbx7wMg zMwqvxK@o20h{|;a-N3||w%pB(e68C^=W5$-W&Xxn6;2#m>q?*+Bcl;pGF;UK8W;-< zTkfIAY^#&%ikG~-grj$Qd3fwrUGL;HXsX9ObXjdV2Zb{w zpH+to+;nJBFu3i7PN#$`3>5;noV8%;K@D=k&Cj5`2A3r%e)phhOHmNn@~gdJt8l8T zm#ng=ft5zZ3!k#O9yj9RUnSl_4VlarIQ&+C+rlL}bk=v4PP-q~gEeWEHraUdRBW*|9< z1ffiw1mGbp5MB!~b+Z4?`pj$lHVhi*qfPF4bAp3ue>Hlxwu{3g);0s0Zvwxj3kd<) zkkK3Xnkqk9Z{jDW^-Us;!g0wbHl{l!oDh za=gcsL512B_0x75lTTMS%jVBAu|L!g_NVF?5BHfN!vx2l?7$FW>!g;c-tCR=waf6yZXLz&+vksKu0|N)>vF_dz==-NfARKLX`~j&;*@rkNXHcV z5n+`sC(M@+Cw)z1MlN=TC9KSH3G;q#*l%dOOsoIKAOaJ#Z(T)3FzHc0*PR|9l3-2x zLjGVf+42&i(H8{jDG)-H>q+7rqI%9cz!dM)vfe>T8pVQtuN;dGSSsptK2ztpSIK=l zshseJuAwf_4v@A2!CMpUhw3w5;a84cQCU9i!dQ9ZpR-kMI<*iWAF`{X8)WfxXLi3Q zc#U>c{(HwejDs^*3vSz$n?mqfgW*9+;4(|qqc?H>qAOq>{@Jo&!VdRJFl>~2dhC1pDx z^x9kz4vQBE^ff6p82`K(GvYI-3ji{O%_{WoK5xqMYyF*R%shsE-jkVlPmPBq0U;i# zEF;gfym4UX15HreW;%`G5x>`2GhLu5?botfHkge~Wq) zUrSY21e{BXXBq^e=PI~K6AMlJ-)+LNvTz%(g7Yx6CU$%*ST~jZivCkfPOM9 ze1cewJ(O=Bih%(aW%cz~rG{|;+Zi}=4;9=`eY-y1!5^xlEN$#F_5wPblGH6iz)Im=U>54p0pJrO+n5J z2K56+b#J=reO016$YXXUzb$z(^|eJnjalp(RRa5Vtb3*}O@JI*7b^QAw+R z*KkhGM6>@yydV6EpJUvtfdA`{ECiZeWo%jgW&rrK216g)NFJSek@)+Z?Hy}Ax3Ce> zQ3`!}LSeIFz=nB!u&|A^A0!E`S%qpbrrDQ-WE|%`MVeU^lDxplu(0t{o8A`VT~SWP zpZ9*jj+Ms=<=gDDSyA_Nj_cxVwUkWztaDOKyN_pgWAHncvFb1wZ&eHd4%q3@M>ZUeG9q}FB<&N7pxoFg*zO!y(F zc?|M%+zap4e4YYD&!JcI$*6YU#P|TXi3lGI(lxNzPh@3kuqNeFl1&O4agXAZ208|< zDyeok&KQJ4Q%g_iHM1-w5gZ?=lgjE})wAUzEW#3&DcDvGt$mUzM2$KTL5o(WIZen1 zOt#N4;<$^o-VqmP<6NkX%keZPcSi3?xt$;b;3dB~+D4I5aChRZ8;6N>`2SeVfyk=Odi6IHk}CZr{^dWJ zILbiY99$eqIvvQ~r(G#;v-->E1-R=0=;N(O6CPty=x5y)yE~X?jM@5(PV*L?HF|i7 zjT;b|pu9NspIzzXcAs0>L27jt{1CW3CS5$|OER;YC&mcMwx@&@D)+; z{_d##CTHwb8{MnB3eN`$jXB2d5Yd?c(8sfD=heRVrMql}3$snUWCgI`SWtS4r~oYX zCLJ0~#K;nE)g=nIz7)L;>t$gOxgoqq{XO#3M0f%A&wt!(%Pt4vi^p2CX3?$#zXZmT zA5*~z9dHMs6e1IQr6B%cO}63W=&UnTBs4nqSi2G^P)-mao~<(dJ-X0byIgEz z7(b@AAzcJ!Tm)TKJvyaE5wi`s2LwP=B1I(@WN0Ny@&G9s)6jSBK0_La&FT#IMMen^^01=ig^$np0rW>PnrPw4Hgmzdqd5 zz8_%~k9B7ZYx@QSRDlCado1dr@3^1$qP?Kp&ovoUN)Z}HBdl@$C|0G zuUIXznWF`C>~FPc>JiVI21LQan~%EThy`y~dNk3cj_d@FDfWO|3WYIW?{J`pi0G## zv~rq(v}kD!I|vdE?rQ*xX*?K57Qpf$`we@lV*vaFhxG)dhE$XK3GBu5y+?p{+lZPj z3LklIOArfhK2l683;EP2TWfgmJySib6~=!lK|+7H6Hs?_j}!cp*beI5Mq!7)y^(WI z<2-IK!50FhM5)suKo1lSBUAu-p>RLH^dau02!!vG| zrZl9@Y@en0)jT~rYmA{_W=jdQCbeck zM+8TP0zfluJTT)tsxIZg=Dti3t*m8XaI?33aP7k~Y$lZgBrI-G-uYJ!_9{IPqi-sG z+i75J0ZY0+bRRNxLAsCX*}L97qX%*CUUlhi>BTc%Q7&lqd4Mhq++-%bl_VZiJTZc0 zYRSoOoa&sIUioRW09(=99QZ}(ZdMIQ618=a6AS4Mqh8uk*LZ?+P8-GG6{f92hAk9( z>SR8HP&_$Qzu0_T%z(42foj=i7@++TfV|RV%>r@cI`c;QMjL1sOuB?aIe918(Ks)c z1w|`pC0kD;VcaQm3W^L8Ei?k_8HK*0P65YA(mWoMhSD&Xw| zWGI`LO{`KS zT_~f2eG4a2H6S?P1mX;AVaXZ{%=tzYmr z?Zyw3-uu`O@aL^enER}bCk-Lz{A~`C_pYt{Dlg&7>i$t|g zQD9yHQr^FxjT5V7Yzl172N0UgQ2}en{oup>3?m;bZ{hY0lueenSjLM*1N!E`Ix zoU`%*ly8vPgh!Vp92EH8DbI&FkJC(w6Zg7|mCjN+Q&f}$#3qFUEO{qnhpxmmOH9itc+#m<`h2SBl2_cmPpoYS-yv&@V>3f3pW6j1>*vkB;V_~e zu_S%mPHOd41nwBqdDyWG_}V3z1^20#taISlQMzgOv{g6)X zRC!7-;c8?s46*;;U1RUXM2vTS4>qROId$(W8?y8A3E3L3_x27XllL`W1Ff2Q3nO2$P2DXi_lpm}X?T+XwQx#3Yfkb%ED` zIl~q3bCo?@fC&`BQ3OY2t<|eA?R3q|n^SnLSNHYlK$pF#!?*hK%G-j;I<5Z;xp8G; zM-lrmo5_U69Qy74n9=vV@oD6M2({x%dB%_Um+g*`V}Q0zZO6ecmMC*w{oRr4K3S>= z#Vc26=*yDs;qligH-4NP@4Nz+*Yk8{o|j{g_rd4zFJ%1t2wX5(iHjb^Z<^vhxZV>d zo{Kr>KuO{y{x8)R)4*pTkMAw(Nm7lO95wUsed?wtWFnJ~h&JUlpf0nQBKoK7g?hrg zPK*?~`5SJd8`GCc1XMfvi&G&4h2?#wY-BHRxS~+Nra&amxWp{O4cIZ8iI5p->=4zd zoM>!aW54Tlh{_H&tjAO~9{FHKm;aKCU{?#j- zvsq>2?J;c}x;W}^!uE~V1sy2?E;SFN^K&;4(IcAO z)I02WZJ*mHf+9|tz>iF>FNou^i)Csz*Vlnq(w&dWjBpZ?;+I;;!i=)6hN%^vKyQu#onwf6%@s{3W3tSxjR@YQ`>9BZT zTx$Bp>{UdZ*(7L~y-s)e%a~@Qf9ptG7QY~*Iz21#8j9RYUuT#Xlxo7HBT@Zd^#rw+22;8au4q z8vC{Bed+N400n6Qo~LR=f3GLSQ?kwz`i%;?$;$`|P<8B|m2O-`+jS+`b#RtZSgU4$ zP7m_3hV&D7GitrTU3#fOA4JblawTuJ@85D3*(>FQ{~yaqA?IM#|AaAU`TRZ#`k&26 zNigxty7AMJjoK+>&A5T&P1y z6ypJjyyCiD?2W2>aT?br7RCiLQB6!0I}}!l zVpQA1vxvvMJzJ%KP!C_8cRUz;$dYyEWqtKV165J3{9q*o$_ZKgsTNkS%$FOc+`LS8 zO{E)JnKmIaSo8CTs?9KSHc-L{oS-_s*Zdn@>aKLrabQ=-{(ML~pPo}U@@3V;Sq$GM zK4pfak|LBJRC%SHjqGEillb4;!Ou*`wP4hAZqTf;jkSy014kY3hx4J=gZMpQuv!*N z<)?xz*D~?!*|HBlr#k+qyl|KZybuz|B_o8R<$I6c$mm9#*27ZF}($+p(sAP6t&ZVB;%xu+<_&A&{oSx3a z5Y_9uXdl<1J(9qIO%ej>wNch*qt=+5kQUXR(2Q1_F z7F#71_awgA;$`6gN`~V3vqy_LY7-84&vC3`jucQY3-Pa!{}pWw%a?JJi!#8B?!qO3 z%u4j+!q_lU!9m$dj~270dqNFAIK@dLlZF^9MY)joXBmq*bUp!%SCAk*YeFK+(x_1$ zJ$F#e&CcdYd_U0zC%;b7(i{J6D_H{u7>zvuX&w7>HbdT1hg|W6+cG0_1%J^>5kfUI;V ze7>t=`e7rS81)uEL05(1@)_QPrO1c*F*Bc%iOGF20>b4Kio3`g^+hlxslap_p-R>0 zKzZ&G>6r0!q-q!@FC)uBC#-y(SyLRubEhdn3QU&1GlJ<_rw* zqxJSKyuB@4V;Jmc3F+pab_K!%PJhS9QDSN=7spLx`sr{&;Aj<_3X}7oj{8wS zO4dsd^?iJq%xs3nbW}kWo&za_}2D@ys~ z5>gWLtw0>;O=)7Q>C<|Zi1y`{Sq-PKf$g3dkA|Il)>Sl17$+rEW^mCW{JS6sfA%P1 z;kH0I206p=p8T&?>33E13khxn1r4EhD@S<@H3yB-{Gr{g63SybxyTZAD+^ z18&+N6vyamYd45|!fU5qJeX|#eAUffyudCq+yY$uGJ8*+&1!|-|6rL-M$EQ(Bd6L% zhfDkF*$>@3YW6x#G~`+!0A6*J0v8JdOoY^jP?~6MhwNWtstaEUVQ1DP>&{B6%xGLl zD##~9hXB1w1$p?mCW}w6&7Hq3EtEq2gYLE-09FY4F;wD8Q3&tu$b&r)Nn6XF+7q4- zu#|{-hVO_5>AxoC&YTo0_MaBN!-x~Dbp4w<(e6AUo<3)`z?rBlP5n2v(?>QXa(p1= z67^M)11utCer!M4K=wOw@C9Z^J0O&wj^(6AEwEq;>>kl0oF{ z4f%fom_)Buby{U~nbDfjH2~km@8Ky}#3nz4Sw)b;>>{fP6O}wyd<+G}KO$pj*A5O= zYV>=Pk=Qg2yxH}65ySAVX8Q$QW8Nsfb*zzTH7(h2gTG8tcd5lPKKi!yv?oIV0;Z3G zSrrj(n1YvzsB-K@Kug`;$0YK$E?1rJu=dg9Ulr}6$DOQ}R^(7`W=STztrJoR17w)U zG}2Rg;{94%ghhr0R$Q1m6i{XJe%RqVak;OhEy%~Icl+2IO~tCxVY)X%iYGo2#Dd$V z82>u`Q^+JFg$r3j=B_hu=ieDs$*S(Po*!Oy(9#N6I6Ky-p@@3NW*DwXjx~*aTZBhw z0NB_<3K%Zvg>rTiF!w+)M)3E?2O0R$(w!PksBa<*3L0Y0&<$k*LhJuf7bg1~Nt;!u zLe@-o0|+}N<$%ru3b@gqLt~rz6kIaK-!%PZ;{KgHA{K@?(9a20SK+S;!Iy{q?<&o> zOYVpBenaV@o=j9E9qyEVoy*bOsqqG};7-5l4=qDxsiMpd;j44{W-&hUQxW*-zeoi| zJuY~M(d*w(ZQpt{Tg72zg=FN0Au1F+aHA#z?H*=`}m?EfXhmU3FAhqekbzH5ewMvaLVmX;Q|9qb=;rwju)9 z(ws#`O`}J}EGT#qX)H49jO8T7u3jtHA9||lF)*JCBTD^L@pLdZ-<()F=y{W2JLl2-2^Xkz{2_XI@TfD5J}& z4*h0Vt6Fv)ik1KC&sxRRuH-vbs40z zdW|9s0XxhOx#}ydzYrH{eYhT+9pi@(AW@}PM2cQXWBEnt7NWSGlg_&)Jv>Sc6i$Bt zVI*O;1W{Qwi4LR@q)%@i_5cO9=C;!6DW4cbKRWVe^;z5c28?~L(F=xDL#i=f%aQNL zG@F~cC35X7H!E-Zk{Z=i2W2ywQf1xWE$;oNVpIez(Bgpr8Lg{nsw9iU{21e3B@-OD zzE8Qd3y02)S{AMFiBv+b8BBX{x=?H!^nHj@tRTyM*%e>sdF9slfvR72w)ogj=%bQ) zge7!vtb>WgsLujiSWCeZ-RR;?uX;_GIE2P5?xVk<%rGM@4Xrrw4CDB{zxn0ej*^IP zZq3uTCl8ZQTB^l=PwvYtnRA>^5Q?+Q6(@Eii(gQBp% zFoCX>9e7HYi%wDa^GP;2!_}h&G%C8JGXz_&2hXV3;?fKQHgoJeW>)z-!J3{&?G z#MI~G9mSZd+nHISCA7pNm=f<){X1os>f#?Uh6qSJ<%?~aT0XSD9_M;l_Rj1T!NiUc z7Bhq2W3B{4pfOjjgh@zb?%9+i^;Gh3J`O_>P8#czy7F9hXnJRpCY(Ql8T7_mk}u@& zCZ6zIOD%-Q4?!O{1bxRJrzeN#ifH}fXcqYlrR@sqG5o`A0iBe@5GrACG#MRjvwCS{e*b!!^~#x}J?aOhRB|tC=V z4ct_o$W#ktNB%CSL)3m~d!Teg)J|I#?p?VP>6JGqTPf-=j19Ld+d|fu_NP{4YM_D! zL$d`EhsFjJ&Dq)BA{k7IAgKr*2wIx`m>{>p9vnQsuAH5}6>euvlvo?jvG(-Vq>6|^ z)Gi0^!#{NI1Ym3|aSld`UYmz>-`?F^v)n*xfF~C_`JL3AT}Jq#_w4{2K<~G{I-%@7 zwk-%1uK1ZzjZt5S(}Rdrl_kZZ!jn+epA^1?mqkxjVBt76lqdg)dD|i8pTQ9UIR<`- zdfvL(^eTSmZbPVS?cGZ=hTS%$q9cCCF;T;l>>Ugx&$?HIwfRMkb|ckfEb|m~n&j1O z!ai=R3Z9u&Q~hk_NCnBXlMZ`vC9Y;IIs4TC)-^V6BI`RA(ube%XZST98;s_q+Dugz zA}oIm0`d?mDX(Gjs;A9U5{%>pDQTq`MBpsCRoz%+e+{n)IEIi8ei2cC6pKwH5|QdU zi5A`Ezx6RG!P!!SVFO-#1)zE+SWK@Z*3iScdZ*ICV0f1xZEb_C@bOE^hc;v!9gKeJ$ z8j_pvU|*&PTd0h8n6Mm?{Ovgk+rYZ~22@q2nq8#o?P>Z|T{{OlDZJ-+F%8cOMAuL8p$5XnvcpU9YSF?AQy?m>`KW>dA|@|LKG+ zw$eAjZs4uK1lC2d3yaq1VcofjsI$;X6%cXsKhk}oI4m_2^wwi%0)y9MQ?AQi?cFVn12F>D? zi`!MU&()0T?y9}mhL=Y!kmgBzC)|wJ0kC}{c!^Kt5bfwNUXbD7=sJ)1Z$KXLWCP*_ zQ@$CxrCp7>e>|MAWOQ5yjws@0OEbQ!OVF#ipMs8nSkfj70AR%d~uSGD#K2^*+MmvC@b+_)4@O%h~l5S%Ml93ibZtA zZNSPV7Re`Nc`AJLkeu@5x*CQSd=%}4C{{>S%N-KV%&iXd0Do_0&>g;XwFa8IHaosr zaH(m{Buu<4sbY0~t@aME^e+)|drTONNWY(I%kunXVl(?x3oJPok^?o`vIk)6S$-fG zMu#~u@ST3ix~@%MaKMHn-3lpjL2ne34%J0e1(3j9NL0h6PVF9)#)v5MF!0z3+7z+O^(>(~o7{G; zn$1n|@>PU;QkzuaGNm20OXB{CuS`-?TTfg>5o$^e|5ruDt>otveW;-cGv#g+b}f4cK7pYpiq+ftJ>tx5E8QPQ^d=%OxK z7v4_P$@bk&2#bA@N$t>xBEr4%(;_w%r!qOH==c0Z>iJPjUO;oGe9qbVgB&T2TWNxj zf{Ml$yzjF2bvW&vQ|^*}YlZ1?ng^H?NbKK@*Ly;Ft**b#UeS%Is(Ev1ENjOjIBZ z>Hxk0X8jR;&-Fva7^NrNtTM8;mQa>m?~2E-n(C?t2ikO~GmTEjv8Y|KLQ=vk(R!z# z3r@F@+82#GMU#>#se2Mo|5JYe4N7Bzf6ggwE&#h<`Id0#U;ZSh-jGQx)S;!}>XGqp z(2)vK1V0|&1-+?@5oH!%=1zfpSc9AcHi%MAo>0fC%?@Gh}>OGCu5qNlt-a?0^Z#!zoV4$jkNl_;c)*wH!Amj@rXV z8Clb(Wd^O46b4&N*>T#^ftk?;;e>J-L;_o~aa_d1hIh&GBI&>)u#(UmO_-#g%ksH; z*mF>?`(wl&aZ=zbo&yL4RoK0fn<92#@@kdznSLZyM9&*m>x2GR8&wm)RgSONCyQHT zdiq8B_0cnM%)7a9tBd?>FG>==pY_5FE>(ITz5Wg&^~+md=1dH(zfH1ee0XIs#!g#t z<2fc#x<(Z*ks!627+hZBn5ChkLMUU6(f32)vKse1M_T?>KOPX*0f*(W!moK}5?Zn! zXRR-_EVh{OJ=CT%<#}r>eTlX*jQVwV;v^T%MME(m8mXHMngCDF8?s>1>t!h|#zAHd za;s{%u>_})pcm35$^J-YiV9U{8_Iajz$QaEF9ZOf9y$LpkJ!Eh6_;@kD^#2ClaerV zDT;7&Uu+!w9nYop8s}NIh&G$68D|`|+)x*PDCI2P6Br??MjkDSzIV7Do}voQd;*xz z^Xd2zS&lXJTf=^_mLQ{@_!zbQf9Q<8!YL6&e)6$F>6mV{7hm0>UD6KVK)OmSAu8I2 z*l>;Vobuf|HZHS+da*86&eMR_0l<`H1 z8_Wv5D@%Z<;K8_<5N-d6ng|i z0c}eH^hLWI+@MFB6i~>k^#kYsiu97JGZ_YegB6S3AKA z!P>}KemaeOqIruGT@|j4eg$NTPx;MieN9n@hTw^Xchk-jnp5BL*zjeN&;-3enjoeh zu^*_?8I%f_a`JL1U+@3a<(RutRUxUslBq%%O>#d%W&IF|nvkeghR%70#U%s-QkHjn zcZgTHmU)i#6Lag_+l)e`4pnHTo24(KmFw};y~8LphQ}JPzo50m=nqn!uULUf0GSt`D6Epa!Slf67^ZlU1dZBY05L0V zHU3%55Cykkb|!bye{nmM0#f0k5Vqh9c=2XwVo!QS)IxO|FyqLb8Wt1a1z<0J!Y18Lf00qV5GU~@b^=YO*@JKz5DKUA`wQ=L7?Q|5~7aEsgU<#X2RDAoZdnK>2NGGmniEZ!Cf<#4p zI%3Xa?Sg*l1r$%DIN+IKy$|p?)&`okr_iaEz_CO<4r$B=9QPBx2fT{iWHbGQ$Y8w1G7opd(BfOecd&4paLK5*L4q*mIqVdWwKI@@bxfd&ze9 zcBjH`BBILwj!1x4By~*mH22xWf7faI^kEQ2oD%|DCPAp~E}td*iMLX;Dm79`W$R$@ zN%(ml0K>XjlfnL%8BU_gi!4*|lvqaOMs;e7So801?j<*ebA^@c?W*(L>0(qIZsd#4OfUTPnh^x zLu%c@No|5Q&yki~1v^Vco?dXQ^Ggc{8IVF6A}yX0fAZ7P6OjO0;7-0M+iL`60Q6Bf zm=woLVQ$;cvJ3v5>RIEO^N^gci4)GaYhD0L%h>Qj%dWL3VFwmF85KdvO0=QhhxVUW zovU5?!Ek=}gJEwD5>J^d?cis z`ATygi?)WVL&NuX66y13O;gj?c^FSwC6^32?n8-bI|tWn)9Po=6J^lOv#}x1IL4)wavA zQW@T&7+QpsYpZsMu5#7GbwiGf%Vh?XY-2oA&6i8v```s8L_PH-@Fj&Uid!CU0F80m z!mfoLJH0Y_rg965+9b;DVaTjfF)oJ?nGQ>dfC|}SDL*(Hc$;01Wv+?Tx-cKoTvU^& zW!L90&sIIYZU6$yoE2UY*UMvM>L+EX82VOqxCViDfGmxwDFGe0^R~L3pL z+8I@HdSljc`Q-*An?2IAWtedn$WqNh-Q-&c#C8(nk`QytrTT~O9;TM*u$N9*QZ@!W z%^}9ZAZ(-(Ps9*W4g`PujX`dU^RCv^ZL?Pm zmEYP#!{S;>IAm`0zX4#jCVhf+o464|Sp)?Y!e0$xwmLpQre1f#Lg|9Fi@G@ex8k4m zbUF-I?Pay=y*0#*Ljr07AlxtG{SDoToBZ{OwtdpP7do&;CU>1d zMx%%H?dosbaT>7x7xwA6q>##-1}4M;Xov1s9bKuF`GqZ--D3v>>IDCSV&lHh%r$pB zMfKBf6$O0gHE-e|9A<5`)-ld6ZMxk5PU-`%|6mWR7OfN6C&8%}(9_J1vSs&wXHTV! z>9jnWJ1-w4dH?B1woX+lJYsi0ikGv|Ucjeo_sn24mf*Q?3DG!>$!O?c(DB08Rx+ga z6|PTUg^Uaj7n^y&N2~N9<~nxJXlm-}Nfv7c+jQak-WMl}d^b2{rLQ@>@&NKYg${z> zdcPc@R8<+LE+?gbh-y$r9iS%fB&RX7FexG0y9dqn!s1yZ#Bx{A91Ykn1g2+(XMXKw z%~Wd0xe8Cq`jdSQwKmJJy;v=>WBk6YO24SnB9}JE=2I_aQIfwnE7tg5l;rcjmBXet z5_+u~~ghVGN|y4;+S$|x}Y39e^m4E(fMEKBjQm{=l1yww!@(6=p~X} zO`Dc3aW*1GXUSM*X2-aGVhes4%jTCOF?s5#yKV5$ptnXu6h%F_ZnWWTevVdcrF!&F zZL@_4gXvN94K10+2@l?+dXm1xOVgbEw8pJ; z&*1m~00nvhp2=!Ne`cbKJQmVv65yu^z~1RWFBh+YuS(VUH+dF}&;4NC#T~<<_0Q-3 zU&^y<1?X_nWjs$7V-H;&uZWf@jvhqRe@S>UViWJ_i{u$rybaz${FMv ze7e?PlSP>$#b*9@w*qrX?+p6#D!Tkttiq}CGuY672p?DhFjE1c5ur4-4Z8Y3S#B{t z(2}xx;jbeU0E|Lt+lAz5s?Y>FWh{QTG&jgaAP0fW@fo;BGm2Zq= zF%CwU?gK9!_I5Aw=NwQTfK#1@B80);TQ94phTkL696!9 z05kMd@FHfl(L)m3Mi<>IYFp~kQA$;6lnJ~ghy~GPT|u^wZ=X-OKM}!VNH5BY11dmR zHpyL~^a&plhv7g5{tjP-O(|+nC&c)4OJ1JD=*SliOI_`I{|^5R8I$%BQDF}weXrZ% zArGyxluEH|$Ce_YzrQ_rlH+DDl+Or8FBZe?ITf}|3OeW<$Mbg^A*-3JuJU|~ZNU5C zz?XoVpI$dC%>?Nz#?mNLw9ni_?slPzYWxT_mI_ri&Qgu!! z+V{Zk)!}PXO&5o9yTNHEmNChK(;g4;*C-|hW{`3JA_LGC<3d3g+ep<2KylzW+O$;8 zB%tAeqsCg>I6fzsqQl7Q$?UeMKM zQ3?9$NB}C+FCz?ecrnrOA>#LHoL1bAgT;ro(-}m(T~U<<{RPT6my-ExkPQ#vT(bbm z{h(f3B2#(0Zixj=^J(6Rm9S+X9R(Y;CF!Gd(VxCU)Q6ka9vE;t0YZkq5qD(!J7p8U z+1P1i`Sq=ms>JqYfPvYG707R8DN_mUxY$k$_EeMe=%XJ^lcVM0pk`jK=CqQJG(cw| z6utRM{^zJ7^kHN|!b9nO;HuFB;M$f8%iW^<=)fnH>b;zOw-FDMhjoHC#bB{pYiaHx z^Y=qG8R{x63$tH?s?|9cpKGHr4o1gP|GAMs4PPWco7C;3J&A;r7oe+y@k~TTcG&u` z=HtuQg7(A=nalO-k#SJ2RfPH$ng<~CAW4DZ3*f~Z5kwI5FY%C|2TC$-gY8TW9mm_L zPFXB0gU^%T6V-yHZvWA=>m5dC8U@$!u4k~zO?P7s6Sqs3f7huI%)`_4j|6IF8$0Fd$}LJG$u1GMd1qYe|8+857{+0zs6? z6ryNV2=dKyNC(0rXA|A(#eHiw^zOzm|0Tu+PE)w67*2-OnyjMo&bZzJn^bC z!G+T?T-~5H1=?0fPFM>r&dY_iBe%){zTOCLGJQAuY$Y)frLRsiipP>eqqYE^TqIk%E*&swAc9~GxuaFRiCw2;G; zZsb7_JpceAbU~WTN#PEOl)*&*M>J~i?CX|Wl2CMNHY3&q9DQu0$M_Yzm)r4WpfbiJ z3<8FcX%f=7(bMsu%#?PQmiM)1Hy7M$6R8Z=dx9(hHtNvFqB;lw zx13?I$Pzzz7Tc~mPZ%WDAnem7T2%o2BkX?-zSZIi!Kas-TD*LULHCTnNJpzC?CRaP zCl$SP(s2t9cd`024b|%Jd;8rP@kALWo#|lVf{xnTtUh(`!cP=_mvQT zM-6w(hs(05%_Jjm7U$N2J<1w0+r=YplF=_~f2xz{V-^jlo`_;$+Np`LJ74HpP)m#g za(58NLa2L&sR7SsLDV@w_k@8wTA*Vy8XAtC1Tc##!b`J(27>JKn_&snyG)`@7H&nk z$L*&t>>%T5G4CmpVD(O@;m0l*H8Thf-s^v|4AV>WX!$1oT-1+MWHQ}V!Nu(@Xia}< zcU5?CrY~(U%a3ALFWt8F6PcWHMw`3x6^meb4DoF{2FbTZfZm0^x^bJ>(gD4&E&y$S zQrL1eb7@DV5#<&b1eVI#-@E`_=CkexY7w~~rpy$lrqB4Zd*cwmFq*ZKbmMPZxNnTd-`B%dVa5@+PV&Zx%@GL2v&fE^F zi;t2V#x`!R72OpPQGmvQ?ejF!<-bD_kB-FiMR-1EOy(@5C}OTm(7N+8>!F$fT9T*> z95l#$hzt^eu&fCHSmKLSa#`8W7#J1N*2@^8jYvd+&0ww7QCiTRXt#cy?w@5DkG#^I z6C-ZKO}DmDGWC=nlqj(>#C1nADqw!o`i|K`j zRCKhC^_h)Sa5cK2|9zThusd!j-tYz>$D(P_`ht{7KRUDCA!tcuXh(?l?a#o$?to5H4mAT5 zzwoxJ*BjYD4r$ixxS7uWV`C zXKIVjzhc&=i5eFJ8YkcP5f#`3Rb|?uUh6^o2l2!o$pQ7}(XRLC`hd5U@e+~09VNM`)7#c_Xv{Zl6{)O|}ADigO%Tc5n zuf#KfGZh=`z#G~>BvMGrH3`8Y4yue{0~TrUiZ7T39jQ>(2J4;;B*N}C5RJ8G+o2t3 zTSYmuA0|aODfY^dFc~t10z2bz>~Gi#%FgH69mWNdenBZ>U$jtbi8~OYFp(e_$uVWh zXMn={)yccpJHaNz1??AM#TTmmH0&nj20~H}=ac2ueR3haU(W5HN~mrO*DTW#zHm6y z{QG^kI4-k9xMnyku_cUzRhWSU#`aC9+J~zfI3Ygey_8h?nY19z5Q1Gr@p(^Xcs#`N z5En~=HU35iQtN~0?hmyle0y9Tk;#M3C=gqy3<$_VVm{?mPcU+Q*c|rtoE352XfBGV zAZ}RjaQ*z}xnkA(7mP}V9qeyyaiDdU*}&A3JWJzDl?d)r83P?4Lh#5}H~9w|pf`P8 z-vIrIXH=TE=?X{X)z}$~!SYx6_-(brPzMeS1_Q{qix`OuB;9|E-yA{!^4oby!>V6; zY%^4uRvP1EpXjcu(4`}4ShO>q`6eMrr(dFLz zM?flyHMM1Bd5g1C(DTmtzJfacpKUn#0jpU7|7hFR`8r@}QvpIs>TeDHlRVIC!Ba?7 z%FtEXqUBtfK#U?lkjqwS35Y(Ve-Xp=h(mQc2x{FOf^ZY3d?{F z>s~t@Bm_}?L(Ap7Ge;-Wfh_<&{?4pGa`|63pZV zLM0{st2BqSqtEJ0!t5DdOAY56if1hd(hy7aH&7$nc2Z_KK-Q`SMUg}SkNf^sxvJt) zTGsJ6zB$LQ(5nUCmF+K^gkQ;&?#I-?I~R(ouQOGGHlM&iMvj0x)rvhGjw1Q2>SlQ7 zvC!(9Kq$_|&AIYo}Cexq_L1GV{WL6>;YM*q`-9X$-{F8H057Frd!k|VaDZ>xZ4#s>%CIM{5Z>&Gc z5qiF^B~gU5J)*9kdvPaV)`Z2gE&mF76n|HyaL9f=OAZs+vUX9}eBO8@)mI=R*Wc8Q zG(&%8uzX^P8=ShR$|e>U-q7OA|5LCQfv_5Kh1%?bDT=D!iV{XFaQ~VvqW=GU4dmfe z*?p+?>Bx-dn$4|&B@l`!l{Z&V-bv~T|IgV`XsLJ9XVTkh2Iu}T#eu(~L-Iub`@;c-QK-=Z|6%0!kFXT2eiWF84Y}A$~V*8a8C+q-AieU~_N@#l~=ClW{$VhM0)4w+(o%VZ37zH8o{umo&=O z_FrD`SkZ9589&XMc2GhI_8t?QOh&weGR8mhE0Usk&!jN!-*Jq+`&u?(jVQZYo50nO z&*w8ZCZdbNnAs#o?V0qm91FY@FUR#}BtHR}i65;u@1Zf@bWqQh14@f{r=1~maG`Ov z_5M*7CeU#OsPzev8>Y_QY*9Z(|A8Ry}Wq0GjHW=PmS^c zvR0XETQqivW6ny+uX?rl{*4?>qk!cSh|Ix#0)b(f=}q85!2tCQX+F0JzCc3ozTp86 z7Z;N1X3ni__0`%oSGB(cg|`|Uo{CS)o@HL4a5__nP2qk>U??rN` zZ8iFkP`VoAMyGdex-_w5sS^<^U;M1_jrv zNx0~sDCY)gp4!={#9ozgi9N!PHSNDT3c#?5Ajnr8x)%$EzLw;{(IRT~G1A`v)S5F@ zXkQP9wb-}ewQdA!q=yatL~=aDiZgrTEW*)-!CsLqxLJ#*NF)QWa@AJyBc*()SL_AC=0sogp zL`kzum~GJ?P7&B68~&9#a7l&Bjic@T?f;l*q9n4}mcNTC=WbYBl0n5sz&BA$ACeXCo%LxI)o;8sTA}r7bp<4ea(#-?U(S(q@DzG#m+8Cbvi? z^p$RGCyo9~Lo)7Q@b+hGHKqL=#|ug&e`NsGe;qPi_xcnw7GLaUQQXY$kW z-R%2trcYoBnhdOY{Xc;mpT=tl2PX;x)#8Vg;pb1Dj76>t7GstO%=AQjI#i^PD*F6Yh`qTq(-6*>mMD;JTarfl z6o)@pE}o%-u-7}iLN)h7-dPS9bH=tNvoC@FFZpvc+RXokm4b6Fgk~&5jw!6qNru)Y zk`QTSk+eyS;Z%7~?1H)AxGL@{{tX`!HT=T)uRbfGmqn~!_Z8frUt2YcGBEOZw{%P9 z07-#0Q%d`*n#WR{7eZ;E3vDb=hQcB4dkH9mpK~8I6d?m=zSN%m1bd;n@{0$-i{Ln1wV15>jNMt{(~KLVHu*fc!}2Nx&s8w z8&b@|ibb2FQw^GACPMI_AGRfIA@EP<|Be3(RKZ*38}g7Ly8t5nk_+lHiw^WFmAGsK zo8wZiegyjPljmWS|_duZqKkny~hb-CgX_Sijid&}TfT^~h zK;W~1WuKam@VGm5q~o}izdjW_H@CW^JAP~z4>j?^llL`BbTMEE*rE#6 zQL%tiC-8Ek0ocjLt1VYB2kCF5@a5ttCnBADSW8KT=U?4Akn;D%Idf{ayUv?S1->qs z6I?qcRH*!uy0HpbI$wxj7^dhjeA0viY#z8Cxd|%Mp>S zU|7QY=`wZ&!5D-WFhB=UzlJounX|tALBsTTWh-7DuX^U8L*+`yagFV<0p421El;b_ z&*xUHBg@Qn76+w)57cV^?{DKzKHfKcK+QKqw_*srkL#PkxB=e%#i6UYq0cU;A!v!P zI0-1xqPKYXnmQ@ljiAdDlMktO#W{AFVsSYOIz z&P`v^vh{hfz5B4K$pUpBaWU&0W~5+1?qIo_aXb~h_n3AOkP_NOA~NJ zU~@I4liwjT><=}a18lJv^4J0m@CpK{#WW^#mow%R?d`Cn^ED}< zce&%T>Ni-iyEuX#gaJP+c+>9mM}NtGf}E+AyS#TLs9FG>F{aD2bnVl4 zH;}3I!2R4WvV%R~q~2M~?5iqJZ&btLaM@%Ya9XIk!FdjmM}f-BNL&OD_b-B^XWphq zRMfC1C$hfm<6>x~i=x|t)dPy57OZT818xCur^PEZZcEo%L6#-4fXm}RL}dBpB^9ro z6~aycL`qg~EbcSB&VozV)U!NZTpgv!!L*gWXus-;Fb0g>U-M4DFZBr?)sPoUEJj8} z(Ij%!kx8xMq9An7w>_I-gFt7*ytw|J<%tN|v}_IGN(f`Ns2uX-`%l6|5!P^z?)s1y zFlg@oo0`y|1$P0}%|l;oucA`>kg6rJF4?HV34w(HY4=v7g}dC-)~0t)i2bp<7A}}W z+oG@N(Y zQtR#dueiV~uwu(2o{ys!t~crB$6E00B3=p9O5=252LNdQo3&Z5YkdAuWnfbUIdp+& zH;Y_}!+UVq1xE)>!(x5zgeYwDEDZaSXOVaIgq}=COA+?#lDRtL`zv$R-~CO~(7FUd zOb=JFfGF?AX^yV=4S$(l&$6z-c$mz)$Dk#B4B?p_EU5T&i{D&P(JVO8GhNP)bZdOK zn1&*en47~|X(VC^2diT;4u6!~1QBn-HU9$bN@fkFc4#(R z`Z&gQ_0rTzc!vcI{4A;~nVVYaDGW&#ph#Te@sfUy#BE0Z#|g2A+z%mFyR$M>mmwAf zPCbkaB=O6Wo)~OdfGZ>D&<(C(DJ~4{{^QyqYA~a;U=;Q&L*>zCJTJqNm){18jqn}g zkNii!(-jwE^GWtUZC)mjs=YM9MhddI8X?CgJVH=e_zQ>gz!j!ogPl^}Di*=gqWbAu z4Tbv1KO33u#=r|v&azBXe8oOQQ_&Lh^Q@Brcp)Pq`7d7;AWM-aNyO0@C+$}sLJCbH z{c2f8mqy{dyuaFGSgp^TeD-Gw%TsU2Rk~E6|Jrp{w3*_?07O8$zg)vn*rpMMU%Ax) zh=9s4fY9+;MdzRX(wBal3dq1YH>m9oAM6p#D?I6}uh92QHSM}vhg4>Z%TpFhu22Qt z*PCSx!}40s@%rpr+#$16hrBr@mI~v|wd;%smf=)sS~Jr1YIC}zHt;#@dY4}XH! zp;JbLnPnKwaNBB=#ua503~)WSjDMy+eG=d=Hyd#~iohi_EvGtI-&Ln{ny_JyN%3yJ z?a<76cdHO9dwcy!Ni2OU5m%%zIo40NDYMUDN*kKcvvITMJ5SJ*6Ct1qIud#m?yhDg z?m~*USpRCsw0Hx+Wq~xlm1J|m19Ql7^PymQn}BJC^`-IqfO}th1n8FYTvw#7h?hs2 zU9Jy%4N|ONv=?ZeYLv4qg}|AK;@3uw%~Jad$0;A758wrI0R0e@BgWEQ5pO@ZY4v$Y z@2Lwnxj?P{I*4|atLoaXV|2UvvfuAp24@)$3Bz?hAR}SO^tzB?j@|L+K$FrvS>45l zMyI6A6g$o#ym$^%&`!6slkNy4^$>km<2sR2^#iz&c!rilzfzQ9WbeEIq3#heni5=6 zTlnsThj+y?lY}jcF%!M|*uj<9h*_7C;apZ35es&MuWfy*LrMDWWQ*06#LVsb5fWE8 z%{0CPLnG~jN{#)3Kp%Ya`gDN+ZX?F}itZWdn%hYd?v2(V1DoJ^k}^zTDmP~o{6(t$ zmaUy%5jb`xqX)F!xPYVG<1HhdQ%Lj-qIGwAKJxDQkC_Vu?Lcn@#2+S*Ty|b)TiAtb z1P<~1d?}W&>A0Une<1EKD$4^#0ugdvnoa6Z6?utG4g8a~Nk#k@`@mQ{Sm3w1~T@E^+T zlZ9Q!rnptwiL#s{13}Bjx}$zQAqCr<$T7I?eUMjN&)**%ps~_RoQTB^%jF(=vZC{aa)5W3gO4N)=EW@=%i zrqUsjy~UFACRK~KR8KN=n{N?c!#-1pD8I7kG^RIF-YQJ zj$d=RKUs&BYL%TF6{h(iW1Tli!y{F&&E4)gLuPZj^Sf*er=&XJc=?#pVfmENHZ6Ms z3=CAje*Bio(_~NI7$)LHU`*A_s<_5xcwg)Z?n0)~ex4tdJS&Xq zKoF~@xo#W}nVh%C12!XHi*q~WcFA%GW-fIEBy7QA^b9|%2dTO}&Blnp5m0F-G)@t- z+MgcFXfJa~x@x4a|4Fl;ZyZ_RTnNLwnsvldn);Fpa49(aqWy)_ZV!sH?P2814177v zv15LqH6wBHjg4Cme6E(|0JkB$Hy3wVd8atZUYn=BJRH7Z8s2_wlZ-N^G~difv};n> z#}H_SeI7uS+w{7tS3xs2Ep^)JRz-Pk^B?To;WwvwV(otMA0f`8wZ9TA4@Cv*an3_5 znS9a?f4#n%i@rGk^IKCCgB1Q=u+UVg&VMSnH&4jJXSKf=2Iy=OlN;Ois_^nZT@u__ngdflLZg9@`UL$ApP<;@>73U7Z z>V;ioHw)Kx004eINO1Rgi_VP5n--y8#f1_sP5A5cOTGmiyNtkF)g~#cbkl3+b5)kYcJ~swB=%nUBgRB%vd^!=#Tv z?`G`jw<($Ou0%5v(cy4cZAIRuv5~5h%C}L1=CIXo|>a-{!hEyv(c6&Nx_ zmrc;-$^gBB+R>~50mJ9~M?)X^s8!+wlA+|mTF77E(T2f0)0$Vqb7n|b6Gdj|Iw==2 zYr=$%dzF4^&4s@+6%%}CKz&5B1OhO79FV&ADEQ>1 zLlE1Mk<(NP!UP#yYr*fXV3efiSo6BhALGX}fKx2r@n)0oi@Bm9z4_s6}iU4N2cY%}~m?(NcYX6RGC=F5}&vHRI(@v;?iTC+^ zFP4`HA$%t#CI)m}ZKjUOJ=wp7Znq2?@1-Jp8 z>uN-QZRHlMLqVaoSWM2qDS*=-0^0!M&>AyUJ!C&J!ijz}$d~Px|7@H<$ng?rCe2i- zdVMDpycrBM6;>q7$S#>fX2(z#yuvI}pZtow=H3Lx{DBCg;aDU|sL6)?rUh(xcTwd{ z;#kqf5OJG@04WpTm*?H1{$_UzSW9)h)Y7Q(0~qZvS36N&mY;{GZ5J z^8nvrZma@gH*Y3f<`x=6lva*V&ZvMDjG9BvEqx{%-`pfms-iy3=^z#4{5QY>x}`hG;}-YrdZ45$&U3o`BqpD*Q}X=! z(1ErT4hBL9NZMrVa2Jz_^EGq$S!dS*IK27sPNG9&Mip8r@mVa_rceZ9a{=?JhqZHag|`i#o^QZXFsV6vV=4=2H%LvgUE zL+q&Deag*BODn8gsJzCeD`4JrG~efURuFydHCI;hu60erANVSMxt{K);$9XKn^6Mq z$8XiEGeRG>^BpEZf}uG*NK!qL`-1*kDx}MY=ERi$M=Y?ovpmz1t(4F0MxD*5%fJep zIu*Dy?UPwVOfv@0b(7L)kHr!OAKKBn;_UC-<;<~4E$Rv{j^OZ8&d#e-VEd=!4>Dml zz>JK z`aC3FEbeGv&gKNtnn6`c&^; z_}>RSx{{3oNF%BUWT5!&nQ|9Y2@a7gy+l^J*}K-Ku5S)Pn*C~-H>D24eS4N6HX`Ke zqgj)3^!~T7QlR&%X-ON z=7N4$u)0+P)_n74X)`|}zAH_R9HG={7yX*b=lft1GRlE7vbPBFK^lmy)r{Xab`>TZ zutm7B?dJrDFi-(0Qe`A7T9&vxo{R$}y-v58vB=76E_Khe^(`y9 zchl#B=NFK#{uF-%_5qb-wS~?|`W?+kYlx=O4pu^+M4yMAtXX;YOH>@((3yixo1u~g z)|fb``)sT7BCfjehVcqogSZWLR_TEroZ^(fsTjkTM0wkBS_X z7?Tfsqo1gJ^`}~p+Mo$}RWI~3qGAmD4xJ-u!Zq{Tt&7cFi2NOvdX82m1CI>iM6l0~ zX$e*l{y4v3lzKU2v0XyFPDj6g5`J|4AzW5l>m=^#*VWB(s@k|-3KN+Q!XX`pZF^i1 zxSe{`W5z81y=XTM0*;P>)W&Y3Y~J}R&8PbH3)XW+CMVJC?PXspdzyEnoPWm<%#f)H z$g#T|S7QJGAl^Zm@JZnhCQ|}Wv070okQjUx!*>8Qt|1{B#$ZZCB$n~%)7uD*Vtm>= zMdv+z7ePKgjW|(1t$uj+=4NF=N1fVn0A=@*RJqO=XannoB+%n)v`U}>QW<@9*t$Tq zAyKR>JbW$+(EdV|GKp9kpYf1G3~>n3rwGV9UDfMBQ^!Q!(5^x4p+QUz^I zTyzJC*4o6aiqK0x*7eO*XX7&wudE%EcQAHy+@1IPYLqzZ)l}+iN_|@?BF0-t2D9A_ zHo9!rkS*bx5vB{9_+xT~N$OF|AJNZ>+5XtgoOxvS3hW$E(x+h*k|dI;vMM^~d3SY| zTNVwwRoex#ROgFS&79zEyXHQa63l@A8Dn<4x&o(YKUt_*Qk9e~AfP+TjSP7_A3jj7 zN>!kFm57?gi6AO=cx_TKCtiAw~+G4-X_u|yK7Z;`R2L@^{ z66u~p=C=r(==U?)4{tTFos0?WMd1u}Ve4%+N)JhcFK!Kis-TuJ%HGB`7ba3CNspp_ z!2VW!T~rzDDsA~>yBO+xUUir?_D$eimA<=~eHmSVk7my?M9J=6lu z>ai_{s$Uedk4YN7MNjrFor>^Zf68b;Bt-JQHm@YDCFy>gC&)I($a>?&49rw@9yUsX zpcWh!+Fs7Pq6{O=i5$2p^E{1j=$vho z-{hib{3}6FE5x7)0ZVj@dr@^*n%d@1Sz4E4j2;t7;cJOMw*|EvTNP0)(2U`@1v6V5 z!iK4(xCx;B*+&k@S#NUFL%19M%l(ioZh6k!4Xu)O_V8)NUgRBx6^d8opQ49cF{*4* zLB5}9&>boOI{iiL9|Mat-m=`6<{0W~D>7|Ko59}FVbjy=N05dE9J5Qe+brqJ{P<#` z`mIpqCMHIYh;aW0kXi{Ic6-nveAjyDb!KF2uS>6^Kt72{FInbREm{;Ui!GMWN?uVw zs!+|#Br{)d#kYq!*ySPKU>bi)9fe%6#G2z-jgqhpfh&cJSF#&uUNBfi8B!sG@iR0# z3?x~3k>V;|M3-DWVHIH`u`0m*5-QhL)$4fMYdq2pRN2il8>*dRCoMwSfX=;R%9nxa<0E+i#SE^ELb z%nEb@3{3nK7n#1XpNxTPXqtF^-l0>$vp~W>D$V?@I*;(!=Z1u?NhGKU<5_&mHy$CD zcd(vzQmp@{h*Gw?&&7FRc*lCrRim{*e>c;6g?^4%1IcfD0v_5`m)XEa65E6gP zriBha;#yjP9j8Pcy^mUa`767#6#?rRB>y8DZCPQUdOrQJ!lQ$vL#`0G$d&l`sO)@`zY*SUJ_kcC0acHvbP#4^dKXNdpHC)^Yl zN9xZ2=rr@hx#CMrB^AXWUQ^fIrv>|0fhzSf!imaV1B9SRWXAV*8zl;%cjLeZgMFvQ zpqP-Cirn}fViGc7KkhUEk%Rwt7E~JXG0WYme7V(C*o~JUDBH>APiS7Y_VO9`fKKQ; z^DHfsbll3rJHGx%>sAyuR{(eQYHh*) zG{nG>J(ae8Oapilr2zLCaz^DN0xd>>-4oHiRG67Y zJ%A-F4|3FIR8oSLnDINUt}db5)#R^oc`e(}S6nzv*dlJKD=F*o4d= zf4xR`oALOY7*FqbdepC@;}#JybDfM(6PCUI^G;UHHaLxndS(BN)(aN>jMkYdDKHPG zr_kWK{{#xE@p*x!=tYpBnQG$K47W2b{a6RaAl#(>RmX@Yeaf|wbQ&@93K50}fHIZy zp_ETRd*w|upv0v3N`oG4e2WlY)60eO5G}NTugyR;fuwnTgAV|Rmp>26;T71Za7C7C z(L0@wPas~z0Nww#F(e?t!@#gP5Bot}wUtFGhK)-_$Cl|w11nr2aW*;@mjP{LtL^uC z%0ltaSMT}T)kRtJth|Hj%9Tq}LZa?H2DvD60x~z(4PT5#w^^C<8cJkO`L_ZzTfv^}vr~-@lQ@z-D~CH#r8#2@u+D6*|6A9zv#rZQ@_B?!;9SXwtI} zy*)T`Rg#=91l!A02%j@B^)CAMvYKZQ#K}Zr-14SGLoZS`7|r`f9iXnad=S6`T;Zqf z5hN3JAHi*a=5p@c9#%x=;gAbLsyR;sP>}vZdSWzsmxe9+Cthvc=@zxyVs{iOX46xq z<`O1r0){fQ1AAmia}CW$=%>%Z%6vN4APttJZMxy*XpyjKJMO9WdIUjf(@=ETXuium z@tY3`33NMS7uUeix?D%A3_Ubnao(XwGAdy~6zQyt4GP3lgh1-gLQ(q8?#>jY_vtvA z%e>62+)B4#0peg#jqt=&ID|V^vJ0PnS5f(A!5rJ>awWDPD2X~nBNpkcQg1```($az z=e^5U>sunjO&~HTj8)Y>sC}_2I0%%WYAQ}aZQp*t`9q0$9L&|o*`Ei1m7E>km-2=h zVK-|$fIG9DI!V1hK6P0KLcFBo_btqN>{VQFj{LXVKtW{6p5bcQB6*D1>YM6vK2g@^ zJl36Fqve{QlMfbQ2GOt7^dEdmZ`B~fIJaEY z7fk~wR;ZLltg28oC+*N?|MzlTqSOiLRH{aUh5W0I9;s;mMjux}VNpD!=A4|kvn2&3 zlpEFRbHts$HkZxcAh$CKF;hHYc6#~${^WEY>|fEn40ae{xKpl^b?YCeO#Z{Ffqicl z35m;vId|#7*6S;mutsbSq;HTCK21(K6*tOvx?3 zL0iW1cpbmC#8CQ$bXdryhR8rpXAHq(TK;}!{C(-n(fp_LM0rib|HB|`8zn?8DXQ3?%p194mUiXuJZ`mnEL{ZhdJFn~aD2>5pRv^1WTZ!Asr z%!TWXJ-$W^&V%)ag*)-CZ4)QUtvCO#4^Qb1RRlgm`zdO}1o@DR;-2u0+ z^gA?con}P-@KhZnSL>73I@n~GAP8ud0!FSS)UuTWnGU|0)f~YXD!NMuz1#XBBG6!w zcjv|sORtx>UIKdlf+z-yT=EE)|A*al#GD>M9dqsy=r`$N2Cc?Az^(P!Wo4>F1Z=}8 zx7f}Jyz$<<^U}CZ_O#Fgbp8DKKPNh&0r& zvn{I4E_iVV5>2bJ2fue4i%+yM(uL9*6vK8rJP}Rx#o7{xZVb*zUfDJ~KFkVVAg?KG z#D~kg@v5gxbQV@{zDqK&N5c}ym;x3rnoxtkp~yf^{4R);7A7p{QCJ0bEx{hD06u{! zCoXJ&s`gFt`I^vx4rOp5qcw2SXA)-%JtP0yXHk1Bm^BSZBQtY_T84kbAGH8g zLDjaAH>CcFe`SD9=P!Cg!#oa3a8vBw{nM18=0!m&!~_P#A9S-+JhciYKed%y*e>Hv z&lNPoS6zrN;)7b%ihSJHn7QlIgZm{<#2MvJ|BQ^simC=2fiD4oRQ0 z!Y8`pRu;1J&>F1CZqU#r76TC#*LF=U!58SRXgd07hIwy*%Vb`9F;DAH`$HYzNr^z} zgEJSBmz`CU&U!0FTUCio@81g#9LbCE&a@f(!ioijfhrCbL`YeCS0%xsTQN@V3s<_c zoA%3BlB%m7g2k6_ie?}?Umr+)sU~&EKC#wEx+ZoGPIfGv0KYM#B}xdWewWs9Hi14(WQmubJB|s7LS1@xoKn!K1eb&&{$S@UDe&`j<3uE~Rq3eo z#EW$Qo8tD}xaFNb;p0U=dw+19vA{n~wSU9UqEgb$B^myvpXFoSW&b|VP#|tMOK`%*6MY><^zXfuJ-bqz`{fC0kpEQU&kcawy5nDO5BKh>szmHRyTs|4eM-B!GUP7%I$6` z65?P7PYNenAR@&DOfWV60a)8H!rV4AXeZ~cboa-@s5aFsc96?$G^3QX$~i+|t23@= zg5b1GZnz)V9s7NOzN%mOOP19j@r7~YjJ=xuR6JI*SlF_#Ur81T@5dbv&BKc-fuEd?>ocnJd0@!wvaFUSd61`6EC*Ti>zjb8MM`%+ zUTcSoB`O-2iG@eB<8_9Qtea2`@xtBhB(*N_aqC^UDaXRIsNqCh2978|2k}y~19!F0 zs_aR97Q)0-x@h88kdS-w{=`)up~`&ap^&+{>GaD4zDegBhyg?>x@TP})VjGMWNjjF zMKuT>!qe3zYWF z)3YO|5x%CyiIH<%c=_6Dx1}(}d9LxvI`j2=*5nK?yF@aASctEKUB^nTZo7cXRh?m6 zWaS+229z0^8x6QMvJ^wr)kUsRYm~OYZo%cRmgUMZD7`Njrhw79#CE%R!jz=#9k5fm zu&$qym7L%cdt_x}8~u#J6fvV9Dn3QXTaf_*HWyi=@H-kn&qtH@g<$Y5%pLiFyTVlj zw$G}AvB`V;OY9~5O&AeS?4ZkUOsH6&rZ1zDUk@nZ3kJZUg!JFqr0E`xLYa6>_dOr3 zkOAPeouTfopW%*}3_8#99<+8D3LeU#jP@kd1yW%Sv5j%)B|0#q&x3ky826`bcBO>O z_&Yxdqy+0p<`cFE>r#d$hBnOXpu<#C$>~Hy^2%3wq%ym76`5B{&^yXG`&#-&MM7@n zfb=r{wqe0p$mRw?i}VnE?!Ls*LuW>Y|K32YdZPKP#4f)0G|DI|j^^+oM1= zt^?IwTy4lJoc)$=?eBtGLCeq>L3hHGp$#*ugINkdkUkyKE$abq``boJ@IC)U%CzIZ zlmm1v&`vTPGgsuC@0&Gs`z&4&8SX_=(z@zk%lcQ~own9)-&AmEeAidL4{LNhf2MP% zAwYZ8Kv(kcz~5G+as5^#&@>jHK3gOfCi!BWggLsJxHuxq0g!~20Q6WFy;a`6W#KgLFyeCxAQ&13w>TF8PEB(EnJ*DjzC9q zX4G(vIm{ua1b!#yM`Ljx0d$-wEx;N_m^zkV+Aa{IZT5&csCaw+l+!6^!i!q4un6@5 z6ZEh5d0S@%(0Y+^g0W``jxUU$r<1rn!Q$zK8A%bBNJDNXX&c}ZMs;f57wwZw+}3_C z>nQGAc*M|`TU_PHHiDh()dD~gaI%=K+Pa#s5hEK1`wfd^prRqWQ@|47OI5AGrejp+ zBwrVkZ)qnSlOof_W>%fwX@0pAY+jRsn%i&B+snnRfuJ^^RalUA;<7ZWc?8B z5w83vq1+$gKQD=JF=0%{5dZ{mL!Zei?*g6Z&wuWCl3!VI#!tT@J|**R|6_!m0+HK@ za5tT)0h!hgy;O>$K$GdmrboJ;JFaqMZ1&{ezGwh1%J1#^Gly|Z+q70X)y%jvYrF=5 znjm;z`NuGBg02NVKv~nPDwo93$dZWbm+=yZ=d!KDoCG7Q|7f1sp&<58+v4@EkUXJ4 z0e2enXV*+$t8}fobmXosf86Q75Nmx~?Rh7k!%qWFL?f4h+1UWz?Y2ocW1Vu#eSgn?;uAQ#l%Nj|Ir25aaG!LZ*>S;J2p8yn$aNSu&SF7h$guS;|+7J$avAW;}{f5>G0Of%pm<(j2f7n5u8jgr2S(j)>^2o^)*@ zjq+}=-G=UwRBqNTzsgOqjOW`ZH=IPo9Of_o=VEUBcJ`7_fQkB6;B+U>eyFB&qWTQT{ z*Jtae$&)~ThB#%b_&t2<8WU0F=gK?dDt8Sf!A~Aa&Be*DXN5XY?zvF$?&H$AvJZZw?%>B+(R)9khX$xv-kFd<>X_c0Iv8-ol_}>VxfoPQ&xpmu~*u18X zZ)f`H9;ZRsRoJkJ=IQ7qe~9Bf^IP5@@u;6LzXQt+PEo)ci5ye0?2*X>}x>|q?8KKBP4M<)Q3ns+Ap zI+C%#6@CHVQY|Tyvep23)-5Q4x3S}fh7>fsY`0=GCY6G9hKTN_kvo!v1*MPSVE#Jjelz1XFO-+a<%!}!as>36O2vau^Jj9jbm7O zFT#T*U#r?-T+GuuWWny3V^@6^mX;DVcFv zD|EfrNi~{jD%gbX*gyHHUF>izb4NpQ_l9%}{N?y{hpo5ZJ1x#g{^ix|hlv{(RW~wD zYKF+Lv-9nq-`x)lzy$z6cKJ>y7gq$H2R-1qdz?zpA3mOsKRssi&=t+;Tc5KNh}ppL zd&F5oAgsf`JL%g_yCn*sr!#-an~GUMjv4vDg`}Z;Q101FX<&#ys%H1UZ#U1tI#PZlzru|tFkMbi1(I<#>)Fxro2TWz_0m6~L{xR84?z*Wh zbr6l#Gb5OTB5B-ylBfYN=LiJRz@Oivy1r^9R42_Ywxu^TXZ>35A$V665YTuKbg_ZN zO2co+Rqh-L>F)EM6~tWpHd@}Nmnsg+7jh$k7wQQK=YNu3jq2#1-Qgv9_t|fYO{>PJ z)7GQoY@$oCj^S-r<88r!z`*FYNS*Q)U50v0q4)=EdZb^eXd7#K0mj)LF>v6Mht4*v zDm1-QyXJ#1%E0Sde>zwGEYQY8JufXOp7P z@I`^H^9fm!sNvrVKQaTscXY(?$goK>?V{H=2RzHhO=}Q+EPG?ug`1jqEXl)Q&h!;J zrX?mh`z3AM^#{H4Ps?4e{<(j%&}NA#3l|rWUG2aX92d~NmI-Tcwp>@P{p0?Jo%2myvpDwfN%#`Kg$PV$ zBe7-=@Ml$?ctvCi;vG;TL*Q*Mk8hFmNAw)$b7LFB+i6Lhocw1B=Eg&CeFFho?>~sD zQgjifw)r>fSSMF<7rF}G1Vjm53m;MBbAm5;`n@DS;RsEUcK(|z`bEHnrKbZ#J7(!n zUQ;Dgw-=#8m$u1vILO!6Hod!%?0U!qPeGfmFC_hR_C~S}d*%x=rjHO&T^bOY8yfB` zOBh#$-Q!8YNA`1af377WWFGJ2-f?sS8{Y$Ab$5YGNT-0*WYGB}Q)nU>+U}in+2504 ztpvM!h~_!xc_!N|XJo+Vs``oX#q(gzMu1lq`F?NRIq=T0e)}u?# z3rtSVR;#BkbZ!jZGa{92OU=0>D15!cUlO&S9h*o2@CKpMVkvSVNV=>WCXwo49gBBC z12VGREi^J!Z%m~m6&@i&a<$UppeB->%?wURF(dT)LI!M`a$4`x82sx>G>~U5aDus* zA?HLmyTA4Elgl9f=pU#INhO($0t|&oS)It}HXG)!W?0)mX&TnSg`q00T_S?m{ij44 zdFwrThPOl-d?*o;9h)gsM+Ro!j|cbw-z#E$b&EGiY{mp{R}fG(Z0`zet)AeJf`qXH zW!q9Q&JjluV{vu{rsk=b+ZsB?HIE)-F#)=?L2qVx<7S|F(tC-IazNQWtrGbNs%mjU z7%#Ts&iF`xiWmAx{Wkz^dHWSRPS4qi<5ELEe-_4H`q`a$Nt~5hFx5w!OM(D;&we}vx#$8KQ*$LmLdtkJ}Nigdo{+) z@tM~o?2(~-O$PxG#N>9}{)zen6%mo#a!%<7$d=oO_?pMb&XXPz6HWpt;l_36j#0tZ zyU(<1-Zp^rY;kI7-KIt^;Z*BP+|8tJ= zT0AH>6t>mCY+)_#k;S)YE72Fjrx4Ra-zTO z000bEL7xsq5j>yk6o~_7(Rk#&A?HHe{80&w6?75w-?#08SZBs&=Y3D4c`#~vq9afM zYXR}>JN&B@GZR(t^k#bS^~wi%AtHdMZPcY(i|>MH53PBZywgX-Wj39+SV7eolpc#= za{zq97pUoYvYhjm;^0+;?8q+L$g?#)GdbBOdKpXy@}x%$N|Qq zz8*Lq-j57uv{1q-d9;pL?aa}OQg|{sK+Q*+a)gLb+0W1Hs{W4*ijwDxq$19O+i=>H zvx1#2Aj+Bq!fmsdXhzB*PJg9j_DiFYnAD?Oc{Z`eVecY~cy~)eMMxv_HA_F)SOB-S zK5XKf!*ADppu|Le%Ko<{rg_~@gEy3n-roA*o}a9@S0^pqJyvKHraO#c_T*J#c|K$> z0~ftQs+>xf$^ZOE3>1Ml&pzr4M5WmoFBF*!Fz=Ft4u@M~4a%2Fm=UtdSP-fw_E{_F zIbq~+V$#2cZVP|Xw2sTe5V}J21^m32jo|k}_RWtyfq$N6olbSXqyf!UV;KP&>TRS? zstV11fH`~YC}P1c+62sags0J1lrZ_F;7=Edie7*ZsIHHN53`d&gi>xaN@uE3dND%; z2x%DC$x~t{10h4wiei^q>B$V^Uyrq|=NPAQ;A~E(S`K)Uu@OPJM);QsfM9kK%5Ujo zD!ZXj--E{_S&*HbyD}=4QYzuigrzPBIYWer$U1Eq2f(!!T6Zln*?G;Rc@YFy4en7= z2?_u;VY0;7S&)Mv!QBwE`na_v6=HP0-nO7B{b3FnlO1X-JQ1`-*d*Yrp#&|*wmsmpF>JQ>$+qz{6oX0v9zELy( zJqmW=2YHT2ctR6rG|)NiLa{L1Bf`{m1Y8fgou?*%1RgNb=&>7d0;$cmu0DQmCai_H}&rr5){H zNin9RkE7ij&G=#B5M4*73jlD+!%G)?l%z!CvWw`7`pJfKpv-;2pnCf&Ru_Lx=3&G> zGH~K>X&aYkm@w%>5;J~c)R@%y z5E_hzn=Y^qx&tKGeRoGF7DM?U_O%+y5dq-*-x5J=6O))jv&?2MuI;vLbP%D>7m4^X zT_S)L_TGr1f7hpNRGu-@HR0-EwtRpa%3=4-Jx)=Sz2^(T!2zAjgY|mrHXEep14`4f zQY~&XMq`L?rt>dubS7`!R980sc;dp~O#Lp-Ip+X*r4X}#*F{_lTqEc#W__>}3dWG{TE1a>pc zoj%ASHp@-)9|)Q`Dk~awJGlyV%c7pn#O-xOgv3eNP`Q0a|mNkI`>v$n9 zA(%@R0am4jK`5JNTXbU3oS= zn183)xm|jq0$@UI@Pw&KG7|q>uY)RRPH?30v*<4+R1?bcU6Ag{v|x>g(ggGd39ZDR zg7Svxb2(C&0;o?3Zza-sMrSKm4{%P&l+8rpO^z?4rL~ix@B~{jk~^$ZjP1v!g)A8? zRZ{yRf+uv?Uq&rAlnpG76BYMXRbnYC`T3}$lZh#T>DbsGFZWvaxEP){`Or0$f;Po6 z^uy*)w?r;IE=_$jOy?&&@#)k7`XVD28Wp8)0c!g6JUV|O0c`oe&1KU1s?O#vMUsIL zrWaGa=*EdMNK_fRDEJC>pPO?5X^^JGFipknhovOeQv+Ob;~;w^>encFAx}e2f?yQZ z;KNJ=hQ1v9-oeI@<19rSU8G6J(yXYvPT2 zBMH zhOSLo56&^s!A8esSnpwv6@qBm6)Un}mGN4uZvb+Czo0+qTxisOZjqcg7uj1?J$imf z19>j~NusZsYHaS*u$=EEQ>1=KIPw2qdXabFuOnQH-4rAzk?hZuH6o~H3FV;@i4&mG zDr51^#snDij_yOCWuag}?kyduwC!f3NuCt5_0X@(m|0MgI{C_$Q{yi|*TGWIhz*5J zP!SSY$4oHB`)Y9s`hOEXw2hCtY`P=*)l^@duLX%R)Sv2V@dUKU623u!*7KN&V^PMd z#$An~i^aI{S2_MEq&E^%_B%5snCq~5dwTP8BuI;20a|);(pWr|lNe9$1dud0} z#)+Q%&8=V)9HjdCmLN0pV^I{)xHT5VRYE4JW8_DhEcND zYHnCvN{JM*sHbu6)jbse9%JxW`y*Q#nT3M?uo4Kq0W=bjnazUWRYC#_o6*o`i=PSk z+dmwrKlS4xBNVda=|5leRNHYi1LG`pRnFFTm*6RQ*0@JgyyNRO{y;}xWd1NK5+ZClmQQbv}9Lg6VRe1(D9k^~ur1;b60pKKK@zf#4~h=~IeucI}`(11+-ga`R^Mb)!U~RY}bR zJ=?9|<+pdNxAhqo7+FY}W}|N|_n*Q1JVV>FK;QfqX~hFlh?rP7aC9EcEOjnibL*Q(vfZjG|y$Wo0F~2%20$qXyi8EAE<^APj z>=vz13%=u_k1W1BcrilWA8skv1!gPPc-UL1C3JjhUv@?(LH2Of)1&bK00kcbpDlDm zf0%vhlE@3K9p*1;Q=%}H7JTt>`l$D(ZpqTvuVvVv&i!?9attraYMXU(Q~JQlh!IC_ zh!F@wA4=EW$vv-C$H3?x`6tddVp~LHRytN~^{g|ZVuPeHfuP>e#@pwCVf1Jz1l~hy zgS91O^9r$Im2*JiKk=3DUO;vqdBdG=^*{VL5t7Gn=KpT~Iz?=P4UP?e^4jtNq#v4` zb}53wEW+OVvublb!2ar2a<-BzK6T4C7iWh@6X*LKj))dUQY!Q*bfPLSG9FTEW@XqcPNjpsqtp3_?PB5_`!zoC$JcK499Z00O5v}v{- zpce`bLUe>Np8>EaJhWD}Ap^pbKIJ2fLnqD!{)hK(ZrH5=!=Nn9=s) z{*G&K%_CDeP@>gID*UEC?UlCtQ|66^x{)OLIBEQ*CUpssh@XI+!?QIW``~vL#2$(L zrc=9Ds@>C8E?JJ(fGN^{55Z^cL__U+baPUyWk$;YJ^w0n#JqK)xFC8fLRU2;821ZnZZW#L9%h6OIvFhc`b3>^$cq;87?$wm1hqm(pAVX?&w z2Y`~Cvon*#@Bx$=YvzFtzOC_cDIXZMQYJz{!YQQCj?o1QVN<7%$K3O>z0Ya@3a6m63Lfb?I5!>78r^EVz z8~lNg2>fbVm+p4$CI$h0CejWcCM;)B`nvkp4mXCCs)4@~6K{k4&Qy@6bc_V{I_N#T zPmng&5XN*}(=vz@ABS)mrvbei%r1LM+MHM9@df3{G6ubGB$@WAVo8^=a=r};oL0E) z;CowAAb2pvh0~ap{hO%El=b|9_Hw4l4woj0$(!&&Ts}H-VbBK7Zv!Hex)le4WOj)xHXbV~g(~20t-? zbDv3vib7bqAyf>k&FbuFpZ|J(Eu(w(wVr+V^y*wZI93A>k4^Q!gRmG=C7q6mE<`b6 z&@GSC9_KLAOddXSy*GT&b@Ae>@SkjSFpnWOONTC(jT(exTv#bh({7|+gJ$ETc}Ko; ze_=932Dq~2*&C63r+i;qQjf4(tUt70o6f;WVQ;Pr9JvJ4ar?)o88`(aMrG$`-=r|*hZ^!GZtKpXEpzxu!K|` z{QBMa4R}WrY%~CtOx_S)fSj%Sz1eEOiAv?QNFmAPj%~}WFOUyr`@bVhjxrKPaJTQf zH48Vi&q}nK@bt6Rks}uR*~9)CtkZdug|u9a;FRRgdE4b^=N!+ZpOSGxWP^86IISFk zW?JWe-V2vHd-;SjR+Rgirn!q(Vc0DGR!IGHK~JUk-7+=h%JIw0?CnqMSMznz;s!lN zR<3Dgu5%vamZ$=QOAZGVhn&>nrnBieEXF;%R=ZDt?c)4ucb_BhRNuWU zL)qqhq8-<`Ns*TP*~Cib@l6#8{{!tZg30UNriM2fsBj$~p`9)Yc*^R$WZO6^9v!J2&62#ukGKEbmtOQermE{I z>s(h*x@Xf|D@7}R8|P~}fq|rS_Faf}m9EW?PyF}3?)NkbY_C&yxzB_3geLl6wHc4_ zDo+qk|LqNKBrR9qzhU<78<`oV26#^FSTy?F#QIHbGrgI6cM$Y1!vF9K5}5z!%@x|* zC~q1xSC(-0thOW>eCO4S9bcSQk<{EZNs4|-)rs6pTQu1bvvq98jf}ZYssRGt(R&@J ztGB@A*=u;x!#}bb6l`gvyf3wHl;BQx^RYm=a_;voIXBDM?u*!Ix-Os%0Dc~*7=bi_ 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%0D*4oXo@R`$+&*O5SOuKSbXkRP%!hllcL-q=sMm; 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 Na~Te}$0I|Msm(%Iz#9Mn literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/.gitignore b/packages/firebase_vertexai/firebase_ai/example/ios/.gitignore new file mode 100644 index 000000000000..7a7f9873ad7d --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist b/packages/firebase_vertexai/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..7c5696400627 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Flutter/Debug.xcconfig b/packages/firebase_vertexai/firebase_ai/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..ec97fc6f3021 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Flutter/Release.xcconfig b/packages/firebase_vertexai/firebase_ai/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..c4855bfe2000 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Podfile b/packages/firebase_vertexai/firebase_ai/example/ios/Podfile new file mode 100644 index 000000000000..e51a31d9ca9d --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '13.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..98cd0c1ded4a --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,756 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3414F5B6C6F086F6373F1948 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5F1FA05866A2D0FCA3287B20 /* GoogleService-Info.plist */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; + 901FEC83A38129064032C578 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94CE5BFCDF90764354BB6740 /* Pods_Runner.framework */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + B7B3CA2D70F15615E1B8E5D8 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 154D9627A1C14A5ACE0B7B0D /* Pods_RunnerTests.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 154D9627A1C14A5ACE0B7B0D /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 232D95ECCEC6F04B9CEC8925 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 560CA017EC76D8AAE2E21549 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 5F1FA05866A2D0FCA3287B20 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 8ACDC47C7E9AF1A1B9595598 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 94CE5BFCDF90764354BB6740 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A85D07EF8959748E1D3E564B /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + B0B22A9E291076BD22BA9F10 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + E1D0571EA0792087F8F27457 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0F5F3CD1ED7DB09B81C92173 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B7B3CA2D70F15615E1B8E5D8 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + 901FEC83A38129064032C578 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 3C3B3E8596675CC144D1BD5B /* Pods */ = { + isa = PBXGroup; + children = ( + E1D0571EA0792087F8F27457 /* Pods-Runner.debug.xcconfig */, + 232D95ECCEC6F04B9CEC8925 /* Pods-Runner.release.xcconfig */, + 560CA017EC76D8AAE2E21549 /* Pods-Runner.profile.xcconfig */, + A85D07EF8959748E1D3E564B /* Pods-RunnerTests.debug.xcconfig */, + 8ACDC47C7E9AF1A1B9595598 /* Pods-RunnerTests.release.xcconfig */, + B0B22A9E291076BD22BA9F10 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + 5F1FA05866A2D0FCA3287B20 /* GoogleService-Info.plist */, + 3C3B3E8596675CC144D1BD5B /* Pods */, + A50BECFB61A452F592070BAA /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + A50BECFB61A452F592070BAA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 94CE5BFCDF90764354BB6740 /* Pods_Runner.framework */, + 154D9627A1C14A5ACE0B7B0D /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + F5C7CFE0E232B64D613F0623 /* [CP] Check Pods Manifest.lock */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + 0F5F3CD1ED7DB09B81C92173 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + F51794D56D63ACA383D5C2E4 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 34F21DFC67109DEAFD936E80 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + 3414F5B6C6F086F6373F1948 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 34F21DFC67109DEAFD936E80 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + F51794D56D63ACA383D5C2E4 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + F5C7CFE0E232B64D613F0623 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = YYX2P3XVJ7; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A85D07EF8959748E1D3E564B /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8ACDC47C7E9AF1A1B9595598 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B0B22A9E291076BD22BA9F10 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = YYX2P3XVJ7; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = YYX2P3XVJ7; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000000..f9b0d7c5ea15 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..8178cd1c619c --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000000..f9b0d7c5ea15 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/AppDelegate.swift b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000000..b6363034812b --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@main +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d36b1fab2d9d --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 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 literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 000000000000..0bedcf2fd467 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000000..89c2725b70f1 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000000..f2e259c7c939 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Base.lproj/Main.storyboard b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Info.plist b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..a80f00ea0116 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + NSMicrophoneUsageDescription + We need access to the microphone to record audio. + + diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Runner-Bridging-Header.h b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000000..308a2a560b42 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/firebase_app_id_file.json b/packages/firebase_vertexai/firebase_ai/example/ios/firebase_app_id_file.json new file mode 100644 index 000000000000..59a23a1a01cc --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/ios/firebase_app_id_file.json @@ -0,0 +1,7 @@ +{ + "file_generated_by": "FlutterFire CLI", + "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", + "GOOGLE_APP_ID": "1:651313571784:ios:2f1472905da3e8e9b1c2fd", + "FIREBASE_PROJECT_ID": "vertex-ai-example-ef5a2", + "GCM_SENDER_ID": "651313571784" +} \ No newline at end of file diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/main.dart b/packages/firebase_vertexai/firebase_ai/example/lib/main.dart new file mode 100644 index 000000000000..92c56ea88d79 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/main.dart @@ -0,0 +1,321 @@ +// 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. + +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:flutter/material.dart'; + +// Import after file is generated through flutterfire_cli. +// import 'package:firebase_ai_example/firebase_options.dart'; + +import 'pages/chat_page.dart'; +import 'pages/audio_page.dart'; +import 'pages/function_calling_page.dart'; +import 'pages/image_prompt_page.dart'; +import 'pages/token_count_page.dart'; +import 'pages/schema_page.dart'; +import 'pages/imagen_page.dart'; +import 'pages/document.dart'; +import 'pages/video_page.dart'; +import 'pages/bidi_page.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + // Enable this line instead once have the firebase_options.dart generated and + // imported through flutterfire_cli. + // await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + await Firebase.initializeApp(); + await FirebaseAuth.instance.signInAnonymously(); + runApp(const GenerativeAISample()); +} + +class GenerativeAISample extends StatefulWidget { + const GenerativeAISample({super.key}); + + @override + State createState() => _GenerativeAISampleState(); +} + +class _GenerativeAISampleState extends State { + bool _useVertexBackend = false; + late GenerativeModel _currentModel; + late ImagenModel _currentImagenModel; + int _currentBottomNavIndex = 0; + + @override + void initState() { + super.initState(); + + _initializeModel(_useVertexBackend); + } + + void _initializeModel(bool useVertexBackend) { + if (useVertexBackend) { + final vertexInstance = + FirebaseAI.vertexAI(auth: FirebaseAuth.instance); + _currentModel = vertexInstance.generativeModel(model: 'gemini-1.5-flash'); + _currentImagenModel = _initializeImagenModel(vertexInstance); + } else { + final googleAI = FirebaseAI.googleAI(auth: FirebaseAuth.instance); + _currentModel = googleAI.generativeModel(model: 'gemini-2.0-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-generate-002', + generationConfig: generationConfig, + safetySettings: ImagenSafetySettings( + ImagenSafetyFilterLevel.blockLowAndAbove, + ImagenPersonFilterLevel.allowAdult, + ), + ); + } + + void _toggleBackend(bool value) { + setState(() { + _useVertexBackend = value; + }); + _initializeModel(_useVertexBackend); + } + + void _onBottomNavTapped(int index) { + setState(() { + _currentBottomNavIndex = index; + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter + ${_useVertexBackend ? 'Vertex AI' : 'Google AI'}', + debugShowCheckedModeBanner: false, + themeMode: ThemeMode.dark, + theme: ThemeData( + colorScheme: ColorScheme.fromSeed( + brightness: Brightness.dark, + seedColor: const Color.fromARGB(255, 171, 222, 244), + ), + useMaterial3: true, + ), + home: HomeScreen( + key: ValueKey( + '${_useVertexBackend}_${_currentModel.hashCode}', + ), + model: _currentModel, + imagenModel: _currentImagenModel, + useVertexBackend: _useVertexBackend, + onBackendChanged: _toggleBackend, + selectedIndex: _currentBottomNavIndex, + onSelectedIndexChanged: _onBottomNavTapped, + ), + ); + } +} + +class HomeScreen extends StatefulWidget { + final GenerativeModel model; + final ImagenModel imagenModel; + final bool useVertexBackend; + final ValueChanged onBackendChanged; + final int selectedIndex; + final ValueChanged onSelectedIndexChanged; + + const HomeScreen({ + super.key, + required this.model, + required this.imagenModel, + required this.useVertexBackend, + required this.onBackendChanged, + required this.selectedIndex, + required this.onSelectedIndexChanged, + }); + + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + void _onItemTapped(int index) { + widget.onSelectedIndexChanged(index); + } + +// Method to build the selected page on demand + Widget _buildSelectedPage( + int index, + GenerativeModel currentModel, + ImagenModel currentImagenModel, + bool useVertexBackend, + ) { + switch (index) { + case 0: + return ChatPage(title: 'Chat', model: currentModel); + case 1: + return AudioPage(title: 'Audio', model: currentModel); + case 2: + return TokenCountPage(title: 'Token Count', model: currentModel); + case 3: + // FunctionCallingPage initializes its own model as per original design + return FunctionCallingPage( + title: 'Function Calling', + useVertexBackend: useVertexBackend, + ); + 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: + return DocumentPage(title: 'Document Prompt', model: currentModel); + case 8: + return VideoPage(title: 'Video Prompt', model: currentModel); + case 9: + return BidiPage(title: 'Bidi Stream', model: currentModel); + default: + // Fallback to the first page in case of an unexpected index + return ChatPage(title: 'Chat', model: currentModel); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + 'Flutter + ${widget.useVertexBackend ? 'Vertex AI' : 'Google AI'}', + ), + actions: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Google AI', + style: TextStyle( + fontSize: 12, + color: widget.useVertexBackend + ? Theme.of(context) + .colorScheme + .onSurface + .withValues(alpha: 0.7) + : Theme.of(context).colorScheme.primary, + ), + ), + Switch( + value: widget.useVertexBackend, + onChanged: widget.onBackendChanged, + activeTrackColor: Colors.green.withValues(alpha: 0.5), + inactiveTrackColor: Colors.blueGrey.withValues(alpha: 0.5), + activeColor: Colors.green, + inactiveThumbColor: Colors.blueGrey, + ), + Text( + 'Vertex AI', + style: TextStyle( + fontSize: 12, + color: widget.useVertexBackend + ? Theme.of(context).colorScheme.primary + : Theme.of(context) + .colorScheme + .onSurface + .withValues(alpha: 0.7), + ), + ), + ], + ), + ), + ], + ), + body: Center( + child: _buildSelectedPage( + widget.selectedIndex, + widget.model, + widget.imagenModel, + widget.useVertexBackend, + ), + ), + bottomNavigationBar: BottomNavigationBar( + type: BottomNavigationBarType.fixed, + selectedFontSize: 10, + unselectedFontSize: 9, + selectedItemColor: Theme.of(context).colorScheme.primary, + unselectedItemColor: + Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7), + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.chat), + label: 'Chat', + tooltip: 'Chat', + ), + BottomNavigationBarItem( + icon: Icon(Icons.mic), + label: 'Audio', + tooltip: 'Audio Prompt', + ), + BottomNavigationBarItem( + icon: Icon(Icons.numbers), + label: 'Tokens', + tooltip: 'Token Count', + ), + BottomNavigationBarItem( + icon: Icon(Icons.functions), + label: 'Functions', + tooltip: 'Function Calling', + ), + BottomNavigationBarItem( + icon: Icon(Icons.image), + label: 'Image', + tooltip: 'Image Prompt', + ), + BottomNavigationBarItem( + icon: Icon(Icons.image_search), + label: 'Imagen', + tooltip: 'Imagen Model', + ), + BottomNavigationBarItem( + icon: Icon(Icons.schema), + label: 'Schema', + tooltip: 'Schema Prompt', + ), + BottomNavigationBarItem( + icon: Icon(Icons.edit_document), + label: 'Document', + tooltip: 'Document Prompt', + ), + BottomNavigationBarItem( + icon: Icon(Icons.video_collection), + label: 'Video', + tooltip: 'Video Prompt', + ), + BottomNavigationBarItem( + icon: Icon(Icons.stream), + label: 'Bidi', + tooltip: 'Bidi Stream', + ), + ], + currentIndex: widget.selectedIndex, + onTap: _onItemTapped, + ), + ); + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/audio_page.dart b/packages/firebase_vertexai/firebase_ai/example/lib/pages/audio_page.dart new file mode 100644 index 000000000000..8708bcb01c15 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/pages/audio_page.dart @@ -0,0 +1,186 @@ +// 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:io'; + +import 'package:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import '../widgets/message_widget.dart'; +import 'package:record/record.dart'; +import 'package:path_provider/path_provider.dart'; + +final record = AudioRecorder(); + +class AudioPage extends StatefulWidget { + const AudioPage({super.key, required this.title, required this.model}); + + final String title; + final GenerativeModel model; + + @override + State createState() => _AudioPageState(); +} + +class _AudioPageState extends State { + ChatSession? chat; + final ScrollController _scrollController = ScrollController(); + final List _messages = []; + bool _recording = false; + + @override + void initState() { + super.initState(); + chat = widget.model.startChat(); + } + + Future recordAudio() async { + if (!await record.hasPermission()) { + print('Audio recording permission denied'); + return; + } + + final dir = Directory( + '${(await getApplicationDocumentsDirectory()).path}/libs/recordings', + ); + + // ignore: avoid_slow_async_io + if (!await dir.exists()) { + await dir.create(recursive: true); + } + + String filePath = + '${dir.path}/recording_${DateTime.now().millisecondsSinceEpoch}.wav'; + + await record.start( + const RecordConfig( + encoder: AudioEncoder.wav, + ), + path: filePath, + ); + } + + Future stopRecord() async { + var path = await record.stop(); + + if (path == null) { + print('Failed to stop recording'); + return; + } + + debugPrint('Recording saved to: $path'); + + try { + File file = File(path); + final audio = await file.readAsBytes(); + debugPrint('Audio file size: ${audio.length} bytes'); + + final audioPart = InlineDataPart('audio/wav', audio); + + await _submitAudioToModel(audioPart); + + await file.delete(); + debugPrint('Recording deleted successfully.'); + } catch (e) { + debugPrint('Error processing recording: $e'); + } + } + + Future _submitAudioToModel(audioPart) async { + try { + String textPrompt = 'What is in the audio recording?'; + final prompt = TextPart('What is in the audio recording?'); + + setState(() { + _messages.add(MessageData(text: textPrompt, fromUser: true)); + }); + + final response = await widget.model.generateContent([ + Content.multi([prompt, audioPart]), + ]); + + setState(() { + _messages.add(MessageData(text: response.text, fromUser: false)); + }); + + debugPrint(response.text); + } catch (e) { + debugPrint('Error sending audio to model: $e'); + } + } + + @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: _messages[idx].text, + image: _messages[idx].image, + isFromUser: _messages[idx].fromUser ?? false, + ); + }, + itemCount: _messages.length, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, + horizontal: 15, + ), + child: Row( + children: [ + IconButton( + onPressed: () async { + setState(() { + _recording = !_recording; + }); + if (_recording) { + await recordAudio(); + } else { + await stopRecord(); + } + }, + icon: Icon( + Icons.mic, + color: _recording + ? Colors.blueGrey + : Theme.of(context).colorScheme.primary, + ), + ), + const SizedBox.square( + dimension: 15, + ), + const Text( + 'Tap the mic to record, tap again to submit', + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/bidi_page.dart b/packages/firebase_vertexai/firebase_ai/example/lib/pages/bidi_page.dart new file mode 100644 index 000000000000..acd936a7e827 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/pages/bidi_page.dart @@ -0,0 +1,448 @@ +// 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 'dart:async'; +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import '../widgets/message_widget.dart'; +import '../utils/audio_player.dart'; +import '../utils/audio_recorder.dart'; + +class BidiPage extends StatefulWidget { + const BidiPage({super.key, required this.title, required this.model}); + + final String title; + final GenerativeModel model; + + @override + State createState() => _BidiPageState(); +} + +class LightControl { + final int? brightness; + final String? colorTemperature; + + LightControl({this.brightness, this.colorTemperature}); +} + +class _BidiPageState extends State { + final ScrollController _scrollController = ScrollController(); + final TextEditingController _textController = TextEditingController(); + final FocusNode _textFieldFocus = FocusNode(); + final List _messages = []; + bool _loading = false; + bool _sessionOpening = false; + bool _recording = false; + late LiveGenerativeModel _liveModel; + late LiveSession _session; + final _audioManager = AudioStreamManager(); + final _audioRecorder = InMemoryAudioRecorder(); + var _chunkBuilder = BytesBuilder(); + var _audioIndex = 0; + StreamController _stopController = StreamController(); + + @override + void initState() { + super.initState(); + + final config = LiveGenerationConfig( + speechConfig: SpeechConfig(voiceName: 'Fenrir'), + responseModalities: [ + ResponseModalities.audio, + ], + ); + + _liveModel = FirebaseAI.vertexAI().liveGenerativeModel( + model: 'gemini-2.0-flash-exp', + liveGenerationConfig: config, + tools: [ + Tool.functionDeclarations([lightControlTool]), + ], + ); + } + + void _scrollDown() { + WidgetsBinding.instance.addPostFrameCallback( + (_) => _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration( + milliseconds: 750, + ), + curve: Curves.easeOutCirc, + ), + ); + } + + @override + void dispose() { + if (_sessionOpening) { + _audioManager.stopAudioPlayer(); + _audioManager.disposeAudioPlayer(); + + _audioRecorder.stopRecording(); + + _stopController.close(); + + _sessionOpening = false; + _session.close(); + } + super.dispose(); + } + + @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: _messages[idx].text, + image: _messages[idx].image, + isFromUser: _messages[idx].fromUser ?? false, + ); + }, + itemCount: _messages.length, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, + horizontal: 15, + ), + child: Row( + children: [ + Expanded( + child: TextField( + autofocus: true, + focusNode: _textFieldFocus, + controller: _textController, + onSubmitted: _sendTextPrompt, + ), + ), + const SizedBox.square( + dimension: 15, + ), + IconButton( + tooltip: 'Start Streaming', + onPressed: !_loading + ? () async { + await _setupSession(); + } + : null, + icon: Icon( + Icons.network_wifi, + color: _sessionOpening + ? Theme.of(context).colorScheme.secondary + : Theme.of(context).colorScheme.primary, + ), + ), + IconButton( + tooltip: 'Send Stream Message', + onPressed: !_loading + ? () async { + if (_recording) { + await _stopRecording(); + } else { + await _startRecording(); + } + } + : null, + icon: Icon( + _recording ? Icons.stop : Icons.mic, + color: _loading + ? Theme.of(context).colorScheme.secondary + : Theme.of(context).colorScheme.primary, + ), + ), + if (!_loading) + IconButton( + onPressed: () async { + await _sendTextPrompt(_textController.text); + }, + icon: Icon( + Icons.send, + color: Theme.of(context).colorScheme.primary, + ), + ) + else + const CircularProgressIndicator(), + ], + ), + ), + ], + ), + ), + ); + } + + final lightControlTool = FunctionDeclaration( + 'setLightValues', + 'Set the brightness and color temperature of a room light.', + parameters: { + 'brightness': Schema.integer( + description: 'Light level from 0 to 100. ' + 'Zero is off and 100 is full brightness.', + ), + 'colorTemperature': Schema.string( + description: 'Color temperature of the light fixture, ' + 'which can be `daylight`, `cool` or `warm`.', + ), + }, + ); + + Future> _setLightValues({ + int? brightness, + String? colorTemperature, + }) async { + final apiResponse = { + 'colorTemprature': 'warm', + 'brightness': brightness, + }; + return apiResponse; + } + + Future _setupSession() async { + setState(() { + _loading = true; + }); + + if (!_sessionOpening) { + _session = await _liveModel.connect(); + _sessionOpening = true; + _stopController = StreamController(); + unawaited( + processMessagesContinuously( + stopSignal: _stopController, + ), + ); + } else { + _stopController.add(true); + await _stopController.close(); + + await _session.close(); + await _audioManager.stopAudioPlayer(); + await _audioManager.disposeAudioPlayer(); + _sessionOpening = false; + } + + setState(() { + _loading = false; + }); + } + + Future _startRecording() async { + setState(() { + _recording = true; + }); + try { + await _audioRecorder.checkPermission(); + final audioRecordStream = _audioRecorder.startRecordingStream(); + // Map the Uint8List stream to InlineDataPart stream + final mediaChunkStream = audioRecordStream.map((data) { + return InlineDataPart('audio/pcm', data); + }); + await _session.sendMediaStream(mediaChunkStream); + } catch (e) { + _showError(e.toString()); + } + } + + Future _stopRecording() async { + try { + await _audioRecorder.stopRecording(); + } catch (e) { + _showError(e.toString()); + } + + setState(() { + _recording = false; + }); + } + + Future _sendTextPrompt(String textPrompt) async { + setState(() { + _loading = true; + }); + try { + final prompt = Content.text(textPrompt); + await _session.send(input: prompt, turnComplete: true); + } catch (e) { + _showError(e.toString()); + } + + setState(() { + _loading = false; + }); + } + + Future processMessagesContinuously({ + required StreamController stopSignal, + }) async { + bool shouldContinue = true; + + //listen to the stop signal stream + stopSignal.stream.listen((stop) { + if (stop) { + shouldContinue = false; + } + }); + + while (shouldContinue) { + try { + await for (final message in _session.receive()) { + // Process the received message + await _handleLiveServerMessage(message); + } + } catch (e) { + _showError(e.toString()); + break; + } + + // Optionally add a delay before restarting, if needed + await Future.delayed( + const Duration(milliseconds: 100), + ); // Small delay to prevent tight loops + } + } + + Future _handleLiveServerMessage(LiveServerResponse response) async { + final message = response.message; + + if (message is LiveServerContent) { + if (message.modelTurn != null) { + await _handleLiveServerContent(message); + } + if (message.turnComplete != null && message.turnComplete!) { + await _handleTurnComplete(); + } + if (message.interrupted != null && message.interrupted!) { + log('Interrupted: $response'); + } + } else if (message is LiveServerToolCall && message.functionCalls != null) { + await _handleLiveServerToolCall(message); + } + } + + Future _handleLiveServerContent(LiveServerContent response) async { + final partList = response.modelTurn?.parts; + if (partList != null) { + for (final part in partList) { + if (part is TextPart) { + await _handleTextPart(part); + } else if (part is InlineDataPart) { + await _handleInlineDataPart(part); + } else { + log('receive part with type ${part.runtimeType}'); + } + } + } + } + + Future _handleTextPart(TextPart part) async { + if (!_loading) { + setState(() { + _loading = true; + }); + } + _messages.add(MessageData(text: part.text, fromUser: false)); + setState(() { + _loading = false; + _scrollDown(); + }); + } + + Future _handleInlineDataPart(InlineDataPart part) async { + if (part.mimeType.startsWith('audio')) { + _chunkBuilder.add(part.bytes); + _audioIndex++; + if (_audioIndex == 15) { + Uint8List chunk = await audioChunkWithHeader( + _chunkBuilder.toBytes(), + 24000, + ); + _audioManager.addAudio(chunk); + _chunkBuilder.clear(); + _audioIndex = 0; + } + } + } + + Future _handleTurnComplete() async { + if (_chunkBuilder.isNotEmpty) { + Uint8List chunk = await audioChunkWithHeader( + _chunkBuilder.toBytes(), + 24000, + ); + _audioManager.addAudio(chunk); + _audioIndex = 0; + _chunkBuilder.clear(); + } + } + + Future _handleLiveServerToolCall(LiveServerToolCall response) async { + final functionCalls = response.functionCalls!.toList(); + if (functionCalls.isNotEmpty) { + final functionCall = functionCalls.first; + if (functionCall.name == 'setLightValues') { + var color = functionCall.args['colorTemperature']! as String; + var brightness = functionCall.args['brightness']! as int; + final functionResult = await _setLightValues( + brightness: brightness, + colorTemperature: color, + ); + await _session.send( + input: Content.functionResponse(functionCall.name, functionResult), + ); + } else { + throw UnimplementedError( + 'Function not declared to the model: ${functionCall.name}', + ); + } + } + } + + 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_vertexai/firebase_ai/example/lib/pages/chat_page.dart b/packages/firebase_vertexai/firebase_ai/example/lib/pages/chat_page.dart new file mode 100644 index 000000000000..df0afea88482 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/pages/chat_page.dart @@ -0,0 +1,176 @@ +// 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:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import '../widgets/message_widget.dart'; + +class ChatPage extends StatefulWidget { + const ChatPage({super.key, required this.title, required this.model}); + + final String title; + final GenerativeModel model; + + @override + State createState() => _ChatPageState(); +} + +class _ChatPageState extends State { + ChatSession? _chat; + final ScrollController _scrollController = ScrollController(); + final TextEditingController _textController = TextEditingController(); + final FocusNode _textFieldFocus = FocusNode(); + final List _messages = []; + bool _loading = false; + + @override + void initState() { + super.initState(); + _chat = widget.model.startChat(); + } + + 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: _messages[idx].text, + image: _messages[idx].image, + isFromUser: _messages[idx].fromUser ?? false, + ); + }, + itemCount: _messages.length, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, + horizontal: 15, + ), + child: Row( + children: [ + Expanded( + child: TextField( + autofocus: true, + focusNode: _textFieldFocus, + controller: _textController, + onSubmitted: _sendChatMessage, + ), + ), + const SizedBox.square( + dimension: 15, + ), + if (!_loading) + IconButton( + onPressed: () async { + await _sendChatMessage(_textController.text); + }, + icon: Icon( + Icons.send, + color: Theme.of(context).colorScheme.primary, + ), + ) + else + const CircularProgressIndicator(), + ], + ), + ), + ], + ), + ), + ); + } + + Future _sendChatMessage(String message) async { + setState(() { + _loading = true; + }); + + try { + _messages.add(MessageData(text: message, fromUser: true)); + var response = await _chat?.sendMessage( + Content.text(message), + ); + var text = response?.text; + _messages.add(MessageData(text: text, fromUser: false)); + + if (text == null) { + _showError('No response from API.'); + return; + } else { + setState(() { + _loading = false; + _scrollDown(); + }); + } + } catch (e) { + _showError(e.toString()); + setState(() { + _loading = false; + }); + } finally { + _textController.clear(); + setState(() { + _loading = false; + }); + _textFieldFocus.requestFocus(); + } + } + + 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_vertexai/firebase_ai/example/lib/pages/document.dart b/packages/firebase_vertexai/firebase_ai/example/lib/pages/document.dart new file mode 100644 index 000000000000..ec5114e8b13a --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/pages/document.dart @@ -0,0 +1,117 @@ +// 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:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:flutter/services.dart'; +import '../widgets/message_widget.dart'; + +class DocumentPage extends StatefulWidget { + const DocumentPage({super.key, required this.title, required this.model}); + + final String title; + final GenerativeModel model; + + @override + State createState() => _DocumentPageState(); +} + +class _DocumentPageState extends State { + ChatSession? chat; + late final GenerativeModel model; + final List _messages = []; + bool _loading = false; + + @override + void initState() { + super.initState(); + chat = widget.model.startChat(); + } + + Future _testDocumentReading(model) async { + try { + ByteData docBytes = + await rootBundle.load('assets/documents/gemini_summary.pdf'); + + const _prompt = + 'Write me a summary in one sentence what this document is about.'; + + final prompt = TextPart(_prompt); + + setState(() { + _messages.add(MessageData(text: _prompt, fromUser: true)); + }); + + final pdfPart = + InlineDataPart('application/pdf', docBytes.buffer.asUint8List()); + + final response = await widget.model.generateContent([ + Content.multi([prompt, pdfPart]), + ]); + + setState(() { + _messages.add(MessageData(text: response.text, fromUser: false)); + }); + } catch (e) { + print('Error sending document to model: $e'); + } + } + + @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( + itemBuilder: (context, idx) { + return MessageWidget( + text: _messages[idx].text, + isFromUser: _messages[idx].fromUser ?? false, + ); + }, + itemCount: _messages.length, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, + horizontal: 15, + ), + child: Center( + child: SizedBox( + child: ElevatedButton( + onPressed: !_loading + ? () async { + await _testDocumentReading(widget.model); + } + : null, + child: const Text('Test Document Reading'), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/function_calling_page.dart b/packages/firebase_vertexai/firebase_ai/example/lib/pages/function_calling_page.dart new file mode 100644 index 000000000000..cf79b61a7104 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/pages/function_calling_page.dart @@ -0,0 +1,200 @@ +// 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:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import '../widgets/message_widget.dart'; + +class FunctionCallingPage extends StatefulWidget { + const FunctionCallingPage({ + super.key, + required this.title, + required this.useVertexBackend, + }); + + final String title; + final bool useVertexBackend; + + @override + State createState() => _FunctionCallingPageState(); +} + +class Location { + final String city; + final String state; + + Location(this.city, this.state); +} + +class _FunctionCallingPageState extends State { + late final GenerativeModel _functionCallModel; + final List _messages = []; + bool _loading = false; + + @override + void initState() { + super.initState(); + if (widget.useVertexBackend) { + var vertexAI = FirebaseAI.vertexAI(auth: FirebaseAuth.instance); + _functionCallModel = vertexAI.generativeModel( + model: 'gemini-2.0-flash', + tools: [ + Tool.functionDeclarations([fetchWeatherTool]), + ], + ); + } else { + var googleAI = FirebaseAI.googleAI(auth: FirebaseAuth.instance); + _functionCallModel = googleAI.generativeModel( + model: 'gemini-2.0-flash', + tools: [ + Tool.functionDeclarations([fetchWeatherTool]), + ], + ); + } + } + + // This is a hypothetical API to return a fake weather data collection for + // certain location + Future> fetchWeather( + Location location, + String date, + ) async { + // TODO(developer): Call a real weather API. + // Mock response from the API. In developer live code this would call the + // external API and return what that API returns. + final apiResponse = { + 'temperature': 38, + 'chancePrecipitation': '56%', + 'cloudConditions': 'partly-cloudy', + }; + return apiResponse; + } + + /// Actual function to demonstrate the function calling feature. + final fetchWeatherTool = FunctionDeclaration( + 'fetchWeather', + 'Get the weather conditions for a specific city on a specific date.', + parameters: { + 'location': Schema.object( + description: 'The name of the city and its state for which to get ' + 'the weather. Only cities in the USA are supported.', + properties: { + 'city': Schema.string( + description: 'The city of the location.', + ), + 'state': Schema.string( + description: 'The state of the location.', + ), + }, + ), + 'date': Schema.string( + description: 'The date for which to get the weather. ' + 'Date must be in the format: YYYY-MM-DD.', + ), + }, + ); + + @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( + itemBuilder: (context, idx) { + return MessageWidget( + text: _messages[idx].text, + isFromUser: _messages[idx].fromUser ?? false, + ); + }, + itemCount: _messages.length, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, + horizontal: 15, + ), + child: Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: !_loading + ? () async { + await _testFunctionCalling(); + } + : null, + child: const Text('Test Function Calling'), + ), + ), + ], + ), + ), + ], + ), + ), + ); + } + + Future _testFunctionCalling() async { + setState(() { + _loading = true; + }); + final functionCallChat = _functionCallModel.startChat(); + const prompt = 'What is the weather like in Boston on 10/02 in year 2024?'; + + // Send the message to the generative model. + var response = await functionCallChat.sendMessage( + Content.text(prompt), + ); + + final functionCalls = response.functionCalls.toList(); + // When the model response with a function call, invoke the function. + if (functionCalls.isNotEmpty) { + final functionCall = functionCalls.first; + if (functionCall.name == 'fetchWeather') { + Map location = + functionCall.args['location']! as Map; + var date = functionCall.args['date']! as String; + var city = location['city'] as String; + var state = location['state'] as String; + final functionResult = await fetchWeather(Location(city, state), date); + // Send the response to the model so that it can use the result to + // generate text for the user. + response = await functionCallChat.sendMessage( + Content.functionResponse(functionCall.name, functionResult), + ); + } else { + throw UnimplementedError( + 'Function not declared to the model: ${functionCall.name}', + ); + } + } + // When the model responds with non-null text content, print it. + if (response.text case final text?) { + _messages.add(MessageData(text: text)); + setState(() { + _loading = false; + }); + } + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/image_prompt_page.dart b/packages/firebase_vertexai/firebase_ai/example/lib/pages/image_prompt_page.dart new file mode 100644 index 000000000000..5dff25a2efe1 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/pages/image_prompt_page.dart @@ -0,0 +1,243 @@ +// 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:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:flutter/services.dart'; +import '../widgets/message_widget.dart'; + +class ImagePromptPage extends StatefulWidget { + const ImagePromptPage({super.key, required this.title, required this.model}); + + final String title; + final GenerativeModel model; + + @override + State createState() => _ImagePromptPageState(); +} + +class _ImagePromptPageState extends State { + final ScrollController _scrollController = ScrollController(); + final TextEditingController _textController = TextEditingController(); + final FocusNode _textFieldFocus = FocusNode(); + final List _generatedContent = []; + bool _loading = false; + + 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) { + var content = _generatedContent[idx]; + return MessageWidget( + text: content.text, + image: content.image, + isFromUser: content.fromUser ?? false, + ); + }, + itemCount: _generatedContent.length, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, + horizontal: 15, + ), + child: Row( + children: [ + Expanded( + child: TextField( + autofocus: true, + focusNode: _textFieldFocus, + controller: _textController, + ), + ), + const SizedBox.square( + dimension: 15, + ), + if (!_loading) + IconButton( + onPressed: () async { + await _sendImagePrompt(_textController.text); + }, + icon: Icon( + Icons.image, + color: Theme.of(context).colorScheme.primary, + ), + ), + if (!_loading) + IconButton( + onPressed: () async { + await _sendStorageUriPrompt(_textController.text); + }, + icon: Icon( + Icons.storage, + color: Theme.of(context).colorScheme.primary, + ), + ) + else + const CircularProgressIndicator(), + ], + ), + ), + ], + ), + ), + ); + } + + Future _sendImagePrompt(String message) async { + setState(() { + _loading = true; + }); + try { + ByteData catBytes = await rootBundle.load('assets/images/cat.jpg'); + ByteData sconeBytes = await rootBundle.load('assets/images/scones.jpg'); + final content = [ + Content.multi([ + TextPart(message), + // The only accepted mime types are image/*. + InlineDataPart('image/jpeg', catBytes.buffer.asUint8List()), + InlineDataPart('image/jpeg', sconeBytes.buffer.asUint8List()), + ]), + ]; + _generatedContent.add( + MessageData( + image: Image.asset('assets/images/cat.jpg'), + text: message, + fromUser: true, + ), + ); + _generatedContent.add( + MessageData( + image: Image.asset('assets/images/scones.jpg'), + fromUser: true, + ), + ); + + var response = await widget.model.generateContent(content); + var text = response.text; + _generatedContent.add(MessageData(text: text, fromUser: false)); + + if (text == null) { + _showError('No response from API.'); + return; + } else { + setState(() { + _loading = false; + _scrollDown(); + }); + } + } catch (e) { + _showError(e.toString()); + setState(() { + _loading = false; + }); + } finally { + _textController.clear(); + setState(() { + _loading = false; + }); + _textFieldFocus.requestFocus(); + } + } + + Future _sendStorageUriPrompt(String message) async { + setState(() { + _loading = true; + }); + try { + final content = [ + Content.multi([ + TextPart(message), + FileData( + 'image/jpeg', + 'gs://vertex-ai-example-ef5a2.appspot.com/foodpic.jpg', + ), + ]), + ]; + _generatedContent.add(MessageData(text: message, fromUser: true)); + + var response = await widget.model.generateContent(content); + var text = response.text; + _generatedContent.add(MessageData(text: text, fromUser: false)); + + if (text == null) { + _showError('No response from API.'); + return; + } else { + setState(() { + _loading = false; + _scrollDown(); + }); + } + } catch (e) { + _showError(e.toString()); + setState(() { + _loading = false; + }); + } finally { + _textController.clear(); + setState(() { + _loading = false; + }); + _textFieldFocus.requestFocus(); + } + } + + 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_vertexai/firebase_ai/example/lib/pages/imagen_page.dart b/packages/firebase_vertexai/firebase_ai/example/lib/pages/imagen_page.dart new file mode 100644 index 000000000000..c957f207278e --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/pages/imagen_page.dart @@ -0,0 +1,214 @@ +// 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:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +//import 'package:firebase_storage/firebase_storage.dart'; +import '../widgets/message_widget.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; + + 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: _generatedContent[idx].image, + isFromUser: _generatedContent[idx].fromUser ?? false, + ); + }, + itemCount: _generatedContent.length, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, + horizontal: 15, + ), + child: Row( + children: [ + Expanded( + child: TextField( + autofocus: true, + focusNode: _textFieldFocus, + controller: _textController, + ), + ), + const SizedBox.square( + dimension: 15, + ), + if (!_loading) + IconButton( + onPressed: () async { + await _testImagen(_textController.text); + }, + icon: Icon( + Icons.image_search, + color: Theme.of(context).colorScheme.primary, + ), + tooltip: 'Imagen raw data', + ) + else + const CircularProgressIndicator(), + // NOTE: Keep this API private until future release. + // if (!_loading) + // IconButton( + // onPressed: () async { + // await _testImagenGCS(_textController.text); + // }, + // icon: Icon( + // Icons.imagesearch_roller, + // color: Theme.of(context).colorScheme.primary, + // ), + // tooltip: 'Imagen GCS', + // ) + // else + // const CircularProgressIndicator(), + ], + ), + ), + ], + ), + ), + ); + } + + Future _testImagen(String prompt) async { + setState(() { + _loading = true; + }); + + try { + var response = await widget.model.generateImages(prompt); + + if (response.images.isNotEmpty) { + var imagenImage = response.images[0]; + + _generatedContent.add( + MessageData( + image: Image.memory(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(() { + _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_vertexai/firebase_ai/example/lib/pages/schema_page.dart b/packages/firebase_vertexai/firebase_ai/example/lib/pages/schema_page.dart new file mode 100644 index 000000000000..fcbdef64499e --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/pages/schema_page.dart @@ -0,0 +1,182 @@ +// 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:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import '../widgets/message_widget.dart'; + +class SchemaPromptPage extends StatefulWidget { + const SchemaPromptPage({super.key, required this.title, required this.model}); + + final String title; + final GenerativeModel model; + + @override + State createState() => _SchemaPromptPageState(); +} + +class _SchemaPromptPageState extends State { + final ScrollController _scrollController = ScrollController(); + final TextEditingController _textController = TextEditingController(); + final FocusNode _textFieldFocus = FocusNode(); + final List _messages = []; + bool _loading = false; + + 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: _messages[idx].text, + isFromUser: _messages[idx].fromUser ?? false, + ); + }, + itemCount: _messages.length, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, + horizontal: 15, + ), + child: Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: !_loading + ? () async { + await _promptSchemaTest(); + } + : null, + child: const Text('Schema Prompt'), + ), + ), + ], + ), + ), + ], + ), + ), + ); + } + + Future _promptSchemaTest() async { + setState(() { + _loading = true; + }); + try { + final content = [ + Content.text( + "For use in a children's card game, generate 10 animal-based " + 'characters.', + ), + ]; + + final jsonSchema = Schema.object( + properties: { + 'characters': Schema.array( + items: Schema.object( + properties: { + 'name': Schema.string(), + 'age': Schema.integer(), + 'species': Schema.string(), + 'accessory': + Schema.enumString(enumValues: ['hat', 'belt', 'shoes']), + }, + ), + ), + }, + optionalProperties: ['accessory'], + ); + + final response = await widget.model.generateContent( + content, + generationConfig: GenerationConfig( + responseMimeType: 'application/json', + responseSchema: jsonSchema, + ), + ); + + var text = response.text; + _messages.add(MessageData(text: text, fromUser: false)); + + if (text == null) { + _showError('No response from API.'); + return; + } else { + setState(() { + _loading = false; + _scrollDown(); + }); + } + } catch (e) { + _showError(e.toString()); + setState(() { + _loading = false; + }); + } finally { + _textController.clear(); + setState(() { + _loading = false; + }); + _textFieldFocus.requestFocus(); + } + } + + 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_vertexai/firebase_ai/example/lib/pages/token_count_page.dart b/packages/firebase_vertexai/firebase_ai/example/lib/pages/token_count_page.dart new file mode 100644 index 000000000000..8e2455d5d429 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/pages/token_count_page.dart @@ -0,0 +1,106 @@ +// 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:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import '../widgets/message_widget.dart'; + +class TokenCountPage extends StatefulWidget { + const TokenCountPage({super.key, required this.title, required this.model}); + + final String title; + final GenerativeModel model; + + @override + State createState() => _TokenCountPageState(); +} + +class _TokenCountPageState extends State { + final List _messages = []; + bool _loading = false; + + @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( + itemBuilder: (context, idx) { + return MessageWidget( + text: _messages[idx].text, + isFromUser: _messages[idx].fromUser ?? false, + ); + }, + itemCount: _messages.length, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, + horizontal: 15, + ), + child: Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: !_loading + ? () async { + await _testCountToken(); + } + : null, + child: const Text('Count Tokens'), + ), + ), + ], + ), + ), + ], + ), + ), + ); + } + + Future _testCountToken() async { + setState(() { + _loading = true; + }); + + const prompt = 'tell a short story'; + final content = Content.text(prompt); + final tokenResponse = await widget.model.countTokens([content]); + final tokenResult = 'Count token: ${tokenResponse.totalTokens}, billable ' + 'characters: ${tokenResponse.totalBillableCharacters}'; + _messages.add(MessageData(text: tokenResult, fromUser: false)); + + final contentResponse = await widget.model.generateContent([content]); + final contentMetaData = 'result metadata, promptTokenCount:' + '${contentResponse.usageMetadata!.promptTokenCount}, ' + 'candidatesTokenCount:' + '${contentResponse.usageMetadata!.candidatesTokenCount}, ' + 'totalTokenCount:' + '${contentResponse.usageMetadata!.totalTokenCount}'; + _messages.add(MessageData(text: contentMetaData, fromUser: false)); + setState(() { + _loading = false; + }); + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/video_page.dart b/packages/firebase_vertexai/firebase_ai/example/lib/pages/video_page.dart new file mode 100644 index 000000000000..0a98c9a82486 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/pages/video_page.dart @@ -0,0 +1,116 @@ +// 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:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:flutter/services.dart'; +import '../widgets/message_widget.dart'; + +class VideoPage extends StatefulWidget { + const VideoPage({super.key, required this.title, required this.model}); + + final String title; + final GenerativeModel model; + + @override + State createState() => _VideoPageState(); +} + +class _VideoPageState extends State { + ChatSession? chat; + late final GenerativeModel model; + final List _messages = []; + bool _loading = false; + + @override + void initState() { + super.initState(); + chat = widget.model.startChat(); + } + + Future _testVideo(model) async { + try { + ByteData videoBytes = + await rootBundle.load('assets/videos/landscape.mp4'); + + const _prompt = 'Can you tell me what is in the video?'; + + final prompt = TextPart(_prompt); + + setState(() { + _messages.add(MessageData(text: _prompt, fromUser: true)); + }); + + final videoPart = + InlineDataPart('video/mp4', videoBytes.buffer.asUint8List()); + + final response = await widget.model.generateContent([ + Content.multi([prompt, videoPart]), + ]); + + setState(() { + _messages.add(MessageData(text: response.text, fromUser: false)); + }); + } catch (e) { + print('Error sending video to model: $e'); + } + } + + @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( + itemBuilder: (context, idx) { + return MessageWidget( + text: _messages[idx].text, + isFromUser: _messages[idx].fromUser ?? false, + ); + }, + itemCount: _messages.length, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, + horizontal: 15, + ), + child: Center( + child: SizedBox( + child: ElevatedButton( + onPressed: !_loading + ? () async { + await _testVideo(widget.model); + } + : null, + child: const Text('Test Video Prompt'), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/utils/audio_player.dart b/packages/firebase_vertexai/firebase_ai/example/lib/utils/audio_player.dart new file mode 100644 index 000000000000..3c5559481ed7 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/utils/audio_player.dart @@ -0,0 +1,143 @@ +// 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 'dart:async'; + +import 'package:just_audio/just_audio.dart'; + +/// Creates a WAV audio chunk with a properly formatted header. +Future audioChunkWithHeader( + List data, + int sampleRate, +) async { + var channels = 1; + + int byteRate = ((16 * sampleRate * channels) / 8).round(); + + var size = data.length; + var fileSize = size + 36; + + Uint8List header = Uint8List.fromList([ + // "RIFF" + 82, 73, 70, 70, + fileSize & 0xff, + (fileSize >> 8) & 0xff, + (fileSize >> 16) & 0xff, + (fileSize >> 24) & 0xff, + // WAVE + 87, 65, 86, 69, + // fmt + 102, 109, 116, 32, + // fmt chunk size 16 + 16, 0, 0, 0, + // Type of format + 1, 0, + // One channel + channels, 0, + // Sample rate + sampleRate & 0xff, + (sampleRate >> 8) & 0xff, + (sampleRate >> 16) & 0xff, + (sampleRate >> 24) & 0xff, + // Byte rate + byteRate & 0xff, + (byteRate >> 8) & 0xff, + (byteRate >> 16) & 0xff, + (byteRate >> 24) & 0xff, + // Uhm + ((16 * channels) / 8).round(), 0, + // bitsize + 16, 0, + // "data" + 100, 97, 116, 97, + size & 0xff, + (size >> 8) & 0xff, + (size >> 16) & 0xff, + (size >> 24) & 0xff, + // incoming data + ...data, + ]); + return header; +} + +class ByteStreamAudioSource extends StreamAudioSource { + ByteStreamAudioSource(this.bytes) : super(tag: 'Byte Stream Audio'); + + final Uint8List bytes; + + @override + Future request([int? start, int? end]) async { + start ??= 0; + end ??= bytes.length; + return StreamAudioResponse( + sourceLength: bytes.length, + contentLength: end - start, + offset: start, + stream: Stream.value(bytes.sublist(start, end)), + contentType: 'audio/wav', // Or the appropriate content type + ); + } +} + +class AudioStreamManager { + final _audioPlayer = AudioPlayer(); + final _audioChunkController = StreamController(); + var _audioSource = ConcatenatingAudioSource( + children: [], + ); + + AudioStreamManager() { + _initAudioPlayer(); + } + + Future _initAudioPlayer() async { + // 1. Create a ConcatenatingAudioSource to handle the stream + await _audioPlayer.setAudioSource(_audioSource); + + // 2. Listen to the stream of audio chunks + _audioChunkController.stream.listen(_addAudioChunk); + + await _audioPlayer.play(); // Start playing (even if initially empty) + + _audioPlayer.processingStateStream.listen((state) async { + if (state == ProcessingState.completed) { + await _audioPlayer + .pause(); // Or player.stop() if you want to release resources + await _audioPlayer.seek(Duration.zero, index: 0); + await _audioSource.clear(); + await _audioPlayer.play(); + } + }); + } + + Future _addAudioChunk(Uint8List chunk) async { + var buffer = ByteStreamAudioSource(chunk); + + await _audioSource.add(buffer); + } + + void addAudio(Uint8List chunk) { + _audioChunkController.add(chunk); + } + + Future stopAudioPlayer() async { + await _audioPlayer.stop(); + } + + Future disposeAudioPlayer() async { + await _audioPlayer.dispose(); + await _audioChunkController.close(); + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/utils/audio_recorder.dart b/packages/firebase_vertexai/firebase_ai/example/lib/utils/audio_recorder.dart new file mode 100644 index 000000000000..1f3710cd0c8f --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/utils/audio_recorder.dart @@ -0,0 +1,245 @@ +// 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:async'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:record/record.dart'; + +/// An exception thrown when microphone permission is denied or not granted. +class MicrophonePermissionDeniedException implements Exception { + /// The optional message associated with the permission denial. + final String? message; + + /// Creates a new [MicrophonePermissionDeniedException] with an optional [message]. + MicrophonePermissionDeniedException([this.message]); + + @override + String toString() { + if (message == null) { + return 'MicrophonePermissionDeniedException'; + } + return 'MicrophonePermissionDeniedException: $message'; + } +} + +class Resampler { + /// Resamples 16-bit integer PCM audio data from a source sample rate to a + /// target sample rate using linear interpolation. + /// + /// [sourceRate]: The sample rate of the input audio data. + /// [targetRate]: The desired sample rate of the output audio data. + /// [input]: The input audio data as a Uint8List containing 16-bit PCM samples. + /// + /// Returns a new Uint8List containing 16-bit PCM samples resampled to the + /// target rate. + static Uint8List resampleLinear16( + int sourceRate, + int targetRate, + Uint8List input, + ) { + if (sourceRate == targetRate) return input; // No resampling needed + + final outputLength = (input.length * targetRate / sourceRate).round(); + final output = Uint8List(outputLength); + final inputData = Int16List.view(input.buffer); + final outputData = Int16List.view(output.buffer); + + for (int i = 0; i < outputLength ~/ 2; i++) { + final sourcePosition = i * sourceRate / targetRate; + final index1 = sourcePosition.floor(); + final index2 = index1 + 1; + final weight2 = sourcePosition - index1; + final weight1 = 1.0 - weight2; + + // Ensure indices are within the valid range + final sample1 = inputData[index1.clamp(0, inputData.length - 1)]; + final sample2 = inputData[index2.clamp(0, inputData.length - 1)]; + + // Interpolate and convert back to 16-bit integer + final interpolatedSample = + (sample1 * weight1 + sample2 * weight2).toInt(); + + outputData[i] = interpolatedSample; + } + + return output; + } +} + +class InMemoryAudioRecorder { + final _audioChunks = []; + final _recorder = AudioRecorder(); + StreamSubscription? _recordSubscription; + late String? _lastAudioPath; + AudioEncoder _encoder = AudioEncoder.pcm16bits; + + Future _getPath() async { + String suffix; + if (_encoder == AudioEncoder.pcm16bits) { + suffix = 'pcm'; + } else if (_encoder == AudioEncoder.aacLc) { + suffix = 'm4a'; + } else { + suffix = 'wav'; + } + final dir = await getDownloadsDirectory(); + final path = + '${dir!.path}/audio_${DateTime.now().millisecondsSinceEpoch}.$suffix'; + return path; + } + + Future checkPermission() async { + final hasPermission = await _recorder.hasPermission(); + if (!hasPermission) { + throw MicrophonePermissionDeniedException('Not having mic permission'); + } + } + + Future _isEncoderSupported(AudioEncoder encoder) async { + final isSupported = await _recorder.isEncoderSupported( + encoder, + ); + + if (!isSupported) { + debugPrint('${encoder.name} is not supported on this platform.'); + debugPrint('Supported encoders are:'); + + for (final e in AudioEncoder.values) { + if (await _recorder.isEncoderSupported(e)) { + debugPrint('- ${e.name}'); + } + } + } + + return isSupported; + } + + Future startRecording({bool fromFile = false}) async { + if (!await _isEncoderSupported(_encoder)) { + return; + } + var recordConfig = RecordConfig( + encoder: _encoder, + sampleRate: 16000, + numChannels: 1, + androidConfig: const AndroidRecordConfig( + muteAudio: true, + audioSource: AndroidAudioSource.mic, + ), + ); + final devs = await _recorder.listInputDevices(); + debugPrint(devs.toString()); + _lastAudioPath = await _getPath(); + if (fromFile) { + await _recorder.start(recordConfig, path: _lastAudioPath!); + } else { + final stream = await _recorder.startStream(recordConfig); + _recordSubscription = stream.listen(_audioChunks.add); + } + } + + Future startRecordingFile() async { + if (!await _isEncoderSupported(_encoder)) { + return; + } + var recordConfig = RecordConfig( + encoder: _encoder, + sampleRate: 16000, + numChannels: 1, + ); + final devs = await _recorder.listInputDevices(); + debugPrint(devs.toString()); + _lastAudioPath = await _getPath(); + await _recorder.start(recordConfig, path: _lastAudioPath!); + } + + Stream startRecordingStream() async* { + if (!await _isEncoderSupported(_encoder)) { + return; + } + var recordConfig = RecordConfig( + encoder: _encoder, + sampleRate: 16000, + numChannels: 1, + ); + final devices = await _recorder.listInputDevices(); + debugPrint(devices.toString()); + final stream = await _recorder.startStream(recordConfig); + + await for (final data in stream) { + yield data; + } + } + + Future stopRecording() async { + await _recordSubscription?.cancel(); + _recordSubscription = null; + + await _recorder.stop(); + } + + Future fetchAudioBytes({ + bool fromFile = false, + bool removeHeader = false, + }) async { + Uint8List resultBytes; + if (fromFile) { + resultBytes = await _getAudioBytesFromFile(_lastAudioPath!); + } else { + final builder = BytesBuilder(); + _audioChunks.forEach(builder.add); + resultBytes = builder.toBytes(); + } + + // resample + resultBytes = Resampler.resampleLinear16(44100, 16000, resultBytes); + final dir = await getDownloadsDirectory(); + final path = '${dir!.path}/audio_resampled.pcm'; + final file = File(path); + final sink = file.openWrite(); + + sink.add(resultBytes); + + await sink.close(); + return resultBytes; + } + + Future _removeWavHeader(Uint8List audio) async { + // Assuming a standard WAV header size of 44 bytes + const wavHeaderSize = 44; + final audioData = audio.sublist(wavHeaderSize); + return audioData; + } + + Future _getAudioBytesFromFile( + String filePath, { + bool removeHeader = false, + }) async { + final file = File(_lastAudioPath!); + + if (!file.existsSync()) { + throw Exception('Audio file not found: ${file.path}'); + } + + var pcmBytes = await file.readAsBytes(); + if (removeHeader) { + pcmBytes = await _removeWavHeader(pcmBytes); + } + return pcmBytes; + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/widgets/message_widget.dart b/packages/firebase_vertexai/firebase_ai/example/lib/widgets/message_widget.dart new file mode 100644 index 000000000000..b8a0f23ce03b --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/lib/widgets/message_widget.dart @@ -0,0 +1,68 @@ +// 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:flutter/material.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; + +class MessageData { + MessageData({this.image, this.text, this.fromUser}); + final Image? image; + final String? text; + final bool? fromUser; +} + +class MessageWidget extends StatelessWidget { + final Image? image; + final String? text; + final bool isFromUser; + + const MessageWidget({ + super.key, + this.image, + this.text, + required this.isFromUser, + }); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: + isFromUser ? MainAxisAlignment.end : MainAxisAlignment.start, + children: [ + Flexible( + child: Container( + constraints: const BoxConstraints(maxWidth: 600), + decoration: BoxDecoration( + color: isFromUser + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context).colorScheme.surfaceContainerHighest, + borderRadius: BorderRadius.circular(18), + ), + padding: const EdgeInsets.symmetric( + vertical: 15, + horizontal: 20, + ), + margin: const EdgeInsets.only(bottom: 8), + child: Column( + children: [ + if (text case final text?) MarkdownBody(data: text), + if (image case final image?) image, + ], + ), + ), + ), + ], + ); + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/.gitignore b/packages/firebase_vertexai/firebase_ai/example/macos/.gitignore new file mode 100644 index 000000000000..746adbb6b9e1 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/firebase_vertexai/firebase_ai/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000000..4b81f9b2d200 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Flutter/Flutter-Release.xcconfig b/packages/firebase_vertexai/firebase_ai/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000000..5caa9d1579e4 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Podfile b/packages/firebase_vertexai/firebase_ai/example/macos/Podfile new file mode 100644 index 000000000000..b52666a10389 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.15' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj b/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..4bc66a519ca5 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,805 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 1E1464098F5197FB1E35FDA1 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E05DB31CC6D204C7C78D127 /* Pods_RunnerTests.framework */; }; + 20C13FC2C906153EF4A40292 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 08B0491E23641E5BA5DD096C /* GoogleService-Info.plist */; }; + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 3D1CF19370CB8E26E5C667A5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4DAA18FE8B79A454BF3F8CB /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 08B0491E23641E5BA5DD096C /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; + 0E05DB31CC6D204C7C78D127 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 3A40C9AE19ACEC6C433878E9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 587C61AFC0E2B0BF5340F8E8 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 5C2B5E4F1CE100E1FA5D9DC5 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 766A2E414AFDFA56243527A6 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 816B0EE72BF94FC5261D04E6 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + A2911B8EF91B3925874FDE6A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + C4DAA18FE8B79A454BF3F8CB /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1E1464098F5197FB1E35FDA1 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3D1CF19370CB8E26E5C667A5 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 08B0491E23641E5BA5DD096C /* GoogleService-Info.plist */, + BE277C424FC00920BE07E371 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + BE277C424FC00920BE07E371 /* Pods */ = { + isa = PBXGroup; + children = ( + A2911B8EF91B3925874FDE6A /* Pods-Runner.debug.xcconfig */, + 816B0EE72BF94FC5261D04E6 /* Pods-Runner.release.xcconfig */, + 766A2E414AFDFA56243527A6 /* Pods-Runner.profile.xcconfig */, + 3A40C9AE19ACEC6C433878E9 /* Pods-RunnerTests.debug.xcconfig */, + 587C61AFC0E2B0BF5340F8E8 /* Pods-RunnerTests.release.xcconfig */, + 5C2B5E4F1CE100E1FA5D9DC5 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C4DAA18FE8B79A454BF3F8CB /* Pods_Runner.framework */, + 0E05DB31CC6D204C7C78D127 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 33B83C0D35C3606AED8215FE /* [CP] Check Pods Manifest.lock */, + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + E10F886575A4AF9F1D3D5C5B /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 1D3525FBE401B81EB0265948 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + 20C13FC2C906153EF4A40292 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 1D3525FBE401B81EB0265948 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33B83C0D35C3606AED8215FE /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + E10F886575A4AF9F1D3D5C5B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3A40C9AE19ACEC6C433878E9 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 587C61AFC0E2B0BF5340F8E8 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5C2B5E4F1CE100E1FA5D9DC5 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..b0a82f087ad1 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/AppDelegate.swift b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000000..b3c176141221 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..a2ec33f19f11 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..82b6f9d9a33e198f5747104729e1fcef999772a5 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..13b35eba55c6dabc3aac36f33d859266c18fa0d0 GIT binary patch literal 5680 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 literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f5fa40fb3d1e0710331a48de5d256da3f275d GIT binary patch literal 520 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 literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..2f1632cfddf3d9dade342351e627a0a75609fb46 GIT binary patch literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr + + + + + + + + + + + + + + + + + + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000000..92fb3cd54e84 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.example + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Debug.xcconfig b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000000..36b0fd9464f4 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Release.xcconfig b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000000..dff4f49561c8 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Warnings.xcconfig b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000000..42bcbf4780b1 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/DebugProfile.entitlements b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000000..b4bd9ee174a1 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.files.downloads.read-write + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.device.audio-input + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Info.plist b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Info.plist new file mode 100644 index 000000000000..a81b3fd0d617 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + NSMicrophoneUsageDescription + Permission to Record audio + + diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/MainFlutterWindow.swift b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000000..3cc05eb23491 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Release.entitlements b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Release.entitlements new file mode 100644 index 000000000000..2f9659c917fb --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Release.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.downloads.read-write + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/firebase_app_id_file.json b/packages/firebase_vertexai/firebase_ai/example/macos/firebase_app_id_file.json new file mode 100644 index 000000000000..f4a21e85e553 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/macos/firebase_app_id_file.json @@ -0,0 +1,7 @@ +{ + "file_generated_by": "FlutterFire CLI", + "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", + "GOOGLE_APP_ID": "1:651313571784:ios:722e4f9cad0b9e5db1c2fd", + "FIREBASE_PROJECT_ID": "vertex-ai-example-ef5a2", + "GCM_SENDER_ID": "651313571784" +} \ No newline at end of file diff --git a/packages/firebase_vertexai/firebase_ai/example/pubspec.yaml b/packages/firebase_vertexai/firebase_ai/example/pubspec.yaml new file mode 100644 index 000000000000..ed83fd2a6a08 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/pubspec.yaml @@ -0,0 +1,51 @@ +name: firebase_ai_example +description: "Example project to show how to use the Firebase AI SDK." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +version: 1.0.0+1 + +environment: + sdk: '>=3.2.0 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + + cupertino_icons: ^1.0.6 + firebase_ai: ^0.1.0 + firebase_core: ^3.13.0 + firebase_storage: ^12.4.5 + flutter: + sdk: flutter + flutter_markdown: ^0.6.20 + just_audio: ^0.9.43 + path_provider: ^2.1.5 + record: ^5.2.1 + +dev_dependencies: + flutter_lints: ^4.0.0 + flutter_test: + sdk: flutter + +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + assets: + - assets/images/ + - assets/documents/ + - assets/videos/ + +dependency_overrides: + firebase_ai: + path: ../../firebase_ai diff --git a/packages/firebase_vertexai/firebase_ai/example/web/favicon.png b/packages/firebase_vertexai/firebase_ai/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-192.png b/packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-512.png b/packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-maskable-192.png b/packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 zcmdT|`#%%j|KDb2V@0DPm$^(Lx5}lO%Yv(=e*7hl@QqKS50#~#^IQPxBmuh|i9sXnt4ch@VT0F7% zMtrs@KWIOo+QV@lSs66A>2pz6-`9Jk=0vv&u?)^F@HZ)-6HT=B7LF;rdj zskUyBfbojcX#CS>WrIWo9D=DIwcXM8=I5D{SGf$~=gh-$LwY?*)cD%38%sCc?5OsX z-XfkyL-1`VavZ?>(pI-xp-kYq=1hsnyP^TLb%0vKRSo^~r{x?ISLY1i7KjSp z*0h&jG(Rkkq2+G_6eS>n&6>&Xk+ngOMcYrk<8KrukQHzfx675^^s$~<@d$9X{VBbg z2Fd4Z%g`!-P}d#`?B4#S-9x*eNlOVRnDrn#jY@~$jfQ-~3Od;A;x-BI1BEDdvr`pI z#D)d)!2_`GiZOUu1crb!hqH=ezs0qk<_xDm_Kkw?r*?0C3|Io6>$!kyDl;eH=aqg$B zsH_|ZD?jP2dc=)|L>DZmGyYKa06~5?C2Lc0#D%62p(YS;%_DRCB1k(+eLGXVMe+=4 zkKiJ%!N6^mxqM=wq`0+yoE#VHF%R<{mMamR9o_1JH8jfnJ?NPLs$9U!9!dq8 z0B{dI2!M|sYGH&9TAY34OlpIsQ4i5bnbG>?cWwat1I13|r|_inLE?FS@Hxdxn_YZN z3jfUO*X9Q@?HZ>Q{W0z60!bbGh557XIKu1?)u|cf%go`pwo}CD=0tau-}t@R2OrSH zQzZr%JfYa`>2!g??76=GJ$%ECbQh7Q2wLRp9QoyiRHP7VE^>JHm>9EqR3<$Y=Z1K^SHuwxCy-5@z3 zVM{XNNm}yM*pRdLKp??+_2&!bp#`=(Lh1vR{~j%n;cJv~9lXeMv)@}Odta)RnK|6* zC+IVSWumLo%{6bLDpn)Gz>6r&;Qs0^+Sz_yx_KNz9Dlt^ax`4>;EWrIT#(lJ_40<= z750fHZ7hI{}%%5`;lwkI4<_FJw@!U^vW;igL0k+mK)-j zYuCK#mCDK3F|SC}tC2>m$ZCqNB7ac-0UFBJ|8RxmG@4a4qdjvMzzS&h9pQmu^x&*= zGvapd1#K%Da&)8f?<9WN`2H^qpd@{7In6DNM&916TRqtF4;3`R|Nhwbw=(4|^Io@T zIjoR?tB8d*sO>PX4vaIHF|W;WVl6L1JvSmStgnRQq zTX4(>1f^5QOAH{=18Q2Vc1JI{V=yOr7yZJf4Vpfo zeHXdhBe{PyY;)yF;=ycMW@Kb>t;yE>;f79~AlJ8k`xWucCxJfsXf2P72bAavWL1G#W z;o%kdH(mYCM{$~yw4({KatNGim49O2HY6O07$B`*K7}MvgI=4x=SKdKVb8C$eJseA$tmSFOztFd*3W`J`yIB_~}k%Sd_bPBK8LxH)?8#jM{^%J_0|L z!gFI|68)G}ex5`Xh{5pB%GtlJ{Z5em*e0sH+sU1UVl7<5%Bq+YrHWL7?X?3LBi1R@_)F-_OqI1Zv`L zb6^Lq#H^2@d_(Z4E6xA9Z4o3kvf78ZDz!5W1#Mp|E;rvJz&4qj2pXVxKB8Vg0}ek%4erou@QM&2t7Cn5GwYqy%{>jI z)4;3SAgqVi#b{kqX#$Mt6L8NhZYgonb7>+r#BHje)bvaZ2c0nAvrN3gez+dNXaV;A zmyR0z@9h4@6~rJik-=2M-T+d`t&@YWhsoP_XP-NsVO}wmo!nR~QVWU?nVlQjNfgcTzE-PkfIX5G z1?&MwaeuzhF=u)X%Vpg_e@>d2yZwxl6-r3OMqDn8_6m^4z3zG##cK0Fsgq8fcvmhu z{73jseR%X%$85H^jRAcrhd&k!i^xL9FrS7qw2$&gwAS8AfAk#g_E_tP;x66fS`Mn@SNVrcn_N;EQm z`Mt3Z%rw%hDqTH-s~6SrIL$hIPKL5^7ejkLTBr46;pHTQDdoErS(B>``t;+1+M zvU&Se9@T_BeK;A^p|n^krIR+6rH~BjvRIugf`&EuX9u69`9C?9ANVL8l(rY6#mu^i z=*5Q)-%o*tWl`#b8p*ZH0I}hn#gV%|jt6V_JanDGuekR*-wF`u;amTCpGG|1;4A5$ zYbHF{?G1vv5;8Ph5%kEW)t|am2_4ik!`7q{ymfHoe^Z99c|$;FAL+NbxE-_zheYbV z3hb0`uZGTsgA5TG(X|GVDSJyJxsyR7V5PS_WSnYgwc_D60m7u*x4b2D79r5UgtL18 zcCHWk+K6N1Pg2c;0#r-)XpwGX?|Iv)^CLWqwF=a}fXUSM?n6E;cCeW5ER^om#{)Jr zJR81pkK?VoFm@N-s%hd7@hBS0xuCD0-UDVLDDkl7Ck=BAj*^ps`393}AJ+Ruq@fl9 z%R(&?5Nc3lnEKGaYMLmRzKXow1+Gh|O-LG7XiNxkG^uyv zpAtLINwMK}IWK65hOw&O>~EJ}x@lDBtB`yKeV1%GtY4PzT%@~wa1VgZn7QRwc7C)_ zpEF~upeDRg_<#w=dLQ)E?AzXUQpbKXYxkp>;c@aOr6A|dHA?KaZkL0svwB^U#zmx0 zzW4^&G!w7YeRxt<9;d@8H=u(j{6+Uj5AuTluvZZD4b+#+6Rp?(yJ`BC9EW9!b&KdPvzJYe5l7 zMJ9aC@S;sA0{F0XyVY{}FzW0Vh)0mPf_BX82E+CD&)wf2!x@{RO~XBYu80TONl3e+ zA7W$ra6LcDW_j4s-`3tI^VhG*sa5lLc+V6ONf=hO@q4|p`CinYqk1Ko*MbZ6_M05k zSwSwkvu;`|I*_Vl=zPd|dVD0lh&Ha)CSJJvV{AEdF{^Kn_Yfsd!{Pc1GNgw}(^~%)jk5~0L~ms|Rez1fiK~s5t(p1ci5Gq$JC#^JrXf?8 z-Y-Zi_Hvi>oBzV8DSRG!7dm|%IlZg3^0{5~;>)8-+Nk&EhAd(}s^7%MuU}lphNW9Q zT)DPo(ob{tB7_?u;4-qGDo!sh&7gHaJfkh43QwL|bbFVi@+oy;i;M zM&CP^v~lx1U`pi9PmSr&Mc<%HAq0DGH?Ft95)WY`P?~7O z`O^Nr{Py9M#Ls4Y7OM?e%Y*Mvrme%=DwQaye^Qut_1pOMrg^!5u(f9p(D%MR%1K>% zRGw%=dYvw@)o}Fw@tOtPjz`45mfpn;OT&V(;z75J*<$52{sB65$gDjwX3Xa!x_wE- z!#RpwHM#WrO*|~f7z}(}o7US(+0FYLM}6de>gQdtPazXz?OcNv4R^oYLJ_BQOd_l172oSK$6!1r@g+B@0ofJ4*{>_AIxfe-#xp>(1 z@Y3Nfd>fmqvjL;?+DmZk*KsfXJf<%~(gcLwEez%>1c6XSboURUh&k=B)MS>6kw9bY z{7vdev7;A}5fy*ZE23DS{J?8at~xwVk`pEwP5^k?XMQ7u64;KmFJ#POzdG#np~F&H ze-BUh@g54)dsS%nkBb}+GuUEKU~pHcYIg4vSo$J(J|U36bs0Use+3A&IMcR%6@jv$ z=+QI+@wW@?iu}Hpyzlvj-EYeop{f65GX0O%>w#0t|V z1-svWk`hU~m`|O$kw5?Yn5UhI%9P-<45A(v0ld1n+%Ziq&TVpBcV9n}L9Tus-TI)f zd_(g+nYCDR@+wYNQm1GwxhUN4tGMLCzDzPqY$~`l<47{+l<{FZ$L6(>J)|}!bi<)| zE35dl{a2)&leQ@LlDxLQOfUDS`;+ZQ4ozrleQwaR-K|@9T{#hB5Z^t#8 zC-d_G;B4;F#8A2EBL58s$zF-=SCr`P#z zNCTnHF&|X@q>SkAoYu>&s9v@zCpv9lLSH-UZzfhJh`EZA{X#%nqw@@aW^vPcfQrlPs(qQxmC|4tp^&sHy!H!2FH5eC{M@g;ElWNzlb-+ zxpfc0m4<}L){4|RZ>KReag2j%Ot_UKkgpJN!7Y_y3;Ssz{9 z!K3isRtaFtQII5^6}cm9RZd5nTp9psk&u1C(BY`(_tolBwzV_@0F*m%3G%Y?2utyS zY`xM0iDRT)yTyYukFeGQ&W@ReM+ADG1xu@ruq&^GK35`+2r}b^V!m1(VgH|QhIPDE X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-maskable-512.png b/packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/packages/firebase_vertexai/firebase_ai/example/web/index.html b/packages/firebase_vertexai/firebase_ai/example/web/index.html new file mode 100644 index 000000000000..adc47a626031 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + flutterfire_vertexai + + + + + + diff --git a/packages/firebase_vertexai/firebase_ai/example/web/manifest.json b/packages/firebase_vertexai/firebase_ai/example/web/manifest.json new file mode 100644 index 000000000000..ffebd446235c --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "flutterfire_vertexai", + "short_name": "flutterfire_vertexai", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/packages/firebase_vertexai/firebase_ai/lib/firebase_ai.dart b/packages/firebase_vertexai/firebase_ai/lib/firebase_ai.dart new file mode 100644 index 000000000000..30add92c6062 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/lib/firebase_ai.dart @@ -0,0 +1,82 @@ +// 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. + +export 'src/api.dart' + show + BlockReason, + Candidate, + CitationMetadata, + Citation, + CountTokensResponse, + FinishReason, + GenerateContentResponse, + GenerationConfig, + HarmBlockThreshold, + HarmCategory, + HarmProbability, + HarmBlockMethod, + PromptFeedback, + SafetyRating, + SafetySetting, + // TODO(cynthiajiang) remove in next breaking change. + TaskType, + UsageMetadata; +export 'src/base_model.dart' + show GenerativeModel, ImagenModel, LiveGenerativeModel; +export 'src/chat.dart' show ChatSession, StartChatExtension; +export 'src/content.dart' + show + Content, + InlineDataPart, + FileData, + FunctionCall, + FunctionResponse, + Part, + TextPart; +export 'src/error.dart' + show + FirebaseAIException, + FirebaseAISdkException, + InvalidApiKey, + ServerException, + UnsupportedUserLocation; +export 'src/firebase_ai.dart' show FirebaseAI; +export 'src/function_calling.dart' + show + FunctionCallingConfig, + FunctionCallingMode, + FunctionDeclaration, + Tool, + ToolConfig; +export 'src/imagen_api.dart' + show + ImagenSafetySettings, + ImagenFormat, + ImagenSafetyFilterLevel, + ImagenPersonFilterLevel, + ImagenGenerationConfig, + ImagenAspectRatio; +export 'src/imagen_content.dart' show ImagenInlineImage; +export 'src/live_api.dart' + show + LiveGenerationConfig, + SpeechConfig, + ResponseModalities, + LiveServerMessage, + LiveServerContent, + LiveServerToolCall, + LiveServerToolCallCancellation, + LiveServerResponse; +export 'src/live_session.dart' show LiveSession; +export 'src/schema.dart' show Schema, SchemaType; diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/api.dart b/packages/firebase_vertexai/firebase_ai/lib/src/api.dart similarity index 99% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/api.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/api.dart index 4c8990d39844..f42e57c86a5e 100644 --- a/packages/firebase_vertexai/firebase_vertexai/lib/src/api.dart +++ b/packages/firebase_vertexai/firebase_ai/lib/src/api.dart @@ -56,7 +56,7 @@ final class GenerateContentResponse { /// /// If the prompt was blocked, or the first candidate was finished for a reason /// of [FinishReason.recitation] or [FinishReason.safety], accessing this text - /// will throw a [VertexAIException]. + /// will throw a [FirebaseAIException]. /// /// If the first candidate's content contains any text parts, this value is /// the concatenation of the text. @@ -71,7 +71,7 @@ final class GenerateContentResponse { :final blockReasonMessage, ) => // TODO: Add a specific subtype for this exception? - throw VertexAIException('Response was blocked' + throw FirebaseAIException('Response was blocked' '${blockReason != null ? ' due to $blockReason' : ''}' '${blockReasonMessage != null ? ': $blockReasonMessage' : ''}'), _ => null, @@ -84,7 +84,7 @@ final class GenerateContentResponse { ), ... ] => - throw VertexAIException( + throw FirebaseAIException( // ignore: prefer_interpolation_to_compose_strings 'Candidate was blocked due to $finishReason' + (finishMessage != null && finishMessage.isNotEmpty @@ -221,7 +221,7 @@ final class Candidate { } else { suffix = ''; } - throw VertexAIException( + throw FirebaseAIException( 'Candidate was blocked due to $finishReason$suffix'); } return switch (content.parts) { diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/base_model.dart b/packages/firebase_vertexai/firebase_ai/lib/src/base_model.dart similarity index 99% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/base_model.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/base_model.dart index fea23c3f93ad..0ad3390d1e38 100644 --- a/packages/firebase_vertexai/firebase_vertexai/lib/src/base_model.dart +++ b/packages/firebase_vertexai/firebase_ai/lib/src/base_model.dart @@ -11,7 +11,6 @@ // 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. -library vertexai_model; import 'dart:async'; import 'dart:convert'; diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/chat.dart b/packages/firebase_vertexai/firebase_ai/lib/src/chat.dart similarity index 100% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/chat.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/chat.dart diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/client.dart b/packages/firebase_vertexai/firebase_ai/lib/src/client.dart similarity index 99% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/client.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/client.dart index 8a5912c3e1ae..ba3eed67b6fe 100644 --- a/packages/firebase_vertexai/firebase_vertexai/lib/src/client.dart +++ b/packages/firebase_vertexai/firebase_ai/lib/src/client.dart @@ -69,7 +69,7 @@ final class HttpApiClient implements ApiClient { body: _utf8Json.encode(body), ); if (response.statusCode >= 500) { - throw VertexAIException( + throw FirebaseAIException( 'Server Error [${response.statusCode}]: ${response.body}'); } diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/content.dart b/packages/firebase_vertexai/firebase_ai/lib/src/content.dart similarity index 100% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/content.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/content.dart diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/developer/api.dart b/packages/firebase_vertexai/firebase_ai/lib/src/developer/api.dart similarity index 100% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/developer/api.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/developer/api.dart diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/error.dart b/packages/firebase_vertexai/firebase_ai/lib/src/error.dart similarity index 87% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/error.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/error.dart index 375b241c54bf..498d2e2a417e 100644 --- a/packages/firebase_vertexai/firebase_vertexai/lib/src/error.dart +++ b/packages/firebase_vertexai/firebase_ai/lib/src/error.dart @@ -15,9 +15,9 @@ /// Exception thrown when generating content fails. /// /// The [message] may explain the cause of the failure. -final class VertexAIException implements Exception { +final class FirebaseAIException implements Exception { // ignore: public_member_api_docs - VertexAIException(this.message); + FirebaseAIException(this.message); /// Message of the exception final String message; @@ -27,7 +27,7 @@ final class VertexAIException implements Exception { } /// Exception thrown when the server rejects the API key. -final class InvalidApiKey implements VertexAIException { +final class InvalidApiKey implements FirebaseAIException { // ignore: public_member_api_docs InvalidApiKey(this.message); @override @@ -38,14 +38,14 @@ final class InvalidApiKey implements VertexAIException { } /// Exception thrown when the user location is unsupported. -final class UnsupportedUserLocation implements VertexAIException { +final class UnsupportedUserLocation implements FirebaseAIException { static const _message = 'User location is not supported for the API use.'; @override String get message => _message; } /// Exception thrown when the service API is not enabled. -final class ServiceApiNotEnabled implements VertexAIException { +final class ServiceApiNotEnabled implements FirebaseAIException { // ignore: public_member_api_docs ServiceApiNotEnabled(this._projectId); @@ -69,7 +69,7 @@ final class ServiceApiNotEnabled implements VertexAIException { } /// Exception thrown when the quota is exceeded. -final class QuotaExceeded implements VertexAIException { +final class QuotaExceeded implements FirebaseAIException { // ignore: public_member_api_docs QuotaExceeded(this.message); @override @@ -80,7 +80,7 @@ final class QuotaExceeded implements VertexAIException { } /// Exception thrown when the server failed to generate content. -final class ServerException implements VertexAIException { +final class ServerException implements FirebaseAIException { // ignore: public_member_api_docs ServerException(this.message); @override @@ -95,9 +95,9 @@ final class ServerException implements VertexAIException { /// This exception indicates a likely problem with the SDK implementation such /// as an inability to parse a new response format. Resolution paths may include /// updating to a new version of the SDK, or filing an issue. -final class VertexAISdkException implements Exception { +final class FirebaseAISdkException implements Exception { // ignore: public_member_api_docs - VertexAISdkException(this.message); + FirebaseAISdkException(this.message); /// Message of the exception final String message; @@ -106,7 +106,7 @@ final class VertexAISdkException implements Exception { String toString() => '$message\n' 'This indicates a problem with the Vertex AI in Firebase SDK. ' 'Try updating to the latest version ' - '(https://pub.dev/packages/firebase_vertexai/versions), ' + '(https://pub.dev/packages/firebase_ai/versions), ' 'or file an issue at ' 'https://github.com/firebase/flutterfire/issues.'; } @@ -150,7 +150,7 @@ final class LiveWebSocketClosedException implements Exception { } /// Parse the error json object. -VertexAIException parseError(Object jsonObject) { +FirebaseAIException parseError(Object jsonObject) { return switch (jsonObject) { { 'message': final String message, @@ -180,6 +180,6 @@ VertexAIException parseError(Object jsonObject) { }; } -/// Throw [VertexAISdkException] for unhandled format. +/// Throw [FirebaseAISdkException] for unhandled format. Exception unhandledFormat(String name, Object? jsonObject) => - VertexAISdkException('Unhandled format for $name: $jsonObject'); + FirebaseAISdkException('Unhandled format for $name: $jsonObject'); diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/firebase_ai.dart b/packages/firebase_vertexai/firebase_ai/lib/src/firebase_ai.dart new file mode 100644 index 000000000000..9adceb7886e1 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/lib/src/firebase_ai.dart @@ -0,0 +1,187 @@ +// 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. + +import 'package:firebase_app_check/firebase_app_check.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' + show FirebasePluginPlatform; +import 'package:meta/meta.dart'; + +import '../firebase_ai.dart'; +import 'base_model.dart'; + +const _defaultLocation = 'us-central1'; + +/// The entrypoint for generative models. +class FirebaseAI extends FirebasePluginPlatform { + FirebaseAI._( + {required this.app, + required this.location, + required bool useVertexBackend, + this.appCheck, + this.auth}) + : _useVertexBackend = useVertexBackend, + super(app.name, 'plugins.flutter.io/firebase_vertexai'); + + /// The [FirebaseApp] for this current [FirebaseAI] instance. + FirebaseApp app; + + /// The optional [FirebaseAppCheck] for this current [FirebaseAI] instance. + /// https://firebase.google.com/docs/app-check + FirebaseAppCheck? appCheck; + + /// The optional [FirebaseAuth] for this current [FirebaseAI] instance. + FirebaseAuth? auth; + + /// The service location for this [FirebaseAI] instance. + String location; + + final bool _useVertexBackend; + + static final Map _cachedInstances = {}; + + /// Returns an instance using a specified [FirebaseApp]. + /// + /// If [app] is not provided, the default Firebase app will be used. + /// If pass in [appCheck], request session will get protected from abusing. + static FirebaseAI vertexAI({ + FirebaseApp? app, + FirebaseAppCheck? appCheck, + FirebaseAuth? auth, + String? location, + }) { + app ??= Firebase.app(); + + if (_cachedInstances.containsKey(app.name)) { + return _cachedInstances[app.name]!; + } + + location ??= _defaultLocation; + + FirebaseAI newInstance = FirebaseAI._( + app: app, + location: location, + appCheck: appCheck, + auth: auth, + useVertexBackend: true, + ); + _cachedInstances[app.name] = newInstance; + + return newInstance; + } + + /// Returns an instance using a specified [FirebaseApp]. + /// + /// If [app] is not provided, the default Firebase app will be used. + /// If pass in [appCheck], request session will get protected from abusing. + static FirebaseAI googleAI({ + FirebaseApp? app, + FirebaseAppCheck? appCheck, + FirebaseAuth? auth, + }) { + app ??= Firebase.app(); + + if (_cachedInstances.containsKey(app.name)) { + return _cachedInstances[app.name]!; + } + + FirebaseAI newInstance = FirebaseAI._( + app: app, + location: _defaultLocation, + appCheck: appCheck, + auth: auth, + useVertexBackend: false, + ); + _cachedInstances[app.name] = newInstance; + + return newInstance; + } + + /// Create a [GenerativeModel] backed by the generative model named [model]. + /// + /// The [model] argument can be a model name (such as `'gemini-pro'`) or a + /// model code (such as `'models/gemini-pro'`). + /// There is no creation time check for whether the `model` string identifies + /// a known and supported model. If not, attempts to generate content + /// will fail. + /// + /// The optional [safetySettings] and [generationConfig] can be used to + /// control and guide the generation. See [SafetySetting] and + /// [GenerationConfig] for details. + GenerativeModel generativeModel({ + required String model, + List? safetySettings, + GenerationConfig? generationConfig, + List? tools, + ToolConfig? toolConfig, + Content? systemInstruction, + }) { + return createGenerativeModel( + model: model, + app: app, + appCheck: appCheck, + useVertexBackend: _useVertexBackend, + auth: auth, + location: location, + safetySettings: safetySettings, + generationConfig: generationConfig, + tools: tools, + toolConfig: toolConfig, + systemInstruction: systemInstruction, + ); + } + + /// Create a [ImagenModel]. + /// + /// The optional [safetySettings] can be used to control and guide the + /// generation. See [ImagenSafetySettings] for details. + @experimental + ImagenModel imagenModel( + {required String model, + ImagenGenerationConfig? generationConfig, + ImagenSafetySettings? safetySettings}) { + return createImagenModel( + app: app, + location: location, + model: model, + useVertexBackend: _useVertexBackend, + generationConfig: generationConfig, + safetySettings: safetySettings, + appCheck: appCheck, + auth: auth); + } + + /// Create a [LiveGenerativeModel] for real-time interaction. + /// + /// The optional [liveGenerationConfig] can be used to control and guide the + /// generation. See [LiveGenerationConfig] for details. + LiveGenerativeModel liveGenerativeModel({ + required String model, + LiveGenerationConfig? liveGenerationConfig, + List? tools, + Content? systemInstruction, + }) { + return createLiveGenerativeModel( + app: app, + location: location, + model: model, + liveGenerationConfig: liveGenerationConfig, + tools: tools, + systemInstruction: systemInstruction, + appCheck: appCheck, + auth: auth, + ); + } +} diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/function_calling.dart b/packages/firebase_vertexai/firebase_ai/lib/src/function_calling.dart similarity index 100% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/function_calling.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/function_calling.dart diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/generative_model.dart b/packages/firebase_vertexai/firebase_ai/lib/src/generative_model.dart similarity index 99% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/generative_model.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/generative_model.dart index 8533e3519287..2f61bd3e4c2d 100644 --- a/packages/firebase_vertexai/firebase_vertexai/lib/src/generative_model.dart +++ b/packages/firebase_vertexai/firebase_ai/lib/src/generative_model.dart @@ -13,7 +13,7 @@ // limitations under the License. // ignore_for_file: use_late_for_private_fields_and_variables -part of vertexai_model; +part of 'base_model.dart'; /// A multimodel generative model (like Gemini). /// diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/imagen_api.dart b/packages/firebase_vertexai/firebase_ai/lib/src/imagen_api.dart similarity index 100% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/imagen_api.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/imagen_api.dart diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/imagen_content.dart b/packages/firebase_vertexai/firebase_ai/lib/src/imagen_content.dart similarity index 100% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/imagen_content.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/imagen_content.dart diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/imagen_model.dart b/packages/firebase_vertexai/firebase_ai/lib/src/imagen_model.dart similarity index 99% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/imagen_model.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/imagen_model.dart index 9027f09f3208..bf4731a3b264 100644 --- a/packages/firebase_vertexai/firebase_vertexai/lib/src/imagen_model.dart +++ b/packages/firebase_vertexai/firebase_ai/lib/src/imagen_model.dart @@ -11,7 +11,8 @@ // 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. -part of vertexai_model; + +part of 'base_model.dart'; /// Represents a remote Imagen model with the ability to generate images using /// text prompts. diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/live_api.dart b/packages/firebase_vertexai/firebase_ai/lib/src/live_api.dart similarity index 97% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/live_api.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/live_api.dart index 0454f7fe91b9..d0480f2f7e22 100644 --- a/packages/firebase_vertexai/firebase_vertexai/lib/src/live_api.dart +++ b/packages/firebase_vertexai/firebase_ai/lib/src/live_api.dart @@ -265,14 +265,14 @@ class LiveClientToolResponse { /// Parses a JSON object received from the live server into a [LiveServerResponse]. /// /// This function handles different types of server messages, including: -/// - Error messages, which result in a [VertexAIException] being thrown. +/// - Error messages, which result in a [FirebaseAIException] being thrown. /// - `serverContent` messages containing model-generated content. /// - `toolCall` messages indicating function calls requested by the model. /// - `toolCallCancellation` messages to cancel pending function calls. /// - `setupComplete` messages signaling the completion of the server setup. /// /// If the JSON object does not match any of the expected formats, an -/// [VertexAISdkException] is thrown. +/// [FirebaseAISdkException] is thrown. /// /// Example: /// ```dart @@ -293,8 +293,8 @@ class LiveClientToolResponse { /// ``` /// /// Throws: -/// - [VertexAIException]: If the JSON object contains an error message. -/// - [VertexAISdkException]: If the JSON object does not match any expected format. +/// - [FirebaseAIException]: If the JSON object contains an error message. +/// - [FirebaseAISdkException]: If the JSON object does not match any expected format. /// /// Parameters: /// - [jsonObject]: The JSON object received from the live server. diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/live_model.dart b/packages/firebase_vertexai/firebase_ai/lib/src/live_model.dart similarity index 99% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/live_model.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/live_model.dart index 06e443341e98..aecc6269ab15 100644 --- a/packages/firebase_vertexai/firebase_vertexai/lib/src/live_model.dart +++ b/packages/firebase_vertexai/firebase_ai/lib/src/live_model.dart @@ -11,7 +11,8 @@ // 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. -part of vertexai_model; + +part of 'base_model.dart'; const _apiUrl = 'ws/google.firebase.vertexai'; const _apiUrlSuffix = 'LlmBidiService/BidiGenerateContent/locations'; diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/live_session.dart b/packages/firebase_vertexai/firebase_ai/lib/src/live_session.dart similarity index 98% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/live_session.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/live_session.dart index a7b5a3108214..34835ff11247 100644 --- a/packages/firebase_vertexai/firebase_vertexai/lib/src/live_session.dart +++ b/packages/firebase_vertexai/firebase_ai/lib/src/live_session.dart @@ -92,7 +92,7 @@ class LiveSession { await _sendMediaChunk(chunk); } } catch (e) { - throw VertexAISdkException(e.toString()); + throw FirebaseAISdkException(e.toString()); } finally { log('Stream processing completed.'); } diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/schema.dart b/packages/firebase_vertexai/firebase_ai/lib/src/schema.dart similarity index 100% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/schema.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/schema.dart diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/utils/mutex.dart b/packages/firebase_vertexai/firebase_ai/lib/src/utils/mutex.dart similarity index 100% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/utils/mutex.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/utils/mutex.dart diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/vertex_version.dart b/packages/firebase_vertexai/firebase_ai/lib/src/vertex_version.dart similarity index 100% rename from packages/firebase_vertexai/firebase_vertexai/lib/src/vertex_version.dart rename to packages/firebase_vertexai/firebase_ai/lib/src/vertex_version.dart diff --git a/packages/firebase_vertexai/firebase_ai/pubspec.yaml b/packages/firebase_vertexai/firebase_ai/pubspec.yaml new file mode 100644 index 000000000000..dd0538f2932d --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/pubspec.yaml @@ -0,0 +1,39 @@ +name: firebase_ai +description: Firebase AI SDK. +version: 0.1.0 +homepage: https://firebase.google.com/docs/vertex-ai/get-started?platform=flutter +topics: + - firebase + - vertexai + - gemini + - generative-ai + +# Explicit about the supported platforms. +platforms: + android: + ios: + macos: + web: + +environment: + sdk: '>=3.2.0 <4.0.0' + flutter: ">=3.16.0" + +dependencies: + firebase_app_check: ^0.3.2+5 + firebase_auth: ^5.5.2 + firebase_core: ^3.13.0 + firebase_core_platform_interface: ^5.3.1 + flutter: + sdk: flutter + http: ^1.1.0 + meta: ^1.15.0 + web_socket_channel: ^3.0.1 + +dev_dependencies: + flutter_lints: ^4.0.0 + flutter_test: + sdk: flutter + matcher: ^0.12.16 + mockito: ^5.0.0 + plugin_platform_interface: ^2.1.3 diff --git a/packages/firebase_vertexai/firebase_ai/test/base_model_test.dart b/packages/firebase_vertexai/firebase_ai/test/base_model_test.dart new file mode 100644 index 000000000000..456111126c85 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/base_model_test.dart @@ -0,0 +1,141 @@ +// 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/src/base_model.dart'; +import 'package:firebase_ai/src/client.dart'; +import 'package:firebase_app_check/firebase_app_check.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +// Mock FirebaseApp +// ignore: avoid_implementing_value_types +class MockFirebaseApp extends Mock implements FirebaseApp { + @override + FirebaseOptions get options => MockFirebaseOptions(); + + @override + bool get isAutomaticDataCollectionEnabled => true; +} + +// Mock FirebaseOptions +// ignore: must_be_immutable, avoid_implementing_value_types +class MockFirebaseOptions extends Mock implements FirebaseOptions { + @override + String get projectId => 'test-project'; + + @override + String get appId => 'test-app-id'; +} + +// Mock Firebase App Check +class MockFirebaseAppCheck extends Mock implements FirebaseAppCheck { + @override + Future getToken([bool? forceRefresh = false]) async => + super.noSuchMethod(Invocation.method(#getToken, [forceRefresh])); +} + +// Mock Firebase Auth +class MockFirebaseAuth extends Mock implements FirebaseAuth { + @override + User? get currentUser => super.noSuchMethod(Invocation.getter(#currentUser)); +} + +// Mock Firebase User +class MockUser extends Mock implements User { + @override + Future getIdToken([bool? forceRefresh = false]) async => + super.noSuchMethod(Invocation.method(#getIdToken, [forceRefresh])); +} + +class MockApiClient extends Mock implements ApiClient { + @override + Future> makeRequest( + Uri uri, Map params) async { + // Simulate a successful API response + return {'mockResponse': 'success'}; + } +} + +void main() { + group('BaseModel', () { + test('firebaseTokens returns a function that generates headers', () async { + final tokenFunction = BaseModel.firebaseTokens(null, null, null); + final headers = await tokenFunction(); + expect(headers['x-goog-api-client'], contains('gl-dart')); + expect(headers['x-goog-api-client'], contains('fire')); + expect(headers.length, 1); + }); + + test('firebaseTokens includes App Check token if available', () async { + final mockAppCheck = MockFirebaseAppCheck(); + when(mockAppCheck.getToken()) + .thenAnswer((_) async => 'test-app-check-token'); + final tokenFunction = BaseModel.firebaseTokens(mockAppCheck, null, null); + final headers = await tokenFunction(); + expect(headers['X-Firebase-AppCheck'], 'test-app-check-token'); + expect(headers['x-goog-api-client'], contains('gl-dart')); + expect(headers['x-goog-api-client'], contains('fire')); + expect(headers.length, 2); + }); + + test('firebaseTokens includes Auth ID token if available', () async { + final mockAuth = MockFirebaseAuth(); + final mockUser = MockUser(); + when(mockUser.getIdToken()).thenAnswer((_) async => 'test-id-token'); + when(mockAuth.currentUser).thenReturn(mockUser); + final tokenFunction = BaseModel.firebaseTokens(null, mockAuth, null); + final headers = await tokenFunction(); + expect(headers['Authorization'], 'Firebase test-id-token'); + expect(headers['x-goog-api-client'], contains('gl-dart')); + expect(headers['x-goog-api-client'], contains('fire')); + expect(headers.length, 2); + }); + + test( + 'firebaseTokens includes App ID if automatic data collection is enabled', + () async { + final mockApp = MockFirebaseApp(); + + final tokenFunction = BaseModel.firebaseTokens(null, null, mockApp); + final headers = await tokenFunction(); + expect(headers['X-Firebase-AppId'], 'test-app-id'); + expect(headers['x-goog-api-client'], contains('gl-dart')); + expect(headers['x-goog-api-client'], contains('fire')); + expect(headers.length, 2); + }); + + test('firebaseTokens includes all tokens if available', () async { + final mockAppCheck = MockFirebaseAppCheck(); + when(mockAppCheck.getToken()) + .thenAnswer((_) async => 'test-app-check-token'); + final mockAuth = MockFirebaseAuth(); + final mockUser = MockUser(); + when(mockUser.getIdToken()).thenAnswer((_) async => 'test-id-token'); + when(mockAuth.currentUser).thenReturn(mockUser); + final mockApp = MockFirebaseApp(); + + final tokenFunction = + BaseModel.firebaseTokens(mockAppCheck, mockAuth, mockApp); + final headers = await tokenFunction(); + expect(headers['X-Firebase-AppCheck'], 'test-app-check-token'); + expect(headers['Authorization'], 'Firebase test-id-token'); + expect(headers['X-Firebase-AppId'], 'test-app-id'); + expect(headers['x-goog-api-client'], contains('gl-dart')); + expect(headers['x-goog-api-client'], contains('fire')); + expect(headers.length, 4); + }); + }); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/chat_test.dart b/packages/firebase_vertexai/firebase_ai/test/chat_test.dart new file mode 100644 index 000000000000..ab5819f0f12a --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/chat_test.dart @@ -0,0 +1,132 @@ +// 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. + +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:firebase_ai/src/base_model.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'mock.dart'; +import 'utils/matchers.dart'; +import 'utils/stub_client.dart'; + +void main() { + setupFirebaseVertexAIMocks(); + // ignore: unused_local_variable + late FirebaseApp app; + + group('Chat', () { + const defaultModelName = 'some-model'; + setUpAll(() async { + // Initialize Firebase + app = await Firebase.initializeApp(); + }); + + (ClientController, GenerativeModel) createModel([ + String modelName = defaultModelName, + ]) { + final client = ClientController(); + final model = createModelWithClient( + app: app, + useVertexBackend: true, + model: modelName, + client: client.client, + location: 'us-central1'); + return (client, model); + } + + test('includes chat history in prompt', () async { + final (client, model) = createModel('models/$defaultModelName'); + final chat = model.startChat(history: [ + Content.text('Hi!'), + Content.model([TextPart('Hello, how can I help you today?')]), + ]); + const prompt = 'Some prompt'; + final response = await client.checkRequest( + () => chat.sendMessage(Content.text(prompt)), + verifyRequest: (_, request) { + final contents = request['contents']; + expect(contents, hasLength(3)); + }, + response: arbitraryGenerateContentResponse, + ); + expect( + chat.history.last, + matchesContent(response.candidates.first.content), + ); + }); + + test('forwards safety settings', () async { + final (client, model) = createModel('models/$defaultModelName'); + final chat = model.startChat(safetySettings: [ + SafetySetting(HarmCategory.dangerousContent, HarmBlockThreshold.high, + HarmBlockMethod.severity), + ]); + const prompt = 'Some prompt'; + await client.checkRequest( + () => chat.sendMessage(Content.text(prompt)), + verifyRequest: (_, request) { + expect(request['safetySettings'], [ + { + 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', + 'threshold': 'BLOCK_ONLY_HIGH', + 'method': 'SEVERITY' + }, + ]); + }, + response: arbitraryGenerateContentResponse, + ); + }); + + test('forwards safety settings and config when streaming', () async { + final (client, model) = createModel('models/$defaultModelName'); + final chat = model.startChat(safetySettings: [ + SafetySetting(HarmCategory.dangerousContent, HarmBlockThreshold.high, + HarmBlockMethod.probability), + ], generationConfig: GenerationConfig(stopSequences: ['a'])); + const prompt = 'Some prompt'; + final responses = await client.checkStreamRequest( + () async => chat.sendMessageStream(Content.text(prompt)), + verifyRequest: (_, request) { + expect(request['safetySettings'], [ + { + 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', + 'threshold': 'BLOCK_ONLY_HIGH', + 'method': 'PROBABILITY', + }, + ]); + }, + responses: [arbitraryGenerateContentResponse], + ); + await responses.drain(); + }); + + test('forwards generation config', () async { + final (client, model) = createModel('models/$defaultModelName'); + final chat = model.startChat( + generationConfig: GenerationConfig(stopSequences: ['a']), + ); + const prompt = 'Some prompt'; + await client.checkRequest( + () => chat.sendMessage(Content.text(prompt)), + verifyRequest: (_, request) { + expect(request['generationConfig'], { + 'stopSequences': ['a'], + }); + }, + response: arbitraryGenerateContentResponse, + ); + }); + }); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/content_test.dart b/packages/firebase_vertexai/firebase_ai/test/content_test.dart new file mode 100644 index 000000000000..fc2957d89d73 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/content_test.dart @@ -0,0 +1,215 @@ +// 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. + +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:firebase_ai/src/content.dart'; +import 'package:firebase_ai/src/error.dart'; +import 'package:flutter_test/flutter_test.dart'; + +// Mock google_ai classes (if needed) +// ... + +void main() { + group('Content tests', () { + test('constructor', () { + final content = Content('user', + [TextPart('Test'), InlineDataPart('image/png', Uint8List(0))]); + expect(content.role, 'user'); + expect(content.parts[0], isA()); + expect((content.parts[0] as TextPart).text, 'Test'); + expect(content.parts[1], isA()); + expect((content.parts[1] as InlineDataPart).mimeType, 'image/png'); + expect((content.parts[1] as InlineDataPart).bytes.length, 0); + }); + + test('text()', () { + final content = Content('user', [TextPart('Test')]); + expect(content.role, 'user'); + expect(content.parts[0], isA()); + }); + + test('data()', () { + final content = + Content('user', [InlineDataPart('image/png', Uint8List(0))]); + expect(content.parts[0], isA()); + }); + + test('multi()', () { + final content = Content('user', + [TextPart('Test'), InlineDataPart('image/png', Uint8List(0))]); + expect(content.parts.length, 2); + expect(content.parts[0], isA()); + expect(content.parts[1], isA()); + }); + + test('toJson', () { + final content = Content('user', + [TextPart('Test'), InlineDataPart('image/png', Uint8List(0))]); + final json = content.toJson(); + expect(json['role'], 'user'); + expect((json['parts']! as List).length, 2); + expect((json['parts']! as List)[0]['text'], 'Test'); + expect( + (json['parts']! as List)[1]['inlineData']['mimeType'], 'image/png'); + expect((json['parts']! as List)[1]['inlineData']['data'].length, 0); + }); + + test('parseContent', () { + final json = { + 'role': 'user', + 'parts': [ + {'text': 'Hello'}, + ] + }; + final content = parseContent(json); + expect(content.role, 'user'); + expect(content.parts.length, 1); + expect(content.parts[0], isA()); + expect(reason: 'TextPart', (content.parts[0] as TextPart).text, 'Hello'); + }); + }); + + group('Part tests', () { + test('TextPart toJson', () { + final part = TextPart('Test'); + final json = part.toJson(); + expect((json as Map)['text'], 'Test'); + }); + + test('DataPart toJson', () { + final part = InlineDataPart('image/png', Uint8List(0)); + final json = part.toJson(); + expect((json as Map)['inlineData']['mimeType'], 'image/png'); + expect(json['inlineData']['data'], ''); + }); + + test('FunctionCall toJson', () { + final part = FunctionCall( + 'myFunction', + { + 'arguments': [ + {'text': 'Test'} + ], + }, + id: 'myFunctionId'); + final json = part.toJson(); + expect((json as Map)['functionCall']['name'], 'myFunction'); + expect(json['functionCall']['args'].length, 1); + expect(json['functionCall']['args']['arguments'].length, 1); + expect(json['functionCall']['args']['arguments'][0]['text'], 'Test'); + expect(json['functionCall']['id'], 'myFunctionId'); + }); + + test('FunctionResponse toJson', () { + final part = FunctionResponse( + 'myFunction', + { + 'inlineData': { + 'mimeType': 'application/octet-stream', + 'data': Uint8List(0) + } + }, + id: 'myFunctionId'); + final json = part.toJson(); + expect((json as Map)['functionResponse']['name'], 'myFunction'); + expect(json['functionResponse']['response']['inlineData']['mimeType'], + 'application/octet-stream'); + expect(json['functionResponse']['response']['inlineData']['data'], + Uint8List(0)); + expect(json['functionResponse']['id'], 'myFunctionId'); + }); + + test('FileData toJson', () { + final part = FileData('image/png', 'gs://bucket-name/path'); + final json = part.toJson(); + expect((json as Map)['file_data']['mime_type'], 'image/png'); + expect(json['file_data']['file_uri'], 'gs://bucket-name/path'); + }); + }); + + group('parsePart', () { + test('parses TextPart correctly', () { + final json = {'text': 'Hello, world!'}; + final result = parsePart(json); + expect(result, isA()); + expect((result as TextPart).text, 'Hello, world!'); + }); + + test('parses FunctionCall correctly', () { + final json = { + 'functionCall': { + 'name': 'myFunction', + 'args': {'arg1': 1, 'arg2': 'value'}, + 'id': '123', + } + }; + final result = parsePart(json); + expect(result, isA()); + final functionCall = result as FunctionCall; + expect(functionCall.name, 'myFunction'); + expect(functionCall.args, {'arg1': 1, 'arg2': 'value'}); + expect(functionCall.id, '123'); + }); + + test('parses FileData correctly', () { + final json = { + 'file_data': { + 'file_uri': 'file:///path/to/file.txt', + 'mime_type': 'text/plain', + } + }; + final result = parsePart(json); + expect(result, isA()); + final fileData = result as FileData; + expect(fileData.fileUri, 'file:///path/to/file.txt'); + expect(fileData.mimeType, 'text/plain'); + }); + + test('parses InlineDataPart correctly', () { + final json = { + 'inlineData': { + 'mimeType': 'image/png', + 'data': base64Encode([1, 2, 3]) + } + }; + final result = parsePart(json); + expect(result, isA()); + final inlineData = result as InlineDataPart; + expect(inlineData.mimeType, 'image/png'); + expect(inlineData.bytes, [1, 2, 3]); + }); + + test('throws UnimplementedError for functionResponse', () { + final json = { + 'functionResponse': {'name': 'test', 'response': {}} + }; + expect(() => parsePart(json), throwsA(isA())); + }); + + test('throws unhandledFormat for invalid JSON', () { + final json = {'invalid': 'data'}; + expect(() => parsePart(json), throwsA(isA())); + }); + + test('throws unhandledFormat for null input', () { + expect(() => parsePart(null), throwsA(isA())); + }); + + test('throws unhandledFormat for empty map', () { + expect(() => parsePart({}), throwsA(isA())); + }); + }); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/error_test.dart b/packages/firebase_vertexai/firebase_ai/test/error_test.dart new file mode 100644 index 000000000000..5fb5a1370de0 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/error_test.dart @@ -0,0 +1,165 @@ +// 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/src/error.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('VertexAI Exceptions', () { + test('VertexAIException toString', () { + final exception = FirebaseAIException('Test message'); + expect(exception.toString(), 'VertexAIException: Test message'); + }); + + test('InvalidApiKey toString', () { + final exception = InvalidApiKey('Invalid API key provided.'); + expect(exception.toString(), 'Invalid API key provided.'); + }); + + test('UnsupportedUserLocation message', () { + final exception = UnsupportedUserLocation(); + expect( + exception.message, 'User location is not supported for the API use.'); + }); + + test('ServiceApiNotEnabled message', () { + final exception = ServiceApiNotEnabled('projects/test-project'); + expect( + exception.message, + 'The Vertex AI in Firebase SDK requires the Vertex AI in Firebase API ' + '(`firebasevertexai.googleapis.com`) to be enabled in your Firebase project. Enable this API ' + 'by visiting the Firebase Console at ' + 'https://console.firebase.google.com/project/test-project/genai ' + 'and clicking "Get started". If you enabled this API recently, wait a few minutes for the ' + 'action to propagate to our systems and then retry.'); + }); + + test('QuotaExceeded toString', () { + final exception = QuotaExceeded('Quota for this API has been exceeded.'); + expect(exception.toString(), 'Quota for this API has been exceeded.'); + }); + + test('ServerException toString', () { + final exception = ServerException('Server error occurred.'); + expect(exception.toString(), 'Server error occurred.'); + }); + + test('VertexAISdkException toString', () { + final exception = FirebaseAISdkException('SDK failed to parse response.'); + expect( + exception.toString(), + 'SDK failed to parse response.\n' + 'This indicates a problem with the Vertex AI in Firebase SDK. ' + 'Try updating to the latest version ' + '(https://pub.dev/packages/firebase_ai/versions), ' + 'or file an issue at ' + 'https://github.com/firebase/flutterfire/issues.'); + }); + + test('ImagenImagesBlockedException toString', () { + final exception = + ImagenImagesBlockedException('All images were blocked.'); + expect(exception.toString(), 'All images were blocked.'); + }); + + test('LiveWebSocketClosedException toString - DEADLINE_EXCEEDED', () { + final exception = LiveWebSocketClosedException( + 'DEADLINE_EXCEEDED: Connection timed out.'); + expect(exception.toString(), + 'The current live session has expired. Please start a new session.'); + }); + + test('LiveWebSocketClosedException toString - RESOURCE_EXHAUSTED', () { + final exception = LiveWebSocketClosedException( + 'RESOURCE_EXHAUSTED: Too many connections.'); + expect( + exception.toString(), + 'You have exceeded the maximum number of concurrent sessions. ' + 'Please close other sessions and try again later.'); + }); + + test('LiveWebSocketClosedException toString - Other', () { + final exception = + LiveWebSocketClosedException('WebSocket connection closed.'); + expect(exception.toString(), 'WebSocket connection closed.'); + }); + + group('parseError', () { + test('parses API_KEY_INVALID', () { + final json = { + 'message': 'Invalid API key', + 'details': [ + {'reason': 'API_KEY_INVALID'} + ] + }; + final exception = parseError(json); + expect(exception, isInstanceOf()); + expect(exception.message, 'Invalid API key'); + }); + + test('parses UNSUPPORTED_USER_LOCATION', () { + final json = { + 'message': 'User location is not supported for the API use.' + }; + final exception = parseError(json); + expect(exception, isInstanceOf()); + }); + + test('parses QUOTA_EXCEEDED', () { + final json = {'message': 'Quota exceeded: Limit reached.'}; + final exception = parseError(json); + expect(exception, isInstanceOf()); + expect(exception.message, 'Quota exceeded: Limit reached.'); + }); + + test('parses SERVICE_API_NOT_ENABLED', () { + final json = { + 'message': 'API not enabled', + 'status': 'PERMISSION_DENIED', + 'details': [ + { + 'metadata': { + 'service': 'firebasevertexai.googleapis.com', + 'consumer': 'projects/my-project-id', + } + } + ] + }; + final exception = parseError(json); + expect(exception, isInstanceOf()); + expect( + (exception as ServiceApiNotEnabled).message, + 'The Vertex AI in Firebase SDK requires the Vertex AI in Firebase API ' + '(`firebasevertexai.googleapis.com`) to be enabled in your Firebase project. Enable this API ' + 'by visiting the Firebase Console at ' + 'https://console.firebase.google.com/project/my-project-id/genai ' + 'and clicking "Get started". If you enabled this API recently, wait a few minutes for the ' + 'action to propagate to our systems and then retry.'); + }); + + test('parses SERVER_ERROR', () { + final json = {'message': 'Internal server error.'}; + final exception = parseError(json); + expect(exception, isInstanceOf()); + expect(exception.message, 'Internal server error.'); + }); + + test('parses UNHANDLED_FORMAT', () { + final json = {'unexpected': 'format'}; + expect(() => parseError(json), + throwsA(isInstanceOf())); + }); + }); + }); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/firebase_vertexai_test.dart b/packages/firebase_vertexai/firebase_ai/test/firebase_vertexai_test.dart new file mode 100644 index 000000000000..8edb2d0f8480 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/firebase_vertexai_test.dart @@ -0,0 +1,81 @@ +// 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. + +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:firebase_app_check/firebase_app_check.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'mock.dart'; + +void main() { + setupFirebaseVertexAIMocks(); + // ignore: unused_local_variable + late FirebaseApp app; + // ignore: unused_local_variable + late FirebaseAppCheck appCheck; + late FirebaseApp customApp; + late FirebaseAppCheck customAppCheck; + + group('FirebaseAI Tests', () { + late FirebaseApp app; + + setUpAll(() async { + // Initialize Firebase + app = await Firebase.initializeApp(); + customApp = await Firebase.initializeApp( + name: 'custom-app', + options: Firebase.app().options, + ); + appCheck = FirebaseAppCheck.instance; + customAppCheck = FirebaseAppCheck.instanceFor(app: customApp); + }); + + test('Singleton behavior', () { + final instance1 = FirebaseAI.vertexAI(); + final instance2 = FirebaseAI.vertexAI(app: app); + expect(identical(instance1, instance2), isTrue); + }); + + test('Instance creation with defaults', () { + final vertexAI = FirebaseAI.vertexAI(app: app); + expect(vertexAI.app, equals(app)); + expect(vertexAI.location, equals('us-central1')); + }); + + test('Instance creation with custom', () { + final vertexAI = FirebaseAI.vertexAI( + app: customApp, + appCheck: customAppCheck, + location: 'custom-location'); + expect(vertexAI.app, equals(customApp)); + expect(vertexAI.appCheck, equals(customAppCheck)); + expect(vertexAI.location, equals('custom-location')); + }); + + test('generativeModel creation', () { + final vertexAI = FirebaseAI.vertexAI(); + + final model = vertexAI.generativeModel( + model: 'gemini-pro', + generationConfig: GenerationConfig(maxOutputTokens: 1024), + systemInstruction: Content.system('You are a helpful assistant.'), + ); + + expect(model, isA()); + }); + + // ... other tests (e.g., with different parameters) + }); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/google_ai_generative_model_test.dart b/packages/firebase_vertexai/firebase_ai/test/google_ai_generative_model_test.dart new file mode 100644 index 000000000000..9883102c2729 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/google_ai_generative_model_test.dart @@ -0,0 +1,731 @@ +// 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:firebase_ai/src/base_model.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'mock.dart'; +import 'utils/matchers.dart'; +import 'utils/stub_client.dart'; + +void main() { + setupFirebaseVertexAIMocks(); + late FirebaseApp app; + setUpAll(() async { + // Initialize Firebase + app = await Firebase.initializeApp(); + }); + group('GenerativeModel', () { + const defaultModelName = 'some-model'; + + (ClientController, GenerativeModel) createModel({ + String modelName = defaultModelName, + List? tools, + ToolConfig? toolConfig, + Content? systemInstruction, + }) { + final client = ClientController(); + final model = createModelWithClient( + useVertexBackend: false, + app: app, + model: modelName, + client: client.client, + tools: tools, + toolConfig: toolConfig, + systemInstruction: systemInstruction, + location: 'us-central1'); + return (client, model); + } + + test('strips leading "models/" from model name', () async { + final (client, model) = createModel( + modelName: 'models/$defaultModelName', + ); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([Content.text(prompt)]), + response: arbitraryGenerateContentResponse, + verifyRequest: (uri, _) { + expect(uri.path, endsWith('/models/some-model:generateContent')); + }, + ); + }); + + test('allows specifying a tuned model', () async { + final (client, model) = createModel( + modelName: 'tunedModels/$defaultModelName', + ); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([Content.text(prompt)]), + response: arbitraryGenerateContentResponse, + verifyRequest: (uri, _) { + expect(uri.path, endsWith('/tunedModels/some-model:generateContent')); + }, + ); + }); + + test('allows specifying an API version', () async { + final (client, model) = createModel( + // requestOptions: RequestOptions(apiVersion: 'override_version'), + ); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([Content.text(prompt)]), + response: arbitraryGenerateContentResponse, + verifyRequest: (uri, _) { + expect(uri.path, startsWith('/override_version/')); + }, + ); + }, skip: 'No support for overriding API version'); + + group('generate unary content', () { + test('can make successful request', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + const result = 'Some response'; + final response = await client.checkRequest( + () => model.generateContent([Content.text(prompt)]), + verifyRequest: (uri, request) { + expect( + uri, + Uri.parse( + 'https://firebasevertexai.googleapis.com/v1beta/' + 'projects/123/' + 'models/some-model:generateContent', + ), + ); + expect(request, { + 'model': 'models/$defaultModelName', + 'contents': [ + { + 'role': 'user', + 'parts': [ + {'text': prompt}, + ], + }, + ], + }); + }, + response: { + 'candidates': [ + { + 'content': { + 'role': 'model', + 'parts': [ + {'text': result}, + ], + }, + }, + ], + }, + ); + expect( + response, + matchesGenerateContentResponse( + GenerateContentResponse([ + Candidate( + Content('model', [TextPart(result)]), + null, + null, + null, + null, + ), + ], null), + ), + ); + }); + + test('can override safety settings', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent( + [Content.text(prompt)], + safetySettings: [ + SafetySetting( + HarmCategory.dangerousContent, + HarmBlockThreshold.high, + null, + ), + ], + ), + response: arbitraryGenerateContentResponse, + verifyRequest: (_, request) { + expect(request['safetySettings'], [ + { + 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', + 'threshold': 'BLOCK_ONLY_HIGH', + }, + ]); + }, + ); + }); + + test('can override generation config', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([ + Content.text(prompt), + ], generationConfig: GenerationConfig(stopSequences: ['a'])), + verifyRequest: (_, request) { + expect(request['generationConfig'], { + 'stopSequences': ['a'], + }); + }, + response: arbitraryGenerateContentResponse, + ); + }); + + test('can pass system instructions', () async { + const instructions = 'Do a good job'; + final (client, model) = createModel( + systemInstruction: Content.system(instructions), + ); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([Content.text(prompt)]), + verifyRequest: (_, request) { + expect(request['systemInstruction'], { + 'role': 'system', + 'parts': [ + {'text': instructions}, + ], + }); + }, + response: arbitraryGenerateContentResponse, + ); + }); + + test('can pass tools and function calling config', () async { + final (client, model) = createModel( + tools: [ + Tool.functionDeclarations([ + FunctionDeclaration( + 'someFunction', + 'Some cool function.', + parameters: { + 'schema1': Schema.string(description: 'Some parameter.'), + }, + ), + ]), + ], + toolConfig: ToolConfig( + functionCallingConfig: FunctionCallingConfig.any( + {'someFunction'}, + ), + ), + ); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([Content.text(prompt)]), + verifyRequest: (_, request) { + expect(request['tools'], [ + { + 'functionDeclarations': [ + { + 'name': 'someFunction', + 'description': 'Some cool function.', + 'parameters': { + 'type': 'OBJECT', + 'properties': { + 'schema1': { + 'type': 'STRING', + 'description': 'Some parameter.' + } + }, + 'required': ['schema1'] + } + }, + ], + }, + ]); + expect(request['toolConfig'], { + 'functionCallingConfig': { + 'mode': 'ANY', + 'allowedFunctionNames': ['someFunction'], + }, + }); + }, + response: arbitraryGenerateContentResponse, + ); + }); + + test('can override tools and function calling config', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent( + [Content.text(prompt)], + tools: [ + Tool.functionDeclarations([ + FunctionDeclaration( + 'someFunction', + 'Some cool function.', + parameters: { + 'schema1': Schema.string(description: 'Some parameter.'), + }, + ), + ]), + ], + toolConfig: ToolConfig( + functionCallingConfig: FunctionCallingConfig.any( + {'someFunction'}, + ), + ), + ), + verifyRequest: (_, request) { + expect(request['tools'], [ + { + 'functionDeclarations': [ + { + 'name': 'someFunction', + 'description': 'Some cool function.', + 'parameters': { + 'type': 'OBJECT', + 'properties': { + 'schema1': { + 'type': 'STRING', + 'description': 'Some parameter.' + } + }, + 'required': ['schema1'] + } + }, + ], + }, + ]); + expect(request['toolConfig'], { + 'functionCallingConfig': { + 'mode': 'ANY', + 'allowedFunctionNames': ['someFunction'], + }, + }); + }, + response: arbitraryGenerateContentResponse, + ); + }); + + test('can enable code execution', () async { + final (client, model) = createModel(tools: [ + // Tool(codeExecution: CodeExecution()), + ]); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([Content.text(prompt)]), + verifyRequest: (_, request) { + expect(request['tools'], [ + {'codeExecution': {}} + ]); + }, + response: arbitraryGenerateContentResponse, + ); + }, skip: 'No support for code executation'); + + test('can override code execution', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([ + Content.text(prompt) + ], tools: [ + // Tool(codeExecution: CodeExecution()), + ]), + verifyRequest: (_, request) { + expect(request['tools'], [ + {'codeExecution': {}} + ]); + }, + response: arbitraryGenerateContentResponse, + ); + }, skip: 'No support for code execution'); + }); + + group('generate content stream', () { + test('can make successful request', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + final results = {'First response', 'Second Response'}; + final response = await client.checkStreamRequest( + () async => model.generateContentStream([Content.text(prompt)]), + verifyRequest: (uri, request) { + expect( + uri, + Uri.parse( + 'https://firebasevertexai.googleapis.com/v1beta/' + 'projects/123/' + 'models/some-model:streamGenerateContent', + ), + ); + expect(request, { + 'model': 'models/$defaultModelName', + 'contents': [ + { + 'role': 'user', + 'parts': [ + {'text': prompt}, + ], + }, + ], + }); + }, + responses: [ + for (final result in results) + { + 'candidates': [ + { + 'content': { + 'role': 'model', + 'parts': [ + {'text': result}, + ], + }, + }, + ], + }, + ], + ); + expect( + response, + emitsInOrder([ + for (final result in results) + matchesGenerateContentResponse( + GenerateContentResponse([ + Candidate( + Content('model', [TextPart(result)]), + null, + null, + null, + null, + ), + ], null), + ), + ]), + ); + }); + + test('can override safety settings', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + final responses = await client.checkStreamRequest( + () async => model.generateContentStream( + [Content.text(prompt)], + safetySettings: [ + SafetySetting( + HarmCategory.dangerousContent, + HarmBlockThreshold.high, + null, + ), + ], + ), + verifyRequest: (_, request) { + expect(request['safetySettings'], [ + { + 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', + 'threshold': 'BLOCK_ONLY_HIGH', + }, + ]); + }, + responses: [arbitraryGenerateContentResponse], + ); + await responses.drain(); + }); + + test('can override generation config', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + final responses = await client.checkStreamRequest( + () async => model.generateContentStream([ + Content.text(prompt), + ], generationConfig: GenerationConfig(stopSequences: ['a'])), + verifyRequest: (_, request) { + expect(request['generationConfig'], { + 'stopSequences': ['a'], + }); + }, + responses: [arbitraryGenerateContentResponse], + ); + await responses.drain(); + }); + }); + + group('count tokens', () { + test('can make successful request', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + final response = await client.checkRequest( + () => model.countTokens([Content.text(prompt)]), + verifyRequest: (uri, request) { + expect( + uri, + Uri.parse( + 'https://firebasevertexai.googleapis.com/v1beta/' + 'projects/123/' + 'models/some-model:countTokens', + ), + ); + expect(request, { + 'generateContentRequest': { + 'model': 'models/$defaultModelName', + 'contents': [ + { + 'role': 'user', + 'parts': [ + {'text': prompt}, + ], + }, + ], + } + }); + }, + response: {'totalTokens': 2}, + ); + expect(response, matchesCountTokensResponse(CountTokensResponse(2))); + }); + + test('can override GenerateContentRequest fields', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + await client.checkRequest( + response: {'totalTokens': 100}, + () => model.countTokens( + [Content.text(prompt)], + // safetySettings: [ + // SafetySetting( + // HarmCategory.dangerousContent, + // HarmBlockThreshold.high, + // null, + // ), + // ], + // generationConfig: GenerationConfig(stopSequences: ['a']), + // tools: [ + // Tool(functionDeclarations: [ + // FunctionDeclaration( + // 'someFunction', + // 'Some cool function.', + // Schema(SchemaType.string, description: 'Some parameter.'), + // ), + // ]), + // ], + // toolConfig: ToolConfig( + // functionCallingConfig: FunctionCallingConfig( + // mode: FunctionCallingMode.any, + // allowedFunctionNames: {'someFunction'}, + // ), + // ), + ), + verifyRequest: (_, countTokensRequest) { + expect(countTokensRequest, isNotNull); + final request = countTokensRequest['generateContentRequest']! + as Map; + expect(request['safetySettings'], [ + { + 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', + 'threshold': 'BLOCK_ONLY_HIGH', + }, + ]); + expect(request['generationConfig'], { + 'stopSequences': ['a'], + }); + expect(request['tools'], [ + { + 'functionDeclarations': [ + { + 'name': 'someFunction', + 'description': 'Some cool function.', + 'parameters': { + 'type': 'STRING', + 'description': 'Some parameter.', + }, + }, + ], + }, + ]); + expect(request['toolConfig'], { + 'functionCallingConfig': { + 'mode': 'ANY', + 'allowedFunctionNames': ['someFunction'], + }, + }); + }, + ); + }, skip: 'Only content argument supported for countTokens'); + }); + + group('embed content', () { + test('can make successful request', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + final response = await client.checkRequest( + () async { + // await model.embedContent(Content.text(prompt)); + }, + verifyRequest: (uri, request) { + expect( + uri, + Uri.parse( + 'https://firebasevertexai.googleapis.com/v1beta/' + 'projects/123/' + 'models/some-model:embedContent', + ), + ); + expect(request, { + 'content': { + 'role': 'user', + 'parts': [ + {'text': prompt}, + ], + }, + }); + }, + response: { + 'embedding': { + 'values': [0.1, 0.2, 0.3], + }, + }, + ); + expect( + response, + // matchesEmbedContentResponse( + // EmbedContentResponse(ContentEmbedding([0.1, 0.2, 0.3])), + // ), + isNotNull, + ); + }); + + test('embed content with reduced output dimensionality', () async { + final (client, model) = createModel(); + const content = 'Some content'; + const outputDimensionality = 1; + final embeddingValues = [0.1]; + + await client.checkRequest(() async { + Content.text(content); + // await model.embedContent( + // Content.text(content), + // outputDimensionality: outputDimensionality, + // ); + }, verifyRequest: (_, request) { + expect(request, + containsPair('outputDimensionality', outputDimensionality)); + }, response: { + 'embedding': {'values': embeddingValues}, + }); + }); + }, skip: 'No support for embedding content'); + + group('batch embed contents', () { + test('can make successful request', () async { + final (client, model) = createModel(); + const prompt1 = 'Some prompt'; + const prompt2 = 'Another prompt'; + final embedding1 = [0.1, 0.2, 0.3]; + final embedding2 = [0.4, 0.5, 1.6]; + final response = await client.checkRequest( + () async { + // await model.batchEmbedContents([ + // EmbedContentRequest(Content.text(prompt1)), + // EmbedContentRequest(Content.text(prompt2)), + // ]); + }, + verifyRequest: (uri, request) { + expect( + uri, + Uri.parse( + 'https://firebasevertexai.googleapis.com/v1beta/' + 'projects/123/' + 'models/some-model:batchEmbedContents', + ), + ); + expect(request, { + 'requests': [ + { + 'content': { + 'role': 'user', + 'parts': [ + {'text': prompt1}, + ], + }, + 'model': 'models/$defaultModelName', + }, + { + 'content': { + 'role': 'user', + 'parts': [ + {'text': prompt2}, + ], + }, + 'model': 'models/$defaultModelName', + }, + ], + }); + }, + response: { + 'embeddings': [ + {'values': embedding1}, + {'values': embedding2}, + ], + }, + ); + expect( + response, + isNotNull, + // matchesBatchEmbedContentsResponse( + // BatchEmbedContentsResponse([ + // ContentEmbedding(embedding1), + // ContentEmbedding(embedding2), + // ]), + // ), + ); + }); + + test('batch embed contents with reduced output dimensionality', () async { + final (client, model) = createModel(); + const content1 = 'Some content 1'; + const content2 = 'Some content 2'; + const outputDimensionality = 1; + final embeddingValues1 = [0.1]; + final embeddingValues2 = [0.4]; + + await client.checkRequest(() async { + Content.text(content1); + Content.text(content2); + // await model.batchEmbedContents([ + // EmbedContentRequest( + // Content.text(content1), + // outputDimensionality: outputDimensionality, + // ), + // EmbedContentRequest( + // Content.text(content2), + // outputDimensionality: outputDimensionality, + // ), + // ]); + }, verifyRequest: (_, request) { + expect(request['requests'], [ + containsPair('outputDimensionality', outputDimensionality), + containsPair('outputDimensionality', outputDimensionality), + ]); + }, response: { + 'embeddings': [ + {'values': embeddingValues1}, + {'values': embeddingValues2}, + ], + }); + }); + }, skip: 'No support for embed content'); + }); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/google_ai_response_parsing_test.dart b/packages/firebase_vertexai/firebase_ai/test/google_ai_response_parsing_test.dart new file mode 100644 index 000000000000..eec1b98938b4 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/google_ai_response_parsing_test.dart @@ -0,0 +1,770 @@ +// 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:convert'; + +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:firebase_ai/src/developer/api.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'utils/matchers.dart'; + +void main() { + group('throws errors for invalid GenerateContentResponse', () { + test('with empty content', () { + const response = ''' +{ + "candidates": [ + { + "content": {}, + "index": 0 + } + ], + "promptFeedback": { + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + expect( + () => DeveloperSerialization().parseGenerateContentResponse(decoded), + throwsA( + isA().having( + (e) => e.message, + 'message', + startsWith('Unhandled format for Content:'), + ), + ), + ); + }); + + test('with a blocked prompt', () { + const response = ''' +{ + "promptFeedback": { + "blockReason": "SAFETY", + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "HIGH" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + DeveloperSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse( + [], + PromptFeedback(BlockReason.safety, null, [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating(HarmCategory.hateSpeech, HarmProbability.high), + SafetyRating(HarmCategory.harassment, HarmProbability.negligible), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ]), + ), + ), + ); + expect( + () => generateContentResponse.text, + throwsA( + isA().having( + (e) => e.message, + 'message', + startsWith('Response was blocked due to safety'), + ), + ), + ); + }); + }); + + group('parses successful GenerateContentResponse', () { + test('with a basic reply', () async { + const response = ''' +{ + "candidates": [ + { + "content": { + "parts": [ + { + "text": "Mountain View, California, United States" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } + ], + "promptFeedback": { + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + DeveloperSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse( + [ + Candidate( + Content.model([ + TextPart('Mountain View, California, United States'), + ]), + [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.hateSpeech, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.harassment, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ], + null, + FinishReason.stop, + null, + ), + ], + PromptFeedback(null, null, [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating(HarmCategory.hateSpeech, HarmProbability.negligible), + SafetyRating(HarmCategory.harassment, HarmProbability.negligible), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ]), + ), + ), + ); + }); + + test('with a citation', () async { + const response = ''' +{ + "candidates": [ + { + "content": { + "parts": [ + { + "text": "placeholder" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ], + "citationMetadata": { + "citationSources": [ + { + "startIndex": 574, + "endIndex": 705, + "uri": "https://example.com/", + "license": "" + }, + { + "startIndex": 899, + "endIndex": 1026, + "uri": "https://example.com/", + "license": "" + }, + { + "startIndex": 899, + "endIndex": 1026 + }, + { + "uri": "https://example.com/", + "license": "" + }, + {} + ] + } + } + ], + "promptFeedback": { + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + DeveloperSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse( + [ + Candidate( + Content.model([TextPart('placeholder')]), + [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.hateSpeech, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.harassment, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ], + CitationMetadata([ + Citation(574, 705, Uri.https('example.com'), ''), + Citation(899, 1026, Uri.https('example.com'), ''), + ]), + FinishReason.stop, + null, + ), + ], + PromptFeedback(null, null, [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating(HarmCategory.hateSpeech, HarmProbability.negligible), + SafetyRating(HarmCategory.harassment, HarmProbability.negligible), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ]), + ), + ), + ); + }); + + test('with a vertex formatted citation', () async { + const response = ''' +{ + "candidates": [ + { + "content": { + "parts": [ + { + "text": "placeholder" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ], + "citationMetadata": { + "citations": [ + { + "startIndex": 574, + "endIndex": 705, + "uri": "https://example.com/", + "license": "" + }, + { + "startIndex": 899, + "endIndex": 1026, + "uri": "https://example.com/", + "license": "" + }, + { + "startIndex": 899, + "endIndex": 1026 + }, + { + "uri": "https://example.com/", + "license": "" + }, + {} + ] + } + } + ], + "promptFeedback": { + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + DeveloperSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse( + [ + Candidate( + Content.model([TextPart('placeholder')]), + [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.hateSpeech, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.harassment, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ], + CitationMetadata([ + Citation(574, 705, Uri.https('example.com'), ''), + Citation(899, 1026, Uri.https('example.com'), ''), + ]), + FinishReason.stop, + null, + ), + ], + PromptFeedback(null, null, [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating(HarmCategory.hateSpeech, HarmProbability.negligible), + SafetyRating(HarmCategory.harassment, HarmProbability.negligible), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ]), + ), + ), + ); + }); + + test('with code execution', () async { + const response = ''' +{ + "candidates": [ + { + "content": { + "parts": [ + { + "executableCode": { + "language": "PYTHON", + "code": "print('hello world')" + } + }, + { + "codeExecutionResult": { + "outcome": "OUTCOME_OK", + "output": "hello world" + } + }, + { + "text": "hello world" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0 + } + ] +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + DeveloperSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse( + [ + Candidate( + Content.model([ + // ExecutableCode(Language.python, 'print(\'hello world\')'), + // CodeExecutionResult(Outcome.ok, 'hello world'), + TextPart('hello world') + ]), + [], + null, + FinishReason.stop, + null, + ), + ], + null, + ), + ), + ); + }, skip: 'Code Execution Unsupported'); + + test('allows missing content', () async { + const response = ''' +{ + "candidates": [ + { + "finishReason": "SAFETY", + "index": 0, + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "LOW" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "MEDIUM" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } + ] +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + DeveloperSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse([ + Candidate( + Content(null, []), + [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.hateSpeech, HarmProbability.negligible), + SafetyRating( + HarmCategory.harassment, HarmProbability.negligible), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ], + CitationMetadata([]), + FinishReason.safety, + null), + ], null), + ), + ); + }); + + test('text getter joins content', () async { + const response = ''' +{ + "candidates": [ + { + "content": { + "parts": [ + { + "text": "Initial text" + }, + { + "functionCall": {"name": "someFunction", "args": {}} + }, + { + "text": " And more text" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0 + } + ] +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + DeveloperSerialization().parseGenerateContentResponse(decoded); + expect(generateContentResponse.text, 'Initial text And more text'); + expect(generateContentResponse.candidates.single.text, + 'Initial text And more text'); + }); + }); + + group('parses and throws error responses', () { + test('for invalid API key', () async { + const response = ''' +{ + "error": { + "code": 400, + "message": "API key not valid. Please pass a valid API key.", + "status": "INVALID_ARGUMENT", + "details": [ + { + "@type": "type.googleapis.com/google.rpc.ErrorInfo", + "reason": "API_KEY_INVALID", + "domain": "googleapis.com", + "metadata": { + "service": "generativelanguage.googleapis.com" + } + }, + { + "@type": "type.googleapis.com/google.rpc.DebugInfo", + "detail": "Invalid API key: AIzv00G7VmUCUeC-5OglO3hcXM" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final expectedThrow = throwsA( + isA().having( + (e) => e.message, + 'message', + 'API key not valid. Please pass a valid API key.', + ), + ); + expect( + () => DeveloperSerialization().parseGenerateContentResponse(decoded), + expectedThrow); + expect(() => DeveloperSerialization().parseCountTokensResponse(decoded), + expectedThrow); + // expect(() => parseEmbedContentResponse(decoded), expectedThrow); + }); + + test('for unsupported user location', () async { + const response = r''' +{ + "error": { + "code": 400, + "message": "User location is not supported for the API use.", + "status": "FAILED_PRECONDITION", + "details": [ + { + "@type": "type.googleapis.com/google.rpc.DebugInfo", + "detail": "[ORIGINAL ERROR] generic::failed_precondition: User location is not supported for the API use. [google.rpc.error_details_ext] { message: \"User location is not supported for the API use.\" }" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final expectedThrow = throwsA( + isA().having( + (e) => e.message, + 'message', + 'User location is not supported for the API use.', + ), + ); + expect( + () => DeveloperSerialization().parseGenerateContentResponse(decoded), + expectedThrow); + expect(() => DeveloperSerialization().parseCountTokensResponse(decoded), + expectedThrow); + // expect(() => parseEmbedContentResponse(decoded), expectedThrow); + }); + + test('for general server errors', () async { + const response = r''' +{ + "error": { + "code": 404, + "message": "models/unknown is not found for API version v1, or is not supported for GenerateContent. Call ListModels to see the list of available models and their supported methods.", + "status": "NOT_FOUND", + "details": [ + { + "@type": "type.googleapis.com/google.rpc.DebugInfo", + "detail": "[ORIGINAL ERROR] generic::not_found: models/unknown is not found for API version v1, or is not supported for GenerateContent. Call ListModels to see the list of available models and their supported methods. [google.rpc.error_details_ext] { message: \"models/unknown is not found for API version v1, or is not supported for GenerateContent. Call ListModels to see the list of available models and their supported methods.\" }" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final expectedThrow = throwsA( + isA().having( + (e) => e.message, + 'message', + startsWith( + 'models/unknown is not found for API version v1, ' + 'or is not supported for GenerateContent.', + ), + ), + ); + expect( + () => DeveloperSerialization().parseGenerateContentResponse(decoded), + expectedThrow); + expect(() => DeveloperSerialization().parseCountTokensResponse(decoded), + expectedThrow); + // expect(() => parseEmbedContentResponse(decoded), expectedThrow); + }); + }); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/imagen_test.dart b/packages/firebase_vertexai/firebase_ai/test/imagen_test.dart new file mode 100644 index 000000000000..4bd7ae5b763a --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/imagen_test.dart @@ -0,0 +1,241 @@ +// 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:convert'; +import 'dart:typed_data'; + +import 'package:firebase_ai/src/error.dart'; +import 'package:firebase_ai/src/imagen_content.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('ImagenInlineImage', () { + test('fromJson with valid base64', () { + final json = { + 'mimeType': 'image/png', + 'bytesBase64Encoded': + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=' + }; + final image = ImagenInlineImage.fromJson(json); + expect(image.mimeType, 'image/png'); + expect(image.bytesBase64Encoded, isA()); + expect(image.bytesBase64Encoded, isNotEmpty); + }); + + test('fromJson with invalid base64', () { + final json = { + 'mimeType': 'image/png', + 'bytesBase64Encoded': 'invalid_base64_string' + }; + // Expect that the constructor throws an exception. + expect(() => ImagenInlineImage.fromJson(json), throwsFormatException); + }); + + test('toJson', () { + final image = ImagenInlineImage( + mimeType: 'image/png', + bytesBase64Encoded: Uint8List.fromList(utf8.encode('Hello, world!')), + ); + final json = image.toJson(); + expect(json, { + 'mimeType': 'image/png', + 'bytesBase64Encoded': 'SGVsbG8sIHdvcmxkIQ==', + }); + }); + }); + + group('ImagenGCSImage', () { + test('fromJson', () { + final json = { + 'mimeType': 'image/jpeg', + 'gcsUri': + 'gs://test-project-id-1234.firebasestorage.app/images/1234567890123/sample_0.jpg' + }; + final image = ImagenGCSImage.fromJson(json); + expect(image.mimeType, 'image/jpeg'); + expect(image.gcsUri, + 'gs://test-project-id-1234.firebasestorage.app/images/1234567890123/sample_0.jpg'); + }); + + test('toJson', () { + final image = ImagenGCSImage( + mimeType: 'image/jpeg', + gcsUri: + 'gs://test-project-id-1234.firebasestorage.app/images/1234567890123/sample_0.jpg', + ); + final json = image.toJson(); + expect(json, { + 'mimeType': 'image/jpeg', + 'gcsUri': + 'gs://test-project-id-1234.firebasestorage.app/images/1234567890123/sample_0.jpg', + }); + }); + }); + + group('ImagenGenerationResponse', () { + test('fromJson with gcsUri', () { + final json = { + 'predictions': [ + { + 'mimeType': 'image/jpeg', + 'gcsUri': + 'gs://test-project-id-1234.firebasestorage.app/images/1234567890123/sample_0.jpg' + }, + { + 'mimeType': 'image/jpeg', + 'gcsUri': + 'gs://test-project-id-1234.firebasestorage.app/images/1234567890123/sample_1.jpg' + }, + { + 'mimeType': 'image/jpeg', + 'gcsUri': + 'gs://test-project-id-1234.firebasestorage.app/images/1234567890123/sample_2.jpg' + }, + { + 'mimeType': 'image/jpeg', + 'gcsUri': + 'gs://test-project-id-1234.firebasestorage.app/images/1234567890123/sample_3.jpg' + } + ] + }; + final response = ImagenGenerationResponse.fromJson(json); + expect(response.images, isA>()); + expect(response.images.length, 4); + expect(response.filteredReason, isNull); + }); + + test('fromJson with bytesBase64Encoded', () { + final json = { + 'predictions': [ + { + 'mimeType': 'image/jpeg', + 'bytesBase64Encoded': 'SGVsbG8sIHdvcmxkIQ==' + }, + { + 'mimeType': 'image/jpeg', + 'bytesBase64Encoded': 'SGVsbG8sIHdvcmxkIQ==' + }, + { + 'mimeType': 'image/jpeg', + 'bytesBase64Encoded': 'SGVsbG8sIHdvcmxkIQ==' + }, + { + 'mimeType': 'image/jpeg', + 'bytesBase64Encoded': 'SGVsbG8sIHdvcmxkIQ==' + } + ] + }; + final response = + ImagenGenerationResponse.fromJson(json); + expect(response.images, isA>()); + expect(response.images.length, 4); + expect(response.filteredReason, isNull); + }); + + test('fromJson with bytesBase64Encoded and raiFilteredReason', () { + final json = { + 'predictions': [ + { + 'mimeType': 'image/png', + 'bytesBase64Encoded': + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=' + }, + { + 'mimeType': 'image/png', + 'bytesBase64Encoded': + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=' + }, + { + 'raiFilteredReason': + 'Your current safety filter threshold filtered out 2 generated images. You will not be charged for blocked images. Try rephrasing the prompt. If you think this was an error, send feedback.' + } + ] + }; + final response = + ImagenGenerationResponse.fromJson(json); + expect(response.images, isA>()); + expect(response.images.length, 2); + expect(response.filteredReason, + 'Your current safety filter threshold filtered out 2 generated images. You will not be charged for blocked images. Try rephrasing the prompt. If you think this was an error, send feedback.'); + }); + + test('fromJson with only raiFilteredReason', () { + final json = { + 'predictions': [ + { + 'raiFilteredReason': + "Unable to show generated images. All images were filtered out because they violated Vertex AI's usage guidelines. You will not be charged for blocked images. Try rephrasing the prompt. If you think this was an error, send feedback. Support codes: 39322892, 29310472" + } + ] + }; + // Expect that the constructor throws an exception. + expect(() => ImagenGenerationResponse.fromJson(json), + throwsA(isA())); + }); + + test('fromJson with empty predictions', () { + final json = {'predictions': {}}; + // Expect that the constructor throws an exception. + expect(() => ImagenGenerationResponse.fromJson(json), + throwsA(isA())); + }); + + test('fromJson with unsupported type', () { + final json = { + 'predictions': [ + { + 'mimeType': 'image/jpeg', + 'gcsUri': + 'gs://test-project-id-1234.firebasestorage.app/images/1234567890123/sample_0.jpg' + }, + ] + }; + // Expect that the constructor throws an exception. + expect(() => ImagenGenerationResponse.fromJson(json), + throwsA(isA())); + }); + }); + + group('parseImagenGenerationResponse', () { + test('with valid response', () { + final json = { + 'predictions': [ + { + 'mimeType': 'image/jpeg', + 'gcsUri': + 'gs://test-project-id-1234.firebasestorage.app/images/1234567890123/sample_0.jpg' + }, + ] + }; + final response = parseImagenGenerationResponse(json); + expect(response.images, isA>()); + expect(response.images.length, 1); + expect(response.filteredReason, isNull); + }); + + test('with error', () { + final json = { + 'error': { + 'code': 400, + 'message': + "Image generation failed with the following error: The prompt could not be submitted. This prompt contains sensitive words that violate Google's Responsible AI practices. Try rephrasing the prompt. If you think this was an error, send feedback. Support codes: 42876398", + 'status': 'INVALID_ARGUMENT' + } + }; + // Expect that the function throws an exception. + expect(() => parseImagenGenerationResponse(json), + throwsA(isA())); + }); + }); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/live_test.dart b/packages/firebase_vertexai/firebase_ai/test/live_test.dart new file mode 100644 index 000000000000..b215c6e4ca08 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/live_test.dart @@ -0,0 +1,239 @@ +// 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:firebase_ai/src/content.dart'; +import 'package:firebase_ai/src/error.dart'; +import 'package:firebase_ai/src/live_api.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('LiveAPI Tests', () { + test('SpeechConfig toJson() returns correct JSON', () { + final speechConfigWithVoice = SpeechConfig(voiceName: 'Aoede'); + expect(speechConfigWithVoice.toJson(), { + 'voice_config': { + 'prebuilt_voice_config': {'voice_name': 'Aoede'} + } + }); + + final speechConfigWithoutVoice = SpeechConfig(); + expect(speechConfigWithoutVoice.toJson(), {}); + }); + + test('ResponseModalities enum toJson() returns correct value', () { + expect(ResponseModalities.text.toJson(), 'TEXT'); + expect(ResponseModalities.image.toJson(), 'IMAGE'); + expect(ResponseModalities.audio.toJson(), 'AUDIO'); + }); + + test('LiveGenerationConfig toJson() returns correct JSON', () { + final liveGenerationConfig = LiveGenerationConfig( + speechConfig: SpeechConfig(voiceName: 'Charon'), + responseModalities: [ResponseModalities.text, ResponseModalities.audio], + candidateCount: 2, + maxOutputTokens: 100, + temperature: 0.8, + topP: 0.95, + topK: 40, + ); + + expect(liveGenerationConfig.toJson(), { + 'candidateCount': 2, + 'maxOutputTokens': 100, + 'temperature': 0.8, + 'topP': 0.95, + 'topK': 40, + 'speechConfig': { + 'voice_config': { + 'prebuilt_voice_config': {'voice_name': 'Charon'} + } + }, + 'responseModalities': ['TEXT', 'AUDIO'], + }); + + final liveGenerationConfigWithoutOptionals = LiveGenerationConfig(); + expect(liveGenerationConfigWithoutOptionals.toJson(), {}); + }); + + test('LiveServerContent constructor and properties', () { + final content = Content.text('Hello, world!'); + final message = LiveServerContent( + modelTurn: content, + turnComplete: true, + interrupted: false, + ); + expect(message.modelTurn, content); + expect(message.turnComplete, true); + expect(message.interrupted, false); + + final message2 = LiveServerContent(); + expect(message2.modelTurn, null); + expect(message2.turnComplete, null); + expect(message2.interrupted, null); + }); + + test('LiveServerToolCall constructor and properties', () { + final functionCall = FunctionCall('test', {}); + final message = LiveServerToolCall(functionCalls: [functionCall]); + expect(message.functionCalls, [functionCall]); + + final message2 = LiveServerToolCall(); + expect(message2.functionCalls, null); + }); + + test('LiveServerToolCallCancellation constructor and properties', () { + final message = LiveServerToolCallCancellation(functionIds: ['1', '2']); + expect(message.functionIds, ['1', '2']); + + final message2 = LiveServerToolCallCancellation(); + expect(message2.functionIds, null); + }); + + test('LiveClientRealtimeInput toJson() returns correct JSON', () { + final part = InlineDataPart('audio/pcm', Uint8List.fromList([1, 2, 3])); + final message = LiveClientRealtimeInput(mediaChunks: [part]); + expect(message.toJson(), { + 'realtime_input': { + 'media_chunks': [ + { + 'mimeType': 'audio/pcm', + 'data': 'AQID', + } + ], + }, + }); + + final message2 = LiveClientRealtimeInput(); + expect(message2.toJson(), { + 'realtime_input': { + 'media_chunks': null, + }, + }); + }); + + test('LiveClientContent toJson() returns correct JSON', () { + final content = Content.text('some test input'); + final message = LiveClientContent(turns: [content], turnComplete: true); + expect(message.toJson(), { + 'client_content': { + 'turns': [ + { + 'role': 'user', + 'parts': [ + {'text': 'some test input'} + ] + } + ], + 'turn_complete': true, + } + }); + + final message2 = LiveClientContent(); + expect(message2.toJson(), { + 'client_content': { + 'turns': null, + 'turn_complete': null, + } + }); + }); + + test('LiveClientToolResponse toJson() returns correct JSON', () { + final response = FunctionResponse('test', {}); + final message = LiveClientToolResponse(functionResponses: [response]); + expect(message.toJson(), { + 'functionResponses': [ + { + 'functionResponse': {'name': 'test', 'response': {}} + } + ] + }); + + final message2 = LiveClientToolResponse(); + expect(message2.toJson(), {'functionResponses': null}); + }); + + test('parseServerMessage parses serverContent message correctly', () { + final jsonObject = { + 'serverContent': { + 'modelTurn': { + 'parts': [ + {'text': 'Hello, world!'} + ] + }, + 'turnComplete': true, + } + }; + final response = parseServerResponse(jsonObject); + expect(response.message, isA()); + final contentMessage = response.message as LiveServerContent; + expect(contentMessage.turnComplete, true); + expect(contentMessage.modelTurn, isA()); + }); + + test('parseServerMessage parses toolCall message correctly', () { + final jsonObject = { + 'toolCall': { + 'functionCalls': [ + { + 'name': 'test1', + 'args': {'foo1': 'bar1'} + }, + { + 'name': 'test2', + 'args': {'foo2': 'bar2'} + } + ] + } + }; + final response = parseServerResponse(jsonObject); + expect(response.message, isA()); + final toolCallMessage = response.message as LiveServerToolCall; + expect(toolCallMessage.functionCalls, isA>()); + }); + + test('parseServerMessage parses toolCallCancellation message correctly', + () { + final jsonObject = { + 'toolCallCancellation': { + 'ids': ['1', '2'] + } + }; + final response = parseServerResponse(jsonObject); + expect(response.message, isA()); + final cancellationMessage = + response.message as LiveServerToolCallCancellation; + expect(cancellationMessage.functionIds, ['1', '2']); + }); + + test('parseServerMessage parses setupComplete message correctly', () { + final jsonObject = {'setupComplete': {}}; + final response = parseServerResponse(jsonObject); + expect(response.message, isA()); + }); + + test('parseServerMessage throws VertexAIException for error message', () { + final jsonObject = {'error': {}}; + expect(() => parseServerResponse(jsonObject), + throwsA(isA())); + }); + + test('parseServerMessage throws VertexAISdkException for unhandled format', + () { + final jsonObject = {'unknown': {}}; + expect(() => parseServerResponse(jsonObject), + throwsA(isA())); + }); + }); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/mock.dart b/packages/firebase_vertexai/firebase_ai/test/mock.dart new file mode 100644 index 000000000000..ed883d924371 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/mock.dart @@ -0,0 +1,73 @@ +// 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. + +import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; +import 'package:firebase_core_platform_interface/test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockFirebaseAppVertexAI implements TestFirebaseCoreHostApi { + @override + Future initializeApp( + String appName, + PigeonFirebaseOptions initializeAppRequest, + ) async { + return PigeonInitializeResponse( + name: appName, + options: initializeAppRequest, + pluginConstants: {}, + ); + } + + @override + Future> initializeCore() async { + return [ + PigeonInitializeResponse( + name: defaultFirebaseAppName, + options: PigeonFirebaseOptions( + apiKey: '123', + projectId: '123', + appId: '123', + messagingSenderId: '123', + ), + pluginConstants: {}, + ) + ]; + } + + @override + Future optionsFromResource() async { + return PigeonFirebaseOptions( + apiKey: '123', + projectId: '123', + appId: '123', + messagingSenderId: '123', + ); + } +} + +void setupFirebaseVertexAIMocks() { + TestWidgetsFlutterBinding.ensureInitialized(); + + TestFirebaseCoreHostApi.setup(MockFirebaseAppVertexAI()); +} + +// FirebaseVertexAIPlatform Mock +class MockFirebaseVertexAI extends Mock + with + // ignore: prefer_mixin, plugin_platform_interface needs to migrate to use `mixin` + MockPlatformInterfaceMixin { + MockFirebaseVertexAI(); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/model_test.dart b/packages/firebase_vertexai/firebase_ai/test/model_test.dart new file mode 100644 index 000000000000..2ddf4d55406c --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/model_test.dart @@ -0,0 +1,464 @@ +// 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. + +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:firebase_ai/src/base_model.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'mock.dart'; +import 'utils/matchers.dart'; +import 'utils/stub_client.dart'; + +void main() { + setupFirebaseVertexAIMocks(); + // ignore: unused_local_variable + late FirebaseApp app; + setUpAll(() async { + // Initialize Firebase + app = await Firebase.initializeApp(); + }); + group('GenerativeModel', () { + const defaultModelName = 'some-model'; + + (ClientController, GenerativeModel) createModel({ + String modelName = defaultModelName, + List? tools, + ToolConfig? toolConfig, + Content? systemInstruction, + }) { + final client = ClientController(); + final model = createModelWithClient( + useVertexBackend: true, + app: app, + model: modelName, + client: client.client, + tools: tools, + toolConfig: toolConfig, + systemInstruction: systemInstruction, + location: 'us-central1'); + return (client, model); + } + + test('strips leading "models/" from model name', () async { + final (client, model) = createModel( + modelName: 'models/$defaultModelName', + ); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([Content.text(prompt)]), + response: arbitraryGenerateContentResponse, + verifyRequest: (uri, _) { + expect(uri.path, endsWith('/models/some-model:generateContent')); + }, + ); + }); + + test('allows specifying a tuned model', () async { + final (client, model) = createModel( + modelName: 'tunedModels/$defaultModelName', + ); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([Content.text(prompt)]), + response: arbitraryGenerateContentResponse, + verifyRequest: (uri, _) { + expect(uri.path, endsWith('/tunedModels/some-model:generateContent')); + }, + ); + }); + + group('generate unary content', () { + test('can make successful request', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + const result = 'Some response'; + final response = await client.checkRequest( + () => model.generateContent([Content.text(prompt)]), + verifyRequest: (uri, request) { + expect( + uri, + Uri.parse( + 'https://firebasevertexai.googleapis.com/v1beta/projects/123/locations/us-central1/publishers/google/models/some-model:generateContent', + ), + ); + expect(request, { + 'model': 'models/$defaultModelName', + 'contents': [ + { + 'role': 'user', + 'parts': [ + {'text': prompt}, + ], + }, + ], + }); + }, + response: { + 'candidates': [ + { + 'content': { + 'role': 'model', + 'parts': [ + {'text': result}, + ], + }, + }, + ], + }, + ); + expect( + response, + matchesGenerateContentResponse( + GenerateContentResponse([ + Candidate( + Content('model', [TextPart(result)]), + null, + null, + null, + null, + ), + ], null), + ), + ); + }); + + test('can override safety settings', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent( + [Content.text(prompt)], + safetySettings: [ + SafetySetting( + HarmCategory.dangerousContent, + HarmBlockThreshold.high, + HarmBlockMethod.probability, + ), + ], + ), + response: arbitraryGenerateContentResponse, + verifyRequest: (_, request) { + expect(request['safetySettings'], [ + { + 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', + 'threshold': 'BLOCK_ONLY_HIGH', + 'method': 'PROBABILITY', + }, + ]); + }, + ); + }); + + test('can override generation config', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([ + Content.text(prompt), + ], generationConfig: GenerationConfig(stopSequences: ['a'])), + verifyRequest: (_, request) { + expect(request['generationConfig'], { + 'stopSequences': ['a'], + }); + }, + response: arbitraryGenerateContentResponse, + ); + }); + + test('can override GenerationConfig repetition penalties', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([Content.text(prompt)], + generationConfig: GenerationConfig( + presencePenalty: 0.5, frequencyPenalty: 0.2)), + verifyRequest: (_, request) { + expect(request['generationConfig'], { + 'presencePenalty': 0.5, + 'frequencyPenalty': 0.2, + }); + }, + response: arbitraryGenerateContentResponse, + ); + }); + + test('can pass system instructions', () async { + const instructions = 'Do a good job'; + final (client, model) = createModel( + systemInstruction: Content.system(instructions), + ); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([Content.text(prompt)]), + verifyRequest: (_, request) { + expect(request['systemInstruction'], { + 'role': 'system', + 'parts': [ + {'text': instructions}, + ], + }); + }, + response: arbitraryGenerateContentResponse, + ); + }); + + test('can pass tools and function calling config', () async { + final (client, model) = createModel( + tools: [ + Tool.functionDeclarations([ + FunctionDeclaration( + 'someFunction', + 'Some cool function.', + parameters: { + 'schema1': Schema.string(description: 'Some parameter.') + }, + ), + ]), + ], + toolConfig: ToolConfig( + functionCallingConfig: FunctionCallingConfig.any( + {'someFunction'}, + ), + ), + ); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent([Content.text(prompt)]), + verifyRequest: (_, request) { + expect(request['tools'], [ + { + 'functionDeclarations': [ + { + 'name': 'someFunction', + 'description': 'Some cool function.', + 'parameters': { + 'type': 'OBJECT', + 'properties': { + 'schema1': { + 'type': 'STRING', + 'description': 'Some parameter.' + } + }, + 'required': ['schema1'] + } + }, + ], + }, + ]); + expect(request['toolConfig'], { + 'functionCallingConfig': { + 'mode': 'ANY', + 'allowedFunctionNames': ['someFunction'], + }, + }); + }, + response: arbitraryGenerateContentResponse, + ); + }); + + test('can override tools and function calling config', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + await client.checkRequest( + () => model.generateContent( + [Content.text(prompt)], + tools: [ + Tool.functionDeclarations([ + FunctionDeclaration( + 'someFunction', + 'Some cool function.', + parameters: { + 'schema1': Schema.string(description: 'Some parameter.') + }, + ), + ]), + ], + toolConfig: ToolConfig( + functionCallingConfig: FunctionCallingConfig.any( + {'someFunction'}, + ), + ), + ), + verifyRequest: (_, request) { + expect(request['tools'], [ + { + 'functionDeclarations': [ + { + 'name': 'someFunction', + 'description': 'Some cool function.', + 'parameters': { + 'type': 'OBJECT', + 'properties': { + 'schema1': { + 'type': 'STRING', + 'description': 'Some parameter.' + } + }, + 'required': ['schema1'] + } + }, + ], + }, + ]); + expect(request['toolConfig'], { + 'functionCallingConfig': { + 'mode': 'ANY', + 'allowedFunctionNames': ['someFunction'], + }, + }); + }, + response: arbitraryGenerateContentResponse, + ); + }); + }); + + group('generate content stream', () { + test('can make successful request', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + final results = {'First response', 'Second Response'}; + final response = await client.checkStreamRequest( + () async => model.generateContentStream([Content.text(prompt)]), + verifyRequest: (uri, request) { + expect( + uri, + Uri.parse( + 'https://firebasevertexai.googleapis.com/v1beta/projects/123/locations/us-central1/publishers/google/models/some-model:streamGenerateContent', + ), + ); + expect(request, { + 'model': 'models/$defaultModelName', + 'contents': [ + { + 'role': 'user', + 'parts': [ + {'text': prompt}, + ], + }, + ], + }); + }, + responses: [ + for (final result in results) + { + 'candidates': [ + { + 'content': { + 'role': 'model', + 'parts': [ + {'text': result}, + ], + }, + }, + ], + }, + ], + ); + expect( + response, + emitsInOrder([ + for (final result in results) + matchesGenerateContentResponse( + GenerateContentResponse([ + Candidate( + Content('model', [TextPart(result)]), + null, + null, + null, + null, + ), + ], null), + ), + ]), + ); + }); + + test('can override safety settings', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + final responses = await client.checkStreamRequest( + () async => model.generateContentStream( + [Content.text(prompt)], + safetySettings: [ + SafetySetting( + HarmCategory.dangerousContent, + HarmBlockThreshold.high, + HarmBlockMethod.severity, + ), + ], + ), + verifyRequest: (_, request) { + expect(request['safetySettings'], [ + { + 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', + 'threshold': 'BLOCK_ONLY_HIGH', + 'method': 'SEVERITY', + }, + ]); + }, + responses: [arbitraryGenerateContentResponse], + ); + await responses.drain(); + }); + + test('can override generation config', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + final responses = await client.checkStreamRequest( + () async => model.generateContentStream([ + Content.text(prompt), + ], generationConfig: GenerationConfig(stopSequences: ['a'])), + verifyRequest: (_, request) { + expect(request['generationConfig'], { + 'stopSequences': ['a'], + }); + }, + responses: [arbitraryGenerateContentResponse], + ); + await responses.drain(); + }); + }); + + group('count tokens', () { + test('can make successful request', () async { + final (client, model) = createModel(); + const prompt = 'Some prompt'; + final response = await client.checkRequest( + () => model.countTokens([Content.text(prompt)]), + verifyRequest: (uri, request) { + expect( + uri, + Uri.parse( + 'https://firebasevertexai.googleapis.com/v1beta/projects/123/locations/us-central1/publishers/google/models/some-model:countTokens', + ), + ); + expect(request, { + 'contents': [ + { + 'role': 'user', + 'parts': [ + {'text': prompt}, + ], + }, + ], + }); + }, + response: {'totalTokens': 2}, + ); + expect(response, matchesCountTokensResponse(CountTokensResponse(2))); + }); + }); + }); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/response_parsing_test.dart b/packages/firebase_vertexai/firebase_ai/test/response_parsing_test.dart new file mode 100644 index 000000000000..1414a5e3c50a --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/response_parsing_test.dart @@ -0,0 +1,875 @@ +// 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. + +import 'dart:convert'; + +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:firebase_ai/src/api.dart'; +import 'package:firebase_ai/src/error.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'utils/matchers.dart'; + +void main() { + group('throws errors for invalid GenerateContentResponse', () { + test('with empty content', () { + const response = ''' +{ + "candidates": [ + { + "content": {}, + "index": 0 + } + ], + "promptFeedback": { + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + expect( + () => VertexSerialization().parseGenerateContentResponse(decoded), + throwsA( + isA().having( + (e) => e.message, + 'message', + startsWith('Unhandled format for Content:'), + ), + ), + ); + }); + + test('with a blocked prompt', () { + const response = ''' +{ + "promptFeedback": { + "blockReason": "SAFETY", + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "HIGH" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + VertexSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse( + [], + PromptFeedback(BlockReason.safety, null, [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating(HarmCategory.hateSpeech, HarmProbability.high), + SafetyRating(HarmCategory.harassment, HarmProbability.negligible), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ]), + ), + ), + ); + expect( + () => generateContentResponse.text, + throwsA( + isA().having( + (e) => e.message, + 'message', + startsWith('Response was blocked due to safety'), + ), + ), + ); + }); + test('with service api not enabled', () { + const response = ''' +{ + "error": { + "code": 403, + "message": "Vertex AI in Firebase API has not been used in project test-project-id-1234 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/firebasevertexai.googleapis.com/overview?project=test-project-id-1234 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.", + "status": "PERMISSION_DENIED", + "details": [ + { + "@type": "type.googleapis.com/google.rpc.Help", + "links": [ + { + "description": "Google developers console API activation", + "url": "https://console.developers.google.com/apis/api/firebasevertexai.googleapis.com/overview?project=test-project-id-1234" + } + ] + }, + { + "@type": "type.googleapis.com/google.rpc.ErrorInfo", + "reason": "SERVICE_DISABLED", + "domain": "googleapis.com", + "metadata": { + "service": "firebasevertexai.googleapis.com", + "consumer": "projects/test-project-id-1234" + } + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + expect( + () => VertexSerialization().parseGenerateContentResponse(decoded), + throwsA( + isA().having( + (e) => e.message, + 'message', + startsWith( + 'The Vertex AI in Firebase SDK requires the Vertex AI in Firebase API'), + ), + ), + ); + }); + + test('with quota exceed', () { + const response = ''' +{ + "error": { + "code": 429, + "message": "Quota exceeded for quota metric 'Generate Content API requests per minute' and limit 'GenerateContent request limit per minute for a region' of service 'generativelanguage.googleapis.com' for consumer 'project_number:348715329010'.", + "status": "RESOURCE_EXHAUSTED", + "details": [ + { + "@type": "type.googleapis.com/google.rpc.ErrorInfo", + "reason": "RATE_LIMIT_EXCEEDED", + "domain": "googleapis.com", + "metadata": { + "service": "generativelanguage.googleapis.com", + "consumer": "projects/348715329010", + "quota_limit_value": "0", + "quota_limit": "GenerateContentRequestsPerMinutePerProjectPerRegion", + "quota_location": "us-east2", + "quota_metric": "generativelanguage.googleapis.com/generate_content_requests" + } + }, + { + "@type": "type.googleapis.com/google.rpc.Help", + "links": [ + { + "description": "Request a higher quota limit.", + "url": "https://cloud.google.com/docs/quota#requesting_higher_quota" + } + ] + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + expect( + () => VertexSerialization().parseGenerateContentResponse(decoded), + throwsA( + isA().having( + (e) => e.message, + 'message', + startsWith('Quota exceeded for quota metric'), + ), + ), + ); + }); + }); + + group('parses successful GenerateContentResponse', () { + test('with a basic reply', () async { + const response = ''' +{ + "candidates": [ + { + "content": { + "parts": [ + { + "text": "Mountain View, California, United States" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } + ], + "promptFeedback": { + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + VertexSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse( + [ + Candidate( + Content.model([ + TextPart('Mountain View, California, United States'), + ]), + [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.hateSpeech, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.harassment, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ], + null, + FinishReason.stop, + null, + ), + ], + PromptFeedback(null, null, [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating(HarmCategory.hateSpeech, HarmProbability.negligible), + SafetyRating(HarmCategory.harassment, HarmProbability.negligible), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ]), + ), + ), + ); + }); + + test('with a citation', () async { + const response = ''' +{ + "candidates": [ + { + "content": { + "parts": [ + { + "text": "placeholder" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ], + "citationMetadata": { + "citationSources": [ + { + "startIndex": 574, + "endIndex": 705, + "uri": "https://example.com/", + "license": "" + }, + { + "startIndex": 899, + "endIndex": 1026, + "uri": "https://example.com/", + "license": "" + }, + { + "startIndex": 899, + "endIndex": 1026 + }, + { + "uri": "https://example.com/", + "license": "" + }, + {} + ] + } + } + ], + "promptFeedback": { + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + VertexSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse( + [ + Candidate( + Content.model([TextPart('placeholder')]), + [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.hateSpeech, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.harassment, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ], + CitationMetadata([ + Citation(574, 705, Uri.https('example.com'), ''), + Citation(899, 1026, Uri.https('example.com'), ''), + ]), + FinishReason.stop, + null, + ), + ], + PromptFeedback(null, null, [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating(HarmCategory.hateSpeech, HarmProbability.negligible), + SafetyRating(HarmCategory.harassment, HarmProbability.negligible), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ]), + ), + ), + ); + }); + + test('with a vertex formatted citation', () async { + const response = ''' +{ + "candidates": [ + { + "content": { + "parts": [ + { + "text": "placeholder" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ], + "citationMetadata": { + "citations": [ + { + "startIndex": 574, + "endIndex": 705, + "uri": "https://example.com/", + "license": "" + }, + { + "startIndex": 899, + "endIndex": 1026, + "uri": "https://example.com/", + "license": "" + }, + { + "startIndex": 899, + "endIndex": 1026 + }, + { + "uri": "https://example.com/", + "license": "" + }, + {} + ] + } + } + ], + "promptFeedback": { + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + VertexSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse( + [ + Candidate( + Content.model([TextPart('placeholder')]), + [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.hateSpeech, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.harassment, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ], + CitationMetadata([ + Citation(574, 705, Uri.https('example.com'), ''), + Citation(899, 1026, Uri.https('example.com'), ''), + ]), + FinishReason.stop, + null, + ), + ], + PromptFeedback(null, null, [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating(HarmCategory.hateSpeech, HarmProbability.negligible), + SafetyRating(HarmCategory.harassment, HarmProbability.negligible), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ]), + ), + ), + ); + }); + + test('allows missing content', () async { + const response = ''' +{ + "candidates": [ + { + "finishReason": "SAFETY", + "index": 0, + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "LOW" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "MEDIUM" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } + ] +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + VertexSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse([ + Candidate( + Content(null, []), + [ + SafetyRating( + HarmCategory.sexuallyExplicit, + HarmProbability.negligible, + ), + SafetyRating( + HarmCategory.hateSpeech, HarmProbability.negligible), + SafetyRating( + HarmCategory.harassment, HarmProbability.negligible), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.negligible, + ), + ], + CitationMetadata([]), + FinishReason.safety, + null), + ], null), + ), + ); + }); + + test('response including usage metadata', () async { + const response = ''' +{ + "candidates": [{ + "content": { + "role": "model", + "parts": [{ + "text": "Here is a description of the image:" + }] + }, + "finishReason": "STOP" + }], + "usageMetadata": { + "promptTokenCount": 1837, + "candidatesTokenCount": 76, + "totalTokenCount": 1913, + "promptTokensDetails": [{ + "modality": "TEXT", + "tokenCount": 76 + }, { + "modality": "IMAGE", + "tokenCount": 1806 + }], + "candidatesTokensDetails": [{ + "modality": "TEXT", + "tokenCount": 76 + }] + } +} + '''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + VertexSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse.text, 'Here is a description of the image:'); + expect(generateContentResponse.usageMetadata?.totalTokenCount, 1913); + expect( + generateContentResponse + .usageMetadata?.promptTokensDetails?[1].modality, + ContentModality.image); + expect( + generateContentResponse + .usageMetadata?.promptTokensDetails?[1].tokenCount, + 1806); + expect( + generateContentResponse + .usageMetadata?.candidatesTokensDetails?.first.modality, + ContentModality.text); + expect( + generateContentResponse + .usageMetadata?.candidatesTokensDetails?.first.tokenCount, + 76); + }); + + test('countTokens with modality fields returned', () async { + const response = ''' +{ + "totalTokens": 1837, + "totalBillableCharacters": 117, + "promptTokensDetails": [{ + "modality": "IMAGE", + "tokenCount": 1806 + }, { + "modality": "TEXT", + "tokenCount": 31 + }] +} + '''; + final decoded = jsonDecode(response) as Object; + final countTokensResponse = + VertexSerialization().parseCountTokensResponse(decoded); + expect(countTokensResponse.totalTokens, 1837); + expect(countTokensResponse.promptTokensDetails?.first.modality, + ContentModality.image); + expect(countTokensResponse.promptTokensDetails?.first.tokenCount, 1806); + }); + + test('text getter joins content', () async { + const response = ''' +{ + "candidates": [ + { + "content": { + "parts": [ + { + "text": "Initial text" + }, + { + "functionCall": {"name": "someFunction", "args": {}} + }, + { + "text": " And more text" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0 + } + ] +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + VertexSerialization().parseGenerateContentResponse(decoded); + expect(generateContentResponse.text, 'Initial text And more text'); + expect(generateContentResponse.candidates.single.text, + 'Initial text And more text'); + }); + }); + + group('parses and throws error responses', () { + test('for invalid API key', () async { + const response = ''' +{ + "error": { + "code": 400, + "message": "API key not valid. Please pass a valid API key.", + "status": "INVALID_ARGUMENT", + "details": [ + { + "@type": "type.googleapis.com/google.rpc.ErrorInfo", + "reason": "API_KEY_INVALID", + "domain": "googleapis.com", + "metadata": { + "service": "generativelanguage.googleapis.com" + } + }, + { + "@type": "type.googleapis.com/google.rpc.DebugInfo", + "detail": "Invalid API key: AIzv00G7VmUCUeC-5OglO3hcXM" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final expectedThrow = throwsA( + isA().having( + (e) => e.message, + 'message', + 'API key not valid. Please pass a valid API key.', + ), + ); + expect(() => VertexSerialization().parseGenerateContentResponse(decoded), + expectedThrow); + expect(() => VertexSerialization().parseCountTokensResponse(decoded), + expectedThrow); + }); + + test('for unsupported user location', () async { + const response = r''' +{ + "error": { + "code": 400, + "message": "User location is not supported for the API use.", + "status": "FAILED_PRECONDITION", + "details": [ + { + "@type": "type.googleapis.com/google.rpc.DebugInfo", + "detail": "[ORIGINAL ERROR] generic::failed_precondition: User location is not supported for the API use. [google.rpc.error_details_ext] { message: \"User location is not supported for the API use.\" }" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final expectedThrow = throwsA( + isA().having( + (e) => e.message, + 'message', + 'User location is not supported for the API use.', + ), + ); + expect(() => VertexSerialization().parseGenerateContentResponse(decoded), + expectedThrow); + expect(() => VertexSerialization().parseCountTokensResponse(decoded), + expectedThrow); + }); + + test('for general server errors', () async { + const response = r''' +{ + "error": { + "code": 404, + "message": "models/unknown is not found for API version v1, or is not supported for GenerateContent. Call ListModels to see the list of available models and their supported methods.", + "status": "NOT_FOUND", + "details": [ + { + "@type": "type.googleapis.com/google.rpc.DebugInfo", + "detail": "[ORIGINAL ERROR] generic::not_found: models/unknown is not found for API version v1, or is not supported for GenerateContent. Call ListModels to see the list of available models and their supported methods. [google.rpc.error_details_ext] { message: \"models/unknown is not found for API version v1, or is not supported for GenerateContent. Call ListModels to see the list of available models and their supported methods.\" }" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final expectedThrow = throwsA( + isA().having( + (e) => e.message, + 'message', + startsWith( + 'models/unknown is not found for API version v1, ' + 'or is not supported for GenerateContent.', + ), + ), + ); + expect(() => VertexSerialization().parseGenerateContentResponse(decoded), + expectedThrow); + expect(() => VertexSerialization().parseCountTokensResponse(decoded), + expectedThrow); + }); + }); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/schema_test.dart b/packages/firebase_vertexai/firebase_ai/test/schema_test.dart new file mode 100644 index 000000000000..b6c691e0f97e --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/schema_test.dart @@ -0,0 +1,140 @@ +// 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. + +import 'package:firebase_ai/src/schema.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('Schema Tests', () { + // Test basic constructors and toJson() for primitive types + test('Schema.boolean', () { + final schema = + Schema.boolean(description: 'A boolean value', nullable: true); + expect(schema.type, SchemaType.boolean); + expect(schema.description, 'A boolean value'); + expect(schema.nullable, true); + expect(schema.toJson(), { + 'type': 'BOOLEAN', + 'description': 'A boolean value', + 'nullable': true, + }); + }); + + test('Schema.integer', () { + final schema = Schema.integer(format: 'int32'); + expect(schema.type, SchemaType.integer); + expect(schema.format, 'int32'); + expect(schema.toJson(), { + 'type': 'INTEGER', + 'format': 'int32', + }); + }); + + test('Schema.number', () { + final schema = Schema.number(format: 'double', nullable: false); + expect(schema.type, SchemaType.number); + expect(schema.format, 'double'); + expect(schema.nullable, false); + expect(schema.toJson(), { + 'type': 'NUMBER', + 'format': 'double', + 'nullable': false, + }); + }); + + test('Schema.string', () { + final schema = Schema.string(); + expect(schema.type, SchemaType.string); + expect(schema.toJson(), {'type': 'STRING'}); + }); + + test('Schema.enumString', () { + final schema = Schema.enumString(enumValues: ['value1', 'value2']); + expect(schema.type, SchemaType.string); + expect(schema.format, 'enum'); + expect(schema.enumValues, ['value1', 'value2']); + expect(schema.toJson(), { + 'type': 'STRING', + 'format': 'enum', + 'enum': ['value1', 'value2'], + }); + }); + + // Test constructors and toJson() for complex types + test('Schema.array', () { + final itemSchema = Schema.string(); + final schema = Schema.array(items: itemSchema); + expect(schema.type, SchemaType.array); + expect(schema.items, itemSchema); + expect(schema.toJson(), { + 'type': 'ARRAY', + 'items': {'type': 'STRING'}, + }); + }); + + test('Schema.object', () { + final properties = { + 'name': Schema.string(), + 'age': Schema.integer(), + }; + final schema = Schema.object( + properties: properties, + optionalProperties: ['age'], + ); + expect(schema.type, SchemaType.object); + expect(schema.properties, properties); + expect(schema.optionalProperties, ['age']); + expect(schema.toJson(), { + 'type': 'OBJECT', + 'properties': { + 'name': {'type': 'STRING'}, + 'age': {'type': 'INTEGER'}, + }, + 'required': ['name'], + }); + }); + + test('Schema.object with empty optionalProperties', () { + final properties = { + 'name': Schema.string(), + 'age': Schema.integer(), + }; + final schema = Schema.object( + properties: properties, + ); + expect(schema.type, SchemaType.object); + expect(schema.properties, properties); + expect(schema.toJson(), { + 'type': 'OBJECT', + 'properties': { + 'name': {'type': 'STRING'}, + 'age': {'type': 'INTEGER'}, + }, + 'required': ['name', 'age'], + }); + }); + + // Test SchemaType.toJson() + test('SchemaType.toJson', () { + expect(SchemaType.string.toJson(), 'STRING'); + expect(SchemaType.number.toJson(), 'NUMBER'); + expect(SchemaType.integer.toJson(), 'INTEGER'); + expect(SchemaType.boolean.toJson(), 'BOOLEAN'); + expect(SchemaType.array.toJson(), 'ARRAY'); + expect(SchemaType.object.toJson(), 'OBJECT'); + }); + + // Add more tests as needed to cover other scenarios and edge cases + }); +} diff --git a/packages/firebase_vertexai/firebase_ai/test/utils/matchers.dart b/packages/firebase_vertexai/firebase_ai/test/utils/matchers.dart new file mode 100644 index 000000000000..39c23188b677 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/utils/matchers.dart @@ -0,0 +1,100 @@ +// 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. +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:http/http.dart' as http; +import 'package:matcher/matcher.dart'; + +Matcher matchesPart(Part part) => switch (part) { + TextPart(text: final text) => + isA().having((p) => p.text, 'text', text), + InlineDataPart(mimeType: final mimeType, bytes: final bytes) => + isA() + .having((p) => p.mimeType, 'mimeType', mimeType) + .having((p) => p.bytes, 'bytes', bytes), + FileData(mimeType: final mimeType, fileUri: final fileUri) => + isA() + .having((p) => p.mimeType, 'mimeType', mimeType) + .having((p) => p.fileUri, 'fileUri', fileUri), + FunctionCall(name: final name, args: final args) => isA() + .having((p) => p.name, 'name', name) + .having((p) => p.args, 'args', args), + FunctionResponse(name: final name, response: final response) => + isA() + .having((p) => p.name, 'name', name) + .having((p) => p.response, 'args', response), + }; + +Matcher matchesContent(Content content) => isA() + .having((c) => c.role, 'role', content.role) + .having((c) => c.parts, 'parts', content.parts.map(matchesPart).toList()); + +Matcher matchesCandidate(Candidate candidate) => isA().having( + (c) => c.content, + 'content', + matchesContent(candidate.content), + ); + +Matcher matchesGenerateContentResponse(GenerateContentResponse response) => + isA() + .having( + (r) => r.candidates, + 'candidates', + response.candidates.map(matchesCandidate).toList(), + ) + .having( + (r) => r.promptFeedback, + 'promptFeedback', + response.promptFeedback == null + ? isNull + : matchesPromptFeedback(response.promptFeedback!), + ); + +Matcher matchesPromptFeedback( + PromptFeedback promptFeedback, +) => + isA() + .having((p) => p.blockReason, 'blockReason', promptFeedback.blockReason) + .having( + (p) => p.blockReasonMessage, + 'blockReasonMessage', + promptFeedback.blockReasonMessage, + ) + .having( + (p) => p.safetyRatings, + 'safetyRatings', + unorderedMatches( + promptFeedback.safetyRatings.map(matchesSafetyRating)), + ); + +Matcher matchesSafetyRating(SafetyRating safetyRating) => isA() + .having((s) => s.category, 'category', safetyRating.category) + .having((s) => s.probability, 'probability', safetyRating.probability); + +Matcher matchesCountTokensResponse(CountTokensResponse response) => + isA().having( + (r) => r.totalTokens, + 'totalTokens', + response.totalTokens, + ); + +Matcher matchesRequest(http.Request request) => isA() + .having((r) => r.headers, 'headers', request.headers) + .having((r) => r.method, 'method', request.method) + .having((r) => r.bodyBytes, 'bodyBytes', request.bodyBytes) + .having((r) => r.url, 'url', request.url); + +Matcher matchesBaseRequest(http.BaseRequest request) => isA() + .having((r) => r.headers, 'headers', request.headers) + .having((r) => r.method, 'method', request.method) + .having((r) => r.url, 'url', request.url); diff --git a/packages/firebase_vertexai/firebase_ai/test/utils/stub_client.dart b/packages/firebase_vertexai/firebase_ai/test/utils/stub_client.dart new file mode 100644 index 000000000000..fa0704316574 --- /dev/null +++ b/packages/firebase_vertexai/firebase_ai/test/utils/stub_client.dart @@ -0,0 +1,93 @@ +// 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. + +import 'dart:collection'; + +import 'package:firebase_ai/src/client.dart'; + +class ClientController { + final _client = _ControlledClient(); + ApiClient get client => _client; + + /// Run [body] and return [response] for a single call to + /// [ApiClient.streamRequest]. + /// + /// Check expectations for the request URI and JSON payload with the + /// [verifyRequest] callback. + Future checkRequest( + Future Function() body, { + required Map response, + void Function(Uri, Map)? verifyRequest, + }) async { + _client._requestExpectations.addLast(verifyRequest); + _client._responses.addLast([response]); + final result = await body(); + assert(_client._responses.isEmpty); + return result; + } + + /// Run [body] and return [responses] for a single call to + /// [ApiClient.streamRequest]. + /// + /// Check expectations for the request URI and JSON payload with the + /// [verifyRequest] callback. + Future checkStreamRequest( + Future Function() body, { + required Iterable> responses, + void Function(Uri, Map)? verifyRequest, + }) async { + _client._requestExpectations.addLast(verifyRequest); + _client._responses.addLast(responses.toList()); + final result = await body(); + assert(_client._responses.isEmpty); + return result; + } +} + +final class _ControlledClient implements ApiClient { + final _requestExpectations = + Queue)?>(); + final _responses = Queue>>(); + + @override + Future> makeRequest( + Uri uri, + Map body, + ) async { + _requestExpectations.removeFirst()?.call(uri, body); + return _responses.removeFirst().single; + } + + @override + Stream> streamRequest( + Uri uri, + Map body, + ) { + _requestExpectations.removeFirst()?.call(uri, body); + return Stream.fromIterable(_responses.removeFirst()); + } +} + +const Map arbitraryGenerateContentResponse = { + 'candidates': [ + { + 'content': { + 'role': 'model', + 'parts': [ + {'text': 'Some Response'}, + ], + }, + }, + ], +}; diff --git a/packages/firebase_vertexai/firebase_vertexai/example/.gitignore b/packages/firebase_vertexai/firebase_vertexai/example/.gitignore index 53bed76d8faa..0498b592dfa0 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/.gitignore +++ b/packages/firebase_vertexai/firebase_vertexai/example/.gitignore @@ -48,4 +48,3 @@ app.*.map.json firebase_options.dart google-services.json GoogleService-Info.plist -firebase.json diff --git a/packages/firebase_vertexai/firebase_vertexai/example/android/settings.gradle b/packages/firebase_vertexai/firebase_vertexai/example/android/settings.gradle index 40cbd22bb13b..9151bc043341 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/android/settings.gradle +++ b/packages/firebase_vertexai/firebase_vertexai/example/android/settings.gradle @@ -19,9 +19,6 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "7.3.0" apply false - // START: FlutterFire Configuration - id "com.google.gms.google-services" version "4.3.15" apply false - // END: FlutterFire Configuration id "org.jetbrains.kotlin.android" version "1.9.22" apply false } diff --git a/packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart b/packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart index f8d0788df899..e6d880e15427 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart +++ b/packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart @@ -38,77 +38,23 @@ void main() async { // await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); await Firebase.initializeApp(); await FirebaseAuth.instance.signInAnonymously(); - runApp(const GenerativeAISample()); -} -class GenerativeAISample extends StatefulWidget { - const GenerativeAISample({super.key}); + var vertexInstance = + FirebaseVertexAI.instanceFor(auth: FirebaseAuth.instance); + final model = vertexInstance.generativeModel(model: 'gemini-1.5-flash'); - @override - State createState() => _GenerativeAISampleState(); + runApp(GenerativeAISample(model: model)); } -class _GenerativeAISampleState extends State { - bool _useVertexBackend = false; - late GenerativeModel _currentModel; - late ImagenModel _currentImagenModel; - int _currentBottomNavIndex = 0; - - @override - void initState() { - super.initState(); - - _initializeModel(_useVertexBackend); - } - - void _initializeModel(bool useVertexBackend) { - if (useVertexBackend) { - final vertexInstance = - FirebaseVertexAI.instanceFor(auth: FirebaseAuth.instance); - _currentModel = vertexInstance.generativeModel(model: 'gemini-1.5-flash'); - _currentImagenModel = _initializeImagenModel(vertexInstance); - } else { - final googleAI = FirebaseVertexAI.googleAI(auth: FirebaseAuth.instance); - _currentModel = googleAI.generativeModel(model: 'gemini-2.0-flash'); - _currentImagenModel = _initializeImagenModel(googleAI); - } - } - - ImagenModel _initializeImagenModel(FirebaseVertexAI instance) { - var generationConfig = ImagenGenerationConfig( - numberOfImages: 1, - aspectRatio: ImagenAspectRatio.square1x1, - imageFormat: ImagenFormat.jpeg(compressionQuality: 75), - ); - return instance.imagenModel( - model: 'imagen-3.0-generate-002', - generationConfig: generationConfig, - safetySettings: ImagenSafetySettings( - ImagenSafetyFilterLevel.blockLowAndAbove, - ImagenPersonFilterLevel.allowAdult, - ), - ); - } +class GenerativeAISample extends StatelessWidget { + final GenerativeModel model; - void _toggleBackend(bool value) { - setState(() { - _useVertexBackend = value; - }); - _initializeModel(_useVertexBackend); - } - - void _onBottomNavTapped(int index) { - setState(() { - _currentBottomNavIndex = index; - }); - } + const GenerativeAISample({super.key, required this.model}); @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter + ${_useVertexBackend ? 'Vertex AI' : 'Google AI'}', - debugShowCheckedModeBanner: false, - themeMode: ThemeMode.dark, + title: 'Flutter + Vertex AI', theme: ThemeData( colorScheme: ColorScheme.fromSeed( brightness: Brightness.dark, @@ -116,204 +62,137 @@ class _GenerativeAISampleState extends State { ), useMaterial3: true, ), - home: HomeScreen( - key: ValueKey( - '${_useVertexBackend}_${_currentModel.hashCode}', - ), - model: _currentModel, - imagenModel: _currentImagenModel, - useVertexBackend: _useVertexBackend, - onBackendChanged: _toggleBackend, - selectedIndex: _currentBottomNavIndex, - onSelectedIndexChanged: _onBottomNavTapped, - ), + home: HomeScreen(model: model), ); } } class HomeScreen extends StatefulWidget { final GenerativeModel model; - final ImagenModel imagenModel; - final bool useVertexBackend; - final ValueChanged onBackendChanged; - final int selectedIndex; - final ValueChanged onSelectedIndexChanged; - - const HomeScreen({ - super.key, - required this.model, - required this.imagenModel, - required this.useVertexBackend, - required this.onBackendChanged, - required this.selectedIndex, - required this.onSelectedIndexChanged, - }); + const HomeScreen({super.key, required this.model}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { - void _onItemTapped(int index) { - widget.onSelectedIndexChanged(index); - } - -// Method to build the selected page on demand - Widget _buildSelectedPage( - int index, - GenerativeModel currentModel, - ImagenModel currentImagenModel, - bool useVertexBackend, - ) { - switch (index) { - case 0: - return ChatPage(title: 'Chat', model: currentModel); - case 1: - return AudioPage(title: 'Audio', model: currentModel); - case 2: - return TokenCountPage(title: 'Token Count', model: currentModel); - case 3: - // FunctionCallingPage initializes its own model as per original design - return FunctionCallingPage( + int _selectedIndex = 0; + + List get _pages => [ + // Build _pages dynamically + ChatPage(title: 'Chat', model: widget.model), + AudioPage(title: 'Audio', model: widget.model), + TokenCountPage(title: 'Token Count', model: widget.model), + const FunctionCallingPage( title: 'Function Calling', - useVertexBackend: useVertexBackend, - ); - 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: - return DocumentPage(title: 'Document Prompt', model: currentModel); - case 8: - return VideoPage(title: 'Video Prompt', model: currentModel); - case 9: - return BidiPage(title: 'Bidi Stream', model: currentModel); - default: - // Fallback to the first page in case of an unexpected index - return ChatPage(title: 'Chat', model: currentModel); - } + ), // function calling will initial its own model + ImagePromptPage(title: 'Image Prompt', model: widget.model), + ImagenPage(title: 'Imagen Model', model: widget.model), + SchemaPromptPage(title: 'Schema Prompt', model: widget.model), + DocumentPage(title: 'Document Prompt', model: widget.model), + VideoPage(title: 'Video Prompt', model: widget.model), + BidiPage(title: 'Bidi Stream', model: widget.model), + ]; + + void _onItemTapped(int index) { + setState(() { + _selectedIndex = index; + }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text( - 'Flutter + ${widget.useVertexBackend ? 'Vertex AI' : 'Google AI'}', - ), - actions: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Google AI', - style: TextStyle( - fontSize: 12, - color: widget.useVertexBackend - ? Theme.of(context) - .colorScheme - .onSurface - .withValues(alpha: 0.7) - : Theme.of(context).colorScheme.primary, - ), - ), - Switch( - value: widget.useVertexBackend, - onChanged: widget.onBackendChanged, - activeTrackColor: Colors.green.withValues(alpha: 0.5), - inactiveTrackColor: Colors.blueGrey.withValues(alpha: 0.5), - activeColor: Colors.green, - inactiveThumbColor: Colors.blueGrey, - ), - Text( - 'Vertex AI', - style: TextStyle( - fontSize: 12, - color: widget.useVertexBackend - ? Theme.of(context).colorScheme.primary - : Theme.of(context) - .colorScheme - .onSurface - .withValues(alpha: 0.7), - ), - ), - ], - ), - ), - ], + title: const Text('Flutter + Vertex AI'), ), body: Center( - child: _buildSelectedPage( - widget.selectedIndex, - widget.model, - widget.imagenModel, - widget.useVertexBackend, - ), + child: _pages.elementAt(_selectedIndex), ), bottomNavigationBar: BottomNavigationBar( - type: BottomNavigationBarType.fixed, - selectedFontSize: 10, - unselectedFontSize: 9, - selectedItemColor: Theme.of(context).colorScheme.primary, - unselectedItemColor: - Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7), - items: const [ + items: [ BottomNavigationBarItem( - icon: Icon(Icons.chat), + icon: Icon( + Icons.chat, + color: Theme.of(context).colorScheme.primary, + ), label: 'Chat', tooltip: 'Chat', ), BottomNavigationBarItem( - icon: Icon(Icons.mic), - label: 'Audio', + icon: Icon( + Icons.mic, + color: Theme.of(context).colorScheme.primary, + ), + label: 'Audio Prompt', tooltip: 'Audio Prompt', ), BottomNavigationBarItem( - icon: Icon(Icons.numbers), - label: 'Tokens', + icon: Icon( + Icons.numbers, + color: Theme.of(context).colorScheme.primary, + ), + label: 'Token Count', tooltip: 'Token Count', ), BottomNavigationBarItem( - icon: Icon(Icons.functions), - label: 'Functions', + icon: Icon( + Icons.functions, + color: Theme.of(context).colorScheme.primary, + ), + label: 'Function Calling', tooltip: 'Function Calling', ), BottomNavigationBarItem( - icon: Icon(Icons.image), - label: 'Image', + icon: Icon( + Icons.image, + color: Theme.of(context).colorScheme.primary, + ), + label: 'Image Prompt', tooltip: 'Image Prompt', ), BottomNavigationBarItem( - icon: Icon(Icons.image_search), - label: 'Imagen', + icon: Icon( + Icons.image_search, + color: Theme.of(context).colorScheme.primary, + ), + label: 'Imagen Model', tooltip: 'Imagen Model', ), BottomNavigationBarItem( - icon: Icon(Icons.schema), - label: 'Schema', + icon: Icon( + Icons.schema, + color: Theme.of(context).colorScheme.primary, + ), + label: 'Schema Prompt', tooltip: 'Schema Prompt', ), BottomNavigationBarItem( - icon: Icon(Icons.edit_document), - label: 'Document', + icon: Icon( + Icons.edit_document, + color: Theme.of(context).colorScheme.primary, + ), + label: 'Document Prompt', tooltip: 'Document Prompt', ), BottomNavigationBarItem( - icon: Icon(Icons.video_collection), - label: 'Video', + icon: Icon( + Icons.video_collection, + color: Theme.of(context).colorScheme.primary, + ), + label: 'Video Prompt', tooltip: 'Video Prompt', ), BottomNavigationBarItem( - icon: Icon(Icons.stream), - label: 'Bidi', + icon: Icon( + Icons.stream, + color: Theme.of(context).colorScheme.primary, + ), + label: 'Bidi Stream', tooltip: 'Bidi Stream', ), ], - currentIndex: widget.selectedIndex, + currentIndex: _selectedIndex, onTap: _onItemTapped, ), ); diff --git a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/bidi_page.dart b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/bidi_page.dart index 31b693c96208..6245824c144e 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/bidi_page.dart +++ b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/bidi_page.dart @@ -59,7 +59,7 @@ class _BidiPageState extends State { super.initState(); final config = LiveGenerationConfig( - speechConfig: SpeechConfig(voiceName: 'Fenrir'), + speechConfig: SpeechConfig(voice: Voice.fenrir), responseModalities: [ ResponseModalities.audio, ], diff --git a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/function_calling_page.dart b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/function_calling_page.dart index dce322c1822d..130afff5ce92 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/function_calling_page.dart +++ b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/function_calling_page.dart @@ -18,14 +18,9 @@ import 'package:firebase_auth/firebase_auth.dart'; import '../widgets/message_widget.dart'; class FunctionCallingPage extends StatefulWidget { - const FunctionCallingPage({ - super.key, - required this.title, - required this.useVertexBackend, - }); + const FunctionCallingPage({super.key, required this.title}); final String title; - final bool useVertexBackend; @override State createState() => _FunctionCallingPageState(); @@ -46,23 +41,14 @@ class _FunctionCallingPageState extends State { @override void initState() { super.initState(); - if (widget.useVertexBackend) { - var vertexAI = FirebaseVertexAI.instanceFor(auth: FirebaseAuth.instance); - _functionCallModel = vertexAI.generativeModel( - model: 'gemini-2.0-flash', - tools: [ - Tool.functionDeclarations([fetchWeatherTool]), - ], - ); - } else { - var googleAI = FirebaseVertexAI.googleAI(auth: FirebaseAuth.instance); - _functionCallModel = googleAI.generativeModel( - model: 'gemini-2.0-flash', - tools: [ - Tool.functionDeclarations([fetchWeatherTool]), - ], - ); - } + var vertex_instance = + FirebaseVertexAI.instanceFor(auth: FirebaseAuth.instance); + _functionCallModel = vertex_instance.generativeModel( + model: 'gemini-1.5-flash', + tools: [ + Tool.functionDeclarations([fetchWeatherTool]), + ], + ); } // This is a hypothetical API to return a fake weather data collection for @@ -160,7 +146,7 @@ class _FunctionCallingPageState extends State { _loading = true; }); final functionCallChat = _functionCallModel.startChat(); - const prompt = 'What is the weather like in Boston on 10/02 in year 2024?'; + const prompt = 'What is the weather like in Boston on 10/02 this year?'; // Send the message to the generative model. var response = await functionCallChat.sendMessage( diff --git a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/imagen_page.dart b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/imagen_page.dart index 0ab750b13fef..04f89e4f1253 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/imagen_page.dart +++ b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/imagen_page.dart @@ -25,7 +25,7 @@ class ImagenPage extends StatefulWidget { }); final String title; - final ImagenModel model; + final GenerativeModel model; @override State createState() => _ImagenPageState(); @@ -37,6 +37,26 @@ class _ImagenPageState extends State { final FocusNode _textFieldFocus = FocusNode(); final List _generatedContent = []; bool _loading = false; + late final ImagenModel _imagenModel; + + @override + void initState() { + super.initState(); + var generationConfig = ImagenGenerationConfig( + negativePrompt: 'frog', + numberOfImages: 1, + aspectRatio: ImagenAspectRatio.square1x1, + imageFormat: ImagenFormat.jpeg(compressionQuality: 75), + ); + _imagenModel = FirebaseVertexAI.instance.imagenModel( + model: 'imagen-3.0-generate-001', + generationConfig: generationConfig, + safetySettings: ImagenSafetySettings( + ImagenSafetyFilterLevel.blockLowAndAbove, + ImagenPersonFilterLevel.allowAdult, + ), + ); + } void _scrollDown() { WidgetsBinding.instance.addPostFrameCallback( @@ -134,7 +154,7 @@ class _ImagenPageState extends State { }); try { - var response = await widget.model.generateImages(prompt); + var response = await _imagenModel.generateImages(prompt); if (response.images.isNotEmpty) { var imagenImage = response.images[0]; @@ -166,7 +186,7 @@ class _ImagenPageState extends State { // }); // var gcsUrl = 'gs://vertex-ai-example-ef5a2.appspot.com/imagen'; - // var response = await widget.model.generateImagesGCS(prompt, gcsUrl); + // var response = await _imagenModel.generateImagesGCS(prompt, gcsUrl); // if (response.images.isNotEmpty) { // var imagenImage = response.images[0]; diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/firebase_vertexai.dart b/packages/firebase_vertexai/firebase_vertexai/lib/firebase_vertexai.dart index 694470ceef23..1ff7f50478f7 100644 --- a/packages/firebase_vertexai/firebase_vertexai/lib/firebase_vertexai.dart +++ b/packages/firebase_vertexai/firebase_vertexai/lib/firebase_vertexai.dart @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -export 'src/api.dart' +import 'package:firebase_ai/firebase_ai.dart' + show FirebaseAIException, FirebaseAISdkException; + +export 'package:firebase_ai/firebase_ai.dart' show BlockReason, Candidate, @@ -29,47 +32,34 @@ export 'src/api.dart' PromptFeedback, SafetyRating, SafetySetting, - // TODO(cynthiajiang) remove in next breaking change. - TaskType, - UsageMetadata; -export 'src/base_model.dart' - show GenerativeModel, ImagenModel, LiveGenerativeModel; -export 'src/chat.dart' show ChatSession, StartChatExtension; -export 'src/content.dart' - show + UsageMetadata, + GenerativeModel, + ImagenModel, + LiveGenerativeModel, + ChatSession, + StartChatExtension, Content, InlineDataPart, FileData, FunctionCall, FunctionResponse, Part, - TextPart; -export 'src/error.dart' - show - VertexAIException, - VertexAISdkException, + TextPart, InvalidApiKey, ServerException, - UnsupportedUserLocation; -export 'src/firebase_vertexai.dart' show FirebaseVertexAI; -export 'src/function_calling.dart' - show + UnsupportedUserLocation, FunctionCallingConfig, FunctionCallingMode, FunctionDeclaration, Tool, - ToolConfig; -export 'src/imagen_api.dart' - show + ToolConfig, ImagenSafetySettings, ImagenFormat, ImagenSafetyFilterLevel, ImagenPersonFilterLevel, ImagenGenerationConfig, - ImagenAspectRatio; -export 'src/imagen_content.dart' show ImagenInlineImage; -export 'src/live_api.dart' - show + ImagenAspectRatio, + ImagenInlineImage, LiveGenerationConfig, SpeechConfig, ResponseModalities, @@ -77,6 +67,18 @@ export 'src/live_api.dart' LiveServerContent, LiveServerToolCall, LiveServerToolCallCancellation, - LiveServerResponse; -export 'src/live_session.dart' show LiveSession; -export 'src/schema.dart' show Schema, SchemaType; + LiveServerResponse, + LiveSession, + Schema, + SchemaType; +export 'src/firebase_vertexai.dart' show FirebaseVertexAI; + +/// Exception thrown when generating content fails. +typedef VertexAIException = FirebaseAIException; + +/// Exception indicating a stale package version or implementation bug. +/// +/// This exception indicates a likely problem with the SDK implementation such +/// as an inability to parse a new response format. Resolution paths may include +/// updating to a new version of the SDK, or filing an issue. +typedef VertexAISdkException = FirebaseAISdkException; diff --git a/packages/firebase_vertexai/firebase_vertexai/lib/src/firebase_vertexai.dart b/packages/firebase_vertexai/firebase_vertexai/lib/src/firebase_vertexai.dart index d9f2e7b1610f..fbb27f622645 100644 --- a/packages/firebase_vertexai/firebase_vertexai/lib/src/firebase_vertexai.dart +++ b/packages/firebase_vertexai/firebase_vertexai/lib/src/firebase_vertexai.dart @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'package:firebase_ai/firebase_ai.dart'; +// ignore: implementation_imports +import 'package:firebase_ai/src/base_model.dart' + show createGenerativeModel, createLiveGenerativeModel, createImagenModel; import 'package:firebase_app_check/firebase_app_check.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; @@ -19,9 +23,6 @@ import 'package:firebase_core_platform_interface/firebase_core_platform_interfac show FirebasePluginPlatform; import 'package:meta/meta.dart'; -import '../firebase_vertexai.dart'; -import 'base_model.dart'; - const _defaultLocation = 'us-central1'; /// The entrypoint for [FirebaseVertexAI]. @@ -69,13 +70,13 @@ class FirebaseVertexAI extends FirebasePluginPlatform { FirebaseAuth? auth, String? location, }) => - vertexAI(app: app, appCheck: appCheck, auth: auth, location: location); + _vertexAI(app: app, appCheck: appCheck, auth: auth, location: location); /// Returns an instance using a specified [FirebaseApp]. /// /// If [app] is not provided, the default Firebase app will be used. /// If pass in [appCheck], request session will get protected from abusing. - static FirebaseVertexAI vertexAI({ + static FirebaseVertexAI _vertexAI({ FirebaseApp? app, FirebaseAppCheck? appCheck, FirebaseAuth? auth, @@ -102,34 +103,6 @@ class FirebaseVertexAI extends FirebasePluginPlatform { return newInstance; } - /// Returns an instance using a specified [FirebaseApp]. - /// - /// If [app] is not provided, the default Firebase app will be used. - /// If pass in [appCheck], request session will get protected from abusing. - static FirebaseVertexAI googleAI({ - FirebaseApp? app, - FirebaseAppCheck? appCheck, - FirebaseAuth? auth, - }) { - app ??= Firebase.app(); - var instanceKey = '${app.name}::googleai'; - - if (_cachedInstances.containsKey(instanceKey)) { - return _cachedInstances[instanceKey]!; - } - - FirebaseVertexAI newInstance = FirebaseVertexAI._( - app: app, - location: _defaultLocation, - appCheck: appCheck, - auth: auth, - useVertexBackend: false, - ); - _cachedInstances[instanceKey] = newInstance; - - return newInstance; - } - /// Create a [GenerativeModel] backed by the generative model named [model]. /// /// The [model] argument can be a model name (such as `'gemini-pro'`) or a diff --git a/packages/firebase_vertexai/firebase_vertexai/pubspec.yaml b/packages/firebase_vertexai/firebase_vertexai/pubspec.yaml index 18e2905b85e6..9dcb087392bd 100644 --- a/packages/firebase_vertexai/firebase_vertexai/pubspec.yaml +++ b/packages/firebase_vertexai/firebase_vertexai/pubspec.yaml @@ -20,6 +20,7 @@ environment: flutter: ">=3.16.0" dependencies: + firebase_ai: ^0.1.0 firebase_app_check: ^0.3.2+5 firebase_auth: ^5.5.2 firebase_core: ^3.13.0 @@ -37,3 +38,7 @@ dev_dependencies: matcher: ^0.12.16 mockito: ^5.0.0 plugin_platform_interface: ^2.1.3 + +dependency_overrides: + firebase_ai: + path: ../firebase_ai diff --git a/packages/firebase_vertexai/firebase_vertexai/test/base_model_test.dart b/packages/firebase_vertexai/firebase_vertexai/test/base_model_test.dart index 0a886841ed83..456111126c85 100644 --- a/packages/firebase_vertexai/firebase_vertexai/test/base_model_test.dart +++ b/packages/firebase_vertexai/firebase_vertexai/test/base_model_test.dart @@ -11,12 +11,12 @@ // 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:'; + +import 'package:firebase_ai/src/base_model.dart'; +import 'package:firebase_ai/src/client.dart'; import 'package:firebase_app_check/firebase_app_check.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; -import 'package:firebase_vertexai/src/base_model.dart'; -import 'package:firebase_vertexai/src/client.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; diff --git a/packages/firebase_vertexai/firebase_vertexai/test/chat_test.dart b/packages/firebase_vertexai/firebase_vertexai/test/chat_test.dart index e0655978e898..026d14dca580 100644 --- a/packages/firebase_vertexai/firebase_vertexai/test/chat_test.dart +++ b/packages/firebase_vertexai/firebase_vertexai/test/chat_test.dart @@ -11,9 +11,10 @@ // 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/src/base_model.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_vertexai/firebase_vertexai.dart'; -import 'package:firebase_vertexai/src/base_model.dart'; import 'package:flutter_test/flutter_test.dart'; import 'mock.dart'; diff --git a/packages/firebase_vertexai/firebase_vertexai/test/content_test.dart b/packages/firebase_vertexai/firebase_vertexai/test/content_test.dart index 5aa62fc32aa0..21db7a03f852 100644 --- a/packages/firebase_vertexai/firebase_vertexai/test/content_test.dart +++ b/packages/firebase_vertexai/firebase_vertexai/test/content_test.dart @@ -15,8 +15,9 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:firebase_vertexai/src/content.dart'; -import 'package:firebase_vertexai/src/error.dart'; +import 'package:firebase_ai/src/content.dart'; +import 'package:firebase_vertexai/firebase_vertexai.dart' + show VertexAISdkException; import 'package:flutter_test/flutter_test.dart'; // Mock google_ai classes (if needed) diff --git a/packages/firebase_vertexai/firebase_vertexai/test/error_test.dart b/packages/firebase_vertexai/firebase_vertexai/test/error_test.dart index f548c7ad97df..83bff112adbe 100644 --- a/packages/firebase_vertexai/firebase_vertexai/test/error_test.dart +++ b/packages/firebase_vertexai/firebase_vertexai/test/error_test.dart @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:firebase_vertexai/src/error.dart'; +import 'package:firebase_ai/src/error.dart'; +import 'package:firebase_vertexai/firebase_vertexai.dart' + show VertexAIException, VertexAISdkException; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -62,7 +64,7 @@ void main() { 'SDK failed to parse response.\n' 'This indicates a problem with the Vertex AI in Firebase SDK. ' 'Try updating to the latest version ' - '(https://pub.dev/packages/firebase_vertexai/versions), ' + '(https://pub.dev/packages/firebase_ai/versions), ' 'or file an issue at ' 'https://github.com/firebase/flutterfire/issues.'); }); diff --git a/packages/firebase_vertexai/firebase_vertexai/test/google_ai_generative_model_test.dart b/packages/firebase_vertexai/firebase_vertexai/test/google_ai_generative_model_test.dart index 5440175503f1..9be8a316a11d 100644 --- a/packages/firebase_vertexai/firebase_vertexai/test/google_ai_generative_model_test.dart +++ b/packages/firebase_vertexai/firebase_vertexai/test/google_ai_generative_model_test.dart @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'package:firebase_ai/src/base_model.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_vertexai/firebase_vertexai.dart'; -import 'package:firebase_vertexai/src/base_model.dart'; import 'package:flutter_test/flutter_test.dart'; import 'mock.dart'; diff --git a/packages/firebase_vertexai/firebase_vertexai/test/google_ai_response_parsing_test.dart b/packages/firebase_vertexai/firebase_vertexai/test/google_ai_response_parsing_test.dart index 1005e6dd0555..76dc0adebf65 100644 --- a/packages/firebase_vertexai/firebase_vertexai/test/google_ai_response_parsing_test.dart +++ b/packages/firebase_vertexai/firebase_vertexai/test/google_ai_response_parsing_test.dart @@ -14,8 +14,8 @@ import 'dart:convert'; +import 'package:firebase_ai/src/developer/api.dart'; import 'package:firebase_vertexai/firebase_vertexai.dart'; -import 'package:firebase_vertexai/src/developer/api.dart'; import 'package:flutter_test/flutter_test.dart'; import 'utils/matchers.dart'; diff --git a/packages/firebase_vertexai/firebase_vertexai/test/imagen_test.dart b/packages/firebase_vertexai/firebase_vertexai/test/imagen_test.dart index d030e6f89495..4bd7ae5b763a 100644 --- a/packages/firebase_vertexai/firebase_vertexai/test/imagen_test.dart +++ b/packages/firebase_vertexai/firebase_vertexai/test/imagen_test.dart @@ -15,8 +15,8 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:firebase_vertexai/src/error.dart'; -import 'package:firebase_vertexai/src/imagen_content.dart'; +import 'package:firebase_ai/src/error.dart'; +import 'package:firebase_ai/src/imagen_content.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/packages/firebase_vertexai/firebase_vertexai/test/live_test.dart b/packages/firebase_vertexai/firebase_vertexai/test/live_test.dart index 460cbfa69fb6..4852b52adf4b 100644 --- a/packages/firebase_vertexai/firebase_vertexai/test/live_test.dart +++ b/packages/firebase_vertexai/firebase_vertexai/test/live_test.dart @@ -13,9 +13,10 @@ // limitations under the License. import 'dart:typed_data'; -import 'package:firebase_vertexai/src/content.dart'; -import 'package:firebase_vertexai/src/error.dart'; -import 'package:firebase_vertexai/src/live_api.dart'; +import 'package:firebase_ai/src/content.dart'; +import 'package:firebase_ai/src/live_api.dart'; +import 'package:firebase_vertexai/firebase_vertexai.dart' + show VertexAISdkException; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/packages/firebase_vertexai/firebase_vertexai/test/model_test.dart b/packages/firebase_vertexai/firebase_vertexai/test/model_test.dart index 86e20ff99421..a0727913a68b 100644 --- a/packages/firebase_vertexai/firebase_vertexai/test/model_test.dart +++ b/packages/firebase_vertexai/firebase_vertexai/test/model_test.dart @@ -11,9 +11,10 @@ // 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/src/base_model.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_vertexai/firebase_vertexai.dart'; -import 'package:firebase_vertexai/src/base_model.dart'; import 'package:flutter_test/flutter_test.dart'; import 'mock.dart'; diff --git a/packages/firebase_vertexai/firebase_vertexai/test/response_parsing_test.dart b/packages/firebase_vertexai/firebase_vertexai/test/response_parsing_test.dart index 505031344b00..97b2d580877c 100644 --- a/packages/firebase_vertexai/firebase_vertexai/test/response_parsing_test.dart +++ b/packages/firebase_vertexai/firebase_vertexai/test/response_parsing_test.dart @@ -14,9 +14,9 @@ import 'dart:convert'; +import 'package:firebase_ai/src/api.dart'; +import 'package:firebase_ai/src/error.dart'; import 'package:firebase_vertexai/firebase_vertexai.dart'; -import 'package:firebase_vertexai/src/api.dart'; -import 'package:firebase_vertexai/src/error.dart'; import 'package:flutter_test/flutter_test.dart'; import 'utils/matchers.dart'; diff --git a/packages/firebase_vertexai/firebase_vertexai/test/schema_test.dart b/packages/firebase_vertexai/firebase_vertexai/test/schema_test.dart index cf66017c87d1..b6c691e0f97e 100644 --- a/packages/firebase_vertexai/firebase_vertexai/test/schema_test.dart +++ b/packages/firebase_vertexai/firebase_vertexai/test/schema_test.dart @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:firebase_vertexai/src/schema.dart'; +import 'package:firebase_ai/src/schema.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/packages/firebase_vertexai/firebase_vertexai/test/utils/stub_client.dart b/packages/firebase_vertexai/firebase_vertexai/test/utils/stub_client.dart index 11b450f85c37..fa0704316574 100644 --- a/packages/firebase_vertexai/firebase_vertexai/test/utils/stub_client.dart +++ b/packages/firebase_vertexai/firebase_vertexai/test/utils/stub_client.dart @@ -14,7 +14,7 @@ import 'dart:collection'; -import 'package:firebase_vertexai/src/client.dart'; +import 'package:firebase_ai/src/client.dart'; class ClientController { final _client = _ControlledClient(); From 74f9775f2ef237a5292c43e90c277a1bcc79f9a3 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 5 May 2025 20:47:49 +0000 Subject: [PATCH 02/13] Move to new directory --- .../firebase_ai/CHANGELOG.md | 0 .../firebase_ai/LICENSE | 0 .../firebase_ai/README.md | 0 .../firebase_ai/example/.gitignore | 0 .../firebase_ai/example/.metadata | 0 .../firebase_ai/example/README.md | 0 .../firebase_ai/example/analysis_options.yaml | 0 .../firebase_ai/example/android/.gitignore | 0 .../firebase_ai/example/android/app/build.gradle | 0 .../android/app/src/debug/AndroidManifest.xml | 0 .../android/app/src/main/AndroidManifest.xml | 0 .../main/kotlin/com/example/example/MainActivity.kt | 0 .../src/main/res/drawable-v21/launch_background.xml | 0 .../app/src/main/res/drawable/launch_background.xml | 0 .../app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../app/src/main/res/values-night/styles.xml | 0 .../android/app/src/main/res/values/styles.xml | 0 .../android/app/src/profile/AndroidManifest.xml | 0 .../firebase_ai/example/android/build.gradle | 0 .../firebase_ai/example/android/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 .../firebase_ai/example/android/settings.gradle | 0 .../example/assets/documents/gemini_summary.pdf | Bin .../firebase_ai/example/assets/images/cat.jpg | Bin .../firebase_ai/example/assets/images/scones.jpg | Bin .../firebase_ai/example/assets/videos/landscape.mp4 | Bin .../firebase_ai/example/ios/.gitignore | 0 .../example/ios/Flutter/AppFrameworkInfo.plist | 0 .../firebase_ai/example/ios/Flutter/Debug.xcconfig | 0 .../example/ios/Flutter/Release.xcconfig | 0 .../firebase_ai/example/ios/Podfile | 0 .../example/ios/Runner.xcodeproj/project.pbxproj | 0 .../project.xcworkspace/contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../xcshareddata/xcschemes/Runner.xcscheme | 0 .../ios/Runner.xcworkspace/contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../example/ios/Runner/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../AppIcon.appiconset/Icon-App-1024x1024@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin .../AppIcon.appiconset/Icon-App-83.5x83.5@2x.png | Bin .../LaunchImage.imageset/Contents.json | 0 .../LaunchImage.imageset/LaunchImage.png | Bin .../LaunchImage.imageset/LaunchImage@2x.png | Bin .../LaunchImage.imageset/LaunchImage@3x.png | Bin .../Assets.xcassets/LaunchImage.imageset/README.md | 0 .../ios/Runner/Base.lproj/LaunchScreen.storyboard | 0 .../example/ios/Runner/Base.lproj/Main.storyboard | 0 .../firebase_ai/example/ios/Runner/Info.plist | 0 .../example/ios/Runner/Runner-Bridging-Header.h | 0 .../example/ios/firebase_app_id_file.json | 0 .../firebase_ai/example/lib/main.dart | 0 .../firebase_ai/example/lib/pages/audio_page.dart | 0 .../firebase_ai/example/lib/pages/bidi_page.dart | 0 .../firebase_ai/example/lib/pages/chat_page.dart | 0 .../firebase_ai/example/lib/pages/document.dart | 0 .../example/lib/pages/function_calling_page.dart | 0 .../example/lib/pages/image_prompt_page.dart | 0 .../firebase_ai/example/lib/pages/imagen_page.dart | 0 .../firebase_ai/example/lib/pages/schema_page.dart | 0 .../example/lib/pages/token_count_page.dart | 0 .../firebase_ai/example/lib/pages/video_page.dart | 0 .../firebase_ai/example/lib/utils/audio_player.dart | 0 .../example/lib/utils/audio_recorder.dart | 0 .../example/lib/widgets/message_widget.dart | 0 .../firebase_ai/example/macos/.gitignore | 0 .../example/macos/Flutter/Flutter-Debug.xcconfig | 0 .../example/macos/Flutter/Flutter-Release.xcconfig | 0 .../firebase_ai/example/macos/Podfile | 0 .../example/macos/Runner.xcodeproj/project.pbxproj | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/xcschemes/Runner.xcscheme | 0 .../Runner.xcworkspace/contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../example/macos/Runner/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../AppIcon.appiconset/app_icon_1024.png | Bin .../AppIcon.appiconset/app_icon_128.png | Bin .../AppIcon.appiconset/app_icon_16.png | Bin .../AppIcon.appiconset/app_icon_256.png | Bin .../AppIcon.appiconset/app_icon_32.png | Bin .../AppIcon.appiconset/app_icon_512.png | Bin .../AppIcon.appiconset/app_icon_64.png | Bin .../example/macos/Runner/Base.lproj/MainMenu.xib | 0 .../example/macos/Runner/Configs/AppInfo.xcconfig | 0 .../example/macos/Runner/Configs/Debug.xcconfig | 0 .../example/macos/Runner/Configs/Release.xcconfig | 0 .../example/macos/Runner/Configs/Warnings.xcconfig | 0 .../example/macos/Runner/DebugProfile.entitlements | 0 .../firebase_ai/example/macos/Runner/Info.plist | 0 .../example/macos/Runner/MainFlutterWindow.swift | 0 .../example/macos/Runner/Release.entitlements | 0 .../example/macos/firebase_app_id_file.json | 0 .../firebase_ai/example/pubspec.yaml | 0 .../firebase_ai/example/web/favicon.png | Bin .../firebase_ai/example/web/icons/Icon-192.png | Bin .../firebase_ai/example/web/icons/Icon-512.png | Bin .../example/web/icons/Icon-maskable-192.png | Bin .../example/web/icons/Icon-maskable-512.png | Bin .../firebase_ai/example/web/index.html | 0 .../firebase_ai/example/web/manifest.json | 0 .../firebase_ai/lib/firebase_ai.dart | 0 .../firebase_ai/lib/src/api.dart | 0 .../firebase_ai/lib/src/base_model.dart | 0 .../firebase_ai/lib/src/chat.dart | 0 .../firebase_ai/lib/src/client.dart | 0 .../firebase_ai/lib/src/content.dart | 0 .../firebase_ai/lib/src/developer/api.dart | 0 .../firebase_ai/lib/src/error.dart | 0 .../firebase_ai/lib/src/firebase_ai.dart | 0 .../firebase_ai/lib/src/function_calling.dart | 0 .../firebase_ai/lib/src/generative_model.dart | 0 .../firebase_ai/lib/src/imagen_api.dart | 0 .../firebase_ai/lib/src/imagen_content.dart | 0 .../firebase_ai/lib/src/imagen_model.dart | 0 .../firebase_ai/lib/src/live_api.dart | 0 .../firebase_ai/lib/src/live_model.dart | 0 .../firebase_ai/lib/src/live_session.dart | 0 .../firebase_ai/lib/src/schema.dart | 0 .../firebase_ai/lib/src/utils/mutex.dart | 0 .../firebase_ai/lib/src/vertex_version.dart | 0 .../firebase_ai/pubspec.yaml | 0 .../firebase_ai/test/base_model_test.dart | 0 .../firebase_ai/test/chat_test.dart | 0 .../firebase_ai/test/content_test.dart | 0 .../firebase_ai/test/error_test.dart | 0 .../firebase_ai/test/firebase_vertexai_test.dart | 0 .../test/google_ai_generative_model_test.dart | 0 .../test/google_ai_response_parsing_test.dart | 0 .../firebase_ai/test/imagen_test.dart | 0 .../firebase_ai/test/live_test.dart | 0 .../firebase_ai/test/mock.dart | 0 .../firebase_ai/test/model_test.dart | 0 .../firebase_ai/test/response_parsing_test.dart | 0 .../firebase_ai/test/schema_test.dart | 0 .../firebase_ai/test/utils/matchers.dart | 0 .../firebase_ai/test/utils/stub_client.dart | 0 .../firebase_vertexai/pubspec.yaml | 2 +- 157 files changed, 1 insertion(+), 1 deletion(-) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/CHANGELOG.md (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/LICENSE (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/README.md (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/.gitignore (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/.metadata (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/README.md (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/analysis_options.yaml (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/.gitignore (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/build.gradle (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/debug/AndroidManifest.xml (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/main/AndroidManifest.xml (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/main/res/drawable-v21/launch_background.xml (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/main/res/drawable/launch_background.xml (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/main/res/values-night/styles.xml (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/main/res/values/styles.xml (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/app/src/profile/AndroidManifest.xml (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/build.gradle (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/gradle.properties (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/gradle/wrapper/gradle-wrapper.properties (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/android/settings.gradle (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/assets/documents/gemini_summary.pdf (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/assets/images/cat.jpg (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/assets/images/scones.jpg (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/assets/videos/landscape.mp4 (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/.gitignore (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Flutter/Debug.xcconfig (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Flutter/Release.xcconfig (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Podfile (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner.xcworkspace/contents.xcworkspacedata (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/AppDelegate.swift (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Base.lproj/LaunchScreen.storyboard (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Base.lproj/Main.storyboard (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Info.plist (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/Runner/Runner-Bridging-Header.h (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/ios/firebase_app_id_file.json (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/main.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/pages/audio_page.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/pages/bidi_page.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/pages/chat_page.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/pages/document.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/pages/function_calling_page.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/pages/image_prompt_page.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/pages/imagen_page.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/pages/schema_page.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/pages/token_count_page.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/pages/video_page.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/utils/audio_player.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/utils/audio_recorder.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/lib/widgets/message_widget.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/.gitignore (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Flutter/Flutter-Debug.xcconfig (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Flutter/Flutter-Release.xcconfig (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Podfile (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner.xcworkspace/contents.xcworkspacedata (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/AppDelegate.swift (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Base.lproj/MainMenu.xib (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Configs/AppInfo.xcconfig (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Configs/Debug.xcconfig (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Configs/Release.xcconfig (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Configs/Warnings.xcconfig (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/DebugProfile.entitlements (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Info.plist (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/MainFlutterWindow.swift (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/Runner/Release.entitlements (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/macos/firebase_app_id_file.json (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/pubspec.yaml (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/web/favicon.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/web/icons/Icon-192.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/web/icons/Icon-512.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/web/icons/Icon-maskable-192.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/web/icons/Icon-maskable-512.png (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/web/index.html (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/example/web/manifest.json (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/firebase_ai.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/api.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/base_model.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/chat.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/client.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/content.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/developer/api.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/error.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/firebase_ai.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/function_calling.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/generative_model.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/imagen_api.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/imagen_content.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/imagen_model.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/live_api.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/live_model.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/live_session.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/schema.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/utils/mutex.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/lib/src/vertex_version.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/pubspec.yaml (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/base_model_test.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/chat_test.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/content_test.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/error_test.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/firebase_vertexai_test.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/google_ai_generative_model_test.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/google_ai_response_parsing_test.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/imagen_test.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/live_test.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/mock.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/model_test.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/response_parsing_test.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/schema_test.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/utils/matchers.dart (100%) rename packages/{firebase_vertexai => firebase_ai}/firebase_ai/test/utils/stub_client.dart (100%) diff --git a/packages/firebase_vertexai/firebase_ai/CHANGELOG.md b/packages/firebase_ai/firebase_ai/CHANGELOG.md similarity index 100% rename from packages/firebase_vertexai/firebase_ai/CHANGELOG.md rename to packages/firebase_ai/firebase_ai/CHANGELOG.md diff --git a/packages/firebase_vertexai/firebase_ai/LICENSE b/packages/firebase_ai/firebase_ai/LICENSE similarity index 100% rename from packages/firebase_vertexai/firebase_ai/LICENSE rename to packages/firebase_ai/firebase_ai/LICENSE diff --git a/packages/firebase_vertexai/firebase_ai/README.md b/packages/firebase_ai/firebase_ai/README.md similarity index 100% rename from packages/firebase_vertexai/firebase_ai/README.md rename to packages/firebase_ai/firebase_ai/README.md diff --git a/packages/firebase_vertexai/firebase_ai/example/.gitignore b/packages/firebase_ai/firebase_ai/example/.gitignore similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/.gitignore rename to packages/firebase_ai/firebase_ai/example/.gitignore diff --git a/packages/firebase_vertexai/firebase_ai/example/.metadata b/packages/firebase_ai/firebase_ai/example/.metadata similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/.metadata rename to packages/firebase_ai/firebase_ai/example/.metadata diff --git a/packages/firebase_vertexai/firebase_ai/example/README.md b/packages/firebase_ai/firebase_ai/example/README.md similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/README.md rename to packages/firebase_ai/firebase_ai/example/README.md diff --git a/packages/firebase_vertexai/firebase_ai/example/analysis_options.yaml b/packages/firebase_ai/firebase_ai/example/analysis_options.yaml similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/analysis_options.yaml rename to packages/firebase_ai/firebase_ai/example/analysis_options.yaml diff --git a/packages/firebase_vertexai/firebase_ai/example/android/.gitignore b/packages/firebase_ai/firebase_ai/example/android/.gitignore similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/.gitignore rename to packages/firebase_ai/firebase_ai/example/android/.gitignore diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/build.gradle b/packages/firebase_ai/firebase_ai/example/android/app/build.gradle similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/build.gradle rename to packages/firebase_ai/firebase_ai/example/android/app/build.gradle diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/debug/AndroidManifest.xml b/packages/firebase_ai/firebase_ai/example/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/debug/AndroidManifest.xml rename to packages/firebase_ai/firebase_ai/example/android/app/src/debug/AndroidManifest.xml diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/AndroidManifest.xml b/packages/firebase_ai/firebase_ai/example/android/app/src/main/AndroidManifest.xml similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/main/AndroidManifest.xml rename to packages/firebase_ai/firebase_ai/example/android/app/src/main/AndroidManifest.xml diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/packages/firebase_ai/firebase_ai/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt rename to packages/firebase_ai/firebase_ai/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/firebase_ai/firebase_ai/example/android/app/src/main/res/drawable-v21/launch_background.xml similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/drawable-v21/launch_background.xml rename to packages/firebase_ai/firebase_ai/example/android/app/src/main/res/drawable-v21/launch_background.xml diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/drawable/launch_background.xml b/packages/firebase_ai/firebase_ai/example/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/drawable/launch_background.xml rename to packages/firebase_ai/firebase_ai/example/android/app/src/main/res/drawable/launch_background.xml diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/firebase_ai/firebase_ai/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to packages/firebase_ai/firebase_ai/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/firebase_ai/firebase_ai/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to packages/firebase_ai/firebase_ai/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/firebase_ai/firebase_ai/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to packages/firebase_ai/firebase_ai/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/firebase_ai/firebase_ai/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to packages/firebase_ai/firebase_ai/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/firebase_ai/firebase_ai/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to packages/firebase_ai/firebase_ai/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/values-night/styles.xml b/packages/firebase_ai/firebase_ai/example/android/app/src/main/res/values-night/styles.xml similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/values-night/styles.xml rename to packages/firebase_ai/firebase_ai/example/android/app/src/main/res/values-night/styles.xml diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/values/styles.xml b/packages/firebase_ai/firebase_ai/example/android/app/src/main/res/values/styles.xml similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/main/res/values/styles.xml rename to packages/firebase_ai/firebase_ai/example/android/app/src/main/res/values/styles.xml diff --git a/packages/firebase_vertexai/firebase_ai/example/android/app/src/profile/AndroidManifest.xml b/packages/firebase_ai/firebase_ai/example/android/app/src/profile/AndroidManifest.xml similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/app/src/profile/AndroidManifest.xml rename to packages/firebase_ai/firebase_ai/example/android/app/src/profile/AndroidManifest.xml diff --git a/packages/firebase_vertexai/firebase_ai/example/android/build.gradle b/packages/firebase_ai/firebase_ai/example/android/build.gradle similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/build.gradle rename to packages/firebase_ai/firebase_ai/example/android/build.gradle diff --git a/packages/firebase_vertexai/firebase_ai/example/android/gradle.properties b/packages/firebase_ai/firebase_ai/example/android/gradle.properties similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/gradle.properties rename to packages/firebase_ai/firebase_ai/example/android/gradle.properties diff --git a/packages/firebase_vertexai/firebase_ai/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/firebase_ai/firebase_ai/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/gradle/wrapper/gradle-wrapper.properties rename to packages/firebase_ai/firebase_ai/example/android/gradle/wrapper/gradle-wrapper.properties diff --git a/packages/firebase_vertexai/firebase_ai/example/android/settings.gradle b/packages/firebase_ai/firebase_ai/example/android/settings.gradle similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/android/settings.gradle rename to packages/firebase_ai/firebase_ai/example/android/settings.gradle diff --git a/packages/firebase_vertexai/firebase_ai/example/assets/documents/gemini_summary.pdf b/packages/firebase_ai/firebase_ai/example/assets/documents/gemini_summary.pdf similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/assets/documents/gemini_summary.pdf rename to packages/firebase_ai/firebase_ai/example/assets/documents/gemini_summary.pdf diff --git a/packages/firebase_vertexai/firebase_ai/example/assets/images/cat.jpg b/packages/firebase_ai/firebase_ai/example/assets/images/cat.jpg similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/assets/images/cat.jpg rename to packages/firebase_ai/firebase_ai/example/assets/images/cat.jpg diff --git a/packages/firebase_vertexai/firebase_ai/example/assets/images/scones.jpg b/packages/firebase_ai/firebase_ai/example/assets/images/scones.jpg similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/assets/images/scones.jpg rename to packages/firebase_ai/firebase_ai/example/assets/images/scones.jpg diff --git a/packages/firebase_vertexai/firebase_ai/example/assets/videos/landscape.mp4 b/packages/firebase_ai/firebase_ai/example/assets/videos/landscape.mp4 similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/assets/videos/landscape.mp4 rename to packages/firebase_ai/firebase_ai/example/assets/videos/landscape.mp4 diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/.gitignore b/packages/firebase_ai/firebase_ai/example/ios/.gitignore similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/.gitignore rename to packages/firebase_ai/firebase_ai/example/ios/.gitignore diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist b/packages/firebase_ai/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist rename to packages/firebase_ai/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Flutter/Debug.xcconfig b/packages/firebase_ai/firebase_ai/example/ios/Flutter/Debug.xcconfig similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Flutter/Debug.xcconfig rename to packages/firebase_ai/firebase_ai/example/ios/Flutter/Debug.xcconfig diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Flutter/Release.xcconfig b/packages/firebase_ai/firebase_ai/example/ios/Flutter/Release.xcconfig similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Flutter/Release.xcconfig rename to packages/firebase_ai/firebase_ai/example/ios/Flutter/Release.xcconfig diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Podfile b/packages/firebase_ai/firebase_ai/example/ios/Podfile similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Podfile rename to packages/firebase_ai/firebase_ai/example/ios/Podfile diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj b/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj rename to packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/firebase_ai/firebase_ai/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to packages/firebase_ai/firebase_ai/example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/firebase_ai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/firebase_ai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_ai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/firebase_ai/firebase_ai/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/AppDelegate.swift b/packages/firebase_ai/firebase_ai/example/ios/Runner/AppDelegate.swift similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/AppDelegate.swift rename to packages/firebase_ai/firebase_ai/example/ios/Runner/AppDelegate.swift diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/firebase_ai/firebase_ai/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Base.lproj/Main.storyboard b/packages/firebase_ai/firebase_ai/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Base.lproj/Main.storyboard rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Base.lproj/Main.storyboard diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Info.plist b/packages/firebase_ai/firebase_ai/example/ios/Runner/Info.plist similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Info.plist rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Info.plist diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/Runner/Runner-Bridging-Header.h b/packages/firebase_ai/firebase_ai/example/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/Runner/Runner-Bridging-Header.h rename to packages/firebase_ai/firebase_ai/example/ios/Runner/Runner-Bridging-Header.h diff --git a/packages/firebase_vertexai/firebase_ai/example/ios/firebase_app_id_file.json b/packages/firebase_ai/firebase_ai/example/ios/firebase_app_id_file.json similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/ios/firebase_app_id_file.json rename to packages/firebase_ai/firebase_ai/example/ios/firebase_app_id_file.json diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/main.dart b/packages/firebase_ai/firebase_ai/example/lib/main.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/main.dart rename to packages/firebase_ai/firebase_ai/example/lib/main.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/audio_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/audio_page.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/pages/audio_page.dart rename to packages/firebase_ai/firebase_ai/example/lib/pages/audio_page.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/bidi_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/bidi_page.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/pages/bidi_page.dart rename to packages/firebase_ai/firebase_ai/example/lib/pages/bidi_page.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/chat_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/chat_page.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/pages/chat_page.dart rename to packages/firebase_ai/firebase_ai/example/lib/pages/chat_page.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/document.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/document.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/pages/document.dart rename to packages/firebase_ai/firebase_ai/example/lib/pages/document.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/function_calling_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/function_calling_page.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/pages/function_calling_page.dart rename to packages/firebase_ai/firebase_ai/example/lib/pages/function_calling_page.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/image_prompt_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/image_prompt_page.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/pages/image_prompt_page.dart rename to packages/firebase_ai/firebase_ai/example/lib/pages/image_prompt_page.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/imagen_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/imagen_page.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/pages/imagen_page.dart rename to packages/firebase_ai/firebase_ai/example/lib/pages/imagen_page.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/schema_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/schema_page.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/pages/schema_page.dart rename to packages/firebase_ai/firebase_ai/example/lib/pages/schema_page.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/token_count_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/token_count_page.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/pages/token_count_page.dart rename to packages/firebase_ai/firebase_ai/example/lib/pages/token_count_page.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/pages/video_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/video_page.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/pages/video_page.dart rename to packages/firebase_ai/firebase_ai/example/lib/pages/video_page.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/utils/audio_player.dart b/packages/firebase_ai/firebase_ai/example/lib/utils/audio_player.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/utils/audio_player.dart rename to packages/firebase_ai/firebase_ai/example/lib/utils/audio_player.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/utils/audio_recorder.dart b/packages/firebase_ai/firebase_ai/example/lib/utils/audio_recorder.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/utils/audio_recorder.dart rename to packages/firebase_ai/firebase_ai/example/lib/utils/audio_recorder.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/lib/widgets/message_widget.dart b/packages/firebase_ai/firebase_ai/example/lib/widgets/message_widget.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/lib/widgets/message_widget.dart rename to packages/firebase_ai/firebase_ai/example/lib/widgets/message_widget.dart diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/.gitignore b/packages/firebase_ai/firebase_ai/example/macos/.gitignore similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/.gitignore rename to packages/firebase_ai/firebase_ai/example/macos/.gitignore diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/firebase_ai/firebase_ai/example/macos/Flutter/Flutter-Debug.xcconfig similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Flutter/Flutter-Debug.xcconfig rename to packages/firebase_ai/firebase_ai/example/macos/Flutter/Flutter-Debug.xcconfig diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Flutter/Flutter-Release.xcconfig b/packages/firebase_ai/firebase_ai/example/macos/Flutter/Flutter-Release.xcconfig similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Flutter/Flutter-Release.xcconfig rename to packages/firebase_ai/firebase_ai/example/macos/Flutter/Flutter-Release.xcconfig diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Podfile b/packages/firebase_ai/firebase_ai/example/macos/Podfile similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Podfile rename to packages/firebase_ai/firebase_ai/example/macos/Podfile diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj b/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj rename to packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/firebase_ai/firebase_ai/example/macos/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcworkspace/contents.xcworkspacedata rename to packages/firebase_ai/firebase_ai/example/macos/Runner.xcworkspace/contents.xcworkspacedata diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/firebase_ai/firebase_ai/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/firebase_ai/firebase_ai/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/AppDelegate.swift b/packages/firebase_ai/firebase_ai/example/macos/Runner/AppDelegate.swift similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/AppDelegate.swift rename to packages/firebase_ai/firebase_ai/example/macos/Runner/AppDelegate.swift diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Base.lproj/MainMenu.xib b/packages/firebase_ai/firebase_ai/example/macos/Runner/Base.lproj/MainMenu.xib similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Base.lproj/MainMenu.xib rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Base.lproj/MainMenu.xib diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/firebase_ai/firebase_ai/example/macos/Runner/Configs/AppInfo.xcconfig similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/AppInfo.xcconfig rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Configs/AppInfo.xcconfig diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Debug.xcconfig b/packages/firebase_ai/firebase_ai/example/macos/Runner/Configs/Debug.xcconfig similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Debug.xcconfig rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Configs/Debug.xcconfig diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Release.xcconfig b/packages/firebase_ai/firebase_ai/example/macos/Runner/Configs/Release.xcconfig similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Release.xcconfig rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Configs/Release.xcconfig diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Warnings.xcconfig b/packages/firebase_ai/firebase_ai/example/macos/Runner/Configs/Warnings.xcconfig similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Configs/Warnings.xcconfig rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Configs/Warnings.xcconfig diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/DebugProfile.entitlements b/packages/firebase_ai/firebase_ai/example/macos/Runner/DebugProfile.entitlements similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/DebugProfile.entitlements rename to packages/firebase_ai/firebase_ai/example/macos/Runner/DebugProfile.entitlements diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Info.plist b/packages/firebase_ai/firebase_ai/example/macos/Runner/Info.plist similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Info.plist rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Info.plist diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/MainFlutterWindow.swift b/packages/firebase_ai/firebase_ai/example/macos/Runner/MainFlutterWindow.swift similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/MainFlutterWindow.swift rename to packages/firebase_ai/firebase_ai/example/macos/Runner/MainFlutterWindow.swift diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/Runner/Release.entitlements b/packages/firebase_ai/firebase_ai/example/macos/Runner/Release.entitlements similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/Runner/Release.entitlements rename to packages/firebase_ai/firebase_ai/example/macos/Runner/Release.entitlements diff --git a/packages/firebase_vertexai/firebase_ai/example/macos/firebase_app_id_file.json b/packages/firebase_ai/firebase_ai/example/macos/firebase_app_id_file.json similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/macos/firebase_app_id_file.json rename to packages/firebase_ai/firebase_ai/example/macos/firebase_app_id_file.json diff --git a/packages/firebase_vertexai/firebase_ai/example/pubspec.yaml b/packages/firebase_ai/firebase_ai/example/pubspec.yaml similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/pubspec.yaml rename to packages/firebase_ai/firebase_ai/example/pubspec.yaml diff --git a/packages/firebase_vertexai/firebase_ai/example/web/favicon.png b/packages/firebase_ai/firebase_ai/example/web/favicon.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/web/favicon.png rename to packages/firebase_ai/firebase_ai/example/web/favicon.png diff --git a/packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-192.png b/packages/firebase_ai/firebase_ai/example/web/icons/Icon-192.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-192.png rename to packages/firebase_ai/firebase_ai/example/web/icons/Icon-192.png diff --git a/packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-512.png b/packages/firebase_ai/firebase_ai/example/web/icons/Icon-512.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-512.png rename to packages/firebase_ai/firebase_ai/example/web/icons/Icon-512.png diff --git a/packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-maskable-192.png b/packages/firebase_ai/firebase_ai/example/web/icons/Icon-maskable-192.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-maskable-192.png rename to packages/firebase_ai/firebase_ai/example/web/icons/Icon-maskable-192.png diff --git a/packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-maskable-512.png b/packages/firebase_ai/firebase_ai/example/web/icons/Icon-maskable-512.png similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/web/icons/Icon-maskable-512.png rename to packages/firebase_ai/firebase_ai/example/web/icons/Icon-maskable-512.png diff --git a/packages/firebase_vertexai/firebase_ai/example/web/index.html b/packages/firebase_ai/firebase_ai/example/web/index.html similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/web/index.html rename to packages/firebase_ai/firebase_ai/example/web/index.html diff --git a/packages/firebase_vertexai/firebase_ai/example/web/manifest.json b/packages/firebase_ai/firebase_ai/example/web/manifest.json similarity index 100% rename from packages/firebase_vertexai/firebase_ai/example/web/manifest.json rename to packages/firebase_ai/firebase_ai/example/web/manifest.json diff --git a/packages/firebase_vertexai/firebase_ai/lib/firebase_ai.dart b/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/firebase_ai.dart rename to packages/firebase_ai/firebase_ai/lib/firebase_ai.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/api.dart b/packages/firebase_ai/firebase_ai/lib/src/api.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/api.dart rename to packages/firebase_ai/firebase_ai/lib/src/api.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/base_model.dart b/packages/firebase_ai/firebase_ai/lib/src/base_model.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/base_model.dart rename to packages/firebase_ai/firebase_ai/lib/src/base_model.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/chat.dart b/packages/firebase_ai/firebase_ai/lib/src/chat.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/chat.dart rename to packages/firebase_ai/firebase_ai/lib/src/chat.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/client.dart b/packages/firebase_ai/firebase_ai/lib/src/client.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/client.dart rename to packages/firebase_ai/firebase_ai/lib/src/client.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/content.dart b/packages/firebase_ai/firebase_ai/lib/src/content.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/content.dart rename to packages/firebase_ai/firebase_ai/lib/src/content.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/developer/api.dart b/packages/firebase_ai/firebase_ai/lib/src/developer/api.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/developer/api.dart rename to packages/firebase_ai/firebase_ai/lib/src/developer/api.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/error.dart b/packages/firebase_ai/firebase_ai/lib/src/error.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/error.dart rename to packages/firebase_ai/firebase_ai/lib/src/error.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/firebase_ai.dart b/packages/firebase_ai/firebase_ai/lib/src/firebase_ai.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/firebase_ai.dart rename to packages/firebase_ai/firebase_ai/lib/src/firebase_ai.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/function_calling.dart b/packages/firebase_ai/firebase_ai/lib/src/function_calling.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/function_calling.dart rename to packages/firebase_ai/firebase_ai/lib/src/function_calling.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/generative_model.dart b/packages/firebase_ai/firebase_ai/lib/src/generative_model.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/generative_model.dart rename to packages/firebase_ai/firebase_ai/lib/src/generative_model.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/imagen_api.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen_api.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/imagen_api.dart rename to packages/firebase_ai/firebase_ai/lib/src/imagen_api.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/imagen_content.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen_content.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/imagen_content.dart rename to packages/firebase_ai/firebase_ai/lib/src/imagen_content.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/imagen_model.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen_model.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/imagen_model.dart rename to packages/firebase_ai/firebase_ai/lib/src/imagen_model.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/live_api.dart b/packages/firebase_ai/firebase_ai/lib/src/live_api.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/live_api.dart rename to packages/firebase_ai/firebase_ai/lib/src/live_api.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/live_model.dart b/packages/firebase_ai/firebase_ai/lib/src/live_model.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/live_model.dart rename to packages/firebase_ai/firebase_ai/lib/src/live_model.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/live_session.dart b/packages/firebase_ai/firebase_ai/lib/src/live_session.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/live_session.dart rename to packages/firebase_ai/firebase_ai/lib/src/live_session.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/schema.dart b/packages/firebase_ai/firebase_ai/lib/src/schema.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/schema.dart rename to packages/firebase_ai/firebase_ai/lib/src/schema.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/utils/mutex.dart b/packages/firebase_ai/firebase_ai/lib/src/utils/mutex.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/utils/mutex.dart rename to packages/firebase_ai/firebase_ai/lib/src/utils/mutex.dart diff --git a/packages/firebase_vertexai/firebase_ai/lib/src/vertex_version.dart b/packages/firebase_ai/firebase_ai/lib/src/vertex_version.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/lib/src/vertex_version.dart rename to packages/firebase_ai/firebase_ai/lib/src/vertex_version.dart diff --git a/packages/firebase_vertexai/firebase_ai/pubspec.yaml b/packages/firebase_ai/firebase_ai/pubspec.yaml similarity index 100% rename from packages/firebase_vertexai/firebase_ai/pubspec.yaml rename to packages/firebase_ai/firebase_ai/pubspec.yaml diff --git a/packages/firebase_vertexai/firebase_ai/test/base_model_test.dart b/packages/firebase_ai/firebase_ai/test/base_model_test.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/base_model_test.dart rename to packages/firebase_ai/firebase_ai/test/base_model_test.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/chat_test.dart b/packages/firebase_ai/firebase_ai/test/chat_test.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/chat_test.dart rename to packages/firebase_ai/firebase_ai/test/chat_test.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/content_test.dart b/packages/firebase_ai/firebase_ai/test/content_test.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/content_test.dart rename to packages/firebase_ai/firebase_ai/test/content_test.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/error_test.dart b/packages/firebase_ai/firebase_ai/test/error_test.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/error_test.dart rename to packages/firebase_ai/firebase_ai/test/error_test.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/firebase_vertexai_test.dart b/packages/firebase_ai/firebase_ai/test/firebase_vertexai_test.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/firebase_vertexai_test.dart rename to packages/firebase_ai/firebase_ai/test/firebase_vertexai_test.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/google_ai_generative_model_test.dart b/packages/firebase_ai/firebase_ai/test/google_ai_generative_model_test.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/google_ai_generative_model_test.dart rename to packages/firebase_ai/firebase_ai/test/google_ai_generative_model_test.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/google_ai_response_parsing_test.dart b/packages/firebase_ai/firebase_ai/test/google_ai_response_parsing_test.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/google_ai_response_parsing_test.dart rename to packages/firebase_ai/firebase_ai/test/google_ai_response_parsing_test.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/imagen_test.dart b/packages/firebase_ai/firebase_ai/test/imagen_test.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/imagen_test.dart rename to packages/firebase_ai/firebase_ai/test/imagen_test.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/live_test.dart b/packages/firebase_ai/firebase_ai/test/live_test.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/live_test.dart rename to packages/firebase_ai/firebase_ai/test/live_test.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/mock.dart b/packages/firebase_ai/firebase_ai/test/mock.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/mock.dart rename to packages/firebase_ai/firebase_ai/test/mock.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/model_test.dart b/packages/firebase_ai/firebase_ai/test/model_test.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/model_test.dart rename to packages/firebase_ai/firebase_ai/test/model_test.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/response_parsing_test.dart b/packages/firebase_ai/firebase_ai/test/response_parsing_test.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/response_parsing_test.dart rename to packages/firebase_ai/firebase_ai/test/response_parsing_test.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/schema_test.dart b/packages/firebase_ai/firebase_ai/test/schema_test.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/schema_test.dart rename to packages/firebase_ai/firebase_ai/test/schema_test.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/utils/matchers.dart b/packages/firebase_ai/firebase_ai/test/utils/matchers.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/utils/matchers.dart rename to packages/firebase_ai/firebase_ai/test/utils/matchers.dart diff --git a/packages/firebase_vertexai/firebase_ai/test/utils/stub_client.dart b/packages/firebase_ai/firebase_ai/test/utils/stub_client.dart similarity index 100% rename from packages/firebase_vertexai/firebase_ai/test/utils/stub_client.dart rename to packages/firebase_ai/firebase_ai/test/utils/stub_client.dart diff --git a/packages/firebase_vertexai/firebase_vertexai/pubspec.yaml b/packages/firebase_vertexai/firebase_vertexai/pubspec.yaml index 9dcb087392bd..1d7b89e5d3c0 100644 --- a/packages/firebase_vertexai/firebase_vertexai/pubspec.yaml +++ b/packages/firebase_vertexai/firebase_vertexai/pubspec.yaml @@ -41,4 +41,4 @@ dev_dependencies: dependency_overrides: firebase_ai: - path: ../firebase_ai + path: ../../firebase_ai/firebase_ai From 24fd68db523a8ad3a457cc68e1c19376fc36f08f Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 8 May 2025 01:11:25 +0000 Subject: [PATCH 03/13] Add ::vertexai and ::googlai instance key suffixes --- .../firebase_ai/lib/src/firebase_ai.dart | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/firebase_ai/firebase_ai/lib/src/firebase_ai.dart b/packages/firebase_ai/firebase_ai/lib/src/firebase_ai.dart index 9adceb7886e1..acbcb3c9a069 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/firebase_ai.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/firebase_ai.dart @@ -63,9 +63,10 @@ class FirebaseAI extends FirebasePluginPlatform { String? location, }) { app ??= Firebase.app(); + var instanceKey = '${app.name}::vertexai'; - if (_cachedInstances.containsKey(app.name)) { - return _cachedInstances[app.name]!; + if (_cachedInstances.containsKey(instanceKey)) { + return _cachedInstances[instanceKey]!; } location ??= _defaultLocation; @@ -77,7 +78,7 @@ class FirebaseAI extends FirebasePluginPlatform { auth: auth, useVertexBackend: true, ); - _cachedInstances[app.name] = newInstance; + _cachedInstances[instanceKey] = newInstance; return newInstance; } @@ -92,9 +93,10 @@ class FirebaseAI extends FirebasePluginPlatform { FirebaseAuth? auth, }) { app ??= Firebase.app(); + var instanceKey = '${app.name}::googleai'; - if (_cachedInstances.containsKey(app.name)) { - return _cachedInstances[app.name]!; + if (_cachedInstances.containsKey(instanceKey)) { + return _cachedInstances[instanceKey]!; } FirebaseAI newInstance = FirebaseAI._( @@ -104,7 +106,7 @@ class FirebaseAI extends FirebasePluginPlatform { auth: auth, useVertexBackend: false, ); - _cachedInstances[app.name] = newInstance; + _cachedInstances[instanceKey] = newInstance; return newInstance; } From 412328a8879bc171acff12ff1f00fe0e20085a93 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 8 May 2025 01:18:18 +0000 Subject: [PATCH 04/13] Use ovrride and new SpeaechConfig API --- packages/firebase_ai/firebase_ai/example/pubspec.yaml | 2 ++ .../firebase_vertexai/example/lib/pages/bidi_page.dart | 2 +- .../firebase_vertexai/example/pubspec.yaml | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/firebase_ai/firebase_ai/example/pubspec.yaml b/packages/firebase_ai/firebase_ai/example/pubspec.yaml index ed83fd2a6a08..fe0fc37c6e25 100644 --- a/packages/firebase_ai/firebase_ai/example/pubspec.yaml +++ b/packages/firebase_ai/firebase_ai/example/pubspec.yaml @@ -49,3 +49,5 @@ flutter: dependency_overrides: firebase_ai: path: ../../firebase_ai + firebase_vertexai: + path: ../../../firebase_vertexai/firebase_vertexai diff --git a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/bidi_page.dart b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/bidi_page.dart index 6245824c144e..31b693c96208 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/bidi_page.dart +++ b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/bidi_page.dart @@ -59,7 +59,7 @@ class _BidiPageState extends State { super.initState(); final config = LiveGenerationConfig( - speechConfig: SpeechConfig(voice: Voice.fenrir), + speechConfig: SpeechConfig(voiceName: 'Fenrir'), responseModalities: [ ResponseModalities.audio, ], diff --git a/packages/firebase_vertexai/firebase_vertexai/example/pubspec.yaml b/packages/firebase_vertexai/firebase_vertexai/example/pubspec.yaml index 9723d9cdb990..759e375e2812 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/pubspec.yaml +++ b/packages/firebase_vertexai/firebase_vertexai/example/pubspec.yaml @@ -45,3 +45,9 @@ flutter: - assets/images/ - assets/documents/ - assets/videos/ + +dependency_overrides: + firebase_ai: + path: ../../../firebase_ai/firebase_ai + firebase_vertexai: + path: ../../firebase_vertexai From 2b276f18fe10ab421aeb6cea9ae1e804a84f7c5c Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 8 May 2025 01:22:50 +0000 Subject: [PATCH 05/13] Remove export of TaskType --- packages/firebase_ai/firebase_ai/lib/firebase_ai.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart b/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart index 30add92c6062..aa5160ea03ca 100644 --- a/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart +++ b/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart @@ -29,8 +29,6 @@ export 'src/api.dart' PromptFeedback, SafetyRating, SafetySetting, - // TODO(cynthiajiang) remove in next breaking change. - TaskType, UsageMetadata; export 'src/base_model.dart' show GenerativeModel, ImagenModel, LiveGenerativeModel; From 39c3e69ea32567be99bc953c3d57db2a550dbe0f Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 8 May 2025 01:24:54 +0000 Subject: [PATCH 06/13] Remove duplicate testing of the base model --- .../test/base_model_test.dart | 141 ------------------ 1 file changed, 141 deletions(-) delete mode 100644 packages/firebase_vertexai/firebase_vertexai/test/base_model_test.dart diff --git a/packages/firebase_vertexai/firebase_vertexai/test/base_model_test.dart b/packages/firebase_vertexai/firebase_vertexai/test/base_model_test.dart deleted file mode 100644 index 456111126c85..000000000000 --- a/packages/firebase_vertexai/firebase_vertexai/test/base_model_test.dart +++ /dev/null @@ -1,141 +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/src/base_model.dart'; -import 'package:firebase_ai/src/client.dart'; -import 'package:firebase_app_check/firebase_app_check.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:firebase_core/firebase_core.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; - -// Mock FirebaseApp -// ignore: avoid_implementing_value_types -class MockFirebaseApp extends Mock implements FirebaseApp { - @override - FirebaseOptions get options => MockFirebaseOptions(); - - @override - bool get isAutomaticDataCollectionEnabled => true; -} - -// Mock FirebaseOptions -// ignore: must_be_immutable, avoid_implementing_value_types -class MockFirebaseOptions extends Mock implements FirebaseOptions { - @override - String get projectId => 'test-project'; - - @override - String get appId => 'test-app-id'; -} - -// Mock Firebase App Check -class MockFirebaseAppCheck extends Mock implements FirebaseAppCheck { - @override - Future getToken([bool? forceRefresh = false]) async => - super.noSuchMethod(Invocation.method(#getToken, [forceRefresh])); -} - -// Mock Firebase Auth -class MockFirebaseAuth extends Mock implements FirebaseAuth { - @override - User? get currentUser => super.noSuchMethod(Invocation.getter(#currentUser)); -} - -// Mock Firebase User -class MockUser extends Mock implements User { - @override - Future getIdToken([bool? forceRefresh = false]) async => - super.noSuchMethod(Invocation.method(#getIdToken, [forceRefresh])); -} - -class MockApiClient extends Mock implements ApiClient { - @override - Future> makeRequest( - Uri uri, Map params) async { - // Simulate a successful API response - return {'mockResponse': 'success'}; - } -} - -void main() { - group('BaseModel', () { - test('firebaseTokens returns a function that generates headers', () async { - final tokenFunction = BaseModel.firebaseTokens(null, null, null); - final headers = await tokenFunction(); - expect(headers['x-goog-api-client'], contains('gl-dart')); - expect(headers['x-goog-api-client'], contains('fire')); - expect(headers.length, 1); - }); - - test('firebaseTokens includes App Check token if available', () async { - final mockAppCheck = MockFirebaseAppCheck(); - when(mockAppCheck.getToken()) - .thenAnswer((_) async => 'test-app-check-token'); - final tokenFunction = BaseModel.firebaseTokens(mockAppCheck, null, null); - final headers = await tokenFunction(); - expect(headers['X-Firebase-AppCheck'], 'test-app-check-token'); - expect(headers['x-goog-api-client'], contains('gl-dart')); - expect(headers['x-goog-api-client'], contains('fire')); - expect(headers.length, 2); - }); - - test('firebaseTokens includes Auth ID token if available', () async { - final mockAuth = MockFirebaseAuth(); - final mockUser = MockUser(); - when(mockUser.getIdToken()).thenAnswer((_) async => 'test-id-token'); - when(mockAuth.currentUser).thenReturn(mockUser); - final tokenFunction = BaseModel.firebaseTokens(null, mockAuth, null); - final headers = await tokenFunction(); - expect(headers['Authorization'], 'Firebase test-id-token'); - expect(headers['x-goog-api-client'], contains('gl-dart')); - expect(headers['x-goog-api-client'], contains('fire')); - expect(headers.length, 2); - }); - - test( - 'firebaseTokens includes App ID if automatic data collection is enabled', - () async { - final mockApp = MockFirebaseApp(); - - final tokenFunction = BaseModel.firebaseTokens(null, null, mockApp); - final headers = await tokenFunction(); - expect(headers['X-Firebase-AppId'], 'test-app-id'); - expect(headers['x-goog-api-client'], contains('gl-dart')); - expect(headers['x-goog-api-client'], contains('fire')); - expect(headers.length, 2); - }); - - test('firebaseTokens includes all tokens if available', () async { - final mockAppCheck = MockFirebaseAppCheck(); - when(mockAppCheck.getToken()) - .thenAnswer((_) async => 'test-app-check-token'); - final mockAuth = MockFirebaseAuth(); - final mockUser = MockUser(); - when(mockUser.getIdToken()).thenAnswer((_) async => 'test-id-token'); - when(mockAuth.currentUser).thenReturn(mockUser); - final mockApp = MockFirebaseApp(); - - final tokenFunction = - BaseModel.firebaseTokens(mockAppCheck, mockAuth, mockApp); - final headers = await tokenFunction(); - expect(headers['X-Firebase-AppCheck'], 'test-app-check-token'); - expect(headers['Authorization'], 'Firebase test-id-token'); - expect(headers['X-Firebase-AppId'], 'test-app-id'); - expect(headers['x-goog-api-client'], contains('gl-dart')); - expect(headers['x-goog-api-client'], contains('fire')); - expect(headers.length, 4); - }); - }); -} From 53c9767de479237f775795b4a6bba30c5eb27503 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 8 May 2025 01:25:38 +0000 Subject: [PATCH 07/13] Use 2.0.0 as initial version --- packages/firebase_ai/firebase_ai/CHANGELOG.md | 4 ++-- packages/firebase_ai/firebase_ai/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/firebase_ai/firebase_ai/CHANGELOG.md b/packages/firebase_ai/firebase_ai/CHANGELOG.md index 6e551e77ef40..3a390d673f0e 100644 --- a/packages/firebase_ai/firebase_ai/CHANGELOG.md +++ b/packages/firebase_ai/firebase_ai/CHANGELOG.md @@ -1,3 +1,3 @@ -## 0.5.0 +## 2.0.0 -- Initial release following package rename. +- Initial release following package rename from `firebase_vertexai`. diff --git a/packages/firebase_ai/firebase_ai/pubspec.yaml b/packages/firebase_ai/firebase_ai/pubspec.yaml index dd0538f2932d..fea4be4a4f49 100644 --- a/packages/firebase_ai/firebase_ai/pubspec.yaml +++ b/packages/firebase_ai/firebase_ai/pubspec.yaml @@ -1,6 +1,6 @@ name: firebase_ai description: Firebase AI SDK. -version: 0.1.0 +version: 2.0.0 homepage: https://firebase.google.com/docs/vertex-ai/get-started?platform=flutter topics: - firebase From d82873596cc729c53205eb16067a7b7efd0da219 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 8 May 2025 01:29:30 +0000 Subject: [PATCH 08/13] Reformat --- packages/firebase_ai/firebase_ai/example/lib/main.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/firebase_ai/firebase_ai/example/lib/main.dart b/packages/firebase_ai/firebase_ai/example/lib/main.dart index 92c56ea88d79..b9d2beeba9a6 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/main.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/main.dart @@ -63,8 +63,7 @@ class _GenerativeAISampleState extends State { void _initializeModel(bool useVertexBackend) { if (useVertexBackend) { - final vertexInstance = - FirebaseAI.vertexAI(auth: FirebaseAuth.instance); + final vertexInstance = FirebaseAI.vertexAI(auth: FirebaseAuth.instance); _currentModel = vertexInstance.generativeModel(model: 'gemini-1.5-flash'); _currentImagenModel = _initializeImagenModel(vertexInstance); } else { From 589e846892365c42fcb3083b80dc52f60c344b2e Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 8 May 2025 01:29:39 +0000 Subject: [PATCH 09/13] Add a failing test - check if CI is already running --- packages/firebase_ai/firebase_ai/test/model_test.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/firebase_ai/firebase_ai/test/model_test.dart b/packages/firebase_ai/firebase_ai/test/model_test.dart index 2ddf4d55406c..6d9a6ca9b643 100644 --- a/packages/firebase_ai/firebase_ai/test/model_test.dart +++ b/packages/firebase_ai/firebase_ai/test/model_test.dart @@ -29,6 +29,9 @@ void main() { // Initialize Firebase app = await Firebase.initializeApp(); }); + test('canary test', () { + expect(true, false); + }); group('GenerativeModel', () { const defaultModelName = 'some-model'; From 6e9f14076f59c242e5d8097ed8a6eee8777132c9 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 8 May 2025 01:36:03 +0000 Subject: [PATCH 10/13] Remove failing test CI is confirmed running for this package. --- packages/firebase_ai/firebase_ai/test/model_test.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/firebase_ai/firebase_ai/test/model_test.dart b/packages/firebase_ai/firebase_ai/test/model_test.dart index 6d9a6ca9b643..2ddf4d55406c 100644 --- a/packages/firebase_ai/firebase_ai/test/model_test.dart +++ b/packages/firebase_ai/firebase_ai/test/model_test.dart @@ -29,9 +29,6 @@ void main() { // Initialize Firebase app = await Firebase.initializeApp(); }); - test('canary test', () { - expect(true, false); - }); group('GenerativeModel', () { const defaultModelName = 'some-model'; From 9c15180c7e6eaacb33c3df75186cde94b3421b20 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 8 May 2025 01:42:54 +0000 Subject: [PATCH 11/13] Add a copy of the analysis options in the new parent directory --- packages/firebase_ai/analysis_options.yaml | 88 ++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 packages/firebase_ai/analysis_options.yaml diff --git a/packages/firebase_ai/analysis_options.yaml b/packages/firebase_ai/analysis_options.yaml new file mode 100644 index 000000000000..f41d32929033 --- /dev/null +++ b/packages/firebase_ai/analysis_options.yaml @@ -0,0 +1,88 @@ +# Copyright 2021 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# in the LICENSE file. + +include: all_lint_rules.yaml +analyzer: + # TODO(rrousselGit): disable implicit-cast/implicit-dynamic + errors: + # Otherwise cause the import of all_lint_rules to warn because of some rules conflicts. + # We explicitly enabled even conflicting rules and are fixing the conflict + # in this file + included_file_warning: ignore + +linter: + rules: + ## Disabled rules because the repository doesn't respect them (yet) + + always_put_control_body_on_new_line: false + comment_references: false + prefer_constructors_over_static_methods: false + prefer_final_fields: false + prefer_final_locals: false + omit_local_variable_types: false + avoid_equals_and_hash_code_on_mutable_classes: false + + ############# + + # Personal preference. I don't find it more readable + cascade_invocations: false + + # Conflicts with `prefer_single_quotes` + # Single quotes are easier to type and don't compromise on readability. + prefer_double_quotes: false + + # Conflicts with `omit_local_variable_types` and other rules. + # As per Dart guidelines, we want to avoid unnecessary types to make the code + # more readable. + # See https://dart.dev/guides/language/effective-dart/design#avoid-type-annotating-initialized-local-variables + always_specify_types: false + + # Incompatible with `prefer_final_locals` + # Having immutable local variables makes larger functions more predictible + # so we will use `prefer_final_locals` instead. + unnecessary_final: false + + # Not quite suitable for Flutter, which may have a `build` method with a single + # return, but that return is still complex enough that a "body" is worth it. + prefer_expression_function_bodies: false + + # Conflicts with the convention used by flutter, which puts `Key key` + # and `@required Widget child` last. + always_put_required_named_parameters_first: false + + # This project doesn't use Flutter-style todos + flutter_style_todos: false + + # There are situations where we voluntarily want to catch everything, + # especially as a library. + avoid_catches_without_on_clauses: false + + # Boring as it sometimes force a line of 81 characters to be split in two. + # As long as we try to respect that 80 characters limit, going slightly + # above is fine. + lines_longer_than_80_chars: false + + # Conflicts with disabling `implicit-dynamic` + avoid_annotating_with_dynamic: false + + # conflicts with `prefer_relative_imports` + always_use_package_imports: false + + # Disabled for now until we have NNBD as it otherwise conflicts with `missing_return` + no_default_cases: false + + # False positive, null checks don't need a message + prefer_asserts_with_message: false + + # Cumbersome with `context.select` + avoid_types_on_closure_parameters: false + + # Too many false positive (builders) + diagnostic_describe_all_properties: false + + # false positives (setter-like functions) + avoid_positional_boolean_parameters: false + + # Does not apply to providers + prefer_const_constructors_in_immutables: false From a6087fb8e08ebaa9db31633b44f99cce2ee472b4 Mon Sep 17 00:00:00 2001 From: Cynthia J Date: Wed, 30 Apr 2025 09:34:45 -0700 Subject: [PATCH 12/13] Revert to latest version of example app Minor code changes for the interface change. --- .../firebase_vertexai/example/.gitignore | 1 + .../example/android/settings.gradle | 3 + .../firebase_vertexai/example/lib/main.dart | 290 +++++++++++++----- .../lib/pages/function_calling_page.dart | 34 +- .../example/lib/pages/imagen_page.dart | 26 +- 5 files changed, 237 insertions(+), 117 deletions(-) diff --git a/packages/firebase_vertexai/firebase_vertexai/example/.gitignore b/packages/firebase_vertexai/firebase_vertexai/example/.gitignore index 0498b592dfa0..53bed76d8faa 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/.gitignore +++ b/packages/firebase_vertexai/firebase_vertexai/example/.gitignore @@ -48,3 +48,4 @@ app.*.map.json firebase_options.dart google-services.json GoogleService-Info.plist +firebase.json diff --git a/packages/firebase_vertexai/firebase_vertexai/example/android/settings.gradle b/packages/firebase_vertexai/firebase_vertexai/example/android/settings.gradle index 9151bc043341..40cbd22bb13b 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/android/settings.gradle +++ b/packages/firebase_vertexai/firebase_vertexai/example/android/settings.gradle @@ -19,6 +19,9 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "7.3.0" apply false + // START: FlutterFire Configuration + id "com.google.gms.google-services" version "4.3.15" apply false + // END: FlutterFire Configuration id "org.jetbrains.kotlin.android" version "1.9.22" apply false } diff --git a/packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart b/packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart index e6d880e15427..d4246d4211e1 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart +++ b/packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart @@ -38,23 +38,78 @@ void main() async { // await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); await Firebase.initializeApp(); await FirebaseAuth.instance.signInAnonymously(); + runApp(const GenerativeAISample()); +} - var vertexInstance = - FirebaseVertexAI.instanceFor(auth: FirebaseAuth.instance); - final model = vertexInstance.generativeModel(model: 'gemini-1.5-flash'); +class GenerativeAISample extends StatefulWidget { + const GenerativeAISample({super.key}); - runApp(GenerativeAISample(model: model)); + @override + State createState() => _GenerativeAISampleState(); } -class GenerativeAISample extends StatelessWidget { - final GenerativeModel model; +class _GenerativeAISampleState extends State { + bool _useVertexBackend = false; + late GenerativeModel _currentModel; + late ImagenModel _currentImagenModel; + int _currentBottomNavIndex = 0; + + @override + void initState() { + super.initState(); + + _initializeModel(_useVertexBackend); + } - const GenerativeAISample({super.key, required this.model}); + void _initializeModel(bool useVertexBackend) { + if (useVertexBackend) { + final vertexInstance = + FirebaseVertexAI.instanceFor(auth: FirebaseAuth.instance); + _currentModel = vertexInstance.generativeModel(model: 'gemini-1.5-flash'); + _currentImagenModel = _initializeImagenModel(vertexInstance); + } else { + final googleAI = FirebaseVertexAI.googleAI(auth: FirebaseAuth.instance); + _currentModel = googleAI.generativeModel(model: 'gemini-2.0-flash'); + _currentImagenModel = _initializeImagenModel(googleAI); + } + } + + ImagenModel _initializeImagenModel(FirebaseVertexAI instance) { + var generationConfig = ImagenGenerationConfig( + negativePrompt: 'frog', + numberOfImages: 1, + aspectRatio: ImagenAspectRatio.square1x1, + imageFormat: ImagenFormat.jpeg(compressionQuality: 75), + ); + return instance.imagenModel( + model: 'imagen-3.0-generate-001', + generationConfig: generationConfig, + safetySettings: ImagenSafetySettings( + ImagenSafetyFilterLevel.blockLowAndAbove, + ImagenPersonFilterLevel.allowAdult, + ), + ); + } + + void _toggleBackend(bool value) { + setState(() { + _useVertexBackend = value; + }); + _initializeModel(_useVertexBackend); + } + + void _onBottomNavTapped(int index) { + setState(() { + _currentBottomNavIndex = index; + }); + } @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter + Vertex AI', + title: 'Flutter + ${_useVertexBackend ? 'Vertex AI' : 'Google AI'}', + debugShowCheckedModeBanner: false, + themeMode: ThemeMode.dark, theme: ThemeData( colorScheme: ColorScheme.fromSeed( brightness: Brightness.dark, @@ -62,137 +117,204 @@ class GenerativeAISample extends StatelessWidget { ), useMaterial3: true, ), - home: HomeScreen(model: model), + home: HomeScreen( + key: ValueKey( + '${_useVertexBackend}_${_currentModel.hashCode}', + ), + model: _currentModel, + imagenModel: _currentImagenModel, + useVertexBackend: _useVertexBackend, + onBackendChanged: _toggleBackend, + selectedIndex: _currentBottomNavIndex, + onSelectedIndexChanged: _onBottomNavTapped, + ), ); } } class HomeScreen extends StatefulWidget { final GenerativeModel model; - const HomeScreen({super.key, required this.model}); + final ImagenModel imagenModel; + final bool useVertexBackend; + final ValueChanged onBackendChanged; + final int selectedIndex; + final ValueChanged onSelectedIndexChanged; + + const HomeScreen({ + super.key, + required this.model, + required this.imagenModel, + required this.useVertexBackend, + required this.onBackendChanged, + required this.selectedIndex, + required this.onSelectedIndexChanged, + }); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { - int _selectedIndex = 0; - - List get _pages => [ - // Build _pages dynamically - ChatPage(title: 'Chat', model: widget.model), - AudioPage(title: 'Audio', model: widget.model), - TokenCountPage(title: 'Token Count', model: widget.model), - const FunctionCallingPage( - title: 'Function Calling', - ), // function calling will initial its own model - ImagePromptPage(title: 'Image Prompt', model: widget.model), - ImagenPage(title: 'Imagen Model', model: widget.model), - SchemaPromptPage(title: 'Schema Prompt', model: widget.model), - DocumentPage(title: 'Document Prompt', model: widget.model), - VideoPage(title: 'Video Prompt', model: widget.model), - BidiPage(title: 'Bidi Stream', model: widget.model), - ]; - void _onItemTapped(int index) { - setState(() { - _selectedIndex = index; - }); + widget.onSelectedIndexChanged(index); + } + +// Method to build the selected page on demand + Widget _buildSelectedPage( + int index, + GenerativeModel currentModel, + ImagenModel currentImagenModel, + bool useVertexBackend, + ) { + switch (index) { + case 0: + return ChatPage(title: 'Chat', model: currentModel); + case 1: + return AudioPage(title: 'Audio', model: currentModel); + case 2: + return TokenCountPage(title: 'Token Count', model: currentModel); + case 3: + // FunctionCallingPage initializes its own model as per original design + return FunctionCallingPage( + title: 'Function Calling', + useVertexBackend: useVertexBackend, + ); + 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: + return DocumentPage(title: 'Document Prompt', model: currentModel); + case 8: + return VideoPage(title: 'Video Prompt', model: currentModel); + case 9: + return BidiPage(title: 'Bidi Stream', model: currentModel); + default: + // Fallback to the first page in case of an unexpected index + return ChatPage(title: 'Chat', model: currentModel); + } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Flutter + Vertex AI'), + title: Text( + 'Flutter + ${widget.useVertexBackend ? 'Vertex AI' : 'Google AI'}', + ), + actions: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Google AI', + style: TextStyle( + fontSize: 12, + color: widget.useVertexBackend + ? Theme.of(context) + .colorScheme + .onSurface + .withValues(alpha: 0.7) + : Theme.of(context).colorScheme.primary, + ), + ), + Switch( + value: widget.useVertexBackend, + onChanged: widget.onBackendChanged, + activeTrackColor: Colors.green.withValues(alpha: 0.5), + inactiveTrackColor: Colors.blueGrey.withValues(alpha: 0.5), + activeColor: Colors.green, + inactiveThumbColor: Colors.blueGrey, + ), + Text( + 'Vertex AI', + style: TextStyle( + fontSize: 12, + color: widget.useVertexBackend + ? Theme.of(context).colorScheme.primary + : Theme.of(context) + .colorScheme + .onSurface + .withValues(alpha: 0.7), + ), + ), + ], + ), + ), + ], ), body: Center( - child: _pages.elementAt(_selectedIndex), + child: _buildSelectedPage( + widget.selectedIndex, + widget.model, + widget.imagenModel, + widget.useVertexBackend, + ), ), bottomNavigationBar: BottomNavigationBar( - items: [ + type: BottomNavigationBarType.fixed, + selectedFontSize: 10, + unselectedFontSize: 9, + selectedItemColor: Theme.of(context).colorScheme.primary, + unselectedItemColor: + Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7), + items: const [ BottomNavigationBarItem( - icon: Icon( - Icons.chat, - color: Theme.of(context).colorScheme.primary, - ), + icon: Icon(Icons.chat), label: 'Chat', tooltip: 'Chat', ), BottomNavigationBarItem( - icon: Icon( - Icons.mic, - color: Theme.of(context).colorScheme.primary, - ), - label: 'Audio Prompt', + icon: Icon(Icons.mic), + label: 'Audio', tooltip: 'Audio Prompt', ), BottomNavigationBarItem( - icon: Icon( - Icons.numbers, - color: Theme.of(context).colorScheme.primary, - ), - label: 'Token Count', + icon: Icon(Icons.numbers), + label: 'Tokens', tooltip: 'Token Count', ), BottomNavigationBarItem( - icon: Icon( - Icons.functions, - color: Theme.of(context).colorScheme.primary, - ), - label: 'Function Calling', + icon: Icon(Icons.functions), + label: 'Functions', tooltip: 'Function Calling', ), BottomNavigationBarItem( - icon: Icon( - Icons.image, - color: Theme.of(context).colorScheme.primary, - ), - label: 'Image Prompt', + icon: Icon(Icons.image), + label: 'Image', tooltip: 'Image Prompt', ), BottomNavigationBarItem( - icon: Icon( - Icons.image_search, - color: Theme.of(context).colorScheme.primary, - ), - label: 'Imagen Model', + icon: Icon(Icons.image_search), + label: 'Imagen', tooltip: 'Imagen Model', ), BottomNavigationBarItem( - icon: Icon( - Icons.schema, - color: Theme.of(context).colorScheme.primary, - ), - label: 'Schema Prompt', + icon: Icon(Icons.schema), + label: 'Schema', tooltip: 'Schema Prompt', ), BottomNavigationBarItem( - icon: Icon( - Icons.edit_document, - color: Theme.of(context).colorScheme.primary, - ), - label: 'Document Prompt', + icon: Icon(Icons.edit_document), + label: 'Document', tooltip: 'Document Prompt', ), BottomNavigationBarItem( - icon: Icon( - Icons.video_collection, - color: Theme.of(context).colorScheme.primary, - ), - label: 'Video Prompt', + icon: Icon(Icons.video_collection), + label: 'Video', tooltip: 'Video Prompt', ), BottomNavigationBarItem( - icon: Icon( - Icons.stream, - color: Theme.of(context).colorScheme.primary, - ), - label: 'Bidi Stream', + icon: Icon(Icons.stream), + label: 'Bidi', tooltip: 'Bidi Stream', ), ], - currentIndex: _selectedIndex, + currentIndex: widget.selectedIndex, onTap: _onItemTapped, ), ); diff --git a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/function_calling_page.dart b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/function_calling_page.dart index 130afff5ce92..dce322c1822d 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/function_calling_page.dart +++ b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/function_calling_page.dart @@ -18,9 +18,14 @@ import 'package:firebase_auth/firebase_auth.dart'; import '../widgets/message_widget.dart'; class FunctionCallingPage extends StatefulWidget { - const FunctionCallingPage({super.key, required this.title}); + const FunctionCallingPage({ + super.key, + required this.title, + required this.useVertexBackend, + }); final String title; + final bool useVertexBackend; @override State createState() => _FunctionCallingPageState(); @@ -41,14 +46,23 @@ class _FunctionCallingPageState extends State { @override void initState() { super.initState(); - var vertex_instance = - FirebaseVertexAI.instanceFor(auth: FirebaseAuth.instance); - _functionCallModel = vertex_instance.generativeModel( - model: 'gemini-1.5-flash', - tools: [ - Tool.functionDeclarations([fetchWeatherTool]), - ], - ); + if (widget.useVertexBackend) { + var vertexAI = FirebaseVertexAI.instanceFor(auth: FirebaseAuth.instance); + _functionCallModel = vertexAI.generativeModel( + model: 'gemini-2.0-flash', + tools: [ + Tool.functionDeclarations([fetchWeatherTool]), + ], + ); + } else { + var googleAI = FirebaseVertexAI.googleAI(auth: FirebaseAuth.instance); + _functionCallModel = googleAI.generativeModel( + model: 'gemini-2.0-flash', + tools: [ + Tool.functionDeclarations([fetchWeatherTool]), + ], + ); + } } // This is a hypothetical API to return a fake weather data collection for @@ -146,7 +160,7 @@ class _FunctionCallingPageState extends State { _loading = true; }); final functionCallChat = _functionCallModel.startChat(); - const prompt = 'What is the weather like in Boston on 10/02 this year?'; + const prompt = 'What is the weather like in Boston on 10/02 in year 2024?'; // Send the message to the generative model. var response = await functionCallChat.sendMessage( diff --git a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/imagen_page.dart b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/imagen_page.dart index 04f89e4f1253..0ab750b13fef 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/imagen_page.dart +++ b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/imagen_page.dart @@ -25,7 +25,7 @@ class ImagenPage extends StatefulWidget { }); final String title; - final GenerativeModel model; + final ImagenModel model; @override State createState() => _ImagenPageState(); @@ -37,26 +37,6 @@ class _ImagenPageState extends State { final FocusNode _textFieldFocus = FocusNode(); final List _generatedContent = []; bool _loading = false; - late final ImagenModel _imagenModel; - - @override - void initState() { - super.initState(); - var generationConfig = ImagenGenerationConfig( - negativePrompt: 'frog', - numberOfImages: 1, - aspectRatio: ImagenAspectRatio.square1x1, - imageFormat: ImagenFormat.jpeg(compressionQuality: 75), - ); - _imagenModel = FirebaseVertexAI.instance.imagenModel( - model: 'imagen-3.0-generate-001', - generationConfig: generationConfig, - safetySettings: ImagenSafetySettings( - ImagenSafetyFilterLevel.blockLowAndAbove, - ImagenPersonFilterLevel.allowAdult, - ), - ); - } void _scrollDown() { WidgetsBinding.instance.addPostFrameCallback( @@ -154,7 +134,7 @@ class _ImagenPageState extends State { }); try { - var response = await _imagenModel.generateImages(prompt); + var response = await widget.model.generateImages(prompt); if (response.images.isNotEmpty) { var imagenImage = response.images[0]; @@ -186,7 +166,7 @@ class _ImagenPageState extends State { // }); // var gcsUrl = 'gs://vertex-ai-example-ef5a2.appspot.com/imagen'; - // var response = await _imagenModel.generateImagesGCS(prompt, gcsUrl); + // var response = await widget.model.generateImagesGCS(prompt, gcsUrl); // if (response.images.isNotEmpty) { // var imagenImage = response.images[0]; From 89a1f5e84fe3e59b10ceb6f684c62fa397afcee4 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 8 May 2025 01:54:17 +0000 Subject: [PATCH 13/13] Restore the same example app in both packages Minor code changes in the _vertexai example app for the interface differences --- .../firebase_vertexai/example/lib/main.dart | 44 ++++++++++--------- .../lib/pages/function_calling_page.dart | 4 +- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart b/packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart index d4246d4211e1..0a31f6f8a306 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart +++ b/packages/firebase_vertexai/firebase_vertexai/example/lib/main.dart @@ -14,6 +14,7 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_ai/firebase_ai.dart'; import 'package:firebase_vertexai/firebase_vertexai.dart'; import 'package:flutter/material.dart'; @@ -62,35 +63,38 @@ class _GenerativeAISampleState extends State { } void _initializeModel(bool useVertexBackend) { + var generationConfig = ImagenGenerationConfig( + negativePrompt: 'frog', + numberOfImages: 1, + aspectRatio: ImagenAspectRatio.square1x1, + imageFormat: ImagenFormat.jpeg(compressionQuality: 75), + ); if (useVertexBackend) { final vertexInstance = FirebaseVertexAI.instanceFor(auth: FirebaseAuth.instance); _currentModel = vertexInstance.generativeModel(model: 'gemini-1.5-flash'); - _currentImagenModel = _initializeImagenModel(vertexInstance); + _currentImagenModel = vertexInstance.imagenModel( + model: 'imagen-3.0-generate-001', + generationConfig: generationConfig, + safetySettings: ImagenSafetySettings( + ImagenSafetyFilterLevel.blockLowAndAbove, + ImagenPersonFilterLevel.allowAdult, + ), + ); } else { - final googleAI = FirebaseVertexAI.googleAI(auth: FirebaseAuth.instance); + final googleAI = FirebaseAI.googleAI(auth: FirebaseAuth.instance); _currentModel = googleAI.generativeModel(model: 'gemini-2.0-flash'); - _currentImagenModel = _initializeImagenModel(googleAI); + _currentImagenModel = googleAI.imagenModel( + model: 'imagen-3.0-generate-001', + generationConfig: generationConfig, + safetySettings: ImagenSafetySettings( + ImagenSafetyFilterLevel.blockLowAndAbove, + ImagenPersonFilterLevel.allowAdult, + ), + ); } } - ImagenModel _initializeImagenModel(FirebaseVertexAI instance) { - var generationConfig = ImagenGenerationConfig( - negativePrompt: 'frog', - numberOfImages: 1, - aspectRatio: ImagenAspectRatio.square1x1, - imageFormat: ImagenFormat.jpeg(compressionQuality: 75), - ); - return instance.imagenModel( - model: 'imagen-3.0-generate-001', - generationConfig: generationConfig, - safetySettings: ImagenSafetySettings( - ImagenSafetyFilterLevel.blockLowAndAbove, - ImagenPersonFilterLevel.allowAdult, - ), - ); - } - void _toggleBackend(bool value) { setState(() { _useVertexBackend = value; diff --git a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/function_calling_page.dart b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/function_calling_page.dart index dce322c1822d..7499a07bc636 100644 --- a/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/function_calling_page.dart +++ b/packages/firebase_vertexai/firebase_vertexai/example/lib/pages/function_calling_page.dart @@ -13,8 +13,10 @@ // limitations under the License. import 'package:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; import 'package:firebase_vertexai/firebase_vertexai.dart'; import 'package:firebase_auth/firebase_auth.dart'; + import '../widgets/message_widget.dart'; class FunctionCallingPage extends StatefulWidget { @@ -55,7 +57,7 @@ class _FunctionCallingPageState extends State { ], ); } else { - var googleAI = FirebaseVertexAI.googleAI(auth: FirebaseAuth.instance); + var googleAI = FirebaseAI.googleAI(auth: FirebaseAuth.instance); _functionCallModel = googleAI.generativeModel( model: 'gemini-2.0-flash', tools: [