Giter Site home page Giter Site logo

openai's Introduction

OpenAI

logo


Swift Workflow Twitter

This repository contains Swift community-maintained implementation over OpenAI public API.

What is OpenAI

OpenAI is a non-profit artificial intelligence research organization founded in San Francisco, California in 2015. It was created with the purpose of advancing digital intelligence in ways that benefit humanity as a whole and promote societal progress. The organization strives to develop AI (Artificial Intelligence) programs and systems that can think, act and adapt quickly on their own – autonomously. OpenAI's mission is to ensure safe and responsible use of AI for civic good, economic growth and other public benefits; this includes cutting-edge research into important topics such as general AI safety, natural language processing, applied reinforcement learning methods, machine vision algorithms etc.

The OpenAI API can be applied to virtually any task that involves understanding or generating natural language or code. We offer a spectrum of models with different levels of power suitable for different tasks, as well as the ability to fine-tune your own custom models. These models can be used for everything from content generation to semantic search and classification.

Installation

OpenAI is available with Swift Package Manager. The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the swift compiler. Once you have your Swift package set up, adding OpenAI as a dependency is as easy as adding it to the dependencies value of your Package.swift.

dependencies: [
    .package(url: "https://github.com/MacPaw/OpenAI.git", branch: "main")
]

Usage

Initialization

To initialize API instance you need to obtain API token from your Open AI organization.

Remember that your API key is a secret! Do not share it with others or expose it in any client-side code (browsers, apps). Production requests must be routed through your own backend server where your API key can be securely loaded from an environment variable or key management service.

company

Once you have a token, you can initialize OpenAI class, which is an entry point to the API.

⚠️ 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.

let openAI = OpenAI(apiToken: "YOUR_TOKEN_HERE")

Optionally you can initialize OpenAI with token, organization identifier and timeoutInterval.

let configuration = OpenAI.Configuration(token: "YOUR_TOKEN_HERE", organizationIdentifier: "YOUR_ORGANIZATION_ID_HERE", timeoutInterval: 60.0)
let openAI = OpenAI(configuration: configuration)

Once token you posses the token, and the instance is initialized you are ready to make requests.

Completions

Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position.

Request

struct CompletionsQuery: Codable {
    /// ID of the model to use.
    public let model: Model
    /// The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays.
    public let prompt: String
    /// What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer.
    public let temperature: Double?
    /// The maximum number of tokens to generate in the completion.
    public let maxTokens: Int?
    /// An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
    public let topP: Double?
    /// Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.
    public let frequencyPenalty: Double?
    /// Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.
    public let presencePenalty: Double?
    /// Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence.
    public let stop: [String]?
    /// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.
    public let user: String?
}

Response

struct CompletionsResult: Codable, Equatable {
    public struct Choice: Codable, Equatable {
        public let text: String
        public let index: Int
    }

    public let id: String
    public let object: String
    public let created: TimeInterval
    public let model: Model
    public let choices: [Choice]
    public let usage: Usage
}

Example

let query = CompletionsQuery(model: .textDavinci_003, prompt: "What is 42?", temperature: 0, maxTokens: 100, topP: 1, frequencyPenalty: 0, presencePenalty: 0, stop: ["\\n"])
openAI.completions(query: query) { result in
  //Handle result here
}
//or
let result = try await openAI.completions(query: query)
(lldb) po result
▿ CompletionsResult
  - id : "cmpl-6P9be2p2fQlwB7zTOl0NxCOetGmX3"
  - object : "text_completion"
  - created : 1671453146.0
  - model : OpenAI.Model.textDavinci_003
  ▿ choices : 1 element
    ▿ 0 : Choice
      - text : "\n\n42 is the answer to the ultimate question of life, the universe, and everything, according to the book The Hitchhiker\'s Guide to the Galaxy."
      - index : 0

Completions Streaming

Completions streaming is available by using completionsStream function. Tokens will be sent one-by-one.

Closures

openAI.completionsStream(query: query) { partialResult in
    switch partialResult {
    case .success(let result):
        print(result.choices)
    case .failure(let error):
        //Handle chunk error here
    }
} completion: { error in
    //Handle streaming error here
}

Combine

openAI
    .completionsStream(query: query)
    .sink { completion in
        //Handle completion result here
    } receiveValue: { result in
        //Handle chunk here
    }.store(in: &cancellables)

Structured concurrency

for try await result in openAI.completionsStream(query: query) {
   //Handle result here
}

Review Completions Documentation for more info.

Chats

Using the OpenAI Chat API, you can build your own applications with gpt-3.5-turbo to do things like:

  • Draft an email or other piece of writing
  • Write Python code
  • Answer questions about a set of documents
  • Create conversational agents
  • Give your software a natural language interface
  • Tutor in a range of subjects
  • Translate languages
  • Simulate characters for video games and much more

Request

 struct ChatQuery: Codable {
     /// ID of the model to use. Currently, only gpt-3.5-turbo and gpt-3.5-turbo-0301 are supported.
     public let model: Model
     /// The messages to generate chat completions for
     public let messages: [Chat]
     /// A list of functions the model may generate JSON inputs for.
     public let functions: [ChatFunctionDeclaration]?
     /// What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and  We generally recommend altering this or top_p but not both.
     public let temperature: Double?
     /// An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
     public let topP: Double?
     /// How many chat completion choices to generate for each input message.
     public let n: Int?
     /// Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence.
     public let stop: [String]?
     /// The maximum number of tokens to generate in the completion.
     public let maxTokens: Int?
     /// Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.
     public let presencePenalty: Double?
     /// Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.
     public let frequencyPenalty: Double?
     ///Modify the likelihood of specified tokens appearing in the completion.
     public let logitBias: [String:Int]?
     /// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.
     public let user: String?
}

Response

struct ChatResult: Codable, Equatable {
    public struct Choice: Codable, Equatable {
        public let index: Int
        public let message: Chat
        public let finishReason: String
    }
    
    public struct Usage: Codable, Equatable {
        public let promptTokens: Int
        public let completionTokens: Int
        public let totalTokens: Int
    }
    
    public let id: String
    public let object: String
    public let created: TimeInterval
    public let model: Model
    public let choices: [Choice]
    public let usage: Usage
}

Example

