Code Monkey home page Code Monkey logo

openai-kit's Introduction

OpenAIKit

Swift

OpenAIKit is a Swift package used to communicate with the OpenAI API.

Setup

Add the dependency to Package.swift:

dependencies: [
    ...
    .package(url: "https://github.com/dylanshine/openai-kit.git", from: "1.0.0")
],
targets: [
    .target(name: "App", dependencies: [
        .product(name: "OpenAIKit", package: "openai-kit"),
    ]),

It is encouraged to use environment variables to inject the OpenAI API key, instead of hardcoding it in the source code.

# .env

OPENAI_API_KEY="YOUR-API-KEY"
OPENAI_ORGANIZATION="YOUR-ORGANIZATION"

โš ๏ธ OpenAI strongly recommends developers of client-side applications proxy requests through a separate backend service to keep their API key safe. API keys can access and manipulate customer billing, usage, and organizational data, so it's a significant risk to expose them.

Create a OpenAIKit.Client by passing a configuration.

var apiKey: String {
    ProcessInfo.processInfo.environment["OPENAI_API_KEY"]!
}

var organization: String {
    ProcessInfo.processInfo.environment["OPENAI_ORGANIZATION"]!
}

...

// Generally we would advise on creating a single HTTPClient for the lifecycle of your application and recommend shutting it down on application close.

let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)

let httpClient = HTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup))

defer {
    // it's important to shutdown the httpClient after all requests are done, even if one failed. See: https://github.com/swift-server/async-http-client
    try? httpClient.syncShutdown()
}

let configuration = Configuration(apiKey: apiKey, organization: organization)

let openAIClient = OpenAIKit.Client(httpClient: httpClient, configuration: configuration)

If you don't want to use SwiftNIO you can use URLSession.

let urlSession = URLSession(configuration: .default)
let configuration = Configuration(apiKey: apiKey, organization: organization)
let openAIClient = OpenAIKit.Client(session: urlSession, configuration: configuration)

Using the API

The OpenAIKit.Client implements a handful of methods to interact with the OpenAI API:

import OpenAIKit

let completion = try await openAIClient.completions.create(
    model: Model.GPT3.davinci,
    prompts: ["Write a haiku"]
)

What's Implemented

Error handling

If the request to the API failed for any reason an OpenAIKit.APIErrorResponse is thrown. Simply ensure you catch errors thrown like any other throwing function

do {
   ...
} catch let error as APIErrorResponse {
    print(error)
}

openai-kit's People

Contributors

arthurgarzajr avatar dylanshine avatar fjcaetano avatar ftp27 avatar hyouuu avatar joshgalvan avatar jsky74 avatar pabraiventures avatar ronaldmannak avatar ryu0118 avatar sulsanaul avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

openai-kit's Issues

Making AsyncHTTPClient an implementation detail? Supporting URLSession...

@ronaldmannak I was thinking based on your last contribution, that it might make sense to restructure the use of AsyncHTTPClient to clean up usage. I was also thinking it might make sense to potentially allow passing either an AsyncHTTPClient or URLSession in order to perform the requests. Haven't really sat down to think this through, but I'd be curious to hear your thoughts.

request handler error

It seems the response object is not mapping to the codable 'Completion' object properly:

I'm getting this error:

keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"id\", intValue: nil) (\"id\").", underlyingError: nil))

There's no way to use a background session to make Chat requests

