Skip to content

Commit

Permalink
Merge branch 'release/2.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
antonio-war committed Apr 12, 2023
2 parents 0c19ae8 + a4a7b61 commit 74a3b24
Show file tree
Hide file tree
Showing 16 changed files with 354 additions and 36 deletions.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,42 @@ All methods of the image feature are also available in Async/Await version.
```swift
let result: Result<UIImage, Error> = await swiftyGPT.image(prompt: "Draw an unicorn", size: .x256)
```
---

# Completion

SwiftyGPT also provides methods for creating completions using models like Davinci or Babbage. Given a prompt, the model will return one or more predicted completions based on the 'choices' parameters which you have already seen before.
Also in this case it is obviously possible to set some parameters in such a way as to best condition our response.

```swift
swiftyGPT.completion(prompt: "Say \"Hello\" in italian", model: .text_davinci_003) { result in
switch result {
case .success(let response):
print(response)
case .failure(let error):
if let error = error as? SwiftyGPTError {
print(error.message)
} else {
print(error.localizedDescription)
}
}
}
```

In case of success methods return a SwiftyGPTCompletionResponse object which is the entire transcript of HTTP response.
To get the concrete response text you have to check the content of the 'choices' attribute.

```swift
let text = response.choices.first?.text
```

## Async/Await

All methods of the completion feature are also available in Async/Await version.

```swift
let result: Result<SwiftyGPTCompletionResponse, Error> = await swiftyGPT.completion(prompt: "Say \"Hello\" in italian")
```

---

