jamesrochabrun
SwiftOpenAI
Swift

The most complete open-source Swift package for interacting with OpenAI's public API.

Last updated Jun 30, 2026
653
Stars
127
Forks
10
Issues
0
Stars/day
Attention Score
84
Language breakdown
Swift 100.0%
โ–ธ Files click to expand
README

SwiftOpenAI

repoOpenAI

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

An open-source Swift package designed for effortless interaction with OpenAI's public API.

๐Ÿš€ Now also available as CLI and also as MCP

Table of Contents

Description

SwiftOpenAI is an open-source Swift package that streamlines interactions with all OpenAI's API endpoints, now with added support for Azure, AIProxy, Assistant stream APIs, and the new Realtime API for low-latency bidirectional voice conversations.

OpenAI ENDPOINTS

- Transcriptions - Translations - Speech - Realtime - Function Calling - Structured Outputs - Vision - Streaming Responses

BETA

- Assistants File Object - Message File Object - Run Step object - Run Step details - Message Delta Object - Run Step Delta Object - Vector store File - Vector store File Batch

Getting an API Key

โš ๏ธ Important

To interact with OpenAI services, you'll need an API key. Follow these steps to obtain one:

  • Visit OpenAI.
  • Sign up for an account or log in if you already have one.
  • Navigate to the API key page and follow the instructions to generate a new API key.
For more information, consult OpenAI's official documentation.

โš ๏ธ Please take precautions to keep your API key secure per OpenAI's guidance:

Remember that your API key is a secret! Do not share it with others or expose
it in any client-side code (browsers, apps). Production requests must be
routed through your backend server where your API key can be securely
loaded from an environment variable or key management service.

SwiftOpenAI has built-in support for AIProxy, which is a backend for AI apps, to satisfy this requirement. To configure AIProxy, see the instructions here.

Installation

Swift Package Manager

  • Open your Swift project in Xcode.
  • Go to File -> Add Package Dependency.
  • In the search bar, enter this URL.
  • Choose the version you'd like to install (see the note below).
  • Click Add Package.
Note: Xcode has a quirk where it defaults an SPM package's upper limit to 2.0.0. This package is beyond that limit, so you should not accept the defaults that Xcode proposes. Instead, enter the lower bound of the release version that you'd like to support, and then tab out of the input box for Xcode to adjust the upper bound. Alternatively, you may select branch -> main to stay on the bleeding edge.

Compatibility

Platform Support

SwiftOpenAI supports both Apple platforms and Linux.

  • Apple platforms include iOS 15+, macOS 13+, and watchOS 9+.
  • Linux: SwiftOpenAI on Linux uses AsyncHTTPClient to work around URLSession bugs in Apple's Foundation framework, and can be used with the Vapor server framework.

OpenAI-Compatible Providers

SwiftOpenAI supports various providers that are OpenAI-compatible, including but not limited to:

Check OpenAIServiceFactory for convenience initializers that you can use to provide custom URLs.

Usage

To use SwiftOpenAI in your project, first import the package:

import SwiftOpenAI

Then, initialize the service using your OpenAI API key:

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

You can optionally specify an organization name if needed.

let apiKey = "youropenaiapikeyhere"
let oganizationID = "yourorganixationid"
let service = OpenAIServiceFactory.service(apiKey: apiKey, organizationID: oganizationID)

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

For reasoning models, ensure that you extend the timeoutIntervalForRequest in the URL session configuration to a higher value. The default is 60 seconds, which may be insufficient, as requests to reasoning models can take longer to process and respond.

To configure it:

let apiKey = "youropenaiapikeyhere"
let organizationID = "yourorganizationid"
let session = URLSession.shared
session.configuration.timeoutIntervalForRequest = 360 // e.g., 360 seconds or more.
let httpClient = URLSessionHTTPClientAdapter(urlSession: session)
let service = OpenAIServiceFactory.service(apiKey: apiKey, organizationID: organizationID, httpClient: httpClient)

That's all you need to begin accessing the full range of OpenAI endpoints.

How to get the status code of network errors

You may want to build UI around the type of error that the API returns. For example, a 429 means that your requests are being rate limited. The APIError type has a case responseUnsuccessful with two associated values: a description and statusCode. Here is a usage example using the chat completion 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
   // Work with choices
} catch APIError.responseUnsuccessful(let description, let statusCode) {
   print("Network error with status code: \(statusCode) and description: \(description)")
} catch {
   print(error.localizedDescription)
}

Audio

Audio Transcriptions

