Skip to content

Commit fd64a94

Browse files
committed
tool can give alternative definition by model
1 parent 41b1757 commit fd64a94

File tree

4 files changed

+95
-55
lines changed

4 files changed

+95
-55
lines changed

src/extension/tools/common/toolsRegistry.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,18 @@ export interface ICopilotTool<T> extends vscode.LanguageModelTool<T> {
4949

5050
/**
5151
* Optionally get a programmatic override for the LM tool information. This
52-
* can be driven by EXP for example. ⚠️ A tool using an alternative
53-
* definition MUST still accept its default parameters because the
54-
* alternative definition will only be applied within the Copilot extension,
55-
* not other extensions' usages via `vscode.lm.tools`.
52+
* can be driven by EXP for example, or customized based on the current model.
53+
* ⚠️ A tool using an alternative definition MUST still accept its default
54+
* parameters because the alternative definition will only be applied within
55+
* the Copilot extension, not other extensions' usages via `vscode.lm.tools`.
56+
*
57+
* @param modelInfo Optional information about the currently selected language model.
58+
* If provided, allows customizing the tool definition per model.
5659
*/
57-
alternativeDefinition?(): vscode.LanguageModelToolInformation | undefined;
60+
alternativeDefinition?(modelInfo?: vscode.LanguageModelChat): vscode.LanguageModelToolInformation | undefined;
5861
}
5962

