|
|
@ -1,49 +1,48 @@ |
|
|
import 'dart:async'; |
|
|
import 'dart:async'; |
|
|
import 'package:flutter/services.dart'; |
|
|
import 'package:flutter/services.dart'; |
|
|
import 'package:get/get.dart'; |
|
|
import 'package:get/get.dart'; |
|
|
|
|
|
import '../../core/utils/logger.dart'; |
|
|
import 'package:flutter_dotenv/flutter_dotenv.dart'; |
|
|
import 'package:flutter_dotenv/flutter_dotenv.dart'; |
|
|
import '../services/azure_asr_service.dart'; |
|
|
|
|
|
import '../services/azure_tts_service.dart'; |
|
|
|
|
|
import '../services/chat_history_service.dart'; |
|
|
|
|
|
import '../../modules/chat/models/message_model.dart'; |
|
|
import '../../modules/chat/models/message_model.dart'; |
|
|
import '../../core/utils/logger.dart'; |
|
|
import 'chat_history_service.dart'; |
|
|
|
|
|
import '../../data/services/azure_asr_service.dart'; |
|
|
/// 语音交互服务,用于管理原生的 VoiceInteractionService |
|
|
import '../../data/services/azure_tts_service.dart'; |
|
|
|
|
|
|
|
|
|
|
|
/// 语音交互服务 |
|
|
|
|
|
/// |
|
|
|
|
|
/// 该服务提供了与Android端的VoiceInteractionService.kt通信的接口, |
|
|
|
|
|
/// 用于管理后台语音交互服务的生命周期和接收语音交互事件 |
|
|
class VoiceInteractionService extends GetxService { |
|
|
class VoiceInteractionService extends GetxService { |
|
|
static VoiceInteractionService get to => Get.find(); |
|
|
static VoiceInteractionService get to => Get.find(); |
|
|
|
|
|
// 方法通道和事件通道 |
|
|
// 服务运行状态 |
|
|
|
|
|
final _isRunning = false.obs; |
|
|
|
|
|
bool get isRunning => _isRunning.value; |
|
|
|
|
|
|
|
|
|
|
|
// 语音识别状态 |
|
|
|
|
|
final _isRecognitionActive = false.obs; |
|
|
|
|
|
bool get isRecognitionActive => _isRecognitionActive.value; |
|
|
|
|
|
|
|
|
|
|
|
// 聊天记录 - 可被 ChatView 监听 |
|
|
|
|
|
final RxList<Message> personalAssistantMessages = <Message>[].obs; |
|
|
|
|
|
|
|
|
|
|
|
// 方法通道 |
|
|
|
|
|
static const MethodChannel _channel = MethodChannel('com.deep_voice.voice_interaction'); |
|
|
static const MethodChannel _channel = MethodChannel('com.deep_voice.voice_interaction'); |
|
|
|
|
|
|
|
|
// 事件通道 |
|
|
|
|
|
static const EventChannel _eventChannel = EventChannel('com.deep_voice.voice_interaction_events'); |
|
|
static const EventChannel _eventChannel = EventChannel('com.deep_voice.voice_interaction_events'); |
|
|
|
|
|
|
|
|
// 配置信息 |
|
|
// 服务状态 |
|
|
late final String _azureSpeechKey; |
|
|
final _isServiceRunning = false.obs; |
|
|
late final String _azureSpeechRegion; |
|
|
bool get isServiceRunning => _isServiceRunning.value; |
|
|
late final String _volcanoAiApiKey; |
|
|
|
|
|
|
|
|
// 事件流控制器 |
|
|
|
|
|
StreamController<VoiceInteractionEvent>? _eventStreamController; |
|
|
|
|
|
Stream<VoiceInteractionEvent>? _eventStream; |
|
|
|
|
|
Stream<VoiceInteractionEvent>? get eventStream => _eventStream; |
|
|
|
|
|
|
|
|
// 事件通道状态 |
|
|
// 事件通道状态 |
|
|
bool _isEventChannelReady = false; |
|
|
bool _isEventChannelReady = false; |
|
|
Completer<void>? _eventChannelReadyCompleter; |
|
|
Completer<void>? _eventChannelReadyCompleter; |
|
|
|
|
|
StreamSubscription? _eventSubscription; |
|
|
|
|
|
|
|
|
// 初始化标记,避免重复初始化 |
|
|
// 配置信息 |
|
|
bool _isInitialized = false; |
|
|
late String _azureSpeechKey; |
|
|
|
|
|
late String _azureSpeechRegion; |
|
|
|
|
|
late String _volcanoAiApiKey; |
|
|
|
|
|
|
|
|
|
|
|
// 初始化状态标志 |
|
|
|
|
|
static bool _isInitialized = false; |
|
|
|
|
|
|
|
|
|
|
|
/// 构造函数 |
|
|
VoiceInteractionService() { |
|
|
VoiceInteractionService() { |
|
|
_loadConfig(); |
|
|
|
|
|
_setupEventChannel(); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 从环境变量加载配置 |
|
|
/// 从环境变量加载配置 |
|
|
@ -53,39 +52,44 @@ class VoiceInteractionService extends GetxService { |
|
|
_volcanoAiApiKey = dotenv.env['VOLCANO_AI_API_KEY'] ?? ''; |
|
|
_volcanoAiApiKey = dotenv.env['VOLCANO_AI_API_KEY'] ?? ''; |
|
|
|
|
|
|
|
|
if (_azureSpeechKey.isEmpty || _azureSpeechRegion.isEmpty) { |
|
|
if (_azureSpeechKey.isEmpty || _azureSpeechRegion.isEmpty) { |
|
|
Logger.error('未找到 Azure 语音服务配置。请在 .env 文件中设置 AZURE_SPEECH_KEY 和 AZURE_SPEECH_REGION'); |
|
|
Logger.warning('未找到 Azure 语音服务配置。请在 .env 文件中设置 AZURE_SPEECH_KEY 和 AZURE_SPEECH_REGION'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (_volcanoAiApiKey.isEmpty) { |
|
|
if (_volcanoAiApiKey.isEmpty) { |
|
|
Logger.error('未找到火山 AI 配置。请在 .env 文件中设置 VOLCANO_AI_API_KEY'); |
|
|
Logger.warning('未找到火山 AI API 密钥。请在 .env 文件中设置 VOLCANO_AI_API_KEY'); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 设置事件通道监听 |
|
|
/// 设置事件通道 |
|
|
void _setupEventChannel() { |
|
|
void _setupEventChannel() { |
|
|
_eventChannelReadyCompleter = Completer<void>(); |
|
|
_eventChannelReadyCompleter = Completer<void>(); |
|
|
|
|
|
|
|
|
_eventChannel.receiveBroadcastStream().listen( |
|
|
_eventSubscription = _eventChannel |
|
|
(event) { |
|
|
.receiveBroadcastStream() |
|
|
if (event is Map) { |
|
|
.listen((event) { |
|
|
final String eventType = event['type'] as String? ?? ''; |
|
|
if (event is Map) { |
|
|
|
|
|
final String eventType = event['type'] as String? ?? ''; |
|
|
// 处理通道准备好的事件 |
|
|
|
|
|
if (eventType == 'channelReady') { |
|
|
// 处理通道准备好的事件 |
|
|
_isEventChannelReady = true; |
|
|
if (eventType == 'channelReady') { |
|
|
if (!_eventChannelReadyCompleter!.isCompleted) { |
|
|
_isEventChannelReady = true; |
|
|
_eventChannelReadyCompleter!.complete(); |
|
|
if (!_eventChannelReadyCompleter!.isCompleted) { |
|
|
|
|
|
_eventChannelReadyCompleter!.complete(); |
|
|
|
|
|
} |
|
|
|
|
|
return; |
|
|
} |
|
|
} |
|
|
return; |
|
|
|
|
|
|
|
|
_handleVoiceInteractionEvent(event); |
|
|
} |
|
|
} |
|
|
|
|
|
}, onError: (error) { |
|
|
|
|
|
Logger.error('语音交互事件通道错误: $error'); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
_handleEvent(event); |
|
|
/// 创建事件流 |
|
|
} |
|
|
void _createEventStream() { |
|
|
}, |
|
|
_eventStreamController = StreamController<VoiceInteractionEvent>.broadcast(); |
|
|
onError: (error) { |
|
|
_eventStream = _eventStreamController?.stream; |
|
|
Logger.error('语音交互事件通道错误: $error'); |
|
|
|
|
|
} |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 等待事件通道准备好 |
|
|
/// 等待事件通道准备好 |
|
|
@ -96,152 +100,67 @@ class VoiceInteractionService extends GetxService { |
|
|
await _eventChannelReadyCompleter!.future.timeout(timeout); |
|
|
await _eventChannelReadyCompleter!.future.timeout(timeout); |
|
|
return true; |
|
|
return true; |
|
|
} on TimeoutException { |
|
|
} on TimeoutException { |
|
|
Logger.error('等待事件通道准备好超时'); |
|
|
Logger.error('等待语音交互事件通道准备好超时'); |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 初始化服务 |
|
|
/// 初始化服务 |
|
|
Future<VoiceInteractionService> init() async { |
|
|
Future<VoiceInteractionService> initialize() async { |
|
|
// 如果已经初始化过,直接返回 |
|
|
|
|
|
if (_isInitialized) { |
|
|
|
|
|
return this; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// 等待事件通道准备好 |
|
|
if (_isInitialized) { |
|
|
final bool eventChannelReady = await _waitForEventChannel(); |
|
|
|
|
|
if (!eventChannelReady) { |
|
|
|
|
|
Logger.error('事件通道未准备好,初始化失败'); |
|
|
|
|
|
return this; |
|
|
return this; |
|
|
} |
|
|
} |
|
|
|
|
|
_isInitialized = true; |
|
|
|
|
|
|
|
|
// 获取服务运行状态 |
|
|
_loadConfig(); |
|
|
await _updateServiceStatus(); |
|
|
_setupEventChannel(); |
|
|
|
|
|
_createEventStream(); |
|
|
|
|
|
// 检查服务是否正在运行 |
|
|
|
|
|
await _checkServiceStatus(); |
|
|
|
|
|
|
|
|
// 加载个人助手的历史记录 |
|
|
// 等待事件通道准备好 |
|
|
_loadPersonalAssistantHistory(); |
|
|
await _waitForEventChannel(); |
|
|
|
|
|
|
|
|
// 标记为已初始化 |
|
|
Logger.info('语音交互服务初始化完成'); |
|
|
_isInitialized = true; |
|
|
|
|
|
return this; |
|
|
return this; |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
Logger.error('VoiceInteractionService 初始化失败: $e'); |
|
|
Logger.error('语音交互服务初始化失败: $e'); |
|
|
return this; |
|
|
return this; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 加载个人助手的历史记录 |
|
|
/// 检查服务状态 |
|
|
void _loadPersonalAssistantHistory() { |
|
|
Future<void> _checkServiceStatus() async { |
|
|
if (Get.isRegistered<ChatHistoryService>()) { |
|
|
|
|
|
final historyService = Get.find<ChatHistoryService>(); |
|
|
|
|
|
final messages = historyService.loadHistory('personal_assistant'); |
|
|
|
|
|
if (messages.isNotEmpty) { |
|
|
|
|
|
personalAssistantMessages.assignAll(messages); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 处理来自原生端的事件 |
|
|
|
|
|
void _handleEvent(dynamic event) { |
|
|
|
|
|
try { |
|
|
try { |
|
|
if (event is! Map) { |
|
|
final bool isRunning = await _channel.invokeMethod('isVoiceInteractionServiceRunning') ?? false; |
|
|
return; |
|
|
_isServiceRunning.value = isRunning; |
|
|
} |
|
|
Logger.info('语音交互服务状态: ${isRunning ? "运行中" : "未运行"}'); |
|
|
|
|
|
|
|
|
final type = event['type'] as String?; |
|
|
|
|
|
if (type == null) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
switch (type) { |
|
|
|
|
|
case 'recognitionStarted': |
|
|
|
|
|
_isRecognitionActive.value = true; |
|
|
|
|
|
|
|
|
|
|
|
// 检测到后台服务的语音识别开始运行,主动停止Flutter端的语音识别和TTS |
|
|
|
|
|
// _stopFlutterVoiceServices(); |
|
|
|
|
|
break; |
|
|
|
|
|
case 'chatHistory': |
|
|
|
|
|
// 处理聊天记录 |
|
|
|
|
|
_handleChatHistory(event); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
Logger.error('处理原生端事件时出错: $e'); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 处理聊天记录事件 |
|
|
|
|
|
void _handleChatHistory(Map<dynamic, dynamic> event) { |
|
|
|
|
|
try { |
|
|
|
|
|
final agentId = event['agentId'] as String?; |
|
|
|
|
|
final userMessage = event['userMessage'] as String?; |
|
|
|
|
|
final assistantMessage = event['assistantMessage'] as String?; |
|
|
|
|
|
final timestamp = event['timestamp'] as int?; |
|
|
|
|
|
|
|
|
|
|
|
if (agentId == null || userMessage == null || assistantMessage == null || timestamp == null) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 只处理个人助手的聊天记录 |
|
|
|
|
|
if (agentId != 'personal_assistant') { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 添加用户消息 |
|
|
|
|
|
personalAssistantMessages.add(Message( |
|
|
|
|
|
role: 'user', |
|
|
|
|
|
content: userMessage, |
|
|
|
|
|
timestamp: DateTime.fromMillisecondsSinceEpoch(timestamp), |
|
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
|
|
// 添加助手消息 |
|
|
|
|
|
personalAssistantMessages.add(Message( |
|
|
|
|
|
role: 'assistant', |
|
|
|
|
|
content: assistantMessage, |
|
|
|
|
|
timestamp: DateTime.fromMillisecondsSinceEpoch(timestamp + 1000), // 助手消息时间戳稍后 |
|
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
|
|
// 保存到历史记录 |
|
|
|
|
|
if (Get.isRegistered<ChatHistoryService>()) { |
|
|
|
|
|
final historyService = Get.find<ChatHistoryService>(); |
|
|
|
|
|
historyService.saveHistory('personal_assistant', personalAssistantMessages); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
Logger.error('处理聊天记录事件失败: $e'); |
|
|
Logger.error('检查语音交互服务状态失败: $e'); |
|
|
} |
|
|
_isServiceRunning.value = false; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 停止 Flutter 端的语音服务 |
|
|
|
|
|
void _stopFlutterVoiceServices() { |
|
|
|
|
|
// 停止 TTS |
|
|
|
|
|
if (Get.isRegistered<AzureTtsService>()) { |
|
|
|
|
|
Get.find<AzureTtsService>().stop(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 停止 ASR |
|
|
|
|
|
if (Get.isRegistered<AzureAsrService>()) { |
|
|
|
|
|
Get.find<AzureAsrService>().stopContinuousRecognition(); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 启动语音交互服务 |
|
|
/// 启动语音交互服务 |
|
|
Future<bool> startService() async { |
|
|
Future<bool> startService() async { |
|
|
if (!_isInitialized) { |
|
|
if (_isServiceRunning.value) { |
|
|
await init(); |
|
|
Logger.info('语音交互服务已经在运行'); |
|
|
|
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// 启动服务 |
|
|
final bool result = await _channel.invokeMethod('startVoiceInteractionService', { |
|
|
final result = await _channel.invokeMethod<bool>('startVoiceInteractionService', { |
|
|
|
|
|
'azure_speech_key': _azureSpeechKey, |
|
|
'azure_speech_key': _azureSpeechKey, |
|
|
'azure_speech_region': _azureSpeechRegion, |
|
|
'azure_speech_region': _azureSpeechRegion, |
|
|
'volcano_ai_api_key': _volcanoAiApiKey, |
|
|
'volcano_ai_api_key': _volcanoAiApiKey, |
|
|
}) ?? false; |
|
|
}) ?? false; |
|
|
|
|
|
|
|
|
// 更新状态 |
|
|
|
|
|
if (result) { |
|
|
if (result) { |
|
|
await _updateServiceStatus(); |
|
|
_isServiceRunning.value = true; |
|
|
|
|
|
Logger.info('语音交互服务启动成功'); |
|
|
|
|
|
} else { |
|
|
|
|
|
Logger.error('语音交互服务启动失败'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return result; |
|
|
return result; |
|
|
@ -253,12 +172,19 @@ class VoiceInteractionService extends GetxService { |
|
|
|
|
|
|
|
|
/// 停止语音交互服务 |
|
|
/// 停止语音交互服务 |
|
|
Future<bool> stopService() async { |
|
|
Future<bool> stopService() async { |
|
|
|
|
|
if (!_isServiceRunning.value) { |
|
|
|
|
|
Logger.info('语音交互服务未运行'); |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
final result = await _channel.invokeMethod<bool>('stopVoiceInteractionService') ?? false; |
|
|
final bool result = await _channel.invokeMethod('stopVoiceInteractionService') ?? false; |
|
|
|
|
|
|
|
|
// 更新状态 |
|
|
|
|
|
if (result) { |
|
|
if (result) { |
|
|
await _updateServiceStatus(); |
|
|
_isServiceRunning.value = false; |
|
|
|
|
|
Logger.info('语音交互服务停止成功'); |
|
|
|
|
|
} else { |
|
|
|
|
|
Logger.error('语音交互服务停止失败'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return result; |
|
|
return result; |
|
|
@ -268,10 +194,22 @@ class VoiceInteractionService extends GetxService { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 暂停语音交互 |
|
|
/// 暂停语音交互(停止TTS和语音识别,但保持服务运行) |
|
|
Future<bool> pauseVoiceInteraction() async { |
|
|
Future<bool> pauseVoiceInteraction() async { |
|
|
|
|
|
if (!_isServiceRunning.value) { |
|
|
|
|
|
Logger.info('语音交互服务未运行,无法暂停'); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
final result = await _channel.invokeMethod<bool>('pauseVoiceInteraction') ?? false; |
|
|
final bool result = await _channel.invokeMethod('pauseVoiceInteraction') ?? false; |
|
|
|
|
|
|
|
|
|
|
|
if (result) { |
|
|
|
|
|
Logger.info('语音交互暂停成功'); |
|
|
|
|
|
} else { |
|
|
|
|
|
Logger.error('语音交互暂停失败'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
return result; |
|
|
return result; |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
Logger.error('暂停语音交互失败: $e'); |
|
|
Logger.error('暂停语音交互失败: $e'); |
|
|
@ -279,34 +217,116 @@ class VoiceInteractionService extends GetxService { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 更新服务运行状态 |
|
|
/// 处理来自原生端的语音交互事件 |
|
|
Future<void> _updateServiceStatus() async { |
|
|
void _handleVoiceInteractionEvent(dynamic event) { |
|
|
try { |
|
|
if (event is! Map || _eventStreamController == null) return; |
|
|
final status = await _channel.invokeMethod<bool>('isVoiceInteractionServiceRunning') ?? false; |
|
|
|
|
|
_isRunning.value = status; |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
Logger.error('获取语音交互服务状态失败: $e'); |
|
|
|
|
|
_isRunning.value = false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 释放资源 |
|
|
final Map<dynamic, dynamic> eventMap = event; |
|
|
Future<void> dispose() async { |
|
|
final String eventType = eventMap['type'] as String? ?? ''; |
|
|
try { |
|
|
final int timestamp = eventMap['timestamp'] as int? ?? 0; |
|
|
if (_isRunning.value) { |
|
|
|
|
|
await stopService(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_isInitialized = false; |
|
|
// 添加时间戳日志,帮助调试 |
|
|
} catch (e) { |
|
|
Logger.info('收到原生端事件: $eventType, 时间戳: $timestamp, 当前时间: ${DateTime.now().millisecondsSinceEpoch}'); |
|
|
Logger.error('释放语音交互服务资源失败: $e'); |
|
|
|
|
|
|
|
|
switch (eventType) { |
|
|
|
|
|
case 'chatHistory': |
|
|
|
|
|
final String agentId = eventMap['agentId'] as String? ?? ''; |
|
|
|
|
|
final String userMessage = eventMap['userMessage'] as String? ?? ''; |
|
|
|
|
|
final String assistantMessage = eventMap['assistantMessage'] as String? ?? ''; |
|
|
|
|
|
|
|
|
|
|
|
_eventStreamController?.add(ChatHistoryEvent( |
|
|
|
|
|
agentId: agentId, |
|
|
|
|
|
userMessage: userMessage, |
|
|
|
|
|
assistantMessage: assistantMessage, |
|
|
|
|
|
timestamp: timestamp, |
|
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
|
|
// 保存聊天记录 |
|
|
|
|
|
_saveChatHistory(agentId, userMessage, assistantMessage, timestamp); |
|
|
|
|
|
|
|
|
|
|
|
Logger.info('收到聊天历史事件: agentId=$agentId'); |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
case 'recognitionStarted': |
|
|
|
|
|
_eventStreamController?.add(RecognitionStartedEvent( |
|
|
|
|
|
timestamp: timestamp, |
|
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Logger.info('收到语音识别开始事件'); |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
Logger.warning('收到未知类型的语音交互事件: $eventType'); |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@override |
|
|
@override |
|
|
void onClose() { |
|
|
void onClose() { |
|
|
// 清理资源 |
|
|
// 清理资源 |
|
|
dispose(); |
|
|
_eventSubscription?.cancel(); |
|
|
|
|
|
_eventStreamController?.close(); |
|
|
super.onClose(); |
|
|
super.onClose(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 保存聊天记录 |
|
|
|
|
|
void _saveChatHistory(String agentId, String userMessage, String assistantMessage, int timestamp) { |
|
|
|
|
|
try { |
|
|
|
|
|
// 获取ChatHistoryService实例 |
|
|
|
|
|
final chatHistoryService = Get.find<ChatHistoryService>(); |
|
|
|
|
|
|
|
|
|
|
|
// 创建用户消息和助手消息 |
|
|
|
|
|
final userMsg = Message( |
|
|
|
|
|
role: 'user', |
|
|
|
|
|
content: userMessage, |
|
|
|
|
|
timestamp: DateTime.fromMillisecondsSinceEpoch(timestamp), |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
final assistantMsg = Message( |
|
|
|
|
|
role: 'assistant', |
|
|
|
|
|
content: assistantMessage, |
|
|
|
|
|
timestamp: DateTime.fromMillisecondsSinceEpoch(timestamp + 1), // 确保助手消息时间戳晚于用户消息 |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// 加载现有历史记录 |
|
|
|
|
|
final existingMessages = chatHistoryService.loadHistory(agentId); |
|
|
|
|
|
|
|
|
|
|
|
// 添加新消息 |
|
|
|
|
|
existingMessages.addAll([userMsg, assistantMsg]); |
|
|
|
|
|
|
|
|
|
|
|
// 保存更新后的历史记录 |
|
|
|
|
|
chatHistoryService.saveHistory(agentId, existingMessages); |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
Logger.error('保存聊天记录失败: $e'); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 语音交互事件基类 |
|
|
|
|
|
abstract class VoiceInteractionEvent { |
|
|
|
|
|
final int timestamp; |
|
|
|
|
|
|
|
|
|
|
|
VoiceInteractionEvent({required this.timestamp}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 聊天历史事件 |
|
|
|
|
|
class ChatHistoryEvent extends VoiceInteractionEvent { |
|
|
|
|
|
final String agentId; |
|
|
|
|
|
final String userMessage; |
|
|
|
|
|
final String assistantMessage; |
|
|
|
|
|
|
|
|
|
|
|
ChatHistoryEvent({ |
|
|
|
|
|
required this.agentId, |
|
|
|
|
|
required this.userMessage, |
|
|
|
|
|
required this.assistantMessage, |
|
|
|
|
|
required int timestamp, |
|
|
|
|
|
}) : super(timestamp: timestamp); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 语音识别开始事件 |
|
|
|
|
|
class RecognitionStartedEvent extends VoiceInteractionEvent { |
|
|
|
|
|
RecognitionStartedEvent({ |
|
|
|
|
|
required int timestamp, |
|
|
|
|
|
}) : super(timestamp: timestamp); |
|
|
} |
|
|
} |