IceCubesApp/Packages/Network/Sources/Network/OpenAIClient.swift

134 lines
4.3 KiB
Swift
Raw Normal View History

2023-01-13 17:43:02 +00:00
import Foundation
protocol OpenAIRequest: Encodable {
var model: String { get }
}
2023-01-13 17:43:02 +00:00
public struct OpenAIClient {
private let endpoint: URL = .init(string: "https://icecubesrelay.fly.dev/openai")!
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-13 12:38:28 +00:00
public struct ChatRequest: OpenAIRequest {
public struct Message: Encodable {
public let role = "user"
public let content: String
}
2023-03-13 12:38:28 +00:00
let model = "gpt-3.5-turbo"
let messages: [Message]
2023-03-13 12:38:28 +00:00
let temperature: CGFloat
2023-03-13 12:38:28 +00:00
public init(content: String, temperature: CGFloat) {
2023-03-13 12:38:28 +00:00
messages = [.init(content: content)]
2023-01-13 17:43:02 +00:00
self.temperature = temperature
}
}
public struct VisionRequest: OpenAIRequest {
public struct Message: Encodable {
public struct MessageContent: Encodable {
public struct ImageUrl: Encodable {
public let url: URL
}
public let type: String
public let text: String?
public let imageUrl: ImageUrl?
}
public let role = "user"
public let content: [MessageContent]
}
let model = "gpt-4-vision-preview"
let messages: [Message]
let maxTokens = 50
}
2023-03-13 12:38:28 +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)
case addTags(input: String)
case insertTags(input: String)
case imageDescription(image: URL)
2023-01-17 10:36:01 +00:00
var request: OpenAIRequest {
2023-01-13 17:43:02 +00:00
switch self {
case let .correct(input):
2023-09-16 12:15:03 +00:00
ChatRequest(content: "Fix the spelling and grammar mistakes in the following text: \(input)", temperature: 0.2)
case let .addTags(input):
2023-09-16 12:15:03 +00:00
ChatRequest(content: "Replace relevant words with camel-cased hashtags in the following text. Don't try to search for context or add hashtags if there is not enough context: \(input)", temperature: 0.1)
case let .insertTags(input):
2023-09-16 12:15:03 +00:00
ChatRequest(content: "Return the input with added camel-cased hashtags at the end of the input. Don't try to search for context or add hashtags if there is not enough context: \(input)", temperature: 0.2)
2023-01-13 17:43:02 +00:00
case let .shorten(input):
2023-09-16 12:15:03 +00:00
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-09-16 12:15:03 +00:00
ChatRequest(content: "Make this text catchy, more fun: \(input)", temperature: 1)
case let .imageDescription(image):
2023-12-04 19:36:15 +00:00
VisionRequest(messages: [.init(content: [.init(type: "text", text: "Whats in this image? Be brief, it's for image alt description on a social network. Don't write in the first person.", imageUrl: nil)
, .init(type: "image_url", text: nil, imageUrl: .init(url: image))])])
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 {
public struct Message: Decodable {
public let role: String
public let content: String
}
2023-03-13 12:38:28 +00:00
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
public var trimmedText: String {
guard var text = choices.first?.message?.content else {
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)
var request = URLRequest(url: endpoint)
2023-01-13 17:43:02 +00:00
request.httpMethod = "POST"
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
}
}
}
extension OpenAIClient: Sendable {}
extension OpenAIClient.Prompt: Sendable {}
extension OpenAIClient.ChatRequest: Sendable {}
extension OpenAIClient.ChatRequest.Message: Sendable {}
extension OpenAIClient.Response: Sendable {}
extension OpenAIClient.Response.Choice: Sendable {}
extension OpenAIClient.Response.Choice.Message: Sendable {}