63+
6064
export interface ICopilotToolCtor {
6165
readonly toolName: ToolName;
6266
new(...args: any[]): ICopilotTool<any>;

src/extension/tools/node/test/testToolsService.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -143,29 +143,41 @@ export class TestToolsService extends BaseToolsService implements IToolsService
143143
const toolMap = new Map(this.tools.map(t => [t.name, t]));
144144

145145
const packageJsonTools = getPackagejsonToolsForTest();
146-
return this.tools.filter(tool => {
147-
// 0. Check if the tool was enabled or disabled via the tool picker
148-
const toolPickerSelection = request.tools.get(getContributedToolName(tool.name));
149-
if (typeof toolPickerSelection === 'boolean') {
150-
return toolPickerSelection;
151-
}
146+
return this.tools
147+
.map(tool => {
148+
// Apply model-specific alternative if available via alternativeDefinition
149+
const owned = this._copilotTools.get(getToolName(tool.name) as ToolName);
150+
if (owned?.value?.alternativeDefinition) {
151+
const alternative = owned.value.alternativeDefinition(request.model);
152+
if (alternative) {
153+
return alternative;
154+
}
155+
}
156+
return tool;
157+
})
158+
.filter(tool => {
159+
// 0. Check if the tool was enabled or disabled via the tool picker
160+
const toolPickerSelection = request.tools.get(getContributedToolName(tool.name));
161+
if (typeof toolPickerSelection === 'boolean') {
162+
return toolPickerSelection;
163+
}
152164

153-
// 1. Check for what the consumer wants explicitly
154-
const explicit = filter?.(tool);
155-
if (explicit !== undefined) {
156-
return explicit;
157-
}
165+
// 1. Check for what the consumer wants explicitly
166+
const explicit = filter?.(tool);
167+
if (explicit !== undefined) {
168+
return explicit;
169+
}
158170

159-
// 2. Check if the request's tools explicitly asked for this tool to be enabled
160-
for (const ref of request.toolReferences) {
161-
const usedTool = toolMap.get(ref.name);
162-
if (usedTool?.tags.includes(`enable_other_tool_${tool.name}`)) {
163-
return true;
171+
// 2. Check if the request's tools explicitly asked for this tool to be enabled
172+
for (const ref of request.toolReferences) {
173+
const usedTool = toolMap.get(ref.name);
174+
if (usedTool?.tags.includes(`enable_other_tool_${tool.name}`)) {
175+
return true;
176+
}
164177
}
165-
}
166178

167-
return packageJsonTools.has(tool.name);
168-
});
179+
return packageJsonTools.has(tool.name);
180+
});
169181

170182
}
171183

src/extension/tools/vscode-node/toolsService.ts

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -94,39 +94,51 @@ export class ToolsService extends BaseToolsService {
9494
getEnabledTools(request: vscode.ChatRequest, filter?: (tool: vscode.LanguageModelToolInformation) => boolean | undefined): vscode.LanguageModelToolInformation[] {
9595
const toolMap = new Map(this.tools.map(t => [t.name, t]));
9696

97-
return this.tools.filter(tool => {
98-
// 0. Check if the tool was disabled via the tool picker. If so, it must be disabled here
99-
const toolPickerSelection = request.tools.get(getContributedToolName(tool.name));
100-
if (toolPickerSelection === false) {
101-
return false;
102-
}
103-
104-
// 1. Check for what the consumer wants explicitly
105-
const explicit = filter?.(tool);
106-
if (explicit !== undefined) {
107-
return explicit;
108-
}
109-
110-
// 2. Check if the request's tools explicitly asked for this tool to be enabled
111-
for (const ref of request.toolReferences) {
112-
const usedTool = toolMap.get(ref.name);
113-
if (usedTool?.tags.includes(`enable_other_tool_${tool.name}`)) {
114-
return true;
97+
return this.tools
98+
.map(tool => {
99+
// Apply model-specific alternative if available via alternativeDefinition
100+
const owned = this._copilotTools.value.get(getToolName(tool.name) as ToolName);
101+
if (owned?.alternativeDefinition) {
102+
const alternative = owned.alternativeDefinition(request.model);
103+
if (alternative) {
104+
return alternative;
105+
}
106+
}
107+
return tool;
108+
})
109+
.filter(tool => {
110+
// 0. Check if the tool was disabled via the tool picker. If so, it must be disabled here
111+
const toolPickerSelection = request.tools.get(getContributedToolName(tool.name));
112+
if (toolPickerSelection === false) {
113+
return false;
115114
}
116-
}
117115

118-
// 3. If this tool is neither enabled nor disabled, then consumer didn't have opportunity to enable/disable it.
119-
// This can happen when a tool is added during another tool call (e.g. installExt tool installs an extension that contributes tools).
120-
if (toolPickerSelection === undefined && tool.tags.includes('extension_installed_by_tool')) {
121-
return true;
122-
}
116+
// 1. Check for what the consumer wants explicitly
117+
const explicit = filter?.(tool);
118+
if (explicit !== undefined) {
119+
return explicit;
120+
}
123121

124-
// Tool was enabled via tool picker
125-
if (toolPickerSelection === true) {
126-
return true;
127-
}
122+
// 2. Check if the request's tools explicitly asked for this tool to be enabled
123+
for (const ref of request.toolReferences) {
124+
const usedTool = toolMap.get(ref.name);
125+
if (usedTool?.tags.includes(`enable_other_tool_${tool.name}`)) {
126+
return true;
127+
}
128+
}
128129

129-
return false;
130-
});
130+
// 3. If this tool is neither enabled nor disabled, then consumer didn't have opportunity to enable/disable it.
131+
// This can happen when a tool is added during another tool call (e.g. installExt tool installs an extension that contributes tools).
132+
if (toolPickerSelection === undefined && tool.tags.includes('extension_installed_by_tool')) {
133+
return true;
134+
}
135+
136+
// Tool was enabled via tool picker
137+
if (toolPickerSelection === true) {
138+
return true;
139+
}
140+
141+
return false;
142+
});
131143
}
132144
}

test/base/extHostContext/simulationExtHostToolsService.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,19 @@ export class SimulationExtHostToolsService extends BaseToolsService implements I
122122

123123
getEnabledTools(request: ChatRequest, filter?: (tool: LanguageModelToolInformation) => boolean | undefined): LanguageModelToolInformation[] {
124124
const packageJsonTools = getPackagejsonToolsForTest();
125-
return this.tools.filter(tool => filter?.(tool) ?? (!this._disabledTools.has(getToolName(tool.name)) && packageJsonTools.has(tool.name)));
125+
return this.tools
126+
.map(tool => {
127+
// Apply model-specific alternative if available via alternativeDefinition
128+
const owned = this.copilotTools.get(getToolName(tool.name) as ToolName);
129+
if (owned?.alternativeDefinition) {
130+
const alternative = owned.alternativeDefinition(request.model);
131+
if (alternative) {
132+
return alternative;
133+
}
134+
}
135+
return tool;
136+
})
137+
.filter(tool => filter?.(tool) ?? (!this._disabledTools.has(getToolName(tool.name)) && packageJsonTools.has(tool.name)));
126138
}
127139

128140
addTestToolOverride(info: LanguageModelToolInformation, tool: LanguageModelTool<unknown>): void {

0 commit comments

Comments
 (0)