Skip to content

Commit 7bc8ea7

Browse files
committed
Merge branch 'feature/ollama-support' into develop
2 parents 4cbd5c3 + 96bf018 commit 7bc8ea7

23 files changed

Lines changed: 1097 additions & 458 deletions

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: 37 additions & 1 deletion
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
},
@@ -171,7 +175,7 @@ struct ChatModelEditView: View {
171175
)
172176

173177
TextField(text: textFieldBinding) {
174-
Text("Max Tokens (Including Reply)")
178+
Text("Context Window")
175179
.multilineTextAlignment(.trailing)
176180
}
177181
.overlay(alignment: .trailing) {
@@ -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: 11 additions & 9 deletions
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 }
@@ -83,14 +84,13 @@ struct EmbeddingModelEdit: ReducerProtocol {
8384
)
8485
return .run { send in
8586
do {
86-
let tokenUsage =
87-
try await EmbeddingService(
88-
configuration: UserPreferenceEmbeddingConfiguration()
89-
.overriding {
90-
$0.model = model
91-
}
92-
).embed(text: "Hello").usage.total_tokens
93-
await send(.testSucceeded("Used \(tokenUsage) tokens."))
87+
_ = try await EmbeddingService(
88+
configuration: UserPreferenceEmbeddingConfiguration()
89+
.overriding {
90+
$0.model = model
91+
}
92+
).embed(text: "Hello")
93+
await send(.testSucceeded("Succeeded!"))
9494
} catch {
9595
await send(.testFailed(error.localizedDescription))
9696
}
@@ -155,6 +155,7 @@ extension EmbeddingModelEdit.State {
155155
format: model.format,
156156
maxTokens: model.info.maxTokens,
157157
modelName: model.info.modelName,
158+
ollamaKeepAlive: model.info.ollamaKeepAlive,
158159
apiKeySelection: .init(
159160
apiKeyName: model.info.apiKeyName,
160161
apiKeyManagement: .init(availableAPIKeyNames: [model.info.apiKeyName])
@@ -175,7 +176,8 @@ extension EmbeddingModel {
175176
baseURL: state.baseURL.trimmingCharacters(in: .whitespacesAndNewlines),
176177
isFullURL: state.isFullURL,
177178
maxTokens: state.maxTokens,
178-
modelName: state.modelName.trimmingCharacters(in: .whitespacesAndNewlines)
179+
modelName: state.modelName.trimmingCharacters(in: .whitespacesAndNewlines),
180+
ollamaKeepAlive: state.ollamaKeepAlive
179181
)
180182
)
181183
}

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

Pro

Submodule Pro updated from 908dd29 to 13a9fde

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
}

0 commit comments

Comments
 (0)