Parameters
public struct AudioTranscriptionParameters: Encodable {
   
   /// The name of the file asset is not documented in OpenAI's official documentation; however, it is essential for constructing the multipart request.
   let fileName: String
   /// The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
   let file: Data
   /// ID of the model to use. Only whisper-1 is currently available.
   let model: String
   /// The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency.
   let language: String?
   /// An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.
   let prompt: String?
   /// The format of the transcript output, in one of these options: json, text, srt, verbose_json, or vtt. Defaults to json
   let responseFormat: String?
   /// The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit. Defaults to 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
   }
}

Response

public struct AudioObject: Decodable {        /// The transcribed text if the request uses the transcriptions API, or the translated text if the request uses the translations endpoint.    public let text: String }

Usage

let fileName = "narcos.m4a" let data = Data(contentsOfURL:_) // Data retrieved from the file named "narcos.m4a". let parameters = AudioTranscriptionParameters(fileName: fileName, file: data) // Important: in the file name always provide the file extension. let audioObject =  try await service.createTranscription(parameters: parameters)

Audio Translations

Parameters
public struct AudioTranslationParameters: Encodable {        /// The name of the file asset is not documented in OpenAI's official documentation; however, it is essential for constructing the multipart request.    let fileName: String    /// The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.    let file: Data    /// ID of the model to use. Only whisper-1 is currently available.    let model: String    /// An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.    let prompt: String?    /// The format of the transcript output, in one of these options: json, text, srt, verbose_json, or vtt. Defaults to json    let responseFormat: String?    /// The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit. Defaults to 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    } }

Response

public struct AudioObject: Decodable {        /// The transcribed text if the request uses the transcriptions API, or the translated text if the request uses the translations endpoint.    public let text: String }

Usage

let fileName = "german.m4a" let data = Data(contentsOfURL:_) // Data retrieved from the file named "german.m4a". let parameters = AudioTranslationParameters(fileName: fileName, file: data) // Important: in the file name always provide the file extension. let audioObject = try await service.createTranslation(parameters: parameters)

Audio Speech

Parameters
/// Generates audio from the input text.
public struct AudioSpeechParameters: Encodable {

/// One of the available TTS models: tts-1 or tts-1-hd let model: String /// The text to generate audio for. The maximum length is 4096 characters. let input: String /// The voice to use when generating the audio. Supported voices are alloy, echo, fable, onyx, nova, and shimmer. Previews of the voices are available in the Text to speech guide. let voice: String /// Defaults to mp3, The format to audio in. Supported formats are mp3, opus, aac, and flac. let responseFormat: String? /// Defaults to 1, The speed of the generated audio. Select a value from 0.25 to 4.0. 1.0 is the default. 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 } }

Response

/// The audio speech response. public struct AudioSpeechObject: Decodable {

/// The audio file content data. public let output: Data }

Usage

let prompt = "Hello, how are you today?" let parameters = AudioSpeechParameters(model: .tts1, input: prompt, voice: .shimmer) let audioObjectData = try await service.createSpeech(parameters: parameters).output playAudio(from: audioObjectData)

// Play data private func playAudio(from data: Data) { do { // Initialize the audio player with the data audioPlayer = try AVAudioPlayer(data: data) audioPlayer?.prepareToPlay() audioPlayer?.play() } catch { // Handle errors print("Error playing audio: \(error.localizedDescription)") } }

Audio Realtime

The Realtime API enables bidirectional voice conversations with OpenAI's models using WebSockets and low-latency audio streaming. The API supports both audio-to-audio and text-to-audio interactions with built-in voice activity detection, transcription, and function calling.

Platform Requirements: iOS 15+, macOS 13+, watchOS 9+. Requires AVFoundation (not available on Linux).

Permissions Required:

  • Add NSMicrophoneUsageDescription to your Info.plist
  • On macOS: Enable sandbox entitlements for microphone access and outgoing network connections
Parameters
/// Configuration for creating a realtime session public struct OpenAIRealtimeSessionConfiguration: Encodable, Sendable {

/// The input audio format. Options: .pcm16, .g711ulaw, .g711alaw. Default is .pcm16 let inputAudioFormat: AudioFormat? /// Configuration for input audio transcription using Whisper let inputAudioTranscription: InputAudioTranscription? /// System instructions for the model. Recommended default provided let instructions: String? /// Maximum tokens for response output. Can be .value(Int) or .infinite let maxResponseOutputTokens: MaxResponseOutputTokens? /// Output modalities: [.audio, .text] or [.text] only. Default is [.audio, .text] let modalities: [Modality]? /// The output audio format. Options: .pcm16, .g711ulaw, .g711alaw. Default is .pcm16 let outputAudioFormat: AudioFormat? /// Audio playback speed. Range: 0.25 to 4.0. Default is 1.0 let speed: Double? /// Sampling temperature for model responses. Range: 0.6 to 1.2. Default is 0.8 let temperature: Double? /// Array of tools/functions available for the model to call let tools: [Tool]? /// Tool selection mode: .none, .auto, .required, or .specific(functionName: String) let toolChoice: ToolChoice? /// Voice activity detection configuration. Options: .serverVAD or .semanticVAD let turnDetection: TurnDetection? /// The voice to use. Options: "alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse" let voice: String?

/// Available audio formats public enum AudioFormat: String, Encodable, Sendable { case pcm16 case g711_ulaw = "g711-ulaw" case g711_alaw = "g711-alaw" }

/// Output modalities public enum Modality: String, Encodable, Sendable { case audio case text }

/// Turn detection configuration public struct TurnDetection: Encodable, Sendable { /// Server-based VAD with customizable timing public static func serverVAD( prefixPaddingMs: Int = 300, silenceDurationMs: Int = 500, threshold: Double = 0.5 ) -> TurnDetection

/// Semantic VAD with eagerness level public static func semanticVAD(eagerness: Eagerness = .medium) -> TurnDetection

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

Response

/// Messages received from the realtime API public enum OpenAIRealtimeMessage: Sendable {    case error(String?)                    // Error occurred    case sessionCreated                    // Session successfully created    case sessionUpdated                    // Configuration updated    case responseCreated                   // Model started generating response    case responseAudioDelta(String)        // Audio chunk (base64 PCM16)    case inputAudioBufferSpeechStarted     // User started speaking (VAD detected)    case responseFunctionCallArgumentsDone(name: String, arguments: String, callId: String)    case responseTranscriptDelta(String)   // Partial AI transcript    case responseTranscriptDone(String)    // Complete AI transcript    case inputAudioBufferTranscript(String)           // User audio transcript    case inputAudioTranscriptionDelta(String)         // Partial user transcription    case inputAudioTranscriptionCompleted(String)     // Complete user transcription }

Supporting Types

/// Manages microphone input and audio playback for realtime conversations. /// Audio played through AudioController does not interfere with mic input (the model won't hear itself). @RealtimeActor public final class AudioController {

/// Initialize with specified modes /// - Parameter modes: Array of .record (for microphone) and/or .playback (for audio output) public init(modes: [Mode]) async throws

public enum Mode { case record // Enable microphone streaming case playback // Enable audio playback }

/// Returns an AsyncStream of microphone audio buffers /// - Throws: OpenAIError if .record mode wasn't enabled during initialization public func micStream() throws -> AsyncStream<AVAudioPCMBuffer>

/// Plays base64-encoded PCM16 audio from the model /// - Parameter base64String: Base64-encoded PCM16 audio data public func playPCM16Audio(base64String: String)

/// Interrupts current audio playback (useful when user starts speaking) public func interruptPlayback()

/// Stops all audio operations public func stop() }

/// Utility for encoding audio buffers to base64 public enum AudioUtils { /// Converts AVAudioPCMBuffer to base64 string for transmission public static func base64EncodeAudioPCMBuffer(from buffer: AVAudioPCMBuffer) -> String?

/// Checks if headphones are connected public static var headphonesConnected: Bool }

Usage

// 1. Create session configuration let configuration = OpenAIRealtimeSessionConfiguration(    voice: "alloy",    instructions: "You are a helpful AI assistant. Be concise and friendly.",    turnDetection: .serverVAD(       prefixPaddingMs: 300,       silenceDurationMs: 500,       threshold: 0.5    ),    inputAudioTranscription: .init(model: "whisper-1") )

// 2. Create realtime session let session = try await service.realtimeSession( model: "gpt-4o-mini-realtime-preview-2024-12-17", configuration: configuration )

// 3. Initialize audio controller for recording and playback let audioController = try await AudioController(modes: [.record, .playback])

// 4. Handle incoming messages from OpenAI Task { for await message in session.receiver { switch message { case .responseAudioDelta(let audio): // Play audio from the model audioController.playPCM16Audio(base64String: audio)

case .inputAudioBufferSpeechStarted: // User started speaking - interrupt model's audio audioController.interruptPlayback()

case .responseTranscriptDelta(let text): // Display partial model transcript print("Model (partial): \(text)")

case .responseTranscriptDone(let text): // Display complete model transcript print("Model: \(text)")

case .inputAudioTranscriptionCompleted(let text): // Display user's transcribed speech print("User: \(text)")

case .responseFunctionCallArgumentsDone(let name, let args, let callId): // Handle function call from model print("Function call: \(name) with args: \(args)") // Execute function and send result back

case .error(let error): print("Error: \(error ?? "Unknown error")")

default: break } } }

// 5. Stream microphone audio to OpenAI Task { do { for try await buffer in audioController.micStream() { // Encode audio buffer to base64 guard let base64Audio = AudioUtils.base64EncodeAudioPCMBuffer(from: buffer) else { continue }

// Send audio to OpenAI try await session.sendMessage( OpenAIRealtimeInputAudioBufferAppend(audio: base64Audio) ) } } catch { print("Microphone error: \(error)") } }

// 6. Manually trigger a response (optional - usually VAD handles this) try await session.sendMessage( OpenAIRealtimeResponseCreate() )

// 7. Update session configuration mid-conversation (optional) let newConfig = OpenAIRealtimeSessionConfiguration( voice: "shimmer", temperature: 0.9 ) try await session.sendMessage( OpenAIRealtimeSessionUpdate(sessionConfig: newConfig) )

// 8. Cleanup when done audioController.stop() session.disconnect()

Function Calling

// Define tools in configuration let tools: [OpenAIRealtimeSessionConfiguration.Tool] = [    .init(       name: "get_weather",       description: "Get the current weather in a location",       parameters: [          "type": "object",          "properties": [             "location": [                "type": "string",                "description": "City name, e.g. San Francisco"             ]          ],          "required": ["location"]       ]    ) ]

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

// Handle function calls in message receiver case .responseFunctionCallArgumentsDone(let name, let args, let callId): if name == "get_weather" { // Parse arguments and execute function let result = getWeather(arguments: args)

// Send result back to model try await session.sendMessage( OpenAIRealtimeConversationItemCreate( item: .functionCallOutput( callId: callId, output: result ) ) ) }

Advanced Features

  • Voice Activity Detection (VAD): Choose between server-based VAD (with configurable timing) or semantic VAD (with eagerness levels)
  • Transcription: Enable Whisper transcription for both user input and model output
  • Session Updates: Change voice, instructions, or tools mid-conversation without reconnecting
  • Response Triggers: Manually trigger model responses or rely on automatic VAD
  • Platform-Specific Behavior: Automatically selects optimal audio API based on platform and headphone connection
For a complete implementation example, see Examples/RealtimeExample/RealtimeExample.swift in the repository.

Chat

Parameters
public struct ChatCompletionParameters: Encodable {
   
   /// A list of messages comprising the conversation so far. Example Python code
   public var messages: [Message]
   /// ID of the model to use. See the model endpoint compatibility table for details on which models work with the Chat API.
   /// Supports GPT-4, GPT-4o, GPT-5, and other models. For GPT-5 family: .gpt5, .gpt5Mini, .gpt5Nano
   public var model: String
   /// Whether or not to store the output of this chat completion request for use in our model distillation or evals products.
   /// Defaults to false
   public var store: Bool?
   /// Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. Defaults to 0
   /// See more information about frequency and presence penalties.
   public var frequencyPenalty: Double?
   /// Controls how the model responds to function calls. none means the model does not call a function, and responds to the end-user. auto means the model can pick between an end-user or calling a function. Specifying a particular function via {"name": "my_function"} forces the model to call that function. none is the default when no functions are present. auto is the default if functions are present.
   @available(*, deprecated, message: "Deprecated in favor of tool_choice.")
   public var functionCall: FunctionCall?
   /// Controls which (if any) function is called by the model. none means the model will not call a function and instead generates a message. 
   /// auto means the model can pick between generating a message or calling a function. Specifying a particular function via {&quot;type: &quot;function&quot;, &quot;function&quot;: {&quot;name&quot;: &quot;my_function&quot;}} forces the model to call that function.
   /// none is the default when no functions are present. auto is the default if functions are present.
   public var toolChoice: ToolChoice?
   /// A list of functions the model may generate JSON inputs for.
   @available(*, deprecated, message: "Deprecated in favor of tools.")
   public var functions: [ChatFunction]?
   /// A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for.
   public var tools: [Tool]?
   /// Whether to enable parallel function calling during tool use. Defaults to true.
   public var parallelToolCalls: Bool?
   /// Modify the likelihood of specified tokens appearing in the completion.
   /// Accepts a json object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. Defaults to null.
   public var logitBias: [Int: Double]?
   /// Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the content of message. This option is currently not available on the gpt-4-vision-preview model. Defaults to false.
   public var logprobs: Bool?
   /// An integer between 0 and 5 specifying the number of most likely tokens to return at each token position, each with an associated log probability. logprobs must be set to true if this parameter is used.
   public var topLogprobs: Int?
   /// The maximum number of tokens that can be generated in the chat completion. This value can be used to control costs for text generated via API.
   /// This value is now deprecated in favor of maxcompletiontokens, and is not compatible with o1 series models
   public var maxTokens: Int?
   /// An upper bound for the number of tokens that can be generated for a completion, including visible output tokens and reasoning tokens
   public var maCompletionTokens: Int?
   /// How many chat completion choices to generate for each input message. Defaults to 1.
   public var n: Int?
   /// Output types that you would like the model to generate for this request. Most models are capable of generating text, which is the default:
   /// ["text"]
   ///The gpt-4o-audio-preview model can also be used to generate audio. To request that this model generate both text and audio responses, you can use:
   /// ["text", "audio"]
   public var modalities: [String]?
   /// Parameters for audio output. Required when audio output is requested with modalities: ["audio"]. Learn more.
   public var audio: Audio?
   /// Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics. Defaults to 0
   /// See more information about frequency and presence penalties.
   public var presencePenalty: Double?
   /// An object specifying the format that the model must output. Used to enable JSON mode.
   /// Setting to { type: &quot;json_object&quot; } enables JSON mode, which guarantees the message the model generates is valid JSON.
   ///Important: when using JSON mode you must still instruct the model to produce JSON yourself via some conversation message, for example via your system message. If you don't do this, the model may generate an unending stream of whitespace until the generation reaches the token limit, which may take a lot of time and give the appearance of a "stuck" request. Also note that the message content may be partial (i.e. cut off) if finishreason=&quot;length&quot;, which indicates the generation exceeded maxtokens or the conversation exceeded the max context length.
   public var responseFormat: ResponseFormat?
   /// Specifies the latency tier to use for processing the request. This parameter is relevant for customers subscribed to the scale tier service:
   /// If set to 'auto', the system will utilize scale tier credits until they are exhausted.
   /// If set to 'default', the request will be processed in the shared cluster.
   /// When this parameter is set, the response body will include the service_tier utilized.
   public var serviceTier: String?
   /// This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same seed and parameters should return the same result.
   /// Determinism is not guaranteed, and you should refer to the system_fingerprint response parameter to monitor changes in the backend.
   public var seed: Int?
   /// Up to 4 sequences where the API will stop generating further tokens. Defaults to null.
   public var stop: [String]?
   /// If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a data: [DONE] message. Example Python code.
   /// Defaults to false.
   var stream: Bool? = nil
   /// Options for streaming response. Only set this when you set stream: true
   var streamOptions: StreamOptions?
   /// What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
   /// We generally recommend altering this or top_p but not both. Defaults to 1.
   public var temperature: Double?
   /// An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
   /// We generally recommend altering this or temperature but not both. Defaults to 1
   public var topP: Double?
   /// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.
   /// Learn more.
   public var user: String?
   