Using a background session prevents Chat requests from failing if the app goes to background. The extension on URLSession uses dataTask with completionHandler, creating an exception:

    func dataTask(with request: URLRequest, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTaskProtocol {
        dataTask(with: request, completionHandler: completionHandler) as URLSessionDataTask
    }

*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'

One way to fix this issue is to save an array of continuations, use downloadTask and expose a way to send the URLSession configuration so we can set a delegate an control downloads:

    extension OpenAIService: URLSessionDownloadDelegate {
          func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
               guard let data = try? Data(contentsOf: location) else {
               continuations[downloadTask]?.resume(throwing: OpenAIServiceError.errorRequestingPrompt)
               return
            }
          continuations[downloadTask]?.resume(returning: data)
          continuations[downloadTask] = nil
        }
    }

    private lazy var backgrounUrlSession: URLSession = {
        let config = URLSessionConfiguration.background(withIdentifier: "com.mumme.fanart.openai")
        config.sessionSendsLaunchEvents = true
        config.shouldUseExtendedBackgroundIdleMode = true
        return URLSession(configuration: config, delegate: self, delegateQueue: nil)
    }()

    var continuations = [URLSessionTask: CheckedContinuation<Data, Error>]()

    func task(for request: URLRequest) async throws -> Data {
        try await withCheckedThrowingContinuation { continuation in
            let downloadTask = backgroundSession.downloadTask(with: request)
            continuations[downloadTask] = continuation
            downloadTask.resume()
        }
    }

WARNING: Hang Risk on setting up HTTP client

Hi, thanks for the good work on the library!

When I setup the http client as mentioned in your docs, i.e.:

let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let httpClient = HTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup))

I receive the following warning:

Hang Risk
Thread running at QOS_CLASS_USER_INTERACTIVE waiting on a lower QoS thread running at QOS_CLASS_DEFAULT. Investigate ways to avoid priority inversions

Any help on how to fix this would be appreciated!

Example for usage of openai-kit

@dylanshine This seems to be nice library for integration of OpenAI. I m here just suggesting if you can include Example of how to use this. e.g. usage with env variables and usage without env variables and demonstrating usage of basic classes.

Cannt find HTTPClient in scope

Hello I tried downloading your package via SPM and everything seems to work fine except it not recognizing HTTPClient. Was there another dependency that I need to install?
Screenshot 2023-03-01 at 10 37 04 PM

Fine-tunes

Is anyone working on the fine-tunes endpoints?

Crash in RequestHandler stream(request:)

After cancelling a Task, this preconditionFailure is triggered:

preconditionFailure(
"Tried to use a second iterator on response body stream. Multiple iterators are not supported.",
file: file, line: line
)

Workaround is to only call continuation.finish(throwing: error) in the catch block of stream(request:) in RequestHandler and remove this:
let data = try? await response.body.reduce(into: Data()) { $0.append(.init(buffer: $1)) }

Not being maintained?

Hello,
Is this repo being maintained? Not seeing a release since June.
I'm wanting to use the latest speech endpoint, but I'm not seeing that exposed.
Should I be going a different direction/forking this repo, or will support be coming soon?

Thanks!
David

Stream fails the first time it's called

Client.chats.stream() fails the first time it's called. An empty stream is returned and the subsequent for try await chat in stream { ... } will never loop.

However, if Client.chats.create() is invoked before the first Client.chats.stream(), then the first Client.chats.stream() will succeed.

keyNotFound

After adding the package I copied the below code and added the function call inside a

func data() {
Task{
do {
let completion = try await openAIClient.completions.create(
model: Model.GPT3.textAda001,
prompts: ["Write a haiku"])
} catch {
print("Error")
}
}
}

After it triggered, Im the getting this error in the console:
keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "id", intValue: nil) ("id").", underlyingError: nil))

Path prefix for proxy

I've made a simple golang proxy server for OpenAI and faced unavailability with defining prefix path for API structure. For example, in my server, I have auth path:

  • proxy.com/v1/auth

and API paths

  • proxy.com/proxy/openai/
  • proxy.com/proxy/new-api/

So we need to set up an API prefix /proxy/openai to use the framework. I already made the necessary changes to support the feature, but I think better to start with creating an issue instead of a push request.

ChatGPT plug-ins

Has anyone looked at the ChatGPT plug-ins specs? Would it be within the scope of this project to add basic support for plug-ins?

Support for gpt-4