let query = ChatQuery(model: .gpt3_5Turbo, messages: [.init(role: .user, content: "who are you")])
let result = try await openAI.chats(query: query)
(lldb) po result
▿ ChatResult
  - id : "chatcmpl-6pwjgxGV2iPP4QGdyOLXnTY0LE3F8"
  - object : "chat.completion"
  - created : 1677838528.0
  - model : "gpt-3.5-turbo-0301"
  ▿ choices : 1 element
    ▿ 0 : Choice
      - index : 0
      ▿ message : Chat
        - role : "assistant"
        - content : "\n\nI\'m an AI language model developed by OpenAI, created to provide assistance and support for various tasks such as answering questions, generating text, and providing recommendations. Nice to meet you!"
      - finish_reason : "stop"
  ▿ usage : Usage
    - prompt_tokens : 10
    - completion_tokens : 39
    - total_tokens : 49

Chats Streaming

Chats streaming is available by using chatStream function. Tokens will be sent one-by-one.

Closures

openAI.chatsStream(query: query) { partialResult in
    switch partialResult {
    case .success(let result):
        print(result.choices)
    case .failure(let error):
        //Handle chunk error here
    }
} completion: { error in
    //Handle streaming error here
}

Combine

openAI
    .chatsStream(query: query)
    .sink { completion in
        //Handle completion result here
    } receiveValue: { result in
        //Handle chunk here
    }.store(in: &cancellables)

Structured concurrency

for try await result in openAI.chatsStream(query: query) {
   //Handle result here
}

Function calls

let openAI = OpenAI(apiToken: "...")
// Declare functions which GPT-3 might decide to call.
let functions = [
  ChatFunctionDeclaration(
      name: "get_current_weather",
      description: "Get the current weather in a given location",
      parameters:
        JSONSchema(
          type: .object,
          properties: [
            "location": .init(type: .string, description: "The city and state, e.g. San Francisco, CA"),
            "unit": .init(type: .string, enumValues: ["celsius", "fahrenheit"])
          ],
          required: ["location"]
        )
  )
]
let query = ChatQuery(
  model: "gpt-3.5-turbo-0613",  // 0613 is the earliest version with function calls support.
  messages: [
      Chat(role: .user, content: "What's the weather like in Boston?")
  ],
  functions: functions
)
let result = try await openAI.chats(query: query)

Result will be (serialized as JSON here for readability):

{
  "id": "chatcmpl-1234",
  "object": "chat.completion",
  "created": 1686000000,
  "model": "gpt-3.5-turbo-0613",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "function_call": {
          "name": "get_current_weather",
          "arguments": "{\n  \"location\": \"Boston, MA\"\n}"
        }
      },
      "finish_reason": "function_call"
    }
  ],
  "usage": { "total_tokens": 100, "completion_tokens": 18, "prompt_tokens": 82 }
}

Review Chat Documentation for more info.

Images

Given a prompt and/or an input image, the model will generate a new image.

As Artificial Intelligence continues to develop, so too does the intriguing concept of Dall-E. Developed by OpenAI, a research lab for artificial intelligence purposes, Dall-E has been classified as an AI system that can generate images based on descriptions provided by humans. With its potential applications spanning from animation and illustration to design and engineering - not to mention the endless possibilities in between - it's easy to see why there is such excitement over this new technology.

Create Image

Request

struct ImagesQuery: Codable {
    /// A text description of the desired image(s). The maximum length is 1000 characters.
    public let prompt: String
    /// The number of images to generate. Must be between 1 and 10.
    public let n: Int?
    /// The size of the generated images. Must be one of 256x256, 512x512, or 1024x1024.
    public let size: String?
}

Response

struct ImagesResult: Codable, Equatable {
    public struct URLResult: Codable, Equatable {
        public let url: String
    }
    public let created: TimeInterval
    public let data: [URLResult]
}

Example

let query = ImagesQuery(prompt: "White cat with heterochromia sitting on the kitchen table", n: 1, size: "1024x1024")
openAI.images(query: query) { result in
  //Handle result here
}
//or
let result = try await openAI.images(query: query)
(lldb) po result
▿ ImagesResult
  - created : 1671453505.0
  ▿ data : 1 element
    ▿ 0 : URLResult
      - url : "https://oaidalleapiprodscus.blob.core.windows.net/private/org-CWjU5cDIzgCcVjq10pp5yX5Q/user-GoBXgChvLBqLHdBiMJBUbPqF/img-WZVUK2dOD4HKbKwW1NeMJHBd.png?st=2022-12-19T11%3A38%3A25Z&se=2022-12-19T13%3A38%3A25Z&sp=r&sv=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2022-12-19T09%3A35%3A16Z&ske=2022-12-20T09%3A35%3A16Z&sks=b&skv=2021-08-06&sig=mh52rmtbQ8CXArv5bMaU6lhgZHFBZz/ePr4y%2BJwLKOc%3D"

Generated image

Generated Image

Create Image Edit

Creates an edited or extended image given an original image and a prompt.

Request

public struct ImageEditsQuery: Codable {
    /// The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask is not provided, image must have transparency, which will be used as the mask.
    public let image: Data
    public let fileName: String
    /// An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where image should be edited. Must be a valid PNG file, less than 4MB, and have the same dimensions as image.
    public let mask: Data?
    public let maskFileName: String?
    /// A text description of the desired image(s). The maximum length is 1000 characters.
    public let prompt: String
    /// The number of images to generate. Must be between 1 and 10.
    public let n: Int?
    /// The size of the generated images. Must be one of 256x256, 512x512, or 1024x1024.
    public let size: String?
}

Response

Uses the ImagesResult response similarly to ImagesQuery.

Example

let data = image.pngData()
let query = ImageEditQuery(image: data, fileName: "whitecat.png", prompt: "White cat with heterochromia sitting on the kitchen table with a bowl of food", n: 1, size: "1024x1024")
openAI.imageEdits(query: query) { result in
  //Handle result here
}
//or
let result = try await openAI.imageEdits(query: query)

Create Image Variation

Creates a variation of a given image.

Request

public struct ImageVariationsQuery: Codable {
    /// The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask is not provided, image must have transparency, which will be used as the mask.
    public let image: Data
    public let fileName: String
    /// The number of images to generate. Must be between 1 and 10.
    public let n: Int?
    /// The size of the generated images. Must be one of 256x256, 512x512, or 1024x1024.
    public let size: String?
}

Response

Uses the ImagesResult response similarly to ImagesQuery.

Example

let data = image.pngData()
let query = ImageVariationQuery(image: data, fileName: "whitecat.png", n: 1, size: "1024x1024")
openAI.imageVariations(query: query) { result in
  //Handle result here
}
//or
let result = try await openAI.imageVariations(query: query)

Review Images Documentation for more info.

Audio

The speech to text API provides two endpoints, transcriptions and translations, based on our state-of-the-art open source large-v2 Whisper model. They can be used to:

