/OpenAIAPI

A Swift network client generated from the OpenAI OpenAPI definition.

Primary LanguageSwiftMIT LicenseMIT

Swift MIT license

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 the client can be 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 using swiftlint as a SPM plugin. As of today (25 March 2023) this is problematic:

  • The swiftlint dependency checkout is huge, which wastes time and CPU. I can deal with it because my CI is a self-hosted runner. An alternative is to generate a project with xcodegen and add a phase that uses a swiftlint installed with brew, or to write a plugin that uses a swiftlint binary (like this).

  • The current release (0.51.0-rc.2) ignores the excludes directive. This is fixed in main (see issue #4722) but requesting your plugin with branch: "main" has the unfortunate consequence of marking your package as unstable, which requires clients to be marked as unstable too (even if I tag OpenAPI as 1.0.1). What a mess. A workaround is to comment the swiftlint plugin in Package.swift, then uncommented in local, and ignore changes to the package (git update-index --skip-worktree Package.swift). This lets me lint locally without consequences for clients.

Links of interest