import 'dart:async'; import 'package:get/get.dart'; import 'package:flutter/widgets.dart'; import '../models/message_model.dart'; import '../../../core/utils/logger.dart'; import '../../../data/services/volcano_ai_service.dart'; import '../../../data/services/tts_service.dart'; import '../../../data/providers/agent_provider.dart'; import '../../../data/models/agent.dart'; import '../../../data/services/asr_service.dart'; import '../../../data/services/chat_history_service.dart'; import '../../../data/models/events/voice_interaction_event.dart'; import '../../../data/services/voice_interaction_service.dart'; class ChatController extends GetxController { // 服务 final VolcanoAIService _aiService = Get.find(); final TtsService _ttsService = Get.find(); final AsrService _asrService = Get.find(); final ChatHistoryService _historyService = Get.find(); final VoiceInteractionService _voiceInteractionService = Get.find(); // 添加ScrollController用于控制列表滚动 final ScrollController scrollController = ScrollController(); // Agent 信息 late final Agent agent; // 消息列表 final RxList messages = [].obs; // 输入控制 final RxString inputText = ''.obs; // 状态控制 final RxBool isLoading = false.obs; final RxBool isSending = false.obs; final RxBool isTtsEnabled = true.obs; final RxBool isVoiceRecognitionActive = false.obs; // 语音交互模式 bool _isVoiceInteractionMode = false; // 语音识别状态 final RxString recognizedText = ''.obs; final RxBool isListening = false.obs; final RxBool isSpeechDetected = false.obs; // 临时消息索引,用于更新临时消息 int _tempMessageIndex = -1; // 流式响应控制 StreamSubscription? _aiResponseSubscription; StreamSubscription? _recognitionSubscription; StreamSubscription? _voiceInteractionSubscription; // 取消标志 bool _shouldCancelAiResponse = false; // 自动停止语音识别计时器 Timer? _autoStopTimer; // 记录最后一次活动时间 DateTime _lastActivityTime = DateTime.now(); @override Future onInit() async { super.onInit(); Logger.info('ChatController onInit'); // 从路由参数获取Agent信息 final Map args = Get.arguments ?? {}; final String agentId = args['agent'] ?? args['agentId'] ?? 'personal_assistant'; // 获取Agent final Agent? foundAgent = AgentProvider.getAgentById(agentId); if (foundAgent == null) { Logger.error('无法找到Agent: $agentId'); Get.back(); return; } agent = foundAgent; _ttsService.initialize(); _asrService.initialize(); // 设置语音交互模式 _isVoiceInteractionMode = args['isVoiceInteraction'] == true; // 设置TTS服务 await _ttsService.setVoice(agent.voice); // 加载聊天历史 _loadChatHistory(); // 如果没有历史记录,添加欢迎消息 if (messages.isEmpty && agent.welcomeMessage.isNotEmpty) { messages.add(Message( role: 'assistant', content: agent.welcomeMessage, timestamp: DateTime.now(), )); // 如果启用了TTS,播放欢迎消息 // if (isTtsEnabled.value) { // _ttsService.speak(agent.welcomeMessage); // } } // 如果isVoiceInteraction为true,自动激活语音识别 if (args['isVoiceInteraction'] == true) { _ttsService.speakOnce(agent.voiceWelcomeMessage ?? agent.welcomeMessage); startVoiceRecognition(); // 自动停止计时器会在startVoiceRecognition中启动 } // 订阅VoiceInteractionService事件 _subscribeToVoiceInteractionEvents(); // 使用微任务在布局完成后自动滚动到底部 WidgetsBinding.instance.addPostFrameCallback((_) { _scrollToBottom(); Future.delayed(const Duration(milliseconds: 500), () { _scrollToBottom(); }); }); } /// 订阅VoiceInteractionService事件 void _subscribeToVoiceInteractionEvents() { if (_voiceInteractionService.eventStream != null) { _voiceInteractionSubscription = _voiceInteractionService.eventStream.listen((event) { if (event is RecognitionStartedEvent) { // 收到recognitionStarted事件时,停止当前的AI交互、录音和TTS _stopCurrentInteraction(); } else if (event is ChatHistoryEvent) { // 收到聊天历史事件,添加到聊天记录 _processChatHistoryEvent(event); } }); } } /// 处理聊天历史事件 void _processChatHistoryEvent(ChatHistoryEvent event) { Logger.info('收到后台聊天历史事件: agentId=${event.agentId}'); // 如果消息不是空的且不是当前Agent的,不处理 if (event.userMessage.isEmpty || event.agentId != agent.id) { return; } // 添加用户消息 final userMsg = Message( role: 'user', content: event.userMessage, timestamp: DateTime.fromMillisecondsSinceEpoch(event.timestamp), ); // 添加助手消息 final assistantMsg = Message( role: 'assistant', content: event.assistantMessage, timestamp: DateTime.fromMillisecondsSinceEpoch(event.timestamp + 1), // 助手消息稍后一毫秒 ); // 防止消息重复 if (!_isDuplicateMessage(userMsg) && !_isDuplicateMessage(assistantMsg)) { messages.add(userMsg); messages.add(assistantMsg); } _scrollToBottom(); } /// 停止当前交互 void _stopCurrentInteraction() { Logger.info('收到后台语音识别开始事件,停止当前AI交互'); // 停止当前AI交互 if (isSending.value || _aiResponseSubscription != null) { _interruptCurrentResponse("收到后台语音识别开始事件,中断当前响应"); } // 停止TTS _ttsService.stop(); // 停止语音识别 if (isVoiceRecognitionActive.value) { stopVoiceRecognition(); } } @override void onClose() { Logger.info('ChatController onClose'); // 清理资源 scrollController.dispose(); _aiResponseSubscription?.cancel(); _recognitionSubscription?.cancel(); _voiceInteractionSubscription?.cancel(); _autoStopTimer?.cancel(); // 停止TTS _ttsService.stop(); if (isVoiceRecognitionActive.value) { _asrService.stopContinuousRecognition(); } super.onClose(); } /// 滚动到列表底部方法 void _scrollToBottom() { if (scrollController.hasClients && messages.isNotEmpty) { try { scrollController.animateTo( scrollController.position.maxScrollExtent, duration: Duration(milliseconds: 300), curve: Curves.easeOut, ); } catch (e) { Logger.error('滚动到底部失败', e); } } } /// 中断当前响应 void _interruptCurrentResponse(String reason) { Logger.info('${reason}'); // 停止TTS _ttsService.stop(); // 取消AI响应流订阅 if (_aiResponseSubscription != null) { _shouldCancelAiResponse = true; _aiResponseSubscription!.cancel(); _aiResponseSubscription = null; // 将加载中的消息转换为正常消息,保留已输出的内容 final index = messages.indexWhere((m) => m.isLoading); if (index != -1) { messages[index] = messages[index].copyWith( isLoading: false, ); messages.refresh(); } // 重置发送状态 isSending.value = false; } } // 发送消息 Future sendMessage(String text) async { if (text.trim().isEmpty) return; _voiceInteractionService.pauseVoiceInteraction(); // 中断当前响应 _interruptCurrentResponse("发送新消息,中断当前响应"); // 添加用户消息 final userMessage = Message( role: 'user', content: text, timestamp: DateTime.now(), ); messages.add(userMessage); _scrollToBottom(); // 清空输入框 inputText.value = ''; // 添加加载中的消息 final loadingMessage = Message.loading(); messages.add(loadingMessage); _scrollToBottom(); isSending.value = true; try { // 构建消息历史 final List> messageHistory = _buildMessageHistory(); // 重置取消标志 _shouldCancelAiResponse = false; String fullResponse = ''; // 获取AI响应流 final responseStream = _aiService.sendMessageStream( messages: messageHistory, systemPrompt: agent.systemPrompt, ); // 订阅响应流 _aiResponseSubscription = responseStream.listen( (chunk) { if (_shouldCancelAiResponse) return; fullResponse += chunk; // 更新加载中的消息内容 _updateLoadingMessage(fullResponse); // 如果TTS启用,使用流式处理发送到TTS if (isTtsEnabled.value) { _ttsService.speakStream(chunk); } }, onDone: () { if (_shouldCancelAiResponse) return; // 完成响应,将isLoading设为false _finalizeResponse(fullResponse); // 如果TTS启用,处理缓冲区中的剩余文本 if (isTtsEnabled.value) { _ttsService.flushStream(); } // 保存聊天历史 _saveChatHistory(); }, onError: (error) { Logger.error('AI响应错误', error); // 更新加载中的消息为错误消息 _handleResponseError(); }, ); } catch (e) { Logger.error('发送消息失败', e); _handleResponseError(); } } // 构建消息历史 List> _buildMessageHistory() { final List> messageHistory = []; // 只取最近的10条消息作为上下文 final recentMessages = messages.where((m) => !m.isLoading).toList(); final startIndex = recentMessages.length > 10 ? recentMessages.length - 10 : 0; for (int i = startIndex; i < recentMessages.length; i++) { final m = recentMessages[i]; messageHistory.add({ 'role': m.role, 'content': m.content, }); } return messageHistory; } // 更新加载中的消息 void _updateLoadingMessage(String content) { final index = messages.indexWhere((m) => m.isLoading); if (index != -1) { messages[index] = messages[index].copyWith( content: content, isLoading: true, ); messages.refresh(); _scrollToBottom(); } } // 完成响应 void _finalizeResponse(String content) { final index = messages.indexWhere((m) => m.isLoading); if (index != -1) { messages[index] = messages[index].copyWith( content: content, isLoading: false, ); messages.refresh(); _scrollToBottom(); } isSending.value = false; _aiResponseSubscription = null; } // 处理响应错误 void _handleResponseError() { final index = messages.indexWhere((m) => m.isLoading); if (index != -1) { messages[index] = messages[index].copyWith( content: '抱歉,我遇到了一些问题,无法回应您的请求。', isLoading: false, ); messages.refresh(); } isSending.value = false; _aiResponseSubscription = null; } // 切换TTS状态 void toggleTts() { isTtsEnabled.toggle(); // 如果关闭TTS,停止当前播放 if (!isTtsEnabled.value) { _ttsService.stop(); } } // 开始语音识别 Future startVoiceRecognition() async { if (isVoiceRecognitionActive.value) return; _voiceInteractionService.pauseVoiceInteraction(); try { // 启动连续语音识别 // 注意:在UI更新后,使用模拟波形动画替代实际麦克风输入的波形数据 final recognitionStream = await _asrService.startContinuousRecognition(); isVoiceRecognitionActive.value = true; isListening.value = true; recognizedText.value = ''; // 重置临时消息索引 _tempMessageIndex = -1; // 启动自动停止计时器 _startAutoStopTimer(); // 监听识别事件 _recognitionSubscription = recognitionStream.listen( _handleRecognitionEvent, onError: _handleRecognitionError, ); } catch (e) { Logger.error('启动语音识别失败', e); isVoiceRecognitionActive.value = false; isListening.value = false; } } // 处理语音识别事件 void _handleRecognitionEvent(RecognitionEvent event) { // 更新活动时间 _updateActivityTime(); switch (event.type) { case RecognitionEventType.intermediateResult: _handleRecognizing(event.text); break; case RecognitionEventType.finalResult: _handleFinalResult(event.text); break; case RecognitionEventType.error: _handleRecognitionEventError(event.error); break; default: // 不做任何处理 break; } } // 更新最后活动时间 void _updateActivityTime() { _lastActivityTime = DateTime.now(); } // 启动自动停止计时器 void _startAutoStopTimer() { // 取消现有计时器 _autoStopTimer?.cancel(); // 初始化最后活动时间 _updateActivityTime(); // 根据模式设置超时时间 final int timeoutSeconds = _isVoiceInteractionMode ? 8 : 15; // 创建新的计时器,每秒检查一次 _autoStopTimer = Timer.periodic(Duration(seconds: 1), (timer) { // 检查是否有活动 bool isActive = _ttsService.isSpeaking || isSpeechDetected.value; // 如果有活动,更新时间并继续等待 if (isActive) { _updateActivityTime(); return; } // 检查空闲时间 int idleSeconds = DateTime.now().difference(_lastActivityTime).inSeconds; if (idleSeconds >= timeoutSeconds) { timer.cancel(); _stopVoiceRecognitionAutomatically(); } }); } // 处理语音识别中的状态 void _handleRecognizing(String text) { if (text.isEmpty) return; recognizedText.value = text; // 设置语音检测状态为true,用于控制波形动画 if (!isSpeechDetected.value) { isSpeechDetected.value = true; } // 更新活动时间 _updateActivityTime(); // 用户开始讲话时,立即停止当前TTS和中断AI响应 if (isSending.value || _aiResponseSubscription != null) { _interruptCurrentResponse("检测到用户开始讲话,中断当前响应"); } // 更新或创建临时消息 if (_tempMessageIndex >= 0 && _tempMessageIndex < messages.length) { // 更新已有的临时消息 messages[_tempMessageIndex] = messages[_tempMessageIndex].copyWith( content: text, ); messages.refresh(); } else { // 添加新的临时消息 final tempMessage = Message( role: 'user', content: text, timestamp: DateTime.now(), ); messages.add(tempMessage); _tempMessageIndex = messages.length - 1; messages.refresh(); } _scrollToBottom(); } // 处理语音识别最终结果 void _handleFinalResult(String text) { if (text.isEmpty) return; Logger.info('处理语音识别最终结果: $text'); recognizedText.value = text; // 更新活动时间 _updateActivityTime(); // 查找并移除临时消息 if (_tempMessageIndex >= 0 && _tempMessageIndex < messages.length) { messages.removeAt(_tempMessageIndex); } // 重置临时消息索引 _tempMessageIndex = -1; // 发送识别到的文本 sendMessage(text); // 重置识别文本,但不关闭语音识别框 recognizedText.value = ''; // 重置语音检测状态 isSpeechDetected.value = false; } // 处理语音识别事件错误 void _handleRecognitionEventError(String? error) { Logger.error('语音识别错误: ${error ?? "未知错误"}'); _cleanupRecognition(); } // 处理语音识别流错误 void _handleRecognitionError(dynamic error) { Logger.error('语音识别错误', error); _cleanupRecognition(); } // 清理语音识别资源 void _cleanupRecognition() { // 查找并移除临时消息 if (_tempMessageIndex >= 0 && _tempMessageIndex < messages.length) { if (messages[_tempMessageIndex].content.trim().isEmpty) { messages.removeAt(_tempMessageIndex); } } // 重置临时消息索引 _tempMessageIndex = -1; // 停止语音识别 isVoiceRecognitionActive.value = false; isListening.value = false; isSpeechDetected.value = false; _asrService.stopContinuousRecognition(); } // 停止语音识别 Future stopVoiceRecognition() async { if (!isVoiceRecognitionActive.value) return; try { await _asrService.stopContinuousRecognition(); _recognitionSubscription?.cancel(); _recognitionSubscription = null; isVoiceRecognitionActive.value = false; isListening.value = false; recognizedText.value = ''; isSpeechDetected.value = false; // 查找并移除临时消息 if (_tempMessageIndex >= 0 && _tempMessageIndex < messages.length) { if (messages[_tempMessageIndex].content.trim().isEmpty) { messages.removeAt(_tempMessageIndex); } } // 重置临时消息索引 _tempMessageIndex = -1; } catch (e) { Logger.error('停止语音识别失败', e); } } // 清空聊天记录 void clearChat() { messages.clear(); // 添加欢迎消息 if (agent.welcomeMessage.isNotEmpty) { messages.add(Message( role: 'assistant', content: agent.welcomeMessage, timestamp: DateTime.now(), )); } // 删除存储的历史记录 _historyService.clearHistory(agent.id); } // 加载聊天历史 void _loadChatHistory() { // 从历史服务加载 final history = _historyService.loadHistory(agent.id); if (history.isNotEmpty) { messages.clear(); messages.addAll(history); } } // 保存聊天历史 void _saveChatHistory() { _historyService.saveHistory(agent.id, messages); } // 添加消息,供外部调用 void addMessage(Message message) { messages.add(message); _saveChatHistory(); _scrollToBottom(); } void _stopVoiceRecognitionAutomatically() { final int timeoutSeconds = _isVoiceInteractionMode ? 8 : 15; Logger.info('${timeoutSeconds}秒无活动,自动停止语音识别'); stopVoiceRecognition(); } /// 检查是否为重复消息 bool _isDuplicateMessage(Message message) { return messages.any((m) => m.role == message.role && m.content == message.content && (m.timestamp.millisecondsSinceEpoch - message.timestamp.millisecondsSinceEpoch).abs() < 5000 ); } }