diff --git a/Cheetah/CheetahApp.swift b/Cheetah/CheetahApp.swift index ad874c1..1304040 100644 --- a/Cheetah/CheetahApp.swift +++ b/Cheetah/CheetahApp.swift @@ -24,6 +24,7 @@ class AppViewModel: ObservableObject { @Published var analyzer: ConversationAnalyzer? @Published var answerRequest = AnswerRequest.none + @Published var errorDescription: String? @Published var transcript: String? @Published var answer: String? @@ -67,35 +68,40 @@ struct CheetahApp: App { // Install manifest needed for the browser extension to talk to ExtensionHelper _ = try? installNativeMessagingManifest() - do { - for try await request in viewModel.$answerRequest.receive(on: RunLoop.main).values { - if let analyzer = viewModel.analyzer { - switch request { - case .answerQuestion: - try await analyzer.answer() - viewModel.answer = analyzer.context[.answer] - viewModel.codeAnswer = analyzer.context[.codeAnswer] - viewModel.answerRequest = .none - - case .refineAnswer(let selection): - try await analyzer.answer(refine: true, selection: selection) - viewModel.answer = analyzer.context[.answer] - viewModel.codeAnswer = analyzer.context[.codeAnswer] - viewModel.answerRequest = .none - - case .analyzeCode: - try await analyzer.analyzeCode(extensionState: extensionState) - viewModel.answer = analyzer.context[.answer] - viewModel.answerRequest = .none - - case .none: - break + while true { + do { + for try await request in viewModel.$answerRequest.receive(on: RunLoop.main).values { + if let analyzer = viewModel.analyzer { + switch request { + case .answerQuestion: + try await analyzer.answer() + viewModel.answer = analyzer.context[.answer] + viewModel.codeAnswer = analyzer.context[.codeAnswer] + viewModel.answerRequest = .none + + case .refineAnswer(let selection): + try await analyzer.answer(refine: true, selection: selection) + viewModel.answer = analyzer.context[.answer] + viewModel.codeAnswer = analyzer.context[.codeAnswer] + viewModel.answerRequest = .none + + case .analyzeCode: + try await analyzer.analyzeCode(extensionState: extensionState) + viewModel.answer = analyzer.context[.answer] + viewModel.answerRequest = .none + + case .none: + break + } } } + } catch let error as ErrorResult { + viewModel.errorDescription = error.message + viewModel.answerRequest = .none + } catch { + viewModel.errorDescription = error.localizedDescription + viewModel.answerRequest = .none } - } catch { - viewModel.answerRequest = .none - //TODO: handle error } } diff --git a/Cheetah/OpenAIExecutor.swift b/Cheetah/OpenAIExecutor.swift index 81557b8..b407bb3 100644 --- a/Cheetah/OpenAIExecutor.swift +++ b/Cheetah/OpenAIExecutor.swift @@ -77,6 +77,8 @@ class OpenAIExecutor { let text = result.choices?.first?.text if let text = text { log(completion: text) + } else if let error = result.error { + throw error } return text } @@ -87,6 +89,8 @@ class OpenAIExecutor { let content = result.choices?.first?.message.content if let content = content { log(completion: content) + } else if let error = result.error { + throw error } return content } diff --git a/Cheetah/Views/AuthTokenView.swift b/Cheetah/Views/AuthTokenView.swift index 3a9c595..a2179ff 100644 --- a/Cheetah/Views/AuthTokenView.swift +++ b/Cheetah/Views/AuthTokenView.swift @@ -30,7 +30,7 @@ struct AuthTokenView: View { } } -struct APIKeyView_Previews: PreviewProvider { +struct AuthTokenView_Previews: PreviewProvider { static var previews: some View { return AuthTokenView( storedToken: Binding.constant(nil), diff --git a/Cheetah/Views/CoachView.swift b/Cheetah/Views/CoachView.swift index 59751c1..901f3cf 100644 --- a/Cheetah/Views/CoachView.swift +++ b/Cheetah/Views/CoachView.swift @@ -7,6 +7,9 @@ struct CoachView: View { @State var answer: String @State var answerSelection = NSRange() + @State var showError = false + @State var errorDescription = "" + init(viewModel: AppViewModel) { self.viewModel = viewModel self.answer = viewModel.answer ?? "" @@ -55,6 +58,17 @@ struct CoachView: View { } } } + .onReceive(viewModel.$errorDescription) { + if let error = $0 { + self.showError = true + self.errorDescription = error + } + } + .alert(errorDescription, isPresented: $showError) { + Button("OK", role: .cancel) { + self.showError = false + } + } HStack { VStack(alignment: .leading, spacing: 20) { if let transcript = viewModel.transcript { diff --git a/Cheetah/Views/ContentView.swift b/Cheetah/Views/ContentView.swift index e44b12d..d16357a 100644 --- a/Cheetah/Views/ContentView.swift +++ b/Cheetah/Views/ContentView.swift @@ -6,7 +6,7 @@ struct ContentView: View { @ViewBuilder var body: some View { - if viewModel.authToken != nil { + if viewModel.authToken?.isEmpty == false { VStack(spacing: 16) { switch viewModel.downloadState { case .pending: diff --git a/OpenAISwift/Models/OpenAI.swift b/OpenAISwift/Models/OpenAI.swift index d6bf82b..a4c1194 100644 --- a/OpenAISwift/Models/OpenAI.swift +++ b/OpenAISwift/Models/OpenAI.swift @@ -12,6 +12,12 @@ public struct OpenAI: Codable { public let choices: [T]? public let usage: UsageResult? public let data: [T]? + public let error: ErrorResult? +} + +public struct ErrorResult: Codable, Error { + public let message: String + public let code: String } public struct TextResult: Payload {