Skip to content

Commit 16a770e

Browse files
committed
Add UI for ollama models
1 parent d741333 commit 16a770e

11 files changed

Lines changed: 129 additions & 7 deletions

File tree

Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEdit.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct ChatModelEdit: ReducerProtocol {
1414
@BindingState var maxTokens: Int = 4000
1515
@BindingState var supportsFunctionCalling: Bool = true
1616
@BindingState var modelName: String = ""
17+
@BindingState var ollamaKeepAlive: String = ""
1718
var apiKeyName: String { apiKeySelection.apiKeyName }
1819
var baseURL: String { baseURLSelection.baseURL }
1920
var isFullURL: Bool { baseURLSelection.isFullURL }
@@ -48,7 +49,7 @@ struct ChatModelEdit: ReducerProtocol {
4849
Scope(state: \.apiKeySelection, action: /Action.apiKeySelection) {
4950
APIKeySelection()
5051
}
51-
52+
5253
Scope(state: \.baseURLSelection, action: /Action.baseURLSelection) {
5354
BaseURLSelection()
5455
}
@@ -135,10 +136,10 @@ struct ChatModelEdit: ReducerProtocol {
135136
state.suggestedMaxTokens = nil
136137
return .none
137138
}
138-
139+
139140
case .apiKeySelection:
140141
return .none
141-
142+
142143
case .baseURLSelection:
143144
return .none
144145

@@ -169,6 +170,7 @@ extension ChatModelEdit.State {
169170
maxTokens: model.info.maxTokens,
170171
supportsFunctionCalling: model.info.supportsFunctionCalling,
171172
modelName: model.info.modelName,
173+
ollamaKeepAlive: model.info.ollamaKeepAlive,
172174
apiKeySelection: .init(
173175
apiKeyName: model.info.apiKeyName,
174176
apiKeyManagement: .init(availableAPIKeyNames: [model.info.apiKeyName])
@@ -195,7 +197,8 @@ extension ChatModel {
195197
}
196198
return state.supportsFunctionCalling
197199
}(),
198-
modelName: state.modelName.trimmingCharacters(in: .whitespacesAndNewlines)
200+
modelName: state.modelName.trimmingCharacters(in: .whitespacesAndNewlines),
201+
ollamaKeepAlive: state.ollamaKeepAlive
199202
)
200203
)
201204
}

Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ struct ChatModelEditView: View {
2424
openAICompatible
2525
case .googleAI:
2626
googleAI
27+
case .ollama:
28+
ollama
2729
}
2830
}
2931
}
@@ -92,6 +94,8 @@ struct ChatModelEditView: View {
9294
Text("OpenAI Compatible").tag(format)
9395
case .googleAI:
9496
Text("Google Generative AI").tag(format)
97+
case .ollama:
98+
Text("Ollama").tag(format)
9599
}
96100
}
97101
},
@@ -344,6 +348,38 @@ struct ChatModelEditView: View {
344348

345349
maxTokensTextField
346350
}
351+
352+
@ViewBuilder
353+
var ollama: some View {
354+
baseURLTextField(prompt: Text("http://127.0.0.1:11434")) {
355+
Text("/api/chat")
356+
}
357+
358+
WithViewStore(
359+
store,
360+
removeDuplicates: { $0.modelName == $1.modelName }
361+
) { viewStore in
362+
TextField("Model Name", text: viewStore.$modelName)
363+
}
364+
365+
maxTokensTextField
366+
367+
WithViewStore(
368+
store,
369+
removeDuplicates: { $0.ollamaKeepAlive == $1.ollamaKeepAlive }
370+
) { viewStore in
371+
TextField(text: viewStore.$ollamaKeepAlive, prompt: Text("Default Value")) {
372+
Text("Keep Alive")
373+
}
374+
}
375+
376+
VStack(alignment: .leading, spacing: 8) {
377+
Text(Image(systemName: "exclamationmark.triangle.fill")) + Text(
378+
" For more details, please visit [https://ollama.com](https://ollama.com)."
379+
)
380+
}
381+
.padding(.vertical)
382+
}
347383
}
348384

