Skip to content

Commit eca5a9e

Browse files
authored
Merge pull request #54 from geeker-ai/gemini-stream
feat: gemini-pro chat with stream
2 parents 71f9c4f + f87ec42 commit eca5a9e

File tree

4 files changed

+174
-11
lines changed

4 files changed

+174
-11
lines changed

lib/util/input_submit_util.dart

+166-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import 'dart:convert';
22
// import 'dart:math';
3+
// import 'dart:math';
34

45
import 'package:dart_openai/dart_openai.dart';
6+
import 'package:flutter_gemini/flutter_gemini.dart';
57
// import 'package:extended_image/extended_image.dart';
68
import 'package:geek_chat/controller/chat_message_controller.dart';
79
import 'package:geek_chat/controller/chat_session_controller.dart';
@@ -396,6 +398,118 @@ class InputSubmitUtil {
396398
}, onError: () {});
397399
}
398400

401+
submitGeminiModel(
402+
ChatSessionController chatSessionController,
403+
ChatMessageController chatMessageController,
404+
SettingsServerController settingsServerController,
405+
QuestionInputController questionInputController) {
406+
String model = chatSessionController.currentSession.model;
407+
AiModel aiModel = AppConstants.getAiModel(model);
408+
409+
/// 创建用户输入的Message
410+
MessageModel userMessage = MessageModel(
411+
msgId: const Uuid().v4(),
412+
role: MessageRole.user.name,
413+
content: questionInputController.inputText,
414+
sId: chatSessionController.currentSession.sid,
415+
model: chatSessionController.currentSession.model,
416+
msgType: 1,
417+
synced: false,
418+
generating: false,
419+
updated: getCurrentDateTime(),
420+
);
421+
422+
/// add quotes in the user message
423+
if (questionInputController.questionInputModel.quotedMessages.isNotEmpty) {
424+
userMessage.quotes = [];
425+
for (MessageModel msg
426+
in questionInputController.questionInputModel.quotedMessages) {
427+
userMessage.quotes!.add(msg.msgId);
428+
}
429+
}
430+
431+
Map<String, String>? headers;
432+
String? baseUrl;
433+
434+
MessageModel targetMessage = MessageModel(
435+
msgId: const Uuid().v4(),
436+
role: MessageRole.assistant.name,
437+
content: "",
438+
sId: chatSessionController.currentSession.sid,
439+
model: chatSessionController.currentSession.model,
440+
msgType: 1,
441+
synced: false,
442+
updated: getCurrentDateTime() + 1,
443+
generating: true,
444+
);
445+
446+
String version = "v1";
447+
if (settingsServerController.defaultServer.provider == "geekerchat") {
448+
headers = {
449+
"Authorization":
450+
"Bearer ${settingsServerController.defaultServer.apiKey}"
451+
};
452+
baseUrl = "${settingsServerController.defaultServer.apiHost}/";
453+
// baseUrl = "http://localhost:3008/";
454+
version = "v1beta";
455+
}
456+
try {
457+
//
458+
Gemini.init(
459+
apiKey: settingsServerController.defaultServer.apiKey,
460+
baseURL: baseUrl,
461+
version: version,
462+
headers: headers,
463+
// enableDebugging: true,
464+
generationConfig: GenerationConfig(maxOutputTokens: 2048),
465+
);
466+
final gemini = Gemini.instance;
467+
final res = gemini.streamChat(
468+
getGeminiChatMessages(
469+
chatMessageController.messages,
470+
chatSessionController.currentSession,
471+
userMessage,
472+
aiModel,
473+
questionInputController.questionInputModel.quotedMessages),
474+
modelName: "models/${aiModel.modelName}");
475+
chatMessageController.addMessage(userMessage);
476+
chatMessageController.addMessage(targetMessage);
477+
chatMessageController.update();
478+
res.listen((event) {
479+
String? content = event.content?.parts?.first.text;
480+
if (content != null) {
481+
targetMessage.content = "${targetMessage.content}$content";
482+
targetMessage.streamContent = targetMessage.content;
483+
}
484+
// }
485+
}, onDone: () {
486+
//
487+
targetMessage.closeStream();
488+
chatMessageController.saveMessage(userMessage);
489+
chatMessageController.saveMessage(targetMessage);
490+
chatSessionController
491+
.updateSessionLastEdit(chatSessionController.currentSession);
492+
chatSessionController.update();
493+
}, onError: (e) {
494+
targetMessage.content = "$e";
495+
targetMessage.closeStream();
496+
logger.e(e);
497+
});
498+
} on Exception catch (e) {
499+
logger.e("submit google model error: $e");
500+
targetMessage.content = e.toString();
501+
targetMessage.closeStream();
502+
} finally {
503+
// chatMessageController.addMessage(userMessage);
504+
// chatMessageController.addMessage(targetMessage);
505+
// chatMessageController.saveMessage(userMessage);
506+
// chatMessageController.saveMessage(targetMessage);
507+
// chatSessionController
508+
// .updateSessionLastEdit(chatSessionController.currentSession);
509+
// chatMessageController.update();
510+
}
511+
}
512+
399513
submitGoogleModel(
400514
ChatSessionController chatSessionController,
401515
ChatMessageController chatMessageController,
@@ -488,19 +602,60 @@ class InputSubmitUtil {
488602
}
489603
}
490604

