Skip to content

Commit

Permalink
Feat/assistants openai api (#2)
Browse files Browse the repository at this point in the history
* feat: Assistants API

* Rem accidentally committed Dev team

* Demoapp syntax fix for ImageCreationView

* assistant paging, modify, fix

* demo: enhancement: Handle local message replacement, README update

* clean, runRetrieveSteps implemented, SupportedFileTypes implemented

* Handle run retrieve steps

* Assistant README, add run retrieve steps

* display run retrieve steps in updating fashion for code_interpreter
  • Loading branch information
cdillard authored Dec 16, 2023
1 parent 1b1765b commit 972718d
Show file tree
Hide file tree
Showing 39 changed files with 2,200 additions and 106 deletions.
10 changes: 10 additions & 0 deletions Demo/App/APIProvidedView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ struct APIProvidedView: View {
@Binding var apiKey: String
@StateObject var chatStore: ChatStore
@StateObject var imageStore: ImageStore
@StateObject var assistantStore: AssistantStore
@StateObject var miscStore: MiscStore

@State var isShowingAPIConfigModal: Bool = true

@Environment(\.idProviderValue) var idProvider
Expand All @@ -35,6 +37,12 @@ struct APIProvidedView: View {
openAIClient: OpenAI(apiToken: apiKey.wrappedValue)
)
)
self._assistantStore = StateObject(
wrappedValue: AssistantStore(
openAIClient: OpenAI(apiToken: apiKey.wrappedValue),
idProvider: idProvider
)
)
self._miscStore = StateObject(
wrappedValue: MiscStore(
openAIClient: OpenAI(apiToken: apiKey.wrappedValue)
Expand All @@ -46,12 +54,14 @@ struct APIProvidedView: View {
ContentView(
chatStore: chatStore,
imageStore: imageStore,
assistantStore: assistantStore,
miscStore: miscStore
)
.onChange(of: apiKey) { newApiKey in
let client = OpenAI(apiToken: newApiKey)
chatStore.openAIClient = client
imageStore.openAIClient = client
assistantStore.openAIClient = client
miscStore.openAIClient = client
}
}
Expand Down
29 changes: 17 additions & 12 deletions Demo/App/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,53 +12,58 @@ import SwiftUI
struct ContentView: View {
@ObservedObject var chatStore: ChatStore
@ObservedObject var imageStore: ImageStore
@ObservedObject var assistantStore: AssistantStore
@ObservedObject var miscStore: MiscStore

@State private var selectedTab = 0
@Environment(\.idProviderValue) var idProvider

var body: some View {
TabView(selection: $selectedTab) {
ChatView(
store: chatStore
store: chatStore,
assistantStore: assistantStore
)
.tabItem {
Label("Chats", systemImage: "message")
}
.tag(0)

AssistantsView(
store: chatStore,
assistantStore: assistantStore
)
.tabItem {
Label("Assistants", systemImage: "eyeglasses")
}
.tag(1)

TranscribeView(
)
.tabItem {
Label("Transcribe", systemImage: "mic")
}
.tag(1)
.tag(2)

ImageView(
store: imageStore
)
.tabItem {
Label("Image", systemImage: "photo")
}
.tag(2)
.tag(3)

MiscView(
store: miscStore
)
.tabItem {
Label("Misc", systemImage: "ellipsis")
}
.tag(3)
.tag(4)
}
}
}

struct ChatsView: View {
var body: some View {
Text("Chats")
.font(.largeTitle)
}
}

struct TranscribeView: View {
var body: some View {
Text("Transcribe: TBD")
Expand Down
6 changes: 4 additions & 2 deletions Demo/Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
Expand Down Expand Up @@ -286,6 +287,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SWIFT_COMPILATION_MODE = wholemodule;
Expand Down Expand Up @@ -315,7 +317,7 @@
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 13.3;
Expand Down Expand Up @@ -354,7 +356,7 @@
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 13.3;
Expand Down
125 changes: 125 additions & 0 deletions Demo/DemoChat/Sources/AssistantStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//
// ChatStore.swift
// DemoChat
//
// Created by Sihao Lu on 3/25/23.
//

import Foundation
import Combine
import OpenAI

public final class AssistantStore: ObservableObject {
public var openAIClient: OpenAIProtocol
let idProvider: () -> String
@Published var selectedAssistantId: String?

@Published var availableAssistants: [Assistant] = []

public init(
openAIClient: OpenAIProtocol,
idProvider: @escaping () -> String
) {
self.openAIClient = openAIClient
self.idProvider = idProvider
}

// MARK: Models

@MainActor
func createAssistant(name: String, description: String, instructions: String, codeInterpreter: Bool, retrievel: Bool, fileIds: [String]? = nil) async -> String? {
do {
let tools = createToolsArray(codeInterpreter: codeInterpreter, retrieval: retrievel)
let query = AssistantsQuery(model: Model.gpt4_1106_preview, name: name, description: description, instructions: instructions, tools:tools, fileIds: fileIds)
let response = try await openAIClient.assistants(query: query, method: "POST", after: nil)

// Refresh assistants with one just created (or modified)
let _ = await getAssistants()

// Returns assistantId
return response.id

} catch {
// TODO: Better error handling
print(error.localizedDescription)
}
return nil
}

@MainActor
func modifyAssistant(asstId: String, name: String, description: String, instructions: String, codeInterpreter: Bool, retrievel: Bool, fileIds: [String]? = nil) async -> String? {
do {
let tools = createToolsArray(codeInterpreter: codeInterpreter, retrieval: retrievel)
let query = AssistantsQuery(model: Model.gpt4_1106_preview, name: name, description: description, instructions: instructions, tools:tools, fileIds: fileIds)
let response = try await openAIClient.assistantModify(query: query, asstId: asstId)

// Returns assistantId
return response.id

} catch {
// TODO: Better error handling
print(error.localizedDescription)
}
return nil
}

@MainActor
func getAssistants(limit: Int = 20, after: String? = nil) async -> [Assistant] {
do {
let response = try await openAIClient.assistants(query: nil, method: "GET", after: after)

var assistants = [Assistant]()
for result in response.data ?? [] {
let codeInterpreter = result.tools?.filter { $0.toolType == "code_interpreter" }.first != nil
let retrieval = result.tools?.filter { $0.toolType == "retrieval" }.first != nil
let fileIds = result.fileIds ?? []

assistants.append(Assistant(id: result.id, name: result.name, description: result.description, instructions: result.instructions, codeInterpreter: codeInterpreter, retrieval: retrieval, fileIds: fileIds))
}
if after == nil {
availableAssistants = assistants
}
else {
availableAssistants = availableAssistants + assistants
}
return assistants

} catch {
// TODO: Better error handling
print(error.localizedDescription)
}
return []
}

func selectAssistant(_ assistantId: String?) {
selectedAssistantId = assistantId
}

@MainActor
func uploadFile(url: URL) async -> FilesResult? {
do {

let mimeType = url.mimeType()

let fileData = try Data(contentsOf: url)

let result = try await openAIClient.files(query: FilesQuery(purpose: "assistants", file: fileData, fileName: url.lastPathComponent, contentType: mimeType))
return result
}
catch {
print("error = \(error)")
return nil
}
}

func createToolsArray(codeInterpreter: Bool, retrieval: Bool) -> [Tool] {
var tools = [Tool]()
if codeInterpreter {
tools.append(Tool(toolType: "code_interpreter"))
}
if retrieval {
tools.append(Tool(toolType: "retrieval"))
}
return tools
}
}
Loading

0 comments on commit 972718d

Please sign in to comment.