   public struct Message: Encodable {
      
      /// The role of the messages author. One of system, user, assistant, or tool message.
      let role: String
      /// The contents of the message. content is required for all messages, and may be null for assistant messages with function calls.
      let content: ContentType
      /// The name of the author of this message. name is required if role is function, and it should be the name of the function whose response is in the content. May contain a-z, A-Z, 0-9, and underscores, with a maximum length of 64 characters.
      let name: String?
      /// The name and arguments of a function that should be called, as generated by the model.
      @available(*, deprecated, message: "Deprecated and replaced by tool_calls")
      let functionCall: FunctionCall?
      /// The tool calls generated by the model, such as function calls.
      let toolCalls: [ToolCall]?
      /// Tool call that this message is responding to.
      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)
               switch self {
               case .text(let text):
                  try container.encode("text", forKey: .type)
                  try container.encode(text, forKey: .text)
               case .imageUrl(let imageDetail):
                  try container.encode("image_url", forKey: .type)
                  try container.encode(imageDetail, forKey: .imageUrl)
               }
            }
            
            public func hash(into hasher: inout Hasher) {
               switch self {
               case .text(let string):
                  hasher.combine(string)
               case .imageUrl(let imageDetail):
                  hasher.combine(imageDetail)
               }
            }
            
            public static func ==(lhs: MessageContent, rhs: MessageContent) -> Bool {
               switch (lhs, rhs) {
               case let (.text(a), .text(b)):
                  return a == b
               case let (.imageUrl(a), .imageUrl(b)):
                  return a == b
               default:
                  return false
               }
            }
         }
      }
      
      public enum Role: String {
         case system // content, role
         case user // content, role
         case assistant // content, role, tool_calls
         case tool // content, role, toolcallid
      }
      
      enum CodingKeys: String, CodingKey {
         case role
         case content
         case name
         case functionCall = "function_call"
         case toolCalls = "tool_calls"
         case toolCallID = "toolcallid"
      }
      
      public init(
         role: Role,
         content: ContentType,
         name: String? = nil,
         functionCall: FunctionCall? = nil,
         toolCalls: [ToolCall]? = nil,
         toolCallID: String? = nil)
      {
         self.role = role.rawValue
         self.content = content
         self.name = name
         self.functionCall = functionCall
         self.toolCalls = toolCalls
         self.toolCallID = toolCallID
      }
   }
   
   @available(*, deprecated, message: "Deprecated in favor of ToolChoice.")
   public enum FunctionCall: Encodable, Equatable {
      case none
      case auto
      case function(String)
      
      enum CodingKeys: String, CodingKey {
         case none = "none"
         case auto = "auto"
         case function = "name"
      }
      
      public func encode(to encoder: Encoder) throws {
         switch self {
         case .none:
            var container = encoder.singleValueContainer()
            try container.encode(CodingKeys.none.rawValue)
         case .auto:
            var container = encoder.singleValueContainer()
            try container.encode(CodingKeys.auto.rawValue)
         case .function(let name):
            var container = encoder.container(keyedBy: CodingKeys.self)
            try container.encode(name, forKey: .function)
         }
      }
   }
   
   /// Documentation
   public struct Tool: Encodable {
      
      /// The type of the tool. Currently, only function is supported.
      let type: String
      /// object
      let function: ChatFunction
      
      public init(
         type: String = "function",
         function: ChatFunction)
      {
         self.type = type
         self.function = function
      }
   }
   
   public struct ChatFunction: Codable, Equatable {
      
      /// The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.
      let name: String
      /// A description of what the function does, used by the model to choose when and how to call the function.
      let description: String?
      /// The parameters the functions accepts, described as a JSON Schema object. See the guide for examples, and the JSON Schema reference for documentation about the format.
      /// Omitting parameters defines a function with an empty parameter list.
      let parameters: JSONSchema?
      /// Defaults to false, Whether to enable strict schema adherence when generating the function call. If set to true, the model will follow the exact schema defined in the parameters field. Only a subset of JSON Schema is supported when strict is true. Learn more about Structured Outputs in the [function calling guide].(https://platform.openai.com/docs/api-reference/chat/docs/guides/function-calling)
      let strict: Bool?
      
      public init(
         name: String,
         strict: Bool?,
         description: String?,
         parameters: JSONSchema?)
      {
         self.name = name
         self.strict = strict
         self.description = description
         self.parameters = parameters
      }
   }
   
   public enum ServiceTier: String, Encodable {
      /// Specifies the latency tier to use for processing the request. This parameter is relevant for customers subscribed to the scale tier service:
      /// If set to 'auto', the system will utilize scale tier credits until they are exhausted.
      /// If set to 'default', the request will be processed in the shared cluster.
      /// When this parameter is set, the response body will include the service_tier utilized.
      case auto
      case default
   }
   
   public struct StreamOptions: Encodable {
      /// If set, an additional chunk will be streamed before the data: [DONE] message.
      /// The usage field on this chunk shows the token usage statistics for the entire request,
      /// and the choices field will always be an empty array. All other chunks will also include
      /// a usage field, but with a null value.
      let includeUsage: Bool

enum CodingKeys: String, CodingKey { case includeUsage = "include_usage" } } /// Parameters for audio output. Required when audio output is requested with modalities: ["audio"] /// Learn more. public struct Audio: Encodable { /// Specifies the voice type. Supported voices are alloy, echo, fable, onyx, nova, and shimmer. public let voice: String /// Specifies the output audio format. Must be one of wav, mp3, flac, opus, or pcm16. public let format: String public init( voice: String, format: String) { self.voice = voice self.format = format } }

