Skip to content

Commit e8040e4

Browse files
committed
Add PaLMChat & Multi LLM Providers support
1 parent 5665b6e commit e8040e4

File tree

26 files changed

+782
-127
lines changed

26 files changed

+782
-127
lines changed

README.MD

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# ChatGPT SwiftUI iOS, macOS, watchOS, tvOS App
1+
# PaLMChat & ChatGPT SwiftUI iOS, macOS, watchOS, tvOS App
22

3-
![Alt text](https://imagizer.imageshack.com/v2/640x480q90/922/hmlopw.png "image")
3+
![Alt text](https://imagizer.imageshack.com/v2/640x480q70/924/4Qgrta.jpg "image")
44

5-
This is a native iOS, macOS, watchOS, tvOS App for interacting with ChatGPT built using SwiftUI and OpenAPI API. It's using Official ChatGPT endpoint with `gpt-3.5-turbo` model.
5+
This is a native iOS, macOS, watchOS, tvOS App for interacting with PaLM API & ChatGPT LLM Chatbots built using SwiftUI, OpenAPI Official ChatGPT API, & Google Generative AI SDK SPM.
66

77
It is also able to render response with markdown and code syntax highlighting.
88

@@ -16,8 +16,7 @@ It is also able to render response with markdown and code syntax highlighting.
1616
## Requirements
1717
- Xcode 14
1818
- Register at openai.com/api
19-
- Create API Key
20-
- Paste API key in ContentView where the ChatGPTAPI instance is declared
19+
- Create API Key from either OpenAI or PaLM API MakerSuite
2120

2221
## ChatGPTSwift API Lib
2322
You can use this standalone api client to access ChatGPT API, you can add dependency for [ChatGPTSwift](https://github.com/alfianlosari/ChatGPTSwift) to access the API only if you want to integrate into your own app.

Shared/ChatGPTAPI.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
import Foundation
99

10-
class ChatGPTAPI: @unchecked Sendable {
10+
class ChatGPTAPI: LLMClient, @unchecked Sendable {
11+
12+
var provider: LLMProvider { .chatGPT }
1113

1214
private let systemMessage: Message
1315
private let temperature: Double
@@ -158,3 +160,4 @@ extension String: CustomNSError {
158160
}
159161

160162

163+

Shared/ChatGPTAPIModels.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,23 @@
77

88
import Foundation
99

10+
enum ChatGPTModel: String, Identifiable, CaseIterable {
11+
12+
var id: Self { self }
13+
14+
case gpt3Turbo = "gpt-3.5-turbo"
15+
case gpt4 = "gpt-4"
16+
17+
var text: String {
18+
switch self {
19+
case .gpt3Turbo:
20+
return "GPT-3.5"
21+
case .gpt4:
22+
return "GPT-4"
23+
}
24+
}
25+
}
26+
1027
struct Message: Codable {
1128
let role: String
1229
let content: String

Shared/ContentView.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ struct ContentView: View {
1616

1717
var body: some View {
1818
chatListView
19-
.navigationTitle("XCA ChatGPT")
19+
.navigationTitle(vm.navigationTitle)
2020
}
2121

2222
var chatListView: some View {
@@ -66,13 +66,14 @@ struct ContentView: View {
6666
}
6767

6868
TextField("Send message", text: $vm.inputMessage, axis: .vertical)
69+
.autocorrectionDisabled()
6970
#if os(iOS) || os(macOS)
7071
.textFieldStyle(.roundedBorder)
7172
#endif
7273
.focused($isTextFieldFocused)
73-
.disabled(vm.isInteractingWithChatGPT)
74+
.disabled(vm.isInteracting)
7475

75-
if vm.isInteractingWithChatGPT {
76+
if vm.isInteracting {
7677
#if os(iOS)
7778
Button {
7879
vm.cancelStreamingResponse()
@@ -122,3 +123,4 @@ struct ContentView_Previews: PreviewProvider {
122123
}
123124
}
124125
}
126+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// LLMClient.swift
3+
// XCAChatGPT
4+
//
5+
// Created by Alfian Losari on 03/06/23.
6+
//
7+
8+
import Foundation
9+
10+
protocol LLMClient {
11+
12+
var provider: LLMProvider { get }
13+
14+
func sendMessageStream(text: String) async throws -> AsyncThrowingStream<String, Error>
15+
func sendMessage(_ text: String) async throws -> String
16+
func deleteHistoryList()
17+
18+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// LLMConfig.swift
3+
// XCAChatGPT
4+
//
5+
// Created by Alfian Losari on 03/06/23.
6+
//
7+
8+
import Foundation
9+
10+
struct LLMConfig: Identifiable, Hashable {
11+
12+
var id: String { apiKey }
13+
14+
let apiKey: String
15+
let type: ConfigType
16+
17+
enum ConfigType: Hashable {
18+
case chatGPT(ChatGPTModel)
19+
case palm
20+
}
21+
22+
func createClient() -> LLMClient {
23+
switch self.type {
24+
case .chatGPT(let model):
25+
return ChatGPTAPI(apiKey: apiKey, model: model.rawValue)
26+
case .palm:
27+
return PaLMChatAPI(apiKey: apiKey)
28+
}
29+
}
30+
31+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//
2+
// LLMProvider.swift
3+
// XCAChatGPT
4+
//
5+
// Created by Alfian Losari on 03/06/23.
6+
//
7+
8+
import Foundation
9+
10+
enum LLMProvider: Identifiable, CaseIterable {
11+
12+
case chatGPT
13+
case palm
14+
15+
var id: Self { self }
16+
17+
var text: String {
18+
switch self {
19+
case .chatGPT:
20+
return "OpenAI ChatGPT"
21+
case .palm:
22+
return "Google PaLM"
23+
}
24+
}
25+
26+
var footerInfo: String {
27+
switch self {
28+
case .chatGPT:
29+
return """
30+
ChatGPT is an artificial intelligence (AI) chatbot developed by OpenAI and released in November 2022. The name "ChatGPT" combines "Chat", referring to its chatbot functionality, and "GPT", which stands for Generative Pre-trained Transformer, a type of large language model (LLM). ChatGPT is built upon OpenAI's foundational GPT models, specifically GPT-3.5 and GPT-4, and has been fine-tuned (an approach to transfer learning) for conversational applications using a combination of supervised and reinforcement learning techniques.
31+
"""
32+
case .palm:
33+
return """
34+
PaLM (Pathways Language Model) is a 540 billion parameter transformer-based large language model developed by Google AI.Researchers also trained smaller versions of PaLM, 8 and 62 billion parameter models, to test the effects of model scale.
35+
36+
PaLM is capable of a wide range of tasks, including commonsense reasoning, arithmetic reasoning, joke explanation, code generation, and translation. When combined with chain-of-thought prompting, PaLM achieved significantly better performance on datasets requiring reasoning of multiple steps, such as word problems and logic-based questions.
37+
"""
38+
}
39+
}
40+
41+
var navigationTitle: String {
42+
switch self {
43+
case .chatGPT:
44+
return "XCA ChatGPT"
45+
46+
case .palm:
47+
return "XCA PaLMChat"
48+
}
49+
}
50+
51+
var imageName: String {
52+
switch self {
53+
case .chatGPT:
54+
return "openai"
55+
case .palm:
56+
return "palm"
57+
}
58+
}
59+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// PaLMChatAPI.swift
3+
// XCAChatGPT
4+
//
5+
// Created by Alfian Losari on 03/06/23.
6+
//
7+
8+
import Foundation
9+
import GoogleGenerativeAI
10+
11+
class PaLMChatAPI: LLMClient {
12+
13+
var provider: LLMProvider { .palm }
14+
private let palmClient: GenerativeLanguage
15+
private var history = [GoogleGenerativeAI.Message]()
16+
17+
init(apiKey: String) {
18+
self.palmClient = .init(apiKey: apiKey)
19+
}
20+
21+
func sendMessage(_ text: String) async throws -> String {
22+
let response = try await palmClient.chat(message: text, history: history)
23+
if let candidate = response.candidates?.first, let responseText = candidate.content {
24+
if let historicMessages = response.messages {
25+
self.history = historicMessages
26+
self.history.append(candidate)
27+
}
28+
return responseText
29+
} else {
30+
throw "No response"
31+
}
32+
}
33+
34+
func sendMessageStream(text: String) async throws -> AsyncThrowingStream<String, Error> {
35+
fatalError("Not supported")
36+
}
37+
38+
func deleteHistoryList() {
39+
self.history = []
40+
}
41+
42+
}

Shared/LLMConfigView.swift

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//
2+
// LLMConfigView.swift
3+
// XCAChatGPT
4+
//
5+
// Created by Alfian Losari on 03/06/23.
6+
//
7+
8+
import SwiftUI
9+
10+
struct LLMConfigView: View {
11+
12+
let onStartChatTapped: (_ result: LLMConfig) -> Void
13+
@State var apiKey = ""
14+
@State var llmProvider = LLMProvider.chatGPT
15+
@State var chatGPTModel = ChatGPTModel.gpt3Turbo
16+
17+
var body: some View {
18+
#if os(macOS)
19+
ScrollView { sectionsView }
20+
.padding(.horizontal)
21+
#else
22+
List { sectionsView }
23+
#endif
24+
}
25+
26+
@ViewBuilder
27+
var sectionsView: some View {
28+
Section("LLM Provider") {
29+
Picker("Provider", selection: $llmProvider) {
30+
ForEach(LLMProvider.allCases) {
31+
Text($0.text).id($0)
32+
}
33+
}
34+
#if !os(watchOS)
35+
.pickerStyle(.segmented)
36+
#endif
37+
}
38+
39+
Section("Configuration") {
40+
TextField("Enter API Key", text: $apiKey)
41+
.autocorrectionDisabled()
42+
#if os(macOS)
43+
.textCase(.none)
44+
.textFieldStyle(.roundedBorder)
45+
#else
46+
.textInputAutocapitalization(.never)
47+
#endif
48+
49+
if llmProvider == .chatGPT {
50+
Picker("Model", selection: $chatGPTModel) {
51+
ForEach(ChatGPTModel.allCases) {
52+
Text($0.text).id($0)
53+
}
54+
}
55+
#if !os(watchOS)
56+
.pickerStyle(.segmented)
57+
#endif
58+
}
59+
}
60+
61+
Section {
62+
Button("Start Chat") {
63+
let type: LLMConfig.ConfigType
64+
switch llmProvider {
65+
case .chatGPT:
66+
type = .chatGPT(chatGPTModel)
67+
case .palm:
68+
type = .palm
69+
}
70+
71+
self.onStartChatTapped(.init(apiKey: apiKey, type: type))
72+
}
73+
#if os(macOS)
74+
.buttonStyle(.borderedProminent)
75+
#endif
76+
.frame(maxWidth: .infinity)
77+
.disabled(apiKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
78+
79+
} footer: {
80+
Text(llmProvider.footerInfo)
81+
.padding(.vertical)
82+
}
83+
}
84+
}
85+
86+
struct LLMConfigView_Previews: PreviewProvider {
87+
static var previews: some View {
88+
NavigationStack {
89+
LLMConfigView { result in
90+
91+
}
92+
}
93+
}
94+
}
95+

Shared/MessageRow.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct MessageRow: Identifiable {
3030

3131
let id = UUID()
3232

33-
var isInteractingWithChatGPT: Bool
33+
var isInteracting: Bool
3434

3535
let sendImage: String
3636
var send: MessageRowType

0 commit comments

Comments
 (0)