Transcribe audio into whatever language the audio is in. Translate and transcribe the audio into english. File uploads are currently limited to 25 MB and the following input file types are supported: mp3, mp4, mpeg, mpga, m4a, wav, and webm.

Audio Create Speech

This function sends an AudioSpeechQuery to the OpenAI API to create audio speech from text using a specific voice and format.

Learn more about voices.
Learn more about models.

Request:

public struct AudioSpeechQuery: Codable, Equatable {
    //...
    public let model: Model // tts-1 or tts-1-hd  
    public let input: String
    public let voice: AudioSpeechVoice
    public let responseFormat: AudioSpeechResponseFormat
    public let speed: String? // Initializes with Double?
    //...
}

Response:

/// Audio data for one of the following formats :`mp3`, `opus`, `aac`, `flac`
public let audioData: Data?

Example:

let query = AudioSpeechQuery(model: .tts_1, input: "Hello, world!", voice: .alloy, responseFormat: .mp3, speed: 1.0)

openAI.audioCreateSpeech(query: query) { result in
    // Handle response here
}
//or
let result = try await openAI.audioCreateSpeech(query: query)

OpenAI Create Speech – Documentation

Audio Transcriptions

Transcribes audio into the input language.

Request

public struct AudioTranscriptionQuery: Codable, Equatable {
    
    public let file: Data
    public let fileName: String
    public let model: Model
    
    public let prompt: String?
    public let temperature: Double?
    public let language: String?
}

Response

public struct AudioTranscriptionResult: Codable, Equatable {
    
    public let text: String
}

Example

let data = Data(contentsOfURL:...)
let query = AudioTranscriptionQuery(file: data, fileName: "audio.m4a", model: .whisper_1)        

openAI.audioTranscriptions(query: query) { result in
    //Handle result here
}
//or
let result = try await openAI.audioTranscriptions(query: query)

Audio Translations

Translates audio into into English.

Request

public struct AudioTranslationQuery: Codable, Equatable {
    
    public let file: Data
    public let fileName: String
    public let model: Model
    
    public let prompt: String?
    public let temperature: Double?
}    

Response

public struct AudioTranslationResult: Codable, Equatable {
    
    public let text: String
}

Example

let data = Data(contentsOfURL:...)
let query = AudioTranslationQuery(file: data, fileName: "audio.m4a", model: .whisper_1)  

openAI.audioTranslations(query: query) { result in
    //Handle result here
}
//or
let result = try await openAI.audioTranslations(query: query)

Review Audio Documentation for more info.

Edits

Creates a new edit for the provided input, instruction, and parameters.

Request

struct EditsQuery: Codable {
    /// ID of the model to use.
    public let model: Model
    /// Input text to get embeddings for.
    public let input: String?
    /// The instruction that tells the model how to edit the prompt.
    public let instruction: String
    /// The number of images to generate. Must be between 1 and 10.
    public let n: Int?
    /// What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer.
    public let temperature: Double?
    /// An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
    public let topP: Double?
}

Response

struct EditsResult: Codable, Equatable {
    
    public struct Choice: Codable, Equatable {
        public let text: String
        public let index: Int
    }

    public struct Usage: Codable, Equatable {
        public let promptTokens: Int
        public let completionTokens: Int
        public let totalTokens: Int
        
        enum CodingKeys: String, CodingKey {
            case promptTokens = "prompt_tokens"
            case completionTokens = "completion_tokens"
            case totalTokens = "total_tokens"
        }
    }
    
    public let object: String
    public let created: TimeInterval
    public let choices: [Choice]
    public let usage: Usage
}

Example

let query = EditsQuery(model: .gpt4, input: "What day of the wek is it?", instruction: "Fix the spelling mistakes")
openAI.edits(query: query) { result in
  //Handle response here
}
//or
let result = try await openAI.edits(query: query)

Review Edits Documentation for more info.

Embeddings

Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms.

Request

struct EmbeddingsQuery: Codable {
    /// ID of the model to use.
    public let model: Model
    /// Input text to get embeddings for
    public let input: String
}

Response

struct EmbeddingsResult: Codable, Equatable {

    public struct Embedding: Codable, Equatable {

        public let object: String
        public let embedding: [Double]
        public let index: Int
    }
    public let data: [Embedding]
    public let usage: Usage
}

Example

let query = EmbeddingsQuery(model: .textSearchBabbageDoc, input: "The food was delicious and the waiter...")
openAI.embeddings(query: query) { result in
  //Handle response here
}
//or
let result = try await openAI.embeddings(query: query)
(lldb) po result
▿ EmbeddingsResult
  ▿ data : 1 element
    ▿ 0 : Embedding
      - object : "embedding"
      ▿ embedding : 2048 elements
        - 0 : 0.0010535449
        - 1 : 0.024234328
        - 2 : -0.0084999
        - 3 : 0.008647452
    .......
        - 2044 : 0.017536353
        - 2045 : -0.005897616
        - 2046 : -0.026559394
        - 2047 : -0.016633155
      - index : 0

(lldb)

Review Embeddings Documentation for more info.

Models

Models are represented as a typealias typealias Model = String.

public extension Model {
    static let gpt4_turbo_preview = "gpt-4-turbo-preview"
    static let gpt4_vision_preview = "gpt-4-vision-preview"
    static let gpt4_0125_preview = "gpt-4-0125-preview"
    static let gpt4_1106_preview = "gpt-4-1106-preview"
    static let gpt4 = "gpt-4"
    static let gpt4_0613 = "gpt-4-0613"
    static let gpt4_0314 = "gpt-4-0314"
    static let gpt4_32k = "gpt-4-32k"
    static let gpt4_32k_0613 = "gpt-4-32k-0613"
    static let gpt4_32k_0314 = "gpt-4-32k-0314"
    
    static let gpt3_5Turbo = "gpt-3.5-turbo"
    static let gpt3_5Turbo_0125 = "gpt-3.5-turbo-0125"
    static let gpt3_5Turbo_1106 = "gpt-3.5-turbo-1106"
    static let gpt3_5Turbo_0613 = "gpt-3.5-turbo-0613"
    static let gpt3_5Turbo_0301 = "gpt-3.5-turbo-0301"
    static let gpt3_5Turbo_16k = "gpt-3.5-turbo-16k"
    static let gpt3_5Turbo_16k_0613 = "gpt-3.5-turbo-16k-0613"
    
    static let textDavinci_003 = "text-davinci-003"
    static let textDavinci_002 = "text-davinci-002"
    static let textCurie = "text-curie-001"
    static let textBabbage = "text-babbage-001"
    static let textAda = "text-ada-001"
    
