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.

360 lines
17 KiB

import Flutter
import UIKit
// 修改OpenAIService类,添加转发方法
extension OpenAIService {
// 为插件提供的公共发送消息方法
public func publicSendMessageStream(messages: [[String: Any]], systemPrompt: String, onToken: @escaping (String) -> Void, onComplete: @escaping () -> Void, onError: @escaping (Error) -> Void, onFunctionCall: @escaping ([String: Any]) -> Void, onFunctionCallResult: @escaping ([String: Any], [String: Any]) -> Void) {
let callback = StreamCallback(
onToken: onToken,
onComplete: onComplete,
onError: onError,
onFunctionCall: onFunctionCall,
onFunctionCallResult: onFunctionCallResult
)
sendMessageStream(messages: messages, systemPrompt: systemPrompt, callback: callback)
}
// 为插件提供的公共发送函数调用结果方法
public func publicSendFunctionCallResult(messages: [[String: Any]], systemPrompt: String, functionCall: [String: Any], functionResult: String, onToken: @escaping (String) -> Void, onComplete: @escaping () -> Void, onError: @escaping (Error) -> Void, onFunctionCall: @escaping ([String: Any]) -> Void, onFunctionCallResult: @escaping ([String: Any], [String: Any]) -> Void) {
let callback = StreamCallback(
onToken: onToken,
onComplete: onComplete,
onError: onError,
onFunctionCall: onFunctionCall,
onFunctionCallResult: onFunctionCallResult
)
sendFunctionCallResult(messages: messages, systemPrompt: systemPrompt, functionCall: functionCall, functionResult: functionResult, callback: callback)
}
}
public class OpenAIServicePlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
// OpenAI服务实例
private let openAIService = OpenAIService()
// 事件接收器
private var eventSink: FlutterEventSink?
// 注册插件
public static func register(with registrar: FlutterPluginRegistrar) {
let methodChannel = FlutterMethodChannel(name: "com.yunqiinnovation.open_ai_service/methods", binaryMessenger: registrar.messenger())
let eventChannel = FlutterEventChannel(name: "com.yunqiinnovation.open_ai_service/events", binaryMessenger: registrar.messenger())
let instance = OpenAIServicePlugin()
registrar.addMethodCallDelegate(instance, channel: methodChannel)
eventChannel.setStreamHandler(instance)
}
// 处理方法调用
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "initialize":
if let args = call.arguments as? [String: Any],
let apiKey = args["apiKey"] as? String {
let baseUrl = args["baseUrl"] as? String ?? ""
let model = args["model"] as? String ?? ""
let mcpServer = args["mcpServer"] as? String ?? ""
let initialized = openAIService.initialize(apiKey: apiKey, baseUrl: baseUrl, model: model, mcpServer: mcpServer)
result(initialized)
} else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "初始化参数无效", details: nil))
}
case "registerFunction":
if let args = call.arguments as? [String: Any],
let name = args["name"] as? String,
let description = args["description"] as? String,
let parameters = args["parameters"] as? [String: Any] {
let registered = openAIService.registerFunction(name: name, description: description, parameters: parameters)
result(registered)
} else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "函数注册参数无效", details: nil))
}
case "sendMessage":
guard let args = call.arguments as? [String: Any],
let messagesRaw = args["messages"] as? [[String: Any]] else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "发送消息参数无效", details: nil))
return
}
let systemPrompt = args["systemPrompt"] as? String ?? ""
// 在后台线程执行
DispatchQueue.global(qos: .userInitiated).async {
do {
let response = try self.openAIService.sendMessage(messages: messagesRaw, systemPrompt: systemPrompt)
// 在主线程返回结果
DispatchQueue.main.async {
result(response)
}
} catch {
// 在主线程返回错误
DispatchQueue.main.async {
result(FlutterError(code: "OPENAI_ERROR", message: error.localizedDescription, details: nil))
}
}
}
case "sendMessageStream":
guard let args = call.arguments as? [String: Any],
let messagesRaw = args["messages"] as? [[String: Any]] else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "发送消息参数无效", details: nil))
return
}
let systemPrompt = args["systemPrompt"] as? String ?? ""
// 检查事件接收器
guard let eventSink = self.eventSink else {
result(FlutterError(code: "NO_EVENT_SINK", message: "没有可用的事件流接收器", details: nil))
return
}
// 在后台线程执行
DispatchQueue.global(qos: .userInitiated).async {
self.openAIService.publicSendMessageStream(
messages: messagesRaw,
systemPrompt: systemPrompt,
onToken: { token in
DispatchQueue.main.async {
eventSink(["type": "token", "content": token])
}
},
onComplete: {
DispatchQueue.main.async {
eventSink(["type": "complete"])
}
},
onError: { error in
DispatchQueue.main.async {
eventSink(["type": "error", "content": error.localizedDescription])
}
},
onFunctionCall: { functionCall in
DispatchQueue.main.async {
do {
let data = try JSONSerialization.data(withJSONObject: functionCall)
if let jsonString = String(data: data, encoding: .utf8) {
eventSink(["type": "function_call", "content": jsonString])
} else {
eventSink(["type": "error", "content": "函数调用数据序列化失败"])
}
} catch {
eventSink(["type": "error", "content": "函数调用数据序列化失败: \(error.localizedDescription)"])
}
}
},
onFunctionCallResult: { functionCall, functionCallResult in
DispatchQueue.main.async {
do {
let functionCallData = try JSONSerialization.data(withJSONObject: functionCall)
let resultData = try JSONSerialization.data(withJSONObject: functionCallResult)
if let functionCallString = String(data: functionCallData, encoding: .utf8),
let resultString = String(data: resultData, encoding: .utf8) {
eventSink(["type": "function_call_result", "function_call": functionCallString, "result": resultString])
} else {
eventSink(["type": "error", "content": "函数调用结果数据序列化失败"])
}
} catch {
eventSink(["type": "error", "content": "函数调用结果数据序列化失败: \(error.localizedDescription)"])
}
}
}
)
// 请求已开始
DispatchQueue.main.async {
result(true)
}
}
case "sendFunctionCallResult":
guard let args = call.arguments as? [String: Any],
let messagesRaw = args["messages"] as? [[String: Any]],
let functionCallRaw = args["functionCall"] as? [String: Any],
let functionResult = args["functionResult"] as? String else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "发送函数调用结果参数无效", details: nil))
return
}
let systemPrompt = args["systemPrompt"] as? String ?? ""
// 检查事件接收器
guard let eventSink = self.eventSink else {
result(FlutterError(code: "NO_EVENT_SINK", message: "没有可用的事件流接收器", details: nil))
return
}
// 在后台线程执行
DispatchQueue.global(qos: .userInitiated).async {
self.openAIService.publicSendFunctionCallResult(
messages: messagesRaw,
systemPrompt: systemPrompt,
functionCall: functionCallRaw,
functionResult: functionResult,
onToken: { token in
DispatchQueue.main.async {
eventSink(["type": "token", "content": token])
}
},
onComplete: {
DispatchQueue.main.async {
eventSink(["type": "complete"])
}
},
onError: { error in
DispatchQueue.main.async {
eventSink(["type": "error", "content": error.localizedDescription])
}
},
onFunctionCall: { functionCall in
DispatchQueue.main.async {
do {
let data = try JSONSerialization.data(withJSONObject: functionCall)
if let jsonString = String(data: data, encoding: .utf8) {
eventSink(["type": "function_call", "content": jsonString])
} else {
eventSink(["type": "error", "content": "函数调用数据序列化失败"])
}
} catch {
eventSink(["type": "error", "content": "函数调用数据序列化失败: \(error.localizedDescription)"])
}
}
},
onFunctionCallResult: { functionCall, functionCallResult in
DispatchQueue.main.async {
do {
let functionCallData = try JSONSerialization.data(withJSONObject: functionCall)
let resultData = try JSONSerialization.data(withJSONObject: functionCallResult)
if let functionCallString = String(data: functionCallData, encoding: .utf8),
let resultString = String(data: resultData, encoding: .utf8) {
eventSink(["type": "function_call_result", "function_call": functionCallString, "result": resultString])
} else {
eventSink(["type": "error", "content": "函数调用结果数据序列化失败"])
}
} catch {
eventSink(["type": "error", "content": "函数调用结果数据序列化失败: \(error.localizedDescription)"])
}
}
}
)
// 请求已开始
DispatchQueue.main.async {
result(true)
}
}
case "createUserMessage":
if let args = call.arguments as? [String: Any],
let content = args["content"] as? String {
let message = openAIService.createUserMessage(content: content)
result(message)
} else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "创建用户消息参数无效", details: nil))
}
case "createAssistantMessage":
if let args = call.arguments as? [String: Any],
let content = args["content"] as? String {
let message = openAIService.createAssistantMessage(content: content)
result(message)
} else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "创建助手消息参数无效", details: nil))
}
case "createUserMessageWithImage":
if let args = call.arguments as? [String: Any],
let text = args["text"] as? String,
let imageBase64 = args["imageBase64"] as? String {
let message = openAIService.createUserMessageWithImage(text: text, imageBase64: imageBase64)
result(message)
} else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "创建图片消息参数无效", details: nil))
}
case "fileToBase64":
if let args = call.arguments as? [String: Any],
let filePath = args["filePath"] as? String {
let maxSizeKB = args["maxSizeKB"] as? Int ?? 20480
let base64 = openAIService.fileToBase64(filePath, maxSizeKB: maxSizeKB)
if let base64 = base64 {
result(base64)
} else {
result(FlutterError(code: "FILE_ERROR", message: "文件转换失败", details: nil))
}
} else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "文件转换参数无效", details: nil))
}
case "initializeMcpClient":
if let args = call.arguments as? [String: Any],
let serverUrl = args["serverUrl"] as? String {
let success = openAIService.initializeMcpClient(serverUrl)
result(success)
} else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "初始化MCP客户端参数无效", details: nil))
}
case "isMcpInitialized":
result(openAIService.checkMcpInitialized())
case "closeMcpClient":
result(openAIService.closeMcpClient())
case "handleMcpToolCall":
if let args = call.arguments as? [String: Any],
let functionCallJson = args["functionCall"] as? String {
// 解析函数调用JSON
guard let data = functionCallJson.data(using: .utf8),
let functionCall = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "无效的函数调用JSON", details: nil))
return
}
// 在后台线程执行
Task {
do {
let toolResult = await openAIService.handleMcpToolCall(functionCall)
// 在主线程返回结果
DispatchQueue.main.async {
result(toolResult)
}
} catch {
// 在主线程返回错误
DispatchQueue.main.async {
result(FlutterError(code: "MCP_TOOL_ERROR", message: "处理MCP工具调用失败: \(error.localizedDescription)", details: nil))
}
}
}
} else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "处理MCP工具调用参数无效", details: nil))
}
default:
result(FlutterMethodNotImplemented)
}
}
// MARK: - FlutterStreamHandler
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
self.eventSink = events
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
self.eventSink = nil
return nil
}
}