forked from intitni/CopilotForXcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathChatService.swift
More file actions
102 lines (87 loc) · 3.61 KB
/
ChatService.swift
File metadata and controls
102 lines (87 loc) · 3.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import ChatContextCollector
import ChatPlugin
import Combine
import Foundation
import OpenAIService
public final class ChatService: ObservableObject {
public let chatGPTService: any ChatGPTServiceType
public var allPluginCommands: [String] { allPlugins.map { $0.command } }
let pluginController: ChatPluginController
let contextController: DynamicContextController
var cancellable = Set<AnyCancellable>()
@Published public internal(set) var isReceivingMessage = false
@Published public internal(set) var systemPrompt = UserDefaults.shared
.value(for: \.defaultChatSystemPrompt)
@Published public internal(set) var extraSystemPrompt = ""
public init<T: ChatGPTServiceType>(chatGPTService: T) {
self.chatGPTService = chatGPTService
pluginController = ChatPluginController(chatGPTService: chatGPTService, plugins: allPlugins)
contextController = DynamicContextController(
chatGPTService: chatGPTService,
contextCollectors: ActiveDocumentChatContextCollector()
)
pluginController.chatService = self
chatGPTService.objectWillChange.sink { [weak self] _ in
self?.objectWillChange.send()
}.store(in: &cancellable)
}
public func send(content: String) async throws {
guard !isReceivingMessage else { throw CancellationError() }
let handledInPlugin = try await pluginController.handleContent(content)
if handledInPlugin { return }
try await contextController.updatePromptToMatchContent(systemPrompt: """
\(systemPrompt)
\(extraSystemPrompt)
""", content: content)
let stream = try await chatGPTService.send(content: content, summary: nil)
isReceivingMessage = true
for try await _ in stream {}
isReceivingMessage = false
}
public func stopReceivingMessage() async {
await pluginController.stopResponding()
await chatGPTService.stopReceivingMessage()
isReceivingMessage = false
}
public func clearHistory() async {
await pluginController.cancel()
await chatGPTService.clearHistory()
isReceivingMessage = false
}
public func resetPrompt() async {
systemPrompt = UserDefaults.shared.value(for: \.defaultChatSystemPrompt)
extraSystemPrompt = ""
}
public func deleteMessage(id: String) async {
await chatGPTService.mutateHistory { messages in
messages.removeAll(where: { $0.id == id })
}
}
public func resendMessage(id: String) async throws {
if let message = (await chatGPTService.history).first(where: { $0.id == id }) {
try await send(content: message.content)
}
}
public func setMessageAsExtraPrompt(id: String) async {
if let message = (await chatGPTService.history).first(where: { $0.id == id }) {
mutateExtraSystemPrompt(message.content)
await mutateHistory { history in
history.append(.init(
role: .assistant,
content: "",
summary: "System prompt updated"
))
}
}
}
/// Setting it to `nil` to reset the system prompt
public func mutateSystemPrompt(_ newPrompt: String?) {
systemPrompt = newPrompt ?? UserDefaults.shared.value(for: \.defaultChatSystemPrompt)
}
public func mutateExtraSystemPrompt(_ newPrompt: String) {
extraSystemPrompt = newPrompt
}
public func mutateHistory(_ mutator: @escaping (inout [ChatMessage]) -> Void) async {
await chatGPTService.mutateHistory(mutator)
}
}