    static let textDavinci_001 = "text-davinci-001"
    static let codeDavinciEdit_001 = "code-davinci-edit-001"
    
    static let tts_1 = "tts-1"
    static let tts_1_hd = "tts-1-hd"
    
    static let whisper_1 = "whisper-1"

    static let dall_e_2 = "dall-e-2"
    static let dall_e_3 = "dall-e-3"
    
    static let davinci = "davinci"
    static let curie = "curie"
    static let babbage = "babbage"
    static let ada = "ada"
    
    static let textEmbeddingAda = "text-embedding-ada-002"
    static let textSearchAda = "text-search-ada-doc-001"
    static let textSearchBabbageDoc = "text-search-babbage-doc-001"
    static let textSearchBabbageQuery001 = "text-search-babbage-query-001"
    static let textEmbedding3 = "text-embedding-3-small"
    static let textEmbedding3Large = "text-embedding-3-large"
    
    static let textModerationStable = "text-moderation-stable"
    static let textModerationLatest = "text-moderation-latest"
    static let moderation = "text-moderation-007"
}

GPT-4 models are supported.

As an example: To use the gpt-4-turbo-preview model, pass .gpt4_turbo_preview as the parameter to the ChatQuery init.

let query = ChatQuery(model: .gpt4_turbo_preview, messages: [
    .init(role: .system, content: "You are Librarian-GPT. You know everything about the books."),
    .init(role: .user, content: "Who wrote Harry Potter?")
])
let result = try await openAI.chats(query: query)
XCTAssertFalse(result.choices.isEmpty)

You can also pass a custom string if you need to use some model, that is not represented above.

List Models

Lists the currently available models.

Response

public struct ModelsResult: Codable, Equatable {
    
    public let data: [ModelResult]
    public let object: String
}

Example

openAI.models() { result in
  //Handle result here
}
//or
let result = try await openAI.models()

Retrieve Model

Retrieves a model instance, providing ownership information.

Request

public struct ModelQuery: Codable, Equatable {
    
    public let model: Model
}    

Response

public struct ModelResult: Codable, Equatable {

    public let id: Model
    public let object: String
    public let ownedBy: String
}

Example

let query = ModelQuery(model: .gpt4)
openAI.model(query: query) { result in
  //Handle result here
}
//or
let result = try await openAI.model(query: query)

Review Models Documentation for more info.

Moderations

Given a input text, outputs if the model classifies it as violating OpenAI's content policy.

Request

public struct ModerationsQuery: Codable {
    
    public let input: String
    public let model: Model?
}    

Response

public struct ModerationsResult: Codable, Equatable {

    public let id: String
    public let model: Model
    public let results: [CategoryResult]
}

Example

let query = ModerationsQuery(input: "I want to kill them.")
openAI.moderations(query: query) { result in
  //Handle result here
}
//or
let result = try await openAI.moderations(query: query)

Review Moderations Documentation for more info.

Utilities

The component comes with several handy utility functions to work with the vectors.

public struct Vector {

    /// Returns the similarity between two vectors
    ///
    /// - Parameters:
    ///     - a: The first vector
    ///     - b: The second vector
    public static func cosineSimilarity(a: [Double], b: [Double]) -> Double {
        return dot(a, b) / (mag(a) * mag(b))
    }

    /// Returns the difference between two vectors. Cosine distance is defined as `1 - cosineSimilarity(a, b)`
    ///
    /// - Parameters:
    ///     - a: The first vector
    ///     - b: The second vector
    public func cosineDifference(a: [Double], b: [Double]) -> Double {
        return 1 - Self.cosineSimilarity(a: a, b: b)
    }
}

Example

let vector1 = [0.213123, 0.3214124, 0.421412, 0.3214521251, 0.412412, 0.3214124, 0.1414124, 0.3214521251, 0.213123, 0.3214124, 0.1414124, 0.4214214, 0.213123, 0.3214124, 0.1414124, 0.3214521251, 0.213123, 0.3214124, 0.1414124, 0.3214521251]
let vector2 = [0.213123, 0.3214124, 0.1414124, 0.3214521251, 0.213123, 0.3214124, 0.1414124, 0.3214521251, 0.213123, 0.511515, 0.1414124, 0.3214521251, 0.213123, 0.3214124, 0.1414124, 0.3214521251, 0.213123, 0.3214124, 0.1414124, 0.3213213]
let similarity = Vector.cosineSimilarity(a: vector1, b: vector2)
print(similarity) //0.9510201910206734

In data analysis, cosine similarity is a measure of similarity between two sequences of numbers.

Screenshot 2022-12-19 at 6 00 33 PM

Read more about Cosine Similarity here.

Combine Extensions

The library contains built-in Combine extensions.

func completions(query: CompletionsQuery) -> AnyPublisher<CompletionsResult, Error>
func images(query: ImagesQuery) -> AnyPublisher<ImagesResult, Error>
func embeddings(query: EmbeddingsQuery) -> AnyPublisher<EmbeddingsResult, Error>
func chats(query: ChatQuery) -> AnyPublisher<ChatResult, Error>
func edits(query: EditsQuery) -> AnyPublisher<EditsResult, Error>
func model(query: ModelQuery) -> AnyPublisher<ModelResult, Error>
func models() -> AnyPublisher<ModelsResult, Error>
func moderations(query: ModerationsQuery) -> AnyPublisher<ModerationsResult, Error>
func audioTranscriptions(query: AudioTranscriptionQuery) -> AnyPublisher<AudioTranscriptionResult, Error>
func audioTranslations(query: AudioTranslationQuery) -> AnyPublisher<AudioTranslationResult, Error>

Example Project

You can find example iOS application in Demo folder.

mockuuups-iphone-13-pro-mockup-perspective-right

Contribution Guidelines

Make your Pull Requests clear and obvious to anyone viewing them.
Set main as your target branch.

Use Conventional Commits principles in naming PRs and branches:

  • Feat: ... for new features and new functionality implementations.
  • Bug: ... for bug fixes.
  • Fix: ... for minor issues fixing, like typos or inaccuracies in code.
  • Chore: ... for boring stuff like code polishing, refactoring, deprecation fixing etc.

PR naming example: Feat: Add Threads API handling or Bug: Fix message result duplication

Branch naming example: feat/add-threads-API-handling or bug/fix-message-result-duplication

Write description to pull requests in following format:

  • What

    ...

  • Why

    ...

  • Affected Areas

    ...

  • More Info

    ...