605+
List<Content> getGeminiChatMessages(List<MessageModel> historyMessages,
606+
SessionModel currentSession, MessageModel userMessage, AiModel aiModel,
607+
[List<MessageModel>? quotedMessages]) {
608+
List<Content> messages = [];
609+
int maxInputTokens = 20000;
610+
maxInputTokens = maxInputTokens -
611+
numTokenCounter(aiModel.modelName, userMessage.content);
612+
// String userMessageContent = userMessage.content;
613+
List<Parts> parts = [];
614+
if (quotedMessages != null && quotedMessages.isNotEmpty) {
615+
for (MessageModel message in quotedMessages) {
616+
// userMessageContent = "${message.content} \n $userMessageContent";
617+
parts.add(Parts(text: message.content));
618+
}
619+
// messages.add(Content(parts: [Parts(text: userMessageContent)]));
620+
parts.add(Parts(text: userMessage.content));
621+
messages.add(Content(parts: parts, role: 'user'));
622+
return messages;
623+
}
624+
int messageCount = 0;
625+
626+
for (int i = 0; i < historyMessages.length; i++) {
627+
MessageModel message = historyMessages[i];
628+
logger.d("message: ${message.content}");
629+
maxInputTokens =
630+
maxInputTokens - numTokenCounter(aiModel.modelName, message.content);
631+
if (maxInputTokens > 0 &&
632+
currentSession.maxContextMsgCount >= messageCount) {
633+
if (message.role == MessageRole.assistant.name) {
634+
if (i < (historyMessages.length - 1) &&
635+
historyMessages[i + 1].role == MessageRole.user.name) {
636+
messages.insert(0,
637+
Content(parts: [Parts(text: message.content)], role: "model"));
638+
messages.insert(
639+
0,
640+
Content(
641+
parts: [Parts(text: historyMessages[i + 1].content)],
642+
role: "user"));
643+
i++;
644+
messageCount += 2;
645+
}
646+
}
647+
}
648+
}
649+
messages
650+
.add(Content(parts: [Parts(text: userMessage.content)], role: "user"));
651+
return messages;
652+
}
653+
491654
List<ChatMessage> getGoogleChatMessages(List<MessageModel> historyMessages,
492655
SessionModel currentSession, MessageModel userMessage, AiModel aiModel,
493656
[List<MessageModel>? quotedMessages]) {
494657
List<ChatMessage> messages = [];
495658
int maxInputTokens = 20000; //32760;
496-
// messages.add(ChatMessage.humanText(currentSession.prompt.content));
497-
// messages.add(ChatMessage.humanText("Hi"));
498-
// messages.add(ChatMessage.ai("Hello! How can I assist you today?"));
499-
// messages.add(ChatMessage.humanText(userMessage.content));
500-
// if (historyMessages.isNotEmpty) {
501-
// historyMessages.first.role != "user";
502-
// historyMessages.removeAt(0);
503-
// }
504659
String nextRole = "user";
505660
String currentRole = "";
506661
maxInputTokens = maxInputTokens -
@@ -518,6 +673,8 @@ class InputSubmitUtil {
518673
for (MessageModel message in historyMessages) {
519674
maxInputTokens =
520675
maxInputTokens - numTokenCounter(aiModel.modelName, message.content);
676+
logger.d(
677+
"maxInputTokens: $maxInputTokens , currentSession.maxContextMsgCount: ${currentSession.maxContextMsgCount}");
521678
if (maxInputTokens > 0 &&
522679
currentSession.maxContextMsgCount > messageCount) {
523680
if (message.role == nextRole) {
@@ -587,7 +744,7 @@ class InputSubmitUtil {
587744
settingsServerController, questionInputController);
588745
return;
589746
} else if (aiModel.aiType == AiType.google) {
590-
await InputSubmitUtil.instance.submitGoogleModel(
747+
await InputSubmitUtil.instance.submitGeminiModel(
591748
chatSessionController,
592749
chatMessageController,
593750
settingsServerController,

macos/Flutter/GeneratedPluginRegistrant.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import window_manager
1616
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
1717
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
1818
IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin"))
19-
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
19+
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
2020
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
2121
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
2222
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))

macos/Podfile.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,4 @@ SPEC CHECKSUMS:
5656

5757
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
5858

59-
COCOAPODS: 1.14.1
59+
COCOAPODS: 1.13.0

pubspec.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,12 @@ dependencies:
175175
# https://pub.dev/packages/langchain_google
176176
langchain_google: ^0.2.1+2
177177
langchain: ^0.3.0
178+
# flutter_gemini: ^2.0.0
179+
flutter_gemini:
180+
# path: ../flutter_gemini
181+
git:
182+
url: https://github.com/zmhu/flutter_gemini.git
183+
ref: main
178184

179185
dev_dependencies:
180186
flutter_test:

0 commit comments

Comments
 (0)