Skip to content

Commit 6a52b24

Browse files
committed
Update chat context collector to handle multiple scopes and control functions
1 parent 050aeb7 commit 6a52b24

File tree

3 files changed

+101
-30
lines changed

3 files changed

+101
-30
lines changed

Core/Sources/ChatContextCollector/ActiveDocumentChatContextCollector.swift

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ import XcodeInspector
77
public struct ActiveDocumentChatContextCollector: ChatContextCollector {
88
public init() {}
99

10-
public func generateSystemPrompt(history: [ChatMessage], content prompt: String) -> String {
10+
public func generateContext(
11+
history: [ChatMessage],
12+
scopes: Set<String>,
13+
content: String
14+
) -> ChatContext? {
1115
let content = getEditorInformation()
1216
let relativePath = content.documentURL.path
1317
.replacingOccurrences(of: content.projectURL.path, with: "")
1418
let selectionRange = content.editorContent?.selections.first ?? .outOfScope
1519
let editorContent = {
16-
if prompt.hasPrefix("@file") {
20+
if scopes.contains("file") {
1721
return """
1822
File Content:```\(content.language.rawValue)
1923
\(content.editorContent?.content ?? "")
@@ -53,7 +57,7 @@ public struct ActiveDocumentChatContextCollector: ChatContextCollector {
5357
"""
5458
}
5559

56-
if prompt.hasPrefix("@selection") {
60+
if scopes.contains("selection") {
5761
return """
5862
Selected Code \
5963
(start from line \(selectionRange.start.line)):```\(content.language.rawValue)
@@ -72,27 +76,30 @@ public struct ActiveDocumentChatContextCollector: ChatContextCollector {
7276
"""
7377
}()
7478

75-
return """
76-
Active Document Context:###
77-
Document Relative Path: \(relativePath)
78-
Selection Range Start: \
79-
Line \(selectionRange.start.line) \
80-
Character \(selectionRange.start.character)
81-
Selection Range End: \
82-
Line \(selectionRange.end.line) \
83-
Character \(selectionRange.end.character)
84-
Cursor Position: \
85-
Line \(selectionRange.end.line) \
86-
Character \(selectionRange.end.character)
87-
\(editorContent)
88-
Line Annotations:
89-
\(
90-
content.editorContent?.lineAnnotations
91-
.map { " - \($0)" }
92-
.joined(separator: "\n") ?? "N/A"
79+
return .init(
80+
systemPrompt: """
81+
Active Document Context:###
82+
Document Relative Path: \(relativePath)
83+
Selection Range Start: \
84+
Line \(selectionRange.start.line) \
85+
Character \(selectionRange.start.character)
86+
Selection Range End: \
87+
Line \(selectionRange.end.line) \
88+
Character \(selectionRange.end.character)
89+
Cursor Position: \
90+
Line \(selectionRange.end.line) \
91+
Character \(selectionRange.end.character)
92+
\(editorContent)
93+
Line Annotations:
94+
\(
95+
content.editorContent?.lineAnnotations
96+
.map { " - \($0)" }
97+
.joined(separator: "\n") ?? "N/A"
98+
)
99+
###
100+
""",
101+
functions: []
93102
)
94-
###
95-
"""
96103
}
97104
}
98105

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
import Foundation
22
import OpenAIService
33

4+
public struct ChatContext {
5+
public var systemPrompt: String
6+
public var functions: [any ChatGPTFunction]
7+
public init(systemPrompt: String, functions: [any ChatGPTFunction]) {
8+
self.systemPrompt = systemPrompt
9+
self.functions = functions
10+
}
11+
}
12+
413
public protocol ChatContextCollector {
5-
func generateSystemPrompt(history: [ChatMessage], content: String) -> String
14+
func generateContext(
15+
history: [ChatMessage],
16+
scopes: Set<String>,
17+
content: String
18+
) -> ChatContext?
619
}
720

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,83 @@
11
import ChatContextCollector
22
import Foundation
33
import OpenAIService
4+
import Parsing
45
import Preferences
56
import XcodeInspector
67

78
final class DynamicContextController {
89
let contextCollectors: [ChatContextCollector]
910
let memory: AutoManagedChatGPTMemory
11+
let functionProvider: ChatFunctionProvider
1012

11-
init(memory: AutoManagedChatGPTMemory, contextCollectors: ChatContextCollector...) {
13+
convenience init(
14+
memory: AutoManagedChatGPTMemory,
15+
functionProvider: ChatFunctionProvider,
16+
contextCollectors: ChatContextCollector...
17+
) {
18+
self.init(
19+
memory: memory,
20+
functionProvider: functionProvider,
21+
contextCollectors: contextCollectors
22+
)
23+
}
24+
25+
init(
26+
memory: AutoManagedChatGPTMemory,
27+
functionProvider: ChatFunctionProvider,
28+
contextCollectors: [ChatContextCollector]
29+
) {
1230
self.memory = memory
31+
self.functionProvider = functionProvider
1332
self.contextCollectors = contextCollectors
1433
}
1534

1635
func updatePromptToMatchContent(systemPrompt: String, content: String) async throws {
36+
var content = content
37+
let scopes = Self.parseScopes(&content)
38+
functionProvider.removeAll()
1739
let language = UserDefaults.shared.value(for: \.chatGPTLanguage)
1840
let oldMessages = await memory.history
41+
let contexts = contextCollectors.compactMap {
42+
$0.generateContext(history: oldMessages, scopes: scopes, content: content)
43+
}
1944
let contextualSystemPrompt = """
2045
\(language.isEmpty ? "" : "You must always reply in \(language)")
2146
\(systemPrompt)
2247
23-
\(
24-
contextCollectors
25-
.map { $0.generateSystemPrompt(history: oldMessages, content: content) }
26-
.joined(separator: "\n")
27-
)
48+
\(contexts.map(\.systemPrompt).filter { !$0.isEmpty }.joined(separator: "\n\n"))
2849
"""
2950
await memory.mutateSystemPrompt(contextualSystemPrompt)
51+
functionProvider.append(functions: contexts.flatMap(\.functions))
52+
}
53+
}
54+
55+
extension DynamicContextController {
56+
static func parseScopes(_ prompt: inout String) -> Set<String> {
57+
guard !prompt.isEmpty else { return [] }
58+
do {
59+
let parser = Parse {
60+
"@"
61+
Many {
62+
Prefix { $0.isLetter }
63+
} separator: {
64+
"+"
65+
} terminator: {
66+
" "
67+
}
68+
Skip {
69+
Many {
70+
" "
71+
}
72+
}
73+
Rest()
74+
}
75+
let (scopes, rest) = try parser.parse(prompt)
76+
prompt = String(rest)
77+
return Set(scopes.map(String.init))
78+
} catch {
79+
return []
80+
}
3081
}
3182
}
3283

0 commit comments

Comments
 (0)