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.
 
 
 
 
 
 

429 lines
12 KiB

import 'dart:async';
import 'dart:convert';
import 'package:flutter/services.dart';
/// ChatAPI服务异常
class ChatApiException implements Exception {
final String message;
ChatApiException(this.message);
@override
String toString() => 'ChatApiException: $message';
}
/// ChatAPI服务事件类型
enum ChatApiEventType {
token,
complete,
error,
functionCall,
}
/// ChatAPI服务事件
class ChatApiEvent {
final ChatApiEventType type;
final dynamic content;
final Map<String, dynamic>? meta;
ChatApiEvent({required this.type, this.content, this.meta});
factory ChatApiEvent.fromMap(Map<String, dynamic> map) {
final typeStr = map['type'] as String;
final content = map['content'];
final rawMeta = map['meta'] as String?;
final meta = rawMeta != null ? jsonDecode(rawMeta) : null;
return ChatApiEvent(
type: _typeFromString(typeStr),
content: content,
meta: meta,
);
}
static ChatApiEventType _typeFromString(String typeStr) {
switch (typeStr) {
case 'token':
return ChatApiEventType.token;
case 'complete':
return ChatApiEventType.complete;
case 'error':
return ChatApiEventType.error;
case 'functionCall':
return ChatApiEventType.functionCall;
default:
throw ArgumentError('未知的事件类型: $typeStr');
}
}
}
/// ChatAPI服务插件
class ChatApiService {
static const MethodChannel _channel =
MethodChannel('com.yunqiinnovation.chat_api/methods');
static const EventChannel _eventChannel =
EventChannel('com.yunqiinnovation.chat_api/events');
/// 事件流控制器
StreamController<ChatApiEvent>? _eventStreamController;
/// 事件流
Stream<ChatApiEvent>? _eventStream;
/// 获取事件流
Stream<ChatApiEvent> get eventStream {
if (_eventStream == null) {
_eventStreamController = StreamController<ChatApiEvent>.broadcast();
_eventStream = _eventStreamController!.stream;
// 监听原生事件
_eventChannel.receiveBroadcastStream().listen(
(dynamic event) {
if (event is Map<dynamic, dynamic>) {
final eventMap = Map<String, dynamic>.from(event);
final chatApiEvent = ChatApiEvent.fromMap(eventMap);
_eventStreamController!.add(chatApiEvent);
}
},
onError: (error) {
_eventStreamController!.addError(ChatApiException('事件流错误: $error'));
},
);
}
return _eventStream!;
}
/// 初始化ChatAPI服务
///
/// [apiKey] OpenAI API密钥
/// [baseUrl] 可选,自定义API基础URL
/// [model] 可选,自定义使用的模型
/// [mcpServer] 可选,MCP服务器地址配置
Future<bool> initialize({
required String apiKey,
String baseUrl = '',
String model = '',
String mcpServer = '',
}) async {
try {
final result = await _channel.invokeMethod<bool>(
'initialize',
{
'apiKey': apiKey,
'baseUrl': baseUrl,
'model': model,
'mcpServer': mcpServer,
},
);
return result ?? false;
} catch (e) {
throw ChatApiException('初始化失败: $e');
}
}
/// 注册函数
///
/// [name] 函数名称
/// [description] 函数描述
/// [parameters] 函数参数
Future<bool> registerFunction({
required String name,
required String description,
required Map<String, dynamic> parameters,
}) async {
try {
final result = await _channel.invokeMethod<bool>(
'registerFunction',
{
'name': name,
'description': description,
'parameters': parameters,
},
);
return result ?? false;
} catch (e) {
throw ChatApiException('注册函数失败: $e');
}
}
/// 创建用户消息
///
/// [content] 消息内容
Future<Map<String, dynamic>> createUserMessage(String content) async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'createUserMessage',
{'content': content},
);
if (result == null) {
throw ChatApiException('创建用户消息失败: 结果为空');
}
return Map<String, dynamic>.from(result);
} catch (e) {
throw ChatApiException('创建用户消息失败: $e');
}
}
/// 创建助手消息
///
/// [content] 消息内容
Future<Map<String, dynamic>> createAssistantMessage(String content) async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'createAssistantMessage',
{'content': content},
);
if (result == null) {
throw ChatApiException('创建助手消息失败: 结果为空');
}
return Map<String, dynamic>.from(result);
} catch (e) {
throw ChatApiException('创建助手消息失败: $e');
}
}
/// 发送消息(非流式输出)
///
/// [messages] 消息列表
Future<String> sendMessage({
required List<Map<String, dynamic>> messages,
String? systemPrompt, //提示词
String? userProperties, //用户属性
}) async {
try {
// 构建新的消息列表
final List<Map<String, dynamic>> updatedMessages = [];
// 如果有提示词,先添加 system role
if (systemPrompt != null && systemPrompt.isNotEmpty) {
updatedMessages.add({
'role': 'system',
'content': systemPrompt,
});
}
// 如果有用户属性,可以作为 user 的一个"隐式指令"传进去
if (userProperties != null && userProperties.isNotEmpty) {
updatedMessages.add({
'role': 'user',
'content': '【用户属性】$userProperties',
});
}
// 添加原始消息
updatedMessages.addAll(messages);
final result = await _channel.invokeMethod<String>(
'sendMessage',
{
'messages': updatedMessages,
},
);
if (result == null) {
throw ChatApiException('发送消息失败: 结果为空');
}
return result;
} catch (e) {
throw ChatApiException('发送消息失败: $e');
}
}
/// 发送消息(流式输出)
///
/// [messages] 消息列表
///
/// 返回一个布尔值,表示请求是否已开始
Future<bool> sendMessageStream({
required List<Map<String, dynamic>> messages,
String? systemPrompt, //提示词
String? userProperties, //用户属性
}) async {
try {
// 构建新的消息列表
final List<Map<String, dynamic>> updatedMessages = [];
// 如果有提示词,先添加 system role
if (systemPrompt != null && systemPrompt.isNotEmpty) {
updatedMessages.add({
'role': 'system',
'content': systemPrompt,
});
}
// 如果有用户属性,可以作为 user 的一个"隐式指令"传进去
if (userProperties != null && userProperties.isNotEmpty) {
updatedMessages.add({
'role': 'user',
'content': '【用户属性】$userProperties',
});
}
// 添加原始消息
updatedMessages.addAll(messages);
final result = await _channel.invokeMethod<bool>(
'sendMessageStream',
{
'messages': updatedMessages,
},
);
return result ?? false;
} catch (e) {
throw ChatApiException('发送流式消息失败: $e');
}
}
/// 发送函数调用结果
///
/// [messages] 消息列表
/// [functionCall] 函数调用信息
/// [functionResult] 函数调用结果
///
/// 返回一个布尔值,表示请求是否已开始
Future<bool> sendFunctionCallResult({
required List<Map<String, dynamic>> messages,
required Map<String, dynamic> functionCall,
required String functionResult,
}) async {
try {
final result = await _channel.invokeMethod<bool>(
'sendFunctionCallResult',
{
'messages': messages,
'functionCall': functionCall,
'functionResult': functionResult,
},
);
return result ?? false;
} catch (e) {
throw ChatApiException('发送函数调用结果失败: $e');
}
}
/// 取消当前流式请求
Future<bool> cancelCurrentStream() async {
try {
final result = await _channel.invokeMethod<bool>('cancelCurrentStream');
return result ?? false;
} catch (e) {
throw ChatApiException('取消请求失败: $e');
}
}
/// 从JSON字符串解析函数调用
Map<String, dynamic> parseFunctionCall(String functionCallJson) {
try {
return json.decode(functionCallJson) as Map<String, dynamic>;
} catch (e) {
throw ChatApiException('解析函数调用失败: $e');
}
}
/// 初始化MCP客户端
///
/// [serverUrl] MCP服务器WebSocket地址
///
/// 返回一个布尔值,表示请求是否已开始初始化(实际初始化过程是异步的)
Future<bool> initializeMcpClient(String serverUrl) async {
try {
final result = await _channel.invokeMethod<bool>(
'initializeMcpClient',
{'serverUrl': serverUrl},
);
return result ?? false;
} catch (e) {
throw ChatApiException('初始化MCP客户端失败: $e');
}
}
/// 检查MCP客户端是否已初始化
Future<bool> isMcpInitialized() async {
try {
final result = await _channel.invokeMethod<bool>('isMcpInitialized');
return result ?? false;
} catch (e) {
throw ChatApiException('检查MCP客户端状态失败: $e');
}
}
/// 关闭MCP客户端
Future<bool> closeMcpClient() async {
try {
final result = await _channel.invokeMethod<bool>('closeMcpClient');
return result ?? false;
} catch (e) {
throw ChatApiException('关闭MCP客户端失败: $e');
}
}
/// 处理MCP工具调用
///
/// [functionCall] 函数调用信息,必须包含name和arguments字段
///
/// 返回工具调用结果
Future<String> handleMcpToolCall(Map<String, dynamic> functionCall) async {
try {
final result = await _channel.invokeMethod<String>(
'handleMcpToolCall',
{'functionCall': jsonEncode(functionCall)},
);
return result ?? "处理MCP工具调用失败: 结果为空";
} catch (e) {
throw ChatApiException('处理MCP工具调用失败: $e');
}
}
/// 处理函数调用(自动处理本地函数和MCP工具)
///
/// 这是一个辅助方法,用于简化函数调用处理流程:
/// 1. MCP工具会被自动处理,不会通过回调返回
/// 2. 只有本地函数才需要手动处理
/// 3. 提供统一的处理接口
///
/// 参数:
/// [functionCall] 函数调用信息
/// [messages] 消息上下文
/// [localFunctionHandler] 本地函数处理器,接收函数名和参数,返回处理结果
///
/// 返回值:
/// 布尔值表示是否成功处理(已将结果发送回OpenAI)
Future<bool> handleFunctionCall({
required Map<String, dynamic> functionCall,
required List<Map<String, dynamic>> messages,
required Future<String> Function(
String name, Map<String, dynamic> arguments)
localFunctionHandler,
}) async {
final name = functionCall['name'] as String;
final argumentsJson = functionCall['arguments'] as String;
// 解析参数
Map<String, dynamic> arguments;
try {
arguments = json.decode(argumentsJson) as Map<String, dynamic>;
} catch (e) {
arguments = <String, dynamic>{};
}
try {
// 调用本地函数处理器
final result = await localFunctionHandler(name, arguments);
// 发送结果回ChatAPI
return sendFunctionCallResult(
messages: messages,
functionCall: functionCall,
functionResult: result,
);
} catch (e) {
throw ChatApiException('处理函数调用失败: $e');
}
}
}