From 75192adde7d74de374182509424613ec979c77a4 Mon Sep 17 00:00:00 2001 From: Alexey Strokin Date: Sun, 15 Oct 2023 23:35:36 +0300 Subject: [PATCH 1/2] add more sense to readme, fix 400 error because of id { "error": { "message": "Additional properties are not allowed ('id' was unexpected) - 'messages.0'", "type": "invalid_request_error", "param": null, "code": null } } --- README.md | 31 +++++++++++++++++++- Sources/OpenAISwift/Models/ChatMessage.swift | 12 ++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e15341a..a15ec31 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,36 @@ Import the framework in your project: [Create an OpenAI API key](https://platform.openai.com/account/api-keys) and add it to your configuration: -`let openAI = OpenAISwift(authToken: "TOKEN")` +`let openAI: OpenAISwift = OpenAISwift(config: OpenAISwift.Config.makeDefaultOpenAI(apiKey: "TOKEN"))` + + +To follow [OpenAI requirements](https://platform.openai.com/docs/api-reference/authentication) + +> Remember that your API key is a secret! Do not share it with others or expose it in any client-side code (browsers, apps). Production requests must be routed through your own backend server where your API key can be securely loaded from an environment variable or key management service. + +and basic industrial safety you should not call OpenAI API directly. + +```swift +private lazy var proxyOpenAIBackend: OpenAISwift = .init( + config: OpenAISwift.Config( + baseURL: "http://localhost", + endpointPrivider: OpenAIEndpointProvider(source: .proxy(path: { _ -> String in + "/chat/completions" + }, method: { _ -> String in + "POST" + })), + session: session, + authorizeRequest: { [weak self] request in + self?.authorizeRequest(&request) + } + )) + + private func authorizeRequest(_ request: inout URLRequest) { + if let apiKey = try? Encryptor.getApiToken() { + request.setValue(apiKey, forHTTPHeaderField: "X-API-KEY") + } + } +``` This framework supports Swift concurrency; each example below has both an async/await and completion handler variant. diff --git a/Sources/OpenAISwift/Models/ChatMessage.swift b/Sources/OpenAISwift/Models/ChatMessage.swift index c987cf9..a99b0c9 100644 --- a/Sources/OpenAISwift/Models/ChatMessage.swift +++ b/Sources/OpenAISwift/Models/ChatMessage.swift @@ -34,6 +34,18 @@ public struct ChatMessage: Codable, Identifiable { self.role = role self.content = content } + + + // MARK: - Custom Encoding + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(role, forKey: .role) + try container.encodeIfPresent(content, forKey: .content) + } + + private enum CodingKeys: String, CodingKey { + case role, content + } } /// A structure that represents a chat conversation. From db66252954f64c59111abb1932866039a64ef029 Mon Sep 17 00:00:00 2001 From: Alexey Strokin Date: Mon, 16 Oct 2023 11:52:21 +0300 Subject: [PATCH 2/2] improve error handling --- Sources/OpenAISwift/OpenAISwift.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/OpenAISwift/OpenAISwift.swift b/Sources/OpenAISwift/OpenAISwift.swift index 67090ac..b8f3899 100644 --- a/Sources/OpenAISwift/OpenAISwift.swift +++ b/Sources/OpenAISwift/OpenAISwift.swift @@ -5,6 +5,7 @@ import FoundationXML #endif public enum OpenAIError: Error { + case networkError(code: Int) case genericError(error: Error) case decodingError(error: Error) case chatError(error: ChatError.Payload) @@ -294,11 +295,15 @@ extension OpenAISwift { let task = session.dataTask(with: request) { (data, response, error) in if let error = error { completionHandler(.failure(error)) + } else if let response = response as? HTTPURLResponse, !(200...299).contains(response.statusCode) { + completionHandler(.failure(OpenAIError.networkError(code: response.statusCode))) } else if let data = data { completionHandler(.success(data)) + } else { + let error = NSError(domain: "OpenAI", code: 6666, userInfo: [NSLocalizedDescriptionKey: "Unknown error"]) + completionHandler(.failure(OpenAIError.genericError(error: error))) } } - task.resume() }