We'll appreciate you including tests to your code if it is needed and possible. ❤️

Links

License

MIT License

Copyright (c) 2023 MacPaw Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

openai's People

Contributors

akinoavx13 avatar art-tykh avatar demircivi avatar djben avatar fluidpixel avatar ingvarus-bc avatar janldeboer avatar javadfaghih avatar jongyeol avatar kalafus avatar kelvinlaukl avatar kevinhermawan avatar krivoblotsky avatar ljoukov avatar longseespace avatar lzell avatar mjdierkes avatar mswagner avatar nathanborror avatar neelvirdy avatar pallavag avatar paramaggarwal avatar philippzagar avatar quilljou avatar rawnly avatar rchatham avatar recouse avatar saik0s avatar sunburstenzo avatar the-ounce 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

openai's Issues

Can it be improved to allow more detailed monitoring of the status of URLSession connections?

OpenAI.swift uses its own URLSession internally, but I have a use-case where I want to monitor the progress of the session from the out side.

I mainly want to monitor the progress of "sending" and "receiving" by means of delegate, etc. Is it possible to improve the interface design to such a way?

  • Change init(configuration: Configuration, session: URLSessionProtocol) to public access level.
  • Provide the delegate protocol so that messages such as completed, sending, receiving, failure, etc. can be messaging to the outside scopes.
  • Change the access level of the session property to public.

add support for gpt-4-1106-preview model

Is your feature request related to a problem? Please describe.
OpenAI just released preview of updated model: gpt-4-1106-preview

Describe the solution you'd like
Add new model to Models enum

Describe alternatives you've considered
Stop using MacPaw/OpenAI? Use Swift Foundation for TCP/IP to OpenAI API

Additional context
https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo

GPT-4 TurboNew
The latest GPT-4 model with improved instruction following, JSON mode, reproducible outputs, parallel function calling, and more. Returns a maximum of 4,096 output tokens. This preview model is not yet suited for production traffic. Learn more.

similar to #111 but focusing on gpt-4-1106-preview

Add fine-tuning

Extend the library by adding fine-tuning capabilities.
Add files and fine-tunes API calls so the library supports model tuning and file retrieval.

Documentation

Customization api url

Because of proxy or public service reasons, I want to relay processing requests and responses.

chat request:

    func chats(query: ChatQuery, timeoutInterval: TimeInterval = 60.0, completion: @escaping (Result<ChatResult, Error>) -> Void) {
        performRequest(request: Request<ChatResult>(body: query, url: .chats, timeoutInterval: timeoutInterval), completion: completion)
    }

change like this:

    func chats(query: ChatQuery, timeoutInterval: TimeInterval = 60.0, url: URL = .chats, completion: @escaping (Result<ChatResult, Error>) -> Void) {
        performRequest(request: Request<ChatResult>(body: query, url: url, timeoutInterval: timeoutInterval), completion: completion)
    }

Streaming is broken

Code

import Foundation
import OpenAI

let openAI = OpenAI(apiToken: "sk-...")

let query = OpenAI.ChatQuery(model: .gpt3_5Turbo, messages: [.init(role: "user", content: "hi!")], stream: true)
let result = try await openAI.chats(query: query)

Exception

Swift/ErrorType.swift:200: Fatal error: Error raised at top level: Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around line 1, column 0." UserInfo={NSDebugDescription=Invalid value around line 1, column 0., NSJSONSerializationErrorIndex=0})))

2023-03-08 14:37:01.246548+0100 CLIChat[81308:5267103] Swift/ErrorType.swift:200: Fatal error: Error raised at top level: Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around line 1, column 0." UserInfo={NSDebugDescription=Invalid value around line 1, column 0., NSJSONSerializationErrorIndex=0})))

It works with stream: false

Use macros to automatically serialise Swift functions into OpenAI functions

A very recent feature added to the library are functions which are passed to the chat request as an array of ChatFunctionDeclaration. The problem is that it's very tedious to write those and then convert them back into real function calls. Maybe it would be possible to use macros to automatically serialise a Swift function into ChatFunctionDeclaration and then call a function automatically upon receiving a ChatFunctionCall.

Not sure if its possible and how would that work since I didn't use macros yet, but would be cool!

Enable basePath when proxy openai with own host with base path

Is your feature request related to a problem? Please describe.
Right, it's pretty nice we can init a OpenAI with custom host, like

 let openAI = OpenAI(configuration: OpenAI.Configuration(
        token: "sk-xxx",
        organizationIdentifier: "org-xxx",
        host: "api.myhost",
        timeoutInterval: 60.0)
    )

It would even move better enabling a basePath like this,

let openAI = OpenAI(configuration: OpenAI.Configuration(
        token: "sk-xxx",
        organizationIdentifier: "org-xxx",
        host: "api.myhost",
        basePath: "/openai/api",
        timeoutInterval: 60.0)
)

Since most of the time we have multiple endpoints for a host.

Describe the solution you'd like

It will be a simple changes on the current implementation actually.

 public init(
            token: String,
            organizationIdentifier: String? = nil,
            host: String = "api.openai.com",
            basePath: String? = "",
            timeoutInterval: TimeInterval = 60.0
        ) {
            self.token = token
            self.organizationIdentifier = organizationIdentifier
            self.host = host
            self.basePath = basePath ?? ""
            self.timeoutInterval = timeoutInterval
        }
....

extension OpenAI {
    
    func buildURL(path: String) -> URL {
        var components = URLComponents()
        components.scheme = "https"
        components.host = configuration.host
        components.path = self.configuration.basePath + path
        return components.url!
    }
}

If it's an acceptable changes, I can draft a PR.

Describe alternatives you've considered
None.

Additional context
None.

APIError not decoded correctly

Describe the bug
An APIError associated with mishandling ChatGPT->Functions endpoint is not decoded correctly:

 Failed to decode request: Optional("{\n  \"error\": {\n    \"message\": [\n      \"Invalid schema for function \'hello\': In context=(\'properties\', \'a\'), array schema missing items\"\n    ],\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": null\n  }\n}\n")

To Reproduce

    let query = ChatQuery(
      model: .gpt3_5Turbo,
      messages: messages,
      functions: [
        .init(
          name: "hello",
          description: "Hi",
          parameters:
            JSONSchema(
              type: .object,
              properties: [
                "a": .init(
                  type: .array,
                  description: "A.",
                  enumValues: [
                    "X",
                    "Y"
                  ]
                ),
                "b": .init(
                  type: .string,
                  description: "B.",
                  enumValues: [
                    "T",
                  ]
                ),
              ],
              required: ["b"]
            )
          
        )
      ]
    )
    
    Self.logger.log("\n## Running request")
    
    let queryResponse = try await openAI.chats(query: query)

