Flutter Firebase集成插件langchain_firebase的使用
Flutter Firebase集成插件langchain_firebase的使用
langchain_firebase
是一个用于在 Flutter 应用中集成 Firebase 的库。本文将详细介绍如何使用 langchain_firebase
插件来实现与 Firebase 的集成。
功能
- Chat模型:
ChatFirebaseVertexAI
:这是Vertex AI for Firebase
API 的封装,支持 Gemini 模型。
许可证
langchain_firebase
项目受 MIT 许可证保护。
完整示例代码
// 忽略以下规则:公共成员应公开文档,避免打印
// 版权所有 2024 Google LLC
//
// 根据Apache许可证2.0版本("许可证")授权;
// 你不能在遵守许可证的情况下使用此文件。
// 你可以从http://www.apache.org/licenses/LICENSE-2.0获得许可证副本。
//
// 除非适用法律要求或书面同意,否则根据许可证分发的软件
// 是按“原样”分发的,没有任何形式的保证或条件。
// 有关许可证下具体语言的许可和限制,请参阅许可证。
import 'dart:convert';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:langchain/langchain.dart';
import 'package:langchain_firebase/langchain_firebase.dart';
void main() async {
await initFirebase();
runApp(const GenerativeAISample());
}
Future<void> initFirebase() async {
await Firebase.initializeApp(
// 替换为你的Firebase项目配置值
options: const FirebaseOptions(
apiKey: 'apiKey',
appId: 'appId',
projectId: 'projectId',
storageBucket: 'storageBucket',
messagingSenderId: 'messagingSenderId',
),
);
}
class GenerativeAISample extends StatelessWidget {
const GenerativeAISample({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter + Firebase Vertex AI + LangChain.dart',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
brightness: Brightness.dark,
seedColor: const Color.fromARGB(255, 171, 222, 244),
),
useMaterial3: true,
),
home: const ChatScreen(
title: 'Flutter + Firebase Vertex AI + LangChain.dart',
),
);
}
}
class ChatScreen extends StatefulWidget {
const ChatScreen({super.key, required this.title});
final String title;
[@override](/user/override)
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: const ChatWidget(),
);
}
}
class ChatWidget extends StatefulWidget {
const ChatWidget({
super.key,
});
[@override](/user/override)
State<ChatWidget> createState() => _ChatWidgetState();
}
class _ChatWidgetState extends State<ChatWidget> {
late final ChatFirebaseVertexAI _model;
late final RunnableSequence<ChatMessage, ChatResult> _chain;
late final ConversationBufferMemory _memory;
late final Tool exchangeRateTool;
final ScrollController _scrollController = ScrollController();
final TextEditingController _textController = TextEditingController();
final FocusNode _textFieldFocus = FocusNode();
final List<({Image? image, String? text, bool fromUser})> _generatedContent =
<({Image? image, String? text, bool fromUser})>[];
bool _loading = false;
[@override](/user/override)
void initState() {
super.initState();
_memory = ConversationBufferMemory(returnMessages: true);
exchangeRateTool = Tool.fromFunction(
name: 'findExchangeRate',
description: '返回给定日期的货币汇率。',
inputJsonSchema: {
'type': 'object',
'properties': {
'currencyDate': {
'type': 'string',
'description': 'YYYY-MM-DD格式的日期或未指定时间段时的"latest"。',
},
'currencyFrom': {
'type': 'string',
'description': '要转换的货币代码,如"USD"。',
},
'currencyTo': {
'type': 'string',
'description': '要转换到的货币代码,如"USD"。',
},
},
'required': ['currencyDate', 'currencyFrom', 'currencyTo'],
},
func: (Map<String, Object?> input) async => {
// 这个假设的API返回JSON数据,例如:
// {"base":"USD","date":"2024-04-17","rates":{"SEK": 0.091}}
'date': input['currencyDate'],
'base': input['currencyFrom'],
'rates': {input['currencyTo']! as String: 0.091},
},
);
final promptTemplate = ChatPromptTemplate.fromTemplates(const [
(ChatMessageType.system, '你是一个有用的助手。'),
(ChatMessageType.messagesPlaceholder, 'history'),
(ChatMessageType.messagePlaceholder, 'input'),
]);
final baseChain = Runnable.mapInput(
(ChatMessage input) async => {
'input': input,
...await _memory.loadMemoryVariables(),
},
).pipe(promptTemplate);
_model = ChatFirebaseVertexAI(
defaultOptions: ChatFirebaseVertexAIOptions(
model: 'gemini-1.5-pro',
tools: [exchangeRateTool],
),
// location: 'us-central1',
);
_chain = baseChain.pipe(_model);
}
void _scrollDown() {
WidgetsBinding.instance.addPostFrameCallback(
(_) async => _scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 750),
curve: Curves.easeOutCirc,
),
);
}
[@override](/user/override)
Widget build(BuildContext context) {
final textFieldDecoration = InputDecoration(
contentPadding: const EdgeInsets.all(15),
hintText: '输入提示...',
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(14)),
borderSide: BorderSide(color: Theme.of(context).colorScheme.secondary),
),
focusedBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(14)),
borderSide: BorderSide(color: Theme.of(context).colorScheme.secondary),
),
);
return Padding(
padding: const EdgeInsets.all(8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ListView.builder(
controller: _scrollController,
itemBuilder: (context, idx) {
final content = _generatedContent[idx];
return MessageWidget(
text: content.text,
image: content.image,
isFromUser: content.fromUser,
);
},
itemCount: _generatedContent.length,
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 15),
child: Row(
children: [
Expanded(
child: TextField(
autofocus: true,
focusNode: _textFieldFocus,
decoration: textFieldDecoration,
controller: _textController,
onSubmitted: _sendChatMessage,
),
),
const SizedBox.square(dimension: 15),
IconButton(
tooltip: 'tokenCount 测试',
onPressed: !_loading
? () async {
await _testCountToken();
}
: null,
icon: Icon(
Icons.numbers,
color: _loading
? Theme.of(context).colorScheme.secondary
: Theme.of(context).colorScheme.primary,
),
),
IconButton(
tooltip: '函数调用测试',
onPressed: !_loading
? () async {
await _testFunctionCalling();
}
: null,
icon: Icon(
Icons.functions,
color: _loading
? Theme.of(context).colorScheme.secondary
: Theme.of(context).colorScheme.primary,
),
),
IconButton(
tooltip: '图像提示',
onPressed: !_loading
? () async {
await _sendImagePrompt(_textController.text);
}
: null,
icon: Icon(
Icons.image,
color: _loading
? Theme.of(context).colorScheme.secondary
: Theme.of(context).colorScheme.primary,
),
),
IconButton(
tooltip: '存储提示',
onPressed: !_loading
? () async {
await _sendStorageUriPrompt(_textController.text);
}
: null,
icon: Icon(
Icons.folder,
color: _loading
? Theme.of(context).colorScheme.secondary
: Theme.of(context).colorScheme.primary,
),
),
if (!_loading)
IconButton(
onPressed: () async {
await _sendChatMessage(_textController.text);
},
icon: Icon(
Icons.send,
color: Theme.of(context).colorScheme.primary,
),
)
else
const CircularProgressIndicator(),
],
),
),
],
),
);
}
Future<void> _sendStorageUriPrompt(String message) async {
setState(() {
_loading = true;
});
try {
final chatMessage = ChatMessage.human(
ChatMessageContent.multiModal([
ChatMessageContent.text(message),
ChatMessageContent.image(
mimeType: 'image/jpeg',
data: 'gs://vertex-ai-example-ef5a2.appspot.com/foodpic.jpg',
),
]),
);
_generatedContent.add((image: null, text: message, fromUser: true));
final response = await _chain.invoke(chatMessage);
final text = response.output.content;
_generatedContent.add((image: null, text: text, fromUser: false));
if (text.isEmpty) {
await _showError('API未响应。');
return;
} else {
setState(() {
_loading = false;
_scrollDown();
});
}
} catch (e) {
await _showError(e.toString());
setState(() {
_loading = false;
});
} finally {
_textController.clear();
setState(() {
_loading = false;
});
_textFieldFocus.requestFocus();
}
}
Future<void> _sendImagePrompt(String message) async {
setState(() {
_loading = true;
});
try {
final ByteData catBytes = await rootBundle.load('assets/images/cat.jpg');
final ByteData sconeBytes = await rootBundle.load('assets/images/scones.jpg');
final chatMessage = ChatMessage.human(
ChatMessageContent.multiModal([
ChatMessageContent.text(message),
ChatMessageContent.image(
mimeType: 'image/jpeg',
data: base64Encode(catBytes.buffer.asUint8List()),
),
ChatMessageContent.image(
mimeType: 'image/jpeg',
data: base64Encode(sconeBytes.buffer.asUint8List()),
),
]),
);
_generatedContent
..add(
(
image: Image.asset('assets/images/cat.jpg'),
text: message,
fromUser: true,
),
)
..add(
(
image: Image.asset('assets/images/scones.jpg'),
text: null,
fromUser: true,
),
);
final response = await _chain.invoke(chatMessage);
final text = response.output.content;
_generatedContent.add((image: null, text: text, fromUser: false));
if (text.isEmpty) {
await _showError('API未响应。');
return;
} else {
await _memory.saveContext(
inputValues: {'input': chatMessage},
outputValues: {'output': response.output},
);
setState(() {
_loading = false;
_scrollDown();
});
}
} catch (e) {
await _showError(e.toString());
setState(() {
_loading = false;
});
} finally {
_textController.clear();
setState(() {
_loading = false;
});
_textFieldFocus.requestFocus();
}
}
Future<void> _sendChatMessage(String message) async {
setState(() {
_textController.clear();
_loading = true;
});
try {
final chatMessage = ChatMessage.humanText(message);
_generatedContent.add((image: null, text: message, fromUser: true));
final response = await _chain.invoke(chatMessage);
final text = response.output.content;
_generatedContent.add((image: null, text: text, fromUser: false));
if (text.isEmpty) {
await _showError('API未响应。');
return;
} else {
await _memory.saveContext(
inputValues: {'input': chatMessage},
outputValues: {'output': response.output},
);
setState(() {
_loading = false;
_scrollDown();
});
}
} catch (e) {
await _showError(e.toString());
setState(() {
_loading = false;
});
} finally {
_textController.clear();
setState(() {
_loading = false;
});
_textFieldFocus.requestFocus();
}
}
Future<void> _testFunctionCalling() async {
setState(() {
_loading = true;
});
final chatMessage = ChatMessage.humanText(
'50美元等于多少瑞典克朗?',
);
// 发送消息到生成模型。
var response = await _chain.invoke(chatMessage);
await _memory.saveContext(
inputValues: {'input': chatMessage},
outputValues: {'output': response.output},
);
final toolCalls = response.output.toolCalls;
// 当模型响应函数调用时,调用该函数。
if (toolCalls.isNotEmpty) {
final toolCall = toolCalls.first;
final result = switch (toolCall.name) {
// 将参数转发到假设的API。
'findExchangeRate' => await exchangeRateTool.invoke(toolCall.arguments),
// 如果模型尝试调用未声明的函数,则抛出异常。
_ => throw UnimplementedError(
'未实现的函数: ${toolCall.name}',
),
};
// 将响应发送回模型,以便它可以使用结果为用户生成文本。
final toolMessage = ChatMessage.tool(
toolCallId: toolCall.id,
content: jsonEncode(result),
);
response = await _chain.invoke(toolMessage);
await _memory.saveContext(
inputValues: {'input': chatMessage},
outputValues: {'output': response.output},
);
}
// 当模型响应非空文本内容时,打印它。
if (response.output.content.isNotEmpty) {
_generatedContent
.add((image: null, text: response.output.content, fromUser: false));
setState(() {
_loading = false;
});
}
}
Future<void> _testCountToken() async {
setState(() {
_loading = true;
});
const prompt = '讲述一个短故事';
final response = await _model.countTokens(PromptValue.string(prompt));
print('token: $response');
setState(() {
_loading = false;
});
}
Future<void> _showError(String message) async {
await showDialog<void>(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('出错了'),
content: SingleChildScrollView(
child: SelectableText(message),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('确定'),
),
],
);
},
);
}
}
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](/user/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,
],
),
),
),
],
);
}
}
更多关于Flutter Firebase集成插件langchain_firebase的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter Firebase集成插件langchain_firebase的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
langchain_firebase
是一个用于在 Flutter 应用中集成 Firebase 和 LangChain 的插件。LangChain 是一个用于构建基于语言模型的应用的框架,而 Firebase 提供了后端服务,如身份验证、数据库和存储等。通过 langchain_firebase
,你可以在 Flutter 应用中轻松地将这两者结合起来。
集成步骤
-
创建 Firebase 项目:
- 访问 Firebase 控制台 并创建一个新项目。
- 在 Firebase 控制台中,添加一个 Android 或 iOS 应用,并按照指示下载
google-services.json
(Android)或GoogleService-Info.plist
(iOS)文件。
-
配置 Flutter 项目:
- 将下载的
google-services.json
文件放入android/app
目录。 - 将
GoogleService-Info.plist
文件放入ios/Runner
目录。
- 将下载的
-
添加依赖: 在
pubspec.yaml
文件中添加langchain_firebase
和其他必要的依赖:dependencies: flutter: sdk: flutter firebase_core: latest_version firebase_auth: latest_version cloud_firestore: latest_version langchain_firebase: latest_version
然后运行
flutter pub get
安装依赖。 -
初始化 Firebase: 在
main.dart
文件中初始化 Firebase:import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Firebase LangChain', home: HomeScreen(), ); } }
-
使用
langchain_firebase
: 你可以在应用中使用langchain_firebase
提供的功能。例如,使用 Firebase 身份验证和 LangChain 进行语言模型交互:import 'package:firebase_auth/firebase_auth.dart'; import 'package:langchain_firebase/langchain_firebase.dart'; import 'package:flutter/material.dart'; class HomeScreen extends StatefulWidget { @override _HomeScreenState createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { final FirebaseAuth _auth = FirebaseAuth.instance; final LangChainFirebase _langChain = LangChainFirebase(); String _response = ''; Future<void> _signIn() async { try { UserCredential userCredential = await _auth.signInAnonymously(); print('Signed in: ${userCredential.user!.uid}'); } catch (e) { print('Error signing in: $e'); } } Future<void> _askQuestion() async { try { String response = await _langChain.askQuestion("What is Flutter?"); setState(() { _response = response; }); } catch (e) { print('Error asking question: $e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Flutter Firebase LangChain'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ ElevatedButton( onPressed: _signIn, child: Text('Sign In Anonymously'), ), ElevatedButton( onPressed: _askQuestion, child: Text('Ask a Question'), ), Text('Response: $_response'), ], ), ), ); } }