Skip to content

Commit 120bfec

Browse files
authored
Merge pull request MacPaw#140 from cdillard/feat/assistants-api-3
feat: Assistants API Beta Implemented
2 parents ac5a5ac + 8afe02d commit 120bfec

Some content is hidden

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

54 files changed

+3782
-168
lines changed

Demo/App/APIProvidedView.swift

+10
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ struct APIProvidedView: View {
1313
@Binding var apiKey: String
1414
@StateObject var chatStore: ChatStore
1515
@StateObject var imageStore: ImageStore
16+
@StateObject var assistantStore: AssistantStore
1617
@StateObject var miscStore: MiscStore
18+
1719
@State var isShowingAPIConfigModal: Bool = true
1820

1921
@Environment(\.idProviderValue) var idProvider
@@ -35,6 +37,12 @@ struct APIProvidedView: View {
3537
openAIClient: OpenAI(apiToken: apiKey.wrappedValue)
3638
)
3739
)
40+
self._assistantStore = StateObject(
41+
wrappedValue: AssistantStore(
42+
openAIClient: OpenAI(apiToken: apiKey.wrappedValue),
43+
idProvider: idProvider
44+
)
45+
)
3846
self._miscStore = StateObject(
3947
wrappedValue: MiscStore(
4048
openAIClient: OpenAI(apiToken: apiKey.wrappedValue)
@@ -46,12 +54,14 @@ struct APIProvidedView: View {
4654
ContentView(
4755
chatStore: chatStore,
4856
imageStore: imageStore,
57+
assistantStore: assistantStore,
4958
miscStore: miscStore
5059
)
5160
.onChange(of: apiKey) { newApiKey in
5261
let client = OpenAI(apiToken: newApiKey)
5362
chatStore.openAIClient = client
5463
imageStore.openAIClient = client
64+
assistantStore.openAIClient = client
5565
miscStore.openAIClient = client
5666
}
5767
}

Demo/App/ContentView.swift

+17-12
Original file line numberDiff line numberDiff line change
@@ -12,53 +12,58 @@ import SwiftUI
1212
struct ContentView: View {
1313
@ObservedObject var chatStore: ChatStore
1414
@ObservedObject var imageStore: ImageStore
15+
@ObservedObject var assistantStore: AssistantStore
1516
@ObservedObject var miscStore: MiscStore
17+
1618
@State private var selectedTab = 0
1719
@Environment(\.idProviderValue) var idProvider
1820

1921
var body: some View {
2022
TabView(selection: $selectedTab) {
2123
ChatView(
22-
store: chatStore
24+
store: chatStore,
25+
assistantStore: assistantStore
2326
)
2427
.tabItem {
2528
Label("Chats", systemImage: "message")
2629
}
2730
.tag(0)
2831

32+
AssistantsView(
33+
store: chatStore,
34+
assistantStore: assistantStore
35+
)
36+
.tabItem {
37+
Label("Assistants", systemImage: "eyeglasses")
38+
}
39+
.tag(1)
40+
2941
TranscribeView(
3042
)
3143
.tabItem {
3244
Label("Transcribe", systemImage: "mic")
3345
}
34-
.tag(1)
46+
.tag(2)
3547

3648
ImageView(
3749
store: imageStore
3850
)
3951
.tabItem {
4052
Label("Image", systemImage: "photo")
4153
}
42-
.tag(2)
43-
54+
.tag(3)
55+
4456
MiscView(
4557
store: miscStore
4658
)
4759
.tabItem {
4860
Label("Misc", systemImage: "ellipsis")
4961
}
50-
.tag(3)
62+
.tag(4)
5163
}
5264
}
5365
}
5466

55-
struct ChatsView: View {
56-
var body: some View {
57-
Text("Chats")
58-
.font(.largeTitle)
59-
}
60-
}
61-
6267
struct TranscribeView: View {
6368
var body: some View {
6469
Text("Transcribe: TBD")

Demo/Demo.xcodeproj/project.pbxproj

+4-2
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@
234234
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
235235
GCC_WARN_UNUSED_FUNCTION = YES;
236236
GCC_WARN_UNUSED_VARIABLE = YES;
237+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
237238
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
238239
MTL_FAST_MATH = YES;
239240
ONLY_ACTIVE_ARCH = YES;
@@ -286,6 +287,7 @@
286287
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
287288
GCC_WARN_UNUSED_FUNCTION = YES;
288289
GCC_WARN_UNUSED_VARIABLE = YES;
290+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
289291
MTL_ENABLE_DEBUG_INFO = NO;
290292
MTL_FAST_MATH = YES;
291293
SWIFT_COMPILATION_MODE = wholemodule;
@@ -315,7 +317,7 @@
315317
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
316318
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
317319
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
318-
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
320+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
319321
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
320322
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
321323
MACOSX_DEPLOYMENT_TARGET = 13.3;
@@ -354,7 +356,7 @@
354356
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
355357
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
356358
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
357-
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
359+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
358360
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
359361
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
360362
MACOSX_DEPLOYMENT_TARGET = 13.3;
+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//
2+
// ChatStore.swift
3+
// DemoChat
4+
//
5+
// Created by Sihao Lu on 3/25/23.
6+
//
7+
8+
import Foundation
9+
import Combine
10+
import OpenAI
11+
12+
public final class AssistantStore: ObservableObject {
13+
public var openAIClient: OpenAIProtocol
14+
let idProvider: () -> String
15+
@Published var selectedAssistantId: String?
16+
17+
@Published var availableAssistants: [Assistant] = []
18+
19+
public init(
20+
openAIClient: OpenAIProtocol,
21+
idProvider: @escaping () -> String
22+
) {
23+
self.openAIClient = openAIClient
24+
self.idProvider = idProvider
25+
}
26+
27+
// MARK: Models
28+
29+
@MainActor
30+
func createAssistant(name: String, description: String, instructions: String, codeInterpreter: Bool, fileSearch: Bool, functions: [FunctionDeclaration], fileIds: [String]? = nil) async -> String? {
31+
do {
32+
let toolResources: ToolResources? = if let fileIds {
33+
ToolResources(fileSearch: nil, codeInterpreter: .init(fileIds: fileIds))
34+
} else {
35+
nil
36+
}
37+
38+
let tools = createToolsArray(codeInterpreter: codeInterpreter, fileSearch: fileSearch, functions: functions)
39+
let query = AssistantsQuery(model: Model.gpt4_o_mini, name: name, description: description, instructions: instructions, tools:tools, toolResources: toolResources)
40+
let response = try await openAIClient.assistantCreate(query: query)
41+
42+
// Refresh assistants with one just created (or modified)
43+
let _ = await getAssistants()
44+
45+
// Returns assistantId
46+
return response.id
47+
48+
} catch {
49+
// TODO: Better error handling
50+
print(error.localizedDescription)
51+
}
52+
return nil
53+
}
54+
55+
@MainActor
56+
func modifyAssistant(asstId: String, name: String, description: String, instructions: String, codeInterpreter: Bool, fileSearch: Bool, functions: [FunctionDeclaration], fileIds: [String]? = nil) async -> String? {
57+
do {
58+
let toolResources: ToolResources? = if let fileIds {
59+
ToolResources(fileSearch: nil, codeInterpreter: .init(fileIds: fileIds))
60+
} else {
61+
nil
62+
}
63+
64+
let tools = createToolsArray(codeInterpreter: codeInterpreter, fileSearch: fileSearch, functions: functions)
65+
let query = AssistantsQuery(model: Model.gpt4_o_mini, name: name, description: description, instructions: instructions, tools:tools, toolResources: toolResources)
66+
let response = try await openAIClient.assistantModify(query: query, assistantId: asstId)
67+
68+
// Returns assistantId
69+
return response.id
70+
71+
} catch {
72+
// TODO: Better error handling
73+
print(error.localizedDescription)
74+
}
75+
return nil
76+
}
77+
78+
@MainActor
79+
func getAssistants(limit: Int = 20, after: String? = nil) async -> [Assistant] {
80+
do {
81+
let response = try await openAIClient.assistants(after: after)
82+
83+
var assistants = [Assistant]()
84+
for result in response.data ?? [] {
85+
let tools = result.tools ?? []
86+
let codeInterpreter = tools.contains { $0 == .codeInterpreter }
87+
let fileSearch = tools.contains { $0 == .fileSearch }
88+
let functions = tools.compactMap {
89+
switch $0 {
90+
case let .function(declaration):
91+
return declaration
92+
default:
93+
return nil
94+
}
95+
}
96+
let fileIds = result.toolResources.codeInterpreter?.fileIds ?? []
97+
98+
assistants.append(Assistant(id: result.id, name: result.name ?? "", description: result.description, instructions: result.instructions, codeInterpreter: codeInterpreter, fileSearch: fileSearch, fileIds: fileIds, functions: functions))
99+
}
100+
if after == nil {
101+
availableAssistants = assistants
102+
}
103+
else {
104+
availableAssistants = availableAssistants + assistants
105+
}
106+
return assistants
107+
108+
} catch {
109+
// TODO: Better error handling
110+
print(error.localizedDescription)
111+
}
112+
return []
113+
}
114+
115+
func selectAssistant(_ assistantId: String?) {
116+
selectedAssistantId = assistantId
117+
}
118+
119+
@MainActor
120+
func uploadFile(url: URL) async -> FilesResult? {
121+
do {
122+
123+
let mimeType = url.mimeType()
124+
125+
let fileData = try Data(contentsOf: url)
126+
127+
let result = try await openAIClient.files(query: FilesQuery(purpose: "assistants", file: fileData, fileName: url.lastPathComponent, contentType: mimeType))
128+
return result
129+
}
130+
catch {
131+
print("error = \(error)")
132+
return nil
133+
}
134+
}
135+
136+
func createToolsArray(codeInterpreter: Bool, fileSearch: Bool, functions: [FunctionDeclaration]) -> [Tool] {
137+
var tools = [Tool]()
138+
if codeInterpreter {
139+
tools.append(.codeInterpreter)
140+
}
141+
if fileSearch {
142+
tools.append(.fileSearch)
143+
}
144+
return tools + functions.map { .function($0) }
145+
}
146+
}

0 commit comments

Comments
 (0)