SwiftOpenAI

GitHub
651 124 简单 1 次阅读 2天前MIT插件语言模型
AI 解读 由 AI 自动生成,仅供参考

SwiftOpenAI 是一款专为 Swift 开发者打造的开源工具包,旨在让苹果生态下的应用轻松对接 OpenAI 的强大能力。它全面覆盖了 OpenAI 公共 API 的所有核心功能,从基础的文本对话、图像生成、语音转录,到高级的函数调用、结构化输出及微调训练,开发者无需重复造轮子,即可在 iOS、macOS、watchOS 甚至 Linux 平台上快速集成人工智能特性。

这款工具主要解决了原生 Swift 项目调用外部 AI 接口时代码繁琐、适配困难的问题。通过提供简洁统一的接口,它显著降低了开发门槛,让开发者能专注于业务逻辑而非底层网络请求的处理。特别值得一提的是,SwiftOpenAI 不仅支持 Azure 和 AIProxy 等替代服务,还率先适配了最新的低延迟双向语音“实时 API"以及助手流式传输功能,为构建即时互动的语音助手或复杂智能体应用提供了坚实的技术支撑。

无论是希望为 App 增添智能对话功能的独立开发者,还是需要构建原型的研究人员,SwiftOpenAI 都是理想的选择。它遵循 MIT 协议,拥有活跃的社区支持,并兼容 Swift 5.9 及 SwiftUI,帮助各类技术背景的用户高效地将前沿 AI 模型融入自己的创意之中。

使用场景

一位 iOS 开发者正在为一款面向视障用户的辅助应用构建实时语音交互功能,需要实现低延迟的双向对话及多模态内容处理。

没有 SwiftOpenAI 时

  • 开发者需手动封装复杂的 HTTP 请求与 WebSocket 连接代码,尤其在处理 OpenAI 最新的 Realtime API 双向音频流时,极易出现断连或延迟过高问题。
  • 集成语音转文字、图像识别及结构化输出等功能时,需要分别编写大量重复的解析逻辑,导致代码库臃肿且难以维护。
  • 缺乏对 Assistants API 和线程管理的原生支持,处理多轮对话上下文时需自行设计状态机,开发周期被大幅拉长。
  • 跨平台适配(如 macOS 或 watchOS)时,需反复调整网络层代码以兼容不同系统的并发机制,测试成本极高。

使用 SwiftOpenAI 后

  • 直接调用封装好的 Realtime API 接口,轻松实现毫秒级低延迟语音对话,底层连接稳定性由库自动保障。
  • 通过统一的 Swift 原生接口一键访问音频转录、视觉分析及结构化数据输出,代码量减少约 70%,逻辑清晰易读。
  • 内置完整的 Assistants、Threads 及 Runs 对象管理,多轮对话上下文自动维护,让复杂交互流程像编写普通函数一样简单。
  • 凭借对 iOS、macOS、watchOS 及 Linux 的全平台兼容特性,同一套代码即可无缝部署到所有苹果生态设备,无需额外适配。

SwiftOpenAI 将繁琐的 API 对接转化为简洁的 Swift 原生体验,让开发者能专注于创造卓越的智能交互功能而非底层通信细节。

运行环境要求

操作系统
  • Linux
  • macOS
  • iOS
  • watchOS
GPU

未说明

内存

未说明

依赖
notes这是一个 Swift 包,主要用于 Apple 平台 (iOS 15+, macOS 13+, watchOS 9+) 和 Linux。在 Linux 上需要使用 AsyncHTTPClient 来替代 URLSession。需要 OpenAI API 密钥才能运行。不支持 Windows。
python不适用 (基于 Swift)
Swift 5.9+
Xcode 15+
AsyncHTTPClient (Linux)
SwiftOpenAI hero image

快速开始

SwiftOpenAI

repoOpenAI

iOS 15+ macOS 13+ watchOS 9+ Linux MIT license swift-version swiftui-version xcode-version swift-package-manager Buy me a coffee

一个开源的 Swift 包,旨在轻松与 OpenAI 的公共 API 进行交互。

🚀 现在也提供 CLIMCP 版本。

目录

简介

SwiftOpenAI 是一个开源的 Swift 包,简化了与 所有 OpenAI API 端点的交互,现在还新增了对 Azure、AIProxy、Assistant 流式 API 以及用于低延迟双向语音对话的新 Realtime API 的支持。

OpenAI 端点

测试版

获取 API 密钥

⚠️ 重要提示

要与 OpenAI 服务交互,您需要一个 API 密钥。请按照以下步骤获取:

  1. 访问 OpenAI
  2. 注册一个 账户 或者如果您已有账户,请 登录
  3. 前往 API 密钥页面,按照说明生成一个新的 API 密钥。

有关更多信息,请参阅 OpenAI 的 官方文档

⚠️ 请务必按照 OpenAI 的指导 保护好您的 API 密钥:

请记住,您的 API 密钥是机密信息!切勿与他人共享或将其暴露在任何客户端代码中(如浏览器、应用程序)。生产环境中的请求必须通过您的后端服务器进行路由,这样您的 API 密钥就可以从环境变量或密钥管理服务中安全加载。

SwiftOpenAI 内置了对 AIProxy 的支持,AIProxy 是一个用于 AI 应用程序的后端服务,可以满足这一要求。要配置 AIProxy,请参阅 此处 的说明。

安装

Swift Package Manager

  1. 在 Xcode 中打开您的 Swift 项目。
  2. 依次选择 文件 -> 添加软件包依赖项
  3. 在搜索栏中输入 此 URL
  4. 选择您想要安装的版本(见下方注释)。
  5. 点击 添加软件包

注意:Xcode 存在一个小问题,它会将 SPM 软件包的上限默认设置为 2.0.0。而本软件包的版本号已超过该限制,因此不应接受 Xcode 提供的默认值。相反,您应手动输入您希望支持的 发布版本的下限,然后点击输入框外以让 Xcode 自动调整上限。或者,您也可以选择 分支 -> main 来保持最新状态。

兼容性

平台支持

SwiftOpenAI 同时支持 Apple 平台和 Linux。

  • Apple 平台包括 iOS 15+、macOS 13+ 和 watchOS 9+。
  • Linux:在 Linux 上,SwiftOpenAI 使用 AsyncHTTPClient 来绕过 Apple Foundation 框架中 URLSession 的一些 bug,并且可以与 Vapor 服务器框架一起使用。

OpenAI 兼容的服务提供商

SwiftOpenAI 支持多种与 OpenAI 兼容的服务提供商,包括但不限于:

您可以查看 OpenAIServiceFactory,找到方便的初始化方法,以便提供自定义的 URL。

使用方法

要在您的项目中使用 SwiftOpenAI,首先导入该包:

import SwiftOpenAI

然后,使用您的 OpenAI API 密钥初始化服务:

let apiKey = "your_openai_api_key_here"
let service = OpenAIServiceFactory.service(apiKey: apiKey)

如果需要,您还可以选择指定组织名称。

let apiKey = "your_openai_api_key_here"
let organizationID = "your_organization_id"
let service = OpenAIServiceFactory.service(apiKey: apiKey, organizationID: organizationID)

https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1408259-timeoutintervalforrequest

对于推理模型,请确保将 URL 会话配置中的 timeoutIntervalForRequest 设置为更高的值。默认值为 60 秒,这可能不够,因为向推理模型发送请求的处理和响应时间可能会更长。

要进行配置:

let apiKey = "your_openai_api_key_here"
let organizationID = "your_organization_id"
let session = URLSession.shared
session.configuration.timeoutIntervalForRequest = 360 // 例如,360 秒或更长。
let httpClient = URLSessionHTTPClientAdapter(urlSession: session)
let service = OpenAIServiceFactory.service(apiKey: apiKey, organizationID: organizationID, httpClient: httpClient)

这样您就可以开始访问 OpenAI 的所有端点了。

如何获取网络错误的状态码

您可能希望根据 API 返回的错误类型来构建 UI。例如,429 表示您的请求已被限流。APIError 类型有一个 .responseUnsuccessful 情况,包含两个关联值:descriptionstatusCode。以下是一个使用聊天完成 API 的示例:

let service = OpenAIServiceFactory.service(apiKey: apiKey)
let parameters = ChatCompletionParameters(messages: [.init(role: .user, content: .text("hello world"))],
                                          model: .gpt4o)
do {
   let choices = try await service.startChat(parameters: parameters).choices
   // 处理 choices
} catch APIError.responseUnsuccessful(let description, let statusCode) {
   print("网络错误,状态码:\(statusCode),描述:\(description)")
} catch {
   print(error.localizedDescription)
}

音频

音频转录

参数

public struct AudioTranscriptionParameters: Encodable {
   
   /// 文件资产的名称在 OpenAI 的官方文档中未提及;然而,它是构建多部分请求所必需的。
   let fileName: String
   /// 音频文件对象(不是文件名),格式可以是 flac、mp3、mp4、mpeg、mpga、m4a、ogg、wav 或 webm。
   let file: Data
   /// 要使用的模型 ID。目前仅支持 whisper-1。
   let model: String
   /// 输入音频的语言。以 [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) 格式提供输入语言可以提高准确性和降低延迟。
   let language: String?
   /// 可选的文本提示,用于指导模型的风格或延续之前的音频片段。[提示](https://platform.openai.com/docs/guides/speech-to-text/prompting) 应与音频语言一致。
   let prompt: String?
   /// 转录输出的格式,可选 json、text、srt、verbose_json 或 vtt。默认为 json。
   let responseFormat: String?
   /// 采样温度,范围在 0 到 1 之间。较高的值(如 0.8)会使输出更具随机性,而较低的值(如 0.2)则会使输出更加专注和确定性。如果设置为 0,模型将使用 [对数概率](https://en.wikipedia.org/wiki/Log_probability) 自动调整温度,直到达到某些阈值。默认为 0。
   let temperature: Double?
   
   public enum Model {
      case whisperOne 
      case custom(model: String)
   }
   
   public init(
      fileName: String,
      file: Data,
      model: Model = .whisperOne,
      prompt: String? = nil,
      responseFormat: String? = nil,
      temperature: Double? = nil,
      language: String? = nil)
   {
      self.fileName = fileName
      self.file = file
      self.model = model.rawValue
      self.prompt = prompt
      self.responseFormat = responseFormat
      self.temperature = temperature
      self.language = language
   }
}

响应

public struct AudioObject: Decodable {
   
   /// 如果请求使用 `transcriptions` API,则为转录文本;如果请求使用 `translations` 端点,则为翻译文本。
   public let text: String
}

使用方法

let fileName = "narcos.m4a"
let data = Data(contentsOfURL:_) // 从名为 "narcos.m4a" 的文件中读取的数据。
let parameters = AudioTranscriptionParameters(fileName: fileName, file: data) // **重要**:文件名中务必包含文件扩展名。
let audioObject =  try await service.createTranscription(parameters: parameters)

音频翻译

参数

public struct AudioTranslationParameters: Encodable {
   
   /// 文件资源的名称并未在 OpenAI 官方文档中提及;然而,它对于构建多部分请求至关重要。
   let fileName: String
   /// 音频文件对象(不是文件名),需要进行翻译,格式可以是 flac、mp3、mp4、mpeg、mpga、m4a、ogg、wav 或 webm 中的一种。
   let file: Data
   /// 要使用的模型 ID。目前仅支持 whisper-1。
   let model: String
   /// 一个可选的文本,用于指导模型的风格或延续之前的音频片段。该 [提示](https://platform.openai.com/docs/guides/speech-to-text/prompting) 应与音频语言一致。
   let prompt: String?
   /// 转录输出的格式,可选择 json、text、srt、verbose_json 或 vtt 中的一种。默认为 json。
   let responseFormat: String?
   /// 采样温度,范围在 0 到 1 之间。较高的值(如 0.8)会使输出更具随机性,而较低的值(如 0.2)则会使其更加专注和确定性。如果设置为 0,模型将使用 [对数概率](https://en.wikipedia.org/wiki/Log_probability) 自动提高温度,直到达到某些阈值为止。默认为 0。
   let temperature: Double?
   
   public enum Model {
      case whisperOne 
      case custom(model: String)
   }
   
   public init(
      fileName: String,
      file: Data,
      model: Model = .whisperOne,
      prompt: String? = nil,
      responseFormat: String? = nil,
      temperature: Double? = nil)
   {
      self.fileName = fileName
      self.file = file
      self.model = model.rawValue
      self.prompt = prompt
      self.responseFormat = responseFormat
      self.temperature = temperature
   }
}

响应

public struct AudioObject: Decodable {
   
   /// 如果请求使用 `transcriptions` API,则为转录文本;如果请求使用 `translations` 端点,则为翻译文本。
   public let text: String
}

用法

let fileName = "german.m4a"
let data = Data(contentsOfURL:_) // 从名为 "german.m4a" 的文件中获取的数据。
let parameters = AudioTranslationParameters(fileName: fileName, file: data) // **重要提示**:文件名中务必包含文件扩展名。
let audioObject = try await service.createTranslation(parameters: parameters)

音频语音

参数

/// [根据输入文本生成音频。](https://platform.openai.com/docs/api-reference/audio/createSpeech)
public struct AudioSpeechParameters: Encodable {

   /// 可用的 [TTS 模型](https://platform.openai.com/docs/models/tts) 之一:tts-1 或 tts-1-hd
   let model: String
   /// 要生成音频的文本。最大长度为 4096 个字符。
   let input: String
   /// 生成音频时要使用的语音。支持的语音有 alloy、echo、fable、onyx、nova 和 shimmer。语音预览可在 [文本转语音指南](https://platform.openai.com/docs/guides/text-to-speech/voice-options) 中找到。
   let voice: String
   /// 默认为 mp3,音频的输出格式。支持的格式有 mp3、opus、aac 和 flac。
   let responseFormat: String?
   /// 默认为 1,生成音频的速度。可选择 0.25 至 4.0 之间的值。1.0 为默认值。
   let speed: Double?

   public enum TTSModel: String {
      case tts1 = "tts-1"
      case tts1HD = "tts-1-hd"
   }

   public enum Voice: String {
      case alloy
      case echo
      case fable
      case onyx
      case nova
      case shimmer
   }

   public enum ResponseFormat: String {
      case mp3
      case opus
      case aac
      case flac
   }
   
   public init(
      model: TTSModel,
      input: String,
      voice: Voice,
      responseFormat: ResponseFormat? = nil,
      speed: Double? = nil)
   {
       self.model = model.rawValue
       self.input = input
       self.voice = voice.rawValue
       self.responseFormat = responseFormat?.rawValue
       self.speed = speed
   }
}

响应

/// [音频语音](https://platform.openai.com/docs/api-reference/audio/createSpeech) 的响应。
public struct AudioSpeechObject: Decodable {

   /// 音频文件的内容数据。
   public let output: Data
}

用法

let prompt = "你好,你今天过得怎么样?"
let parameters = AudioSpeechParameters(model: .tts1,input: prompt,voice: .shimmer)
let audioObjectData = try await service.createSpeech(parameters: parameters).output
playAudio(from: audioObjectData)

// 播放数据
 private func playAudio(from data: Data) {
       do {
           // 使用数据初始化音频播放器
           audioPlayer = try AVAudioPlayer(data: data)
           audioPlayer?.prepareToPlay()
           audioPlayer?.play()
       } catch {
           // 处理错误
           print("播放音频时出错:\(error.localizedDescription)")
       }
   }

音频实时

实时 API 允许通过 WebSockets 和低延迟音频流与 OpenAI 的模型进行双向语音对话。该 API 支持音频到音频以及文本到音频的交互,并内置语音活动检测、转录和函数调用功能。

平台要求: iOS 15+、macOS 13+、watchOS 9+。需要 AVFoundation(Linux 上不可用)。

所需权限:

  • 在 Info.plist 中添加 NSMicrophoneUsageDescription
  • 在 macOS 上:启用沙盒权限以访问麦克风和允许出站网络连接

参数

/// 用于创建实时会话的配置
public struct OpenAIRealtimeSessionConfiguration: Encodable,Sendable {

/// 输入音频格式。选项:.pcm16、.g711_ulaw、.g711_alaw。默认为 .pcm16
   let inputAudioFormat: AudioFormat?
   /// 使用 Whisper 进行输入音频转录的配置
   let inputAudioTranscription: InputAudioTranscription?
   /// 模型的系统指令。提供了推荐的默认值
   let instructions: String?
   /// 响应输出的最大 token 数量。可以是 .value(Int) 或 .infinite
   let maxResponseOutputTokens: MaxResponseOutputTokens?
   /// 输出模态:[.audio, .text] 或仅 [.text]。默认为 [.audio, .text]
   let modalities: [Modality]?
   /// 输出音频格式。选项:.pcm16、.g711_ulaw、.g711_alaw。默认为 .pcm16
   let outputAudioFormat: AudioFormat?
   /// 音频播放速度。范围:0.25 至 4.0。默认为 1.0
   let speed: Double?
   /// 模型响应的采样温度。范围:0.6 至 1.2。默认为 0.8
   let temperature: Double?
   /// 模型可调用的工具/函数数组
   let tools: [Tool]?
   /// 工具选择模式:.none、.auto、.required 或 .specific(functionName: String)
   let toolChoice: ToolChoice?
   /// 语音活动检测配置。选项:.serverVAD 或 .semanticVAD
   let turnDetection: TurnDetection?
   /// 要使用的语音。选项:“alloy”、“ash”、“ballad”、“coral”、“echo”、“sage”、“shimmer”、“verse”
   let voice: String?

   /// 可用的音频格式
   public enum AudioFormat: String, Encodable, Sendable {
      case pcm16
      case g711_ulaw = "g711-ulaw"
      case g711_alaw = "g711-alaw"
   }

   /// 输出模态
   public enum Modality: String, Encodable, Sendable {
      case audio
      case text
   }

   /// 轮次检测配置
   public struct TurnDetection: Encodable, Sendable {
      /// 基于服务器的 VAD,具有可自定义的时间参数
      public static func serverVAD(
         prefixPaddingMs: Int = 300,
         silenceDurationMs: Int = 500,
         threshold: Double = 0.5
      ) -> TurnDetection

      /// 语义 VAD,带有急切程度设置
      public static func semanticVAD(eagerness: Eagerness = .medium) -> TurnDetection

      public enum Eagerness: String, Encodable, Sendable {
         case low, medium, high
      }
   }
}

响应

/// 从实时 API 接收到的消息
public enum OpenAIRealtimeMessage: Sendable {
   case error(String?)                    // 发生错误
   case sessionCreated                    // 会话成功创建
   case sessionUpdated                    // 配置已更新
   case responseCreated                   // 模型开始生成响应
   case responseAudioDelta(String)        // 音频片段(base64 编码的 PCM16)
   case inputAudioBufferSpeechStarted     // 用户开始说话(VAD 检测到)
   case responseFunctionCallArgumentsDone(name: String, arguments: String, callId: String)
   case responseTranscriptDelta(String)   // 部分 AI 转录文本
   case responseTranscriptDone(String)    // 完整的 AI 转录文本
   case inputAudioBufferTranscript(String)           // 用户音频转录文本
   case inputAudioTranscriptionDelta(String)         // 部分用户转录文本
   case inputAudioTranscriptionCompleted(String)     // 完整的用户转录文本
}

辅助类型

/// 管理实时对话中的麦克风输入和音频播放。
/// 通过 AudioController 播放的音频不会干扰麦克风输入(模型不会听到自己的声音)。
@RealtimeActor
public final class AudioController {

   /// 使用指定模式初始化
   /// - 参数 modes:包含 .record(用于麦克风)和/或 .playback(用于音频输出)的数组
   public init(modes: [Mode]) async throws

   public enum Mode {
      case record   // 启用麦克风流式传输
      case playback // 启用音频播放
   }

   /// 返回麦克风音频缓冲区的 AsyncStream
   /// - 抛出:OpenAIError 如果初始化时未启用 .record 模式
   public func micStream() throws -> AsyncStream<AVAudioPCMBuffer>

   /// 播放来自模型的 base64 编码的 PCM16 音频
   /// - 参数 base64String:Base64 编码的 PCM16 音频数据
   public func playPCM16Audio(base64String: String)

   /// 中断当前音频播放(当用户开始说话时很有用)
   public func interruptPlayback()

   /// 停止所有音频操作
   public func stop()
}

/// 用于将音频缓冲区编码为 base64 的工具
public enum AudioUtils {
   /// 将 AVAudioPCMBuffer 转换为可用于传输的 base64 字符串
   public static func base64EncodeAudioPCMBuffer(from buffer: AVAudioPCMBuffer) -> String?

   /// 检查是否已连接耳机
   public static var headphonesConnected: Bool
}

使用示例

// 1. 创建会话配置
let configuration = OpenAIRealtimeSessionConfiguration(
   voice: "alloy",
   instructions: "你是一位乐于助人的 AI 助手。请简明扼要且友好地回答。",
   turnDetection: .serverVAD(
      prefixPaddingMs: 300,
      silenceDurationMs: 500,
      threshold: 0.5
   ),
   inputAudioTranscription: .init(model: "whisper-1")
)

// 2. 创建实时会话
let session = try await service.realtimeSession(
   model: "gpt-4o-mini-realtime-preview-2024-12-17",
   configuration: configuration
)

// 3. 初始化音频控制器以进行录音和播放
let audioController = try await AudioController(modes: [.record, .playback])