enum CodingKeys: String, CodingKey { case messages case model case store case frequencyPenalty = "frequency_penalty" case toolChoice = "tool_choice" case functionCall = "function_call" case tools case parallelToolCalls = "paralleltoolcalls" case functions case logitBias = "logit_bias" case logprobs case topLogprobs = "top_logprobs" case maxTokens = "max_tokens" case maCompletionTokens = "maxcompletiontokens" case n case modalities case audio case responseFormat = "response_format" case presencePenalty = "presence_penalty" case seed case serviceTier = "service_tier" case stop case stream case streamOptions = "stream_options" case temperature case topP = "top_p" case user } public init( messages: [Message], model: Model, store: Bool? = nil, frequencyPenalty: Double? = nil, functionCall: FunctionCall? = nil, toolChoice: ToolChoice? = nil, functions: [ChatFunction]? = nil, tools: [Tool]? = nil, parallelToolCalls: Bool? = nil, logitBias: [Int: Double]? = nil, logProbs: Bool? = nil, topLogprobs: Int? = nil, maxTokens: Int? = nil, n: Int? = nil, modalities: [String]? = nil, audio: Audio? = nil, responseFormat: ResponseFormat? = nil, presencePenalty: Double? = nil, serviceTier: ServiceTier? = nil, seed: Int? = nil, stop: [String]? = nil, temperature: Double? = nil, topProbability: Double? = nil, user: 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 } }