Documentation states that gpt-4 can be used via the API (there's still a waiting list so it's worth mentioning that in the code as a comment).

Whisper API?

This isn't an issue, but will the Whisper API be added soon?

In file upload is fine-tune the only working option?

I wanted to upload files to search or write prompts for them but it seems that the only option available is fine tuning. I am currently using my $18 credits and wonder if the options for search, answers and classification are only available for paying users.

Has someone have had success with file upload for options other than fine tuning?

concurrency crash when not connected to Xcode debugger

I am receiving EXC_BREAKPOINT

It does not occur when the App is connected to Xcode debugger but when attempting to run under normal conditions on an iPhone iOS 16.2 that is disconnected from Xcode 14.2. The app is a SwiftUI interface and the fetch function is stored in a Class Object which is run on presentation of a view as follows:

 .task {
            print("\(#file):\(#function):\(#line)")
            appObservableObject.fetch()
        }

class AppObservableObject: ObservableObject {
.....
.....
    @MainActor
    func fetch(){
        Task {
            do {
                let completion = try await openAIClient.completions.create(
                model: Model.GPT3.davinci,
                prompts: ["Write a haiku"])
                self.response = String(completion.choices.first!.text)
            }catch let error {
                self.response = error.localizedDescription
            }
        }
   }
}

CRASH:

Thread 2 Crashed:
0   libswiftCore.dylib            	       0x1c32ddac4 closure #1 in closure #1 in closure #1 in _assertionFailure(_:_:file:line:flags:) + 360
1   libswiftCore.dylib            	       0x1c32ddac4 closure #1 in closure #1 in closure #1 in _assertionFailure(_:_:file:line:flags:) + 360
2   libswiftCore.dylib            	       0x1c32dd828 closure #1 in closure #1 in _assertionFailure(_:_:file:line:flags:) + 196
3   libswiftCore.dylib            	       0x1c32dd630 closure #1 in _assertionFailure(_:_:file:line:flags:) + 208
4   libswiftCore.dylib            	       0x1c32dd188 _assertionFailure(_:_:file:line:flags:) + 232
5   My App Name Here       	       0x100725478 apiKey.getter + 388
6   My App Name Here       	       0x1007258b0 one-time initialization function for configuration + 36
7   libdispatch.dylib             	       0x1d0ae0fdc _dispatch_client_callout + 20
8   libdispatch.dylib             	       0x1d0ae2828 _dispatch_once_callout + 32
9   My App Name Here       	       0x100725940 configuration.unsafeMutableAddressor + 76
10  My App Name Here       	       0x100725968 one-time initialization function for openAIClient + 36
11  libdispatch.dylib             	       0x1d0ae0fdc _dispatch_client_callout + 20
12  libdispatch.dylib             	       0x1d0ae2828 _dispatch_once_callout + 32
13  My App Name Here       	       0x100725a2c openAIClient.unsafeMutableAddressor + 76
14  My App Name Here       	       0x100723394 sendRequest(prompt:) + 780
15  My App Name Here       	       0x10071c1f9 AppModelObject.fetch() + 1
16  My App Name Here       	       0x100727a65 closure #1 in closure #1 in AppName.body.getter + 1
17  My App Name Here       	       0x10072b945 partial apply for closure #1 in closure #1 in AppName.body.getter + 1
18  SwiftUI                       	       0x1ce0e4171 0x1cce85000 + 19263857
19  SwiftUI                       	       0x1ce0eb491 0x1cce85000 + 19293329
20  SwiftUI                       	       0x1cd768161 0x1cce85000 + 9318753
21  SwiftUI                       	       0x1cd7681c5 0x1cce85000 + 9318853
22  libswift_Concurrency.dylib    	       0x1d39073a1 completeTaskWithClosure(swift::AsyncContext*, swift::SwiftError*) + 1

codable error "id"

"No value associated with key CodingKeys(stringValue: "id", intValue: nil) ("id")."

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.