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
108 lines (92 loc) · 3.69 KB
/
ChatService.swift
File metadata and controls
108 lines (92 loc) · 3.69 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
102
103
104
105
106
107
import ChatContextCollector
import ChatPlugins
import Combine
import Foundation
import OpenAIService
let defaultSystemPrompt = """
You are an AI programming assistant.
Your reply should be concise, clear, informative and logical.
You MUST reply in the format of markdown.
You MUST embed every code you provide in a markdown code block.
You MUST add the programming language name at the start of the markdown code block.
If you are asked to help perform a task, you MUST think step-by-step, then describe each step concisely.
If you are asked to explain code, you MUST explain it step-by-step in a ordered list.
Make your answer short and structured.
"""
public final class ChatService: ObservableObject {
public let chatGPTService: any ChatGPTServiceType
let pluginController: ChatPluginController
let contextController: DynamicContextController
var cancellable = Set<AnyCancellable>()
@Published public internal(set) var systemPrompt = defaultSystemPrompt
@Published public internal(set) var extraSystemPrompt = ""
public init<T: ChatGPTServiceType>(chatGPTService: T) {
self.chatGPTService = chatGPTService
pluginController = ChatPluginController(
chatGPTService: chatGPTService,
plugins:
TerminalChatPlugin.self,
AITerminalChatPlugin.self
)
contextController = DynamicContextController(
chatGPTService: chatGPTService,
contextCollectors: ActiveDocumentChatContextCollector()
)
chatGPTService.objectWillChange.sink { [weak self] _ in
self?.objectWillChange.send()
}.store(in: &cancellable)
}
public func send(content: String) async throws {
let handledInPlugin = try await pluginController.handleContent(content)
if handledInPlugin { return }
try await contextController.updatePromptToMatchContent(systemPrompt: """
\(systemPrompt)
\(extraSystemPrompt)
""", content: content)
_ = try await chatGPTService.send(content: content, summary: nil)
}
public func stopReceivingMessage() async {
await pluginController.stopResponding()
await chatGPTService.stopReceivingMessage()
}
public func clearHistory() async {
await pluginController.cancel()
await chatGPTService.clearHistory()
}
public func resetPrompt() async {
systemPrompt = defaultSystemPrompt
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 ?? defaultSystemPrompt
}
public func mutateExtraSystemPrompt(_ newPrompt: String) {
extraSystemPrompt = newPrompt
}
public func mutateHistory(_ mutator: @escaping (inout [ChatMessage]) -> Void) async {
await chatGPTService.mutateHistory(mutator)
}
}