import 'dart:convert'; import 'package:electronic_assistant/base/base_controller.dart'; import 'package:electronic_assistant/data/bean/agenda.dart'; import 'package:electronic_assistant/data/bean/chat_item.dart'; import 'package:electronic_assistant/data/bean/file_chat_item.dart'; import 'package:electronic_assistant/data/bean/progressing_chat_item.dart'; import 'package:electronic_assistant/data/bean/reference_chat_item.dart'; import 'package:electronic_assistant/data/bean/stream_chat_origin_data.dart'; import 'package:electronic_assistant/data/bean/talk_info.dart'; import 'package:electronic_assistant/data/repositories/chat_repository.dart'; import 'package:electronic_assistant/data/repositories/talk_repository.dart'; import 'package:electronic_assistant/module/chat/start/view.dart'; import 'package:electronic_assistant/resource/colors.gen.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:uuid/uuid.dart'; import '../../utils/http_handler.dart'; class ChatController extends BaseController { final RefreshController refreshController = RefreshController(); final ScrollController listScrollController = ScrollController(); final TextEditingController inputController = TextEditingController(); final RxList chatItems = [].obs; final Rxn talkInfo = Rxn(); final Rxn agenda = Rxn(); var isOpenStart = false; @override void onInit() { super.onInit(); checkArguments(); } void checkArguments() { List arguments = Get.arguments ?? []; if (arguments.isEmpty) { return; } if (arguments[0] is TalkInfo) { talkInfo.value = arguments[0]; sendInitialMessage(); } else if (arguments[0] is String) { talkRepository .talkInfo(arguments[0]) .then((response) => talkInfo.value = response.talkInfo) .then((_) => sendInitialMessage()); } if (arguments.length > 1 && arguments[1] is Agenda) { agenda.value = arguments[1]; } } void sendInitialMessage() { final talkInfo = this.talkInfo.value; if (talkInfo == null) { return; } chatItems.insert( 0, FileChatItem(talkInfo, id: const Uuid().v4(), conversationId: "", role: "user", content: "", createTime: DateTime.now().toString())); if (agenda.value != null) { _sendMessage("以这份谈话为背景,你为我执行“${agenda.value?.content}”这一事项"); } } @override void onReady() { super.onReady(); refreshController.requestLoading(); } onSendClick() { if (inputController.text.isEmpty) { return; } String chatContent = inputController.text; inputController.clear(); _sendMessage(chatContent); } void loadMoreHistory() { bool isEmpty = chatItems.isEmpty; chatRepository .chatHistory(chatItems.isEmpty ? null : chatItems.last.id) .then((value) => chatItems.addAll(value)) .whenComplete(() => refreshController.loadComplete()) .whenComplete(() { if (isEmpty) { scrollToBottom(); } }); } void showStartSheet(BuildContext context) { if (!isOpenStart) { WidgetsBinding.instance.addPostFrameCallback((_) { showModalBottomSheet( context: context, isScrollControlled: true, barrierColor: ColorName.black55, backgroundColor: ColorName.transparent, builder: (BuildContext context) { return const ChatStartPage(); }, ); }); isOpenStart = true; } } void _sendMessage(String chatContent) { chatItems.insert(0, createUserChatItem(chatContent)); ProgressingChatItem progressingChatItem = ProgressingChatItem( id: const Uuid().v4(), conversationId: chatItems.last.conversationId, role: "assistant", content: "", createTime: DateTime.now().toString(), ); chatItems.insert(0, progressingChatItem); scrollToBottom(); chatRepository .streamChat(chatContent, talkId: talkInfo.value?.id, agendaId: agenda.value?.id) .then((stream) { stream.listen((event) { try { Map json = jsonDecode(event.data); if (json.isEmpty) { return; } StreamChatOriginData data = StreamChatOriginData.fromJson(json); if (data.choices == null || data.choices!.isEmpty) { return; } Delta? delta = data.choices![0].delta; if (delta == null) { return; } progressingChatItem.append(delta.content ?? ""); } catch (ignore) {} }, onDone: () { progressingChatItem.content = progressingChatItem.streamContent.value; progressingChatItem.isFinished.value = true; }, onError: (error) { progressingChatItem.isFailed.value = true; progressingChatItem.error.value = "网络错误,请检查网络连接"; debugPrint("error: $error"); debugPrintStack(); }); }).catchError((error) { progressingChatItem.isFailed.value = true; if (error is ServerErrorException) { progressingChatItem.error.value = error.message ?? "服务出错,请稍后再试"; } else { progressingChatItem.error.value = "网络错误,请检查网络连接"; debugPrint("error: $error"); debugPrintStack(); } }); } void scrollToBottom() { Future.delayed(const Duration(milliseconds: 300), () { listScrollController.animateTo( 0, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); }); } createUserChatItem(String chatContent) { final id = const Uuid().v4(); final conversationId = chatItems.isEmpty ? "" : chatItems.last.conversationId; const role = "user"; final content = chatContent; final createTime = DateTime.now().toString(); final talkInfo = this.talkInfo.value; return talkInfo == null ? ChatItem( id: id, conversationId: conversationId, role: role, content: chatContent, createTime: createTime) : ReferenceChatItem( talkInfo: talkInfo, id: id, conversationId: conversationId, role: role, content: content, createTime: createTime); } onDeleteReference() { talkInfo.value = null; agenda.value = null; } }