|
|
@ -9,8 +9,8 @@ import '../../models/events/voice_interaction_event.dart'; |
|
|
import '../../../core/utils/logger.dart'; |
|
|
import '../../../core/utils/logger.dart'; |
|
|
|
|
|
|
|
|
enum VoiceInteractionState { |
|
|
enum VoiceInteractionState { |
|
|
idle, // 等待唤醒 |
|
|
idle, // 等待唤醒 |
|
|
active, // 活跃状态 - 可以同时识别和播放 |
|
|
active, // 活跃状态 - 可以同时识别和播放 |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
class IosVoiceInteractionService extends GetxService { |
|
|
class IosVoiceInteractionService extends GetxService { |
|
|
@ -18,45 +18,47 @@ class IosVoiceInteractionService extends GetxService { |
|
|
final TtsService _ttsService = Get.find<TtsService>(); |
|
|
final TtsService _ttsService = Get.find<TtsService>(); |
|
|
final VolcanoAIService _aiService = Get.find<VolcanoAIService>(); |
|
|
final VolcanoAIService _aiService = Get.find<VolcanoAIService>(); |
|
|
final ChatHistoryService _chatHistoryService = Get.find<ChatHistoryService>(); |
|
|
final ChatHistoryService _chatHistoryService = Get.find<ChatHistoryService>(); |
|
|
|
|
|
|
|
|
// 当前状态 |
|
|
// 当前状态 |
|
|
final Rx<VoiceInteractionState> state = VoiceInteractionState.idle.obs; |
|
|
final Rx<VoiceInteractionState> state = VoiceInteractionState.idle.obs; |
|
|
|
|
|
|
|
|
// 是否识别到用户语音 |
|
|
// 是否识别到用户语音 |
|
|
final RxBool isSpeechDetected = false.obs; |
|
|
final RxBool isSpeechDetected = false.obs; |
|
|
|
|
|
|
|
|
// 是否正在处理AI响应 |
|
|
// 是否正在处理AI响应 |
|
|
final RxBool isProcessingAI = false.obs; |
|
|
final RxBool isProcessingAI = false.obs; |
|
|
|
|
|
|
|
|
// 是否正在播放TTS |
|
|
// 是否正在播放TTS |
|
|
final RxBool isSpeaking = false.obs; |
|
|
final RxBool isSpeaking = false.obs; |
|
|
|
|
|
|
|
|
// 对话历史记录 |
|
|
// 对话历史记录 |
|
|
final List<Map<String, String>> _messageHistory = []; |
|
|
final List<Map<String, String>> _messageHistory = []; |
|
|
|
|
|
|
|
|
// 最大历史记录数 |
|
|
// 最大历史记录数 |
|
|
static const int _maxHistorySize = 10; |
|
|
static const int _maxHistorySize = 10; |
|
|
|
|
|
|
|
|
// 超时计时器 |
|
|
// 超时计时器 |
|
|
Timer? _inactivityTimer; |
|
|
Timer? _inactivityTimer; |
|
|
|
|
|
|
|
|
// 流订阅 |
|
|
// 流订阅 |
|
|
StreamSubscription? _recognitionSubscription; |
|
|
StreamSubscription? _recognitionSubscription; |
|
|
|
|
|
|
|
|
// 当前识别的文本 |
|
|
// 当前识别的文本 |
|
|
String _currentRecognizedText = ''; |
|
|
String _currentRecognizedText = ''; |
|
|
|
|
|
// 音频输入源 |
|
|
|
|
|
bool _audioSourceType = false; |
|
|
// 事件流控制器 |
|
|
// 事件流控制器 |
|
|
final StreamController<VoiceInteractionEvent> _eventStreamController = |
|
|
final StreamController<VoiceInteractionEvent> _eventStreamController = |
|
|
StreamController<VoiceInteractionEvent>.broadcast(); |
|
|
StreamController<VoiceInteractionEvent>.broadcast(); |
|
|
|
|
|
|
|
|
// 获取事件流 |
|
|
// 获取事件流 |
|
|
@override |
|
|
@override |
|
|
Stream<VoiceInteractionEvent> get eventStream => _eventStreamController.stream; |
|
|
Stream<VoiceInteractionEvent> get eventStream => |
|
|
|
|
|
_eventStreamController.stream; |
|
|
|
|
|
|
|
|
// 标识符,用于存储聊天历史 |
|
|
// 标识符,用于存储聊天历史 |
|
|
final String _agentId = 'ios_assistant'; |
|
|
final String _agentId = 'ios_assistant'; |
|
|
|
|
|
|
|
|
/// 初始化服务 |
|
|
/// 初始化服务 |
|
|
@override |
|
|
@override |
|
|
Future<IosVoiceInteractionService> initialize() async { |
|
|
Future<IosVoiceInteractionService> initialize() async { |
|
|
@ -66,10 +68,10 @@ class IosVoiceInteractionService extends GetxService { |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
Logger.error('iOS语音交互服务初始化失败: $e'); |
|
|
Logger.error('iOS语音交互服务初始化失败: $e'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return this; |
|
|
return this; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 处理唤醒事件 |
|
|
// 处理唤醒事件 |
|
|
void onWakeup() { |
|
|
void onWakeup() { |
|
|
_ttsService.speakOnce('我在呢!'); |
|
|
_ttsService.speakOnce('我在呢!'); |
|
|
@ -79,63 +81,61 @@ class IosVoiceInteractionService extends GetxService { |
|
|
_activateInteraction(); |
|
|
_activateInteraction(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 激活交互 |
|
|
// 激活交互 |
|
|
void _activateInteraction() { |
|
|
void _activateInteraction() { |
|
|
// 设置状态为活跃 |
|
|
// 设置状态为活跃 |
|
|
state.value = VoiceInteractionState.active; |
|
|
state.value = VoiceInteractionState.active; |
|
|
|
|
|
|
|
|
// 开始连续监听 |
|
|
// 开始连续监听 |
|
|
_startContinuousListening(); |
|
|
_startContinuousListening(); |
|
|
|
|
|
|
|
|
// 启动不活动计时器 |
|
|
// 启动不活动计时器 |
|
|
_startInactivityTimer(); |
|
|
_startInactivityTimer(); |
|
|
|
|
|
|
|
|
// 发送识别开始事件 |
|
|
// 发送识别开始事件 |
|
|
_sendRecognitionStartedEvent(); |
|
|
_sendRecognitionStartedEvent(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 开始连续监听 |
|
|
// 开始连续监听 |
|
|
Future<void> _startContinuousListening() async { |
|
|
Future<void> _startContinuousListening() async { |
|
|
// 停止之前的监听 |
|
|
// 停止之前的监听 |
|
|
_recognitionSubscription?.cancel(); |
|
|
_recognitionSubscription?.cancel(); |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// 开始连续识别 |
|
|
// 开始连续识别 |
|
|
final recognitionStream = await _asrService.startContinuousRecognition(); |
|
|
final recognitionStream = |
|
|
|
|
|
await _asrService.startContinuousRecognition(_audioSourceType); |
|
|
|
|
|
|
|
|
// 监听识别结果 |
|
|
// 监听识别结果 |
|
|
_recognitionSubscription = recognitionStream.listen( |
|
|
_recognitionSubscription = |
|
|
_handleRecognitionEvent, |
|
|
recognitionStream.listen(_handleRecognitionEvent, onError: (error) { |
|
|
onError: (error) { |
|
|
Logger.error('语音识别错误: $error'); |
|
|
Logger.error('语音识别错误: $error'); |
|
|
_startContinuousListening(); // 尝试重新启动 |
|
|
_startContinuousListening(); // 尝试重新启动 |
|
|
}); |
|
|
} |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
Logger.info('开始连续语音识别'); |
|
|
Logger.info('开始连续语音识别'); |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
Logger.error('启动语音识别失败: $e'); |
|
|
Logger.error('启动语音识别失败: $e'); |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 处理识别事件 |
|
|
// 处理识别事件 |
|
|
void _handleRecognitionEvent(RecognitionEvent event) { |
|
|
void _handleRecognitionEvent(RecognitionEvent event) { |
|
|
// 更新活动时间 |
|
|
// 更新活动时间 |
|
|
_updateActivityTime(); |
|
|
_updateActivityTime(); |
|
|
|
|
|
|
|
|
switch (event.type) { |
|
|
switch (event.type) { |
|
|
case RecognitionEventType.intermediateResult: |
|
|
case RecognitionEventType.intermediateResult: |
|
|
// 检测用户是否开始说话 |
|
|
// 检测用户是否开始说话 |
|
|
if (event.text.isNotEmpty && !isSpeechDetected.value) { |
|
|
if (event.text.isNotEmpty && !isSpeechDetected.value) { |
|
|
isSpeechDetected.value = true; |
|
|
isSpeechDetected.value = true; |
|
|
|
|
|
|
|
|
// 用户开始讲话时,立即中断当前响应 |
|
|
// 用户开始讲话时,立即中断当前响应 |
|
|
_interruptCurrentResponse("检测到用户开始讲话,中断当前响应"); |
|
|
_interruptCurrentResponse("检测到用户开始讲话,中断当前响应"); |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case RecognitionEventType.finalResult: |
|
|
case RecognitionEventType.finalResult: |
|
|
if (event.text.isNotEmpty) { |
|
|
if (event.text.isNotEmpty) { |
|
|
// 最终结果,处理用户输入 |
|
|
// 最终结果,处理用户输入 |
|
|
@ -144,16 +144,16 @@ class IosVoiceInteractionService extends GetxService { |
|
|
// 重置语音检测状态 |
|
|
// 重置语音检测状态 |
|
|
isSpeechDetected.value = false; |
|
|
isSpeechDetected.value = false; |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case RecognitionEventType.sessionStarted: |
|
|
case RecognitionEventType.sessionStarted: |
|
|
Logger.info('语音识别会话开始'); |
|
|
Logger.info('语音识别会话开始'); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case RecognitionEventType.sessionStopped: |
|
|
case RecognitionEventType.sessionStopped: |
|
|
Logger.info('语音识别会话结束'); |
|
|
Logger.info('语音识别会话结束'); |
|
|
|
|
|
|
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case RecognitionEventType.error: |
|
|
case RecognitionEventType.error: |
|
|
case RecognitionEventType.canceled: |
|
|
case RecognitionEventType.canceled: |
|
|
Logger.error('语音识别错误: ${event.error}'); |
|
|
Logger.error('语音识别错误: ${event.error}'); |
|
|
@ -161,11 +161,11 @@ class IosVoiceInteractionService extends GetxService { |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 中断当前响应 |
|
|
// 中断当前响应 |
|
|
void _interruptCurrentResponse(String reason) { |
|
|
void _interruptCurrentResponse(String reason) { |
|
|
Logger.info(reason); |
|
|
Logger.info(reason); |
|
|
|
|
|
|
|
|
// 如果正在活跃状态,需要中断当前操作 |
|
|
// 如果正在活跃状态,需要中断当前操作 |
|
|
if (state.value == VoiceInteractionState.active) { |
|
|
if (state.value == VoiceInteractionState.active) { |
|
|
// 停止TTS播放 |
|
|
// 停止TTS播放 |
|
|
@ -174,40 +174,40 @@ class IosVoiceInteractionService extends GetxService { |
|
|
isProcessingAI.value = false; |
|
|
isProcessingAI.value = false; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 处理用户输入 |
|
|
// 处理用户输入 |
|
|
Future<void> _processUserInput(String text) async { |
|
|
Future<void> _processUserInput(String text) async { |
|
|
Logger.info('处理用户输入: $text'); |
|
|
Logger.info('处理用户输入: $text'); |
|
|
|
|
|
|
|
|
// 设置为处理状态 |
|
|
// 设置为处理状态 |
|
|
isProcessingAI.value = true; |
|
|
isProcessingAI.value = true; |
|
|
|
|
|
|
|
|
// 添加用户消息到历史记录 |
|
|
// 添加用户消息到历史记录 |
|
|
final userMessage = {'role': 'user', 'content': text}; |
|
|
final userMessage = {'role': 'user', 'content': text}; |
|
|
_addToHistory(userMessage); |
|
|
_addToHistory(userMessage); |
|
|
|
|
|
|
|
|
// 创建用户消息对象 |
|
|
// 创建用户消息对象 |
|
|
final userMsg = Message( |
|
|
final userMsg = Message( |
|
|
role: 'user', |
|
|
role: 'user', |
|
|
content: text, |
|
|
content: text, |
|
|
timestamp: DateTime.now(), |
|
|
timestamp: DateTime.now(), |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
// AI响应处理标志 |
|
|
// AI响应处理标志 |
|
|
bool isProcessingCancelled = false; |
|
|
bool isProcessingCancelled = false; |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// 构建消息历史 |
|
|
// 构建消息历史 |
|
|
final messageHistory = _buildMessageHistory(); |
|
|
final messageHistory = _buildMessageHistory(); |
|
|
|
|
|
|
|
|
// 调用AI服务获取响应 |
|
|
// 调用AI服务获取响应 |
|
|
final responseStream = _aiService.sendMessageStream( |
|
|
final responseStream = _aiService.sendMessageStream( |
|
|
messages: messageHistory, |
|
|
messages: messageHistory, |
|
|
systemPrompt: "你是一个智能助手,请简明扼要地回答问题。", |
|
|
systemPrompt: "你是一个智能助手,请简明扼要地回答问题。", |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
String aiResponse = ''; |
|
|
String aiResponse = ''; |
|
|
|
|
|
|
|
|
// 等待AI响应 |
|
|
// 等待AI响应 |
|
|
await for (final chunk in responseStream) { |
|
|
await for (final chunk in responseStream) { |
|
|
// 检查是否被用户打断 |
|
|
// 检查是否被用户打断 |
|
|
@ -217,49 +217,49 @@ class IosVoiceInteractionService extends GetxService { |
|
|
Logger.info('AI响应生成过程中被用户打断'); |
|
|
Logger.info('AI响应生成过程中被用户打断'); |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 如果状态已改变(可能由其他原因导致),停止处理 |
|
|
// 如果状态已改变(可能由其他原因导致),停止处理 |
|
|
if (state.value != VoiceInteractionState.active) { |
|
|
if (state.value != VoiceInteractionState.active) { |
|
|
isProcessingCancelled = true; |
|
|
isProcessingCancelled = true; |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
aiResponse += chunk; |
|
|
aiResponse += chunk; |
|
|
|
|
|
|
|
|
// 立即播放当前文本块,实现边生成边播放 |
|
|
// 立即播放当前文本块,实现边生成边播放 |
|
|
_speakStreamResponse(chunk); |
|
|
_speakStreamResponse(chunk); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 如果处理被取消,不继续后续操作 |
|
|
// 如果处理被取消,不继续后续操作 |
|
|
if (isProcessingCancelled) { |
|
|
if (isProcessingCancelled) { |
|
|
isProcessingAI.value = false; |
|
|
isProcessingAI.value = false; |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 确保完整响应被处理 |
|
|
// 确保完整响应被处理 |
|
|
if (aiResponse.isNotEmpty) { |
|
|
if (aiResponse.isNotEmpty) { |
|
|
// 将AI响应添加到历史记录 |
|
|
// 将AI响应添加到历史记录 |
|
|
final assistantMessage = {'role': 'assistant', 'content': aiResponse}; |
|
|
final assistantMessage = {'role': 'assistant', 'content': aiResponse}; |
|
|
_addToHistory(assistantMessage); |
|
|
_addToHistory(assistantMessage); |
|
|
|
|
|
|
|
|
// 创建助手消息对象 |
|
|
// 创建助手消息对象 |
|
|
final assistantMsg = Message( |
|
|
final assistantMsg = Message( |
|
|
role: 'assistant', |
|
|
role: 'assistant', |
|
|
content: aiResponse, |
|
|
content: aiResponse, |
|
|
timestamp: DateTime.now(), |
|
|
timestamp: DateTime.now(), |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
// 保存聊天历史 |
|
|
// 保存聊天历史 |
|
|
await _saveChatHistory(userMsg, assistantMsg); |
|
|
await _saveChatHistory(userMsg, assistantMsg); |
|
|
|
|
|
|
|
|
// 发送聊天历史事件 |
|
|
// 发送聊天历史事件 |
|
|
_sendChatHistoryEvent(userMsg.content, assistantMsg.content); |
|
|
_sendChatHistoryEvent(userMsg.content, assistantMsg.content); |
|
|
|
|
|
|
|
|
// 刷新TTS流,确保所有文本都被播放 |
|
|
// 刷新TTS流,确保所有文本都被播放 |
|
|
await _ttsService.flushStream(); |
|
|
await _ttsService.flushStream(); |
|
|
isSpeaking.value = false; |
|
|
isSpeaking.value = false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 处理完成 |
|
|
// 处理完成 |
|
|
isProcessingAI.value = false; |
|
|
isProcessingAI.value = false; |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
@ -268,59 +268,59 @@ class IosVoiceInteractionService extends GetxService { |
|
|
isProcessingAI.value = false; |
|
|
isProcessingAI.value = false; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 构建用于AI服务的消息历史 |
|
|
// 构建用于AI服务的消息历史 |
|
|
List<Map<String, String>> _buildMessageHistory() { |
|
|
List<Map<String, String>> _buildMessageHistory() { |
|
|
return List<Map<String, String>>.from(_messageHistory); |
|
|
return List<Map<String, String>>.from(_messageHistory); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 添加消息到历史记录 |
|
|
// 添加消息到历史记录 |
|
|
void _addToHistory(Map<String, String> message) { |
|
|
void _addToHistory(Map<String, String> message) { |
|
|
_messageHistory.add(message); |
|
|
_messageHistory.add(message); |
|
|
|
|
|
|
|
|
// 限制历史记录大小 |
|
|
// 限制历史记录大小 |
|
|
while (_messageHistory.length > _maxHistorySize) { |
|
|
while (_messageHistory.length > _maxHistorySize) { |
|
|
_messageHistory.removeAt(0); |
|
|
_messageHistory.removeAt(0); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 播放流式响应 |
|
|
// 播放流式响应 |
|
|
void _speakStreamResponse(String chunk) { |
|
|
void _speakStreamResponse(String chunk) { |
|
|
if (chunk.isEmpty) return; |
|
|
if (chunk.isEmpty) return; |
|
|
|
|
|
|
|
|
// 更新播放状态 |
|
|
// 更新播放状态 |
|
|
isSpeaking.value = true; |
|
|
isSpeaking.value = true; |
|
|
|
|
|
|
|
|
// 使用流式TTS播放 |
|
|
// 使用流式TTS播放 |
|
|
_ttsService.speakStream(chunk); |
|
|
_ttsService.speakStream(chunk); |
|
|
|
|
|
|
|
|
// 更新活动时间 |
|
|
// 更新活动时间 |
|
|
_updateActivityTime(); |
|
|
_updateActivityTime(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 停止TTS |
|
|
// 停止TTS |
|
|
void _stopTts() { |
|
|
void _stopTts() { |
|
|
_ttsService.stop(); |
|
|
_ttsService.stop(); |
|
|
isSpeaking.value = false; |
|
|
isSpeaking.value = false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 更新最后活动时间 |
|
|
// 更新最后活动时间 |
|
|
DateTime _lastActivityTime = DateTime.now(); |
|
|
DateTime _lastActivityTime = DateTime.now(); |
|
|
void _updateActivityTime() { |
|
|
void _updateActivityTime() { |
|
|
_lastActivityTime = DateTime.now(); |
|
|
_lastActivityTime = DateTime.now(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 启动不活动计时器(长时间无交互会切换到空闲状态) |
|
|
// 启动不活动计时器(长时间无交互会切换到空闲状态) |
|
|
void _startInactivityTimer() { |
|
|
void _startInactivityTimer() { |
|
|
_cancelInactivityTimer(); |
|
|
_cancelInactivityTimer(); |
|
|
|
|
|
|
|
|
_inactivityTimer = Timer.periodic(Duration(seconds: 5), (timer) { |
|
|
_inactivityTimer = Timer.periodic(Duration(seconds: 5), (timer) { |
|
|
// 计算空闲时间 |
|
|
// 计算空闲时间 |
|
|
final idleTime = DateTime.now().difference(_lastActivityTime).inSeconds; |
|
|
final idleTime = DateTime.now().difference(_lastActivityTime).inSeconds; |
|
|
|
|
|
|
|
|
// 如果空闲超过30秒,且不在播放或检测到语音,切换到空闲 |
|
|
// 如果空闲超过30秒,且不在播放或检测到语音,切换到空闲 |
|
|
if (idleTime > 8 && |
|
|
if (idleTime > 8 && |
|
|
!isSpeaking.value && |
|
|
!isSpeaking.value && |
|
|
!isProcessingAI.value && |
|
|
!isProcessingAI.value && |
|
|
!isSpeechDetected.value) { |
|
|
!isSpeechDetected.value) { |
|
|
_resetToIdle(); |
|
|
_resetToIdle(); |
|
|
@ -328,37 +328,37 @@ class IosVoiceInteractionService extends GetxService { |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 取消不活动计时器 |
|
|
// 取消不活动计时器 |
|
|
void _cancelInactivityTimer() { |
|
|
void _cancelInactivityTimer() { |
|
|
_inactivityTimer?.cancel(); |
|
|
_inactivityTimer?.cancel(); |
|
|
_inactivityTimer = null; |
|
|
_inactivityTimer = null; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 重置到空闲状态 |
|
|
// 重置到空闲状态 |
|
|
void _resetToIdle() { |
|
|
void _resetToIdle() { |
|
|
_ttsService.speakOnce('没有听到声音,暂停对话,双击耳机唤醒!'); |
|
|
_ttsService.speakOnce('没有听到声音,暂停对话,双击耳机唤醒!'); |
|
|
_cancelInactivityTimer(); |
|
|
_cancelInactivityTimer(); |
|
|
|
|
|
|
|
|
// 停止TTS |
|
|
// 停止TTS |
|
|
_stopTts(); |
|
|
_stopTts(); |
|
|
|
|
|
|
|
|
// 停止语音识别 |
|
|
// 停止语音识别 |
|
|
_recognitionSubscription?.cancel(); |
|
|
_recognitionSubscription?.cancel(); |
|
|
_asrService.stopContinuousRecognition(); |
|
|
_asrService.stopContinuousRecognition(); |
|
|
|
|
|
|
|
|
// 重置状态 |
|
|
// 重置状态 |
|
|
state.value = VoiceInteractionState.idle; |
|
|
state.value = VoiceInteractionState.idle; |
|
|
isSpeechDetected.value = false; |
|
|
isSpeechDetected.value = false; |
|
|
isProcessingAI.value = false; |
|
|
isProcessingAI.value = false; |
|
|
isSpeaking.value = false; |
|
|
isSpeaking.value = false; |
|
|
|
|
|
|
|
|
// 清空历史记录 |
|
|
// 清空历史记录 |
|
|
_messageHistory.clear(); |
|
|
_messageHistory.clear(); |
|
|
|
|
|
|
|
|
Logger.info('语音交互已重置为空闲状态'); |
|
|
Logger.info('语音交互已重置为空闲状态'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 暂停语音交互 |
|
|
// 暂停语音交互 |
|
|
@override |
|
|
@override |
|
|
void pauseVoiceInteraction() { |
|
|
void pauseVoiceInteraction() { |
|
|
@ -366,53 +366,53 @@ class IosVoiceInteractionService extends GetxService { |
|
|
_stopTts(); |
|
|
_stopTts(); |
|
|
_recognitionSubscription?.cancel(); |
|
|
_recognitionSubscription?.cancel(); |
|
|
_asrService.stopContinuousRecognition(); |
|
|
_asrService.stopContinuousRecognition(); |
|
|
|
|
|
|
|
|
// 重置所有状态 |
|
|
// 重置所有状态 |
|
|
state.value = VoiceInteractionState.idle; |
|
|
state.value = VoiceInteractionState.idle; |
|
|
isSpeechDetected.value = false; |
|
|
isSpeechDetected.value = false; |
|
|
isProcessingAI.value = false; |
|
|
isProcessingAI.value = false; |
|
|
isSpeaking.value = false; |
|
|
isSpeaking.value = false; |
|
|
|
|
|
|
|
|
// 清空历史记录 |
|
|
// 清空历史记录 |
|
|
_messageHistory.clear(); |
|
|
_messageHistory.clear(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 恢复语音交互 |
|
|
// 恢复语音交互 |
|
|
void resumeVoiceInteraction() { |
|
|
void resumeVoiceInteraction() { |
|
|
if (state.value == VoiceInteractionState.idle) { |
|
|
if (state.value == VoiceInteractionState.idle) { |
|
|
_activateInteraction(); |
|
|
_activateInteraction(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 手动停止交互 |
|
|
// 手动停止交互 |
|
|
void stopInteraction() { |
|
|
void stopInteraction() { |
|
|
_resetToIdle(); |
|
|
_resetToIdle(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 获取当前状态 |
|
|
// 获取当前状态 |
|
|
VoiceInteractionState getCurrentState() { |
|
|
VoiceInteractionState getCurrentState() { |
|
|
return state.value; |
|
|
return state.value; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 是否处于活跃状态 |
|
|
// 是否处于活跃状态 |
|
|
@override |
|
|
@override |
|
|
bool isActive() { |
|
|
bool isActive() { |
|
|
return state.value != VoiceInteractionState.idle; |
|
|
return state.value != VoiceInteractionState.idle; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 获取当前对话历史 |
|
|
// 获取当前对话历史 |
|
|
@override |
|
|
@override |
|
|
List<Map<String, String>> getMessageHistory() { |
|
|
List<Map<String, String>> getMessageHistory() { |
|
|
return List<Map<String, String>>.from(_messageHistory); |
|
|
return List<Map<String, String>>.from(_messageHistory); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 获取当前服务ID |
|
|
// 获取当前服务ID |
|
|
@override |
|
|
@override |
|
|
String getServiceId() { |
|
|
String getServiceId() { |
|
|
return _agentId; |
|
|
return _agentId; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 释放资源 |
|
|
// 释放资源 |
|
|
@override |
|
|
@override |
|
|
void onClose() { |
|
|
void onClose() { |
|
|
@ -422,25 +422,25 @@ class IosVoiceInteractionService extends GetxService { |
|
|
_eventStreamController.close(); |
|
|
_eventStreamController.close(); |
|
|
super.onClose(); |
|
|
super.onClose(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 保存聊天历史 |
|
|
// 保存聊天历史 |
|
|
Future<void> _saveChatHistory(Message userMsg, Message assistantMsg) async { |
|
|
Future<void> _saveChatHistory(Message userMsg, Message assistantMsg) async { |
|
|
try { |
|
|
try { |
|
|
// 加载现有历史记录 |
|
|
// 加载现有历史记录 |
|
|
final existingMessages = await _chatHistoryService.loadHistory(_agentId); |
|
|
final existingMessages = await _chatHistoryService.loadHistory(_agentId); |
|
|
|
|
|
|
|
|
// 添加新消息 |
|
|
// 添加新消息 |
|
|
existingMessages.addAll([userMsg, assistantMsg]); |
|
|
existingMessages.addAll([userMsg, assistantMsg]); |
|
|
|
|
|
|
|
|
// 保存更新后的历史记录 |
|
|
// 保存更新后的历史记录 |
|
|
await _chatHistoryService.saveHistory(_agentId, existingMessages); |
|
|
await _chatHistoryService.saveHistory(_agentId, existingMessages); |
|
|
|
|
|
|
|
|
Logger.info('保存聊天历史记录成功'); |
|
|
Logger.info('保存聊天历史记录成功'); |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
Logger.error('保存聊天历史记录失败: $e'); |
|
|
Logger.error('保存聊天历史记录失败: $e'); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 发送聊天历史事件 |
|
|
// 发送聊天历史事件 |
|
|
void _sendChatHistoryEvent(String userMessage, String assistantMessage) { |
|
|
void _sendChatHistoryEvent(String userMessage, String assistantMessage) { |
|
|
final event = ChatHistoryEvent( |
|
|
final event = ChatHistoryEvent( |
|
|
@ -449,19 +449,18 @@ class IosVoiceInteractionService extends GetxService { |
|
|
assistantMessage: assistantMessage, |
|
|
assistantMessage: assistantMessage, |
|
|
timestamp: DateTime.now().millisecondsSinceEpoch, |
|
|
timestamp: DateTime.now().millisecondsSinceEpoch, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
_eventStreamController.add(event); |
|
|
_eventStreamController.add(event); |
|
|
Logger.info('已发送聊天历史事件'); |
|
|
Logger.info('已发送聊天历史事件'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 发送识别开始事件 |
|
|
// 发送识别开始事件 |
|
|
void _sendRecognitionStartedEvent() { |
|
|
void _sendRecognitionStartedEvent() { |
|
|
final event = RecognitionStartedEvent( |
|
|
final event = RecognitionStartedEvent( |
|
|
timestamp: DateTime.now().millisecondsSinceEpoch, |
|
|
timestamp: DateTime.now().millisecondsSinceEpoch, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
_eventStreamController.add(event); |
|
|
_eventStreamController.add(event); |
|
|
Logger.info('已发送识别开始事件'); |
|
|
Logger.info('已发送识别开始事件'); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|