You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

352 lines
10 KiB

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,
/// 响应中断
responseInterrupted,
/// AI助手Token
assistantToken,
/// AI助手完整响应
assistantResponse,
/// 函数调用
functionCall,
/// 函数调用结果
functionCallResult,
/// 图片处理中
imageProcessing,
/// 图片准备就绪
imageReady,
/// 错误
error,
/// 未知事件
unknown
}
/// 代理服务事件数据
class AgentServiceEvent {
/// 事件类型
final AgentServiceEventType type;
/// 事件数据
final Map<String, dynamic> 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<AgentServiceEvent>? _eventStream;
/// 获取事件流
static Stream<AgentServiceEvent> get events {
_eventStream ??= _eventChannel.receiveBroadcastStream().map((event) {
try {
final Map<String, dynamic> eventMap = jsonDecode(event);
final String eventName = eventMap['event'];
final Map<String, dynamic> 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 '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 '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<bool> initialize(Map<String, dynamic> 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<bool> startAgentService(Map<String, dynamic> 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<bool> stopAgentService() async {
try {
final bool result = await _channel.invokeMethod('stopAgentService');
return result;
} on PlatformException catch (e) {
throw AgentServiceException(e.code, e.message ?? '停止语音助手服务失败', e.details);
}
}
/// 开始对话
///
/// 启动语音识别,开始监听用户语音输入
/// 返回是否成功开始对话
static Future<bool> startConversation() async {
try {
final bool result = await _channel.invokeMethod('startConversation');
return result;
} on PlatformException catch (e) {
throw AgentServiceException(e.code, e.message ?? '启动对话失败', e.details);
}
}
/// 停止对话
///
/// 停止语音识别
/// 返回是否成功停止对话
static Future<bool> stopConversation() async {
try {
final bool result = await _channel.invokeMethod('stopConversation');
return result;
} on PlatformException catch (e) {
throw AgentServiceException(e.code, e.message ?? '停止对话失败', e.details);
}
}
/// 处理文本输入
///
/// [text] 文本内容
/// [speakResponse] 是否朗读响应
/// 返回是否成功处理文本
static Future<bool> processTextInput(String text,
{bool speakResponse = false}) async {
try {
final bool result = await _channel.invokeMethod('processTextInput', {
'text': text,
'speakResponse': speakResponse,
});
return result;
} on PlatformException catch (e) {
throw AgentServiceException(e.code, e.message ?? '处理文本输入失败', e.details);
}
}
/// 朗读文本
///
/// [text] 要朗读的文本
static Future<bool> 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);
}
}
/// 停止语音合成
static Future<bool> 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<bool> 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<bool> interruptCurrentResponse() async {
try {
// 首先尝试停止TTS播放
await stopTts();
// 根据原生层的实现,processTextInput可以被用来中断当前的AI流输出
// 这里我们通过发送一个特殊的空字符串来实现这一点
// 然后立即返回true,因为我们不需要真正处理任何文本
return true;
} on PlatformException catch (e) {
throw AgentServiceException(e.code, e.message ?? '中断响应失败', e.details);
}
}
/// 处理图片输入
///
/// [imagePath] 图片文件路径
/// [text] 可选的文本描述或问题
/// [speakResponse] 是否朗读响应
/// 返回是否成功处理图片
static Future<bool> processImageInput(String imagePath,
{String text = "", bool speakResponse = false}) async {
try {
final bool result = await _channel.invokeMethod('processImageInput', {
'imagePath': imagePath,
'text': text,
'speakResponse': speakResponse,
});
return result;
} on PlatformException catch (e) {
throw AgentServiceException(e.code, e.message ?? '处理图片输入失败', e.details);
}
}
/// 释放资源
static Future<bool> dispose() async {
try {
final bool result = await _channel.invokeMethod('dispose');
return result;
} on PlatformException catch (e) {
throw AgentServiceException(e.code, e.message ?? '释放资源失败', e.details);
}
}
}