Skip to content

Commit d2b17e5

Browse files
Merge pull request #143 from mergesort/main
Implementing cross-platform support by supporting Linux-friendly HTTP clients
2 parents 5e16683 + 6a94d26 commit d2b17e5

20 files changed

+766
-189
lines changed

.github/workflows/ci.yml

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,29 @@ on:
1010
branches: [ "main" ]
1111

1212
jobs:
13-
build_and_test:
13+
build_and_test_macos:
1414
runs-on: macos-latest
1515
steps:
16-
- uses: swift-actions/setup-swift@v2
17-
with:
18-
swift-version: "6.0.1"
16+
- uses: actions/checkout@v4
1917
- name: Get swift version
2018
run: swift --version
19+
- name: Build
20+
run: swift build -q
21+
- name: Run tests
22+
run: swift test -q
23+
24+
build_and_test_linux:
25+
runs-on: ubuntu-latest
26+
container:
27+
image: swift:6.0.1-jammy
28+
steps:
29+
- name: Install dependencies
30+
run: |
31+
apt-get update
32+
apt-get install -y curl git
2133
- uses: actions/checkout@v4
34+
- name: Get swift version
35+
run: swift --version
2236
- name: Build
2337
run: swift build -q
2438
- name: Run tests
@@ -33,7 +47,5 @@ jobs:
3347
uses: Homebrew/actions/setup-homebrew@master
3448
- name: Install swiftformat
3549
run: brew install swiftformat
36-
- name: Run linter
37-
run: swiftformat --config rules.swiftformat .
38-
- name: Verify that `swiftformat --config rules.swiftformat .` did not change outputs (if it did, please re-run it and re-commit!)
39-
run: git diff --exit-code
50+
- name: Check formatting
51+
run: swiftformat --config rules.swiftformat --lint .

Package.resolved

Lines changed: 131 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@ let package = Package(
1616
name: "SwiftOpenAI",
1717
targets: ["SwiftOpenAI"]),
1818
],
19+
dependencies: [
20+
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.25.2"),
21+
],
1922
targets: [
2023
// Targets are the basic building blocks of a package, defining a module or a test suite.
2124
// Targets can depend on other targets in this package and products from dependencies.
2225
.target(
23-
name: "SwiftOpenAI"),
26+
name: "SwiftOpenAI",
27+
dependencies: [
28+
.product(name: "AsyncHTTPClient", package: "async-http-client", condition: .when(platforms: [.linux])),
29+
]),
2430
.testTarget(
2531
name: "SwiftOpenAITests",
2632
dependencies: ["SwiftOpenAI"]),

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
<img width="1090" alt="repoOpenAI" src="https://github.com/jamesrochabrun/SwiftOpenAI/assets/5378604/51bc5736-a32f-4a9f-922e-209d950e28f7">
33