// 4. 处理来自 OpenAI 的传入消息
Task {
   for await message in session.receiver {
      switch message {
      case .responseAudioDelta(let audio):
         // 播放来自模型的音频
         audioController.playPCM16Audio(base64String: audio)

      case .inputAudioBufferSpeechStarted:
         // 用户开始说话——中断模型的音频播放
         audioController.interruptPlayback()

      case .responseTranscriptDelta(let text):
         // 显示部分模型转录文本
         print("模型(部分):\(text)")

      case .responseTranscriptDone(let text):
         // 显示完整的模型转录文本
         print("模型:\(text)")

      case .inputAudioTranscriptionCompleted(let text):
         // 显示用户转录的语音
         print("用户:\(text)")

      case .responseFunctionCallArgumentsDone(let name, let args, let callId):
         // 处理来自模型的函数调用
         print("函数调用:\(name),参数:\(args)")
         // 执行函数并将结果返回

      case .error(let error):
         print("错误:\(error ?? '未知错误')")

      default:
         break
      }
   }
}

// 5. 将麦克风音频流式传输到 OpenAI
Task {
   do {
      for try await buffer in audioController.micStream() {
         // 将音频缓冲区编码为 base64
         guard let base64Audio = AudioUtils.base64EncodeAudioPCMBuffer(from: buffer) else {
            continue
         }

// 向 OpenAI 发送音频
         try await session.sendMessage(
            OpenAIRealtimeInputAudioBufferAppend(audio: base64Audio)
         )
      }
   } catch {
      print("麦克风错误:\(error)")
   }
}

// 6. 手动触发响应(可选——通常由 VAD 处理)
try await session.sendMessage(
   OpenAIRealtimeResponseCreate()
)

// 7. 在对话过程中更新会话配置(可选)
let newConfig = OpenAIRealtimeSessionConfiguration(
   voice: "shimmer",
   temperature: 0.9
)
try await session.sendMessage(
   OpenAIRealtimeSessionUpdate(sessionConfig: newConfig)
)

// 8. 完成后进行清理
audioController.stop()
session.disconnect()

函数调用

// 在配置中定义工具
let tools: [OpenAIRealtimeSessionConfiguration.Tool] = [
   .init(
      name: "get_weather",
      description: "获取某个地点的当前天气",
      parameters: [
         "type": "object",
         "properties": [
            "location": [
               "type": "string",
               "description": "城市名称,例如旧金山"
            ]
         ],
         "required": ["location"]
      ]
   )
]

let config = OpenAIRealtimeSessionConfiguration(
   voice: "alloy",
   tools: tools,
   toolChoice: .auto
)

// 在消息接收器中处理函数调用
case .responseFunctionCallArgumentsDone(let name, let args, let callId):
   if name == "get_weather" {
      // 解析参数并执行函数
      let result = getWeather(arguments: args)

      // 将结果返回给模型
      try await session.sendMessage(
         OpenAIRealtimeConversationItemCreate(
            item: .functionCallOutput(
               callId: callId,
               output: result
            )
         )
      )
   }

高级功能

  • 语音活动检测 (VAD): 可选择基于服务器的 VAD(支持自定义时序)或语义 VAD(支持不同积极性级别)
  • 转录: 为用户输入和模型输出同时启用 Whisper 转录
  • 会话更新: 在不重新连接的情况下,可在对话过程中更改语音、指令或工具
  • 响应触发: 可手动触发模型响应,也可依赖自动 VAD
  • 平台特定行为: 根据平台和耳机连接情况,自动选择最优的音频 API

有关完整实现示例,请参阅仓库中的 Examples/RealtimeExample/RealtimeExample.swift 文件。

聊天

参数

public struct ChatCompletionParameters: Encodable {
   
   /// 由迄今为止对话组成的消息列表。[示例 Python 代码](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models)
   public var messages: [Message]
   /// 要使用的模型 ID。有关哪些模型适用于聊天 API 的详细信息,请参阅 [模型端点兼容性](https://platform.openai.com/docs/models/how-we-use-your-data) 表。
   /// 支持 GPT-4、GPT-4o、GPT-5 等模型。对于 GPT-5 系列:.gpt5、.gpt5Mini、.gpt5Nano
   public var model: String
   /// 是否存储此聊天完成请求的输出,以供我们用于 [模型蒸馏](https://platform.openai.com/docs/guides/distillation) 或 [评估](https://platform.openai.com/docs/guides/evals) 产品。
   /// 默认为 false
   public var store: Bool?
   /// 介于 -2.0 和 2.0 之间的数字。正值会根据当前文本中某个标记的出现频率对其施加惩罚,从而降低模型逐字重复同一句话的可能性。默认为 0
   /// [有关频率和存在惩罚的更多信息。](https://platform.openai.com/docs/guides/gpt/parameter-details)
   public var frequencyPenalty: Double?
   /// 控制模型对函数调用的响应方式。none 表示模型不会调用函数,而是直接回复用户;auto 表示模型可以自行决定是回复用户还是调用函数;通过 {"name": "my_function"} 指定特定函数,则强制模型调用该函数。如果没有函数定义,默认为 none;如果有函数定义,则默认为 auto。
   @available(*, deprecated, message: "已弃用,建议使用 tool_choice。")
   public var functionCall: FunctionCall?
   /// 控制模型将调用哪个(如果有的话)函数。none 表示模型不会调用函数,而是生成一条消息。
   /// auto 表示模型可以自行决定是生成消息还是调用函数。通过 `{"type: "function", "function": {"name": "my_function"}}` 指定特定函数,则强制模型调用该函数。
   /// 如果没有函数定义,默认为 none;如果有函数定义,默认为 auto。
   public var toolChoice: ToolChoice?
   /// 模型可能为其生成 JSON 输入的函数列表。
   @available(*, deprecated, message: "已弃用,建议使用 tools。")
   public var functions: [ChatFunction]?
   /// 模型可能调用的工具列表。目前仅支持函数作为工具。使用此参数可提供模型可能为其生成 JSON 输入的函数列表。
   public var tools: [Tool]?
   /// 在使用工具时是否启用并行函数调用。默认为 true。
   public var parallelToolCalls: Bool?
   /// 修改指定标记出现在完成结果中的可能性。
   /// 接受一个 JSON 对象,将标记(由分词器中的标记 ID 指定)映射到 -100 至 100 之间的相关偏置值。从数学上讲,该偏置会在采样之前添加到模型生成的 logits 中。具体效果因模型而异,但介于 -1 和 1 之间的值应会降低或增加被选中的可能性;而接近 -100 或 100 的值则可能导致禁止或独占选择相关标记。默认为 null。
   public var logitBias: [Int: Double]?
   /// 是否返回输出标记的对数概率。如果为真,则返回消息内容中每个输出标记的对数概率。此选项目前不适用于 gpt-4-vision-preview 模型。默认为 false。
   public var logprobs: Bool?
   /// 一个介于 0 和 5 之间的整数,指定在每个标记位置返回最有可能的标记数量,并附带相应的对数概率。如果使用此参数,则必须将 logprobs 设置为 true。
   public var topLogprobs: Int?
   /// 聊天完成中最多可生成的 [标记](https://platform.openai.com/tokenizer) 数量。此值可用于控制通过 API 生成文本的 [成本](https://openai.com/api/pricing/)。
   /// 此值现已弃用,建议使用 max_completion_tokens,且与 [o1 系列模型](https://platform.openai.com/docs/guides/reasoning) 不兼容。
   public var maxTokens: Int?
   /// 完成结果中可生成标记数目的上限,包括可见输出标记和 [推理标记](https://platform.openai.com/docs/guides/reasoning)。
   public var maxCompletionTokens: Int?
   /// 为每条输入消息生成多少个聊天完成选项。默认为 1。
   public var n: Int?
   /// 您希望模型为此请求生成的输出类型。大多数模型能够生成文本,这也是默认设置:
   /// ["text"]
   /// gpt-4o-audio-preview 模型也可用于 [生成音频](https://platform.openai.com/docs/guides/audio)。要请求该模型同时生成文本和音频响应,可以使用:
   /// ["text", "audio"]
   public var modalities: [String]?
   /// 音频输出参数。当 modalities 设置为 ["audio"] 时需要提供。[了解更多信息。](https://platform.openai.com/docs/guides/audio)
   public var audio: Audio?
   /// 介于 -2.0 和 2.0 之间的数字。正值会根据标记是否已在当前文本中出现来对其进行惩罚,从而提高模型谈论新话题的可能性。默认为 0
   /// [有关频率和存在惩罚的更多信息。](https://platform.openai.com/docs/guides/gpt/parameter-details)
   public var presencePenalty: Double?
   /// 一个对象,指定模型必须输出的格式。用于启用 JSON 模式。
   /// 设置为 `{ type: "json_object" }` 可启用 `JSON` 模式,确保模型生成的消息是有效的 JSON。
   /// 重要提示:在使用 `JSON` 模式时,您仍需通过对话中的某条消息(例如系统消息)明确指示模型生成 `JSON`。否则,模型可能会持续生成空白内容,直到达到标记限制,这可能耗费大量时间,并表现为“卡住”的请求。此外,如果 `finish_reason="length"`,则消息内容可能是不完整的(即被截断),这表明生成的标记数超过了 `max_tokens`,或者对话上下文长度超出了限制。
   public var responseFormat: ResponseFormat?
   /// 指定处理请求时使用的延迟等级。此参数适用于订阅了 scale tier 服务的客户:
   /// 如果设置为 'auto',系统将使用 scale tier 积分,直至用尽。
   /// 如果设置为 'default',请求将在共享集群中处理。
   /// 设置此参数后,响应体中将包含所使用的 service_tier。
   public var serviceTier: String?
   /// 此功能处于 `Beta` 阶段。如果指定,我们的系统将尽力进行确定性采样,使得使用相同 `seed` 和参数的重复请求应返回相同的结果。
   /// 确定性无法保证,您应参考 `system_fingerprint` 响应参数来监控后端的变化。
   public var seed: Int?
   /// 最多 4 个序列,API 将在此停止生成更多标记。默认为 null。
   public var stop: [String]?
   /// 如果设置,将发送部分消息增量,类似于 ChatGPT。标记会以纯数据形式按 [服务器发送事件](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format) 的方式陆续发送,直到收到 data: [DONE] 消息为止。[示例 Python 代码](https://cookbook.openai.com/examples/how_to_stream_completions )。
   /// 默认为 false。
   var stream: Bool? = nil
   /// 流式响应选项。仅在设置 stream: true 时才应设置此参数。
   var streamOptions: StreamOptions?
   /// 使用的采样温度,范围为 0 到 2。较高的值(如 0.8)会使输出更具随机性,而较低的值(如 0.2)则使其更加专注和确定性更强。
   /// 我们通常建议调整此参数或 `top_p` 参数,但不要同时调整两者。默认为 1。
   public var temperature: Double?
   /// 一种替代温度采样的方法,称为核采样,在这种方法中,模型会考虑具有 top_p 概率质量的标记结果。因此,0.1 表示仅考虑构成前 10% 概率质量的标记。
   /// 我们通常建议调整此参数或 `temperature` 参数,但不要同时调整两者。默认为 1。
   public var topP: Double?
   /// 代表您的最终用户的唯一标识符,有助于 OpenAI 监控和检测滥用行为。
   /// [了解更多信息](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids)。
   public var user: String?
   
   public struct Message: Encodable {
      
      /// 消息作者的角色。可以是 system、user、assistant 或 tool message。
      let role: String
      /// 消息的内容。所有消息都需要 content,但对于带有函数调用的 assistant 消息,content 可以为空。
      let content: ContentType
      /// 此消息作者的名称。如果角色是 function,则必须填写 name,且应为内容中响应的函数名称。名称可以包含 a-z、A-Z、0-9 和下划线,最大长度为 64 个字符。
      let name: String?
      /// 模型生成的要调用的函数名称和参数。
      @available(*, deprecated, message: "已弃用,由 `tool_calls` 取代")
      let functionCall: FunctionCall?
      /// 模型生成的工具调用,例如函数调用。
      let toolCalls: [ToolCall]?
      /// 本消息所回应的工具调用 ID。
      let toolCallID: String?
      
      public enum ContentType: Encodable {
         
         case text(String)
         case contentArray([MessageContent])
         
         public func encode(to encoder: Encoder) throws {
            var container = encoder.singleValueContainer()
            switch self {
            case .text(let text):
               try container.encode(text)
            case .contentArray(let contentArray):
               try container.encode(contentArray)
            }
         }
         
         public enum MessageContent: Encodable、Equatable 和 Hashable {
            
            case text(String)
            case imageUrl(ImageDetail)
            
            public struct ImageDetail: Encodable、Equatable 和 Hashable {
               
               public let url: URL
               public let detail: String?
               
               enum CodingKeys: String、CodingKey {
                  case url
                  case detail
               }
               
               public func encode(to encoder: Encoder) throws {
                  var container = encoder.container(keyedBy: CodingKeys.self)
                  try container.encode(url, forKey: .url)
                  try container.encode(detail, forKey: .detail)
               }
               
               public init(url: URL、detail: String? = nil) {
                  self.url = url
                  self.detail = detail
               }
            }
            
            enum CodingKeys: String、CodingKey {
               case type
               case text
               case imageUrl = "image_url"
            }
            
            public func encode(to encoder: Encoder) throws {
               var container = encoder.container(keyedBy: CodingKeys.self)
              ...

enum 编码键: String, 编码键 {
          case 包含使用量 = "include_usage"
      }
   }
   
   /// 音频输出参数。当调用模态为 ["audio"] 时,此参数为必填。
   /// [了解更多。](https://platform.openai.com/docs/guides/audio)
   public struct 音频: 可编码 {
      /// 指定语音类型。支持的语音包括 alloy、echo、fable、onyx、nova 和 shimmer。
      public let 语音: String
      /// 指定输出音频格式。必须是 wav、mp3、flac、opus 或 pcm16 中的一种。
      public let 格式: String
      
      public init(
         语音: String,
         格式: String)
      {
         self.语音 = 语音
         self.格式 = 格式
      }
   }

   enum 编码键: String, 编码键 {
      case 消息
      case 模型
      case 存储
      case 频率惩罚 = "frequency_penalty"
      case 工具选择 = "tool_choice"
      case 函数调用 = "function_call"
      case 工具
      case 并行工具调用 = "parallel_tool_calls"
      case 函数
      case 对数偏置 = "logit_bias"
      case 日志概率
      case 最高日志概率 = "top_logprobs"
      case 最大标记数 = "max_tokens"
      case 最大完成标记数 = "max_completion_tokens"
      case 数量
      case 模态
      case 音频
      case 响应格式 = "response_format"
      case 现场惩罚 = "presence_penalty"
      case 种子
      case 服务等级 = "service_tier"
      case 停止
      case 流式
      case 流式选项 = "stream_options"
      case 温度
      case 最高概率 = "top_p"
      case 用户
   }
   
   public init(
      消息: [消息],
      模型: 模型,
      存储: Bool? = nil,
      频率惩罚: Double? = nil,
      函数调用: 函数调用? = nil,
      工具选择: 工具选择? = nil,
      函数: [聊天函数]? = nil,
      工具: [工具]? = nil,
      并行工具调用: Bool? = nil,
      对数偏置: [Int: Double]? = nil,
      日志概率: Bool? = nil,
      最高日志概率: Int? = nil,
      最大标记数: Int? = nil,
      数量: Int? = nil,
      模态: [String]? = nil,
      音频: 音频? = nil,
      响应格式: 响应格式? = nil,
      现场惩罚: Double? = nil,
      服务等级: 服务等级? = nil,
      种子: Int? = nil,
      停止: [String]? = nil,
      温度: Double? = nil,
      最高概率: Double? = nil,
      用户: String? = nil)
   {
      self.messages = messages
      self.model = model.value
      self.store = store
      self.frequencyPenalty = frequencyPenalty
      self.functionCall = functionCall
      self.toolChoice = toolChoice
      self.functions = functions
      self.tools = tools
      self.parallelToolCalls = parallelToolCalls
      self.logitBias = logitBias
      self.logprobs = logProbs
      self.topLogprobs = topLogprobs
      self.maxTokens = maxTokens
      self.n = n
      self.modalities = modalities
      self.audio = audio
      self.responseFormat = responseFormat
      self.presencePenalty = presencePenalty
      self.serviceTier = serviceTier?.rawValue
      self.seed = seed
      self.stop = stop
      self.temperature = temperature
      self.topP = topProbability
      self.user = user
   }
}

聊天完成对象

/// 表示根据提供的输入由模型返回的聊天[完成](https://platform.openai.com/docs/api-reference/chat/object)响应。
public struct ChatCompletionObject: Decodable {
   
   /// 聊天完成的唯一标识符。
   public let id: String
   /// 聊天完成选项列表。如果 n 大于 1,则可以有多个选项。
   public let choices: [ChatChoice]
   /// 聊天完成创建时的 Unix 时间戳(以秒为单位)。
   public let created: Int
   /// 用于聊天完成的模型。
   public let model: String
   /// 用于处理请求的服务层级。仅当请求中指定了 service_tier 参数时,才会包含此字段。
   public let serviceTier: String?
   /// 此指纹代表模型运行所依据的后端配置。
   /// 可与 seed 请求参数结合使用,以了解何时进行了可能影响确定性的后端更改。
   public let systemFingerprint: String?
   /// 对象类型,始终为 chat.completion。
   public let object: String
   /// 完成请求的使用统计信息。
   public let usage: ChatUsage
   
   public struct ChatChoice: Decodable {
      
      /// 模型停止生成标记的原因。如果是自然停止点或提供的停止序列,则为 stop;如果达到请求中指定的最大标记数,则为 length;如果由于内容过滤器的标记而省略了内容,则为 content_filter;如果模型调用了工具,则为 tool_calls;如果模型调用了函数,则为 function_call(已弃用)。
      public let finishReason: IntOrStringValue?
      /// 选项在选项列表中的索引。
      public let index: Int
      /// 模型生成的聊天完成消息。
      public let message: ChatMessage   
      /// 该选项的日志概率信息。
      public let logprobs: LogProb?
      
      public struct ChatMessage: Decodable {
         
         /// 消息的内容。
         public let content: String?
         /// 模型生成的工具调用,例如函数调用。
         public let toolCalls: [ToolCall]?
         /// 模型生成的应被调用的函数名称和参数。
         @available(*, deprecated, message: "已弃用并由 `tool_calls` 替代")
         public let functionCall: FunctionCall?
         /// 此消息作者的角色。
         public let role: String
         /// 由 Vision API 提供。
         public let finishDetails: FinishDetails?
         /// 模型生成的拒绝消息。
         public let refusal: String?
         /// 如果请求了音频输出模态,此对象包含来自模型的音频响应数据。[了解更多](https://platform.openai.com/docs/guides/audio)。
         public let audio: Audio?
         
         /// 由 Vision API 提供。
         public struct FinishDetails: Decodable {
            let type: String
         }
         
         public struct Audio: Decodable {
            /// 此音频响应的唯一标识符。
            public let id: String
            /// 此音频响应将在服务器上不再可用以用于多轮对话的 Unix 时间戳(以秒为单位)。
            public let expiresAt: Int
            /// 模型生成的 Base64 编码音频字节,格式按请求指定。
            public let data: String
            /// 模型生成的音频转录文本。
            public let transcript: String
            
            enum CodingKeys: String, CodingKey {
               case id
               case expiresAt = "expires_at"
               case data
               case transcript
            }
         }
      }
      
      public struct LogProb: Decodable {
         /// 包含日志概率信息的消息内容标记列表。
         let content: [TokenDetail]
      }
      
      public struct TokenDetail: Decodable {
         /// 标记。
         let token: String
         /// 该标记的日志概率。
         let logprob: Double
         /// 表示标记 UTF-8 字节表示的整数列表。在字符由多个标记表示且必须组合其字节表示才能生成正确文本表示的情况下很有用。如果标记没有字节表示,则可能为 null。
         let bytes: [Int]?
         /// 在该标记位置最有可能出现的标记及其日志概率列表。在极少数情况下,返回的 top_logprobs 数量可能会少于请求的数量。
         let topLogprobs: [TopLogProb]
         
         enum CodingKeys: String, CodingKey {
            case token, logprob, bytes
            case topLogprobs = "top_logprobs"
         }
         
         struct TopLogProb: Decodable {
            /// 标记。
            let token: String
            /// 该标记的日志概率。
            let logprob: Double
            /// 表示标记 UTF-8 字节表示的整数列表。在字符由多个标记表示且必须组合其字节表示才能生成正确文本表示的情况下很有用。如果标记没有字节表示,则可能为 null。
            let bytes: [Int]?
         }
      }
   }
   
   public struct ChatUsage: Decodable {
      
      /// 生成的完成内容中的标记数量。
      public let completionTokens: Int
      /// 提示中的标记数量。
      public let promptTokens: Int
      /// 请求中使用的总标记数量(提示 + 完成)。
      public let totalTokens: Int
   }
}

使用方法

let prompt = "给我讲个笑话"
let parameters = ChatCompletionParameters(messages: [.init(role: .user, content: .text(prompt))], model: .gpt4o)
let chatCompletionObject = service.startChat(parameters: parameters)

响应

聊天完成块对象

/// 表示根据提供的输入,由模型返回的聊天完成响应的[流式](https://platform.openai.com/docs/api-reference/chat/streaming)块。
public struct ChatCompletionChunkObject: Decodable {
   