349385
#Preview("OpenAI") {

Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelManagement.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ extension ChatModel: ManageableAIModel {
1111
case .azureOpenAI: return "Azure OpenAI"
1212
case .openAICompatible: return "OpenAI Compatible"
1313
case .googleAI: return "Google Generative AI"
14+
case .ollama: return "Ollama"
1415
}
1516
}
1617

Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEdit.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ struct EmbeddingModelEdit: ReducerProtocol {
1313
@BindingState var format: EmbeddingModel.Format
1414
@BindingState var maxTokens: Int = 8191
1515
@BindingState var modelName: String = ""
16+
@BindingState var ollamaKeepAlive: String = ""
1617
var apiKeyName: String { apiKeySelection.apiKeyName }
1718
var baseURL: String { baseURLSelection.baseURL }
1819
var isFullURL: Bool { baseURLSelection.isFullURL }
@@ -155,6 +156,7 @@ extension EmbeddingModelEdit.State {
155156
format: model.format,
156157
maxTokens: model.info.maxTokens,
157158
modelName: model.info.modelName,
159+
ollamaKeepAlive: model.info.ollamaKeepAlive,
158160
apiKeySelection: .init(
159161
apiKeyName: model.info.apiKeyName,
160162
apiKeyManagement: .init(availableAPIKeyNames: [model.info.apiKeyName])
@@ -175,7 +177,8 @@ extension EmbeddingModel {
175177
baseURL: state.baseURL.trimmingCharacters(in: .whitespacesAndNewlines),
176178
isFullURL: state.isFullURL,
177179
maxTokens: state.maxTokens,
178-
modelName: state.modelName.trimmingCharacters(in: .whitespacesAndNewlines)
180+
modelName: state.modelName.trimmingCharacters(in: .whitespacesAndNewlines),
181+
ollamaKeepAlive: state.ollamaKeepAlive
179182
)
180183
)
181184
}

Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ struct EmbeddingModelEditView: View {
2222
azureOpenAI
2323
case .openAICompatible:
2424
openAICompatible
25+
case .ollama:
26+
ollama
2527
}
2628
}
2729
}
@@ -88,6 +90,8 @@ struct EmbeddingModelEditView: View {
8890
Text("Azure OpenAI").tag(format)
8991
case .openAICompatible:
9092
Text("OpenAI Compatible").tag(format)
93+
case .ollama:
94+
Text("Ollama").tag(format)
9195
}
9296
}
9397
},
@@ -289,6 +293,38 @@ struct EmbeddingModelEditView: View {
289293

290294
maxTokensTextField
291295
}
296+
297+
@ViewBuilder
298+
var ollama: some View {
299+
baseURLTextField(prompt: Text("http://127.0.0.1:11434")) {
300+
Text("/api/embeddings")
301+
}
302+
303+
WithViewStore(
304+
store,
305+
removeDuplicates: { $0.modelName == $1.modelName }
306+
) { viewStore in
307+
TextField("Model Name", text: viewStore.$modelName)
308+
}
309+
310+
maxTokensTextField
311+
312+
WithViewStore(
313+
store,
314+
removeDuplicates: { $0.ollamaKeepAlive == $1.ollamaKeepAlive }
315+
) { viewStore in
316+
TextField(text: viewStore.$ollamaKeepAlive, prompt: Text("Default Value")) {
317+
Text("Keep Alive")
318+
}
319+
}
320+
321+
VStack(alignment: .leading, spacing: 8) {
322+
Text(Image(systemName: "exclamationmark.triangle.fill")) + Text(
323+
" For more details, please visit [https://ollama.com](https://ollama.com)."
324+
)
325+
}
326+
.padding(.vertical)
327+
}
292328
}
293329

294330
class EmbeddingModelManagementView_Editing_Previews: PreviewProvider {

Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelManagement.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extension EmbeddingModel: ManageableAIModel {
1010
case .openAI: return "OpenAI"
1111
case .azureOpenAI: return "Azure OpenAI"
1212
case .openAICompatible: return "OpenAI Compatible"
13+
case .ollama: return "Ollama"
1314
}
1415
}
1516

Tool/Sources/AIModel/ChatModel.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public struct ChatModel: Codable, Equatable, Identifiable {
2121
case azureOpenAI
2222
case openAICompatible
2323
case googleAI
24+
case ollama
2425
}
2526