Response

Chat completion object

/// Represents a chat completion response returned by model, based on the provided input. public struct ChatCompletionObject: Decodable {        /// A unique identifier for the chat completion.    public let id: String    /// A list of chat completion choices. Can be more than one if n is greater than 1.    public let choices: [ChatChoice]    /// The Unix timestamp (in seconds) of when the chat completion was created.    public let created: Int    /// The model used for the chat completion.    public let model: String    /// The service tier used for processing the request. This field is only included if the service_tier parameter is specified in the request.    public let serviceTier: String?    /// This fingerprint represents the backend configuration that the model runs with.    /// Can be used in conjunction with the seed request parameter to understand when backend changes have been made that might impact determinism.    public let systemFingerprint: String?    /// The object type, which is always chat.completion.    public let object: String    /// Usage statistics for the completion request.    public let usage: ChatUsage        public struct ChatChoice: Decodable {              /// The reason the model stopped generating tokens. This will be stop if the model hit a natural stop point or a provided stop sequence, length if the maximum number of tokens specified in the request was reached, contentfilter if content was omitted due to a flag from our content filters, toolcalls if the model called a tool, or function_call (deprecated) if the model called a function.       public let finishReason: IntOrStringValue?       /// The index of the choice in the list of choices.       public let index: Int       /// A chat completion message generated by the model.       public let message: ChatMessage          /// Log probability information for the choice.       public let logprobs: LogProb?              public struct ChatMessage: Decodable {                    /// The contents of the message.          public let content: String?          /// The tool calls generated by the model, such as function calls.          public let toolCalls: [ToolCall]?          /// The name and arguments of a function that should be called, as generated by the model.          @available(*, deprecated, message: "Deprecated and replaced by tool_calls")          public let functionCall: FunctionCall?          /// The role of the author of this message.          public let role: String          /// Provided by the Vision API.          public let finishDetails: FinishDetails?          /// The refusal message generated by the model.          public let refusal: String?          /// If the audio output modality is requested, this object contains data about the audio response from the model. Learn more.          public let audio: Audio?                    /// Provided by the Vision API.          public struct FinishDetails: Decodable {             let type: String          }                    public struct Audio: Decodable {             /// Unique identifier for this audio response.             public let id: String             /// The Unix timestamp (in seconds) for when this audio response will no longer be accessible on the server for use in multi-turn conversations.             public let expiresAt: Int             /// Base64 encoded audio bytes generated by the model, in the format specified in the request.             public let data: String             /// Transcript of the audio generated by the model.             public let transcript: String                          enum CodingKeys: String, CodingKey {                case id                case expiresAt = "expires_at"                case data                case transcript             }          }       }              public struct LogProb: Decodable {          /// A list of message content tokens with log probability information.          let content: [TokenDetail]       }              public struct TokenDetail: Decodable {          /// The token.          let token: String          /// The log probability of this token.          let logprob: Double          /// A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be null if there is no bytes representation for the token.          let bytes: [Int]?          /// List of the most likely tokens and their log probability, at this token position. In rare cases, there may be fewer than the number of requested top_logprobs returned.          let topLogprobs: [TopLogProb]                    enum CodingKeys: String, CodingKey {             case token, logprob, bytes             case topLogprobs = "top_logprobs"          }                    struct TopLogProb: Decodable {             /// The token.             let token: String             /// The log probability of this token.             let logprob: Double             /// A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be null if there is no bytes representation for the token.             let bytes: [Int]?          }       }    }        public struct ChatUsage: Decodable {              /// Number of tokens in the generated completion.       public let completionTokens: Int       /// Number of tokens in the prompt.       public let promptTokens: Int       /// Total number of tokens used in the request (prompt + completion).       public let totalTokens: Int    } }