   /// 聊天完成块的唯一标识符。
   public let id: String
   /// 聊天完成选项列表。如果 n 大于 1,则可以有多个选项。
   public let choices: [ChatChoice]
   /// 聊天完成块创建时的 Unix 时间戳(以秒为单位)。
   public let created: Int
   /// 用于生成完成内容的模型。
   public let model: String
   /// 用于处理请求的服务层级。仅当请求中指定了 service_tier 参数时,才会包含此字段。
   public let serviceTier: String?
   /// 此指纹代表模型运行所依据的后端配置。
   /// 可与 seed 请求参数结合使用,以了解何时发生了可能影响确定性的后端更改。
   public let systemFingerprint: String?
   /// 对象类型,始终为 chat.completion.chunk。
   public let object: String
   
   public struct ChatChoice: Decodable {
      
      /// 由流式模型响应生成的聊天完成增量。
      public let delta: Delta
      /// 模型停止生成标记的原因。如果是模型到达自然停止点或提供的停止序列,则为 stop;如果达到请求中指定的最大标记数,则为 length;如果由于内容过滤器的标记而省略了内容,则为 content_filter;如果模型调用了工具,则为 tool_calls;如果模型调用了函数,则为 function_call(已弃用)。
      public let finishReason: IntOrStringValue?
      /// 选项在选项列表中的索引。
      public let index: Int
      /// 由 Vision API 提供。
      public let finishDetails: FinishDetails?
      
      public struct Delta: Decodable {
         
         /// 块消息的内容。
         public let content: String?
         /// 模型生成的工具调用,例如函数调用。
         public let toolCalls: [ToolCall]?
         /// 模型生成的应被调用的函数名称和参数。
         @available(*, deprecated, message: "已弃用并由 `tool_calls` 替代")
         public let functionCall: FunctionCall?
         /// 此消息作者的角色。
         public let role: String?
      }
      
      public struct LogProb: Decodable {
         /// 包含对数概率信息的消息内容标记列表。
         let content: [TokenDetail]
      }
      
      public struct TokenDetail: Decodable {
         /// 标记。
         let token: String
         /// 该标记的对数概率。
         let logprob: Double
         /// 表示标记 UTF-8 字节表示的整数列表。在字符由多个标记表示且必须将它们的字节表示组合起来才能生成正确文本表示的情况下很有用。如果标记没有字节表示,则可能为 null。
         let bytes: [Int]?
         /// 在该标记位置上最有可能的标记及其对数概率列表。在极少数情况下,返回的 top_logprobs 数量可能会少于请求的数量。
         let topLogprobs: [TopLogProb]
         
         enum CodingKeys: String, CodingKey {
            case token, logprob, bytes
            case topLogprobs = "top_logprobs"
         }
         
         struct TopLogProb: Decodable {
            /// 标记。
            let token: String
            /// 该标记的对数概率。
            let logprob: Double
            /// 表示标记 UTF-8 字节表示的整数列表。在字符由多个标记表示且必须将它们的字节表示组合起来才能生成正确文本表示的情况下很有用。如果标记没有字节表示,则可能为 null。
            let bytes: [Int]?
         }
      }
      
      /// 由 Vision API 提供。
      public struct FinishDetails: Decodable {
         let type: String
      }
   }
}

使用示例

let prompt = "给我讲个笑话"
let parameters = ChatCompletionParameters(messages: [.init(role: .user, content: .text(prompt))], model: .gpt4o)
let chatCompletionObject = try await service.startStreamedChat(parameters: parameters)

函数调用

聊天完成功能还支持函数调用并行函数调用functions 已被弃用,取而代之的是 tools,更多详情请参阅 OpenAI 文档

public struct ToolCall: Codable {

   public let index: Int
   /// 工具调用的 ID。
   public let id: String?
   /// 工具的类型。目前仅支持 `function`。
   public let type: String?
   /// 模型调用的函数。
   public let function: FunctionCall

   public init(
      index: Int,
      id: String,
      type: String = "function",
      function: FunctionCall)
   {
      self.index = index
      self.id = id
      self.type = type
      self.function = function
   }
}

public struct FunctionCall: Codable {

   /// 用于调用函数的参数,由模型以 JSON 格式生成。请注意,模型并不总是生成有效的 JSON,有时可能会“幻觉”出未在您的函数模式中定义的参数。请在调用函数之前,在代码中验证这些参数。
   let arguments: String
   /// 要调用的函数名称。
   let name: String

   public init(
      arguments: String,
      name: String)
   {
      self.arguments = arguments
      self.name = name
   }
}

使用方法

/// 定义一个 `ToolCall`
var tool: ToolCall {
   .init(
      type: "function", // 工具的类型。目前仅支持 "function"。
      function: .init(
         name: "create_image",
         description: "如果请求要求生成一张图片,则调用此函数",
         parameters: .init(
            type: .object,
            properties: [
               "prompt": .init(type: .string, description: "传入的具体提示词"),
               "count": .init(type: .integer, description: "请求的图片数量")
            ],
            required: ["prompt", "count"])))
}

let prompt = "给我看一张独角兽吃冰淇淋的图片"
let content: ChatCompletionParameters.Message.ContentType = .text(prompt)
let parameters = ChatCompletionParameters(messages: [.init(role: .user, content: content)], model: .gpt41106Preview, tools: [tool])
let chatCompletionObject = try await service.startStreamedChat(parameters: parameters)

有关如何在 iOS 中上传 Base64 编码的图片的更多详细信息,请查看本包 Examples 部分中的 ChatFunctionsCalllDemo 示例。

结构化输出

文档:

需要了解的要点:

  • 所有字段必须是必填的:要使用结构化输出,所有字段或函数参数都必须指定为必填。
  • 尽管所有字段必须是必填的(并且模型会为每个参数返回值),但可以通过使用包含空值的联合类型来模拟可选参数。
  • 对象在嵌套深度和大小上有限制:一个模式最多可以有 100 个对象属性,嵌套层次最多为 5 层。
  • additionalProperties):始终需要将对象的 additionalProperties 设置为 false。additionalProperties 控制是否允许对象包含未在 JSON Schema 中定义的额外键/值。 结构化输出只支持生成指定的键/值,因此我们要求开发者将 additionalProperties 设置为 false 才能启用结构化输出。
  • 键的顺序:使用结构化输出时,输出将按照模式中键的顺序产生。
  • 支持递归模式

如何在 SwiftOpenAI 中使用结构化输出

  1. 函数调用:通过工具实现的结构化输出可以通过在函数定义中设置 strict: true 来启用。此功能适用于所有支持工具的模型,包括所有 gpt-4-0613 和 gpt-3.5-turbo-0613 及更高版本的模型。当启用结构化输出时,模型的输出将与提供的工具定义完全一致。

使用以下模式:

{
  "schema": {
    "type": "object",
    "properties": {
      "steps": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "explanation": {
              "type": "string"
            },
            "output": {
              "type": "string"
            }
          },
          "required": ["explanation", "output"],
          "additionalProperties": false
        }
      },
      "final_answer": {
        "type": "string"
      }
    },
    "required": ["steps", "final_answer"],
    "additionalProperties": false
  }
}

您可以像这样使用便捷的 JSONSchema 对象:

// 1: 定义 Step 模式的对象

let stepSchema = JSONSchema(
   type: .object,
   properties: [
      "explanation": JSONSchema(type: .string),
      "output": JSONSchema(
         type: .string)
   ],
   required: ["explanation", "output"],
   additionalProperties: false
)

// 2. 定义 steps 数组模式。

let stepsArraySchema = JSONSchema(type: .array, items: stepSchema)

// 3. 定义 final Answer 模式。

let finalAnswerSchema = JSONSchema(type: .string)

// 4. 定义数学回答 JSON 模式。

let mathResponseSchema = JSONSchema(
      type: .object,
      properties: [
         "steps": stepsArraySchema,
         "final_answer": finalAnswerSchema
      ],
      required: ["steps", "final_answer"],
      additionalProperties: false
)

let tool = ChatCompletionParameters.Tool(
            function: .init(
               name: "math_response",
               strict: true,
               parameters: mathResponseSchema))
)

let prompt = "解方程 8x + 31 = 2"
let systemMessage = ChatCompletionParameters.Message(role: .system, content: .text("你是一位数学辅导老师"))
let userMessage = ChatCompletionParameters.Message(role: .user, content: .text(prompt))
let parameters = ChatCompletionParameters(
   messages: [systemMessage, userMessage],
   model: .gpt4o20240806,
   tools: [tool])

let chat = try await service.startChat(parameters: parameters)
  1. response_format 参数的新选项:开发者现在可以通过 json_schema 提供 JSON Schema,这是 response_format 参数的一个新选项。当模型不调用工具,而是以结构化方式响应用户时,此功能非常有用。该特性适用于我们最新的 GPT-4o 模型:今天发布的 gpt-4o-2024-08-06gpt-4o-mini-2024-07-18。当为 response_format 提供 strict: true 时,模型的输出将与提供的模式匹配。

使用之前的模式,您可以通过便捷的 JSONSchemaResponseFormat 对象将其实现为 JSON 模式:

// 1: 定义 Step 模式对象

let stepSchema = JSONSchema(
   type: .object,
   properties: [
      "explanation": JSONSchema(type: .string),
      "output": JSONSchema(
         type: .string)
   ],
   required: ["explanation", "output"],
   additionalProperties: false
)

// 2. 定义 steps 数组模式。

let stepsArraySchema = JSONSchema(type: .array, items: stepSchema)

// 3. 定义最终答案模式。

let finalAnswerSchema = JSONSchema(type: .string)

// 4. 定义响应格式 JSON 模式。

let responseFormatSchema = JSONSchemaResponseFormat(
   name: "math_response",
   strict: true,
   schema: JSONSchema(
      type: .object,
      properties: [
         "steps": stepsArraySchema,
         "final_answer": finalAnswerSchema
      ],
      required: ["steps", "final_answer"],
      additionalProperties: false
   )
)

let prompt = "解方程 8x + 31 = 2"
let systemMessage = ChatCompletionParameters.Message(role: .system, content: .text("你是一位数学辅导老师"))
let userMessage = ChatCompletionParameters.Message(role: .user, content: .text(prompt))
let parameters = ChatCompletionParameters(
   messages: [systemMessage, userMessage],
   model: .gpt4o20240806,
   responseFormat: .jsonSchema(responseFormatSchema))

SwiftOpenAI 结构化输出支持:

  • 工具结构化输出。
  • 响应格式结构化输出。
  • 递归模式。
  • 可选值模式。
  • Pydantic 模型。

我们目前不支持 Pydantic 模型,用户需要手动使用 JSONSchemaJSONSchemaResponseFormat 对象创建模式。

实用小贴士 🔥 使用 iosAICodeAssistant GPT 来构建 SwiftOpenAI 模式。只需粘贴您的 JSON 模式,并让 GPT 为您创建用于工具和响应格式的 SwiftOpenAI 模式。

更多详情请访问示例项目中的工具响应格式部分。

视觉

Vision API 现已可用;开发者必须通过聊天完成 API 访问它,特别是使用 gpt-4-vision-preview 模型或 gpt-4o 模型。使用其他任何模型都无法提供图像描述。

用法

let imageURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
let prompt = "这是什么?"
let messageContent: [ChatCompletionParameters.Message.ContentType.MessageContent] = [.text(prompt), .imageUrl(.init(url: imageURL)] // 用户可以向服务添加任意数量的 `.imageUrl` 实例。
let parameters = ChatCompletionParameters(messages: [.init(role: .user, content: .contentArray(messageContent))], model: .gpt4o)
let chatCompletionObject = try await service.startStreamedChat(parameters: parameters)

Simulator Screen Recording - iPhone 15 - 2023-11-09 at 17 12 06

有关如何在 iOS 中上传 Base64 编码图像的更多详细信息,请查看本包 Examples 部分中的 ChatVision 示例。

响应

OpenAI 最先进的生成模型响应的接口。支持文本和图像输入,以及文本输出。利用先前响应的输出作为输入,与模型建立有状态的交互。通过内置的文件搜索、网络搜索、计算机使用等工具扩展模型的能力。允许模型通过函数调用访问外部系统和数据。

  • 全面的流式支持,通过 responseCreateStream 方法
  • 全面的 ResponseStreamEvent 枚举,涵盖 40 多种事件类型
  • 增强的 InputMessage 包含用于跟踪响应 ID 的 id 字段
  • 改进的对话状态管理,通过 previousResponseId
  • 实时文本流、函数调用和工具使用事件
  • 支持推理摘要、网络搜索、文件搜索和图像生成事件
  • 新增:支持 GPT-5 模型(gpt-5、gpt-5-mini、gpt-5-nano)
  • 新增:verbosity 参数用于控制响应的详细程度

ModelResponseParameter

ModelResponseParameter 提供了一个全面的接口来创建模型响应:

let parameters = ModelResponseParameter(
    input: .text("生命、宇宙以及一切的终极答案是什么?"),
    model: .gpt5,  // 支持 GPT-5、GPT-5-mini、GPT-5-nano
    text: TextConfiguration(
        format: .text,
        verbosity: "low"  // 新增:控制响应的详细程度(“低”、“中”、“高”)
    ),
    temperature: 0.7
)

let response = try await service.responseCreate(parameters)

可用的 GPT-5 模型

public enum Model {
    case gpt5        // 复杂推理、广泛的世界知识,以及代码密集型或多步骤代理任务
    case gpt5Mini    // 成本优化的推理和聊天;平衡速度、成本和能力
    case gpt5Nano    // 高吞吐量任务,尤其是简单的指令遵循或分类任务
    // ... 其他模型
}

带有 verbosity 的 TextConfiguration

// 创建带有 verbosity 控制的文本配置
let textConfig = TextConfiguration(
    format: .text,       // 可以是 .text、.jsonObject 或 .jsonSchema
    verbosity: "medium"  // 控制响应的详细程度
)

相关指南:

参数

/// [创建模型响应。](https://platform.openai.com/docs/api-reference/responses/create)
public struct ModelResponseParameter: Codable {

   /// 模型的文本、图像或文件输入,用于生成响应。
   /// 作为用户角色的文本输入。
   /// 包含不同类型内容的一个或多个输入项列表。
   public var input: InputType

   /// 用于生成响应的模型ID,例如gpt-4o或o1。OpenAI提供了多种具有不同能力、性能特征和价格定位的模型。
   /// 请参阅模型指南以浏览和比较可用模型。
   public var model: String

   /// 指定要包含在模型响应中的额外输出数据。当前支持的值包括:
   /// file_search_call.results:包含文件搜索工具调用的搜索结果。
   /// message.input_image.image_url:包含来自输入消息的图像URL。
   /// computer_call_output.output.image_url:包含来自计算机调用输出的图像URL。
   public var include: [String]?

   /// 将系统(或开发者)消息作为模型上下文中的第一条消息插入。
   /// 当与previous_response_id一起使用时,先前响应中的指令将不会传递到下一次响应中。这使得在新响应中轻松替换系统(或开发者)消息成为可能。
   public var instructions: String?

   /// 响应中可生成的最大标记数上限,包括可见输出标记和推理标记。
   public var maxOutputTokens: Int?

   /// 可附加到对象的一组16个键值对。这有助于以结构化格式存储有关该对象的附加信息,并通过API或仪表板查询对象。
   /// 键是最大长度为64个字符的字符串。值是最大长度为512个字符的字符串。
   public var metadata: [String: String]?

   /// 是否允许模型并行运行工具调用。
   /// 默认为true
   public var parallelToolCalls: Bool?

   /// 模型之前响应的唯一ID。使用此ID可以创建多轮对话。
   /// 了解有关对话状态的更多信息。
   public var previousResponseId: String?

   /// 仅限o系列模型
   /// 推理模型的配置选项。
   public var reasoning: Reasoning?

   /// 是否将生成的模型响应存储起来,以便稍后通过API检索。
   /// 默认为true
   public var store: Bool?

   /// 如果设置为true,模型响应数据将在生成过程中通过服务器发送事件流式传输到客户端。
   public var stream: Bool?

   /// 使用的采样温度,范围在0到2之间。
   /// 较高的值(如0.8)会使输出更具随机性,而较低的值(如0.2)则会使其更加专注和确定性。
   /// 我们通常建议调整温度或top_p,但不要同时调整两者。
   /// 默认为1
   public var temperature: Double?

   /// 模型文本响应的配置选项。可以是纯文本或结构化的JSON数据。
   public var text: TextConfiguration?

   /// 模型在生成响应时应如何选择使用哪个(或哪些)工具。
   /// 请参阅tools参数,了解如何指定模型可以调用的工具。
   public var toolChoice: ToolChoiceMode?

   /// 模型在生成响应时可能调用的工具数组。可以通过设置tool_choice参数来指定要使用的工具。
   public var tools: [Tool]?

   /// 一种替代温度采样的方法,称为核采样,在这种方法中,模型会考虑具有top_p概率质量的标记结果。
   /// 因此,0.1表示只考虑构成前10%概率质量的标记。
   /// 我们通常建议调整top_p或温度,但不要同时调整两者。
   /// 默认为1
   public var topP: Double?

   /// 模型响应所采用的截断策略。
   /// 默认为禁用
   public var truncation: String?

   /// 代表您最终用户的唯一标识符,有助于OpenAI监控和检测滥用行为。
   public var user: String?
}

响应对象

/// 在检索模型响应时返回的响应对象
public struct ResponseModel: Decodable {

   /// 创建此响应的Unix时间戳(以秒为单位)。
   public let createdAt: Int

   /// 当模型无法生成响应时返回的错误对象。
   public let error: ErrorObject?

   /// 此响应的唯一标识符。
   public let id: String

   /// 关于响应不完整的原因的详细信息。
   public let incompleteDetails: IncompleteDetails?

   /// 将系统(或开发者)消息作为模型上下文中的第一条消息插入。
   public let instructions: String?

   /// 响应中可生成的最大标记数上限,包括可见输出标记和推理标记。
   public let maxOutputTokens: Int?

   /// 可附加到对象的一组16个键值对。
   public let metadata: [String: String]

   /// 用于生成响应的模型ID,例如gpt-4o或o1。
   public let model: String

   /// 此资源的对象类型——始终设置为response。
   public let object: String

   /// 模型生成的一组内容项。
   public let output: [OutputItem]

   /// 是否允许模型并行运行工具调用。
   public let parallelToolCalls: Bool

   /// 模型之前响应的唯一ID。使用此ID可以创建多轮对话。
   public let previousResponseId: String?

   /// 推理模型的配置选项。
   public let reasoning: Reasoning?

   /// 响应生成的状态。可能是已完成、失败、进行中或不完整。
   public let status: String

   /// 使用的采样温度,范围在0到2之间。
   public let temperature: Double?

   /// 模型文本响应的配置选项。
   public let text: TextConfiguration

/// 模型在生成响应时应如何选择使用哪种工具(或哪些工具)。
   public let toolChoice: ToolChoiceMode

   /// 模型在生成响应时可能调用的一组工具。
   public let tools: [Tool]

   /// 一种替代温度采样的方法,称为核采样。
   public let topP: Double?

   /// 模型响应所采用的截断策略。
   public let truncation: String?

   /// 表示令牌使用情况的详细信息。
   public let usage: Usage?

   /// 代表您的最终用户的唯一标识符。
   public let user: String?
   
   /// 一个便捷属性,用于汇总输出数组中 output_text 项的所有文本输出。
   /// 类似于 Python 和 JavaScript SDK 中的 outputText 属性。
   public var outputText: String? 
}

输入类型

// InputType 表示 Response API 的输入
public enum InputType: Codable {
    case string(String)  // 简单的文本输入
    case array([InputItem])  // 复杂对话的输入项数组
}

// InputItem 表示不同类型的输入
public enum InputItem: Codable {
    case message(InputMessage)  // 用户、助手、系统消息
    case functionToolCall(FunctionToolCall)  // 函数调用
    case functionToolCallOutput(FunctionToolCallOutput)  // 函数输出
    // ... 其他输入类型
}

// InputMessage 结构支持响应 ID
public struct InputMessage: Codable {
    public let role: String  // "user"、"assistant"、"system"
    public let content: MessageContent
    public let type: String?  // 始终为 "message"
    public let status: String?  // 助手消息为 "completed"
    public let id: String?  // 助手消息的响应 ID
}

// MessageContent 可以是文本或内容项数组
public enum MessageContent: Codable {
    case text(String)
    case array([ContentItem])  // 用于多模态内容
}

使用示例

简单文本输入

let prompt = "法国的首都是哪里?"
let parameters = ModelResponseParameter(input: .string(prompt), model: .gpt4o)
let response = try await service.responseCreate(parameters)

带推理的文本输入

let prompt = "一只土拨鼠能刨多少木头呢?"
let parameters = ModelResponseParameter(
    input: .string(prompt),
    model: .o3Mini,
    reasoning: Reasoning(effort: "high")
)
let response = try await service.responseCreate(parameters)

图像输入

let textPrompt = "这张图片里有什么?"
let imageUrl = "https://example.com/path/to/image.jpg"
let imageContent = ContentItem.imageUrl(ImageUrlContent(imageUrl: imageUrl))
let textContent = ContentItem.text(TextContent(text: textPrompt))
let message = InputItem(role: "user", content: [textContent, imageContent])
let parameters = ModelResponseParameter(input: .array([message]), model: .gpt4o)
let response = try await service.responseCreate(parameters)

使用工具(网络搜索)

let prompt = "今天有哪些积极的新闻报道?"
let parameters = ModelResponseParameter(
    input: .string(prompt),
    model: .gpt4o,
    tools: [Tool(type: "web_search_preview", function: nil)]
)
let response = try await service.responseCreate(parameters)

使用工具(文件搜索)

let prompt = "这份文档的主要要点是什么?"
let parameters = ModelResponseParameter(
    input: .string(prompt),
    model: .gpt4o,
    tools: [
        Tool(
            type: "file_search",
            function: ChatCompletionParameters.ChatFunction(
                name: "file_search",
                strict: false,
                description: "搜索文件",
                parameters: JSONSchema(
                    type: .object,
                    properties: [
                        "vector_store_ids": JSONSchema(
                            type: .array,
                            items: JSONSchema(type: .string)
                        ),
                        "max_num_results": JSONSchema(type: .integer)
                    ],
                    required: ["vector_store_ids"],
                    additionalProperties: false
                )
            )
        )
    ]
)
let response = try await service.responseCreate(parameters)

函数调用

let prompt = "波士顿今天的天气怎么样?"
let parameters = ModelResponseParameter(
    input: .string(prompt),
    model: .gpt4o,
    tools: [
        Tool(
            type: "function",
            function: ChatCompletionParameters.ChatFunction(
                name: "get_current_weather",
                strict: false,
                description: "获取指定地点的当前天气",
                parameters: JSONSchema(
                    type: .object,
                    properties: [
                        "location": JSONSchema(
                            type: .string,
                            description: "城市和州,例如旧金山,加利福尼亚州"
                        ),
                        "unit": JSONSchema(
                            type: .string,
                            enum: ["摄氏度"、"华氏度"]
                        )
                    ],
                    required: ["location"、"unit"],
                    additionalProperties: false
                )
            )
        )
    ],
    toolChoice: .auto
)
let response = try await service.responseCreate(parameters)

获取响应

let responseId = "resp_abc123"
let response = try await service.responseModel(id: responseId)

流式响应

Response API 支持使用服务器发送事件(SSE)进行流式响应。这使您能够在部分响应生成时立即接收它们,从而实现实时 UI 更新并提升用户体验。

流式事件

// ResponseStreamEvent 枚举表示所有可能的流式事件
public enum ResponseStreamEvent: Decodable {
  case responseCreated(ResponseCreatedEvent)
  case responseInProgress(ResponseInProgressEvent)
  case responseCompleted(ResponseCompletedEvent)
  case responseFailed(ResponseFailedEvent)
  case outputItemAdded(OutputItemAddedEvent)
  case outputTextDelta(OutputTextDeltaEvent)
  case outputTextDone(OutputTextDoneEvent)
  case functionCallArgumentsDelta(FunctionCallArgumentsDeltaEvent)
  case reasoningSummaryTextDelta(ReasoningSummaryTextDeltaEvent)
  case error(ErrorEvent)
  // ... 还有许多其他类型的事件
}

基本流式示例

// 通过设置 stream: true 启用流式传输
let parameters = ModelResponseParameter(
    input: .string("给我讲个故事吧"),
    model: .gpt4o,
    stream: true
)

// 创建流
let stream = try await service.responseCreateStream(parameters)

// 按事件到达顺序处理
for try await event in stream {
    switch event {
    case .outputTextDelta(let delta):
        // 将文本片段追加到您的 UI
        print(delta.delta, terminator: "")
        
    case .responseCompleted(let completed):
        // 响应已完成
        print("\n响应 ID: \(completed.response.id)")
        
    case .error(let error):
        // 处理错误
        print("错误: \(error.message)")
        
    default:
        // 根据需要处理其他事件
        break
    }
}

使用对话状态进行流式处理

// 使用 previousResponseId 保持对话连续性
var previousResponseId: String? = nil
var messages: [(role: String, content: String)] = []

// 第一条消息
let firstParams = ModelResponseParameter(
    input: .string("你好!"),
    model: .gpt4o,
    stream: true
)

let firstStream = try await service.responseCreateStream(firstParams)
var firstResponse = ""

for try await event in firstStream {
    switch event {
    case .outputTextDelta(let delta):
        firstResponse += delta.delta
        
    case .responseCompleted(let completed):
        previousResponseId = completed.response.id
        messages.append((role: "user", content: "你好!"))
        messages.append((role: "assistant", content: firstResponse))
        
    default:
        break
    }
}

// 带有对话上下文的后续消息
var inputArray: [InputItem] = []

// 添加对话历史
for message in messages {
    inputArray.append(.message(InputMessage(
        role: message.role,
        content: .text(message.content)
    )))
}

// 添加新的用户消息
inputArray.append(.message(InputMessage(
    role: "user",
    content: .text("你好吗?")
)))

let followUpParams = ModelResponseParameter(
    input: .array(inputArray),
    model: .gpt4o,
    previousResponseId: previousResponseId,
    stream: true
)

let followUpStream = try await service.responseCreateStream(followUpParams)
// 处理后续流…

使用工具和函数调用的流式处理

let parameters = ModelResponseParameter(
    input: .string("旧金山的天气如何?"),
    model: .gpt4o,
    tools: [
        Tool(
            type: "function",
            function: ChatCompletionParameters.ChatFunction(
                name: "get_weather",
                description: "获取当前天气",
                parameters: JSONSchema(
                    type: .object,
                    properties: [
                        "location": JSONSchema(type: .string)
                    ],
                    required: ["location"]
                )
            )
        )
    ],
    stream: true
)

let stream = try await service.responseCreateStream(parameters)
var functionCallArguments = ""

for try await event in stream {
    switch event {
    case .functionCallArgumentsDelta(let delta):
        // 累积函数调用参数
        functionCallArguments += delta.delta
        
    case .functionCallArgumentsDone(let done):
        // 函数调用已完成
        print("函数: \(done.name)")
        print("参数: \(functionCallArguments)")
        
    case .outputTextDelta(let delta):
        // 普通文本输出
        print(delta.delta, terminator: "")
        
    default:
        break
    }
}

取消流式处理

// 可以使用 Swift 的任务取消机制来取消流
let streamTask = Task {
    let stream = try await service.responseCreateStream(parameters)
    
    for try await event in stream {
        // 检查任务是否被取消
        if Task.isCancelled {
            break
        }
        
        // 处理事件…
    }
}

// 在需要时取消流
streamTask.cancel()

完整的流式处理实现示例

@MainActor
@Observable
class ResponseStreamProvider {
    var messages: [Message] = []
    var isStreaming = false
    var error: String?
    
    private let service: OpenAIService
    private var previousResponseId: String?
    private var streamTask: Task<Void, Never>?
    
    init(service: OpenAIService) {
        self.service = service
    }
    
    func sendMessage(_ text: String) {
        streamTask?.cancel()
        
        // 添加用户消息
        messages.append(Message(role: .user, content: text))
        
        // 开始流式响应
        streamTask = Task {
            await streamResponse(for: text)
        }
    }
    
    private func streamResponse(for userInput: String) async {
        isStreaming = true
        error = nil
        
        // 创建流式消息占位符
        let streamingMessage = Message(role: .assistant, content: "", isStreaming: true)
        messages.append(streamingMessage)
        
        do {
            // 构建对话历史
            var inputArray: [InputItem] = []
            for message in messages.dropLast(2) {
                inputArray.append(.message(InputMessage(
                    role: message.role.rawValue,
                    content: .text(message.content)
                )))
            }
            inputArray.append(.message(InputMessage(
                role: "user",
                content: .text(userInput)
            )))
            
            let parameters = ModelResponseParameter(
                input: .array(inputArray),
                model: .gpt4o,
                previousResponseId: previousResponseId,
                stream: true
            )
            
            let stream = try await service.responseCreateStream(parameters)
            var accumulatedText = ""
            
            for try await event in stream {
                guard !Task.isCancelled else { break }
                
                switch event {
                case .outputTextDelta(let delta):
                    accumulatedText += delta.delta
                    updateStreamingMessage(with: accumulatedText)
                    
                case .responseCompleted(let completed):
                    previousResponseId = completed.response.id
                    finalizeStreamingMessage(with: accumulatedText, responseId: completed.response.id)
                    
                case .error(let errorEvent):
                    throw APIError.requestFailed(description: errorEvent.message)
                    
                default:
                    break
                }
            }
        } catch {
            self.error = error.localizedDescription
            messages.removeLast() // 发生错误时移除流式消息
        }
        
        isStreaming = false
    }
    
    private func updateStreamingMessage(with content: String) {
        if let index = messages.lastIndex(where: { $0.isStreaming }) {
            messages[index].content = content
        }
    }
    
    private func finalizeStreamingMessage(with content: String, responseId: String) {
        if let index = messages.lastIndex(where: { $0.isStreaming }) {
            messages[index].content = content
            messages[index].isStreaming = false
            messages[index].responseId = responseId
        }
    }
}

嵌入

参数

/// [创建](https://platform.openai.com/docs/api-reference/embeddings/create) 一个表示输入文本的嵌入向量。
public struct EmbeddingParameter: Encodable {
   
   /// 要使用的模型ID。您可以使用“列出模型”API查看所有可用的模型,或参阅我们的[模型概述](https://platform.openai.com/docs/models/overview),以了解各模型的说明。
   let model: String
   /// 要嵌入的输入文本,编码为字符串或标记数组。要在单个请求中嵌入多个输入,请传递字符串数组或标记数组的数组。每个输入不得超过该模型的最大输入标记数(text-embedding-ada-002 为 8191 个标记),且不能是空字符串。[如何使用 `tiktoken` 计算标记数](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken)
   let input: String
   
   /// 代表您的最终用户的唯一标识符,有助于 OpenAI 监控和检测滥用行为。[了解更多。](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids)
   let user: String?
   
   public enum Model: String {
      case textEmbeddingAda002 = "text-embedding-ada-002"
   }
   
   public init(
      model: Model = .textEmbeddingAda002,
      input: String,
      user: String? = nil)
   {
      self.model = model.value
      self.input = input
      self.user = user
   }
}

响应

/// [表示由嵌入端点返回的嵌入向量。](https://platform.openai.com/docs/api-reference/embeddings/object)
public struct EmbeddingObject: Decodable {
   
   /// 对象类型,始终为“embedding”。
   public let object: String
   /// 嵌入向量,是一个浮点数列表。向量的长度取决于模型,具体请参阅嵌入指南。[https://platform.openai.com/docs/guides/embeddings]
   public let embedding: [Float]
   /// 嵌入在嵌入列表中的索引。
   public let index: Int
}

使用方法

let prompt = "Hello world."
let embeddingObjects = try await service.createEmbeddings(parameters: parameters).data

微调

参数

/// [创建作业](https://platform.openai.com/docs/api-reference/fine-tuning/create) 以从给定数据集微调指定模型。
/// 响应包括已加入队列的作业详细信息,包括作业状态以及完成后的微调模型名称。
public struct FineTuningJobParameters: Encodable {
   
   /// 要微调的模型名称。您可以选择[支持的模型](https://platform.openai.com/docs/models/overview)之一。
   let model: String
   /// 包含训练数据的已上传文件的 ID。
   /// 有关如何上传文件,请参阅[上传文件](https://platform.openai.com/docs/api-reference/files/upload)。
   /// 您的数据集必须格式化为 JSONL 文件。此外,您必须以 fine-tune 的用途上传文件。
   /// 更多详情请参阅[微调指南](https://platform.openai.com/docs/guides/fine-tuning)。
   let trainingFile: String
   /// 用于微调作业的超参数。
   let hyperparameters: HyperParameters?
   /// 一个最多 18 个字符的字符串,将被添加到您的微调模型名称中。
   /// 例如,后缀为 "custom-model-name" 将生成类似 ft:gpt-3.5-turbo:openai:custom-model-name:7p4lURel 的模型名称。
   /// 默认为 null。
   let suffix: String?
   /// 包含验证数据的已上传文件的 ID。
   /// 如果您提供此文件,则该数据将在微调过程中定期用于生成验证指标。这些指标可以在微调结果文件中查看。训练数据和验证数据不应同时出现在同一文件中。
   /// 您的数据集必须格式化为 JSONL 文件。您必须以 fine-tune 的用途上传文件。
   /// 更多详情请参阅[微调指南](https://platform.openai.com/docs/guides/fine-tuning)。
   let validationFile: String?
   /// 为您的微调作业启用的一组集成。
   let integrations: [Integration]?
   /// 种子控制作业的可重复性。使用相同的种子和作业参数应产生相同的结果,但在极少数情况下可能会有所不同。如果未指定种子,系统将为您生成一个。
   let seed: Int?
   
   /// 目前,以下模型支持[微调](https://platform.openai.com/docs/guides/fine-tuning/what-models-can-be-fine-tuned):
   /// gpt-3.5-turbo-0613(推荐)
   /// babbage-002
   /// davinci-002
   /// OpenAI 预计 gpt-3.5-turbo 是大多数用户在效果和易用性方面最合适的选择,除非您正在迁移旧版微调模型。
   public enum Model: String {
      case gpt35 = "gpt-3.5-turbo-0613" /// 推荐
      case babbage002 = "babbage-002"
      case davinci002 = "davinci-002"
   }
   
   public struct HyperParameters: Encodable {
      /// 训练模型的轮数。一轮指完整遍历一次训练数据集。
      /// 默认为 auto。
      let nEpochs: Int?
      
      public init(
         nEpochs: Int?)
      {
         self.nEpochs = nEpochs
      }
   }
   
   public init(
      model: Model,
      trainingFile: String,
      hyperparameters: HyperParameters? = nil,
      suffix: String? = nil,
      validationFile: String? = nil)
   {
      self.model = model.rawValue
      self.trainingFile = trainingFile
      self.hyperparameters = hyperparameters
      self.suffix = suffix
      self.validationFile = validationFile
   }
}

响应

/// fine_tuning.job 对象表示通过 API 创建的[微调作业](https://platform.openai.com/docs/api-reference/fine-tuning/object)。
public struct FineTuningJobObject: Decodable {
   
   /// 对象标识符,可在 API 端点中引用。
   public let id: String
   /// 微调作业创建时的 Unix 时间戳(以秒为单位)。
   public let createdAt: Int
  /// 对于失败的微调作业,此处将包含有关失败原因的更多信息。
   public let error: OpenAIErrorResponse.Error?
   /// 正在创建的微调模型名称。如果微调作业仍在运行,则该值为 null。
   public let fineTunedModel: String?
   /// 微调作业完成时的 Unix 时间戳(以秒为单位)。如果微调作业仍在运行,则该值为 null。
   public let finishedAt: Int?
   /// 用于微调作业的超参数。更多详情请参阅[微调指南](https://platform.openai.com/docs/guides/fine-tuning)。
   public let hyperparameters: HyperParameters
   /// 正在微调的基础模型。
   public let model: String
   /// 对象类型,始终为 "fine_tuning.job"。
   public let object: String
   /// 拥有微调作业的组织。
   public let organizationId: String
   /// 微调作业的编译结果文件 ID。您可以通过[Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents)检索结果。
   public let resultFiles: [String]
   /// 微调作业的当前状态,可能为 `validating_files`、`queued`、`running`、`succeeded`、`failed` 或 `cancelled`。
   public let status: String
   /// 此微调作业处理的可计费总 token 数。如果微调作业仍在运行,则该值为 null。
   public let trainedTokens: Int?
   
   /// 用于训练的文件 ID。您可以通过[Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents)检索训练数据。
   public let trainingFile: String
   /// 用于验证的文件 ID。您可以通过[Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents)检索验证结果。
   public let validationFile: String?
   
   public enum Status: String {
      case validatingFiles = "validating_files"
      case queued
      case running
      case succeeded
      case failed
      case cancelled
   }
   
   public struct HyperParameters: Decodable {
      /// 训练模型的轮数。一轮指完整遍历一次训练数据集。"auto" 会根据数据集大小决定最佳轮数。如果手动设置轮数,我们支持 1 到 50 轮之间的任何数值。
      public let nEpochs: IntOrStringValue
   }
}

用法 列出微调任务

let fineTuningJobs = try await service.istFineTuningJobs()

创建微调任务

let trainingFileID = "file-Atc9okK0MOuQwQzDJCZXnrh6" // 使用 `Files` API 上传的文件 ID。https://platform.openai.com/docs/api-reference/fine-tuning/create#fine-tuning/create-training_file
let parameters = FineTuningJobParameters(model: .gpt35, trainingFile: trainingFileID)
let fineTuningJob = try await service.createFineTuningJob(parameters: parameters)

获取微调任务

let fineTuningJobID = "ftjob-abc123"
let fineTuningJob = try await service.retrieveFineTuningJob(id: fineTuningJobID)

取消微调任务

let fineTuningJobID = "ftjob-abc123"
let canceledFineTuningJob = try await service.cancelFineTuningJobWith(id: fineTuningJobID)

微调任务事件对象

响应

/// [微调任务事件对象](https://platform.openai.com/docs/api-reference/fine-tuning/event-object)
public struct FineTuningJobEventObject: Decodable {
   
   public let id: String
   
   public let createdAt: Int
   
   public let level: String
   
   public let message: String
   
   public let object: String
   
   public let type: String?
   
   public let data: Data?
   
   public struct Data: Decodable {
      public let step: Int
      public let trainLoss: Double
      public let trainMeanTokenAccuracy: Double
   }
}

用法

let fineTuningJobID = "ftjob-abc123"
let jobEvents = try await service.listFineTuningEventsForJobWith(id: id, after: nil, limit: nil).data

批处理

参数

public struct BatchParameter: Encodable {
   
   /// 包含新批处理请求的已上传文件的 ID。
   /// 请参阅 [上传文件](https://platform.openai.com/docs/api-reference/files/create) 了解如何上传文件。
   /// 您的输入文件必须格式化为 [JSONL 文件](https://platform.openai.com/docs/api-reference/batch/requestInput),并且必须以批处理目的上传。
   let inputFileID: String
   /// 批处理中所有请求将使用的端点。目前仅支持 /v1/chat/completions。
   let endpoint: String
   /// 批处理应在其中完成的时间范围。目前仅支持 24 小时。
   let completionWindow: String
   /// 批处理的可选自定义元数据。
   let metadata: [String: String]?
   
   enum CodingKeys: String, CodingKey {
      case inputFileID = "input_file_id"
      case endpoint
      case completionWindow = "completion_window"
      case metadata
   }
}

响应

public struct BatchObject: Decodable {
   
   let id: String
   /// 对象类型,始终为 batch。
   let object: String
   /// 批处理使用的 OpenAI API 端点。
   let endpoint: String
   
   let errors: Error
   /// 批处理输入文件的 ID。
   let inputFileID: String
   /// 批处理应在其中完成的时间范围。
   let completionWindow: String
   /// 批处理的当前状态。
   let status: String
   /// 包含成功执行请求输出的文件 ID。
   let outputFileID: String
   /// 包含出错请求输出的文件 ID。
   let errorFileID: String
   /// 批处理创建时的 Unix 时间戳(以秒为单位)。
   let createdAt: Int
   /// 批处理开始处理时的 Unix 时间戳(以秒为单位)。
   let inProgressAt: Int
   /// 批处理到期时的 Unix 时间戳(以秒为单位)。
   let expiresAt: Int
   /// 批处理开始收尾时的 Unix 时间戳(以秒为单位)。
   let finalizingAt: Int
   /// 批处理完成时的 Unix 时间戳(以秒为单位)。
   let completedAt: Int
   /// 批处理失败时的 Unix 时间戳(以秒为单位)。
   let failedAt: Int
   /// 批处理到期时的 Unix 时间戳(以秒为单位)。
   let expiredAt: Int
   /// 批处理开始取消时的 Unix 时间戳(以秒为单位)。
   let cancellingAt: Int
   /// 批处理被取消时的 Unix 时间戳(以秒为单位)。
   let cancelledAt: Int
   /// 批处理中不同状态的请求数量。
   let requestCounts: RequestCount
   /// 可附加到对象上的 16 组键值对。这有助于以结构化方式存储有关对象的额外信息。键的最大长度为 64 个字符,值的最大长度为 512 个字符。
   let metadata: [String: String]
   
   public struct Error: Decodable {
      
      let object: String
      let data: [Data]

      public struct Data: Decodable {
         
         /// 用于识别错误类型的错误代码。
         let code: String
         /// 提供更多关于错误细节的人类可读消息。
         let message: String
         /// 如果适用,导致错误的参数名称。
         let param: String?
         /// 如果适用,错误发生在输入文件中的行号。
         let line: Int?
      }
   }
   
   public struct RequestCount: Decodable {
      
      /// 批处理中的总请求数。
      let total: Int
      /// 已成功完成的请求数。
      let completed: Int
      /// 失败的请求数。
      let failed: Int
   }
}

用法

创建批处理

let inputFileID = "file-abc123"
let endpoint = "/v1/chat/completions"
let completionWindow = "24h"
let parameter = BatchParameter(inputFileID: inputFileID, endpoint: endpoint, completionWindow: completionWindow, metadata: nil)
let batch = try await service.createBatch(parameters: parameters)

获取批处理

let batchID = "batch_abc123"
let batch = try await service.retrieveBatch(id: batchID)

取消批处理

let batchID = "batch_abc123"
let batch = try await service.cancelBatch(id: batchID)

列出批处理

let batches = try await service.listBatch(after: nil, limit: nil)

文件

参数

/// [上传文件](https://platform.openai.com/docs/api-reference/files/create) 可用于各种端点/功能。目前,单个组织上传的所有文件大小总和最多为 1 GB。如需提高存储限制,请联系我们。
public struct FileParameters: Encodable {
   
   /// 文件资产的名称未在 OpenAI 官方文档中记录;然而,它对于构建多部分请求至关重要。
   let fileName: String
   /// 要上传的文件对象(而非文件名)。
   /// 如果 purpose 设置为 "fine-tune",该文件将用于微调。
   let file: Data
   /// 上传文件的预期用途。
   /// 对于[微调](https://platform.openai.com/docs/api-reference/fine-tuning),请使用 "fine-tune"。这使我们能够验证上传文件的格式是否适用于微调。
   let purpose: String
   
   public init(
      fileName: String,
      file: Data,
      purpose: String)
   {
      self.fileName = fileName
      self.file = file
      self.purpose = purpose
   }
}

响应

/// [文件对象](https://platform.openai.com/docs/api-reference/files/object) 表示已上传到 OpenAI 的文档。
public struct FileObject: Decodable {
   
   /// 文件标识符,可在 API 端点中引用。
   public let id: String
   /// 文件大小,以字节为单位。
   public let bytes: Int
   /// 文件创建时的 Unix 时间戳(以秒为单位)。
   public let createdAt: Int
   /// 文件名。
   public let filename: String
   /// 对象类型,始终为 "file"。
   public let object: String
   /// 文件的预期用途。目前仅支持 "fine-tune"。
   public let purpose: String
   /// 文件的当前状态,可能为 uploaded、processed、pending、error、deleting 或 deleted。
   public let status: String
   /// 关于文件状态的附加信息。如果文件处于 error 状态,此处将包含描述错误的消息。
   public let statusDetails: String?
   
   public enum Status: String {
      case uploaded
      case processed
      case pending
      case error
      case deleting
      case deleted
   }

   public init(
      id: String,
      bytes: Int,
      createdAt: Int,
      filename: String,
      object: String,
      purpose: String,
      status: Status,
      statusDetails: String?)
   {
      self.id = id
      self.bytes = bytes
      self.createdAt = createdAt
      self.filename = filename
      self.object = object
      self.purpose = purpose
      self.status = status.rawValue
      self.statusDetails = statusDetails
   }
}

用法 列出文件

let files = try await service.listFiles().data

上传文件

let fileName = "worldCupData.jsonl"
let data = Data(contentsOfURL:_) // 从名为 "worldCupData.jsonl" 的文件中获取的数据。
let parameters = FileParameters(fileName: "WorldCupData", file: data, purpose: "fine-tune") // 重要提示:务必提供文件名。
let uploadedFile =  try await service.uploadFile(parameters: parameters) 

删除文件

let fileID = "file-abc123"
let deletedStatus = try await service.deleteFileWith(id: fileID)

获取文件

let fileID = "file-abc123"
let retrievedFile = try await service.retrieveFileWith(id: fileID)

获取文件内容

let fileID = "file-abc123"
let fileContent = try await service.retrieveContentForFileWith(id: fileID)

图像

本库支持最新的 OpenAI 图像生成功能。

  • 参数 创建
/// '创建图像':
/// https://platform.openai.com/docs/api-reference/images/create
public struct 创建图像参数: Encodable {
   
   /// 对所需图像的文本描述。
   /// `gpt-image-1` 的最大长度为 32000 字符,`dall-e-2` 为 1000 字符,`dall-e-3` 为 4000 字符。
   public let prompt: String
   
   // MARK: - 可选属性
   
   /// 允许设置生成图像背景的透明度。
   /// 此参数仅适用于 `gpt-image-1`。
   /// 必须是 `transparent`、`opaque` 或 `auto`(默认值)之一。
   /// 当使用 `auto` 时,模型会自动确定最适合该图像的背景。
   /// 如果选择 `transparent`,输出格式需要支持透明度,因此应设置为 `png`(默认值)或 `webp`。
   public let background: 背景?
   
   /// 用于图像生成的模型。可以是 `dall-e-2`、`dall-e-3` 或 `gpt-image-1`。
   /// 默认为 `dall-e-2`,除非使用了 `gpt-image-1` 特有的参数。
   public let model: 模型?
   
   /// 控制由 `gpt-image-1` 生成图像的内容审核级别。
   /// 必须是 `low`(过滤较宽松)或 `auto`(默认值)。
   public let moderation: 审核?
   
   /// 要生成的图像数量。必须介于 1 到 10 之间。对于 `dall-e-3`,仅支持 `n=1`。
   /// 默认值为 `1`
   public let n: Int?
   
   /// 生成图像的压缩级别(0-100%)。
   /// 该参数仅适用于使用 `webp` 或 `jpeg` 输出格式的 `gpt-image-1`,默认值为 100。
   public let outputCompression: Int?
   
   /// 生成图像返回的格式。
   /// 该参数仅适用于 `gpt-image-1`。
   /// 必须是 `png`、`jpeg` 或 `webp` 中的一个。
   public let outputFormat: 格式?
   
   /// 将要生成的图像质量。
   /// - `auto`(默认值)会自动为给定模型选择最佳质量。
   /// - `high`、`medium` 和 `low` 适用于 gpt-image-1。
   /// - `hd` 和 `standard` 适用于 dall-e-3。
   /// - `standard` 是 dall-e-2 唯一的选择。
   public let quality: 质量?
   
   /// 使用 dall-e-2 和 dall-e-3 生成的图像返回的格式。
   /// 必须是 `url` 或 `b64_json` 中的一个。
   /// URL 在图像生成后仅有效 60 分钟。
   /// 此参数不适用于 `gpt-image-1`,后者始终返回 base64 编码的图像。
   public let responseFormat: 格式?
   
   /// 生成图像的尺寸。
   /// - 对于 gpt-image-1,可以选择 `1024x1024`、`1536x1024`(横版)、`1024x1536`(竖版)或 `auto`(默认值)。
   /// - 对于 dall-e-3,可以选择 `1024x1024`、`1792x1024` 或 `1024x1792`。
   /// - 对于 dall-e-2,可以选择 `256x256`、`512x512` 或 `1024x1024`。
   public let size: String?
   
   /// 生成图像的风格。
   /// 该参数仅适用于 `dall-e-3`。
   /// 必须是 `vivid` 或 `natural` 中的一个。
   /// `vivid` 会使模型倾向于生成超现实且戏剧性的图像。
   /// `natural` 会使模型产生更自然、不那么超现实的图像。
   public let style: 风格?
   
   /// 代表您的最终用户的唯一标识符,有助于 OpenAI 监控和检测滥用行为。
   public let user: String?
}
  • 参数编辑
/// 根据一个或多个源图像和提示词,创建编辑或扩展后的图像。
/// 此端点仅支持 `gpt-image-1` 和 `dall-e-2`。
public struct 创建图像编辑参数: Encodable {
   
   /// 要编辑的图像。
   /// 对于 `gpt-image-1`,每张图像应为小于 25MB 的 `png`、`webp` 或 `jpg` 文件。
   /// 对于 `dall-e-2`,只能提供一张图像,且必须是小于 4MB 的正方形 `png` 文件。
   let image: [Data]
   
   /// 对所需图像的文本描述。
   /// `dall-e-2` 的最大长度为 1000 字符,`gpt-image-1` 为 32000 字符。
   let prompt: String
   
   /// 一张额外的图像,其完全透明的区域指示应如何编辑 `image`。
   /// 如果提供了多张图像,则遮罩将应用于第一张图像。
   /// 必须是有效的 PNG 文件,小于 4MB,并且与 `image` 具有相同的尺寸。
   let mask: Data?
   
   /// 用于图像生成的模型。仅支持 `dall-e-2` 和 `gpt-image-1`。
   /// 默认为 `dall-e-2`,除非使用了 `gpt-image-1` 特有的参数。
   let model: String?
   
   /// 要生成的图像数量。必须介于 1 到 10 之间。
   /// 默认值为 1。
   let n: Int?
   
   /// 将要生成的图像质量。
   /// `high`、`medium` 和 `low` 仅适用于 `gpt-image-1`。
   /// `dall-e-2` 仅支持 `standard` 质量。
   /// 默认值为 `auto`。
   let quality: String?
   
   /// 生成图像返回的格式。
   /// 必须是 `url` 或 `b64_json` 中的一个。
   /// URL 在图像生成后仅有效 60 分钟。
   /// 此参数仅适用于 `dall-e-2`,因为 `gpt-image-1` 始终会返回 base64 编码的图像。
   let responseFormat: String?
   
   /// 生成图像的尺寸。
   /// 对于 `gpt-image-1`,必须是 `1024x1024`、`1536x1024`(横版)、`1024x1536`(竖版)或 `auto`(默认值)之一;
   /// 对于 `dall-e-2`,则必须是 `256x256`、`512x512` 或 `1024x1024` 之一。
   let size: String?
   
   /// 代表您的最终用户的唯一标识符,有助于 OpenAI 监控和检测滥用行为。
   let user: String?
}
  • 参数变体
/// 根据给定图像创建变体。
/// 此端点仅支持 `dall-e-2`。
public struct 创建图像变体参数: Encodable {
   
   /// 用作变体基础的图像。
   /// 必须是有效的 PNG 文件,小于 4MB,且为正方形。
   let image: Data
   
   /// 用于图像生成的模型。目前仅支持 `dall-e-2`。
   /// 默认为 `dall-e-2`。
   let model: String?
   
   /// 要生成的图像数量。必须介于 1 到 10 之间。
   /// 默认值为 1。
   let n: Int?
   
   /// 生成图像返回的格式。
   /// 必须是 `url` 或 `b64_json` 中的一个。
   /// URL 在图像生成后仅有效 60 分钟。
   /// 默认值为 `url`。
   let responseFormat: String?
   
   /// 生成图像的尺寸。
   /// 必须是 `256x256`、`512x512` 或 `1024x1024` 之一。
   /// 默认值为 `1024x1024`。
   let size: String?
   
   /// 代表您的最终用户的唯一标识符,有助于 OpenAI 监控和检测滥用行为。
   let user: String?
}
  • 请求示例
import SwiftOpenAI

let service = OpenAIServiceFactory.service(apiKey: "<YOUR_KEY>")

// ❶ 描述你想要的图像
let prompt = "一幅水彩风格的龙独角兽混合体在雪山之上飞翔"

// ❷ 使用全新的类型构建参数(提交 880a15c)
let params = CreateImageParameters(
    prompt: prompt,
    model:  .gptImage1,      // .dallE3 / .dallE2 也同样有效
    n:      1,               // 1-10  (DALL-E 3 只支持 1)
    quality: .high,          // DALL-E 3 支持 .hd / .standard
    size:   "1024x1024"      // 对于宽屏或长屏图像,可使用 "1792x1024" 或 "1024x1792"
)

do {
    // ❸ 发送请求——返回 `CreateImageResponse`
    let result = try await service.createImages(parameters: params)
    let url    = result.data?.first?.url          // 或者使用 base-64 编码的 `b64Json`
    print("图像 URL:", url ?? "无")
} catch {
    print("生成失败:", error)
}

如需查看示例应用,请前往本仓库中的 Examples/SwiftOpenAIExample 项目。

⚠️ 此库同时保持与先前图像生成功能的兼容性。

为了处理图像尺寸,我们使用了 Dalle 模型。为此定义了一个带有关联值的枚举,以准确表示其尺寸限制。

DALL·E

DALL·E 是一种能够根据自然语言描述生成逼真图像和艺术作品的人工智能系统。目前,DALL·E 3 支持根据提示创建特定尺寸的新图像。而 DALL·E 2 则支持编辑现有图像或基于用户提供的图像生成变体。

DALL·E 3 和 DALL·E 2 均可通过我们的 Images API 使用。您也可以通过 ChatGPT Plus 尝试 DALL·E 3。

模型 描述
dall-e-3 DALL·E 3 新版
最新发布的 DALL·E 模型,于 2023 年 11 月推出。了解更多。
dall-e-2 上一代 DALL·E 模型,于 2022 年 11 月发布。
DALL·E 的第二次迭代,生成的图像更加逼真、准确,且分辨率是原始模型的四倍。

public enum Dalle {

case dalle2(Dalle2ImageSize) case dalle3(Dalle3ImageSize)

public enum Dalle2ImageSize: String { case small = "256x256" case medium = "512x512" case large = "1024x1024" }

public enum Dalle3ImageSize: String { case largeSquare = "1024x1024" case landscape = "1792x1024" case portrait = "1024x1792" }

var model: String { switch self { case .dalle2: return Model.dalle2.rawValue case .dalle3: return Model.dalle3.rawValue } }

var size: String { switch self { case .dalle2(let dalle2ImageSize): return dalle2ImageSize.rawValue case .dalle3(let dalle3ImageSize): return dalle3ImageSize.rawValue } } }

图像创建

参数

public struct ImageCreateParameters: Encodable {
   
   /// 对所需图像的文本描述。对于 dall-e-2,最大长度为1000个字符;对于 dall-e-3,最大长度为4000个字符。
   let prompt: String
   /// 用于生成图像的模型。默认为 dall-e-2。
   let model: String?
   /// 要生成的图像数量。必须介于1到10之间。对于 dall-e-3,仅支持 n=1。
   let n: Int?
   /// 将要生成的图像质量。hd 会生成细节更丰富、整幅图像一致性更高的图像。此参数仅适用于 dall-e-3。默认为 standard。
   let quality: String?
   /// 生成图像的返回格式。必须是 url 或 b64_json 之一。默认为 url。
   let responseFormat: String?
   /// 生成图像的尺寸。对于 dall-e-2,必须是 256x256、512x512 或 1024x1024 之一。对于 dall-e-3 模型,必须是 1024x1024、1792x1024 或 1024x1792 之一。默认为 1024x1024。
   let size: String?
   /// 生成图像的风格。必须是 vivid 或 natural 之一。vivid 会使模型倾向于生成超写实且富有戏剧性的图像。natural 则会使模型生成更自然、不那么超写实的图像。此参数仅适用于 dall-e-3。默认为 vivid。
   let style: String?
   /// 代表您的最终用户的唯一标识符,有助于 OpenAI 监控和检测滥用行为。[了解详情](https://platform.openai.com/docs/guides/safety-best-practices)
   let user: String?
   
   public init(
      prompt: String,
      model: Dalle,
      numberOfImages: Int = 1,
      quality: String? = nil,
      responseFormat: ImageResponseFormat? = nil,
      style: String? = nil,
      user: String? = nil)
   {
   self.prompt = prompt
   self.model = model.model
   self.n = numberOfImages
   self.quality = quality
   self.responseFormat = responseFormat?.rawValue
   self.size = model.size
   self.style = style
   self.user = user
   }   
}

图像编辑

参数

/// [根据原始图像和提示创建编辑或扩展后的图像。](https://platform.openai.com/docs/api-reference/images/createEdit)
public struct ImageEditParameters: Encodable {
   
   /// 要编辑的图像。必须是有效的 PNG 文件,小于 4MB,且为正方形。如果未提供 mask,则图像必须具有透明区域,该透明区域将用作 mask。
   let image: Data
   /// 对所需图像的文本描述。最大长度为1000个字符。
   let prompt: String
   /// 附加图像,其完全透明的区域(例如 alpha 为零的地方)指示应编辑图像的位置。必须是有效的 PNG 文件,小于 4MB,且与 image 具有相同的尺寸。
   let mask: Data?
   /// 用于生成图像的模型。目前仅支持 dall-e-2。默认为 dall-e-2。
   let model: String?
   /// 要生成的图像数量。必须介于1到10之间。默认为1。
   let n: Int?
   /// 生成图像的尺寸。必须是 256x256、512x512 或 1024x1024 之一。默认为 1024x1024。
   let size: String?
   /// 生成图像的返回格式。必须是 url 或 b64_json 之一。默认为 url。
   let responseFormat: String?
   /// 代表您的最终用户的唯一标识符,有助于 OpenAI 监控和检测滥用行为。[了解详情](https://platform.openai.com/docs/guides/safety-best-practices)
   let user: String?
   
   public init(
      image: UIImage,
      model: Dalle? = nil,
      mask: UIImage? = nil,
      prompt: String,
      numberOfImages: Int? = nil,
      responseFormat: ImageResponseFormat? = nil,
      user: String? = nil)
   {
      if (image.pngData() == nil) {
         assertionFailure("无法从图像获取 PNG 数据")
      }
      if let mask, mask.pngData() == nil {
         assertionFailure("无法从 mask 获取 PNG 数据")
      }
      if let model, model.model != Model.dalle2.rawValue {
         assertionFailure("目前仅支持 dall-e-2 [https://platform.openai.com/docs/api-reference/images/createEdit]")
      }
      self.image = image.pngData()!
      self.model = model?.model
      self.mask = mask?.pngData()
      self.prompt = prompt
      self.n = numberOfImages
      self.size = model?.size
      self.responseFormat = responseFormat?.rawValue
      self.user = user
   }
}

图像变体

参数

/// [根据给定图像创建变体。](https://platform.openai.com/docs/api-reference/images/createVariation)
public struct ImageVariationParameters: Encodable {
   
   /// 作为变体基础的图像。必须是有效的 PNG 文件,小于 4MB,且为正方形。
   let image: Data
   /// 用于生成图像的模型。目前仅支持 dall-e-2。默认为 dall-e-2。
   let model: String?
   /// 要生成的图像数量。必须介于1到10之间。默认为1。
   let n: Int?
   /// 生成图像的返回格式。必须是 url 或 b64_json 之一。默认为 url。
   let responseFormat: String?
   /// 生成图像的尺寸。必须是 256x256、512x512 或 1024x1024 之一。默认为 1024x1024。
   let size: String?
   /// 代表您的最终用户的唯一标识符,有助于 OpenAI 监控和检测滥用行为。[了解详情](https://platform.openai.com/docs/guides/safety-best-practices)
   let user: String?
   
   public init(
      image: UIImage,
      model: Dalle? = nil,
      numberOfImages: Int? = nil,
      responseFormat: ImageResponseFormat? = nil,
      user: String? = nil)
   {
      if let model, model.model != Model.dalle2.rawValue {
         assertionFailure("目前仅支持 dall-e-2 [https://platform.openai.com/docs/api-reference/images/createEdit]")
      }
      self.image = image.pngData()!
      self.n = numberOfImages
      self.model = model?.model
      self.size = model?.size
      self.responseFormat = responseFormat?.rawValue
      self.user = user
   }
}

响应

/// [表示由 OpenAI API 生成的图像的 URL 或内容。](https://platform.openai.com/docs/api-reference/images/object)
public struct ImageObject: Decodable {
   /// 如果 response_format 为 url(默认),则为生成图像的 URL。
   public let url: URL?
   /// 如果 response_format 为 b64_json,则为生成图像的 Base64 编码 JSON。
   public let b64Json: String?
   /// 用于生成图像的提示,如果有对提示的修订,则显示修订后的提示。
   public let revisedPrompt: String?
}

使用方法

/// 创建图像
let prompt = "龙和独角兽的混合体"
let createParameters = ImageCreateParameters(prompt: prompt, model: .dalle3(.largeSquare))
let imageURLS = try await service.legacyCreateImages(parameters: createParameters).data.map(\.url)
/// 编辑图像
let data = Data(contentsOfURL:_) // 从图片获取的数据。
let image = UIImage(data: data)
let prompt = "添加一个充满粉红色气球的背景。"
let editParameters = ImageEditParameters(image: image, prompt: prompt, numberOfImages: 4)  
let imageURLS = try await service.legacyEditImage(parameters: parameters).data.map(\.url)
/// 图像变体
let data = Data(contentsOfURL:_) // 从图片获取的数据。
let image = UIImage(data: data)
let variationParameters = ImageVariationParameters(image: image, numberOfImages: 4)
let imageURLS = try await service.legacyCreateImageVariations(parameters: parameters).data.map(\.url)

模型

响应


/// 描述了一个 OpenAI [模型](https://platform.openai.com/docs/api-reference/models/object),该模型可以与 API 一起使用。
public struct ModelObject: Decodable {
   
   /// 模型标识符,可在 API 端点中引用。
   public let id: String
   /// 模型创建的 Unix 时间戳(以秒为单位)。
   public let created: Int
   /// 对象类型,始终为 "model"。
   public let object: String
   /// 拥有该模型的组织。
   public let ownedBy: String
   /// 表示当前模型权限的数组。数组中的每个元素对应特定的权限设置。如果没有权限或数据不可用,该数组可能为 nil。
   public let permission: [Permission]?
   
   public struct Permission: Decodable {
      public let id: String?
      public let object: String?
      public let created: Int?
      public let allowCreateEngine: Bool?
      public let allowSampling: Bool?
      public let allowLogprobs: Bool?
      public let allowSearchIndices: Bool?
      public let allowView: Bool?
      public let allowFineTuning: Bool?
      public let organization: String?
      public let group: String?
      public let isBlocking: Bool?
   }
   
   /// 表示来自 [delete](https://platform.openai.com/docs/api-reference/models/delete) 微调 API 的响应。
   public struct DeletionStatus: Decodable {
      
      public let id: String
      public let object: String
      public let deleted: Bool
   }
}

使用方法

/// 列出模型
let models = try await service.listModels().data
/// 获取模型
let modelID = "gpt-3.5-turbo-instruct"
let retrievedModel = try await service.retrieveModelWith(id: modelID)
/// 删除微调后的模型
let modelID = "fine-tune-model-id"
let deletionStatus = try await service.deleteFineTuneModelWith(id: modelID)

内容审核

参数

/// [用于分类文本是否违反 OpenAI 的内容政策。](https://platform.openai.com/docs/api-reference/moderations/create)
public struct ModerationParameter<Input: Encodable>: Encodable {
   
   /// 要分类的输入文本,可以是字符串或数组。
   let input: Input
   /// 目前有两种内容审核模型可供选择:text-moderation-stable 和 text-moderation-latest。
   /// 默认使用 text-moderation-latest 模型,该模型会随时间自动升级,以确保您始终使用我们最准确的模型。如果您选择 text-moderation-stable 模型,我们在更新该模型之前会提前通知您。text-moderation-stable 模型的准确性可能会略低于 text-moderation-latest 模型。
   let model: String?
   
   enum Model: String {
      case stable = "text-moderation-stable"
      case latest = "text-moderation-latest"
   }
   
   init(
      input: Input,
      model: Model? = nil)
   {
      self.input = input
      self.model = model?.rawValue
   }
}

响应

/// [审核对象](https://platform.openai.com/docs/api-reference/moderations/object)。表示 OpenAI 的内容审核模型针对给定输入生成的政策合规报告。
public struct ModerationObject: Decodable {
   
   /// 审核请求的唯一标识符。
   public let id: String
   /// 用于生成审核结果的模型。
   public let model: String
   /// 审核结果列表。
   public let results: [Moderation]
   
   public struct Moderation: Decodable {
      
      /// 内容是否违反 OpenAI 的使用政策。
      public let flagged: Bool
      /// 各类别的列表,以及这些类别是否被标记。
      public let categories: Category<Bool>
      /// 各类别的列表及其由模型预测的得分。
      public let categoryScores: Category<Double>
      
      public struct Category<T: Decodable>: Decodable {
         
         /// 表达、煽动或宣扬基于种族、性别、民族、宗教、国籍、性取向、残疾状况或种姓的仇恨内容。针对非受保护群体(例如国际象棋棋手)的仇恨内容被视为骚扰。
         public let hate: T
         /// 包含暴力或对目标群体造成严重伤害的仇恨内容,这些群体基于种族、性别、民族、宗教、国籍、性取向、残疾状况或种姓。
         public let hateThreatening: T
         /// 表达、煽动或宣扬针对任何目标的骚扰性语言的内容。
         public let harassment: T
         /// 包含暴力或对任何目标造成严重伤害的骚扰内容。
         public let harassmentThreatening: T
         /// 宣传、鼓励或描绘自残行为的内容,例如自杀、割腕和饮食失调等。
         public let selfHarm: T
         /// 发言者表示正在实施或打算实施自残行为的内容,例如自杀、割腕和饮食失调等。
         public let selfHarmIntent: T
         /// 鼓励实施自残行为,例如自杀、割腕和饮食失调,或提供如何实施此类行为的指导或建议的内容。
         public let selfHarmInstructions: T
         /// 旨在引起性兴奋的内容,例如对性活动的描述,或宣传性服务的内容(不包括性教育和健康相关内容)。
         public let sexual: T
         /// 包含未满 18 岁个人的色情内容。
         public let sexualMinors: T
         /// 描绘死亡、暴力或身体伤害的内容。
         public let violence: T
         /// 以极度写实方式描绘死亡、暴力或身体伤害的内容。
         public let violenceGraphic: T
      }
   }
}

用法

/// 单个提示
let prompt = "我要杀了他"
let parameters = ModerationParameter(input: prompt)
let isFlagged = try await service.createModerationFromText(parameters: parameters)
/// 多个提示
let prompts = ["我要杀了他", "我要去死"]
let parameters = ModerationParameter(input: prompts)
let isFlagged = try await service.createModerationFromTexts(parameters: parameters)

测试版

助手

参数

/// 使用模型和指令创建一个[助手](https://platform.openai.com/docs/api-reference/assistants/createAssistant)。
/// 修改一个[助手](https://platform.openai.com/docs/api-reference/assistants/modifyAssistant)。
public struct AssistantParameters: Encodable {
   
   /// 要使用的模型ID。您可以使用[列出模型](https://platform.openai.com/docs/api-reference/models/list)API查看所有可用的模型,或参阅我们的[模型概述](https://platform.openai.com/docs/models/overview)以获取它们的描述。
   public var model: String?
   /// 助手的名称。最大长度为256个字符。
   public var name: String?
   /// 助手的描述。最大长度为512个字符。
   public var description: String?
   /// 助手所使用的系统指令。最大长度为32768个字符。
   public var instructions: String?
   /// 助手上启用的工具列表。每个助手最多可有128种工具。工具类型可以是代码解释器、检索或函数。默认值为[]
   public var tools: [AssistantObject.Tool] = []
   /// 可附加到对象上的16组键值对。这有助于以结构化格式存储有关该对象的附加信息。键的最大长度为64个字符,值的最大长度为512个字符。
   public var metadata: [String: String]?
   /// 采样温度范围在0到2之间。较高的值(如0.8)会使输出更随机,而较低的值(如0.2)则会使输出更专注且确定性更强。
   /// 默认值为1
   public var temperature: Double?
   /// 一种替代温度采样的方法,称为核采样,在这种方法中,模型会考虑具有top_p概率质量的标记结果。例如,0.1表示仅考虑构成前10%概率质量的标记。
   /// 我们通常建议调整此参数或温度,但不要同时调整两者。
   /// 默认值为1
   public var topP: Double?
   /// 指定模型必须输出的格式。与GPT-4 Turbo以及自gpt-3.5-turbo-1106以来的所有GPT-3.5 Turbo模型兼容。
   /// 设置为{ "type": "json_object" }可启用JSON模式,从而保证模型生成的消息是有效的JSON。
   /// 重要提示:使用JSON模式时,您还必须通过系统消息或用户消息指示模型生成JSON。否则,模型可能会生成无尽的空白内容,直到达到令牌限制,从而导致请求长时间运行并看似“卡住”。此外,如果finish_reason="length",则消息内容可能会被部分截断,这表明生成已超过max_tokens或对话已超过最大上下文长度。
   /// 默认值为`auto`
   public var responseFormat: ResponseFormat?
   
   public enum Action {
      case create(model: String) // 创建助手时需要指定模型。
      case modify(model: String?) // 修改助手时模型为可选。
      
      var model: String? {
         switch self {
         case .create(let model): return model
         case .modify(let model): return model
         }
      }
   }
}

响应

/// 表示一个可以调用模型并使用工具的[助手](https://platform.openai.com/docs/api-reference/assistants)。
public struct AssistantObject: Decodable {
   
   /// 标识符,可在API端点中引用。
   public let id: String
   /// 对象类型,始终为“assistant”。
   public let object: String
   /// 助手创建时的Unix时间戳(以秒为单位)。
   public let createdAt: Int
   /// 助手的名称。最大长度为256个字符。
   public let name: String?
   /// 助手的描述。最大长度为512个字符。
   public let description: String?
   /// 要使用的模型ID。您可以使用[列出模型](https://platform.openai.com/docs/api-reference/models/list)API查看所有可用的模型,或参阅我们的[模型概述](https://platform.openai.com/docs/models/overview)以获取它们的描述。
   public let model: String
   /// 助手所使用的系统指令。最大长度为32768个字符。
   public let instructions: String?
   /// 助手上启用的工具列表。每个助手最多可有128种工具。工具类型可以是代码解释器、检索或函数。
   public let tools: [Tool]
   /// 与此助手关联的[文件](https://platform.openai.com/docs/api-reference/files)ID列表。每个助手最多可关联20个文件。文件按创建日期升序排列。
   /// 助手工具所使用的资源集合。这些资源因工具类型而异。例如,代码解释器工具需要文件ID列表,而文件搜索工具则需要向量存储ID列表。
   public let toolResources: ToolResources?
   /// 可附加到对象上的16组键值对。这有助于以结构化格式存储有关该对象的附加信息。键的最大长度为64个字符,值的最大长度为512个字符。
   public let metadata: [String: String]?
   /// 采样温度范围在0到2之间。较高的值(如0.8)会使输出更随机,而较低的值(如0.2)则会使输出更专注且确定性更强。
   /// 默认值为1
   public var temperature: Double?
   /// 一种替代温度采样的方法,称为核采样,在这种方法中,模型会考虑具有top_p概率质量的标记结果。例如,0.1表示仅考虑构成前10%概率质量的标记。
   /// 我们通常建议调整此参数或温度,但不要同时调整两者。
   /// 默认值为1
   public var topP: Double?
   /// 指定模型必须输出的格式。与GPT-4 Turbo以及自gpt-3.5-turbo-1106以来的所有GPT-3.5 Turbo模型兼容。
   /// 设置为{ "type": "json_object" }可启用JSON模式,从而保证模型生成的消息是有效的JSON。
   /// 重要提示:使用JSON模式时,您还必须通过系统消息或用户消息指示模型生成JSON。否则,模型可能会生成无尽的空白内容,直到达到令牌限制,从而导致请求长时间运行并看似“卡住”。此外,如果finish_reason="length",则消息内容可能会被部分截断,这表明生成已超过max_tokens或对话已超过最大上下文长度。
   /// 默认值为`auto`
   public var responseFormat: ResponseFormat?

```swift
public struct Tool: Codable {
      
      /// 定义的工具类型。
      public let type: String
      public let function: ChatCompletionParameters.ChatFunction?
      
      public enum ToolType: String, CaseIterable {
         case codeInterpreter = "code_interpreter"
         case fileSearch = "file_search"
         case function
      }
      
      /// 辅助属性,用于显示工具类型。
      public var displayToolType: ToolType? { .init(rawValue: type) }
      
      public init(
         type: ToolType,
         function: ChatCompletionParameters.ChatFunction? = nil)
      {
         self.type = type.rawValue
         self.function = function
      }
   }
   
   public struct DeletionStatus: Decodable {
      public let id: String
      public let object: String
      public let deleted: Bool
   }
}

使用方法

创建助手

let parameters = AssistantParameters(action: .create(model: Model.gpt41106Preview.rawValue), name: "数学家教")
let assistant = try await service.createAssistant(parameters: parameters)

获取助手

let assistantID = "asst_abc123"
let assistant = try await service.retrieveAssistant(id: assistantID)

修改助手

let assistantID = "asst_abc123"
let parameters = AssistantParameters(action: .modify, name: "儿童数学家教")
let assistant = try await service.modifyAssistant(id: assistantID, parameters: parameters)

删除助手

let assistantID = "asst_abc123"
let deletionStatus = try await service.deleteAssistant(id: assistantID)

列出助手

let assistants = try await service.listAssistants()

线程

参数

/// 创建一个[线程](https://platform.openai.com/docs/api-reference/threads/createThread)
public struct CreateThreadParameters: Encodable {
   
   /// 用于启动线程的消息列表。
   public var messages: [MessageObject]?
      /// 助手工具使用的资源集合。这些资源因工具类型而异。例如,code_interpreter 工具需要文件 ID 列表,而 file_search 工具则需要向量存储 ID 列表。
   public var toolResources: ToolResources?
   /// 可附加到对象上的 16 组键值对。这有助于以结构化方式存储关于对象的额外信息。键的最大长度为 64 个字符,值的最大长度为 512 个字符。
   public var metadata: [String: String]?
}

响应

/// [线程对象](https://platform.openai.com/docs/api-reference/threads) 表示包含[消息](https://platform.openai.com/docs/api-reference/messages)的线程。
public struct ThreadObject: Decodable {
   
   /// 标识符,可在 API 端点中引用。
   public let id: String
   /// 对象类型,始终为 thread。
   public let object: String
   /// 线程创建时的 Unix 时间戳(以秒为单位)。
   public let createdAt: Int
   /// 助手工具使用的资源集合。这些资源因工具类型而异。例如,code_interpreter 工具需要文件 ID 列表,而 file_search 工具则需要向量存储 ID 列表。
   public var toolResources: ToolResources?
   /// 可附加到对象上的 16 组键值对。这有助于以结构化方式存储关于对象的额外信息。键的最大长度为 64 个字符,值的最大长度为 512 个字符。
   public let metadata: [String: String]
   
}

使用方法

创建线程。

let parameters = CreateThreadParameters()
let thread = try await service.createThread(parameters: parameters)

获取线程。

let threadID = "thread_abc123"
let thread = try await service.retrieveThread(id: threadID)

修改线程。

let threadID = "thread_abc123"
let parameters = CreateThreadParameters(metadata: ["modified": "true", "user": "abc123"])
let thread = try await service.modifyThread(id: threadID, parameters: parameters)

删除线程。

let threadID = "thread_abc123"
let thread = try await service.deleteThread(id: threadID)

消息

参数 创建消息)

public struct MessageParameter: Encodable {
   
   /// 发送消息的实体的角色。允许的值包括:
   /// user:表示消息由实际用户发送,在大多数情况下应使用此值来代表用户生成的消息。
   /// assistant:表示消息由助手生成。使用此值可将助手的消息插入对话中。
   let role: String
   /// 消息的内容,可以是字符串或内容片段数组(文本、图像URL、图像文件)。
   let content: Content
   /// 附加到消息的文件列表,以及这些文件应被添加到的工具。
   let attachments: [MessageAttachment]?
   /// 可附加到对象的一组16个键值对。这有助于以结构化格式存储关于对象的额外信息。键的最大长度为64个字符,值的最大长度为512个字符。
   let metadata: [String: String]?
}

修改消息)

public struct ModifyMessageParameters: Encodable {
   
   /// 可附加到对象的一组16个键值对。这有助于以结构化格式存储关于对象的额外信息。键的最大长度为64个字符,值的最大长度为512个字符。
   public var metadata: [String: String]
}

响应

/// 表示[线程](https://platform.openai.com/docs/api-reference/threads)中的一个[消息](https://platform.openai.com/docs/api-reference/messages)。
public struct MessageObject: Codable {
   
   /// 标识符,可在API端点中引用。
   public let id: String
   /// 对象类型,始终为thread.message。
   public let object: String
   /// 消息创建时的Unix时间戳(以秒为单位)。
   public let createdAt: Int
   /// 此消息所属的[线程](https://platform.openai.com/docs/api-reference/threads)ID。
   public let threadID: String
   /// 消息的状态,可以是in_progress、incomplete或completed。
   public let status: String
   /// 对于未完成的消息,说明其未完成的原因。
   public let incompleteDetails: IncompleteDetails?
   /// 消息完成时的Unix时间戳(以秒为单位)。
   public let completedAt: Int
   /// 产生消息的实体。可以是user或assistant。
   public let role: String
   /// 消息的内容,以文本和/或图像的数组形式呈现。
   public let content: [MessageContent]
   /// 如果适用,撰写此消息的[助手](https://platform.openai.com/docs/api-reference/assistants)ID。
   public let assistantID: String?
   /// 如果适用,与撰写此消息相关的[运行](https://platform.openai.com/docs/api-reference/runs)ID。
   public let runID: String?
   /// 附加到消息的文件列表,以及这些文件被添加到的工具。
   public let attachments: [MessageAttachment]?
   /// 可附加到对象的一组16个键值对。这有助于以结构化格式存储关于对象的额外信息。键的最大长度为64个字符,值的最大长度为512个字符。
   public let metadata: [String: String]?
   
   enum Role: String {
      case user
      case assistant
   }
}

// MARK: MessageContent

public enum MessageContent: Codable {
   
   case imageFile(ImageFile)
   case text(Text)
}

// MARK: Image File

public struct ImageFile: Codable {
   /// 始终为image_file。
   public let type: String
   
   /// 在消息内容中引用一张[文件](https://platform.openai.com/docs/api-reference/files)。
   public let imageFile: ImageFileContent
   
   public struct ImageFileContent: Codable {
      
      /// 消息内容中图像所对应的[文件](https://platform.openai.com/docs/api-reference/files)ID。
      public let fileID: String
   }
}

// MARK: Text

public struct Text: Codable {
   
   /// 始终为text。
   public let type: String
   /// 作为消息一部分的文本内容。
   public let text: TextContent
   
   public struct TextContent: Codable {
      // 构成文本的数据。
      public let value: String
      
      public let annotations: [Annotation]
   }
}

// MARK: Annotation

public enum Annotation: Codable {
   
   case fileCitation(FileCitation)
   case filePath(FilePath)
}

// MARK: FileCitation

/// 消息中指向与助手或消息相关联的特定文件中某一具体引文的引用。当助手使用“retrieval”工具搜索文件时生成。
public struct FileCitation: Codable {
   
   /// 始终为file_citation。
   public let type: String
   /// 需要替换的消息内容中的文本。
   public let text: String
   public let fileCitation: FileCitation
   public  let startIndex: Int
   public let endIndex: Int
   
   public struct FileCitation: Codable {
      
      /// 引文来自的具体文件ID。
      public let fileID: String
      /// 文件中的具体引文。
      public let quote: String

   }
}

// MARK: FilePath

/// 当助手使用code_interpreter工具生成文件时产生的文件路径URL。
public struct FilePath: Codable {
   
   /// 始终为file_path。
   public let type: String
   /// 需要替换的消息内容中的文本。
   public let text: String
   public let filePath: FilePath
   public let startIndex: Int
   public let endIndex: Int
   
   public struct FilePath: Codable {
      /// 所生成文件的ID。
      public let fileID: String
   }
}

用法

创建消息。

let threadID = "thread_abc123"
let prompt = "给我一些生日派对的创意。"
let parameters = MessageParameter(role: "user", content: .stringContent(prompt)")
let message = try await service.createMessage(threadID: threadID, parameters: parameters)

获取消息。

let threadID = "thread_abc123"
let messageID = "msg_abc123"
let message = try await service.retrieveMessage(threadID: threadID, messageID: messageID)

修改消息。

let threadID = "thread_abc123"
let messageID = "msg_abc123"
let parameters = ModifyMessageParameters(metadata: ["modified": "true", "user": "abc123"])
let message = try await service.modifyMessage(threadID: threadID, messageID: messageID, parameters: parameters)

列出消息

let threadID = "thread_abc123"
let messages = try await service.listMessages(threadID: threadID, limit: nil, order: nil, after: nil, before: nil) 

运行

参数

创建运行

public struct RunParameter: Encodable {
   
   /// 用于执行此运行的[助手](https://platform.openai.com/docs/api-reference/assistants)的ID。
    let assistantID: String
   /// 用于执行此运行的[模型](https://platform.openai.com/docs/api-reference/models)的ID。如果在此处提供值,则会覆盖与助手关联的模型。否则,将使用与助手关联的模型。
   let model: String?
   /// 覆盖助手的默认系统消息。这在每次运行时修改行为时非常有用。
   let instructions: String?
   /// 在运行的指令末尾附加额外的指令。这在不覆盖其他指令的情况下,按每次运行修改行为时很有用。
   let additionalInstructions: String?
   /// 在创建运行之前,向线程添加额外的消息。
   let additionalMessages: [MessageParameter]?
   /// 覆盖助手在此运行中可以使用的工具。这在每次运行时修改行为时很有用。
   let tools: [AssistantObject.Tool]?
   /// 可以附加到对象上的16个键值对集合。这有助于以结构化格式存储有关对象的更多信息。键的最大长度为64个字符,值的最大长度为512个字符。
   let metadata: [String: String]?
   /// 使用的采样温度范围为0到2。较高的值(如0.8)会使输出更随机,而较低的值(如0.2)则会使输出更集中和确定性。
   /// 可选,默认为1
   let temperature: Double?
   /// 如果为真,则以服务器发送事件的形式返回运行期间发生的事件流,当运行进入终止状态并发出data: [DONE]消息时结束。
   var stream: Bool
   /// 运行过程中可能使用的最大提示令牌数。运行将尽力在多次回合中仅使用指定数量的提示令牌。如果运行超过指定的提示令牌数,则将以“已完成”状态结束。更多信息请参见incomplete_details。
   let maxPromptTokens: Int?
   /// 运行过程中可能使用的最大完成令牌数。运行将尽力在多次回合中仅使用指定数量的完成令牌。如果运行超过指定的完成令牌数,则将以“已完成”状态结束。更多信息请参见incomplete_details。
   let maxCompletionTokens: Int?
   /// 控制线程在运行前如何截断。可用于控制运行的初始上下文窗口。
   let truncationStrategy: TruncationStrategy?
   /// 控制模型调用哪种工具(如果有的话)。none表示模型不会调用任何工具,而是生成一条消息。auto是默认值,表示模型可以在生成消息或调用工具之间进行选择。指定特定工具,例如{"type": "file_search"}或{"type": "function", "function": {"name": "my_function"}},则强制模型调用该工具。
   let toolChoice: ToolChoice?
   /// 指定模型必须输出的格式。与GPT-4 Turbo以及所有比gpt-3.5-turbo-1106更新的GPT-3.5 Turbo模型兼容。
   /// 设置为{ "type": "json_object" }可启用JSON模式,从而保证模型生成的消息是有效的JSON。
   /// 重要提示:使用JSON模式时,您还必须通过系统消息或用户消息指示模型自行生成JSON。否则,模型可能会生成无休止的空白内容,直到达到令牌限制,从而导致长时间运行且看似“卡住”的请求。此外,如果finish_reason="length",则消息内容可能会被部分截断,这表明生成已超过max_tokens或对话已超过最大上下文长度。
   let responseFormat: ResponseFormat?
}

修改运行

public struct ModifyRunParameters: Encodable {
   
   /// 可以附加到对象上的16个键值对集合。这有助于以结构化格式存储有关对象的更多信息。键的最大长度为64个字符,值的最大长度为512个字符。
   public var metadata: [String: String]
   
   public init(
      metadata: [String : String])
   {
      self.metadata = metadata
   }
}

创建线程并运行

public struct CreateThreadAndRunParameter: Encodable {
   
   /// 用于执行此运行的[助手](https://platform.openai.com/docs/api-reference/assistants)的ID。
   let assistantId: String
   /// 要创建的线程。
   let thread: CreateThreadParameters?
   /// 用于执行此运行的[模型](https://platform.openai.com/docs/api-reference/models)的ID。如果在此处提供值,则会覆盖与助手关联的模型。否则,将使用与助手关联的模型。
   let model: String?
   /// 覆盖助手的默认系统消息。这在每次运行时修改行为时很有用。
   let instructions: String?
   /// 覆盖助手在此运行中可以使用的工具。这在每次运行时修改行为时很有用。
   let tools: [AssistantObject.Tool]?
   /// 可以附加到对象上的16个键值对集合。这有助于以结构化格式存储有关对象的更多信息。键的最大长度为64个字符,值的最大长度为512个字符。
   let metadata: [String: String]?
   /// 使用的采样温度范围为0到2。较高的值(如0.8)会使输出更随机,而较低的值(如0.2)则会使输出更集中和确定性。
   /// 默认为1
   let temperature: Double?
   /// 一种替代温度采样的方法,称为核采样,在这种方法中,模型会考虑具有top_p概率质量的标记结果。因此,0.1表示仅考虑构成顶部10%概率质量的标记。
   /// 我们通常建议调整此参数或温度,但不要同时调整两者。
   let topP: Double?
   /// 如果为真,则以服务器发送事件的形式返回运行期间发生的事件流,当运行进入终止状态并发出data: [DONE]消息时结束。
   var stream: Bool = false
   /// 运行过程中可能使用的最大提示令牌数。运行将尽力在多次回合中仅使用指定数量的提示令牌。如果运行超过指定的提示令牌数,则将以“未完成”状态结束。更多信息请参见incomplete_details。
   let maxPromptTokens: Int?
   /// 运行过程中可能使用的最大完成令牌数。运行将尽力在多次回合中仅使用指定数量的完成令牌。如果运行超过指定的完成令牌数,则将以“已完成”状态结束。更多信息请参见incomplete_details。
   let maxCompletionTokens: Int?
   /// 控制线程在运行前如何截断。可用于控制运行的初始上下文窗口。
   let truncationStrategy: TruncationStrategy?
   /// 控制模型调用哪种工具(如果有的话)。none表示模型不会调用任何工具,而是生成一条消息。auto是默认值,表示模型可以在生成消息或调用工具之间进行选择。指定特定工具,例如{"type": "file_search"}或{"type": "function", "function": {"name": "my_function"}},则强制模型调用该工具。
   let toolChoice: ToolChoice?
   /// 指定模型必须输出的格式。与GPT-4 Turbo以及所有比gpt-3.5-turbo-1106更新的GPT-3.5 Turbo模型兼容。
   /// 设置为{ "type": "json_object" }可启用JSON模式,从而保证模型生成的消息是有效的JSON。
   /// 重要提示:使用JSON模式时,您还必须通过系统消息或用户消息指示模型自行生成JSON。否则,模型可能会生成无尽的空白内容,直到达到令牌限制,从而导致长时间运行且看似“卡住”的请求。此外,如果finish_reason="length",则消息内容可能会被部分截断,这表明生成已超过max_tokens或对话已超过最大上下文长度。
   let responseFormat: ResponseFormat?
}

提交工具输出到运行

public struct RunToolsOutputParameter: Encodable {
   
   /// 提交输出的工具列表。
   public let toolOutputs: [ToolOutput]
   /// 如果为真,则以服务器发送事件的形式返回运行期间发生的事件流,当运行进入终止状态并发出data: [DONE]消息时结束。
   public let stream: Bool
}

响应

public struct RunObject: Decodable {
   
   /// 可以在API端点中引用的标识符。
   public let id: String
   /// 对象类型,始终为thread.run。
   public let object: String
   /// 运行创建时的Unix时间戳(以秒为单位)。
   public let createdAt: Int?
   /// 作为此运行的一部分执行的[线程](https://platform.openai.com/docs/api-reference/threads)的ID。
   public let threadID: String
   /// 用于执行此运行的[助手](https://platform.openai.com/docs/api-reference/assistants)的ID。
   public let assistantID: String
   /// 运行的状态,可能是queued、in_progress、requires_action、cancelling、cancelled、failed、completed或expired。
   public let status: String
   /// 继续运行所需的行动详情。如果没有所需行动,则为null。
   public let requiredAction: RequiredAction?
   /// 与此运行相关的最后一个错误。如果没有错误,则为null。
   public let lastError: LastError?
   /// 运行到期时的Unix时间戳(以秒为单位)。
   public let expiresAt: Int?
   /// 运行开始时的Unix时间戳(以秒为单位)。
   public let startedAt: Int?
   /// 运行取消时的Unix时间戳(以秒为单位)。
   public let cancelledAt: Int?
   /// 运行失败时的Unix时间戳(以秒为单位)。
   public let failedAt: Int?
   /// 运行完成时的Unix时间戳(以秒为单位)。
   public let completedAt: Int?
   /// 运行未完成的原因详情。如果运行未完成,则为null。
   public let incompleteDetails: IncompleteDetails?
   /// 此运行所使用的[助手](https://platform.openai.com/docs/api-reference/assistants)的模型。
   public let model: String
   /// 此运行所使用的[助手](https://platform.openai.com/docs/api-reference/assistants)的指令。
   public let instructions: String?
   /// 此运行所使用的[助手](https://platform.openai.com/docs/api-reference/assistants)的工具列表。
   public let tools: [AssistantObject.Tool]
   /// 可以附加到对象上的16个键值对集合。这有助于以结构化格式存储有关对象的更多信息。键的最大长度为64个字符,值的最大长度为512个字符。
   public let metadata: [String: String]
   /// 与运行相关的使用统计信息。如果运行未处于终端状态(即in_progress、queued等),则此值为null。
   public let usage: Usage?
   /// 为此运行使用的采样温度。如果未设置,则默认为1。
   public let temperature: Double?
   /// 为此运行使用的核采样值。如果未设置,则默认为1。
   public let topP: Double?
   /// 运行过程中指定使用的最大提示令牌数。
   public let maxPromptTokens: Int?
   /// 运行过程中指定使用的最大完成令牌数。
   public let maxCompletionTokens: Int?
   /// 控制线程在运行前如何截断。可用于控制运行的初始上下文窗口。
   public let truncationStrategy: TruncationStrategy?
   /// 控制模型调用哪种工具(如果有的话)。none表示模型不会调用任何工具,而是生成一条消息。auto是默认值,表示模型可以在生成消息或调用工具之间进行选择。指定特定工具,例如{"type": "TOOL_TYPE"}或{"type": "function", "function": {"name": "my_function"}},则强制模型调用该工具。
   public let toolChoice: ToolChoice?
   /// 指定模型必须输出的格式。与GPT-4 Turbo以及所有比gpt-3.5-turbo-1106更新的GPT-3.5 Turbo模型兼容。
   /// 设置为{ "type": "json_object" }可启用JSON模式,从而保证模型生成的消息是有效的JSON。
   /// 重要提示:使用JSON模式时,您还必须通过系统消息或用户消息指示模型自行生成JSON。否则,模型可能会生成无尽的空白内容,直到达到令牌限制,从而导致长时间运行且看似“卡住”的请求。此外,如果finish_reason="length",则消息内容可能会被部分截断,这表明生成已超过max_tokens或对话已超过最大上下文长度。
   public let responseFormat: ResponseFormat?
}

用法

创建一次运行

let assistantID = "asst_abc123"
let parameters = RunParameter(assistantID: assistantID)
let run = try await service.createRun(threadID: threadID, parameters: parameters)

获取一次运行

let threadID = "thread_abc123"
let runID = "run_abc123"
let run = try await service.retrieveRun(threadID: threadID, runID: runID)

修改一次运行

let threadID = "thread_abc123"
let runID = "run_abc123"
let parameters = ModifyRunParameters(metadata: ["modified": "true", "user": "abc123"])
let message = try await service.modifyRun(threadID: threadID, messageID: messageID, parameters: parameters)

列出所有运行

let threadID = "thread_abc123"
let runs = try await service.listRuns(threadID: threadID, limit: nil, order: nil, after: nil, before: nil)

向运行提交工具输出

let threadID = "thread_abc123"
let runID = "run_abc123"
let toolCallID = "call_abc123"
let output = "28C"
let parameters = RunToolsOutputParameter(toolOutputs: [.init(toolCallId: toolCallID, output: output)])
let run = try await service.submitToolOutputsToRun(threadID: threadID, runID: runID, parameters: parameters)

取消一次运行

/// 取消一个正在进行中的运行。
let threadID = "thread_abc123"
let runID = "run_abc123"
let run = try await service.cancelRun(threadID: threadID, runID: runID)

创建线程和运行

let assistantID = "asst_abc123"
let parameters = CreateThreadAndRunParameter(assistantID: assistantID)
let run = service.createThreadAndRun(parameters: parameters)

运行步骤对象

表示运行执行过程中的一个步骤。 响应

public struct RunStepObject: Decodable {
   
   /// 运行步骤的标识符,可在 API 端点中引用。
   public let id: String
   /// 对象类型,始终为 `thread.run.step`。
   public let object: String
   /// 创建该运行步骤时的 Unix 时间戳(以秒为单位)。
   public let createdAt: Int
   /// 与该运行步骤关联的[助手](https://platform.openai.com/docs/api-reference/assistants)的 ID。
   public let assistantId: String
   /// 被运行的[线程](https://platform.openai.com/docs/api-reference/threads)的 ID。
   public let threadId: String
   /// 该运行步骤所属的[运行](https://platform.openai.com/docs/api-reference/runs)的 ID。
   public let runId: String
   /// 运行步骤的类型,可以是 message_creation 或 tool_calls。
   public let type: String
   /// 运行步骤的状态,可以是 in_progress、cancelled、failed、completed 或 expired。
   public let status: String
   /// 运行步骤的详细信息。
   public let stepDetails: RunStepDetails
   /// 与此运行步骤相关的最后一个错误。如果没有错误,则为 null。
   public let lastError: RunObject.LastError?
   /// 运行步骤过期时的 Unix 时间戳(以秒为单位)。如果父级运行已过期,则该步骤也被视为已过期。
   public let expiredAt: Int?
   /// 运行步骤被取消时的 Unix 时间戳(以秒为单位)。
   public let cancelledAt: Int?
   /// 运行步骤失败时的 Unix 时间戳(以秒为单位)。
   public let failedAt: Int?
   /// 运行步骤完成时的 Unix 时间戳(以秒为单位)。
   public let completedAt: Int?
   /// 一组可附加到对象上的 16 个键值对。这有助于以结构化格式存储关于对象的额外信息。键的最大长度为 64 个字符,值的最大长度为 512 个字符。
   public let metadata: [String: String]?
   /// 与运行步骤相关的使用统计信息。当运行步骤状态为 in_progress 时,此值为 null。
   public let usage: Usage?
}

用法 获取一个运行步骤

let threadID = "thread_abc123"
let runID = "run_abc123"
let stepID = "step_abc123"
let runStep = try await service.retrieveRunstep(threadID: threadID, runID: runID, stepID: stepID)

列出所有运行步骤

let threadID = "thread_abc123"
let runID = "run_abc123"
let runSteps = try await service.listRunSteps(threadID: threadID, runID: runID, limit: nil, order: nil, after: nil, before: nil)

运行步骤详情

运行步骤的详细信息。

public struct RunStepDetails: Codable {
   
   /// `message_creation` 或 `tool_calls`
   public let type: String
   /// 运行步骤创建消息的详细信息。
   public let messageCreation: MessageCreation?
   /// 工具调用的详细信息。
   public let toolCalls: [ToolCall]?
}

助手流式传输

助手 API 的流式传输。

您可以流式传输执行运行或在提交工具输出后恢复运行的结果。

通过将参数设置为 "stream": true,您可以从 创建线程和运行创建运行提交工具输出 端点流式传输事件。响应将是一个服务器发送事件流。

OpenAI Python 教程(https://platform.openai.com/docs/assistants/overview?context=with-streaming)

消息增量对象

MessageDeltaObject 表示消息的增量,即在流式传输过程中消息中任何发生变化的字段。

public struct MessageDeltaObject: Decodable {
   
   /// 消息的标识符,可在 API 端点中引用。
   public let id: String
   /// 对象类型,始终为 thread.message.delta。
   public let object: String
   /// 包含消息中已更改字段的增量。
   public let delta: Delta
   
   public struct Delta: Decodable {
      
      /// 生成消息的实体,可以是 user 或 assistant。
      public let role: String
      /// 消息内容,由文本和/或图像组成的数组。
      public let content: [MessageContent]
   }
}

运行步骤增量对象

表示一个 运行步骤增量,即在流式传输过程中运行步骤上任何已更改的字段。

public struct RunStepDeltaObject: Decodable {
   
   /// 运行步骤的标识符,可在 API 端点中引用。
   public let id: String
   /// 对象类型,始终为 thread.run.step.delta。
   public let object: String
   /// 包含运行步骤上已更改字段的增量。
   public let delta: Delta
   
   public struct Delta: Decodable {
      
      /// 运行步骤的详细信息。
      public let stepDetails: RunStepDetails
      
      private enum CodingKeys: String, CodingKey {
         case stepDetails = "step_details"
      }
   }
}

⚠️ 要使用 createRunAndStreamMessage,请先创建助手并启动线程。

用法 通过流式传输创建 运行

createRunAndStreamMessage 会流式传输 事件,您可以根据自己的实现需求选择所需的事件类型。例如,以下是如何访问消息增量和运行步骤增量对象的方法:

let assistantID = "asst_abc123"
let threadID = "thread_abc123"
let messageParameter = MessageParameter(role: .user, content: "告诉我 1235 的平方根")
let message = try await service.createMessage(threadID: threadID, parameters: messageParameter)
let runParameters = RunParameter(assistantID: assistantID)
let stream = try await service.createRunAndStreamMessage(threadID: threadID, parameters: runParameters)

         for try await result in stream {
            switch result {
            case .threadMessageDelta(let messageDelta):
               let content = messageDelta.delta.content.first
               switch content {
               case .imageFile, nil:
                  break
               case .text(let textContent):
                  print(textContent.text.value) // 这将打印出消息的流式响应。
               }
               
            case .threadRunStepDelta(let runStepDelta):
               if let toolCall = runStepDelta.delta.stepDetails.toolCalls?.first?.toolCall {
                  switch toolCall {
                  case .codeInterpreterToolCall(let toolCall):
                     print(toolCall.input ?? "") // 这将打印出代码解释器工具调用的流式响应。
                  case .fileSearchToolCall(let toolCall):
                     print("文件搜索工具调用")
                  case .functionToolCall(let toolCall):
                     print("函数工具调用")
                  case nil:
                     break
                  }
               }
            }
         }

您可以在本包中的 Examples 文件夹 中,导航到“配置助手”选项卡,创建一个助手,并按照后续步骤操作。

流式支持也已添加到:

创建线程和运行

   /// 创建启用流式传输的线程和运行。
   ///
   /// - 参数:创建线程和运行所需的参数。
   /// - 返回:[AssistantStreamEvent](https://platform.openai.com/docs/api-reference/assistants-streaming/events) 对象的异步抛异常流。
   /// - 抛出:如果请求失败,则抛出错误。
   ///
   /// 更多信息,请参阅 [OpenAI 的 Run API 文档](https://platform.openai.com/docs/api-reference/runs/createThreadAndRun)。
   func createThreadAndRunStream(
      parameters: CreateThreadAndRunParameter)
   async throws -> AsyncThrowingStream<AssistantStreamEvent, Error>

提交工具输出

   /// 当运行状态为“requires_action”且 required_action.type 为 submit_tool_outputs 时,此端点可用于在所有工具调用完成后提交工具输出。所有输出必须在单个请求中提交。启用流式传输。
   ///
   /// - 参数:该运行所属的 [线程](https://platform.openai.com/docs/api-reference/threads) ID。
   /// - 参数:需要提交工具输出的运行 ID。
   /// - 参数:运行工具输出所需的参数。
   /// - 返回:[AssistantStreamEvent](https://platform.openai.com/docs/api-reference/assistants-streaming/events) 对象的异步抛异常流。
   /// - 抛出:如果请求失败,则抛出错误。
   ///
   /// 更多信息,请参阅 [OpenAI 的 Run API 文档](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs)。
   func submitToolOutputsToRunStream(
      threadID: String,
      runID: String,
      parameters: RunToolsOutputParameter)
   async throws -> AsyncThrowingStream<AssistantStreamEvent, Error>

向量存储

参数

public struct VectorStoreParameter: Encodable {
   
   /// 向量存储应使用的[文件](https://platform.openai.com/docs/api-reference/files) ID 列表。对于 file_search 等可以访问文件的工具非常有用。
   let fileIDS: [String]?
   /// 向量存储的名称。
   let name: String?
   /// 向量存储的过期策略。
   let expiresAfter: ExpirationPolicy?
   /// 可附加到对象的一组 16 个键值对。这有助于以结构化格式存储关于对象的附加信息。键的最大长度为 64 个字符,值的最大长度为 512 个字符。
   let metadata: [String: String]?
}

响应

public struct VectorStoreObject: Decodable {
   
   /// 标识符,可在 API 端点中引用。
   let id: String
   /// 对象类型,始终为 vector_store。
   let object: String
   /// 创建向量存储时的 Unix 时间戳(以秒为单位)。
   let createdAt: Int
   /// 向量存储的名称。
   let name: String
   /// 向量存储中文件所占用的总字节数。
   let usageBytes: Int
   
   let fileCounts: FileCount
   /// 向量存储的状态,可能为 expired、in_progress 或 completed。状态为 completed 表示向量存储已准备好使用。
   let status: String
   /// 向量存储的过期策略。
   let expiresAfter: ExpirationPolicy?
   /// 向量存储到期的 Unix 时间戳(以秒为单位)。
   let expiresAt: Int?
   /// 向量存储上次活跃的 Unix 时间戳(以秒为单位)。
   let lastActiveAt: Int?
   /// 可附加到对象的一组 16 个键值对。这有助于以结构化格式存储关于对象的附加信息。键的最大长度为 64 个字符,值的最大长度为 512 个字符。
   let metadata: [String: String]
   
   public struct FileCount: Decodable {
      
      /// 当前正在处理的文件数量。
      let inProgress: Int
      /// 已成功处理的文件数量。
      let completed: Int
      /// 处理失败的文件数量。
      let failed: Int
      /// 被取消的文件数量。
      let cancelled: Int
      /// 文件总数。
      let total: Int
   }
}

用法 创建向量存储

let name = "Support FAQ"
let parameters = VectorStoreParameter(name: name)
try vectorStore = try await service.createVectorStore(parameters: parameters)

列出向量存储

let vectorStores = try await service.listVectorStores(limit: nil, order: nil, after: nil, before: nil)

检索向量存储

let vectorStoreID = "vs_abc123"
let vectorStore = try await service.retrieveVectorStore(id: vectorStoreID)

修改向量存储

let vectorStoreID = "vs_abc123"
let vectorStore = try await service.modifyVectorStore(id: vectorStoreID)

删除向量存储

let vectorStoreID = "vs_abc123"
let deletionStatus = try await service.deleteVectorStore(id: vectorStoreID)

向量存储文件

参数

public struct VectorStoreFileParameter: Encodable {
   
   /// 向量存储应使用的[文件](https://platform.openai.com/docs/api-reference/files) ID。对于 file_search 等可以访问文件的工具非常有用。
   let fileID: String
}

响应

public struct VectorStoreFileObject: Decodable {
   
   /// 标识符,可在 API 端点中引用。
   let id: String
   /// 对象类型,始终为 vector_store.file。
   let object: String
   /// 向量存储使用的总字节数。请注意,这可能与原始文件大小不同。
   let usageBytes: Int
   /// 创建向量存储文件时的 Unix 时间戳(以秒为单位)。
   let createdAt: Int
   /// [向量存储](https://platform.openai.com/docs/api-reference/vector-stores/object) 的 ID,该[文件](https://platform.openai.com/docs/api-reference/files) 附属于此向量存储。
   let vectorStoreID: String
   /// 向量存储文件的状态,可能为 in_progress、completed、cancelled 或 failed。状态为 completed 表示向量存储文件已准备好使用。
   let status: String
   /// 与此向量存储文件相关的最后一次错误。如果没有错误,则为 null。
   let lastError: LastError?
}

用法 创建向量存储文件

let vectorStoreID = "vs_abc123"
let fileID = "file-abc123"
let parameters = VectorStoreFileParameter(fileID: fileID)
let vectoreStoreFile = try await service.createVectorStoreFile(vectorStoreID: vectorStoreID, parameters: parameters)

列出向量存储文件

let vectorStoreID = "vs_abc123"
let vectorStoreFiles = try await service.listVectorStoreFiles(vectorStoreID: vectorStoreID, limit: nil, order: nil, aftre: nil, before: nil, filter: nil)

检索向量存储文件

let vectorStoreID = "vs_abc123"
let fileID = "file-abc123"
let vectoreStoreFile = try await service.retrieveVectorStoreFile(vectorStoreID: vectorStoreID, fileID: fileID)

删除向量存储文件

let vectorStoreID = "vs_abc123"
let fileID = "file-abc123"
let deletionStatus = try await service.deleteVectorStoreFile(vectorStoreID: vectorStoreID, fileID: fileID)

向量存储文件批次

参数

public struct VectorStoreFileBatchParameter: Encodable {
   
   /// 向量存储应使用的[文件](https://platform.openai.com/docs/api-reference/files) ID 列表。对于可以访问文件的工具(如 file_search)非常有用。
   let fileIDS: [String]
}

响应

public struct VectorStoreFileBatchObject: Decodable {
   
   /// 标识符,可在 API 端点中引用。
   let id: String
   /// 对象类型,始终为 vector_store.file_batch。
   let object: String
   /// 创建向量存储文件批次时的 Unix 时间戳(以秒为单位)。
   let createdAt: Int
   /// [向量存储](https://platform.openai.com/docs/api-reference/vector-stores/object) 的 ID,该[文件](https://platform.openai.com/docs/api-reference/files)已附加到此向量存储。
   let vectorStoreID: String
   /// 向量存储文件批次的状态,可能为 in_progress、completed、cancelled 或 failed。
   let status: String
   
   let fileCounts: FileCount
}

用法

创建向量存储文件批次

let vectorStoreID = "vs_abc123"
let fileIDS = ["file-abc123", "file-abc456"]
let parameters = VectorStoreFileBatchParameter(fileIDS: fileIDS)
let vectorStoreFileBatch = try await service.
   createVectorStoreFileBatch(vectorStoreID: vectorStoreID, parameters: parameters)

检索向量存储文件批次

let vectorStoreID = "vs_abc123"
let batchID = "vsfb_abc123"
let vectorStoreFileBatch = try await service.retrieveVectorStoreFileBatch(vectorStoreID: vectorStoreID, batchID: batchID)

取消向量存储文件批次

let vectorStoreID = "vs_abc123"
let batchID = "vsfb_abc123"
let vectorStoreFileBatch = try await service.cancelVectorStoreFileBatch(vectorStoreID: vectorStoreID, batchID: batchID)

列出批次中的向量存储文件

let vectorStoreID = "vs_abc123"
let batchID = "vsfb_abc123"
let vectorStoreFiles = try await service.listVectorStoreFilesInABatch(vectorStoreID: vectorStoreID, batchID: batchID)

⚠️ 我们目前仅支持 Assistants Beta 2。如果您需要 Assistants V1 的支持,可以在 jroch-supported-branch-for-assistants-v1 分支或 v2.3 版本中获取。请参阅 OpenAI 文档了解迁移详情。

Anthropic

Anthropic 提供与 OpenAI 的兼容性,更多信息请访问文档

要使用 SwiftOpenAI 调用 Claude 模型,您可以这样做:

let anthropicApiKey = ""
let openAIService = OpenAIServiceFactory.service(apiKey: anthropicApiKey, 
                     overrideBaseURL: "https://api.anthropic.com", 
                     overrideVersion: "v1")

现在您可以这样创建完成参数:

let parameters = ChatCompletionParameters(
   messages: [.init(
   role: .user,
   content: "你是 Claude 吗?")],
   model: .custom("claude-3-7-sonnet-20250219"))

如需更完整的 Anthropic Swift 包,您可以使用 SwiftAnthropic

Azure OpenAI

本库通过 Azure OpenAI 提供对聊天完成和聊天流式完成的支持。目前,DefaultOpenAIAzureService 支持聊天完成,包括流式和非流式选项。

有关 Azure 配置的更多信息,请参阅文档

要实例化 DefaultOpenAIAzureService,您需要提供一个 AzureOpenAIConfiguration

let azureConfiguration = AzureOpenAIConfiguration(
                           resourceName: "YOUR_RESOURCE_NAME", 
                           openAIAPIKey: .apiKey("YOUR_OPENAI_APIKEY), 
                           apiVersion: "THE_API_VERSION")
                           
let service = OpenAIServiceFactory.service(azureConfiguration: azureConfiguration)           

支持的 API 版本可在 Azure 文档 中找到。

当前支持的版本:

2022-12-01 2023-03-15-preview 2023-05-15 2023-06-01-preview 2023-07-01-preview 2023-08-01-preview 2023-09-01-preview

使用 聊天完成

let parameters = ChatCompletionParameters(
                     messages: [.init(role: .user, content: .text(prompt))], 
                     model: .custom("DEPLOYMENT_NAME") /// 您部署模型时选择的部署名称。例如:“gpt-35-turbo-0613”
let completionObject = try await service.startChat(parameters: parameters)

AIProxy

是什么?

AIProxy 是一款用于 iOS 应用程序的后端服务,可将您的应用程序请求代理至 OpenAI。 使用代理可以保护您的 OpenAI 密钥不被泄露,从而避免因密钥被盗而导致意外高额账单。 只有在请求通过您设定的速率限制以及 Apple 的 DeviceCheck 验证后,才会进行代理。 我们提供 AIProxy 支持,以便您能够安全地分发使用 SwiftOpenAI 构建的应用程序。

我的 SwiftOpenAI 代码需要做哪些改动?

通过 AIProxy 代理请求时,只需对你的 Xcode 项目进行两项更改:

  1. 不再使用以下方式初始化 service

     let apiKey = "your_openai_api_key_here"
     let service = OpenAIServiceFactory.service(apiKey: apiKey)
    

而是使用:

    let service = OpenAIServiceFactory.service(
        aiproxyPartialKey: "your_partial_key_goes_here",
        aiproxyServiceURL: "your_service_url_goes_here"
    )

aiproxyPartialKeyaiproxyServiceURL 的值会在 AIProxy 开发者仪表板 上提供给你。

  1. 在 Xcode 中添加一个名为 AIPROXY_DEVICE_CHECK_BYPASS 的环境变量。该令牌同样在 AIProxy 开发者仪表板中提供,是 iOS 模拟器与 AIProxy 后端通信所必需的。
    • 按下 cmd + shift + , 打开 Xcode 的“编辑方案”菜单。
    • 在侧边栏中选择“运行”。
    • 从顶部导航栏中选择“参数”。
    • 在“环境变量”部分(而非“启动时传递的参数”部分)添加一个名为 AIPROXY_DEVICE_CHECK_BYPASS 的环境变量,并将其值设置为我们在 AIProxy 仪表板中提供的令牌。

⚠️ AIPROXY_DEVICE_CHECK_BYPASS 仅适用于模拟器。请勿将其泄露到应用的发布版本中(包括 TestFlight 分发)。如果你按照上述步骤操作,该常量不会泄露,因为环境变量不会被打包进应用安装包中。

什么是 AIPROXY_DEVICE_CHECK_BYPASS 常量?

AIProxy 使用 Apple 的 DeviceCheck 来确保后端接收到的请求确实来自你应用在合法 Apple 设备上的发出。然而,iOS 模拟器无法生成 DeviceCheck 令牌。为了避免你在开发过程中必须频繁地在真机上构建和运行,AIProxy 提供了一种跳过 DeviceCheck 完整性检查的方法。此令牌仅供开发者使用。如果攻击者获取了该令牌,他们就可以在不包含 DeviceCheck 令牌的情况下向你的 AIProxy 项目发起请求,从而绕过一层保护机制。

什么是 aiproxyPartialKey 常量?

该常量可以安全地包含在应用的发布版本中。它是你真实密钥的加密表示的一部分,另一部分则存储在 AIProxy 的后端。当你的应用向 AIProxy 发送请求时,这两部分加密数据会配对、解密,并用于完成对 OpenAI 的请求。

如何在 AIProxy 上设置我的项目?

请参阅 AIProxy 集成指南

⚠️ 免责声明

SwiftOpenAI 的贡献者不对任何由第三方造成的损害或损失承担责任。本库的贡献者提供第三方集成服务仅为方便起见。使用任何第三方服务的风险均由您自行承担。

Ollama

Ollama 现已内置与 OpenAI Chat Completions API 的兼容性,这使得你可以更方便地在本地使用各种工具和应用程序来操作 Ollama。

Screenshot 2024-06-24 at 11 52 35 PM

⚠️ 重要提示

请记住,这些模型是在本地运行的,因此你需要先下载它们。如果你想使用 llama3,可以在终端中运行以下命令:

ollama pull llama3

更多详细信息,请参考 Ollama 文档

如何使用 SwiftOpenAI 在本地调用这些模型?

要在你的应用中使用本地模型并结合 OpenAIService,你需要提供一个 URL。

let service = OpenAIServiceFactory.service(baseURL: "http://localhost:11434")

然后你可以按如下方式使用 completions API:

let prompt = "给我讲个笑话"
let parameters = ChatCompletionParameters(messages: [.init(role: .user, content: .text(prompt))], model: .custom("llama3"))
let chatCompletionObject = service.startStreamedChat(parameters: parameters)

⚠️ 注意:你也可以使用 OpenAIServiceFactory.service(apiKey:overrideBaseURL:proxyPath) 来适配任何兼容 OpenAI 的服务。

参考资料:

Ollama OpenAI 兼容性文档 Ollama OpenAI 兼容性博客文章

备注

你还可以使用这种服务构造函数来提供任意 URL 或 API 密钥,以满足需求。

let service = OpenAIServiceFactory.service(apiKey: "YOUR_API_KEY", baseURL: "http://localhost:11434")

Groq

Screenshot 2024-10-11 at 11 49 04 PM

Groq API 大体上与 OpenAI 的客户端库兼容,例如 SwiftOpenAI。要使用该库与 Groq 集成,你只需创建一个 OpenAIService 实例,如下所示:

let apiKey = "your_api_key"
let service = OpenAIServiceFactory.service(apiKey: apiKey, overrideBaseURL: "https://api.groq.com/", proxyPath: "openai")

有关 Groq 支持的 API,请参阅其 官方文档

xAI

xAI Grok

xAI 为其 Grok 模型提供了兼容 OpenAI 的 completion API。你可以使用 OpenAI SDK 来访问这些模型。

let apiKey = "your_api_xai_key"
let service = OpenAIServiceFactory.service(apiKey: apiKey, overrideBaseURL: "https://api.x.ai", overrideVersion: "v1")

有关 xAI API 的更多信息,请参阅其 官方文档

OpenRouter

Image

OpenRouter 提供了一个兼容 OpenAI 的 completion API,支持 314 种模型和提供商,你可以直接调用,也可以使用 OpenAI SDK 进行调用。此外,还有一些第三方 SDK 可供使用。


// 创建服务

let apiKey = "your_api_key"
let service = OpenAIServiceFactory.service(apiKey: apiKey, 
   overrideBaseURL: "https://openrouter.ai", 
   proxyPath: "api",
   extraHeaders: [
      "HTTP-Referer": "<YOUR_SITE_URL>", // 可选。用于 openrouter.ai 排名的网站 URL。
         "X-Title": "<YOUR_SITE_NAME>"  // 可选。用于 openrouter.ai 排名的网站标题。
   ])

// 发起请求

let prompt = "曼哈顿计划是什么?"
let parameters = ChatCompletionParameters(messages: [.init(role: .user, content: .text(prompt))], model: .custom("deepseek/deepseek-r1:free"))
let stream = service.startStreamedChat(parameters: parameters)

有关 OpenRouter API 的更多信息,请参阅其 快速入门文档

DeepSeek

图片

DeepSeek API 使用与 OpenAI 兼容的 API 格式。通过修改配置,您可以使用 SwiftOpenAI 访问 DeepSeek API。

创建服务

let apiKey = "your_api_key"
let service = OpenAIServiceFactory.service(
   apiKey: apiKey,
   overrideBaseURL: "https://api.deepseek.com")

非流式示例

let prompt = "什么是曼哈顿计划?"
let parameters = ChatCompletionParameters(
    messages: [.init(role: .user, content: .text(prompt))],
    model: .custom("deepseek-reasoner")
)

do {
    let result = try await service.chat(parameters: parameters)
    
    // 访问响应内容
    if let content = result.choices.first?.message.content {
        print("响应: \(content)")
    }
    
    // 如果有推理内容,访问推理内容
    if let reasoning = result.choices.first?.message.reasoningContent {
        print("推理: \(reasoning)")
    }
} catch {
    print("错误: \(error)")
}

流式示例

let prompt = "什么是曼哈顿计划?"
let parameters = ChatCompletionParameters(
    messages: [.init(role: .user, content: .text(prompt))],
    model: .custom("deepseek-reasoner")
)

// 开始流式处理
do {
    let stream = try await service.startStreamedChat(parameters: parameters)
    for try await result in stream {
        let content = result.choices.first?.delta.content ?? ""
        self.message += content
        
        // 可选:如果存在推理内容,进行处理
        if let reasoning = result.choices.first?.delta.reasoningContent {
            self.reasoningMessage += reasoning
        }
    }
} catch APIError.responseUnsuccessful(let description, let statusCode) {
    self.errorMessage = "网络错误,状态码:\(statusCode),描述:\(description)"
} catch {
    self.errorMessage = error.localizedDescription
}

注意事项

  • DeepSeek API 与 OpenAI 的格式兼容,但使用的模型名称不同。
  • 使用 .custom("deepseek-reasoner") 来指定 DeepSeek 模型。
  • reasoningContent 字段是可选的,仅适用于 DeepSeek 的 API。
  • 错误处理遵循与标准 OpenAI 请求相同的模式。

有关 DeepSeek API 的更多信息,请参阅其 文档

Gemini

截图 2024-11-12 上午10:53:43

Gemini 现在可以通过 OpenAI 库访问。公告。 SwiftOpenAI 支持所有 OpenAI 端点,但是请参考 Gemini 文档以了解当前哪些 API 是兼容的。

Gemini 现在可以通过 OpenAI 库访问。请参阅公告 这里。 SwiftOpenAI 支持所有 OpenAI 端点。然而,请参考 Gemini 文档 以了解当前哪些 API 是兼容的。

您可以通过您的 Gemini 令牌实例化一个 OpenAIService,如下所示...

let geminiAPIKey = "your_api_key"
let baseURL = "https://generativelanguage.googleapis.com"
let version = "v1beta"

let service = OpenAIServiceFactory.service(
   apiKey: apiKey, 
   overrideBaseURL: baseURL, 
   overrideVersion: version)

现在您可以使用 .custom 模型参数创建聊天请求,并将模型名称作为字符串传递。

let parameters = ChatCompletionParameters(
      messages: [.init(
      role: .user,
      content: content)],
      model: .custom("gemini-1.5-flash"))

let stream = try await service.startStreamedChat(parameters: parameters)

合作

对于任何拟议的更改,请打开一个指向 main 分支的 PR。非常欢迎提供单元测试 ❤️

版本历史

4.4.92026/04/02
4.4.82025/12/27
4.4.72025/12/09
4.4.62025/12/07
v4.4.52025/11/26
v4.4.42025/11/17
v4.4.32025/11/15
v4.4.22025/11/14
v4.4.12025/11/14
v4.4.02025/11/12
v4.3.42025/10/06
v4.3.32025/09/30
v4.3.22025/08/10
v4.3.12025/07/07
v4.3.02025/06/17
v4.2.02025/06/07
v4.1.12025/05/13
v4.1.02025/04/25
v4.0.72025/04/14
v4.0.62025/03/17

常见问题

相似工具推荐

everything-claude-code

everything-claude-code 是一套专为 AI 编程助手(如 Claude Code、Codex、Cursor 等)打造的高性能优化系统。它不仅仅是一组配置文件,而是一个经过长期实战打磨的完整框架,旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。 通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能,everything-claude-code 能显著提升 AI 在复杂任务中的表现,帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略,使得模型响应更快、成本更低,同时有效防御潜在的攻击向量。 这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库,还是需要 AI 协助进行安全审计与自动化测试,everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目,它融合了多语言支持与丰富的实战钩子(hooks),让 AI 真正成长为懂上

154.3k|★★☆☆☆|今天
开发框架Agent语言模型

gemini-cli

gemini-cli 是一款由谷歌推出的开源 AI 命令行工具,它将强大的 Gemini 大模型能力直接集成到用户的终端环境中。对于习惯在命令行工作的开发者而言,它提供了一条从输入提示词到获取模型响应的最短路径,无需切换窗口即可享受智能辅助。 这款工具主要解决了开发过程中频繁上下文切换的痛点,让用户能在熟悉的终端界面内直接完成代码理解、生成、调试以及自动化运维任务。无论是查询大型代码库、根据草图生成应用,还是执行复杂的 Git 操作,gemini-cli 都能通过自然语言指令高效处理。 它特别适合广大软件工程师、DevOps 人员及技术研究人员使用。其核心亮点包括支持高达 100 万 token 的超长上下文窗口,具备出色的逻辑推理能力;内置 Google 搜索、文件操作及 Shell 命令执行等实用工具;更独特的是,它支持 MCP(模型上下文协议),允许用户灵活扩展自定义集成,连接如图像生成等外部能力。此外,个人谷歌账号即可享受免费的额度支持,且项目基于 Apache 2.0 协议完全开源,是提升终端工作效率的理想助手。

100.8k|★★☆☆☆|4天前
插件Agent图像

markitdown

MarkItDown 是一款由微软 AutoGen 团队打造的轻量级 Python 工具,专为将各类文件高效转换为 Markdown 格式而设计。它支持 PDF、Word、Excel、PPT、图片(含 OCR)、音频(含语音转录)、HTML 乃至 YouTube 链接等多种格式的解析,能够精准提取文档中的标题、列表、表格和链接等关键结构信息。 在人工智能应用日益普及的今天,大语言模型(LLM)虽擅长处理文本,却难以直接读取复杂的二进制办公文档。MarkItDown 恰好解决了这一痛点,它将非结构化或半结构化的文件转化为模型“原生理解”且 Token 效率极高的 Markdown 格式,成为连接本地文件与 AI 分析 pipeline 的理想桥梁。此外,它还提供了 MCP(模型上下文协议)服务器,可无缝集成到 Claude Desktop 等 LLM 应用中。 这款工具特别适合开发者、数据科学家及 AI 研究人员使用,尤其是那些需要构建文档检索增强生成(RAG)系统、进行批量文本分析或希望让 AI 助手直接“阅读”本地文件的用户。虽然生成的内容也具备一定可读性,但其核心优势在于为机器

93.4k|★★☆☆☆|1周前
插件开发框架

LLMs-from-scratch

LLMs-from-scratch 是一个基于 PyTorch 的开源教育项目,旨在引导用户从零开始一步步构建一个类似 ChatGPT 的大型语言模型(LLM)。它不仅是同名技术著作的官方代码库,更提供了一套完整的实践方案,涵盖模型开发、预训练及微调的全过程。 该项目主要解决了大模型领域“黑盒化”的学习痛点。许多开发者虽能调用现成模型,却难以深入理解其内部架构与训练机制。通过亲手编写每一行核心代码,用户能够透彻掌握 Transformer 架构、注意力机制等关键原理,从而真正理解大模型是如何“思考”的。此外,项目还包含了加载大型预训练权重进行微调的代码,帮助用户将理论知识延伸至实际应用。 LLMs-from-scratch 特别适合希望深入底层原理的 AI 开发者、研究人员以及计算机专业的学生。对于不满足于仅使用 API,而是渴望探究模型构建细节的技术人员而言,这是极佳的学习资源。其独特的技术亮点在于“循序渐进”的教学设计:将复杂的系统工程拆解为清晰的步骤,配合详细的图表与示例,让构建一个虽小但功能完备的大模型变得触手可及。无论你是想夯实理论基础,还是为未来研发更大规模的模型做准备

90.1k|★★★☆☆|1周前
语言模型图像Agent

NextChat

NextChat 是一款轻量且极速的 AI 助手,旨在为用户提供流畅、跨平台的大模型交互体验。它完美解决了用户在多设备间切换时难以保持对话连续性,以及面对众多 AI 模型不知如何统一管理的痛点。无论是日常办公、学习辅助还是创意激发,NextChat 都能让用户随时随地通过网页、iOS、Android、Windows、MacOS 或 Linux 端无缝接入智能服务。 这款工具非常适合普通用户、学生、职场人士以及需要私有化部署的企业团队使用。对于开发者而言,它也提供了便捷的自托管方案,支持一键部署到 Vercel 或 Zeabur 等平台。 NextChat 的核心亮点在于其广泛的模型兼容性,原生支持 Claude、DeepSeek、GPT-4 及 Gemini Pro 等主流大模型,让用户在一个界面即可自由切换不同 AI 能力。此外,它还率先支持 MCP(Model Context Protocol)协议,增强了上下文处理能力。针对企业用户,NextChat 提供专业版解决方案,具备品牌定制、细粒度权限控制、内部知识库整合及安全审计等功能,满足公司对数据隐私和个性化管理的高标准要求。

87.6k|★★☆☆☆|1周前
开发框架语言模型

ML-For-Beginners

ML-For-Beginners 是由微软推出的一套系统化机器学习入门课程,旨在帮助零基础用户轻松掌握经典机器学习知识。这套课程将学习路径规划为 12 周,包含 26 节精炼课程和 52 道配套测验,内容涵盖从基础概念到实际应用的完整流程,有效解决了初学者面对庞大知识体系时无从下手、缺乏结构化指导的痛点。 无论是希望转型的开发者、需要补充算法背景的研究人员,还是对人工智能充满好奇的普通爱好者,都能从中受益。课程不仅提供了清晰的理论讲解,还强调动手实践,让用户在循序渐进中建立扎实的技能基础。其独特的亮点在于强大的多语言支持,通过自动化机制提供了包括简体中文在内的 50 多种语言版本,极大地降低了全球不同背景用户的学习门槛。此外,项目采用开源协作模式,社区活跃且内容持续更新,确保学习者能获取前沿且准确的技术资讯。如果你正寻找一条清晰、友好且专业的机器学习入门之路,ML-For-Beginners 将是理想的起点。

85.1k|★★☆☆☆|3天前
图像数据工具视频