Expected behavior
Decode error correctly.

Additional context
I assume the issue is that the error message here is an array, while APIErrorResponse.message is of type String and does not handle decoding [String].

Partial ChatResult data chunks

Often receiving partial chunks of ChatResult from an OpenAI streaming session, which causes a DecodingError inside this package.

Example of data received by StreamingSession.urlSession:

data: {"id":"chatcmpl-7oBwbf0NG9vi74Z4mt3XasGhhdAVl","object":"chat.completion.chunk","created":1692196669,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"function_call":{"arguments":" che"}},"finish_reason":null}]}

data: {"id":"chatcmpl-7oBwbf0NG9vi74Z4mt3XasGhhdAVl","object":"chat.completion.chunk","created":1692196669,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"function_call":{"arguments":"et"}},"finish_reason":null}]}

data: {"id":"chatcmpl-7oBwbf0NG9vi74Z4mt3XasGhhdAVl","object":"chat.completion.chunk","created":1692196669,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"function_call":{"arguments":"ah"}},"finish_reason":null}]}

data: {"id":"chatcmpl-7oBwbf0NG9vi74Z4mt3XasGhhdAVl","object":"chat.completion.chunk","created":1692196669,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"function_call":{"arguments":" on"}},"finish_reason":null}]}

data: {"id":"chatcmpl-7oBwbf0NG9vi74Z4mt3XasGhhdAVl","object":"chat.completion.chunk","created":1692196669,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"function_call":{"arguments":" rocket"}},"finish_reason":null}]}