Usage

let prompt = "Tell me a joke" let parameters = ChatCompletionParameters(messages: [.init(role: .user, content: .text(prompt))], model: .gpt4o) let chatCompletionObject = service.startChat(parameters: parameters)

Response

Chat completion chunk object

/// Represents a streamed chunk of a chat completion response returned by model, based on the provided input. public struct ChatCompletionChunkObject: Decodable {        /// A unique identifier for the chat completion chunk.    public let id: String    /// A list of chat completion choices. Can be more than one if n is greater than 1.    public let choices: [ChatChoice]    /// The Unix timestamp (in seconds) of when the chat completion chunk was created.    public let created: Int    /// The model to generate the completion.    public let model: String    /// The service tier used for processing the request. This field is only included if the service_tier parameter is specified in the request.    public let serviceTier: String?    /// This fingerprint represents the backend configuration that the model runs with.    /// Can be used in conjunction with the seed request parameter to understand when backend changes have been made that might impact determinism.    public let systemFingerprint: String?    /// The object type, which is always chat.completion.chunk.    public let object: String        public struct ChatChoice: Decodable {              /// A chat completion delta generated by streamed model responses.       public let delta: Delta       /// The reason the model stopped generating tokens. This will be stop if the model hit a natural stop point or a provided stop sequence, length if the maximum number of tokens specified in the request was reached, contentfilter if content was omitted due to a flag from our content filters, toolcalls if the model called a tool, or function_call (deprecated) if the model called a function.       public let finishReason: IntOrStringValue?       /// The index of the choice in the list of choices.       public let index: Int       /// Provided by the Vision API.       public let finishDetails: FinishDetails?              public struct Delta: Decodable {                    /// The contents of the chunk message.          public let content: String?          /// The tool calls generated by the model, such as function calls.          public let toolCalls: [ToolCall]?          /// The name and arguments of a function that should be called, as generated by the model.          @available(*, deprecated, message: "Deprecated and replaced by tool_calls")          public let functionCall: FunctionCall?          /// The role of the author of this message.          public let role: String?       }              public struct LogProb: Decodable {          /// A list of message content tokens with log probability information.          let content: [TokenDetail]       }              public struct TokenDetail: Decodable {          /// The token.          let token: String          /// The log probability of this token.          let logprob: Double          /// A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be null if there is no bytes representation for the token.          let bytes: [Int]?          /// List of the most likely tokens and their log probability, at this token position. In rare cases, there may be fewer than the number of requested top_logprobs returned.          let topLogprobs: [TopLogProb]                    enum CodingKeys: String, CodingKey {             case token, logprob, bytes             case topLogprobs = "top_logprobs"          }                    struct TopLogProb: Decodable {             /// The token.             let token: String             /// The log probability of this token.             let logprob: Double             /// A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be null if there is no bytes representation for the token.             let bytes: [Int]?          }       }              /// Provided by the Vision API.       public struct FinishDetails: Decodable {          let type: String       }    } }
Usage
let prompt = "Tell me a joke" let parameters = ChatCompletionParameters(messages: [.init(role: .user, content: .text(prompt))], model: .gpt4o) let chatCompletionObject = try await service.startStreamedChat(parameters: parameters)

Function Calling

Chat Completion also supports Function Calling and Parallel Function Calling. functions has been deprecated in favor of tools check OpenAI Documentation for more.

public struct ToolCall: Codable {

public let index: Int /// The ID of the tool call. public let id: String? /// The type of the tool. Currently, only function is supported. public let type: String? /// The function that the model called. 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 {

/// The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function. let arguments: String /// The name of the function to call. let name: String

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

Usage ``swift /// Define a ToolCall` var tool: ToolCall { .init( type: "function", // The typ


README truncated. View on GitHub

ยฉ 2026 GitRepoTrend ยท jamesrochabrun/SwiftOpenAI ยท Updated daily from GitHub