-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathCopilotLanguageModelToolManager.swift
More file actions
108 lines (94 loc) · 4.5 KB
/
CopilotLanguageModelToolManager.swift
File metadata and controls
108 lines (94 loc) · 4.5 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
import ConversationServiceProvider
import Foundation
import Logger
public extension Notification.Name {
static let gitHubCopilotToolsDidChange = Notification
.Name("com.github.CopilotForXcode.CopilotToolsDidChange")
static let gitHubCopilotCustomAgentToolsDidChange = Notification
.Name("com.github.CopilotForXcode.CustomAgentToolsDidChange")
}
public class CopilotLanguageModelToolManager {
private static var availableLanguageModelTools: [LanguageModelTool]?
public static func updateToolsStatus(_ tools: [LanguageModelTool]) {
// If we have no previous snapshot, just adopt what we received.
guard let previous = availableLanguageModelTools, !previous.isEmpty else {
let sorted = sortTools(tools)
guard sorted != availableLanguageModelTools else { return }
availableLanguageModelTools = sorted
DispatchQueue.main.async {
Logger.client.info("Notify about language model tools change: \(getLanguageModelToolsSummary())")
DistributedNotificationCenter.default().post(name: .gitHubCopilotToolsDidChange, object: nil)
}
return
}
// Map previous and new by name for merging.
let previousByName = Dictionary(previous.map { ($0.name, $0) }) { first, _ in first }
let incomingByName = Dictionary(tools.map { ($0.name, $0) }) { first, _ in first }
var merged: [LanguageModelTool] = []
for (name, oldTool) in previousByName {
if let updated = incomingByName[name] {
merged.append(updated)
} else {
if oldTool.status == .disabled {
merged.append(oldTool) // already disabled, keep as-is
} else {
// Synthesize a disabled copy (all fields same except status).
let disabledCopy = LanguageModelTool(
id: oldTool.id,
type: oldTool.type,
toolProvider: oldTool.toolProvider,
nameForModel: oldTool.nameForModel,
name: oldTool.name,
displayName: oldTool.displayName,
description: oldTool.description,
displayDescription: oldTool.displayDescription,
inputSchema: oldTool.inputSchema,
annotations: oldTool.annotations,
status: .disabled
)
merged.append(disabledCopy)
}
}
}
for (name, newTool) in incomingByName {
if previousByName[name] == nil {
merged.append(newTool)
}
}
let sorted = sortTools(merged)
guard sorted != availableLanguageModelTools else { return }
availableLanguageModelTools = sorted
DispatchQueue.main.async {
Logger.client.info("Notify about language model tools change (merged): \(getLanguageModelToolsSummary())")
DistributedNotificationCenter.default().post(name: .gitHubCopilotToolsDidChange, object: nil)
}
}
// Extracted sorting logic to keep behavior identical.
private static func sortTools(_ tools: [LanguageModelTool]) -> [LanguageModelTool] {
tools.sorted { lhs, rhs in
let lKey = lhs.displayName ?? lhs.name
let rKey = rhs.displayName ?? rhs.name
let primary = lKey.localizedCaseInsensitiveCompare(rKey)
if primary == .orderedSame {
return lhs.name.localizedCaseInsensitiveCompare(rhs.name) == .orderedAscending
}
return primary == .orderedAscending
}
}
private static func getLanguageModelToolsSummary() -> String {
guard let tools = availableLanguageModelTools else { return "" }
return "\(tools.filter { $0.status == .enabled }.count) enabled, \(tools.filter { $0.status == .disabled }.count) disabled."
}
public static func getAvailableLanguageModelTools() -> [LanguageModelTool]? {
return availableLanguageModelTools
}
public static func hasLanguageModelTools() -> Bool {
return availableLanguageModelTools != nil && !availableLanguageModelTools!.isEmpty
}
public static func clearLanguageModelTools() {
availableLanguageModelTools = []
DispatchQueue.main.async {
DistributedNotificationCenter.default().post(name: .gitHubCopilotToolsDidChange, object: nil)
}
}
}