import 'dart:async'; import 'dart:convert'; import 'package:flutter/services.dart'; /// 代理服务事件类型 enum AgentServiceEventType { /// 识别开始 recognitionStarted, /// 识别中 recognizing, /// 识别最终结果 recognitionResult, /// 识别停止 recognitionStopped, /// 识别取消 recognitionCanceled, /// 自动停止 autoStop, /// 识别错误 recognitionError, /// TTS开始 ttsStarted, /// TTS完成 ttsCompleted, /// TTS取消 ttsCanceled, /// TTS停止 ttsStopped, /// 播放开始 playbackStarted, /// 播放完成 playbackCompleted, /// 响应中断 responseInterrupted, /// AI助手Token assistantToken, /// AI助手完整响应 assistantResponse, /// 函数调用 functionCall, /// 函数调用结果 functionCallResult, /// AI助手使用统计 assistantUsages, /// 图片处理中 imageProcessing, /// 图片准备就绪 imageReady, /// 错误 error, /// 未知事件 unknown } /// 代理服务事件数据 class AgentServiceEvent { /// 事件类型 final AgentServiceEventType type; /// 事件数据 final Map data; AgentServiceEvent({required this.type, required this.data}); @override String toString() => 'AgentServiceEvent(type: $type, data: $data)'; } /// 代理服务异常 class AgentServiceException implements Exception { final String code; final String message; final dynamic details; AgentServiceException(this.code, this.message, [this.details]); @override String toString() => '代理服务异常($code): $message'; } /// 代理服务插件 class AgentService { static const MethodChannel _channel = MethodChannel('com.yunqiinnovation.agent_service'); static const EventChannel _eventChannel = EventChannel('com.yunqiinnovation.agent_service/events'); static Stream? _eventStream; /// 获取事件流 static Stream get events { _eventStream ??= _eventChannel.receiveBroadcastStream().map((event) { try { final Map eventMap = jsonDecode(event); final String eventName = eventMap['event']; final Map eventData = eventMap['data']; // 将事件名称转换为枚举 final AgentServiceEventType type = _stringToEventType(eventName); return AgentServiceEvent(type: type, data: eventData); } catch (e) { return AgentServiceEvent( type: AgentServiceEventType.unknown, data: {'error': e.toString(), 'rawEvent': event}); } }); return _eventStream!; } /// 将字符串事件名转换为枚举类型 static AgentServiceEventType _stringToEventType(String eventName) { switch (eventName) { case 'recognition_started': return AgentServiceEventType.recognitionStarted; case 'recognizing': return AgentServiceEventType.recognizing; case 'recognition_result': return AgentServiceEventType.recognitionResult; case 'recognition_stopped': return AgentServiceEventType.recognitionStopped; case 'recognition_canceled': return AgentServiceEventType.recognitionCanceled; case 'auto_stop': return AgentServiceEventType.autoStop; case 'error': return AgentServiceEventType.error; case 'tts_started': return AgentServiceEventType.ttsStarted; case 'tts_completed': return AgentServiceEventType.ttsCompleted; case 'tts_canceled': return AgentServiceEventType.ttsCanceled; case 'tts_stopped': return AgentServiceEventType.ttsStopped; case 'playback_started': return AgentServiceEventType.playbackStarted; case 'playback_completed': return AgentServiceEventType.playbackCompleted; case 'response_interrupted': return AgentServiceEventType.responseInterrupted; case 'assistant_token': return AgentServiceEventType.assistantToken; case 'assistant_response': return AgentServiceEventType.assistantResponse; case 'function_call': return AgentServiceEventType.functionCall; case 'function_call_result': return AgentServiceEventType.functionCallResult; case 'assistant_usages': return AgentServiceEventType.assistantUsages; case 'image_processing': return AgentServiceEventType.imageProcessing; case 'image_ready': return AgentServiceEventType.imageReady; default: return AgentServiceEventType.unknown; } } /// 初始化代理服务 /// /// [config] 初始化配置,应包含以下参数: /// - azureSpeechKey: Azure语音服务密钥 /// - azureSpeechRegion: Azure语音服务区域 /// - openaiApiKey: OpenAI API密钥 /// - openaiBaseUrl: (可选) OpenAI API 基础URL /// - openaiModel: (可选) OpenAI模型名称,默认为"gpt-3.5-turbo" /// - mcpServer: (可选) MCP服务器地址 /// - systemPrompt: (可选) 自定义系统提示词,用于设置AI语音助手的行为和风格 /// /// 返回是否初始化成功 static Future initialize(Map config) async { try { final bool result = await _channel.invokeMethod('initialize', { 'config': config, }); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '初始化失败', e.details); } } /// 启动语音助手服务 /// /// 启动常驻的语音助手服务,支持后台蓝牙设备唤醒和媒体按钮唤醒 /// [config] 配置参数,应包含以下参数: /// - azureSpeechKey: Azure语音服务密钥 /// - azureSpeechRegion: Azure语音服务区域 /// - openaiApiKey: OpenAI API密钥 /// - openaiBaseUrl: (可选) OpenAI API 基础URL /// - openaiModel: (可选) OpenAI模型名称,默认为"gpt-3.5-turbo" /// - mcpServer: (可选) MCP服务器地址 /// /// 返回是否成功启动服务 static Future startAgentService(Map config) async { try { final bool result = await _channel.invokeMethod('startAgentService', { 'config': config, }); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '启动语音助手服务失败', e.details); } } /// 停止语音助手服务 /// /// 停止常驻的语音助手服务 /// 返回是否成功停止服务 static Future stopAgentService() async { try { final bool result = await _channel.invokeMethod('stopAgentService'); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '停止语音助手服务失败', e.details); } } /// 开始对话 /// /// 启动语音识别,开始监听用户语音输入 /// [mode] 语音识别模式:'ble_wakeup'(BLE唤醒), 'phone_call'(通话模式), 'push_to_talk'(按住说话), 'normal'(普通模式) /// 返回是否成功开始对话 // 防抖相关静态字段(全局统一防抖) // 说明:用于限制 startConversation/stopConversation 在极短时间内的重复触发 static DateTime? _lastStartConversationAt; static const Duration _conversationDebounce = Duration(milliseconds: 600); static Future startConversation({String mode = 'normal'}) async { // 防抖:短时间内重复点击直接忽略(冷却式防抖) final now = DateTime.now(); if (_lastStartConversationAt != null && now.difference(_lastStartConversationAt!) < _conversationDebounce) { // 函数级注释:当在冷却时间内再次调用时,直接返回 false 表示此次调用被防抖拦截 // 对于启动操作,如果被防抖拦截,意味着用户点击太快。 // 返回 false 是正确的,因为我们确实没有启动新的会话。 return false; } _lastStartConversationAt = now; try { // 在启动新对话前,确保之前的对话已完全停止 // 这不仅可以清理状态,还能避免因快速切换导致的资源冲突 // await stopConversation(); print('liwei--------- view startConversation'); final bool result = await _channel.invokeMethod('startConversation', { 'mode': mode, }); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '启动对话失败', e.details); } } static Future isInterrupt(bool isInterrupt) async { try { final bool result = await _channel.invokeMethod('isInterrupt', { 'isinterrupt': isInterrupt, }); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '检查是否打断失败', e.details); } } /// 设备识别器 /// /// 启动语音识别,开始监听用户语音输入 /// 返回是否成功开始对话 static Future recognizeCallback() async { try { final bool result = await _channel.invokeMethod('recognizeCallback'); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '设置识别器失败', e.details); } } /// 恢复原始音频状态 /// /// 恢复原始音频状态 /// 返回是否成功恢复原始音频状态 static Future restoreOriginalAudioState() async { try { final bool result = await _channel.invokeMethod('restoreOriginalAudioState'); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '恢复原始音频状态失败', e.details); } } /// 禁用蓝牙音频 /// /// 禁用蓝牙音频 /// 返回是否成功禁用蓝牙音频 static Future disableBluetoothAudio() async { try { final bool result = await _channel.invokeMethod('disableBluetoothAudio'); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '禁用蓝牙音频失败', e.details); } } /// 停止对话 /// /// 停止语音识别 /// 返回是否成功停止对话 static Future stopConversation() async { // 移除防抖拦截! // 停止操作是安全且必要的,不应该被拦截。 // 即使频繁调用,Native 层也能处理(我们已经修复了 Native 层的并发问题)。 try { print('liwei--------- view stopConversation'); final bool result = await _channel.invokeMethod('stopConversation'); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '停止对话失败', e.details); } } // /// 停止对话 // /// // /// 停止语音识别 // /// 返回是否成功停止对话 // static Future audioContinue() async { // try { // final bool result = await _channel.invokeMethod('audioContinue'); // return result; // } on PlatformException catch (e) { // throw AgentServiceException(e.code, e.message ?? '停止对话失败', e.details); // } // } // /// 停止对话 // /// // /// 停止语音识别 // /// 返回是否成功停止对话 // static Future audioPause() async { // try { // final bool result = await _channel.invokeMethod('audioPause'); // return result; // } on PlatformException catch (e) { // throw AgentServiceException(e.code, e.message ?? '停止对话失败', e.details); // } // } /// 处理文本输入 /// /// [text] 文本内容 /// [speakResponse] 是否朗读响应 /// 返回是否成功处理文本 static Future processTextInput(String sessionid, String text, {bool speakResponse = false}) async { try { final bool result = await _channel.invokeMethod('processTextInput', { 'sessionid': sessionid, 'text': text, 'speakResponse': speakResponse, }); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '处理文本输入失败', e.details); } } /// 朗读文本 /// /// [text] 要朗读的文本 static Future speakText(String text) async { try { final bool result = await _channel.invokeMethod('speakText', { 'text': text, }); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '语音合成失败', e.details); } } /// 设置TTS语速 static Future setTtsRate(int rate) async { try { final bool result = await _channel.invokeMethod('setTtsRate', { 'rate': rate, }); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '设置TTS语速失败', e.details); } } /// 停止语音合成 static Future stopAiSteam() async { try { final bool result = await _channel.invokeMethod('stopAiSteam'); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '停止语音合成失败', e.details); } } /// 停止语音合成 static Future stopTts() async { try { final bool result = await _channel.invokeMethod('stopTts'); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '停止语音合成失败', e.details); } } /// 清除聊天历史 static Future clearChatHistory() async { try { final bool result = await _channel.invokeMethod('clearChatHistory'); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '清除聊天历史失败', e.details); } } /// 中断当前响应 /// /// 停止语音合成和AI流输出 static Future interruptCurrentResponse() async { try { final bool result = await _channel.invokeMethod('interruptCurrentResponse'); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '中断响应失败', e.details); } } /// 处理图片输入 /// /// [imagePath] 图片文件路径 /// [text] 可选的文本描述或问题 /// [imageUrl] 图片url /// [speakResponse] 是否朗读响应 /// 返回是否成功处理图片 static Future processImageInput( String sessionid, String imagePath, String imageUrl, {String text = "", bool speakResponse = false}) async { try { final bool result = await _channel.invokeMethod('processImageInput', { 'sessionid': sessionid, 'imagePath': imagePath, 'imageUrl': imageUrl, 'text': text, 'speakResponse': speakResponse, }); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '处理图片输入失败', e.details); } } /// 设置支持的语音识别语言 /// /// [languages] 支持的语言列表,如 ["zh-CN", "en-US"] /// 返回是否设置成功 static Future setSupportedLanguages(List languages) async { try { final bool result = await _channel.invokeMethod('setSupportedLanguages', { 'languages': languages, }); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '设置支持语言失败', e.details); } } /// 更新用户信息 /// /// 更新用户信息,包括姓名、手机号、邮箱等 /// [config] 配置参数,应包含以下参数: /// - azureSpeechKey: Azure语音服务密钥 /// - azureSpeechRegion: Azure语音服务区域 /// - openaiApiKey: OpenAI API密钥 /// - openaiBaseUrl: (可选) OpenAI API 基础URL /// - openaiModel: (可选) OpenAI模型名称,默认为"gpt-3.5-turbo" /// - mcpServer: (可选) MCP服务器地址 /// /// 返回是否成功启动服务 static Future setVipExptime(int vipexptime) async { try { final bool result = await _channel.invokeMethod('setVipExptime', { 'vipexptime': vipexptime, }); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '更新用户信息失败', e.details); } } /// 设置是否可以ble唤醒agent /// /// [canWakeUp] 是否可以ble唤醒agent /// 返回是否设置成功 static Future setCanWakeUp(bool canWakeUp) async { try { final bool result = await _channel.invokeMethod('setCanWakeUp', { 'canWakeUp': canWakeUp, }); return result; } on PlatformException catch (e) { throw AgentServiceException( e.code, e.message ?? '设置是否可以ble唤醒agent失败', e.details); } } /// 释放资源 static Future dispose() async { try { final bool result = await _channel.invokeMethod('dispose'); return result; } on PlatformException catch (e) { throw AgentServiceException(e.code, e.message ?? '释放资源失败', e.details); } } }