Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

follow JSON API #164

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Demo/DemoChat/Sources/ChatStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public final class ChatStore: ObservableObject {
for choice in partialChatResult.choices {
let existingMessages = conversations[conversationIndex].messages
// Function calls are also streamed, so we need to accumulate.
if let functionCallDelta = choice.delta.functionCall {
if let functionCallDelta = choice.delta.function_call {
if let nameDelta = functionCallDelta.name {
functionCallName += nameDelta
}
Expand All @@ -124,7 +124,7 @@ public final class ChatStore: ObservableObject {
}
}
var messageText = choice.delta.content ?? ""
if let finishReason = choice.finishReason,
if let finishReason = choice.finish_reason,
finishReason == "function_call" {
messageText += "Function call: name=\(functionCallName) arguments=\(functionCallArguments)"
}
Expand Down
8 changes: 4 additions & 4 deletions Demo/DemoChat/Sources/MiscStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ public final class MiscStore: ObservableObject {
for result in categoryResults {
let content = """
\(circleEmoji(for: result.categories.hate)) Hate
\(circleEmoji(for: result.categories.hateThreatening)) Hate/Threatening
\(circleEmoji(for: result.categories.selfHarm)) Self-harm
\(circleEmoji(for: result.categories.hate_threatening)) Hate/Threatening
\(circleEmoji(for: result.categories.self_harm)) Self-harm
\(circleEmoji(for: result.categories.sexual)) Sexual
\(circleEmoji(for: result.categories.sexualMinors)) Sexual/Minors
\(circleEmoji(for: result.categories.sexual_minors)) Sexual/Minors
\(circleEmoji(for: result.categories.violence)) Violence
\(circleEmoji(for: result.categories.violenceGraphic)) Violence/Graphic
\(circleEmoji(for: result.categories.violence_graphic)) Violence/Graphic
"""

let message = Message(
Expand Down
4 changes: 2 additions & 2 deletions Demo/DemoChat/Sources/SpeechStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ public final class SpeechStore: ObservableObject {
guard let input = query.input, !input.isEmpty else { return }
do {
let response = try await openAIClient.audioCreateSpeech(query: query)
guard let data = response.audioData else { return }
guard let data = response.audio_data else { return }
let player = try? AVAudioPlayer(data: data)
let audioObject = AudioObject(prompt: input,
audioPlayer: player,
originResponse: response,
format: query.responseFormat.rawValue)
format: query.response_format.rawValue)
audioObjects.append(audioObject)
} catch {
print(error.localizedDescription)
Expand Down
4 changes: 2 additions & 2 deletions Demo/DemoChat/Sources/UI/TextToSpeechView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public struct TextToSpeechView: View {
let query = AudioSpeechQuery(model: selectedSpeechModel,
input: prompt,
voice: voice,
responseFormat: responseFormat,
response_format: responseFormat,
speed: speed)
Task {
await store.createSpeech(query)
Expand Down Expand Up @@ -122,7 +122,7 @@ public struct TextToSpeechView: View {
}
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button {
presentUserDirectoryDocumentPicker(for: object.originResponse.audioData, filename: "GeneratedAudio.\(object.format)")
presentUserDirectoryDocumentPicker(for: object.originResponse.audio_data, filename: "GeneratedAudio.\(object.format)")
} label: {
Image(systemName: "square.and.arrow.down")
}
Expand Down
69 changes: 31 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,13 @@ struct CompletionsQuery: Codable {
/// 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?
public let max_tokens: 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?
public let top_p: 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?
public let frequency_penalty: 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?
public let presence_penalty: 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.
Expand Down Expand Up @@ -134,7 +134,7 @@ struct CompletionsResult: Codable, Equatable {
**Example**

```swift
let query = CompletionsQuery(model: .textDavinci_003, prompt: "What is 42?", temperature: 0, maxTokens: 100, topP: 1, frequencyPenalty: 0, presencePenalty: 0, stop: ["\\n"])
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
}
Expand Down Expand Up @@ -220,19 +220,19 @@ Using the OpenAI Chat API, you can build your own applications with `gpt-3.5-tur
/// 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?
public let top_p: 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?
public let max_tokens: 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?
public let presence_penalty: 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?
public let frequency_penalty: Double?
///Modify the likelihood of specified tokens appearing in the completion.
public let logitBias: [String:Int]?
public let logit_bias: [string:int]?
/// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.
public let user: String?
}
Expand All @@ -245,13 +245,13 @@ struct ChatResult: Codable, Equatable {
public struct Choice: Codable, Equatable {
public let index: Int
public let message: Chat
public let finishReason: String
public let finish_reason: String
}

public struct Usage: Codable, Equatable {
public let promptTokens: Int
public let completionTokens: Int
public let totalTokens: Int
public let prompt_tokens: Int
public let completion_tokens: Int
public let total_tokens: Int
}

public let id: String
Expand Down Expand Up @@ -451,10 +451,10 @@ Creates an edited or extended image given an original image and a prompt.
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
public let file_name: 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?
public let mask_file_name: 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.
Expand All @@ -472,7 +472,7 @@ Uses the ImagesResult response similarly to ImagesQuery.

```swift
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")
let query = ImageEditQuery(image: Data, file_name: "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
}
Expand All @@ -490,7 +490,7 @@ Creates a variation of a given image.
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
public let file_name: 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.
Expand All @@ -506,7 +506,7 @@ Uses the ImagesResult response similarly to ImagesQuery.

```swift
let data = image.pngData()
let query = ImageVariationQuery(image: data, fileName: "whitecat.png", n: 1, size: "1024x1024")
let query = ImageVariationQuery(image: Data, file_name: "whitecat.png", n: 1, size: "1024x1024")
openAI.imageVariations(query: query) { result in
//Handle result here
}
Expand Down Expand Up @@ -539,7 +539,7 @@ 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 response_format: AudioSpeechResponseFormat
public let speed: String? // Initializes with Double?
//...
}
Expand All @@ -549,13 +549,13 @@ public struct AudioSpeechQuery: Codable, Equatable {

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

**Example:**

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

openAI.audioCreateSpeech(query: query) { result in
// Handle response here
Expand All @@ -576,7 +576,7 @@ Transcribes audio into the input language.
public struct AudioTranscriptionQuery: Codable, Equatable {

public let file: Data
public let fileName: String
public let file_name: String
public let model: Model

public let prompt: String?
Expand All @@ -598,7 +598,7 @@ public struct AudioTranscriptionResult: Codable, Equatable {

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

openAI.audioTranscriptions(query: query) { result in
//Handle result here
Expand All @@ -617,7 +617,7 @@ Translates audio into into English.
public struct AudioTranslationQuery: Codable, Equatable {

public let file: Data
public let fileName: String
public let file_name: String
public let model: Model

public let prompt: String?
Expand All @@ -638,7 +638,7 @@ public struct AudioTranslationResult: Codable, Equatable {

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

openAI.audioTranslations(query: query) { result in
//Handle result here
Expand Down Expand Up @@ -668,7 +668,7 @@ struct EditsQuery: Codable {
/// 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?
public let top_p: Double?
}
```

Expand All @@ -683,16 +683,9 @@ struct EditsResult: Codable, Equatable {
}

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 prompt_tokens: Int
public let completion_tokens: Int
public let total_tokens: Int

public let object: String
public let created: TimeInterval
Expand Down Expand Up @@ -898,7 +891,7 @@ public struct ModelResult: Codable, Equatable {

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

Expand Down
2 changes: 1 addition & 1 deletion Sources/OpenAI/OpenAI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ extension OpenAI {
return completion(.failure(OpenAIError.emptyData))
}

completion(.success(AudioSpeechResult(audioData: data)))
completion(.success(AudioSpeechResult(audio_data: data)))
}
task.resume()
} catch {
Expand Down
14 changes: 3 additions & 11 deletions Sources/OpenAI/Public/Models/AudioSpeechQuery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,16 @@ public struct AudioSpeechQuery: Codable, Equatable {
/// The voice to use when generating the audio. Supported voices are alloy, echo, fable, onyx, nova, and shimmer.
public let voice: AudioSpeechVoice
/// The format to audio in. Supported formats are mp3, opus, aac, and flac.
public let responseFormat: AudioSpeechResponseFormat
public let response_format: AudioSpeechResponseFormat
/// The speed of the generated audio. Enter a value between **0.25** and **4.0**. Default: **1.0**
public let speed: String?

public enum CodingKeys: String, CodingKey {
case model
case input
case voice
case responseFormat = "response_format"
case speed
}

public init(model: Model, input: String, voice: AudioSpeechVoice, responseFormat: AudioSpeechResponseFormat = .mp3, speed: Double?) {
public init(model: Model, input: String, voice: AudioSpeechVoice, response_format: AudioSpeechResponseFormat = .mp3, speed: Double?) {
self.model = AudioSpeechQuery.validateSpeechModel(model)
self.speed = AudioSpeechQuery.normalizeSpeechSpeed(speed)
self.input = input
self.voice = voice
self.responseFormat = responseFormat
self.response_format = response_format
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/OpenAI/Public/Models/AudioSpeechResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ import Foundation
public struct AudioSpeechResult {

/// Audio data for one of the following formats :`mp3`, `opus`, `aac`, `flac`
public let audioData: Data?
public let audio_data: Data?
}
16 changes: 8 additions & 8 deletions Sources/OpenAI/Public/Models/AudioTranscriptionQuery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
public enum AudioResponseFormat: String, Codable, Equatable {
case json
case text
case verboseJson = "verbose_json"
case verbose_json
case srt
case vtt
}
Expand All @@ -19,35 +19,35 @@ public struct AudioTranscriptionQuery: Codable, Equatable {
public typealias ResponseFormat = AudioResponseFormat

public let file: Data
public let fileName: String
public let file_name: String
public let model: Model
public let responseFormat: Self.ResponseFormat?
public let response_format: Self.ResponseFormat?

public let prompt: String?
public let temperature: Double?
public let language: String?

public init(file: Data, fileName: String, model: Model, prompt: String? = nil, temperature: Double? = nil, language: String? = nil, responseFormat: Self.ResponseFormat? = nil) {
public init(file: Data, file_name: String, model: Model, prompt: String? = nil, temperature: Double? = nil, language: String? = nil, response_format: Self.ResponseFormat? = nil) {
self.file = file
self.fileName = fileName
self.file_name = file_name
self.model = model
self.prompt = prompt
self.temperature = temperature
self.language = language
self.responseFormat = responseFormat
self.response_format = response_format
}
}

extension AudioTranscriptionQuery: MultipartFormDataBodyEncodable {

func encode(boundary: String) -> Data {
let bodyBuilder = MultipartFormDataBodyBuilder(boundary: boundary, entries: [
.file(paramName: "file", fileName: fileName, fileData: file, contentType: "audio/mpeg"),
.file(paramName: "file", fileName: file_name, fileData: file, contentType: "audio/mpeg"),
.string(paramName: "model", value: model),
.string(paramName: "prompt", value: prompt),
.string(paramName: "temperature", value: temperature),
.string(paramName: "language", value: language),
.string(paramName: "response_format", value: responseFormat)
.string(paramName: "response_format", value: response_format)
])
return bodyBuilder.build()
}
Expand Down
Loading
Loading