Created
March 7, 2025 19:10
-
-
Save suesitran/ff9fd0091738badf00759c19bd489d8a to your computer and use it in GitHub Desktop.
ChatService - VertexAI in Firebase for Flutter Project
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:cloud_firestore/cloud_firestore.dart'; | |
import 'package:file_picker/file_picker.dart'; | |
import 'package:firebase_storage/firebase_storage.dart'; | |
import 'package:firebase_vertexai/firebase_vertexai.dart'; | |
import 'package:flutter/foundation.dart'; | |
import 'package:mime/mime.dart'; | |
class ChatService { | |
late final ChatSession _chatSession; | |
ChatService() { | |
final GenerativeModel model = | |
FirebaseVertexAI.instance.generativeModel(model: 'gemini-2.0-flash'); | |
_chatSession = model.startChat(); | |
} | |
void sendMessage(String prompt, [PlatformFile? file]) async { | |
// get mimetype of file if available | |
final String? mimeType = | |
file != null ? lookupMimeType(file.xFile.name) : null; | |
// create a new document for this chat message | |
final messagesRef = FirebaseFirestore.instance.collection('messages').doc(); | |
Attachment? attachment; | |
if (file != null) { | |
// file is available, upload it to Cloud Storage | |
final Uint8List bytes = await file.xFile.readAsBytes(); | |
await FirebaseStorage.instance.ref(messagesRef.path).putData( | |
bytes, | |
SettableMetadata( | |
contentType: mimeType ?? 'application/octet-stream')); | |
attachment = Attachment( | |
file: file.xFile.path, | |
mimeType: mimeType ?? 'application/octet-stream', | |
data: bytes); | |
} | |
// save Message to Firestore | |
messagesRef.set(Message( | |
time: DateTime.now(), | |
sender: Sender.user, | |
message: prompt, | |
attachment: attachment) | |
.toMap()); | |
// send to Gemini | |
final GenerateContentResponse response = | |
await _chatSession.sendMessage(Content('user', [ | |
TextPart(prompt), | |
if (attachment != null) | |
InlineDataPart(attachment.mimeType, attachment.data) | |
])); | |
// add response to Firestore | |
FirebaseFirestore.instance.collection('messages').add(Message( | |
time: DateTime.now(), | |
sender: Sender.model, | |
message: response.text ?? 'Unable to generate response', | |
).toMap()); | |
} | |
} | |
/// data classes | |
enum Sender { | |
// when user sends message to Gemini | |
user, | |
// when Gemini sends message to user | |
model; | |
static Sender fromName(String? name) { | |
if (name == 'user') { | |
return Sender.user; | |
} else { | |
return Sender.model; | |
} | |
} | |
} | |
class Message { | |
final String id; | |
final DateTime time; | |
final Sender sender; | |
final String message; | |
final Attachment? attachment; | |
Message({ | |
required this.time, | |
required this.sender, | |
required this.message, | |
this.attachment, | |
}) : id = ''; | |
Message.fromMap(this.id, Map<String, dynamic> map) | |
: time = (map['time'] as Timestamp).toDate(), | |
sender = Sender.fromName(map['from']), | |
message = map['message'], | |
attachment = map['attachment'] != null | |
? Attachment.fromMap(map['attachment']) | |
: null; | |
Map<String, dynamic> toMap() => { | |
'time': Timestamp.fromDate(time), | |
'from': sender.name, | |
'message': message, | |
'attachment': attachment?.toMap() | |
}; | |
} | |
class Attachment { | |
final String file; | |
final String mimeType; | |
final Uint8List data; | |
Attachment({required this.file, required this.mimeType, required this.data}); | |
Map<String, dynamic> toMap() => | |
{'file': file, 'mimeType': mimeType, 'data': data.toList()}; | |
Attachment.fromMap(Map<String, dynamic> map) | |
: file = map['file'], | |
mimeType = map['mimeType'], | |
data = Uint8List.fromList(map['data'] ?? []); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment