Skip to content

Commit

Permalink
Merge branch 'release/1.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
antonio-war committed Mar 29, 2023
2 parents 426b82f + 787a895 commit 5979bb8
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 149 deletions.
13 changes: 11 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/antonio-war/SwiftyHTTP",
"state" : {
"revision" : "cd1437c5d5ea4fd87767d3af7b063dd9237a954a",
"version" : "0.1.3"
"revision" : "b4fe68fc801df31b68e8c53fc63b4575d7b264f9",
"version" : "0.1.4"
}
},
{
"identity" : "swiftyranged",
"kind" : "remoteSourceControl",
"location" : "https://github.com/antonio-war/SwiftyRanged",
"state" : {
"revision" : "8ee2f1b071fe5b06334aeab7555df560e08ae555",
"version" : "1.0.0"
}
}
],
Expand Down
10 changes: 3 additions & 7 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,18 @@ let package = Package(
name: "SwiftyGPT",
platforms: [.macOS(.v13), .iOS(.v13)],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "SwiftyGPT",
targets: ["SwiftyGPT"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/antonio-war/SwiftyHTTP", from: "0.1.2")
.package(url: "https://github.com/antonio-war/SwiftyHTTP", from: "0.1.4"),
.package(url: "https://github.com/antonio-war/SwiftyRanged", from: "1.0.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "SwiftyGPT",
dependencies: ["SwiftyHTTP"]),
dependencies: ["SwiftyHTTP", "SwiftyRanged"]),
.testTarget(
name: "SwiftyGPTTests",
dependencies: ["SwiftyGPT", "SwiftyHTTP"]),
Expand Down
69 changes: 41 additions & 28 deletions Sources/SwiftyGPT/SwiftyGPT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,64 @@

import Foundation
import SwiftyHTTP
import SwiftyRanged

public class SwiftyGPT: ObservableObject {
public struct SwiftyGPT {

let apiKey: String
@Published var conversation: [SwiftyGPTMessage]

public init(apiKey: String) {
self.apiKey = apiKey
self.conversation = []
}

// MARK: - Chat

public func chat(messages: [SwiftyGPTMessage], model: SwiftyGPTModel = .stable, @Ranged(0...2) temperature: Float = 1, choices: Int = 1, @Ranged(-2.0...2.0) presencePenalty: Float = 0, @Ranged(-2.0...2.0) frequencyPenalty: Float = 0, user: String? = nil, completion: @escaping (Result<SwiftyGPTResponse, Error>) -> ()) {
let request = SwiftyGPTRequest(messages: messages, model: model, temperature: temperature, choices: choices, stream: false, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user)
SwiftyHTTP.request(with: SwiftyGPTRouter.chat(apiKey, request), body: SwiftyGPTResponse.self) { result in
public func chat(messages: [SwiftyGPTMessage], model: SwiftyGPTModel = .stable, @SwiftyOptionalRanged(0...2) temperature: Float? = nil, choices: Int? = nil, @SwiftyOptionalRanged(0...4096) maxTokens: Int? = nil, @SwiftyOptionalRanged(-2...2) presencePenalty: Float? = nil, @SwiftyOptionalRanged(-2...2) frequencyPenalty: Float? = nil, user: String? = nil, completion: @escaping (Result<SwiftyGPTResponse, Error>) -> ()) {

let request = SwiftyGPTRequest(messages: messages, model: model, temperature: temperature, choices: choices, stream: false, maxTokens: maxTokens, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user)
SwiftyHTTP.request(with: SwiftyGPTRouter.chat(apiKey, request)) { result in
switch result {
case .success(let response):
self.conversation.append(contentsOf: request.messages)
self.conversation.append(contentsOf: response.body.choices.compactMap({ $0.message }))
completion(.success(response.body))
if response.statusCode == 200 {
guard let body = try? JSONDecoder().decode(SwiftyGPTResponse.self, from: response.body) else {
completion(.failure(URLError(.badServerResponse)))
return
}
completion(.success(body))
} 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 chat(messages: [SwiftyGPTMessage], model: SwiftyGPTModel = .stable, @Ranged(0...2) temperature: Float = 1, choices: Int = 1, @Ranged(-2.0...2.0) presencePenalty: Float = 0, @Ranged(-2.0...2.0) frequencyPenalty: Float = 0, user: String? = nil) async -> Result<SwiftyGPTResponse, Error> {
public func chat(messages: [SwiftyGPTMessage], model: SwiftyGPTModel = .stable, @SwiftyOptionalRanged(0...2) temperature: Float? = nil, choices: Int? = nil, @SwiftyOptionalRanged(0...4096) maxTokens: Int? = nil, @SwiftyOptionalRanged(-2...2) presencePenalty: Float? = nil, @SwiftyOptionalRanged(-2...2) frequencyPenalty: Float? = nil, user: String? = nil) async -> Result<SwiftyGPTResponse, Error> {

return await withCheckedContinuation { continuation in
chat(messages: messages, model: model, temperature: temperature, choices: choices, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user) { result in
chat(messages: messages, model: model, temperature: temperature, choices: choices, maxTokens: maxTokens, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user) { result in
continuation.resume(returning: result)
}
}

}

public func chat(message: SwiftyGPTMessage, model: SwiftyGPTModel = .stable, @Ranged(0...2) temperature: Float = 1, choices: Int = 1, @Ranged(-2.0...2.0) presencePenalty: Float = 0, @Ranged(-2.0...2.0) frequencyPenalty: Float = 0, user: String? = nil, completion: @escaping (Result<SwiftyGPTResponse, Error>) -> ()) {
chat(messages: [message], model: model, temperature: temperature, choices: choices, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user, completion: completion)
public func chat(message: SwiftyGPTMessage, model: SwiftyGPTModel = .stable, @SwiftyOptionalRanged(0...2) temperature: Float? = nil, choices: Int? = nil, @SwiftyOptionalRanged(0...4096) maxTokens: Int? = nil, @SwiftyOptionalRanged(-2...2) presencePenalty: Float? = nil, @SwiftyOptionalRanged(-2...2) frequencyPenalty: Float? = nil, user: String? = nil, completion: @escaping (Result<SwiftyGPTResponse, Error>) -> ()) {
chat(messages: [message], model: model, temperature: temperature, choices: choices, maxTokens: maxTokens, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user, completion: completion)
}

public func chat(message: SwiftyGPTMessage, model: SwiftyGPTModel = .stable, @Ranged(0...2) temperature: Float = 1, choices: Int = 1, @Ranged(-2.0...2.0) presencePenalty: Float = 0, @Ranged(-2.0...2.0) frequencyPenalty: Float = 0, user: String? = nil) async -> Result<SwiftyGPTResponse, Error> {
await chat(messages: [message], model: model, temperature: temperature, choices: choices, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user)

public func chat(message: SwiftyGPTMessage, model: SwiftyGPTModel = .stable, @SwiftyOptionalRanged(0...2) temperature: Float? = nil, choices: Int? = nil, @SwiftyOptionalRanged(0...4096) maxTokens: Int? = nil, @SwiftyOptionalRanged(-2...2) presencePenalty: Float? = nil, @SwiftyOptionalRanged(-2...2) frequencyPenalty: Float? = nil, user: String? = nil) async -> Result<SwiftyGPTResponse, Error> {

await chat(messages: [message], model: model, temperature: temperature, choices: choices, maxTokens: maxTokens, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user)
}
public func chat(messages: [String], model: SwiftyGPTModel = .stable, @Ranged(0...2) temperature: Float = 1, @Ranged(-2.0...2.0) presencePenalty: Float = 0, @Ranged(-2.0...2.0) frequencyPenalty: Float = 0, user: String? = nil, completion: @escaping (Result<String, Error>) -> ()) {
chat(messages: messages.map({SwiftyGPTMessage(content: $0)}), model: model, temperature: temperature, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user) { result in

public func chat(messages: [String], model: SwiftyGPTModel = .stable, user: String? = nil, completion: @escaping (Result<String, Error>) -> ()) {
chat(messages: messages.map({SwiftyGPTMessage(content: $0)}), model: model, user: user) { result in
switch result {
case .success(let response):
guard let message = response.choices.first?.message else {
Expand All @@ -64,20 +77,20 @@ public class SwiftyGPT: ObservableObject {
}
}
}
public func chat(messages: [String], model: SwiftyGPTModel = .stable, @Ranged(0...2) temperature: Float = 1, @Ranged(-2.0...2.0) presencePenalty: Float = 0, @Ranged(-2.0...2.0) frequencyPenalty: Float = 0, user: String? = nil) async -> Result<String, Error> {

public func chat(messages: [String], model: SwiftyGPTModel = .stable, user: String? = nil) async -> Result<String, Error> {
return await withCheckedContinuation { continuation in
chat(messages: messages, model: model, temperature: temperature, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user) { result in
chat(messages: messages, model: model, user: user) { result in
continuation.resume(returning: result)
}
}
}
public func chat(message: String, model: SwiftyGPTModel = .stable, @Ranged(0...2) temperature: Float = 1, @Ranged(-2.0...2.0) presencePenalty: Float = 0, @Ranged(-2.0...2.0) frequencyPenalty: Float = 0, user: String? = nil, completion: @escaping (Result<String, Error>) -> ()) {
chat(messages: [message], model: model, temperature: temperature, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user, completion: completion)

public func chat(message: String, model: SwiftyGPTModel = .stable, user: String? = nil, completion: @escaping (Result<String, Error>) -> ()) {
chat(messages: [message], model: model, user: user, completion: completion)
}
public func chat(message: String, model: SwiftyGPTModel = .stable, @Ranged(0...2) temperature: Float = 1, @Ranged(-2.0...2.0) presencePenalty: Float = 0, @Ranged(-2.0...2.0) frequencyPenalty: Float = 0, user: String? = nil) async -> Result<String, Error> {
await chat(messages: [message], model: model, temperature: temperature, presencePenalty: presencePenalty, frequencyPenalty: frequencyPenalty, user: user)

public func chat(message: String, model: SwiftyGPTModel = .stable, user: String? = nil) async -> Result<String, Error> {
await chat(messages: [message], model: model, user: user)
}
}
30 changes: 30 additions & 0 deletions Sources/SwiftyGPT/SwiftyGPTError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// SwiftyGPTError.swift
//
//
// Created by Antonio Guerra on 29/03/23.
//

import Foundation

public struct SwiftyGPTError: Error, Decodable {
let message, type, code: String

enum WrapperCodingKeys: CodingKey {
case error
}

enum CodingKeys: CodingKey {
case message
case type
case code
}

public init(from decoder: Decoder) throws {
let wrapper = try decoder.container(keyedBy: WrapperCodingKeys.self)
let container = try wrapper.nestedContainer(keyedBy: CodingKeys.self, forKey: .error)
self.message = try container.decode(String.self, forKey: .message)
self.type = try container.decode(String.self, forKey: .type)
self.code = try container.decode(String.self, forKey: .code)
}
}
2 changes: 1 addition & 1 deletion Sources/SwiftyGPT/SwiftyGPTMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

public struct SwiftyGPTMessage: Codable, Identifiable, Datable {
public struct SwiftyGPTMessage: Codable, Identifiable {
public let id: UUID = UUID()
public let date: Date = Date()
public let role: SwiftyGPTRole
Expand Down
16 changes: 9 additions & 7 deletions Sources/SwiftyGPT/SwiftyGPTRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
import Foundation
import SwiftyHTTP

public struct SwiftyGPTRequest: SwiftyHTTPBody {
public struct SwiftyGPTRequest: SwiftyHTTPRequestBody {
public let messages: [SwiftyGPTMessage]
public let model: SwiftyGPTModel
public let temperature: Float
public let choices: Int
public let stream: Bool
public let presencePenalty: Float
public let frequencyPenalty: Float
public let temperature: Float?
public let choices: Int?
public let stream: Bool?
public let maxTokens: Int?
public let presencePenalty: Float?
public let frequencyPenalty: Float?
public let user: String?

enum CodingKeys: String, CodingKey {
Expand All @@ -24,8 +25,9 @@ public struct SwiftyGPTRequest: SwiftyHTTPBody {
case temperature
case choices = "n"
case stream
case user
case maxTokens = "max_tokens"
case presencePenalty = "presence_penalty"
case frequencyPenalty = "frequency_penalty"
case user
}
}
2 changes: 1 addition & 1 deletion Sources/SwiftyGPT/SwiftyGPTResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation
import SwiftyHTTP

public struct SwiftyGPTResponse: SwiftyHTTPBody {
public struct SwiftyGPTResponse: SwiftyHTTPResponseBody {
public let id, object: String
public let created: TimeInterval
public let model: SwiftyGPTModel
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftyGPT/SwiftyGPTRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation
import SwiftyHTTP

enum SwiftyGPTRouter: SwiftyHTTPRouter {
enum SwiftyGPTRouter: SwiftyHTTPRequest {

case chat(String, SwiftyGPTRequest)

Expand Down Expand Up @@ -44,7 +44,7 @@ enum SwiftyGPTRouter: SwiftyHTTPRouter {
[]
}

var body: SwiftyHTTPBody? {
var body: SwiftyHTTPRequestBody? {
switch self {
case .chat(_, let request):
return request
Expand Down
12 changes: 0 additions & 12 deletions Sources/SwiftyGPT/Utils/Datable.swift

This file was deleted.

10 changes: 0 additions & 10 deletions Sources/SwiftyGPT/Utils/Rangeable.swift

This file was deleted.

44 changes: 0 additions & 44 deletions Sources/SwiftyGPT/Utils/Ranged.swift

This file was deleted.

40 changes: 40 additions & 0 deletions Tests/SwiftyGPTTests/SwiftyGPTErrorTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// SwiftyGPTErrorTests.swift
//
//
// Created by Antonio Guerra on 29/03/23.
//

import XCTest
@testable import SwiftyGPT

final class SwiftyGPTErrorTests: XCTestCase {

var jsonString: String!

override func setUpWithError() throws {
try super.setUpWithError()
jsonString = """
{
"error": {
"message": "Incorrect API key provided: sk-zVAMH****************************************o25x. You can find your API key at https://platform.openai.com/account/api-keys.",
"type": "invalid_request_error",
"param": null,
"code": "invalid_api_key"
}
}
"""
}

override func tearDownWithError() throws {
try super.tearDownWithError()
jsonString = nil
}

func testDecode() throws {
let data = jsonString.data(using: .utf8)
XCTAssertNotNil(data)
let error = try JSONDecoder().decode(SwiftyGPTError.self, from: data!)
XCTAssertNotNil(error)
}
}
Loading

0 comments on commit 5979bb8

Please sign in to comment.