forked from intitni/CopilotForXcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDynamicContextController.swift
More file actions
117 lines (105 loc) · 3.66 KB
/
DynamicContextController.swift
File metadata and controls
117 lines (105 loc) · 3.66 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
108
109
110
111
112
113
114
115
116
import ChatContextCollector
import Foundation
import OpenAIService
import Parsing
import Preferences
import XcodeInspector
final class DynamicContextController {
let contextCollectors: [ChatContextCollector]
let memory: AutoManagedChatGPTMemory
let functionProvider: ChatFunctionProvider
let configuration: ChatGPTConfiguration
var defaultScopes = [] as Set<String>
convenience init(
memory: AutoManagedChatGPTMemory,
functionProvider: ChatFunctionProvider,
configuration: ChatGPTConfiguration,
contextCollectors: ChatContextCollector...
) {
self.init(
memory: memory,
functionProvider: functionProvider,
configuration: configuration,
contextCollectors: contextCollectors
)
}
init(
memory: AutoManagedChatGPTMemory,
functionProvider: ChatFunctionProvider,
configuration: ChatGPTConfiguration,
contextCollectors: [ChatContextCollector]
) {
self.memory = memory
self.functionProvider = functionProvider
self.configuration = configuration
self.contextCollectors = contextCollectors
}
func updatePromptToMatchContent(systemPrompt: String, content: String) async throws {
var content = content
var scopes = Self.parseScopes(&content)
scopes.formUnion(defaultScopes)
functionProvider.removeAll()
let language = UserDefaults.shared.value(for: \.chatGPTLanguage)
let oldMessages = await memory.history
let contexts = await withTaskGroup(
of: ChatContext.self
) { [scopes, content, configuration] group in
for collector in contextCollectors {
group.addTask {
await collector.generateContext(
history: oldMessages,
scopes: scopes,
content: content,
configuration: configuration
)
}
}
var contexts = [ChatContext]()
for await context in group {
contexts.append(context)
}
return contexts
}
let separator = String(repeating: "=", count: 32) // only 1 token
let contextPrompts = contexts
.flatMap(\.systemPrompt)
.filter { !$0.content.isEmpty }
.sorted { $0.priority > $1.priority }
let contextualSystemPrompt = """
\(language.isEmpty ? "" : "You must always reply in \(language)")
\(systemPrompt)
Below are information related to the conversation, separated by \(separator)
\(contextPrompts.map(\.content).joined(separator: "\n\(separator)\n"))
"""
await memory.mutateSystemPrompt(contextualSystemPrompt)
functionProvider.append(functions: contexts.flatMap(\.functions))
}
}
extension DynamicContextController {
static func parseScopes(_ prompt: inout String) -> Set<String> {
guard !prompt.isEmpty else { return [] }
do {
let parser = Parse {
"@"
Many {
Prefix { $0.isLetter }
} separator: {
"+"
} terminator: {
" "
}
Skip {
Many {
" "
}
}
Rest()
}
let (scopes, rest) = try parser.parse(prompt)
prompt = String(rest)
return Set(scopes.map(String.init))
} catch {
return []
}
}
}