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.
340 lines
13 KiB
340 lines
13 KiB
import Flutter
|
|
import UIKit
|
|
import Foundation
|
|
|
|
/// OpenAI服务插件主类
|
|
public class OpenAIServicePlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
|
|
|
|
// MARK: - 常量
|
|
private static let methodChannelName = "com.yunqiinnovation.open_ai_service/methods"
|
|
private static let eventChannelName = "com.yunqiinnovation.open_ai_service/events"
|
|
|
|
// MARK: - 属性
|
|
private var methodChannel: FlutterMethodChannel?
|
|
private var eventChannel: FlutterEventChannel?
|
|
private var eventSink: FlutterEventSink?
|
|
private var openAIService: OpenAIService?
|
|
|
|
// MARK: - 插件注册
|
|
public static func register(with registrar: FlutterPluginRegistrar) {
|
|
let instance = OpenAIServicePlugin()
|
|
|
|
// 初始化方法通道
|
|
let methodChannel = FlutterMethodChannel(
|
|
name: methodChannelName,
|
|
binaryMessenger: registrar.messenger()
|
|
)
|
|
instance.methodChannel = methodChannel
|
|
registrar.addMethodCallDelegate(instance, channel: methodChannel)
|
|
|
|
// 初始化事件通道
|
|
let eventChannel = FlutterEventChannel(
|
|
name: eventChannelName,
|
|
binaryMessenger: registrar.messenger()
|
|
)
|
|
instance.eventChannel = eventChannel
|
|
eventChannel.setStreamHandler(instance)
|
|
|
|
// 初始化OpenAI服务
|
|
instance.openAIService = OpenAIService()
|
|
}
|
|
|
|
// MARK: - FlutterPlugin
|
|
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
guard let service = openAIService else {
|
|
result(FlutterError(code: "SERVICE_NOT_INITIALIZED",
|
|
message: "OpenAI服务未初始化",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
switch call.method {
|
|
case "initialize":
|
|
handleInitialize(call: call, result: result, service: service)
|
|
|
|
case "registerFunction":
|
|
handleRegisterFunction(call: call, result: result, service: service)
|
|
|
|
case "sendMessage":
|
|
handleSendMessage(call: call, result: result, service: service)
|
|
|
|
case "sendMessageStream":
|
|
handleSendMessageStream(call: call, result: result, service: service)
|
|
|
|
case "sendFunctionCallResult":
|
|
handleSendFunctionCallResult(call: call, result: result, service: service)
|
|
|
|
case "createUserMessage":
|
|
handleCreateUserMessage(call: call, result: result, service: service)
|
|
|
|
case "createAssistantMessage":
|
|
handleCreateAssistantMessage(call: call, result: result, service: service)
|
|
|
|
case "initializeMcpClient":
|
|
handleInitializeMcpClient(call: call, result: result, service: service)
|
|
|
|
case "isMcpInitialized":
|
|
handleIsMcpInitialized(call: call, result: result, service: service)
|
|
|
|
case "closeMcpClient":
|
|
handleCloseMcpClient(call: call, result: result, service: service)
|
|
|
|
case "handleMcpToolCall":
|
|
handleMcpToolCall(call: call, result: result, service: service)
|
|
|
|
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
|
|
}
|
|
|
|
// MARK: - 方法处理
|
|
private func handleInitialize(call: FlutterMethodCall, result: @escaping FlutterResult, service: OpenAIService) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let apiKey = args["apiKey"] as? String else {
|
|
result(FlutterError(code: "INVALID_ARGUMENTS",
|
|
message: "缺少必要参数",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
let baseUrl = args["baseUrl"] as? String ?? ""
|
|
let model = args["model"] as? String ?? ""
|
|
let mcpServer = args["mcpServer"] as? String ?? ""
|
|
|
|
let success = service.initialize(
|
|
apiKey: apiKey,
|
|
baseUrl: baseUrl,
|
|
model: model,
|
|
mcpServer: mcpServer
|
|
)
|
|
|
|
result(success)
|
|
}
|
|
|
|
private func handleRegisterFunction(call: FlutterMethodCall, result: @escaping FlutterResult, service: OpenAIService) {
|
|
guard 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] else {
|
|
result(FlutterError(code: "INVALID_ARGUMENTS",
|
|
message: "缺少必要参数",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
let success = service.registerFunction(name: name, description: description, parameters: parameters)
|
|
result(success)
|
|
}
|
|
|
|
private func handleSendMessage(call: FlutterMethodCall, result: @escaping FlutterResult, service: OpenAIService) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let messagesArray = args["messages"] as? [[String: Any]] else {
|
|
result(FlutterError(code: "INVALID_ARGUMENTS",
|
|
message: "缺少消息参数",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
do {
|
|
let response = try service.sendMessage(messages: messagesArray)
|
|
result(response)
|
|
} catch {
|
|
result(FlutterError(code: "OPENAI_ERROR",
|
|
message: error.localizedDescription,
|
|
details: nil))
|
|
}
|
|
}
|
|
|
|
private func handleSendMessageStream(call: FlutterMethodCall, result: @escaping FlutterResult, service: OpenAIService) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let messagesArray = args["messages"] as? [[String: Any]] else {
|
|
result(FlutterError(code: "INVALID_ARGUMENTS",
|
|
message: "缺少消息参数",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
guard let eventSink = self.eventSink else {
|
|
result(FlutterError(code: "NO_EVENT_SINK",
|
|
message: "没有可用的事件流接收器",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
// 创建回调处理器
|
|
let callback = StreamCallbackImpl(eventSink: eventSink)
|
|
|
|
// 发送流式消息
|
|
service.sendMessageStream(messages: messagesArray, callback: callback)
|
|
|
|
result(true)
|
|
}
|
|
|
|
private func handleSendFunctionCallResult(call: FlutterMethodCall, result: @escaping FlutterResult, service: OpenAIService) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let messagesArray = args["messages"] as? [[String: Any]],
|
|
let functionCall = args["functionCall"] as? [String: Any],
|
|
let functionResult = args["functionResult"] as? String else {
|
|
result(FlutterError(code: "INVALID_ARGUMENTS",
|
|
message: "缺少必要参数",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
guard let eventSink = self.eventSink else {
|
|
result(FlutterError(code: "NO_EVENT_SINK",
|
|
message: "没有可用的事件流接收器",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
// 创建回调处理器
|
|
let callback = StreamCallbackImpl(eventSink: eventSink)
|
|
|
|
// 发送函数调用结果
|
|
service.sendFunctionCallResult(
|
|
messages: messagesArray,
|
|
functionCall: functionCall,
|
|
functionResult: functionResult,
|
|
callback: callback
|
|
)
|
|
|
|
result(true)
|
|
}
|
|
|
|
private func handleCreateUserMessage(call: FlutterMethodCall, result: @escaping FlutterResult, service: OpenAIService) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let content = args["content"] as? String else {
|
|
result(FlutterError(code: "INVALID_ARGUMENTS",
|
|
message: "缺少内容参数",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
let message = service.createUserMessage(content: content)
|
|
result(message)
|
|
}
|
|
|
|
private func handleCreateAssistantMessage(call: FlutterMethodCall, result: @escaping FlutterResult, service: OpenAIService) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let content = args["content"] as? String else {
|
|
result(FlutterError(code: "INVALID_ARGUMENTS",
|
|
message: "缺少内容参数",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
let message = service.createAssistantMessage(content: content)
|
|
result(message)
|
|
}
|
|
|
|
private func handleInitializeMcpClient(call: FlutterMethodCall, result: @escaping FlutterResult, service: OpenAIService) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let serverUrl = args["serverUrl"] as? String else {
|
|
result(FlutterError(code: "INVALID_ARGUMENTS",
|
|
message: "缺少服务器URL参数",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
let success = service.initializeMcpClient(serverUrl: serverUrl)
|
|
result(success)
|
|
}
|
|
|
|
private func handleIsMcpInitialized(call: FlutterMethodCall, result: @escaping FlutterResult, service: OpenAIService) {
|
|
let isInitialized = service.isMcpInitialized()
|
|
result(isInitialized)
|
|
}
|
|
|
|
private func handleCloseMcpClient(call: FlutterMethodCall, result: @escaping FlutterResult, service: OpenAIService) {
|
|
service.closeMcpClient()
|
|
result(true)
|
|
}
|
|
|
|
private func handleMcpToolCall(call: FlutterMethodCall, result: @escaping FlutterResult, service: OpenAIService) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let functionCallJson = args["functionCall"] as? String else {
|
|
result(FlutterError(code: "INVALID_ARGUMENTS",
|
|
message: "缺少函数调用参数",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
Task {
|
|
do {
|
|
let toolResult = try await service.handleMcpToolCall(functionCallJson: functionCallJson)
|
|
DispatchQueue.main.async {
|
|
result(toolResult)
|
|
}
|
|
} catch {
|
|
DispatchQueue.main.async {
|
|
result(FlutterError(code: "MCP_TOOL_ERROR",
|
|
message: "处理MCP工具调用失败: \(error.localizedDescription)",
|
|
details: nil))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - 流式回调实现
|
|
private class StreamCallbackImpl: StreamCallback {
|
|
private let eventSink: FlutterEventSink
|
|
|
|
init(eventSink: @escaping FlutterEventSink) {
|
|
self.eventSink = eventSink
|
|
}
|
|
|
|
func onToken(_ token: String) {
|
|
DispatchQueue.main.async {
|
|
self.eventSink(["type": "token", "content": token])
|
|
}
|
|
}
|
|
|
|
func onComplete() {
|
|
DispatchQueue.main.async {
|
|
self.eventSink(["type": "complete"])
|
|
}
|
|
}
|
|
|
|
func onError(_ error: Error) {
|
|
DispatchQueue.main.async {
|
|
self.eventSink(["type": "error", "content": error.localizedDescription])
|
|
}
|
|
}
|
|
|
|
func onFunctionCall(_ functionCall: [String: Any]) {
|
|
DispatchQueue.main.async {
|
|
if let jsonData = try? JSONSerialization.data(withJSONObject: functionCall),
|
|
let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
self.eventSink(["type": "function_call", "content": jsonString])
|
|
}
|
|
}
|
|
}
|
|
|
|
func onFunctionCallResult(_ functionCall: [String: Any], _ functionCallResult: [String: Any]) {
|
|
DispatchQueue.main.async {
|
|
if let callData = try? JSONSerialization.data(withJSONObject: functionCall),
|
|
let callString = String(data: callData, encoding: .utf8),
|
|
let resultData = try? JSONSerialization.data(withJSONObject: functionCallResult),
|
|
let resultString = String(data: resultData, encoding: .utf8) {
|
|
self.eventSink([
|
|
"type": "function_call_result",
|
|
"function_call": callString,
|
|
"result": resultString
|
|
])
|
|
}
|
|
}
|
|
}
|
|
}
|