Skip to content

Commit 4ba0e04

Browse files
authored
v2
2 parents 836cf23 + dfeac6b commit 4ba0e04

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2798
-253
lines changed

.github/workflows/docs.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Docs
2+
3+
on:
4+
push:
5+
tags: ['*']
6+
branches: [main]
7+
8+
jobs:
9+
build:
10+
runs-on: macos-latest
11+
steps:
12+
- uses: swift-actions/setup-swift@v2
13+
14+
- uses: actions/checkout@v4
15+
16+
- name: Restore .build
17+
id: restore-build
18+
uses: actions/cache/restore@v4
19+
with:
20+
path: .build
21+
restore-keys: "swiftpm-docs-build-${{ runner.os }}-"
22+
key: "swiftpm-docs-build-${{ runner.os }}-${{ github.event.pull_request.base.sha || github.event.after }}"
23+
24+
- name: Generate documentation
25+
run: |
26+
swift package --allow-writing-to-directory ./ResponsesAPI.doccarchive generate-documentation --target ResponsesAPI --disable-indexing --experimental-documentation-coverage --diagnostic-filter error --output-path ./ResponsesAPI.doccarchive
27+
tar -cf ResponsesAPI.doccarchive.tar ./ResponsesAPI.doccarchive/data ./ResponsesAPI.doccarchive/index ./ResponsesAPI.doccarchive/metadata.json ./ResponsesAPI.doccarchive/documentation-coverage.json
28+
29+
- name: Cache .build
30+
if: steps.restore-build.outputs.cache-hit != 'true'
31+
uses: actions/cache/save@v4
32+
with:
33+
path: .build
34+
key: "swiftpm-docs-build-${{ runner.os }}-${{ github.event.pull_request.base.sha || github.event.after }}"
35+
36+
- name: Update latest documentation
37+
id: github-pages
38+
if: github.ref_type != 'tag'
39+
uses: actions/upload-artifact@v4
40+
with:
41+
retention-days: 1
42+
name: github-pages
43+
if-no-files-found: error
44+
path: ./ResponsesAPI.doccarchive.tar
45+
46+
- name: Attach to release
47+
if: github.ref_type == 'tag'
48+
uses: softprops/action-gh-release@v2
49+
with:
50+
files: ./ResponsesAPI.doccarchive.tar
51+
deploy:
52+
needs: build
53+
runs-on: ubuntu-latest
54+
if: github.ref_type != 'tag'
55+
permissions:
56+
pages: write
57+
id-token: write
58+
environment:
59+
name: github-pages
60+
url: ${{ steps.deployment.outputs.page_url }}
61+
steps:
62+
- name: Deploy to GitHub Pages
63+
id: deployment
64+
uses: actions/deploy-pages@v4

.github/workflows/test.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
build:
10+
runs-on: macos-latest
11+
steps:
12+
- uses: swift-actions/setup-swift@v2
13+
14+
- uses: actions/checkout@v4
15+
16+
- name: Restore .build
17+
id: restore-build
18+
uses: actions/cache/restore@v4
19+
with:
20+
path: .build
21+
restore-keys: "swiftpm-tests-build-${{ runner.os }}-"
22+
key: "swiftpm-tests-build-${{ runner.os }}-${{ github.event.pull_request.base.sha || github.event.after }}"
23+
24+
25+
- name: Build
26+
run: swift build --build-tests --enable-code-coverage
27+
28+
- name: Cache .build
29+
if: steps.restore-build.outputs.cache-hit != 'true'
30+
uses: actions/cache/save@v4
31+
with:
32+
path: .build
33+
key: "swiftpm-tests-build-${{ runner.os }}-${{ github.event.pull_request.base.sha || github.event.after }}"
34+
35+
- name: Run tests
36+
run: swift test --skip-build --enable-code-coverage --parallel
37+
38+
- name: Test coverage
39+
uses: maxep/[email protected]
40+
with:
41+
output-file: ./coverage/lcov.info

Package.swift

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// swift-tools-version: 6.0
22

33
import PackageDescription
4+
import CompilerPluginSupport
45