2627
public struct Info: Codable, Equatable {
@@ -42,6 +43,8 @@ public struct ChatModel: Codable, Equatable, Identifiable {
4243
get { modelName }
4344
set { modelName = newValue }
4445
}
46+
@FallbackDecoding<EmptyString>
47+
public var ollamaKeepAlive: String
4548

4649
public init(
4750
apiKeyName: String = "",
@@ -50,7 +53,8 @@ public struct ChatModel: Codable, Equatable, Identifiable {
5053
maxTokens: Int = 4000,
5154
supportsFunctionCalling: Bool = true,
5255
supportsOpenAIAPI2023_11: Bool = false,
53-
modelName: String = ""
56+
modelName: String = "",
57+
ollamaKeepAlive: String = ""
5458
) {
5559
self.apiKeyName = apiKeyName
5660
self.baseURL = baseURL
@@ -59,6 +63,7 @@ public struct ChatModel: Codable, Equatable, Identifiable {
5963
self.supportsFunctionCalling = supportsFunctionCalling
6064
self.supportsOpenAIAPI2023_11 = supportsOpenAIAPI2023_11
6165
self.modelName = modelName
66+
self.ollamaKeepAlive = ollamaKeepAlive
6267
}
6368
}
6469

@@ -83,6 +88,10 @@ public struct ChatModel: Codable, Equatable, Identifiable {
8388
let baseURL = info.baseURL
8489
if baseURL.isEmpty { return "https://generativelanguage.googleapis.com/v1" }
8590
return "\(baseURL)/v1/chat/completions"
91+
case .ollama:
92+
let baseURL = info.baseURL
93+
if baseURL.isEmpty { return "http://localhost:11434/api/chat" }
94+
return "\(baseURL)/api/chat"
8695
}
8796
}
8897
}

Tool/Sources/AIModel/EmbeddingModel.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public struct EmbeddingModel: Codable, Equatable, Identifiable {
2020
case openAI
2121
case azureOpenAI
2222
case openAICompatible
23+
case ollama
2324
}
2425

2526
public struct Info: Codable, Equatable {
@@ -39,21 +40,25 @@ public struct EmbeddingModel: Codable, Equatable, Identifiable {
3940
get { modelName }
4041
set { modelName = newValue }
4142
}
43+
@FallbackDecoding<EmptyString>
44+
public var ollamaKeepAlive: String
4245

4346
public init(
4447
apiKeyName: String = "",
4548
baseURL: String = "",
4649
isFullURL: Bool = false,
4750
maxTokens: Int = 8192,
4851
dimensions: Int = 1536,
49-
modelName: String = ""
52+
modelName: String = "",
53+
ollamaKeepAlive: String = ""
5054
) {
5155
self.apiKeyName = apiKeyName
5256
self.baseURL = baseURL
5357
self.isFullURL = isFullURL
5458
self.maxTokens = maxTokens
5559
self.dimensions = dimensions
5660
self.modelName = modelName
61+
self.ollamaKeepAlive = ollamaKeepAlive
5762
}
5863
}
5964

@@ -74,6 +79,10 @@ public struct EmbeddingModel: Codable, Equatable, Identifiable {
7479
let version = "2024-02-15-preview"
7580
if baseURL.isEmpty { return "" }
7681
return "\(baseURL)/openai/deployments/\(deployment)/embeddings?api-version=\(version)"
82+
case .ollama:
83+
let baseURL = info.baseURL
84+
if baseURL.isEmpty { return "http://localhost:11434/api/embeddings" }
85+
return "\(baseURL)/api/embeddings"
7786
}
7887
}
7988
}

Tool/Sources/OpenAIService/APIs/OpenAIService.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ actor OpenAIService: ChatCompletionsStreamAPI, ChatCompletionsAPI {
5151
request.setValue(apiKey, forHTTPHeaderField: "api-key")
5252
case .googleAI:
5353
assertionFailure("Unsupported")
54+
case .ollama:
55+
assertionFailure("Unsupported")
5456
}
5557
}
5658

@@ -112,6 +114,8 @@ actor OpenAIService: ChatCompletionsStreamAPI, ChatCompletionsAPI {
112114
request.setValue(apiKey, forHTTPHeaderField: "api-key")
113115
case .googleAI:
114116
assertionFailure("Unsupported")
117+
case .ollama:
118+
assertionFailure("Unsupported")
115119
}
116120
}
117121

Tool/Sources/OpenAIService/ChatGPTService.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ public class ChatGPTService: ChatGPTServiceType {
8686
endpoint: endpoint,
8787
requestBody: requestBody
8888
)
89+
case .ollama:
90+
return OllamaService(
91+
apiKey: apiKey,
92+
model: model,
93+
endpoint: endpoint,
94+
requestBody: requestBody
95+
)
8996
}
9097
}
9198

@@ -106,6 +113,13 @@ public class ChatGPTService: ChatGPTServiceType {
106113
endpoint: endpoint,
107114
requestBody: requestBody
108115
)
116+
case .ollama:
117+
return OllamaService(
118+
apiKey: apiKey,
119+
model: model,
120+
endpoint: endpoint,
121+
requestBody: requestBody
122+
)
109123
}
110124
}
111125

0 commit comments

Comments
 (0)