data: {"id":"chatcmpl-

The decoder chokes on the last chunk.

I fixed it in my local copy of the OpenAI package by keeping the incomplete chunk around as leftover for the next round. I don't know enough to know whether it's a valid fix or if there is a better way to address this.

StreamingSession.patch

Support for different URL scheme, port.

The host that you supply to OpenAI.Configuration is converted into a URL by buildURL, which has the scheme hard-coded to https.

With projects like GPT4All allowing users to run a local server that provides an OpenAI-style API to a local engine, it would be useful to be able to either override the scheme and port, or supply the first part of the URL entirely ("http://localhost:4891").

Please fix the error handling

How to get a more understandable description of error? I handle it but I see only the response with type of error

Add support for custom HTTP headers

Is your feature request related to a problem? Please describe.
I would like to integrate with OpenAI via the Helicone proxy, but to do so, I need to be able to specify a custom HTTP header.

Describe the solution you'd like
Proxy APIs like Helicone wrap OpenAI's APIs with additional functionality. To use Helicone, you not only need to point the OpenAI client at the Helicone host, but you also have to set an additional HTTP header on the requests. For details, please refer to https://docs.helicone.ai/getting-started/integration-method/openai-proxy.

To enable this, I propose:

  1. Changing OpenAI.swift's Configuration struct to take an optional dictionary of key/value pairs, where the key is the name of the HTTP header, and the value is its value.
  2. Modifying performRequest() and performSteamingRequest() in that file to pass that optional dictionary to URLRequestBuildable.build() in a similar manner as organizationIdentifier is passed today
  3. Changing the URLRequestBuildable.build() interface to accept that optional dictionary
  4. Changing JSONRequest.build(), MultipartFormDataRequest.build(), etc. to conform to the updated interface, setting the header(s) on the request that is built if any were supplied in the optional dictionary.

Describe alternatives you've considered
I think taking an optional dictionary of headers is preferable to explicitly covering every possible header that might be used by other proxies like Helicone. Other than that, I can't think of too many other ways to do this.

Additional context
N/A

Chat demo App: How to fetch function arguments on the view?

Is your feature request related to a problem? Please describe.
Would be great to be able to fetch function arguments on the view.

Describe the solution you'd like

When defined:

  let weatherFunction = ChatFunctionDeclaration(
            name: "getWeatherData",
            description: "Get the current weather in a given location",
            parameters: .init(
              type: .object,
              properties: [
                "location": .init(type: .string, description: "The city and state, e.g. San Francisco, CA"),
                "degrees": .init(type: .string, description: "The degrees, in celsius. Ex: 10c")

              ],
              required: ["location"]
            )
        )

When prompted "show me the weather in San Francisco"...

Current demo app shows the on the bubble: message.content
Would be great to be able to fetch the property, ex: function.degrees.

This would enable to place specific function properties on tables, graphs, etc...


 var body: some View {
        HStack {
            switch message.role {
            case .assistant:
                Text(message.content)
                    .padding(.horizontal, 16)
                    .padding(.vertical, 12)
                    .background(assistantBackgroundColor)
                    .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
                Spacer(minLength: 24)
            case .user:
                Spacer(minLength: 24)
                Text(message.content)
                    .padding(.horizontal, 16)
                    .padding(.vertical, 12)
                    .foregroundColor(userForegroundColor)
                    .background(userBackgroundColor)
                    .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
            case .function:
              Text(message.content)
                  .font(.footnote.monospaced())
                  .padding(.horizontal, 16)
                  .padding(.vertical, 12)
                  .background(funcBackgroundColor)
                  .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
              Spacer(minLength: 24)
            case .system:
                EmptyView()
            }
        }
    }

Create release for the latest version

As the current release 0.0.1 is still based on the initial version, most changes aren't accessible when installing the package using the Swift package manager.

Note: I'm not quite familiar with how exactly release management is handled here, maybe changing the tag is enough?

CompletionsQuery please add parameter n

Is your feature request related to a problem? Please describe.
this API expects to return multiple options

Describe the solution you'd like
..

Describe alternatives you've considered
...

Additional context
Add any other context or screenshots about the feature request here.

Enable batch prompts

The OpenAI API seems to allow both strings and arrays of strings in order to batch requests:

Here’s the canonical example from the docs using Python:

import openai  # for making OpenAI API requests

num_stories = 10
prompts = ["Once upon a time,"] * num_stories
 
# batched example, with 10 story completions per request
response = openai.Completion.create(
    model="curie",
    prompt=prompts,
    max_tokens=20,
)
 
# match completions to prompts by index
stories = [""] * len(prompts)
for choice in response.choices:
    stories[choice.index] = prompts[choice.index] + choice.text
 
# print stories
for story in stories:
    print(story)

The source inline documentation seems to suggest that this functionality has been considered also for this library …

struct CompletionsQuery: Codable {
    // ...
    /// The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays.
    public let prompt: String
    // ...
}

… but the code itself seems to only implement strings but not arrays for prompts.

Batching seems like a great way to reduce load times based on my experimentation and I’d love to see it come to this library as well.

Thanks for all the amazing work!

Disclaimer: I'm 100% new to Swift.

Demo App: Chat Input Allows White Spaces and Enables Send Button Without Text

Describe the bug
The input text in text chats accepts text with white spaces, resulting in chat bubbles with white spaces. Additionally, users can click the send button without entering any text, causing the creation of empty chat bubbles and generating a response.

To Reproduce
Steps to reproduce the behavior:

  1. Go to the demo app chats page
  2. Input some text with white spaces
  3. Press the send button
  4. A chat bubble is created with spaces

Expected behavior
After clicking the send button, white spaces should be removed, resulting in chat bubbles without any white spaces.

Screenshots
Simulator Screenshot - iPhone 15 - 2023-10-03 at 14 00 20

Additional context
The chat send button is currently enabled even with an empty chat.

Better error differentiation

Right now, using an invalid API key and passing messages that are too long yields the same inscrutable error:

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

It would be nice if these failure modes, among others, were differentiable

Add example project

Adding an example project that demonstrates library usage would be awesome. Maybe some chat-based UX, but not required.
iOS/macOS

Can you all more models?

截屏2023-03-18 11 58 38

The same endpoint has supported all new models like gpt4, all can you make it customized to we can pass a string name for lastest model.

Version 0.2.0 breaks example with completion

Describe the bug
The latest version, 0.2.0, break the completion example, likely due to the stream being true and the decoding not expecting the JSON format which OpenAI returns.

To Reproduce
Steps to reproduce the behavior:

  1. Create a new Xcode project
  2. Use the example of the prompt in the README:
  3. let query = CompletionsQuery(model: .textDavinci_003, prompt: "What is 42?", temperature: 0, max_tokens: 100, top_p: 1, frequency_penalty: 0, presence_penalty: 0, stop: ["\\n"]) openAI.completions(query: query) { result in //Handle result here } //or let result = try await openAI.completions(query: query)
  4. Run the example and observe the following error:
  5. failure(Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around line 1, column 0." UserInfo={NSDebugDescription=Invalid value around line 1, column 0., NSJSONSerializationErrorIndex=0}))))

Expected behavior
Reverting to the version before 0.2.0 works fine with an expected result without error.

Desktop (please complete the following information):

  • OS: macOS 13.3.1

`"The given data was not valid JSON."` Streaming Error

Describe the bug
I'm on version 0.2.4, while streaming responses I still seem to often be getting the following error:
"The data couldn't be read because it isn't in the correct format."

In the console the full output is:
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Unexpected end of file" UserInfo={NSDebugDescription=Unexpected end of file})))

To Reproduce
I usually always get this output when asking GPT-3.5: "Write 1 to 40 using bullet points"

Edit: Opened a PR to fix this: #104

Add Decoder Tests

Is your feature request related to a problem? Please describe.
I've discovered an issue with ModelResult by having a unit test.

Describe the solution you'd like
Adding a set of decoder-related unit tests would ensure the models are correct when comparing with the expected json.

Describe alternatives you've considered
It would be handy to have examples in the sample project, but unit tests would still be the most optimum solution.

Additional context
My proposition is something similar to the below, but with varying examples for all supported endpoint results

class OpenAITestsDecoder: XCTestCase

func testModelsSingle() async throws {
    let data = """
    {
      "data": [
        {
          "id": "gpt-3.5-turbo",
          "object": "model",
          "owned_by": "organization-owner"
        }
      ],
      "object": "list"
    }
    """.data(using: .utf8)!
    
    do {
        let decoded = try JSONDecoder().decode(ModelsResult.self, from: data)
        
        let mock = ModelsResult(data: [
            .init(id: .gpt3_5Turbo, object: "model", ownedBy: "organization-owner")
        ], object: "list")
        
        XCTAssertEqual(decoded, mock)
        
    } catch let error {
        XCTFail(error.localizedDescription)
    }
}

Better error handling for streaming session

Is your feature request related to a problem? Please describe.
Recently I have came over a problem, where my program stopped working getting an error message:

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

After some painful debugging I have found out the problem was with my OpenAI key not working.

Describe the solution you'd like
Would make sense to implement models for errors as they are described by OpenAI on this page:

https://platform.openai.com/docs/guides/error-codes/api-errors

Describe alternatives you've considered
I am not sure why there has not been at least a generic error message printed.

var apiError: Error? = nil
            do {
                let decoder = JSONDecoder()
                let object = try decoder.decode(ResultType.self, from: jsonData)
                onReceiveContent?(self, object)
            } catch {
                apiError = error
            }
            
            if let apiError = apiError {
                do {
                    let decoded = try JSONDecoder().decode(APIErrorResponse.self, from: data)
                    onProcessingError?(self, decoded)
                } catch {
                    onProcessingError?(self, apiError)
                }
            }

I think the problem concerns this part of the code from StreamingSession.swift . I have not fully investigated this in detail, but my assumption would be that the Error message is not decoded and therefore it returns the error of not being able to decode the ResultType.

When I looked over APIErrorResponse it does seem to be quite generic, so it is indeed interesting that it did not decode itself.

Demoapp Xcode project is missing the openAI library

Describe the bug
The demoapp xcode project is missing the openAI library in its dependency settings

To Reproduce
Steps to reproduce the behavior:

  1. Try to build and run the demo App
  2. You will get an error that the openAI library is missing

Expected behavior
Build is successful and app runs

Screenshots
CleanShot 2023-04-18 at 22 43 35

Steps to fix
To fix the issue and run the app, one has to add the library with the plus icon
CleanShot 2023-04-18 at 22 44 41

[Demo App] Add Moderations example

Is your feature request related to a problem? Please describe.
I think moderations could be a useful addition, and seemingly simple enough when the Chat example already exists.

Describe the solution you'd like
Just a clear and obvious way to show the results, e.g.:
Screenshot 2023-04-25 at 9 36 19 pm

Describe alternatives you've considered
Not adding the example as it could be clear enough.

Additional context
Could be an option in Chat instead, to check if a message that you sent would be flagged.

[Demo app] Add audio transcription example; add image generation example

Is your feature request related to a problem? Please describe.
The demo app includes a chat example; audio and image generation remains TBD.

Describe the solution you'd like
Using the OpenAI API, add audio transcribe and image generation demos.

Describe alternatives you've considered
Not doing them.

OpenAI function produces unescaped double-quotes inside JSON String value, causing DecodingError

Describe the bug
When using a JSON function, a JSON Decoding error is sometimes triggered. The error happens because of unescaped double-quotes occurring inside a JSON string value.

To Reproduce
Steps to reproduce the behavior:
The JSON string that causes the error:
"""
{ "question": "Which deep-sea creature is known for its ability to create its own light through bioluminescence?", "question_summary": "Deep-sea creature that produces its own light", "correct_answer": "Deep-sea anglerfish", "incorrect_answer_1": "Lantern shark", "incorrect_answer_2": "Glowing jellyfish", "incorrect_answer_3": "Bioluminescent crab", "humorous_answer": "Lightbulb shrimp", "humorous_failure_reaction": "Close, but not quite as bright!", "explanation": "The Deep-sea anglerfish is a fascinating creature that has a unique adaptation for finding food in the deep sea. It has a long glowing lure that dangles in front of its mouth to attract prey. This lure is bioluminescent and acts like a "fishing rod" to lure unsuspecting prey." }
"""
Error happens at "fishing rod" because double-quote is unescaped

Expected behavior
Double quotes inside JSON string values should be escaped.

Screenshots
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Badly formed object around line 1, column 757." UserInfo={NSDebugDescription=Badly formed object around line 1, column 757., NSJSONSerializationErrorIndex=757}))):
{ "question": "Which deep-sea creature is known for its ability to create its own light through bioluminescence?", "question_summary": "Deep-sea creature that produces its own light", "correct_answer": "Deep-sea anglerfish", "incorrect_answer_1": "Lantern shark", "incorrect_answer_2": "Glowing jellyfish", "incorrect_answer_3": "Bioluminescent crab", "humorous_answer": "Lightbulb shrimp", "humorous_failure_reaction": "Close, but not quite as bright!", "explanation": "The Deep-sea anglerfish is a fascinating creature that has a unique adaptation for finding food in the deep sea. It has a long glowing lure that dangles in front of its mouth to attract prey. This lure is bioluminescent and acts like a "fishing rod" to lure unsuspecting prey." }

Visual image search

Is your feature request related to a problem? Please describe.
I have a usecase in which user should be able to upload an image and search should be based on that image. i.e. if I am a user I will take picture of the object and post it for search and the search should let me know about similar images on the web or for instance the places where I can purchase that object in the nearby etc

Describe the solution you'd like
I will upload an image based on whose features the search will be done throughout the web and similar images links are returned.

Getting KeyNotFound error for `id` when creating `CompletionsQuery`.

When trying to make a completions request, Im getting a KeyNotFound error. This is also happening on a chat request.
Am I missing something in my setup? Thanks for any help!

let openAI = OpenAI(apiToken: "sk-....")
let query = OpenAI.CompletionsQuery(model: .textDavinci_003, prompt: "hello there")
do {
    let result = try await openAI.completions(query: query)
    print(result)
} catch {
    print(error)
}

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))

Background URLSessions support

Is your feature request related to a problem? Please describe.
If the app gets suspended or terminated, the URLSession cannot continue.

Describe the solution you'd like
Either the OpenAI class itself or some other class that would take the responsibility of making the calls would have to be a subclass of NSObject and implement the URLSessionDelegate protocol, then there would have to be a way to register these delegates, associate them with individual calls, and have a way for the AppDelegate to bring them to life and finish the original task even after the app was suspended.
https://developer.apple.com/documentation/foundation/urlsessiondelegate/1617185-urlsessiondidfinishevents

We can model this on the AWSS3TransferUtility:
https://github.com/aws-amplify/aws-sdk-ios/blob/main/AWSS3/AWSS3TransferUtility.m

Describe alternatives you've considered
Not sure if we have an alternative. The main issue is that the background sessions do not support closures so we have to use URLSession delegates.

Additional context
This is about robustness of the system - low connectivity and people leaving the app. Imagine you have to transcribe 1 GB audio file. Even if you split it to smaller parts, you still need to upload them somehow if the user goes to the background. I don't think real users will have the patience to look at a progress bar for 10 minutes or however long it will take.

How to cancel closure openAI.chatsStream(query: query)

Is there any way to cancel chatsStream closure?

openAI.chatsStream(query: query) { partialResult in
    switch partialResult {
    case .success(let result):
        print(result.choices)
    case .failure(let error):
        //Handle chunk error here
    }
} completion: { error in
    //Handle streaming error here
}

I call this function inside SwiftUI OnAppear and when the sheet dismissed the closure keep running, is there any solution for that?

[Demo App] Add support for MacPaw's Fixel font

Is your feature request related to a problem? Please describe.
The Demo App is very Apple-like and I think it would not only be nice to give it some character, but also represent MacPaw's identity better.

Describe the solution you'd like
Using FixelDisplay-Regular.ttf and FixelDisplay-Bold.ttf would most likely cover most use cases.

Describe alternatives you've considered
Sticking with the native Apple San Francisco font so the demo app is less complex (arguably does not benefit the user/developer directly).

Additional context
https://fixel.macpaw.com/

chatsStream(query:) throwing error

Describe the bug
When calling chatStream(query: ChatQuery), it throws error below

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

To Reproduce
Steps to reproduce the behavior:

  1. Call chatStream(query: ChatQuery)
  2. Error thrown

Additional context
It started happening today

Chat demo: switch message.role is not working.

Describe the bug

On the chat demo, switch message.role is not working.

To Reproduce
Steps to reproduce the behavior:

1- Change the function background color for debug purpose

var body: some View {
    HStack {
        switch message.role {
        case .assistant:
            Text(design.event)
           // Text(message.content)
                .padding(.horizontal, 16)
                .padding(.vertical, 12)
                .background(assistantBackgroundColor)
                .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
            Spacer(minLength: 24)
        case .user:
            Spacer(minLength: 24)
            Text(message.content)
                .padding(.horizontal, 16)
                .padding(.vertical, 12)
                .foregroundColor(userForegroundColor)
                .background(userBackgroundColor)
                .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
        case .function:
          Text(message.content)
              .font(.footnote.monospaced())
              .padding(.horizontal, 16)
              .padding(.vertical, 12)
              .background(funcBackgroundColor)
              .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
          Spacer(minLength: 24)
        case .system:
            EmptyView()
        }
    }
}

2- Build the app, and prompt "show me weather for san Francisco!

3- The bubble will not be orange (function)

Expected behavior
The bubble chat should be orange

New OpenAI update

I would love the ability to use the new DALLE 3 API in swift, they just released it so there isn't support yet on this branch.

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.