A Swift network client generated from the OpenAI OpenAPI definition

OpenAIAPI

A Swift network client generated from the OpenAI OpenAPI definition.

OpenAPI is a definition language to describe RESTful APIs like the one from OpenAPI. By using a code generator we are able to keep our client updated and documented with minimal effort. CreateAPI is one of such generators for the Swift language.

Installation

let package = Package(
    ...
    dependencies: [
        .package(url: "git@github.com:OpenAIAPI/OpenAIAPI.git", from: "1.0.1")
    ],
    targets: [
        .target(
            name: "...",
            dependencies: [
                .product(name: "OpenAIAPI", package: "OpenAIAPI")
            ]
        )
    ]
)

Usage

To send a message:

let prompt = "hello there chatgpt!"
let api = APIClient(baseURL: URL(string: "https://api.openai.com/v1")!)
let completionRequest = CreateCompletionRequest(
    model: "text-ada-001",
    prompt: CreateCompletionRequest.Prompt.string(prompt),
    suffix: nil,
    maxTokens: 4_000,
    temperature: 0.7
)
var request = Paths.completions.post(completionRequest)
request.headers = ["Authorization": "Bearer YOUR_API_KEY"]
let completionResponse = try await api.send(request).value

The generated client doesn’t support Server Side Events or multipart encoding, so the client doesn’t support SSE or file upload/download on its own. If god is willing I’ll be adding support for this in the coming days.

Regenerate the client

When OpenAI updates their OpenAPI definition you have to:

  1. Install brew
  2. Install CreateAPI: brew install create-api
  3. Run make: make

Make will

  • download the current definition
  • apply the patch FineTune.patch. This avoids a decoding error by changing FineTune.hyperparams from [String: AnyJSON] to UnknownDictionary.swift
  • regenerate the client

Most likely, no manual changes will be needed.

Need Get 2.1.6?

The SPM package generated depends on Get v1.0.2, and URLQueryEncoder 0.2.0. Shall you ever need Get 2.1.6 and URLQueryEncoder 0.2.1, you may fork Get and add the file below to it.

file: Request+Ext.swift
import Foundation

public extension Request {
    init(
        method: HTTPMethod = .get,
        url: String,
        query: [(String, String?)]? = nil,
        body: Encodable? = nil,
        headers: [String: String]? = nil,
        id: String? = nil
    ) {
        self.init(
            path: url,
            method: method,
            query: query,
            body: body,
            headers: headers,
            id: id
        )
    }
}

About swiftlint

I’m linting thanks to the swiftlint SPM plugin. This works for me because if you look at the .github/workflow folder I’m running self-hosted github runner. In case you pay for a CI, the swiftlint checkout will take a very long time. A more involved alternative is using Swift Package Plugins.

Another issue is that the current release (0.51.0-rc.2) doesn’t contain this fix to honor the excludes directive of .swiftlint.yml. This has the unfortunate consequence of marking your package as unstable. That’s why I added a tab 1.0.1 to the same content. Otherwise people won’t be able to link to 1.0.0 unless their package is unstable too.

Links of interest

GitHub

View Github