diff --git a/lib/core/util/chat_title.dart b/lib/core/util/chat_title.dart index 413677e..4317aba 100644 --- a/lib/core/util/chat_title.dart +++ b/lib/core/util/chat_title.dart @@ -17,6 +17,7 @@ GPTBOX>>>'''; if (title.endsWith('》')) title = title.substring(0, title.length - 1); title = title.replaceAll(_punctionsRm, ''); + title = title.replaceAll('\n', ' '); if (title.length > _maxLen) { title = title.substring(0, _maxLen); diff --git a/lib/data/store/setting.dart b/lib/data/store/setting.dart index eb6628d..1a9e659 100644 --- a/lib/data/store/setting.dart +++ b/lib/data/store/setting.dart @@ -44,7 +44,7 @@ class SettingStore extends PersistentStore { /// Calcualte tokens length // late final calcTokenLen = property('calcTokenLen', true); - late final replay = property('replay', false); + late final replay = property('replay', true); late final countly = property('countly', true); diff --git a/lib/view/page/home/bottom.dart b/lib/view/page/home/bottom.dart index 521b56c..a050f51 100644 --- a/lib/view/page/home/bottom.dart +++ b/lib/view/page/home/bottom.dart @@ -79,11 +79,11 @@ class _HomeBottom extends StatelessWidget { listenable: _chatType, builder: (_, __) { return switch (_chatType.value) { - ChatType.text || ChatType.img => Badge( - isLabelVisible: _filePicked.value != null, - child: IconButton( - onPressed: () => _onTapImgPick(context), - icon: const Icon(Icons.image, size: 19), + ChatType.text || ChatType.img => IconButton( + onPressed: () => _onTapImgPick(context), + icon: Badge( + isLabelVisible: _filePicked.value != null, + child: const Icon(Icons.image, size: 19), ), ), // ChatType.audio => const IconButton( diff --git a/lib/view/page/home/chat.dart b/lib/view/page/home/chat.dart index 0a0c0ce..312b6dc 100644 --- a/lib/view/page/home/chat.dart +++ b/lib/view/page/home/chat.dart @@ -287,11 +287,6 @@ class _ChatPageState extends State<_ChatPage> ListenableBuilder( listenable: _sendBtnRN, builder: (_, __) { - /// TODO: Can't replay image message. - final isImgChat = - chatItem.content.any((e) => e.type == ChatContentType.image) || - _chatType.value != ChatType.text; - if (isImgChat) return UIs.placeholder; final isWorking = _chatStreamSubs.containsKey(_curChatId); if (isWorking) return UIs.placeholder; return buildCircleBtn( diff --git a/lib/view/page/home/req.dart b/lib/view/page/home/req.dart index 0337552..89551ab 100644 --- a/lib/view/page/home/req.dart +++ b/lib/view/page/home/req.dart @@ -1,7 +1,7 @@ /// OpenAI chat request related funcs part of 'home.dart'; -bool _checkSettingsValid(BuildContext context) { +bool _validChatCfg(BuildContext context) { final config = OpenAICfg.current; final urlEmpty = config.url == 'https://api.openai.com' || config.url.isEmpty; if (urlEmpty && config.key.isEmpty) { @@ -15,7 +15,7 @@ bool _checkSettingsValid(BuildContext context) { /// Auto select model and send the request void _onCreateRequest(BuildContext context, String chatId) async { - if (!_checkSettingsValid(context)) return; + if (!_validChatCfg(context)) return; final chatType = _chatType.value; final notSupport = switch (chatType) { @@ -441,7 +441,7 @@ void _onReplay({ required String chatId, required ChatHistoryItem item, }) async { - if (!_checkSettingsValid(context)) return; + if (!_validChatCfg(context)) return; // If is receiving the reply, ignore this action if (_chatStreamSubs.containsKey(chatId)) { @@ -473,82 +473,17 @@ void _onReplay({ chatHistory.items.removeAt(itemIdx + 1); } - final config = OpenAICfg.current; - final questionContent = switch (( - config.prompt, - config.historyLen, - chatHistory.items.length, - )) { - ('', _, _) => item.toMarkdown, + chatHistory.items.removeAt(itemIdx); - /// If prompt is not empty and historyCount == null || 0, - /// append it to the input - (final prompt, 0, _) => '$prompt\n${item.toMarkdown}', + final text = + item.content.firstWhereOrNull((e) => e.type == ChatContentType.text)?.raw; + if (text != null) _inputCtrl.text = text; + final img = item.content + .firstWhereOrNull((e) => e.type == ChatContentType.image) + ?.raw; + if (img != null) _filePicked.value = XFile(img); - /// If this the first msg, append it to the input - (final prompt, _, 1) => '$prompt\n${item.toMarkdown}', - _ => item.content.first.raw, - }; - final question = ChatHistoryItem.single( - raw: questionContent, - role: ChatRole.user, - ); - - final historyCarried = chatHistory.items.reversed - .take(config.historyLen) - .map( - (e) => e.toOpenAI, - ) - .toList(); - - final chatStream = OpenAI.instance.chat.createStream( - model: config.model, - messages: [...historyCarried.reversed, question.toOpenAI], - ); - final assistReply = ChatHistoryItem.single(role: ChatRole.assist); - chatHistory.items.insert(itemIdx + 1, assistReply); - _chatRN.build(); - try { - final sub = chatStream.listen( - (event) { - final delta = event.choices.first.delta.content?.first?.text; - if (delta == null) return; - assistReply.content.first.raw += delta; - _chatItemRNMap[assistReply.id]?.build(); - - _autoScroll(chatId); - }, - onError: (e, trace) { - Loggers.app.warning('Listen chat stream: $e'); - _onStopStreamSub(chatId); - - final msg = 'Error: $e\nTrace:\n$trace'; - chatHistory.items.add(ChatHistoryItem.single( - raw: msg, - role: ChatRole.system, - )); - _chatRN.build(); - _storeChat(chatId); - _sendBtnRN.build(); - }, - onDone: () { - _onStopStreamSub(chatId); - _storeChat(chatId); - _sendBtnRN.build(); - _appbarTitleRN.build(); - SyncService.sync(); - }, - ); - _chatStreamSubs[chatId] = sub; - _sendBtnRN.build(); - } catch (e) { - _onStopStreamSub(chatId); - final msg = 'Chat stream: $e'; - Loggers.app.warning(msg); - context.showSnackBar(msg); - assistReply.content.first.raw += '\n$msg'; - _sendBtnRN.build(); - } + _onCreateRequest(context, chatId); } void _onErr(Object e, StackTrace s, String chatId, String action) { diff --git a/lib/view/page/setting.dart b/lib/view/page/setting.dart index ad0f0f4..a8a42eb 100644 --- a/lib/view/page/setting.dart +++ b/lib/view/page/setting.dart @@ -692,7 +692,7 @@ class _SettingPageState extends State { Widget _buildReplay() { return ListTile( leading: const Icon(Icons.replay), - title: Text('${l10n.replay} (experimental)'), + title: Text(l10n.replay), trailing: StoreSwitch(prop: _store.replay), ); }