Skip to content

Commit f358d3e

Browse files
authored
Document Conversation
1 parent f754704 commit f358d3e

File tree

1 file changed

+178
-13
lines changed

1 file changed

+178
-13
lines changed

README.md

Lines changed: 178 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Add the following to your `Package.swift`:
1717

1818
```swift
1919
dependencies: [
20-
.package(url: "https://github.com/m1guelpf/swift-openai-responses.git", .branch("main"))
20+
.package(url: "https://github.com/m1guelpf/swift-openai-responses.git", .branch("main"))
2121
]
2222
```
2323

@@ -40,8 +40,151 @@ Ask ChatGPT to help you migrate away from CocoaPods.
4040

4141
</details>
4242

43+
## Getting started 🚀
44+
45+
You can build an iMessage-like app with built-in AI chat in 50 lines of code (UI included!):
46+
47+
```swift
48+
import OpenAI
49+
import SwiftUI
50+
51+
struct ContentView: View {
52+
@State private var newMessage: String = ""
53+
@State private var conversation = Conversation(authToken: OPENAI_KEY)
54+
55+
var body: some View {
56+
VStack(spacing: 0) {
57+
ScrollView {
58+
VStack(spacing: 12) {
59+
ForEach(messages, id: \.self) { message in
60+
MessageBubble(message: message)
61+
}
62+
}
63+
.padding()
64+
}
65+
66+
HStack(spacing: 12) {
67+
HStack {
68+
TextField("Chat", text: $newMessage, onCommit: sendMessage)
69+
.frame(height: 40)
70+
.submitLabel(.send)
71+
72+
if newMessage != "" {
73+
Button(action: sendMessage) {
74+
Image(systemName: "arrow.up.circle.fill")
75+
.resizable()
76+
.aspectRatio(contentMode: .fill)
77+
.frame(width: 28, height: 28)
78+
.foregroundStyle(.white, .blue)
79+
}
80+
}
81+
}
82+
.padding(.leading)
83+
.padding(.trailing, 6)
84+
.overlay(RoundedRectangle(cornerRadius: 20).stroke(.quaternary, lineWidth: 1))
85+
}
86+
.padding()
87+
}
88+
.navigationTitle("Chat")
89+
.navigationBarTitleDisplayMode(.inline)
90+
}
91+
92+
func sendMessage() {
93+
guard newMessage != "" else { return }
94+
95+
Task {
96+
try await conversation.send(text: newMessage)
97+
newMessage = ""
98+
}
99+
}
100+
}
101+
```
102+
43103
## Architecture
44104

