import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:agent_service/agent_service.dart'; import 'package:logger/logger.dart'; import 'package:chat_storage/chat_storage.dart'; class Message { final bool isUser; final String text; final DateTime timestamp; final bool isRecognizing; // 是否为语音识别中的临时消息 Message({ required this.isUser, required this.text, DateTime? timestamp, this.isRecognizing = false, }) : this.timestamp = timestamp ?? DateTime.now(); } class AgentController extends GetxController { static AgentController get to => Get.find(); final logger = Logger(); // 聊天消息列表 final messages = [].obs; // 控制器 final TextEditingController textController = TextEditingController(); final ScrollController scrollController = ScrollController(); // 状态标志 final isInitialized = false.obs; final isListening = false.obs; final isSpeaking = false.obs; final isProcessing = false.obs; // 输入模式控制 final isTextInputMode = true.obs; // 当前输入的文本内容 final currentText = ''.obs; // 流事件订阅 StreamSubscription? _eventSubscription; // 用于跟踪AI回复状态 bool _isNewAssistantResponse = true; String _currentResponseId = ''; // 会话ID final String sessionId = 'default_agent'; // 是否已加载聊天记录 final isHistoryLoaded = false.obs; @override void onInit() { super.onInit(); _loadChatHistory(); _subscribeToEvents(); } @override void onClose() { AgentService.stopConversation(); AgentService.stopTts(); textController.dispose(); scrollController.dispose(); _eventSubscription?.cancel(); super.onClose(); } // 加载聊天历史记录 Future _loadChatHistory() async { try { logger.i('正在加载聊天历史...'); final result = await ChatStorage.getMessages( sessionId: sessionId, page: 1, pageSize: 20, ); if (result.messages.isNotEmpty) { // 转换并添加消息到列表 final historyMessages = result.messages.map((msg) => Message( isUser: msg.sender == 'user', text: msg.message, timestamp: DateTime.parse(msg.timestamp), )).toList(); // 消息按时间排序,最新的消息在底部 historyMessages.sort((a, b) => a.timestamp.compareTo(b.timestamp)); messages.assignAll(historyMessages); logger.i('成功加载 ${historyMessages.length} 条聊天记录'); } else { // 没有历史记录时添加欢迎消息 _addWelcomeMessage(); logger.i('没有历史记录,添加欢迎消息'); } } catch (e) { logger.e('加载聊天历史失败: $e'); _addWelcomeMessage(); } finally { isHistoryLoaded.value = true; messages.refresh(); // 在加载完成后直接定位到底部,无需动画 WidgetsBinding.instance.addPostFrameCallback((_) { scrollController.jumpTo(scrollController.position.maxScrollExtent); }); } } // 添加欢迎消息 void _addWelcomeMessage() { messages.add(Message( isUser: false, text: '你好!我是DeepVoice AI助手,我能帮你:\n\n' '• 回答问题和提供信息\n' '• 实时语音翻译\n' '• 推荐音乐和播客\n' '• 语音备忘录和提醒\n' '• 控制耳机功能\n\n' '有什么我可以帮你的吗?', )); } // 订阅代理服务事件 void _subscribeToEvents() { _eventSubscription = AgentService.events.listen((event) { switch (event.type) { case AgentServiceEventType.recognitionStarted: isListening.value = true; isTextInputMode.value = false; isTextInputMode.refresh(); // 只标记识别开始,不添加消息 break; case AgentServiceEventType.recognitionStopped: isListening.value = false; isTextInputMode.value = true; isTextInputMode.refresh(); break; case AgentServiceEventType.recognizing: final text = event.data['text'] ?? ''; currentText.value = text; // 保留当前文本,以便其他地方使用 if (text.isNotEmpty) { // 查找是否有正在识别中的消息 final index = messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); if (index >= 0) { // 更新已有的识别中消息 messages[index] = Message( isUser: true, text: text, isRecognizing: true, timestamp: messages[index].timestamp, ); } else { // 没有识别中的消息,创建新消息 messages.add(Message( isUser: true, text: text, isRecognizing: true, )); } messages.refresh(); _scrollToBottom(); } break; case AgentServiceEventType.recognitionResult: final text = event.data['text'] ?? ''; // 查找是否有正在识别中的消息 final index = messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); if (text.isNotEmpty) { if (index >= 0) { // 将识别中的消息转换为最终消息 messages[index] = Message( isUser: true, text: text, timestamp: messages[index].timestamp, ); messages.refresh(); } else { // 如果没有找到识别中的消息,添加新消息 messages.add(Message(isUser: true, text: text)); } isProcessing.value = true; // 标记为新的AI回复 _isNewAssistantResponse = true; // 滚动到底部 _scrollToBottom(); } else if (index >= 0) { // 如果识别结果为空,移除临时的识别消息 messages.removeAt(index); messages.refresh(); } break; case AgentServiceEventType.ttsStarted: isSpeaking.value = true; break; case AgentServiceEventType.ttsCompleted: case AgentServiceEventType.ttsStopped: case AgentServiceEventType.ttsCanceled: isSpeaking.value = false; break; case AgentServiceEventType.assistantToken: if (!isProcessing.value) isProcessing.value = true; final token = event.data['token'] ?? ''; final responseId = event.data['responseId'] ?? ''; if (token.isNotEmpty) { // 如果是新的回复或者响应ID改变,创建新消息 if (_isNewAssistantResponse || (_currentResponseId != responseId && responseId.isNotEmpty)) { messages.add(Message(isUser: false, text: token)); _isNewAssistantResponse = false; _currentResponseId = responseId; } else { // 否则更新最后一条消息 final lastIndex = messages.length - 1; if (lastIndex >= 0 && !messages[lastIndex].isUser) { messages[lastIndex] = Message( isUser: false, text: messages[lastIndex].text + token, timestamp: messages[lastIndex].timestamp, ); } } // 滚动到底部 _scrollToBottom(); } break; case AgentServiceEventType.assistantResponse: isProcessing.value = false; // 标记当前回复完成 _isNewAssistantResponse = true; break; case AgentServiceEventType.error: isListening.value = false; isSpeaking.value = false; isProcessing.value = false; // 移除临时的识别消息 final index = messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); if (index >= 0) { messages.removeAt(index); messages.refresh(); } logger.e('代理服务错误: ${event.data['message']}'); break; case AgentServiceEventType.autoStop: isListening.value = false; // 移除临时的识别消息 final index = messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); if (index >= 0) { // 只有当消息为空或文本太短时才移除 if (messages[index].text.trim().length < 2) { messages.removeAt(index); messages.refresh(); } else { // 否则转换为正式消息 messages[index] = Message( isUser: true, text: messages[index].text, timestamp: messages[index].timestamp, ); messages.refresh(); // 发送此消息到AI处理 isProcessing.value = true; _isNewAssistantResponse = true; AgentService.processTextInput(messages[index].text, speakResponse: true); } } break; case AgentServiceEventType.functionCall: // final functionName = event.data['name'] ?? ''; // final arguments = event.data['arguments'] ?? ''; // logger.i('mcp: $functionName, $arguments'); break; case AgentServiceEventType.functionCallResult: final result = event.data['result'] ?? ''; final functionCall = event.data['function_call'] ?? ''; break; default: break; } }); } // 滚动到底部 void _scrollToBottom() { WidgetsBinding.instance.addPostFrameCallback((_) { if (scrollController.hasClients) { try { scrollController.animateTo( scrollController.position.maxScrollExtent, duration: Duration(milliseconds: 300), curve: Curves.easeOut, ); } catch (e) { logger.e('滚动到底部失败: $e'); } } }); } // 发送文本消息 Future sendTextMessage() async { final text = textController.text.trim(); if (text.isEmpty) return; final message = Message(isUser: true, text: text); messages.add(message); textController.clear(); // 滚动到底部 _scrollToBottom(); try { isProcessing.value = true; // 标记为新的AI回复 _isNewAssistantResponse = true; await AgentService.processTextInput(text, speakResponse: true); } catch (e) { logger.e('发送文本消息失败: $e'); isProcessing.value = false; } } // 开始语音输入 Future startVoiceInput() async { if (isListening.value) return; try { await AgentService.startConversation(); } catch (e) { logger.e('启动语音输入失败: $e'); } } // 停止语音输入 Future stopVoiceInput() async { if (!isListening.value) return; try { await AgentService.stopConversation(); } catch (e) { logger.e('停止语音输入失败: $e'); } } // 清除聊天历史 Future clearChat() async { try { await AgentService.clearChatHistory(); // 清空本地消息列表 messages.clear(); // 添加欢迎消息 _addWelcomeMessage(); // 重置回复状态 _isNewAssistantResponse = true; } catch (e) { logger.e('清除聊天历史失败: $e'); } } // 切换输入模式 void toggleInputMode() { isTextInputMode.toggle(); // 切换到语音模式时,直接开始语音输入 if (!isTextInputMode.value) { startVoiceInput(); } // 切换到文本模式时,直接停止语音输入 else { stopVoiceInput(); } } }