swift-openai-bits
Provides a Swift library to interact with the OpenAI API service.
Best efforts are done to keep it up-to-date with the full API. Class, function and variable names generally follow the conventions of the API, with some modifications made where the API is inconsistent (eg. where the "creation date" is sometimes created and sometimes createdAt, this library uses created for all of them.)
This library provides an async/await API for access, so requires being compiled with Swift 5.7+.
Requirements
Tools:
- Swift 5.7+
- Xcode 13+
Operating Systems
- macOS 12 or later
- iOS 15 or later
- tvOS 15 or later
- watchOS 8 or later
Installation
Add this to your project with Swift Package Manager.
Xcode
- Add a package via the
Project>[Your Project]>Package Dependencies - Enter
https://github.com/randombitsco/swift-openai-bitsfor the URL - Select
Up to next major versionwith the current version value, or selectBranch:and specifymain. - When prompted, add the
OpenAIBitstarget to your project.
Swift Package Manager
- Open the
Package.swiftfile. - Add the following to in the
Package:
dependencies: [
.package(url: "https://github.com/randombitsco/swift-openai-bits", from: "0.1.0"),
],- In
targets, add a dependency on the library to the relevant target:
.target(
name: "MyTarget",
dependencies: [
.product(name: "OpenAIBits", package: "swift-openai-bits"),
]),Usage
Basic useage requires importing OpenAIBits, and setting up an OpenAI instance, with an OpenAI API Key (and Organization Key, if relevant):
import OpenAIBits
let apiKey: String // your API key. Don't store this in code!
let openai = OpenAI(apiKey: apiKey)
// send a request to the API
let result = try await openai.call(...)Chat
Chat Completions allow you to provide a series of messages that form a chat history, and it will respond with a message based on that history. More information here.
let result: ChatCompletion = try await openai.call(Chat.Completions(
model = .gpt_3_5_turbo,
messages: .from(system: "You are a helpful assistant.")
.from(user: "Who won the world series in 2020?")
.from(assistant: "The Los Angeles Dodgers won the World Series in 2020.")
.from(user: "Where was it played?")
))
let message = result.message
print("Completion: \(message.role): \(message.content)")
print("[Used \(result.usage.totalTokens") tokens]")Text Completions
Completions are the core text generation call. Keep in mind it will use tokens from your account on every call. More information here.
let result: Completion = try await openai.call(Text.Completions(
model: .text_davinci_002,
prompt: "You complete",
maxTokens: 50,
temperature: 0.8
))
let choice = result.choices.first!
print("Completion: \(choice.text)")
print("[Used \(result.usage.totalTokens") tokens]")Text Edits
Edits allow you to provide a starting input and a an instruction, and it will return a new result based on the input, modified according to the instruction. More information here.
let result: Edit = try await openai.call(Text.Edits(
model: .text_davinci_002,
input: """
We is going to the market.
""",
instruction: "Fix the grammar."
))
let choice = result.choices.first!
print("Edit: \(choice.text)")
print("[Used \(result.usage.totalTokens") tokens]")Embeddings
An embedding is a special format of data representation that can be easily utilized by machine learning models and algorithms. More information here.
let result: Embedding = try await openai.call(Embeddings.Create(
model: "text-search-curie-query-001",
input: "Make an embedding of this.",
user: "jblogs"
))
let embedding: [Decimal]? = result.first?.embeddingFiles
Files can be uploaded to perform other tasks, or results of other task downloaded. More information here.
There are several files operations available (check API documentation for details).
Upload
Currently, the only official upload purpose is "fine-tune", although there are options for more deprecated options such as "search".
let fileUrl = URL(fileURLWithPath: "my-training.jsonl")
let file = try await openai.call(Files.Upload(
filename: "my-training.jsonl",
purpose: .fineTune,
url: fileUrl
))
print("File ID: \(file.id)")List
A list of all files attached to the organization, including both uploaded and generated files.
let files: ListOf<File> = try await openai.call(Files.List())
for file in files {
print("ID: \(file.id), Filename: \(file.filename)")
}Details
Details of any file ID.
let file: File = try await openai.call(Files.Detail(id: fileId)
print("Filename: \(file.filename), size: \(file.bytes) bytes")Content
Downloading requires a file ID, which can be retrieved from the list.
let response: BinaryResponse = try await openai.call(Files.Content(
id: file.id
))
print("filename: \(response.filename), size: \(response.data.count) bytes")Delete
Delete a file permanently.
let result: Files.Delete.Response = try await openai.call(Files.Delete(
id: fileToDelete
))
print("File ID: \(response.id), deleted: \(response.deleted)")Fine-Tunes
It is possible to fine-tune some models with additional details specific to your application. More details here.
There are several calls related to fine-tuning.
Create
You can create a fine-tuning job by first using the Files.Upload with a .fineTune purpose, then referencing that file ID here:
let file = try await openai.call(Files.Upload(purpose: .fineTune, url: fileUrl))
let fineTune: FineTune = openai.call(FineTunes.Create(
trainingFile: file.id,
model: .davinci
))
print("Fine Tune ID: \(fineTune.id)")List
Lists all previous fine-tuning jobs.
let fineTunes: ListOf<FineTune> = try await openai.call(FineTunes.List())
for fineTune in fineTunes {
print("ID: \(fineTune.id), Status: \(fineTune.status)")
}Detail
Retrieves the detail for a given fine-tune ID.
let fineTune: FineTune = try await openai.call(FineTunes.Detail(id: someId)
print("Fine Tune ID: \(fineTune.id)")Cancel
Fine-tune jobs may run for some time. You can cancel them if you wish.
let fineTune: FineTune = try await openai.call(FineTunes.Cancel(id: someId))
print("Fine Tune Status: \(fineTune.status")Events
Retrieves just the list of events for a fine-tune job.
let events: ListOf<FineTune.Event> = try await openai.call(FineTunes.Events(id: someId))
for event in events {
print("Created: \(event.created), Level: \(event.level), Message: \(event.message)")
}Delete
Deletes a fine-tuned model.
let response: FineTunes.Delete.Response = try await openai.call(FineTunes.Delete(id: someId))
print("Model ID: \(response.id), Deleted: \(response.deleted)")Images
The DALL-E 2 model can be be used to generate, edit, and make variations of images. More details here.
Create
This is the core image generator. Provide a text prompt and generate one or more images for it.
let result: Generations = try await openai.call(Images.Create(
prompt: "A koala riding a bicycle",
n: 4,
))
for image in result.images {
if case let .url(url) = image {
print("Image URL: \(url)")
}
}Edit
You can provide an image, a matching-size PNG with 100% transparent sections, and a prompt to describe what will be generated in the marked sections. It will produce 1-10 options based on the parameters.
let imageData: Data = ...
let maskData: Data = ...
let result: Generations = try await openai.call(Images.Edit(
image: imageData,
mask: maskData,
prompt: "A space rocket",
n: 6,
responseFormat: .data
))
for image in result.images {
if let .data(data) = image {
try data.write(to: ...)
}
}Variations
You provide an initial image, and it will generate 1 to 10 variations of the original image.
let imageData: Data = ...
let result: Generations = try await openai.call(Images.Variation(
image: imageData,
n: 10,
size: .of512x512,
responseFormat: .url
))
for image in result.images {
if case let .url(url) = image {
print("Image URL: \(url)")
}
}Models
Retrieve information about available models.
List
Gets a list of all models available to you/your organization, including fine-tuned models.
let models: ListOf<Model> = try await openai.call(Models.List())
for model in models {
print("Model ID: \(model.id)")
}Detail
Gets the details for a specific model.
let model: Model = try await openai.call(Models.Detail(
id: .text_davinci_002
))
print("Allow Fine-Tuning: \(model.allowFineTuning)")Delete
You can delete models created/owned by you, which in most cases will be fine-tuned models.
let result: Models.Delete.Result = try await openai.call(Models.Delete(id: ...))
print("Deleted \(result.id): \(result.deleted)")Moderations
OpenAI provides an endpoint to check if text content meets their usage policies. More details here.
Create
Creates a moderation check.
let promptValue: String = ...
let moderation: Moderation = try await openai.call(Moderations.Create(
input: promptValue
))
print("Flagged: \(moderation.results?.first.flagged ?? "false")")Tokens
Along side the OpenAI is the TokenEncoder. It is a struct that can be used and reused, with two methods: encode(text:) and decode(tokens:).
let encoder = TokenEncoder()
let tokens: [Token] = encoder.encoder(text: "A sentence.")
let text: String = encoder.decode(tokens: tokens)
print("Tokens: \(tokens)") // Tokens: [32, 6827, 13]
print("Text: \(text)") // Text: A sentence.You can also use the Tokens.Encode and Tokens.Decode Calls which can be sent to a OpenAI, if you prefer. All processing is done locally.
let openai = OpenAI(apiKey: ...)
let tokens: [Token] = try await openai.call(Tokens.Encode("A sentence."))
let text: String = try await openai.call(Tokens.Decode(tokens))
print("Tokens: \(tokens)") // Tokens: [32, 6827, 13]
print("Text: \(text)") // Text: A sentence.