Skip to content

Commit 7e91a10

Browse files
committed
Merge branch 'release/0.31.0'
2 parents 56394b7 + a1604dd commit 7e91a10

File tree

45 files changed

+2392
-1101
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2392
-1101
lines changed

Core/Sources/ChatContextCollectors/WebChatContextCollector/WebChatContextCollector.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extension WebChatContextCollector {
3232
static func detectLinks(from messages: [ChatMessage]) -> [String] {
3333
return messages.lazy
3434
.compactMap {
35-
$0.content ?? $0.functionCall?.arguments
35+
$0.content ?? $0.toolCalls?.map(\.function.arguments).joined(separator: " ") ?? ""
3636
}
3737
.map(detectLinks(from:))
3838
.flatMap { $0 }

Core/Sources/ChatGPTChatTab/Chat.swift

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ public struct DisplayedChatMessage: Equatable {
99
public enum Role: Equatable {
1010
case user
1111
case assistant
12-
case function
12+
case tool
1313
case ignored
1414
}
1515

1616
public struct Reference: Equatable {
1717
public typealias Kind = ChatMessage.Reference.Kind
18-
18+
1919
public var title: String
2020
public var subtitle: String
2121
public var uri: String
@@ -135,7 +135,7 @@ struct Chat: ReducerProtocol {
135135
await send(.focusOnTextField)
136136
await send(.refresh)
137137
}
138-
138+
139139
case .refresh:
140140
return .run { send in
141141
await send(.chatMenu(.refresh))
@@ -298,8 +298,9 @@ struct Chat: ReducerProtocol {
298298
}.cancellable(id: CancelID.observeDefaultScopesChange(id), cancelInFlight: true)
299299

300300
case .historyChanged:
301-
state.history = service.chatHistory.map { message in
302-
.init(
301+
state.history = service.chatHistory.flatMap { message in
302+
var all = [DisplayedChatMessage]()
303+
all.append(.init(
303304
id: message.id,
304305
role: {
305306
switch message.role {
@@ -312,7 +313,6 @@ struct Chat: ReducerProtocol {
312313
return .assistant
313314
}
314315
return .ignored
315-
case .function: return .function
316316
}
317317
}(),
318318
text: message.summary ?? message.content ?? "",
@@ -325,7 +325,18 @@ struct Chat: ReducerProtocol {
325325
kind: $0.kind
326326
)
327327
}
328-
)
328+
))
329+
330+
for call in message.toolCalls ?? [] {
331+
all.append(.init(
332+
id: message.id + call.id,
333+
role: .tool,
334+
text: call.response.summary ?? call.response.content,
335+
references: []
336+
))
337+
}
338+
339+
return all
329340
}
330341

331342
state.title = {
@@ -401,7 +412,7 @@ struct ChatMenu: ReducerProtocol {
401412
return .run {
402413
await $0(.refresh)
403414
}
404-
415+
405416
case .refresh:
406417
state.temperatureOverride = service.configuration.overriding.temperature
407418
state.chatModelIdOverride = service.configuration.overriding.modelId

Core/Sources/ChatGPTChatTab/ChatPanel.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ struct ChatHistory: View {
258258
trailing: -8
259259
))
260260
.padding(.vertical, 4)
261-
case .function:
261+
case .tool:
262262
FunctionMessage(id: message.id, text: text)
263263
case .ignored:
264264
EmptyView()
@@ -453,7 +453,7 @@ struct ChatPanel_Preview: PreviewProvider {
453453
),
454454
.init(
455455
id: "6",
456-
role: .function,
456+
role: .tool,
457457
text: """
458458
Searching for something...
459459
- abc

Core/Sources/ChatService/ChatService.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ public final class ChatService: ObservableObject {
124124
await chatGPTService.stopReceivingMessage()
125125
isReceivingMessage = false
126126

127-
// if it's stopped before the function finishes, remove the function call.
127+
// if it's stopped before the tool calls finish, remove the message.
128128
await memory.mutateHistory { history in
129-
if history.last?.role == .assistant, history.last?.functionCall != nil {
129+
if history.last?.role == .assistant, history.last?.toolCalls != nil {
130130
history.removeLast()
131131
}
132132
}

Core/Sources/ChatService/ContextAwareAutoManagedChatGPTMemory.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public final class ContextAwareAutoManagedChatGPTMemory: ChatGPTMemory {
3939

4040
public func generatePrompt() async -> ChatGPTPrompt {
4141
let content = (await memory.history)
42-
.last(where: { $0.role == .user || $0.role == .function })?.content
42+
.last(where: { $0.role == .user })?.content
4343
try? await contextController.collectContextInformation(
4444
systemPrompt: """
4545
\(chatService?.systemPrompt ?? "")

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

Lines changed: 10 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.ollamaInfo.keepAlive,
172174
apiKeySelection: .init(
173175
apiKeyName: model.info.apiKeyName,
174176
apiKeyManagement: .init(availableAPIKeyNames: [model.info.apiKeyName])
@@ -193,9 +195,13 @@ extension ChatModel {
193195
if case .googleAI = state.format {
194196
return false
195197
}
198+
if case .ollama = state.format {
199+
return false
200+
}
196201
return state.supportsFunctionCalling
197202
}(),
198-
modelName: state.modelName.trimmingCharacters(in: .whitespacesAndNewlines)
203+
modelName: state.modelName.trimmingCharacters(in: .whitespacesAndNewlines),
204+
ollamaInfo: .init(keepAlive: state.ollamaKeepAlive)
199205
)
200206
)
201207
}

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.ollamaInfo.keepAlive,
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+
ollamaInfo: .init(keepAlive: 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 {

0 commit comments

Comments
 (0)