2023-01-13 17:43:02 +00:00
import Foundation
2023-03-09 12:46:04 +00:00
protocol OpenAIRequest : Encodable {
var path : String { get }
}
2023-01-13 17:43:02 +00:00
public struct OpenAIClient {
2023-03-09 12:46:04 +00:00
private let endpoint : URL = . init ( string : " https://api.openai.com/v1/ " ) !
2023-01-17 10:36:01 +00:00
2023-01-13 17:43:02 +00:00
private var APIKey : String {
if let path = Bundle . main . path ( forResource : " Secret " , ofType : " plist " ) {
let secret = NSDictionary ( contentsOfFile : path )
return secret ? [ " OPENAI_SECRET " ] as ? String ? ? " "
}
return " "
}
2023-01-17 10:36:01 +00:00
2023-01-13 17:43:02 +00:00
private var authorizationHeaderValue : String {
" Bearer \( APIKey ) "
}
2023-01-17 10:36:01 +00:00
2023-01-13 17:43:02 +00:00
private var encoder : JSONEncoder {
let encoder = JSONEncoder ( )
encoder . keyEncodingStrategy = . convertToSnakeCase
return encoder
}
2023-01-17 10:36:01 +00:00
2023-01-13 17:43:02 +00:00
private var decoder : JSONDecoder {
let decoder = JSONDecoder ( )
decoder . keyDecodingStrategy = . convertFromSnakeCase
return decoder
}
2023-03-09 12:46:04 +00:00
public struct ChatRequest : OpenAIRequest {
public struct Message : Encodable {
public let role = " user "
public let content : String
}
let model = " gpt-3.5-turbo "
let messages : [ Message ]
let temperature : CGFloat
var path : String {
" chat/completions "
}
2023-01-17 10:36:01 +00:00
2023-03-09 12:46:04 +00:00
public init ( content : String , temperature : CGFloat ) {
self . messages = [ . init ( content : content ) ]
2023-01-13 17:43:02 +00:00
self . temperature = temperature
}
}
2023-03-09 12:46:04 +00:00
2023-02-21 17:52:30 +00:00
public enum Prompt {
2023-01-13 17:43:02 +00:00
case correct ( input : String )
case shorten ( input : String )
case emphasize ( input : String )
2023-03-09 12:46:04 +00:00
case addTags ( input : String )
case insertTags ( input : String )
2023-01-17 10:36:01 +00:00
2023-03-09 12:46:04 +00:00
var request : OpenAIRequest {
2023-01-13 17:43:02 +00:00
switch self {
case let . correct ( input ) :
2023-03-09 12:46:04 +00:00
return ChatRequest ( content : " Fix the spelling and grammar mistakes in the following text: \( input ) " , temperature : 0.2 )
case let . addTags ( input ) :
return ChatRequest ( content : " Replace relevant words with Twitter hashtags in the following text while keeping the input same. Maximum of 5 hashtags: \( input ) " , temperature : 0.1 )
case let . insertTags ( input ) :
return ChatRequest ( content : " Return the input with added Twitter hashtags at the end of the input with a maximum of 5 hashtags: \( input ) " , temperature : 0.2 )
2023-01-13 17:43:02 +00:00
case let . shorten ( input ) :
2023-03-09 12:46:04 +00:00
return ChatRequest ( content : " Make a shorter version of this text: \( input ) " , temperature : 0.5 )
2023-01-13 17:43:02 +00:00
case let . emphasize ( input ) :
2023-03-09 12:46:04 +00:00
return ChatRequest ( content : " Make this text catchy, more fun: \( input ) " , temperature : 1 )
2023-01-13 17:43:02 +00:00
}
}
}
2023-01-17 10:36:01 +00:00
2023-01-13 17:43:02 +00:00
public struct Response : Decodable {
public struct Choice : Decodable {
2023-03-09 12:46:04 +00:00
public struct Message : Decodable {
public let role : String
public let content : String
}
public let message : Message ?
2023-01-13 17:43:02 +00:00
}
2023-01-17 10:36:01 +00:00
2023-01-13 17:43:02 +00:00
public let choices : [ Choice ]
2023-01-22 05:38:30 +00:00
2023-01-17 18:41:46 +00:00
public var trimmedText : String {
2023-03-09 12:46:04 +00:00
guard var text = choices . first ? . message ? . content else {
2023-01-17 18:41:46 +00:00
return " "
}
while text . first ? . isNewline = = true || text . first ? . isWhitespace = = true {
text . removeFirst ( )
}
return text
}
2023-01-13 17:43:02 +00:00
}
2023-01-17 10:36:01 +00:00
public init ( ) { }
2023-02-21 17:52:30 +00:00
public func request ( _ prompt : Prompt ) async throws -> Response {
2023-01-13 17:43:02 +00:00
do {
let jsonData = try encoder . encode ( prompt . request )
2023-03-09 12:46:04 +00:00
var request = URLRequest ( url : endpoint . appending ( path : prompt . request . path ) )
2023-01-13 17:43:02 +00:00
request . httpMethod = " POST "
request . setValue ( authorizationHeaderValue , forHTTPHeaderField : " Authorization " )
request . setValue ( " application/json " , forHTTPHeaderField : " Content-Type " )
request . httpBody = jsonData
let ( result , _ ) = try await URLSession . shared . data ( for : request )
let response = try decoder . decode ( Response . self , from : result )
return response
2023-01-17 10:36:01 +00:00
} catch {
2023-01-13 17:43:02 +00:00
throw error
}
}
}
2023-02-27 05:39:07 +00:00
extension OpenAIClient : Sendable { }
extension OpenAIClient . Prompt : Sendable { }
2023-03-09 12:46:04 +00:00
extension OpenAIClient . ChatRequest : Sendable { }
extension OpenAIClient . ChatRequest . Message : Sendable { }
2023-02-27 05:39:07 +00:00
extension OpenAIClient . Response : Sendable { }
extension OpenAIClient . Response . Choice : Sendable { }
2023-03-09 12:46:04 +00:00
extension OpenAIClient . Response . Choice . Message : Sendable { }