diff --git a/packages/firebase_ai/firebase_ai/example/lib/constants.dart b/packages/firebase_ai/firebase_ai/example/lib/constants.dart new file mode 100644 index 000000000000..10561d03cf26 --- /dev/null +++ b/packages/firebase_ai/firebase_ai/example/lib/constants.dart @@ -0,0 +1,33 @@ +// Copyright 2026 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. + +/// Centralized model identifiers used across the example app. +abstract final class ExampleModels { + /// Default fast multimodal model. + static const String flashLite = 'gemini-3.5-flash-lite'; + + /// Multimodal text and image generation model. + static const String flashImage = 'gemini-3.1-flash-image'; + + /// Text-to-speech audio model preview. + static const String flashTTS = 'gemini-3.1-flash-tts-preview'; + + /// Live bidirectional audio streaming model for Agent Platform. + static const String liveAgentPlatform = + 'gemini-live-2.5-flash-preview-native-audio-09-2025'; + + /// Live bidirectional audio streaming model for Google AI. + static const String liveGoogleAI = + 'gemini-2.5-flash-native-audio-preview-09-2025'; +} diff --git a/packages/firebase_ai/firebase_ai/example/lib/main.dart b/packages/firebase_ai/firebase_ai/example/lib/main.dart index 99bb621ca308..978551519ecc 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/main.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/main.dart @@ -21,16 +21,19 @@ import 'package:flutter/material.dart'; // Import after file is generated through flutterfire_cli. // import 'package:firebase_ai_example/firebase_options.dart'; +import 'constants.dart'; import 'pages/bidi_page.dart'; +import 'pages/capabilities_page.dart'; import 'pages/chat_page.dart'; import 'pages/function_calling_page.dart'; -import 'pages/image_generation_page.dart'; -import 'pages/capabilities_page.dart'; -import 'pages/server_template_page.dart'; import 'pages/grounding_page.dart'; +import 'pages/image_generation_page.dart'; import 'pages/integration_test_page.dart'; +import 'pages/server_template_page.dart'; import 'pages/tts_page.dart'; +export 'constants.dart'; + void main() async { WidgetsFlutterBinding.ensureInitialized(); // Enable this line instead once have the firebase_options.dart generated and @@ -72,10 +75,10 @@ class _GenerativeAISampleState extends State { final agentPlatformInstance = FirebaseAI.agentPlatform(location: 'global'); _currentModel = - agentPlatformInstance.generativeModel(model: 'gemini-3.5-flash-lite'); + agentPlatformInstance.generativeModel(model: ExampleModels.flashLite); } else { final googleAI = FirebaseAI.googleAI(); - _currentModel = googleAI.generativeModel(model: 'gemini-3.5-flash-lite'); + _currentModel = googleAI.generativeModel(model: ExampleModels.flashLite); } } diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/bidi_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/bidi_page.dart index bf8d3cf5be3f..16353bb10f8e 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/bidi_page.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/bidi_page.dart @@ -18,12 +18,13 @@ import 'package:flutter/material.dart'; import 'package:firebase_ai/firebase_ai.dart'; import 'package:waveform_flutter/waveform_flutter.dart'; +import '../constants.dart'; import '../utils/audio_input.dart'; import '../utils/audio_output.dart'; import '../utils/video_input.dart'; -import '../widgets/message_widget.dart'; import '../widgets/audio_visualizer.dart'; import '../widgets/camera_previews.dart'; +import '../widgets/message_widget.dart'; // ============================================================================ // MEDIA MANAGER @@ -257,12 +258,12 @@ class BidiSessionController extends ChangeNotifier { _liveModel = useVertexBackend ? FirebaseAI.agentPlatform().liveGenerativeModel( - model: 'gemini-live-2.5-flash-preview-native-audio-09-2025', + model: ExampleModels.liveAgentPlatform, liveGenerationConfig: config, tools: tools, ) : FirebaseAI.googleAI().liveGenerativeModel( - model: 'gemini-2.5-flash-native-audio-preview-09-2025', + model: ExampleModels.liveGoogleAI, liveGenerationConfig: config, tools: tools, ); diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/chat_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/chat_page.dart index 43bf4675003a..c7b0fbe153b7 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/chat_page.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/chat_page.dart @@ -14,6 +14,7 @@ import 'package:flutter/material.dart'; import 'package:firebase_ai/firebase_ai.dart'; +import '../constants.dart'; import '../widgets/message_widget.dart'; class ChatPage extends StatefulWidget { @@ -54,12 +55,12 @@ class _ChatPageState extends State { ); if (widget.useAgentPlatform) { _model = FirebaseAI.agentPlatform().generativeModel( - model: 'gemini-3.5-flash-lite', + model: ExampleModels.flashLite, generationConfig: generationConfig, ); } else { _model = FirebaseAI.googleAI().generativeModel( - model: 'gemini-3.5-flash-lite', + model: ExampleModels.flashLite, generationConfig: generationConfig, ); } diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/function_calling_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/function_calling_page.dart index c464df77b2dc..0c55238ba4d4 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/function_calling_page.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/function_calling_page.dart @@ -15,6 +15,7 @@ import 'package:flutter/material.dart'; import 'package:firebase_ai/firebase_ai.dart'; +import '../constants.dart'; import '../utils/function_call_utils.dart'; import '../widgets/message_widget.dart'; @@ -240,21 +241,21 @@ class _FunctionCallingPageState extends State { : FirebaseAI.googleAI(); _functionCallModel = aiClient.generativeModel( - model: 'gemini-3.5-flash-lite', + model: ExampleModels.flashLite, generationConfig: generationConfig, tools: [ Tool.functionDeclarations([fetchWeatherTool]), ], ); _autoFunctionCallModel = aiClient.generativeModel( - model: 'gemini-3.5-flash-lite', + model: ExampleModels.flashLite, generationConfig: generationConfig, tools: [ Tool.functionDeclarations([_autoFetchWeatherTool]), ], ); _parallelAutoFunctionCallModel = aiClient.generativeModel( - model: 'gemini-3.5-flash-lite', + model: ExampleModels.flashLite, generationConfig: generationConfig, tools: [ Tool.functionDeclarations( @@ -263,21 +264,21 @@ class _FunctionCallingPageState extends State { ], ); _codeExecutionModel = aiClient.generativeModel( - model: 'gemini-3.5-flash-lite', + model: ExampleModels.flashLite, generationConfig: generationConfig, tools: [ Tool.codeExecution(), ], ); _complexSchemaModel = aiClient.generativeModel( - model: 'gemini-3.5-flash-lite', + model: ExampleModels.flashLite, generationConfig: generationConfig, tools: [ Tool.functionDeclarations([_autoPlanVacationTool]), ], ); _refDefJsonSchemaModel = aiClient.generativeModel( - model: 'gemini-3.5-flash-lite', + model: ExampleModels.flashLite, generationConfig: generationConfig, tools: [ Tool.functionDeclarations([_autoProcessTransactionTool]), diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/grounding_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/grounding_page.dart index f8d8315bab92..41a66fbb8d3a 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/grounding_page.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/grounding_page.dart @@ -14,6 +14,7 @@ import 'package:flutter/material.dart'; import 'package:firebase_ai/firebase_ai.dart'; +import '../constants.dart'; import '../widgets/message_widget.dart'; class GroundingPage extends StatefulWidget { @@ -78,7 +79,7 @@ class _GroundingPageState extends State { : FirebaseAI.googleAI(); _model = aiProvider.generativeModel( - model: 'gemini-3.5-flash', + model: ExampleModels.flashLite, tools: tools.isNotEmpty ? tools : null, toolConfig: toolConfig, ); diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/image_generation_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/image_generation_page.dart index 8511cb6c72af..7d14960595fb 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/image_generation_page.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/image_generation_page.dart @@ -16,6 +16,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:firebase_ai/firebase_ai.dart'; +import '../constants.dart'; import '../widgets/message_widget.dart'; class ImageGenerationPage extends StatefulWidget { @@ -54,7 +55,7 @@ class _ImageGenerationPageState extends State { : FirebaseAI.googleAI(); _model = aiClient.generativeModel( - model: 'gemini-2.5-flash-image', + model: ExampleModels.flashImage, generationConfig: GenerationConfig( responseModalities: [ResponseModalities.text, ResponseModalities.image], ), diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/integration_test_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/integration_test_page.dart index f13d02d8a481..a7b7c0518021 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/integration_test_page.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/integration_test_page.dart @@ -16,6 +16,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:firebase_ai/firebase_ai.dart'; +import '../constants.dart'; class IntegrationTestPage extends StatefulWidget { const IntegrationTestPage({super.key}); @@ -47,16 +48,24 @@ class TestItem { final String id; final String name; final String description; + final String model; final Future Function(FirebaseAI provider, TestLogger logger) run; TestResult googleAIResult; TestResult agentPlatformResult; TestItem({ required this.id, - required this.name, + required String name, required this.description, - required this.run, - }) : googleAIResult = TestResult.pending(), + required this.model, + required Future Function( + FirebaseAI provider, + TestLogger logger, + String modelName, + ) run, + }) : name = name.contains(model) ? name : '$name ($model)', + run = ((provider, logger) => run(provider, logger, model)), + googleAIResult = TestResult.pending(), agentPlatformResult = TestResult.pending(); } @@ -86,13 +95,13 @@ class _IntegrationTestPageState extends State { _testCases = [ TestItem( id: '1', - name: 'Stateless Text Gen (gemini-3.5-flash-lite)', + name: 'Stateless Text Gen', + model: ExampleModels.flashLite, description: - 'Verifies simple stateless text generation with a precise answer target using gemini-3.5-flash-lite.', - run: (provider, logger) async { - logger.log('Initializing model gemini-3.5-flash-lite...'); - final model = - provider.generativeModel(model: 'gemini-3.5-flash-lite'); + 'Verifies simple stateless text generation with a precise answer target using ${ExampleModels.flashLite}.', + run: (provider, logger, modelName) async { + logger.log('Initializing model $modelName...'); + final model = provider.generativeModel(model: modelName); const prompt = "Reply with exactly the word 'SUCCESS' in uppercase."; logger.log('Sending prompt: "$prompt"'); final response = await model.generateContent([Content.text(prompt)]); @@ -116,12 +125,13 @@ class _IntegrationTestPageState extends State { ), TestItem( id: '2', - name: 'Stateless Text Gen (gemini-3.5-flash)', + name: 'Stateless Text Gen', + model: ExampleModels.flashLite, description: - 'Verifies simple stateless text generation with a precise answer target using gemini-3.5-flash.', - run: (provider, logger) async { - logger.log('Initializing model gemini-3.5-flash...'); - final model = provider.generativeModel(model: 'gemini-3.5-flash'); + 'Verifies simple stateless text generation with a precise answer target using ${ExampleModels.flashLite}.', + run: (provider, logger, modelName) async { + logger.log('Initializing model $modelName...'); + final model = provider.generativeModel(model: modelName); const prompt = "Reply with exactly the word 'SUCCESS' in uppercase."; logger.log('Sending prompt: "$prompt"'); final response = await model.generateContent([Content.text(prompt)]); @@ -146,12 +156,15 @@ class _IntegrationTestPageState extends State { TestItem( id: '3', name: 'System Instructions', + model: ExampleModels.flashLite, description: 'Verifies system instructions config is serialized and respected.', - run: (provider, logger) async { - logger.log('Initializing model with medieval system instruction...'); + run: (provider, logger, modelName) async { + logger.log( + 'Initializing model $modelName with medieval system instruction...', + ); final model = provider.generativeModel( - model: 'gemini-3.5-flash-lite', + model: modelName, systemInstruction: Content.text( 'You are a medieval knight. Respond only with Shakespearean knightly terms.', ), @@ -188,12 +201,13 @@ class _IntegrationTestPageState extends State { TestItem( id: '4', name: 'Stateful Chat History', + model: ExampleModels.flashLite, description: 'Verifies stateful conversation preservation across turns via ChatSession.', - run: (provider, logger) async { - logger.log('Initializing model and starting ChatSession...'); - final model = - provider.generativeModel(model: 'gemini-3.5-flash-lite'); + run: (provider, logger, modelName) async { + logger + .log('Initializing model $modelName and starting ChatSession...'); + final model = provider.generativeModel(model: modelName); final chat = model.startChat(); const prompt1 = 'My secret agent name is Agent Orange.'; @@ -226,9 +240,10 @@ class _IntegrationTestPageState extends State { TestItem( id: '5', name: 'Auto Function Calling', + model: ExampleModels.flashLite, description: 'Verifies automatic function/tool call execution via AutoFunctionDeclaration.', - run: (provider, logger) async { + run: (provider, logger, modelName) async { logger.log( 'Declaring AutoFunctionDeclaration for getSuperHeroPower...', ); @@ -251,8 +266,11 @@ class _IntegrationTestPageState extends State { }, ); + logger.log( + 'Initializing model $modelName with AutoFunctionDeclaration...', + ); final model = provider.generativeModel( - model: 'gemini-3.5-flash-lite', + model: modelName, tools: [ Tool.functionDeclarations([autoPowerTool]), ], @@ -284,9 +302,10 @@ class _IntegrationTestPageState extends State { TestItem( id: '6', name: 'Manual Function Calling', + model: ExampleModels.flashLite, description: 'Verifies manual function call prediction and response handling.', - run: (provider, logger) async { + run: (provider, logger, modelName) async { logger.log('Declaring FunctionDeclaration...'); final powerTool = FunctionDeclaration( 'getSuperHeroPower', @@ -297,8 +316,11 @@ class _IntegrationTestPageState extends State { }, ); + logger.log( + 'Initializing model $modelName for manual function calling...', + ); final model = provider.generativeModel( - model: 'gemini-3.5-flash-lite', + model: modelName, tools: [ Tool.functionDeclarations([powerTool]), ], @@ -365,14 +387,15 @@ class _IntegrationTestPageState extends State { TestItem( id: '7', name: 'Code Execution Tool', + model: ExampleModels.flashLite, description: 'Verifies Tool.codeExecution() internal code execution and parts extraction.', - run: (provider, logger) async { + run: (provider, logger, modelName) async { logger.log( - 'Initializing model gemini-3.5-flash with Tool.codeExecution()...', + 'Initializing model $modelName with Tool.codeExecution()...', ); final model = provider.generativeModel( - model: 'gemini-3.5-flash', + model: modelName, tools: [Tool.codeExecution()], ); @@ -415,15 +438,16 @@ class _IntegrationTestPageState extends State { ), TestItem( id: '8', - name: 'Search Grounding (gemini-3.5-flash)', + name: 'Search Grounding', + model: ExampleModels.flashLite, description: 'Verifies Google Search Grounding tool configuration and grounding metadata.', - run: (provider, logger) async { + run: (provider, logger, modelName) async { logger.log( - 'Initializing model gemini-3.5-flash with Tool.googleSearch()...', + 'Initializing model $modelName with Tool.googleSearch()...', ); final model = provider.generativeModel( - model: 'gemini-3.5-flash', + model: modelName, tools: [Tool.googleSearch()], ); const prompt = 'Who is the current CEO of Google?'; @@ -457,12 +481,12 @@ class _IntegrationTestPageState extends State { TestItem( id: '9', name: 'Streaming Generation', + model: ExampleModels.flashLite, description: 'Verifies generateContentStream works and aggregates chunks correctly.', - run: (provider, logger) async { - logger.log('Initializing model for streaming...'); - final model = - provider.generativeModel(model: 'gemini-3.5-flash-lite'); + run: (provider, logger, modelName) async { + logger.log('Initializing model $modelName for streaming...'); + final model = provider.generativeModel(model: modelName); const prompt = 'Write a 2-paragraph poem about a computer.'; logger.log('Starting prompt stream: "$prompt"'); @@ -499,15 +523,15 @@ class _IntegrationTestPageState extends State { TestItem( id: '10', name: 'Advanced Token Counting', + model: ExampleModels.flashLite, description: 'Verifies counting tokens on multimodal inputs (text + image asset).', - run: (provider, logger) async { + run: (provider, logger, modelName) async { logger.log('Loading cat.jpg asset...'); final catBytes = await rootBundle.load('assets/images/cat.jpg'); - logger.log('Initializing model for token counting...'); - final model = - provider.generativeModel(model: 'gemini-3.5-flash-lite'); + logger.log('Initializing model $modelName for token counting...'); + final model = provider.generativeModel(model: modelName); final content = [ Content.multi([ @@ -538,14 +562,15 @@ class _IntegrationTestPageState extends State { TestItem( id: '11', name: 'Usage Metadata & Thinking', + model: ExampleModels.flashLite, description: 'Verifies extraction of thought logs and usage metadata with ThinkingConfig.', - run: (provider, logger) async { + run: (provider, logger, modelName) async { logger.log( - 'Initializing model gemini-3.5-flash with ThinkingConfig...', + 'Initializing model $modelName with ThinkingConfig...', ); final model = provider.generativeModel( - model: 'gemini-3.5-flash', + model: modelName, generationConfig: GenerationConfig( thinkingConfig: ThinkingConfig.withThinkingBudget( 2048, @@ -586,14 +611,15 @@ class _IntegrationTestPageState extends State { TestItem( id: '12', name: 'Multimodal Response Modality', + model: ExampleModels.flashImage, description: - 'Verifies text + image response output generation via gemini-2.5-flash-image model.', - run: (provider, logger) async { + 'Verifies text + image response output generation via ${ExampleModels.flashImage} model.', + run: (provider, logger, modelName) async { logger.log( - 'Initializing gemini-2.5-flash-image with dual response modalities...', + 'Initializing model $modelName with dual response modalities...', ); final model = provider.generativeModel( - model: 'gemini-2.5-flash-image', + model: modelName, generationConfig: GenerationConfig( responseModalities: [ ResponseModalities.text, diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/tts_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/tts_page.dart index ee00af84b4fd..68b27242eca4 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/tts_page.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/tts_page.dart @@ -18,6 +18,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:firebase_ai/firebase_ai.dart'; import 'package:waveform_flutter/waveform_flutter.dart'; +import '../constants.dart'; import '../utils/audio_output.dart'; import '../widgets/audio_visualizer.dart'; @@ -154,16 +155,15 @@ class _TTSPageState extends State { } // Use the preview model for TTS - const modelName = 'gemini-3.1-flash-tts-preview'; final GenerativeModel model; if (widget.useAgentPlatform) { model = FirebaseAI.agentPlatform().generativeModel( - model: modelName, + model: ExampleModels.flashTTS, generationConfig: config, ); } else { model = FirebaseAI.googleAI().generativeModel( - model: modelName, + model: ExampleModels.flashTTS, generationConfig: config, ); }