import 'dart:async'; import 'package:get/get.dart'; import 'package:flutter/widgets.dart'; import '../models/message_model.dart'; import '../../../data/services/volcano_ai_service.dart'; import '../../../data/services/volcano_tts_api_service.dart'; import '../../../data/services/azure_asr_service.dart'; import '../../../data/providers/agent_provider.dart'; import '../../../data/models/agent.dart'; import '../../../core/utils/logger.dart'; import '../../../data/services/chat_history_service.dart'; class ChatController extends GetxController { // 服务 final VolcanoAIService _aiService = Get.find(); final VolcanoTtsApiService _ttsService = Get.find(); final AzureAsrService _asrService = Get.find(); final ChatHistoryService _historyService = 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; // 语音识别状态 final RxString recognizedText = ''.obs; final RxBool isListening = false.obs; // 临时消息ID,用于语音识别中的消息 String? _tempRecognitionMessageId; // 流式响应控制 StreamSubscription? _aiResponseSubscription; StreamSubscription? _recognitionSubscription; // 取消标志 bool _shouldCancelAiResponse = false; @override void onInit() { super.onInit(); // 从路由参数获取Agent信息 final Map args = Get.arguments ?? {}; final String agentId = args['agentId'] ?? 'personal_assistant'; // 获取Agent final Agent? foundAgent = AgentProvider.getAgentById(agentId); if (foundAgent == null) { Logger.error('无法找到Agent: $agentId'); Get.back(); return; } agent = foundAgent; // 加载聊天历史 _loadChatHistory(); // 如果没有历史记录,添加欢迎消息 if (messages.isEmpty && agent.welcomeMessage.isNotEmpty) { messages.add(Message( role: 'assistant', content: agent.welcomeMessage, timestamp: DateTime.now(), )); // 如果启用了TTS,播放欢迎消息 if (isTtsEnabled.value) { _ttsService.speakSingle(agent.welcomeMessage); } } // 如果playVoiceOnEnter为true,自动激活语音识别 if (args['playVoiceOnEnter'] == true) { // 延迟一段时间后激活语音识别,等待TTS播放完成 startVoiceRecognition(); } // 使用微任务在布局完成后自动滚动到底部 WidgetsBinding.instance.addPostFrameCallback((_) { _scrollToBottom(); }); } @override void onClose() { // 关闭ScrollController scrollController.dispose(); // 取消所有订阅 _aiResponseSubscription?.cancel(); _recognitionSubscription?.cancel(); // 停止TTS if (_ttsService.isPlaying) { _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); } } } // 发送消息 Future sendMessage(String text) async { if (text.trim().isEmpty) return; // 停止TTS if (_ttsService.isPlaying) { _ttsService.stop(); } // 添加用户消息 final userMessage = Message( role: 'user', content: text, timestamp: DateTime.now(), ); messages.add(userMessage); // 滚动到底部显示用户消息 Future.delayed(Duration(milliseconds: 100), _scrollToBottom); // 清空输入框 inputText.value = ''; // 添加加载中的消息 final loadingMessage = Message.loading(); messages.add(loadingMessage); // 再次滚动到底部显示加载中的消息 Future.delayed(Duration(milliseconds: 100), _scrollToBottom); isSending.value = true; try { // 构建消息历史 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, }); } // 重置取消标志 _shouldCancelAiResponse = false; // 准备TTS会话 if (isTtsEnabled.value) { await _ttsService.startSession(agent.voice); } String fullResponse = ''; // 获取AI响应流 final responseStream = _aiService.sendMessageStream( messages: messageHistory, systemPrompt: agent.systemPrompt, ); // 订阅响应流 _aiResponseSubscription = responseStream.listen( (chunk) { if (_shouldCancelAiResponse) return; fullResponse += chunk; // 更新加载中的消息内容,但保持isLoading状态 final index = messages.indexWhere((m) => m.isLoading); if (index != -1) { // 直接更新消息内容,然后使用refresh()触发UI更新 messages[index] = messages[index].copyWith( content: fullResponse, isLoading: true, // 明确设置为true以确保状态一致 ); // 使用refresh()方法刷新列表,触发UI更新 messages.refresh(); // 每次收到新内容后滚动到底部 Future.delayed(Duration(milliseconds: 100), _scrollToBottom); } // 如果TTS启用,发送到TTS if (isTtsEnabled.value && !_shouldCancelAiResponse) { // 直接将文本块发送到TTS,服务会自己处理断句 _ttsService.speak(chunk); } }, onDone: () { if (_shouldCancelAiResponse) return; // 完成响应,此时才将isLoading设为false final index = messages.indexWhere((m) => m.isLoading); if (index != -1) { messages[index] = messages[index].copyWith( content: fullResponse, isLoading: false, ); // 滚动到底部显示完整回复 Future.delayed(Duration(milliseconds: 100), _scrollToBottom); } // 结束TTS会话 if (isTtsEnabled.value) { _ttsService.endSession(); } isSending.value = false; // 保存聊天历史 _saveChatHistory(); }, onError: (error) { Logger.error('AI响应错误', error); // 更新加载中的消息为错误消息 final index = messages.indexWhere((m) => m.isLoading); if (index != -1) { messages[index] = messages[index].copyWith( content: '抱歉,我遇到了一些问题,无法回应您的请求。', isLoading: false, ); } // 结束TTS会话 if (isTtsEnabled.value) { _ttsService.endSession(); } isSending.value = false; }, ); } catch (e) { Logger.error('发送消息失败', e); // 更新加载中的消息为错误消息 final index = messages.indexWhere((m) => m.isLoading); if (index != -1) { messages[index] = messages[index].copyWith( content: '抱歉,我遇到了一些问题,无法回应您的请求。', isLoading: false, ); } isSending.value = false; } } // 切换TTS状态 void toggleTts() { isTtsEnabled.toggle(); // 如果关闭TTS,停止当前播放 if (!isTtsEnabled.value && _ttsService.isPlaying) { _ttsService.stop(); } } // 开始语音识别 Future startVoiceRecognition() async { if (isVoiceRecognitionActive.value) return; // 每次收到新内容后滚动到底部 Future.delayed(Duration(milliseconds: 100), _scrollToBottom); // 停止TTS if (_ttsService.isPlaying) { _ttsService.stop(); // 如果正在接收AI响应,取消它 if (isSending.value) { _shouldCancelAiResponse = true; _aiResponseSubscription?.cancel(); // 移除加载中的消息 messages.removeWhere((m) => m.isLoading); isSending.value = false; } } try { // 启动语音识别 final success = await _asrService.startContinuousRecognition(); if (success) { isVoiceRecognitionActive.value = true; isListening.value = true; recognizedText.value = ''; // 不再添加"正在聆听..."的临时消息 // 监听识别事件 _recognitionSubscription = _asrService.recognitionStream?.listen( (event) { switch (event.type) { case RecognitionEventType.recognizing: // 实时更新识别文本 if (event.text.isNotEmpty) { recognizedText.value = event.text; // 检查是否已经有临时消息 final tempMessageIndex = messages.indexWhere((m) => m.role == 'user' && m.timestamp.millisecondsSinceEpoch > DateTime.now().millisecondsSinceEpoch - 5000); if (tempMessageIndex >= 0) { // 更新已有的临时消息 messages[tempMessageIndex] = messages[tempMessageIndex].copyWith( content: event.text, ); // 每次收到新内容后滚动到底部 Future.delayed( Duration(milliseconds: 100), _scrollToBottom); // 使用refresh()方法刷新列表 messages.refresh(); } else { // 添加新的临时消息,但只有在有实际内容时 final tempMessage = Message( role: 'user', content: event.text, timestamp: DateTime.now(), ); messages.add(tempMessage); } } break; case RecognitionEventType.finalResult: // 最终结果 if (event.text.isNotEmpty) { recognizedText.value = event.text; // 查找并移除临时消息 final tempMessageIndex = messages.indexWhere((m) => m.role == 'user' && m.timestamp.millisecondsSinceEpoch > DateTime.now().millisecondsSinceEpoch - 5000); if (tempMessageIndex >= 0) { messages.removeAt(tempMessageIndex); } // 发送识别到的文本 sendMessage(event.text); // 重置识别文本 recognizedText.value = ''; } break; case RecognitionEventType.sessionStopped: case RecognitionEventType.canceled: // 会话结束 isVoiceRecognitionActive.value = false; isListening.value = false; // 查找并移除临时消息 final tempMessageIndex = messages.indexWhere((m) => m.role == 'user' && m.timestamp.millisecondsSinceEpoch > DateTime.now().millisecondsSinceEpoch - 5000); if (tempMessageIndex >= 0 && messages[tempMessageIndex].content.trim().isEmpty) { messages.removeAt(tempMessageIndex); } break; default: break; } }, onError: (error) { Logger.error('语音识别错误', error); isVoiceRecognitionActive.value = false; isListening.value = false; // 查找并移除临时消息 final tempMessageIndex = messages.indexWhere((m) => m.role == 'user' && m.timestamp.millisecondsSinceEpoch > DateTime.now().millisecondsSinceEpoch - 5000); if (tempMessageIndex >= 0 && messages[tempMessageIndex].content.trim().isEmpty) { messages.removeAt(tempMessageIndex); } }, ); } else { Logger.error('启动语音识别失败'); } } catch (e) { Logger.error('启动语音识别失败', e); isVoiceRecognitionActive.value = false; isListening.value = false; } } // 停止语音识别 Future stopVoiceRecognition() async { if (!isVoiceRecognitionActive.value) return; try { await _asrService.stopContinuousRecognition(); _recognitionSubscription?.cancel(); _recognitionSubscription = null; isVoiceRecognitionActive.value = false; isListening.value = false; recognizedText.value = ''; // 查找并移除临时消息 final tempMessageIndex = messages.indexWhere((m) => m.role == 'user' && m.timestamp.millisecondsSinceEpoch > DateTime.now().millisecondsSinceEpoch - 5000); if (tempMessageIndex >= 0 && messages[tempMessageIndex].content.trim().isEmpty) { messages.removeAt(tempMessageIndex); } } 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(); // 滚动到底部显示新添加的消息 Future.delayed(Duration(milliseconds: 100), _scrollToBottom); } }