44
![iOS 15+](https://img.shields.io/badge/iOS-15%2B-blue.svg)
5+
![macOS 13+](https://img.shields.io/badge/macOS-13%2B-blue.svg)
6+
![watchOS 9+](https://img.shields.io/badge/watchOS-9%2B-blue.svg)
7+
![Linux](https://img.shields.io/badge/Linux-blue.svg)
58
[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/)
69
[![swift-version](https://img.shields.io/badge/swift-5.9-brightgreen.svg)](https://github.com/apple/swift)
710
[![swiftui-version](https://img.shields.io/badge/swiftui-brightgreen)](https://developer.apple.com/documentation/swiftui)
@@ -100,6 +103,14 @@ to stay on the bleeding edge.
100103

101104
## Compatibility
102105

106+
### Platform Support
107+
108+
SwiftOpenAI supports both Apple platforms and Linux.
109+
- **Apple platforms** include iOS 15+, macOS 13+, and watchOS 9+.
110+
- **Linux**: SwiftOpenAI on Linux uses AsyncHTTPClient to work around URLSession bugs in Apple's Foundation framework, and can be used with the [Vapor](https://vapor.codes/) server framework.
111+
112+
### OpenAI-Compatible Providers
113+
103114
SwiftOpenAI supports various providers that are OpenAI-compatible, including but not limited to:
104115

105116
- [Azure OpenAI](#azure-openai)

Sources/OpenAI/AIProxy/AIProxyCertificatePinning.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// Created by Lou Zell on 6/23/24.
66
//
7-
7+
#if !os(Linux)
88
import Foundation
99
import OSLog
1010

@@ -181,3 +181,4 @@ private func getServerCert(secTrust: SecTrust) -> SecCertificate? {
181181
return SecTrustGetCertificateAtIndex(secTrust, 0)
182182
}
183183
}
184+
#endif

Sources/OpenAI/AIProxy/AIProxyService.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// Created by Lou Zell on 3/27/24.
66
//
7-
7+
#if !os(Linux)
88
import Foundation
99

1010
private let aiproxySecureDelegate = AIProxyCertificatePinningDelegate()
@@ -34,19 +34,20 @@ struct AIProxyService: OpenAIService {
3434
organizationID: String? = nil,
3535
debugEnabled: Bool)
3636
{
37-
session = URLSession(
38-
configuration: .default,
39-
delegate: aiproxySecureDelegate,
40-
delegateQueue: nil)
4137
decoder = JSONDecoder()
4238
self.partialKey = partialKey
4339
self.clientID = clientID
4440
self.organizationID = organizationID
4541
self.debugEnabled = debugEnabled
4642
openAIEnvironment = .init(baseURL: serviceURL ?? "https://api.aiproxy.pro", proxyPath: nil, version: "v1")
43+
httpClient = URLSessionHTTPClientAdapter(
44+
urlSession: URLSession(
45+
configuration: .default,
46+
delegate: aiproxySecureDelegate,
47+
delegateQueue: nil))
4748
}
4849

49-
let session: URLSession
50+
let httpClient: HTTPClient
5051
let decoder: JSONDecoder
5152
let openAIEnvironment: OpenAIEnvironment
5253

@@ -1330,3 +1331,4 @@ struct AIProxyService: OpenAIService {
13301331
private let organizationID: String?
13311332

13321333
}
1334+
#endif

Sources/OpenAI/AIProxy/Endpoint+AIProxy.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// Created by Lou Zell on 3/26/24.
66
//
7-
7+
#if !os(Linux)
88
import DeviceCheck
99
import Foundation
1010
import OSLog
@@ -242,3 +242,4 @@ private func copy_mac_address() -> CFData? {
242242
return nil
243243
}
244244
#endif
245+
#endif

Sources/OpenAI/Azure/DefaultOpenAIAzureService.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,21 @@
66
//
77

88
import Foundation
9+
#if os(Linux)
10+
import FoundationNetworking
11+
#endif
12+
13+
// MARK: - DefaultOpenAIAzureService
914

1015
final public class DefaultOpenAIAzureService: OpenAIService {
1116

1217
public init(
1318
azureConfiguration: AzureOpenAIConfiguration,
14-
urlSessionConfiguration: URLSessionConfiguration = .default,
19+
httpClient: HTTPClient,
1520
decoder: JSONDecoder = .init(),
1621
debugEnabled: Bool)
1722
{
18-
session = URLSession(configuration: urlSessionConfiguration)
23+
self.httpClient = httpClient
1924
self.decoder = decoder
2025
openAIEnvironment = OpenAIEnvironment(
2126
baseURL: "https://\(azureConfiguration.resourceName)/openai.azure.com",
@@ -27,7 +32,7 @@ final public class DefaultOpenAIAzureService: OpenAIService {
2732
self.debugEnabled = debugEnabled
2833
}
2934

30-
public let session: URLSession
35+
public let httpClient: HTTPClient
3136
public let decoder: JSONDecoder
3237
public let openAIEnvironment: OpenAIEnvironment
3338

Sources/OpenAI/LocalModelService/LocalModelService.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ struct LocalModelService: OpenAIService {
1414
baseURL: String,
1515
proxyPath: String? = nil,
1616
overrideVersion: String? = nil,
17-
configuration: URLSessionConfiguration = .default,
17+
httpClient: HTTPClient,
1818
decoder: JSONDecoder = .init(),
1919
debugEnabled: Bool)
2020
{
21-
session = URLSession(configuration: configuration)
21+
self.httpClient = httpClient
2222
self.decoder = decoder
2323
self.apiKey = apiKey
2424
openAIEnvironment = OpenAIEnvironment(baseURL: baseURL, proxyPath: proxyPath, version: overrideVersion ?? "v1")
@@ -49,7 +49,7 @@ struct LocalModelService: OpenAIService {
4949
"Currently, this API is not supported. We welcome and encourage contributions to our open-source project. Please consider opening an issue or submitting a pull request to add support for this feature.")
5050
}
5151

52-
let session: URLSession
52+
let httpClient: HTTPClient
5353
let decoder: JSONDecoder
5454
let openAIEnvironment: OpenAIEnvironment
5555

0 commit comments

Comments
 (0)