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+.
- Swift 5.7+
- Xcode 13+
- macOS 12 or later
- iOS 15 or later
- tvOS 15 or later
- watchOS 8 or later
Add this to your project with Swift Package Manager.
- Add a package via the
Project
>[Your Project]
>Package Dependencies
- Enter
https://github.com/randombitsco/swift-openai-bits
for the URL - Select
Up to next major version
with the current version value, or selectBranch:
and specifymain
. - When prompted, add the
OpenAIBits
target to your project.
- Open the
Package.swift
file. - 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"),
]),
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 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]")
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]")
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]")
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?.embedding
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).
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)")
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 of any file ID.
let file: File = try await openai.call(Files.Detail(id: fileId)
print("Filename: \(file.filename), size: \(file.bytes) bytes")
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 a file permanently.
let result: Files.Delete.Response = try await openai.call(Files.Delete(
id: fileToDelete
))
print("File ID: \(response.id), deleted: \(response.deleted)")
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.
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)")
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)")
}
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)")
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")
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)")
}
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)")
The DALL-E 2 model can be be used to generate, edit, and make variations of images. More details here.
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)")
}
}
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: ...)
}
}
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)")
}
}
Retrieve information about available models.
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)")
}
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)")
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)")
OpenAI provides an endpoint to check if text content meets their usage policies. More details here.
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")")
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
Call
s 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.