From 5d2cfae6ab177e15b2e956f29b59373be8f89aaa Mon Sep 17 00:00:00 2001 From: Udo Schneider Date: Wed, 31 Jul 2024 13:56:41 +0200 Subject: [PATCH 1/2] feat: Add configurable endpoint option - Introduced a new initializer that accepts a fully configured endpoint URL. - The existing initializer (accepting `host`, `port`, and `scheme`) remains available and now also configures the endpoint. - Converted the previously stored `host`, `port`, and `scheme` properties to computed properties based on the endpoint to maintain API compatibility. --- Sources/OpenAI/OpenAI.swift | 32 ++++++++++++++++++----------- Tests/OpenAITests/OpenAITests.swift | 7 +++++++ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/Sources/OpenAI/OpenAI.swift b/Sources/OpenAI/OpenAI.swift index 5ff52833..44983f84 100644 --- a/Sources/OpenAI/OpenAI.swift +++ b/Sources/OpenAI/OpenAI.swift @@ -21,18 +21,31 @@ final public class OpenAI: OpenAIProtocol { public let organizationIdentifier: String? /// API host. Set this property if you use some kind of proxy or your own server. Default is api.openai.com - public let host: String - public let port: Int - public let scheme: String + public let endpoint : URL + var scheme : String {endpoint.scheme ?? "https"} + var host : String {endpoint.host ?? "api.openai.com"} + var port : Int {endpoint.port ?? 443} + /// Default request timeout public let timeoutInterval: TimeInterval public init(token: String, organizationIdentifier: String? = nil, host: String = "api.openai.com", port: Int = 443, scheme: String = "https", timeoutInterval: TimeInterval = 60.0) { self.token = token self.organizationIdentifier = organizationIdentifier - self.host = host - self.port = port - self.scheme = scheme + + var components = URLComponents() + components.scheme = scheme + components.host = host + components.port = port + + self.endpoint = components.url! + self.timeoutInterval = timeoutInterval + } + + public init(token: String, organizationIdentifier: String? = nil, endpoint: URL, timeoutInterval: TimeInterval = 60.0) { + self.token = token + self.organizationIdentifier = organizationIdentifier + self.endpoint = endpoint self.timeoutInterval = timeoutInterval } } @@ -198,12 +211,7 @@ extension OpenAI { extension OpenAI { func buildURL(path: String) -> URL { - var components = URLComponents() - components.scheme = configuration.scheme - components.host = configuration.host - components.port = configuration.port - components.path = path - return components.url! + return configuration.endpoint.appendingPathComponent(path) } } diff --git a/Tests/OpenAITests/OpenAITests.swift b/Tests/OpenAITests/OpenAITests.swift index bae3e015..ee1f57c3 100644 --- a/Tests/OpenAITests/OpenAITests.swift +++ b/Tests/OpenAITests/OpenAITests.swift @@ -401,6 +401,13 @@ class OpenAITests: XCTestCase { let chatsURL = openAI.buildURL(path: .chats) XCTAssertEqual(chatsURL, URL(string: "https://my.host.com:443/v1/chat/completions")) } + + func testCustomEndpointURLBuilt() { + let configuration = OpenAI.Configuration(token: "foo", organizationIdentifier: "bar", endpoint: URL(string: "https://api.internal.company.com:443/prod/aiendpoint")!, timeoutInterval: 14) + let openAI = OpenAI(configuration: configuration, session: self.urlSession) + let chatsURL = openAI.buildURL(path: .chats) + XCTAssertEqual(chatsURL, URL(string: "https://api.internal.company.com:443/prod/aiendpoint/v1/chat/completions")) + } } @available(tvOS 13.0, *) From 53249e638e403cf6e702e810a203098891cf5704 Mon Sep 17 00:00:00 2001 From: Udo Schneider Date: Thu, 1 Aug 2024 10:04:19 +0200 Subject: [PATCH 2/2] feat: Enable full endpoint customization, including version path segment This update allows the `endpoint:URL` to specify the entire `basePath`, including the version path segment (e.g., `/v1`). --- Sources/OpenAI/OpenAI.swift | 25 +++++++++++++------------ Tests/OpenAITests/OpenAITests.swift | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Sources/OpenAI/OpenAI.swift b/Sources/OpenAI/OpenAI.swift index 44983f84..de6632db 100644 --- a/Sources/OpenAI/OpenAI.swift +++ b/Sources/OpenAI/OpenAI.swift @@ -37,6 +37,7 @@ final public class OpenAI: OpenAIProtocol { components.scheme = scheme components.host = host components.port = port + components.path = "/v1" self.endpoint = components.url! self.timeoutInterval = timeoutInterval @@ -218,20 +219,20 @@ extension OpenAI { typealias APIPath = String extension APIPath { - static let completions = "/v1/completions" - static let embeddings = "/v1/embeddings" - static let chats = "/v1/chat/completions" - static let edits = "/v1/edits" - static let models = "/v1/models" - static let moderations = "/v1/moderations" + static let completions = "/completions" + static let embeddings = "/embeddings" + static let chats = "/chat/completions" + static let edits = "/edits" + static let models = "/models" + static let moderations = "/moderations" - static let audioSpeech = "/v1/audio/speech" - static let audioTranscriptions = "/v1/audio/transcriptions" - static let audioTranslations = "/v1/audio/translations" + static let audioSpeech = "/audio/speech" + static let audioTranscriptions = "/audio/transcriptions" + static let audioTranslations = "/audio/translations" - static let images = "/v1/images/generations" - static let imageEdits = "/v1/images/edits" - static let imageVariations = "/v1/images/variations" + static let images = "/images/generations" + static let imageEdits = "/images/edits" + static let imageVariations = "/images/variations" func withPath(_ path: String) -> String { self + "/" + path diff --git a/Tests/OpenAITests/OpenAITests.swift b/Tests/OpenAITests/OpenAITests.swift index ee1f57c3..fcefa6cf 100644 --- a/Tests/OpenAITests/OpenAITests.swift +++ b/Tests/OpenAITests/OpenAITests.swift @@ -403,7 +403,7 @@ class OpenAITests: XCTestCase { } func testCustomEndpointURLBuilt() { - let configuration = OpenAI.Configuration(token: "foo", organizationIdentifier: "bar", endpoint: URL(string: "https://api.internal.company.com:443/prod/aiendpoint")!, timeoutInterval: 14) + let configuration = OpenAI.Configuration(token: "foo", organizationIdentifier: "bar", endpoint: URL(string: "https://api.internal.company.com:443/prod/aiendpoint/v1")!, timeoutInterval: 14) let openAI = OpenAI(configuration: configuration, session: self.urlSession) let chatsURL = openAI.buildURL(path: .chats) XCTAssertEqual(chatsURL, URL(string: "https://api.internal.company.com:443/prod/aiendpoint/v1/chat/completions"))