Expand Down
11 changes: 2 additions & 9 deletions Sources/SwiftyGPT/Chat/SwiftyGPTChatMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import Foundation
public struct SwiftyGPTChatMessage: Codable, Identifiable {
public let id: UUID = UUID()
public let date: Date = Date()
public let role: SwiftyGPTRole
public let role: SwiftyGPTChatRole
public let content: String

enum CodingKeys: String, CodingKey {
case role
case content
}

public init(role: SwiftyGPTRole, content: String) {
public init(role: SwiftyGPTChatRole, content: String) {
self.role = role
self.content = content
}
Expand All @@ -28,10 +28,3 @@ public struct SwiftyGPTChatMessage: Codable, Identifiable {
self.content = content
}
}

// MARK: - Role
public enum SwiftyGPTRole: String, Codable {
case system
case user
case assistant
}
2 changes: 1 addition & 1 deletion Sources/SwiftyGPT/Chat/SwiftyGPTChatModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

public enum SwiftyGPTChatModel: String, Codable {
public enum SwiftyGPTChatModel: String, SwiftyGPTModel {
case gpt4 = "gpt-4"
case gpt4_0314 = "gpt-4-0314"
case gpt4_32k = "gpt-4-32k"
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftyGPT/Chat/SwiftyGPTChatRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation
import SwiftyHTTP

public struct SwiftyGPTChatRequest: SwiftyHTTPRequestBody {
public struct SwiftyGPTChatRequest: SwiftyGPTRequest {
public let messages: [SwiftyGPTChatMessage]
public let model: SwiftyGPTChatModel
public let temperature: Float?
Expand Down
29 changes: 5 additions & 24 deletions Sources/SwiftyGPT/Chat/SwiftyGPTChatResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,23 @@
import Foundation
import SwiftyHTTP

public struct SwiftyGPTChatResponse: SwiftyHTTPResponseBody {
public struct SwiftyGPTChatResponse: SwiftyGPTResponse {
public let id, object: String
public let created: TimeInterval
public let model: SwiftyGPTChatModel
public let usage: SwiftyGPTChatUsage
public let usage: SwiftyGPTUsage
public let choices: [SwiftyGPTChatChoice]
}

// MARK: - Choice
public struct SwiftyGPTChatChoice: Codable {
public let message: SwiftyGPTChatMessage
public let finishReason: SwiftyGPTChatFinishReason
public struct SwiftyGPTChatChoice: SwiftyGPTChoice {
public let finishReason: SwiftyGPTFinishReason
public let index: Int
public let message: SwiftyGPTChatMessage

enum CodingKeys: String, CodingKey {
case message
case finishReason = "finish_reason"
case index
}
}

// MARK: - Usage
public struct SwiftyGPTChatUsage: Codable {
public let promptTokens, completionTokens, totalTokens: Int

enum CodingKeys: String, CodingKey {
case promptTokens = "prompt_tokens"
case completionTokens = "completion_tokens"
case totalTokens = "total_tokens"
}
}

// MARK: - FinishReason
public enum SwiftyGPTChatFinishReason: String, Codable {
case stop
case length
case contentFilter = "content_filter"
case null
}
14 changes: 14 additions & 0 deletions Sources/SwiftyGPT/Chat/SwiftyGPTChatRole.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// SwiftyGPTChatRole.swift
//
//
// Created by Antonio Guerra on 11/04/23.
//

import Foundation

public enum SwiftyGPTChatRole: String, Codable {
case system
case user
case assistant
}
51 changes: 51 additions & 0 deletions Sources/SwiftyGPT/Completion/SwiftyGPT+Completion.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// SwiftyGPT+Completion.swift
//
//
// Created by Antonio Guerra on 11/04/23.
//

import Foundation
import SwiftyHTTP
import SwiftyRanged

// MARK: - Completion
extension SwiftyGPT {

public func completion(prompt: String, model: SwiftyGPTCompletionModel = .stable, suffix: String? = nil, @SwiftyOptionalRanged(0...4096) maxTokens: Int? = nil, @SwiftyOptionalRanged(0...2) temperature: Float? = nil, choices: Int? = nil, @SwiftyOptionalRanged(1...5) logprobs: Int? = nil, echo: Bool? = nil, @SwiftyOptionalRanged(-2...2) presencePenalty: Float? = nil, @SwiftyOptionalRanged(-2...2) frequencyPenalty: Float? = nil, user: String? = nil, completion: @escaping (Result<SwiftyGPTCompletionResponse, Error>) -> ()) {

let request = SwiftyGPTCompletionRequest(prompt: prompt, model: model, suffix: suffix, maxTokens: maxTokens, temperature: temperature, choices: choices, stream: false, logprobs: logprobs, echo: echo, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user)
SwiftyHTTP.request(SwiftyGPTRouter.completion(apiKey, request)) { result in
switch result {
case .success(let response):
if response.statusCode == 200 {
guard let body = try? JSONDecoder().decode(SwiftyGPTCompletionResponse.self, from: response.body) else {
completion(.failure(URLError(.badServerResponse)))
return
}

let formattedBody = SwiftyGPTCompletionResponse(id: body.id, object: body.object, created: body.created, usage: body.usage, model: body.model, choices: body.choices.map { SwiftyGPTCompletionChoice(finishReason: $0.finishReason, index: $0.index, text: $0.text.trimmingCharacters(in: .whitespacesAndNewlines), logprobs: $0.logprobs)})

completion(.success(formattedBody))
} else {
guard let error = try? JSONDecoder().decode(SwiftyGPTError.self, from: response.body) else {
completion(.failure(URLError(.badServerResponse)))
return
}
completion(.failure(error))
}
case .failure(let error):
completion(.failure(error))
}
}
}

public func completion(prompt: String, model: SwiftyGPTCompletionModel = .stable, suffix: String? = nil, @SwiftyOptionalRanged(0...4096) maxTokens: Int? = nil, @SwiftyOptionalRanged(0...2) temperature: Float? = nil, choices: Int? = nil, @SwiftyOptionalRanged(1...5) logprobs: Int? = nil, echo: Bool? = nil, @SwiftyOptionalRanged(-2...2) presencePenalty: Float? = nil, @SwiftyOptionalRanged(-2...2) frequencyPenalty: Float? = nil, user: String? = nil) async -> Result<SwiftyGPTCompletionResponse, Error> {

return await withCheckedContinuation { continuation in
completion(prompt: prompt, model: model, suffix: suffix, maxTokens: maxTokens, temperature: temperature, choices: choices, logprobs: logprobs, echo: echo, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user) { result in
continuation.resume(returning: result)
}
}
}
}
28 changes: 28 additions & 0 deletions Sources/SwiftyGPT/Completion/SwiftyGPTCompletionModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// SwiftyGPTCompletionModel.swift
//
//
// Created by Antonio Guerra on 11/04/23.
//

import Foundation

public enum SwiftyGPTCompletionModel: String, SwiftyGPTModel {

case ada
case text_ada_001 = "text-ada-001"

case babbage
case text_babbage_001 = "text-babbage-001"

case curie
case text_curie_001 = "text-curie-001"

case davinci
case text_davinci_002 = "text-davinci-002"
case text_davinci_003 = "text-davinci-003"

public static var stable: SwiftyGPTCompletionModel {
.text_davinci_003
}
}
39 changes: 39 additions & 0 deletions Sources/SwiftyGPT/Completion/SwiftyGPTCompletionRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// SwiftyGPTCompletionRequest.swift
//
//
// Created by Antonio Guerra on 11/04/23.
//

import Foundation
import SwiftyHTTP

public struct SwiftyGPTCompletionRequest: SwiftyGPTRequest {
public let prompt: String
public let model: SwiftyGPTCompletionModel
public let suffix: String?
public let maxTokens: Int?
public let temperature: Float?
public let choices: Int?
public let stream: Bool?
public let logprobs: Int?
public let echo: Bool?
public let presencePenalty: Float?
public let frequencyPenalty: Float?
public let user: String?

enum CodingKeys: String, CodingKey {
case prompt
case model
case suffix
case maxTokens = "max_tokens"
case temperature
case choices = "n"
case stream
case logprobs
case echo
case presencePenalty = "presence_penalty"
case frequencyPenalty = "frequency_penalty"
case user
}
}
32 changes: 32 additions & 0 deletions Sources/SwiftyGPT/Completion/SwiftyGPTCompletionResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// SwiftyGPTCompletionResponse.swift
//
//
// Created by Antonio Guerra on 11/04/23.
//

import Foundation

public struct SwiftyGPTCompletionResponse: SwiftyGPTResponse {
public let id: String
public let object: String
public let created: TimeInterval
public let usage: SwiftyGPTUsage
public let model: SwiftyGPTCompletionModel
public let choices: [SwiftyGPTCompletionChoice]
}

// MARK: - Choice
public struct SwiftyGPTCompletionChoice: SwiftyGPTChoice {
public let finishReason: SwiftyGPTFinishReason
public let index: Int
public let text: String
public let logprobs: Int?

enum CodingKeys: String, CodingKey {
case text
case finishReason = "finish_reason"
case index
case logprobs
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum SwiftyGPTRouter: SwiftyHTTPRequest {

case chat(String, SwiftyGPTChatRequest)
case image(String, SwiftyGPTImageRequest)
case completion(String, SwiftyGPTCompletionRequest)

var baseURL: URL? {
return URL(string: "https://api.openai.com")
Expand All @@ -23,6 +24,8 @@ enum SwiftyGPTRouter: SwiftyHTTPRequest {
return "/v1/chat/completions"
case .image:
return "/v1/images/generations"
case .completion:
return "/v1/completions"
}
}

Expand All @@ -32,12 +35,14 @@ enum SwiftyGPTRouter: SwiftyHTTPRequest {
return .post
case .image:
return .post
case .completion:
return .post
}
}

var headers: [SwiftyHTTPHeader] {
switch self {
case .chat(let apiKey, _), .image(let apiKey, _):
case .chat(let apiKey, _), .image(let apiKey, _), .completion(let apiKey, _):
return [
SwiftyHTTPHeader.contentType(.application(.json)),
SwiftyHTTPHeader.authorization(.bearer(apiKey))
Expand All @@ -55,6 +60,8 @@ enum SwiftyGPTRouter: SwiftyHTTPRequest {
return request
case .image(_, let request):
return request
case .completion(_, let request):
return request
}
}
}
File renamed without changes.
12 changes: 12 additions & 0 deletions Sources/SwiftyGPT/Utils/SwiftyGPTModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// SwiftyGPTModel.swift
//
//
// Created by Antonio Guerra on 11/04/23.
//

import Foundation

protocol SwiftyGPTModel: Codable {
static var stable: Self { get }
}
22 changes: 22 additions & 0 deletions Sources/SwiftyGPT/Utils/SwiftyGPTRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// SwiftyGPTRequest.swift
//
//
// Created by Antonio Guerra on 11/04/23.
//

import Foundation
import SwiftyHTTP

protocol SwiftyGPTRequest: SwiftyHTTPRequestBody {
associatedtype GPTModel: SwiftyGPTModel

var model: GPTModel { get }
var user: String? { get }
var choices: Int? { get }
var maxTokens: Int? { get }
var temperature: Float? { get }
var stream: Bool? { get }
var presencePenalty: Float? { get }
var frequencyPenalty: Float? { get }
}
Loading

0 comments on commit 74a3b24

Please sign in to comment.