105+
### `Conversation`
106+
107+
The Conversation class provides a high-level interface for managing a conversation with the model. It wraps the `ResponsesAPI` class and handles the details of sending and receiving messages, as well as managing the conversation history.
108+
109+
#### Configuring the conversation
110+
111+
To create a `Conversation` instance, all you need is an OpenAI API key, and the model you will be talking to:
112+
113+
```swift
114+
@State private var conversation = Conversation(authToken: OPENAI_API_KEY, using: .gpt4o)
115+
```
116+
117+
You can optionally provide a closure to configure the conversation, adding a system prompt or tools for the model to use:
118+
119+
```swift
120+
@State private var conversation = Conversation(authToken: OPENAI_API_KEY, using: .gpt4o) { config in
121+
// configure the model's behaviour
122+
config.instructions = "You are a coding assistant that talks like a pirate"
123+
124+
// allow the model to browse the web
125+
config.tools = [.webSearch()]
126+
}
127+
```
128+
129+
Your configuration will be reused for all subsequent messages, but you can always change any of the properties (including which model you're talking to!) mid-conversation:
130+
131+
```swift
132+
// update a bunch of properties at once
133+
conversation.updateConfig { config in
134+
config.model = .o3Mini
135+
}
136+
137+
// or update them directly
138+
conversation.truncation = .auto
139+
```
140+
141+
#### Sending messages
142+
143+
Your `Conversation` instance contains various helpers to make communicating with the model easier. For example, you can send a simple text message like this:
144+
145+
```swift
146+
try await conversation.send(text: "Hey!")
147+
```
148+
149+
There are also helpers for providing the output of a tool call or computer use call:
150+
151+
```swift
152+
try await conversation.send(functionCallOutput: .init(callId: callId, output: "{ ... }"))
153+
try await conversation.send(computerCallOutput: .init(callId: callId, output: .screenshot(fileId: "...")))
154+
```
155+
156+
For more complex use cases, you can construct the `Input` yourself:
157+
158+
```swift
159+
try await conversation.send(Input([
160+
.message(content: Input.Content([
161+
.image(fileId: "..."),
162+
.text("Take a look at this image and tell me what you see"),
163+
])),
164+
]))
165+
```
166+
167+
#### Reading messages
168+
169+
You can access the messages in the conversation through the messages property. Note that this won't include function calls and its responses, only the messages between the user and the model. To access the full conversation history, use the `entries` property. For example:
170+
171+
```swift
172+
ScrollView {
173+
ScrollViewReader { scrollView in
174+
VStack(spacing: 12) {
175+
ForEach(conversation.messages, id: \.self) { message in
176+
MessageBubble(message: message)
177+
.id(message.hashValue)
178+
}
179+
}
180+
.onReceive(conversation.messages.publisher) { _ in
181+
withAnimation { scrollView.scrollTo(conversation.messages.last?.hashValue, anchor: .center) }
182+
}
183+
}
184+
}
185+
```
186+
187+
45188
### `ResponsesAPI`
46189

47190
#### Creating a new instance
@@ -56,9 +199,9 @@ Optionally, you can provide an Organization ID and/or project ID:
56199

57200
```swift
58201
let client = ResponsesAPI(
59-
authToken: YOUR_OPENAI_API_KEY,
60-
organizationId: YOUR_ORGANIZATION_ID,
61-
projectId: YOUR_PROJECT_ID
202+
authToken: YOUR_OPENAI_API_KEY,
203+
organizationId: YOUR_ORGANIZATION_ID,
204+
projectId: YOUR_PROJECT_ID
62205
)
63206
```
64207

@@ -77,9 +220,9 @@ To create a new response, call the `create` method with a `Request` instance:
77220

78221
```swift
79222
let response = try await client.create(Request(
80-
model: "gpt-4o",
81-
input: .text("Are semicolons optional in JavaScript?"),
82-
instructions: "You are a coding assistant that talks like a pirate"
223+
model: "gpt-4o",
224+
input: .text("Are semicolons optional in JavaScript?"),
225+
instructions: "You are a coding assistant that talks like a pirate"
83226
))
84227
```
85228

@@ -91,18 +234,40 @@ To stream back the response as it is generated, use the `stream` method:
91234

92235
```swift
93236
let stream = try await client.stream(Request(
94-
model: "gpt-4o",
95-
input: .text("Are semicolons optional in JavaScript?"),
96-
instructions: "You are a coding assistant that talks like a pirate"
237+
model: "gpt-4o",
238+
input: .text("Are semicolons optional in JavaScript?"),
239+
instructions: "You are a coding assistant that talks like a pirate"
97240
))
98241

99242
for try await event in stream {
100-
switch event {
101-
// ...
102-
}
243+
switch event {
244+
// ...
245+
}
103246
}
104247
```
105248

249+
#### Uploading files
250+
251+
While uploading files is not part of the Responses API, you'll need it for sending content to the model (like images, PDFs, etc.). You can upload a file to the model like so:
252+
253+
```swift
254+
let file = try await client.upload(file: .file(name: "image.png", contents: imageData, contentType: "image/png"))
255+
256+
// then, use it on a message
257+
try await conversation.send(Input([
258+
.message(content: Input.Content([
259+
.image(fileId: file.id),
260+
.text("Take a look at this image and tell me what you see"),
261+
])),
262+
]))
263+
```
264+
265+
You can also load files directly from the user's filesystem or the web:
266+
267+
```swift
268+
let file = try await client.upload(file: .url(URL(string: "https://example.com/file.pdf")!, contentType: "application/pdf"))
269+
```
270+
106271
#### Other Stuff
107272

108273
You can retrieve a previously-created response by calling the `get` method with the response ID:

0 commit comments

Comments
 (0)