Skip to content

Commit f04ddbe

Browse files
committed
Pre-release 0.35.120
1 parent 82d3232 commit f04ddbe

File tree

57 files changed

+1985
-431
lines changed

Some content is hidden

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

57 files changed

+1985
-431
lines changed

Core/Sources/ChatService/ChatService.swift

Lines changed: 82 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import OrderedCollections
1717

1818
public protocol ChatServiceType {
1919
var memory: ContextAwareAutoManagedChatMemory { get set }
20-
func send(_ id: String, content: String, skillSet: [ConversationSkill], references: [FileReference], model: String?, agentMode: Bool) async throws
20+
func send(_ id: String, content: String, skillSet: [ConversationSkill], references: [FileReference], model: String?, agentMode: Bool, userLanguage: String?, turnId: String?) async throws
2121
func stopReceivingMessage() async
2222
func upvote(_ id: String, _ rating: ConversationRating) async
2323
func downvote(_ id: String, _ rating: ConversationRating) async
@@ -79,6 +79,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
7979
private var activeRequestId: String?
8080
private(set) public var conversationId: String?
8181
private var skillSet: [ConversationSkill] = []
82+
private var lastUserRequest: ConversationRequest?
8283
private var isRestored: Bool = false
8384
private var pendingToolCallRequests: [String: ToolCallRequest] = [:]
8485
init(provider: any ConversationServiceProvider,
@@ -98,6 +99,18 @@ public final class ChatService: ChatServiceType, ObservableObject {
9899
subscribeToClientToolConfirmationEvent()
99100
}
100101

102+
deinit {
103+
Task { [weak self] in
104+
await self?.stopReceivingMessage()
105+
}
106+
107+
// Clear all subscriptions
108+
cancellables.forEach { $0.cancel() }
109+
cancellables.removeAll()
110+
111+
// Memory will be deallocated automatically
112+
}
113+
101114
private func subscribeToNotifications() {
102115
memory.observeHistoryChange { [weak self] in
103116
Task { [weak self] in
@@ -303,7 +316,16 @@ public final class ChatService: ChatServiceType, ObservableObject {
303316
}
304317
}
305318

306-
public func send(_ id: String, content: String, skillSet: Array<ConversationSkill>, references: Array<FileReference>, model: String? = nil, agentMode: Bool = false) async throws {
319+
public func send(
320+
_ id: String,
321+
content: String,
322+
skillSet: Array<ConversationSkill>,
323+
references: Array<FileReference>,
324+
model: String? = nil,
325+
agentMode: Bool = false,
326+
userLanguage: String? = nil,
327+
turnId: String? = nil
328+
) async throws {
307329
guard activeRequestId == nil else { return }
308330
let workDoneToken = UUID().uuidString
309331
activeRequestId = workDoneToken
@@ -315,11 +337,15 @@ public final class ChatService: ChatServiceType, ObservableObject {
315337
content: content,
316338
references: references.toConversationReferences()
317339
)
318-
await memory.appendMessage(chatMessage)
340+
341+
// If turnId is provided, it is used to update the existing message, no need to append the user message
342+
if turnId == nil {
343+
await memory.appendMessage(chatMessage)
344+
}
319345

320346
// reset file edits
321347
self.resetFileEdits()
322-
348+
323349
// persist
324350
saveChatMessageToStorage(chatMessage)
325351

@@ -363,7 +389,11 @@ public final class ChatService: ChatServiceType, ObservableObject {
363389
ignoredSkills: ignoredSkills,
364390
references: references,
365391
model: model,
366-
agentMode: agentMode)
392+
agentMode: agentMode,
393+
userLanguage: userLanguage,
394+
turnId: turnId
395+
)
396+
self.lastUserRequest = request
367397
self.skillSet = skillSet
368398
try await send(request)
369399
}
@@ -408,12 +438,23 @@ public final class ChatService: ChatServiceType, ObservableObject {
408438
deleteChatMessageFromStorage(id)
409439
}
410440

411-
// Not used for now
412-
public func resendMessage(id: String) async throws {
413-
if let message = (await memory.history).first(where: { $0.id == id })
441+
public func resendMessage(id: String, model: String? = nil) async throws {
442+
if let _ = (await memory.history).first(where: { $0.id == id }),
443+
let lastUserRequest
414444
{
445+
// TODO: clean up contents for resend message
446+
activeRequestId = nil
415447
do {
416-
try await send(id, content: message.content, skillSet: [], references: [])
448+
try await send(
449+
id,
450+
content: lastUserRequest.content,
451+
skillSet: skillSet,
452+
references: lastUserRequest.references ?? [],
453+
model: model != nil ? model : lastUserRequest.model,
454+
agentMode: lastUserRequest.agentMode,
455+
userLanguage: lastUserRequest.userLanguage,
456+
turnId: id
457+
)
417458
} catch {
418459
print("Failed to resend message")
419460
}
@@ -611,17 +652,34 @@ public final class ChatService: ChatServiceType, ObservableObject {
611652
if CLSError.code == 402 {
612653
Task {
613654
await Status.shared
614-
.updateCLSStatus(.error, busy: false, message: CLSError.message)
655+
.updateCLSStatus(.warning, busy: false, message: CLSError.message)
615656
let errorMessage = ChatMessage(
616657
id: progress.turnId,
617658
chatTabID: self.chatTabInfo.id,
618659
clsTurnID: progress.turnId,
619-
role: .system,
620-
content: CLSError.message
660+
role: .assistant,
661+
content: "",
662+
panelMessages: [.init(type: .error, title: String(CLSError.code ?? 0), message: CLSError.message, location: .Panel)]
621663
)
622664
// will persist in resetongoingRequest()
623-
await memory.removeMessage(progress.turnId)
624665
await memory.appendMessage(errorMessage)
666+
667+
if let lastUserRequest {
668+
guard let fallbackModel = CopilotModelManager.getFallbackLLM(
669+
scope: lastUserRequest.agentMode ? .agentPanel : .chatPanel
670+
) else {
671+
resetOngoingRequest()
672+
return
673+
}
674+
do {
675+
CopilotModelManager.switchToFallbackModel()
676+
try await resendMessage(id: progress.turnId, model: fallbackModel.id)
677+
} catch {
678+
Logger.gitHubCopilot.error(error)
679+
resetOngoingRequest()
680+
}
681+
return
682+
}
625683
}
626684
} else if CLSError.code == 400 && CLSError.message.contains("model is not supported") {
627685
Task {
@@ -633,6 +691,8 @@ public final class ChatService: ChatServiceType, ObservableObject {
633691
errorMessage: "Oops, the model is not supported. Please enable it first in [GitHub Copilot settings](https://github.com/settings/copilot)."
634692
)
635693
await memory.appendMessage(errorMessage)
694+
resetOngoingRequest()
695+
return
636696
}
637697
} else {
638698
Task {
@@ -646,10 +706,10 @@ public final class ChatService: ChatServiceType, ObservableObject {
646706
)
647707
// will persist in resetOngoingRequest()
648708
await memory.appendMessage(errorMessage)
709+
resetOngoingRequest()
710+
return
649711
}
650712
}
651-
resetOngoingRequest()
652-
return
653713
}
654714

655715
Task {
@@ -664,9 +724,8 @@ public final class ChatService: ChatServiceType, ObservableObject {
664724
)
665725
// will persist in resetOngoingRequest()
666726
await memory.appendMessage(message)
727+
resetOngoingRequest()
667728
}
668-
669-
resetOngoingRequest()
670729
}
671730

672731
private func resetOngoingRequest() {
@@ -732,7 +791,12 @@ public final class ChatService: ChatServiceType, ObservableObject {
732791

733792
do {
734793
if let conversationId = conversationId {
735-
try await conversationProvider?.createTurn(with: conversationId, request: request, workspaceURL: getWorkspaceURL())
794+
try await conversationProvider?
795+
.createTurn(
796+
with: conversationId,
797+
request: request,
798+
workspaceURL: getWorkspaceURL()
799+
)
736800
} else {
737801
var requestWithTurns = request
738802

Core/Sources/ChatService/ContextAwareAutoManagedChatMemory.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public final class ContextAwareAutoManagedChatMemory: ChatMemory {
1818
systemPrompt: ""
1919
)
2020
}
21+
22+
deinit { }
2123

2224
public func mutateHistory(_ update: (inout [ChatMessage]) -> Void) async {
2325
await memory.mutateHistory(update)

Core/Sources/ConversationTab/Chat.swift

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ public struct DisplayedChatMessage: Equatable {
1515
public enum Role: Equatable {
1616
case user
1717
case assistant
18-
case system
1918
case ignored
2019
}
2120

@@ -28,8 +27,20 @@ public struct DisplayedChatMessage: Equatable {
2827
public var errorMessage: String? = nil
2928
public var steps: [ConversationProgressStep] = []
3029
public var editAgentRounds: [AgentRound] = []
31-
32-
public init(id: String, role: Role, text: String, references: [ConversationReference] = [], followUp: ConversationFollowUp? = nil, suggestedTitle: String? = nil, errorMessage: String? = nil, steps: [ConversationProgressStep] = [], editAgentRounds: [AgentRound] = []) {
30+
public var panelMessages: [CopilotShowMessageParams] = []
31+
32+
public init(
33+
id: String,
34+
role: Role,
35+
text: String,
36+
references: [ConversationReference] = [],
37+
followUp: ConversationFollowUp? = nil,
38+
suggestedTitle: String? = nil,
39+
errorMessage: String? = nil,
40+
steps: [ConversationProgressStep] = [],
41+
editAgentRounds: [AgentRound] = [],
42+
panelMessages: [CopilotShowMessageParams] = []
43+
) {
3344
self.id = id
3445
self.role = role
3546
self.text = text
@@ -39,6 +50,7 @@ public struct DisplayedChatMessage: Equatable {
3950
self.errorMessage = errorMessage
4051
self.steps = steps
4152
self.editAgentRounds = editAgentRounds
53+
self.panelMessages = panelMessages
4254
}
4355
}
4456

@@ -137,6 +149,7 @@ struct Chat {
137149

138150
@Dependency(\.openURL) var openURL
139151
@AppStorage(\.enableCurrentEditorContext) var enableCurrentEditorContext: Bool
152+
@AppStorage(\.chatResponseLocale) var chatResponseLocale
140153

141154
var body: some ReducerOf<Self> {
142155
BindingReducer()
@@ -180,7 +193,7 @@ struct Chat {
180193
let selectedModelFamily = AppState.shared.getSelectedModelFamily() ?? CopilotModelManager.getDefaultChatModel(scope: AppState.shared.modelScope())?.modelFamily
181194
let agentMode = AppState.shared.isAgentModeEnabled()
182195
return .run { _ in
183-
try await service.send(id, content: message, skillSet: skillSet, references: selectedFiles, model: selectedModelFamily, agentMode: agentMode)
196+
try await service.send(id, content: message, skillSet: skillSet, references: selectedFiles, model: selectedModelFamily, agentMode: agentMode, userLanguage: chatResponseLocale)
184197
}.cancellable(id: CancelID.sendMessage(self.id))
185198

186199
case let .toolCallAccepted(toolCallId):
@@ -209,7 +222,7 @@ struct Chat {
209222
let selectedModelFamily = AppState.shared.getSelectedModelFamily() ?? CopilotModelManager.getDefaultChatModel(scope: AppState.shared.modelScope())?.modelFamily
210223

211224
return .run { _ in
212-
try await service.send(id, content: message, skillSet: skillSet, references: selectedFiles, model: selectedModelFamily)
225+
try await service.send(id, content: message, skillSet: skillSet, references: selectedFiles, model: selectedModelFamily, userLanguage: chatResponseLocale)
213226
}.cancellable(id: CancelID.sendMessage(self.id))
214227

215228
case .returnButtonTapped:
@@ -343,9 +356,9 @@ struct Chat {
343356
id: message.id,
344357
role: {
345358
switch message.role {
346-
case .system: return .system
347359
case .user: return .user
348360
case .assistant: return .assistant
361+
case .system: return .ignored
349362
}
350363
}(),
351364
text: message.content,
@@ -360,7 +373,8 @@ struct Chat {
360373
suggestedTitle: message.suggestedTitle,
361374
errorMessage: message.errorMessage,
362375
steps: message.steps,
363-
editAgentRounds: message.editAgentRounds
376+
editAgentRounds: message.editAgentRounds,
377+
panelMessages: message.panelMessages
364378
))
365379

366380
return all

Core/Sources/ConversationTab/ChatPanel.swift

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ public struct ChatPanel: View {
3737
.accessibilityElement(children: .combine)
3838
.accessibilityLabel("Chat Messages Group")
3939

40-
if chat.history.last?.role == .system {
41-
ChatCLSError(chat: chat).padding(.trailing, 16)
42-
} else if (chat.history.last?.followUp) != nil {
40+
if let _ = chat.history.last?.followUp {
4341
ChatFollowUp(chat: chat)
4442
.padding(.trailing, 16)
4543
.padding(.vertical, 8)
@@ -344,10 +342,9 @@ struct ChatHistoryItem: View {
344342
errorMessage: message.errorMessage,
345343
chat: chat,
346344
steps: message.steps,
347-
editAgentRounds: message.editAgentRounds
345+
editAgentRounds: message.editAgentRounds,
346+
panelMessages: message.panelMessages
348347
)
349-
case .system:
350-
FunctionMessage(chat: chat, id: message.id, text: text)
351348
case .ignored:
352349
EmptyView()
353350
}
@@ -516,8 +513,7 @@ struct ChatPanelInputArea: View {
516513
submitChatMessage()
517514
}
518515
dropDownShowingType = nil
519-
},
520-
completions: chatAutoCompletion
516+
}
521517
)
522518
.focused(focusedField, equals: .textField)
523519
.bind($chat.focusedField, to: focusedField)
@@ -800,11 +796,7 @@ struct ChatPanelInputArea: View {
800796
if !chat.isAgentMode {
801797
promptTemplates = await SharedChatService.shared.loadChatTemplates() ?? []
802798
}
803-
804-
guard !promptTemplates.isEmpty else {
805-
return [releaseNotesTemplate]
806-
}
807-
799+
808800
let templates = promptTemplates + [releaseNotesTemplate]
809801
let skippedTemplates = [ "feedback", "help" ]
810802

@@ -831,29 +823,6 @@ struct ChatPanelInputArea: View {
831823
return chatAgents.filter { $0.slug.hasPrefix(prefix) && includedAgents.contains($0.slug) }
832824
}
833825

834-
func chatAutoCompletion(text: String, proposed: [String], range: NSRange) -> [String] {
835-
guard text.count == 1 else { return [] }
836-
let plugins = [String]() // chat.pluginIdentifiers.map { "/\($0)" }
837-
let availableFeatures = plugins + [
838-
// "/exit",
839-
"@code",
840-
"@sense",
841-
"@project",
842-
"@web",
843-
]
844-
845-
let result: [String] = availableFeatures
846-
.filter { $0.hasPrefix(text) && $0 != text }
847-
.compactMap {
848-
guard let index = $0.index(
849-
$0.startIndex,
850-
offsetBy: range.location,
851-
limitedBy: $0.endIndex
852-
) else { return nil }
853-
return String($0[index...])
854-
}
855-
return result
856-
}
857826
func subscribeToActiveDocumentChangeEvent() {
858827
Publishers.CombineLatest(
859828
XcodeInspector.shared.$latestActiveXcode,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy