OpenAI Realtime API 的现代 Swift SDK

Install Size Swift Version GitHub license

该库为使用 OpenAI 全新的 Realtime API 实现多模态对话提供了一个简单的接口。

它可以自动处理录制用户的麦克风和播放助手响应,并为您提供一个透明的 API 层,以用于高级用例。

安装

Swift Package Manager

Swift Package Manager 允许开发者轻松地将软件包集成到他们的 Xcode 项目和软件包中;并且也完全集成到 swift 编译器中。

通过 Xcode 项目使用 SPM

通过 Xcode Package 使用 SPM

一旦您设置好 Swift 软件包,将 Git 链接添加到您的 Package.swift 文件的 dependencies 值中。

dependencies: [
    .package(url: "https://github.com/m1guelpf/swift-realtime-openai.git", .branch("main"))
]

开始使用 🚀

您可以使用内置 AI 聊天的类似 iMessage 的应用程序,代码少于 60 行(包括 UI!)

import OpenAI
import SwiftUI

struct ContentView: View {
	@State private var newMessage: String = ""
	@State private var conversation = Conversation(authToken: OPENAI_KEY)

	var messages: [Item.Message] {
		conversation.entries.compactMap { switch $0 {
			case let .message(message): return message
			default: return nil
		} }
	}

	var body: some View {
		VStack(spacing: 0) {
			ScrollView {
                VStack(spacing: 12) {
                    ForEach(messages, id: \.id) { message in
                        MessageBubble(message: message)
                    }
                }
                .padding()
			}

			HStack(spacing: 12) {
				HStack {
					TextField("Chat", text: $newMessage, onCommit: { sendMessage() })
						.frame(height: 40)
						.submitLabel(.send)

					if newMessage != "" {
						Button(action: sendMessage) {
							Image(systemName: "arrow.up.circle.fill")
								.resizable()
								.aspectRatio(contentMode: .fill)
								.frame(width: 28, height: 28)
								.foregroundStyle(.white, .blue)
						}
					}
				}
				.padding(.leading)
				.padding(.trailing, 6)
				.overlay(RoundedRectangle(cornerRadius: 20).stroke(.quaternary, lineWidth: 1))
			}
			.padding()
		}
		.navigationTitle("Chat")
		.navigationBarTitleDisplayMode(.inline)
		.onAppear { try! conversation.startHandlingVoice() }
	}

	func sendMessage() {
		guard newMessage != "" else { return }

		Task {
			try await conversation.send(from: .user, text: newMessage)
			newMessage = ""
		}
	}
}

或者,如果您只是想要一个简单的应用程序,让用户可以说话,AI 可以回应

import OpenAI
import SwiftUI

struct ContentView: View {
	@State private var conversation = Conversation(authToken: OPENAI_KEY)

	var body: some View {
		Text("Say something!")
			.onAppear { try! conversation.startListening() }
	}
}

功能特性

架构

Conversation (对话)

Conversation 类为管理与模型的对话提供了一个高级接口。它封装了 RealtimeAPI 类,并处理发送和接收消息以及管理对话历史记录的细节。它还可以选择处理录制用户的麦克风并将其发送到 API,以及播放模型响应,因为它们是流式传输进来的。

读取消息

您可以通过 messages 属性访问对话中的消息。请注意,这不包括函数调用及其响应,仅包括用户和模型之间的消息。要访问完整的对话历史记录,请使用 entries 属性。例如

ScrollView {
    ScrollViewReader { scrollView in
        VStack(spacing: 12) {
            ForEach(conversation.messages, id: \.id) { message in
                MessageBubble(message: message).id(message.id)
            }
        }
        .onReceive(conversation.messages.publisher) { _ in
            withAnimation { scrollView.scrollTo(conversation.messages.last?.id, anchor: .center) }
        }
    }
}

自定义会话

您可以使用 setSession(_: Session)updateSession(withChanges: (inout Session) -> Void) 方法自定义当前会话。请注意,它们要求已经建立会话,因此建议您从 whenConnected(_: @Sendable () async throws -> Void) 回调中调用它们,或者首先等待 waitForConnection()。例如

try await conversation.whenConnected {
    try await conversation.updateSession { session in
        // update system prompt
        session.instructions = "You are a helpful assistant."

        // enable transcription of users' voice messages
        session.inputAudioTranscription = Session.InputAudioTranscription()

        // ...
    }
}

处理语音对话

Conversation 类可以自动处理双向语音对话。调用 startListening() 将开始监听用户的声音并将其发送到模型,并播放模型的响应。调用 stopListening() 将停止监听,但继续播放响应。

如果您只想播放模型响应,请调用 startHandlingVoice()。要停止监听和播放响应,请调用 stopHandlingVoice()

手动发送消息

要发送文本消息,请调用 send(from: Item.ItemRole, text: String, response: Response.Config? = nil),提供发送者的角色(.user.assistant.system)和消息的内容。您可以选择性地提供 Response.Config 对象来自定义响应,例如启用或禁用函数调用。

要手动发送音频消息(或其中的一部分),请使用有效的音频块调用 send(audioDelta: Data, commit: Bool = false)。如果 committrue,模型将认为消息已完成并开始响应它。否则,它可能会等待更多音频,具体取决于您的 Session.turnDetection 设置。

手动发送事件

要手动发送事件到 API,请使用 send(event: RealtimeAPI.ClientEvent) 方法。请注意,这绕过了 Conversation 类中的一些逻辑,例如处理中断,因此您应该尽可能优先使用其他方法。

RealtimeAPI

要直接与 API 交互,请创建 RealtimeAPI 的新实例,并提供可用的连接器之一。有一些辅助方法可以让您从 apiKey 或 URLRequest 创建实例,如下所示

let api = RealtimeAPI.webSocket(authToken: YOUR_OPENAI_API_KEY, model: String = "gpt-4o-realtime-preview") // or RealtimeAPI.webSocket(connectingTo: URLRequest)
let api = RealtimeAPI.webRTC(authToken: YOUR_OPENAI_API_KEY, model: String = "gpt-4o-realtime-preview") // or RealtimeAPI.webRTC(connectingTo: URLRequest)

您可以通过 events 属性监听新事件,如下所示

for try await event in api.events {
    switch event {
        case let .sessionCreated(event):
            print(event.session.id)
    }
}

要发送事件到 API,请使用 ClientEvent 实例调用 send 方法

try await api.send(event: .updateSession(session))
try await api.send(event: .appendInputAudioBuffer(encoding: audioData))
try await api.send(event: .createResponse())

许可证

本项目根据 MIT 许可证获得许可 - 有关详细信息,请参阅 LICENSE 文件。