56
let package = Package(
6-
name: "OpenAI",
7+
name: "ResponsesAPI",
78
platforms: [
89
.iOS(.v17),
910
.tvOS(.v17),
@@ -13,19 +14,43 @@ let package = Package(
1314
.macCatalyst(.v17),
1415
],
1516
products: [
16-
.library(name: "OpenAI", type: .static, targets: ["OpenAI"]),
17+
.library(name: "ResponsesAPI", targets: ["ResponsesAPI"]),
1718
],
1819
dependencies: [
1920
.package(url: "https://github.com/SwiftyLab/MetaCodable.git", from: "1.0.0"),
21+
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.1.0"),
22+
.package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.6.0"),
23+
.package(url: "https://github.com/swiftlang/swift-syntax.git", "600.0.1"..<"603.0.0"),
2024
],
2125
targets: [
2226
.target(
23-
name: "OpenAI",
27+
name: "ResponsesAPI",
2428
dependencies: [
29+
"Macros",
2530
.product(name: "MetaCodable", package: "MetaCodable"),
2631
.product(name: "HelperCoders", package: "MetaCodable"),
2732
],
2833
path: "./src"
2934
),
35+
.macro(
36+
name: "Macros",
37+
dependencies: [
38+
.product(name: "SwiftSyntax", package: "swift-syntax"),
39+
.product(name: "SwiftDiagnostics", package: "swift-syntax"),
40+
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
41+
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
42+
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
43+
],
44+
path: "./macros"
45+
),
46+
.testTarget(
47+
name: "Tests",
48+
dependencies: [
49+
"ResponsesAPI", "Macros",
50+
.product(name: "MacroTesting", package: "swift-macro-testing"),
51+
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
52+
],
53+
path: "./tests"
54+
),
3055
]
3156
)

README.md

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
# OpenAI Responses API
2+
> Hand-crafted Swift SDK for the [OpenAI Responses API](https://platform.openai.com/docs/api-reference/responses).
23
34
[![Swift Version](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fm1guelpf%2Fswift-openai-responses%2Fbadge%3Ftype%3Dswift-versions&color=brightgreen)](https://swiftpackageindex.com/m1guelpf/swift-openai-responses)
45
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/m1guelpf/swift-openai-responses/main/LICENSE)
56

6-
An unofficial Swift SDK for the [OpenAI Responses API](https://platform.openai.com/docs/api-reference/responses).
7+
This package contains:
8+
- A fully typed client for the Responses API that _feels_ Swifty
9+
- `Schemable` and `Tool` macros, providing elegant ways to define tools and structured responses.
10+
- A `Conversation` class, handling everything you need for multi-turn streaming conversations in your views.
11+
712

813
## Installation
914

@@ -29,15 +34,15 @@ dependencies: [
2934
- File > Swift Packages > Add Package Dependency
3035
- Add https://github.com/m1guelpf/swift-openai-responses.git
3136
- Select "Branch" with "main"
32-
37+
3338
</details>
3439

3540
<details>
3641

3742
<summary>CocoaPods</summary>
3843

3944
Ask ChatGPT to help you migrate away from CocoaPods.
40-
45+
4146
</details>
4247

4348
## Getting started 🚀
@@ -50,17 +55,17 @@ import SwiftUI
5055

5156
struct ContentView: View {
5257
@State private var newMessage: String = ""
53-
@State private var conversation = Conversation(authToken: OPENAI_KEY, using: .gpt4o)
58+
@State private var conversation = Conversation(authToken: OPENAI_KEY, using: .gpt5)
5459

5560
var body: some View {
5661
VStack(spacing: 0) {
5762
ScrollView {
58-
VStack(spacing: 12) {
59-
ForEach(messages, id: \.self) { message in
60-
MessageBubble(message: message)
61-
}
62-
}
63-
.padding()
63+
VStack(spacing: 12) {
64+
ForEach(conversation.messages, id: \.self) { message in
65+
MessageBubble(message: message)
66+
}
67+
}
68+
.padding()
6469
}
6570

6671
HStack(spacing: 12) {
@@ -92,10 +97,8 @@ struct ContentView: View {
9297
func sendMessage() {
9398
guard newMessage != "" else { return }
9499

95-
Task {
96-
try await conversation.send(text: newMessage)
97-
newMessage = ""
98-
}
100+
conversation.send(text: newMessage)
101+
newMessage = ""
99102
}
100103
}
101104
```
@@ -111,13 +114,13 @@ The Conversation class provides a high-level interface for managing a conversati
111114
To create a `Conversation` instance, all you need is an OpenAI API key, and the model you will be talking to:
112115

113116
```swift
114-
@State private var conversation = Conversation(authToken: OPENAI_API_KEY, using: .gpt4o)
117+
@State private var conversation = Conversation(authToken: OPENAI_API_KEY, using: .gpt5)
115118
```
116119

117120
You can optionally provide a closure to configure the conversation, adding a system prompt or tools for the model to use:
118121

119122
```swift
120-
@State private var conversation = Conversation(authToken: OPENAI_API_KEY, using: .gpt4o) { config in
123+
@State private var conversation = Conversation(authToken: OPENAI_API_KEY, using: .gpt5) { config in
121124
// configure the model's behaviour
122125
config.instructions = "You are a coding assistant that talks like a pirate"
123126

@@ -143,25 +146,25 @@ conversation.truncation = .auto
143146
Your `Conversation` instance contains various helpers to make communicating with the model easier. For example, you can send a simple text message like this:
144147

145148
```swift
146-
try await conversation.send(text: "Hey!")
149+
conversation.send(text: "Hey!")
147150
```
148151

149152
There are also helpers for providing the output of a tool call or computer use call:
150153

151154
```swift
152-
try await conversation.send(functionCallOutput: .init(callId: callId, output: "{ ... }"))
153-
try await conversation.send(computerCallOutput: .init(callId: callId, output: .screenshot(fileId: "...")))
155+
conversation.send(functionCallOutput: .init(callId: callId, output: "{ ... }"))
156+
conversation.send(computerCallOutput: .init(callId: callId, output: .screenshot(fileId: "...")))
154157
```
155158

156159
For more complex use cases, you can construct the `Input` yourself:
157160

158161
```swift
159-
try await conversation.send(Input([
160-
.message(content: Input.Content([
162+
conversation.send([
163+
.message(content: [
161164
.image(fileId: "..."),
162165
.text("Take a look at this image and tell me what you see"),
163-
])),
164-
]))
166+
]),
167+
])
165168
```
166169

167170
#### Reading messages
@@ -184,7 +187,6 @@ ScrollView {
184187
}
185188
```
186189

187-
188190
### `ResponsesAPI`
189191

190192
#### Creating a new instance
@@ -207,7 +209,7 @@ let client = ResponsesAPI(
207209

208210
For more advanced use cases like connecting to a custom server, you can customize the `URLRequest` used to connect to the API:
209211

210-
``` swift
212+
```swift
211213
let urlRequest = URLRequest(url: MY_CUSTOM_ENDPOINT)
212214
urlRequest.addValue("Bearer \(YOUR_API_KEY)", forHTTPHeaderField: "Authorization")
213215

@@ -220,7 +222,7 @@ To create a new response, call the `create` method with a `Request` instance:
220222

221223
```swift
222224
let response = try await client.create(Request(
223-
model: .gpt4o,
225+
model: .gpt5,
224226
input: .text("Are semicolons optional in JavaScript?"),
225227
instructions: "You are a coding assistant that talks like a pirate"
226228
))
@@ -234,7 +236,7 @@ To stream back the response as it is generated, use the `stream` method:
234236

235237
```swift
236238
let stream = try await client.stream(Request(
237-
model: .gpt4o,
239+
model: .gpt5,
238240
input: .text("Are semicolons optional in JavaScript?"),
239241
instructions: "You are a coding assistant that talks like a pirate"
240242
))
@@ -255,7 +257,7 @@ let file = try await client.upload(file: .file(name: "image.png", contents: imag
255257

256258
// then, use it on a message
257259
try await client.create(Request(
258-
model: .gpt4o,
260+
model: .gpt5,
259261
input: .message(content: [
260262
.image(fileId: file.id),
261263
.text("Take a look at this image and tell me what you see"),

0 commit comments

Comments
 (0)