Skip to content

Commit 6ca50e6

Browse files
m4dc4problourens
authored andcommitted
Update getErrors (GetErrors / problems) tool (#394)
* Update getErrors (GetErrors / problems) tool Update this tool so it can gather all errors. * Updated model description so agent knows the tool can gather all errors * Updated implementation such that if no URL is given, the tool returns all errors. * Update tool definition Made the 'filePaths' argument optional, and updated description to indicate how the argument should be used. * Updates from review * Made filePaths on IGetErrorParams optional, updated implementation of invoke accordingly. * Updated prepareInvoke to handle optional argument properly as well. * Tweaks --------- Co-authored-by: Rob Lourens <[email protected]>
1 parent 00a7e88 commit 6ca50e6

File tree

3 files changed

+64
-31
lines changed

3 files changed

+64
-31
lines changed

package.json

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -400,22 +400,19 @@
400400
"canBeReferencedInPrompt": true,
401401
"icon": "$(error)",
402402
"userDescription": "%copilot.tools.errors.description%",
403-
"modelDescription": "Get any compile or lint errors in a code file. If the user mentions errors or problems in a file, they may be referring to these. Use the tool to see the same errors that the user is seeing. Also use this tool after editing a file to validate the change.",
403+
"modelDescription": "Get any compile or lint errors in a specific file or across all files. If the user mentions errors or problems in a file, they may be referring to these. Use the tool to see the same errors that the user is seeing. If the user asks you to analyze all errors, or does not specify a file, use this tool to gather errors for all files. Also use this tool after editing a file to validate the change.",
404404
"tags": [],
405405
"inputSchema": {
406406
"type": "object",
407407
"properties": {
408408
"filePaths": {
409-
"description": "The absolute paths to the files to check for errors.",
409+
"description": "The absolute paths to the files to check for errors. Omit 'filePaths' when retrieving all errors.",
410410
"type": "array",
411411
"items": {
412412
"type": "string"
413413
}
414414
}
415-
},
416-
"required": [
417-
"filePaths"
418-
]
415+
}
419416
}
420417
},
421418
{

src/extension/tools/node/getErrorsTool.tsx

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as l10n from '@vscode/l10n';
77
import { BasePromptElementProps, PromptElement, PromptElementProps } from '@vscode/prompt-tsx';
88
import type * as vscode from 'vscode';
99
import { ILanguageDiagnosticsService } from '../../../platform/languages/common/languageDiagnosticsService';
10+
import { ILogService } from '../../../platform/log/common/logService';
1011
import { IPromptPathRepresentationService } from '../../../platform/prompts/common/promptPathRepresentationService';
1112
import { IWorkspaceService } from '../../../platform/workspace/common/workspaceService';
1213
import { getLanguage } from '../../../util/common/languages';
@@ -24,10 +25,14 @@ import { DiagnosticContext, Diagnostics } from '../../prompts/node/inline/diagno
2425
import { ToolName } from '../common/toolNames';
2526
import { ICopilotTool, ToolRegistry } from '../common/toolsRegistry';
2627
import { checkCancellation, formatUriForFileWidget, resolveToolInputPath } from './toolUtils';
28+
import { coalesce } from '../../../util/vs/base/common/arrays';
2729

2830
interface IGetErrorsParams {
29-
filePaths: string[];
31+
// Note that empty array is not the same as absence; empty array
32+
// will not return any errors. Absence returns all errors.
33+
filePaths?: string[];
3034
// sparse array of ranges, as numbers because it goes through JSON
35+
// ignored if filePaths is missing / null.
3136
ranges?: ([a: number, b: number, c: number, d: number] | undefined)[];
3237
}
3338

@@ -39,12 +44,18 @@ class GetErrorsTool extends Disposable implements ICopilotTool<IGetErrorsParams>
3944
@ILanguageDiagnosticsService private readonly languageDiagnosticsService: ILanguageDiagnosticsService,
4045
@IWorkspaceService private readonly workspaceService: IWorkspaceService,
4146
@IPromptPathRepresentationService private readonly promptPathRepresentationService: IPromptPathRepresentationService,
47+
@ILogService private readonly logService: ILogService
4248
) {
4349
super();
4450
}
4551

4652
async invoke(options: vscode.LanguageModelToolInvocationOptions<IGetErrorsParams>, token: CancellationToken) {
47-
const diagnostics = await Promise.all(options.input.filePaths.map(async (filePath, i): Promise<{ context: DiagnosticContext; uri: URI; diagnostics: vscode.Diagnostic[] }> => {
53+
const getAll = () => this.languageDiagnosticsService.getAllDiagnostics()
54+
.map(d => ({ uri: d[0], diagnostics: d[1].filter(e => e.severity <= DiagnosticSeverity.Warning) }))
55+
// filter any documents w/o warnings or errors
56+
.filter(d => d.diagnostics.length > 0);
57+
58+
const getSome = (filePaths: string[]) => filePaths.map((filePath, i) => {
4859
const uri = resolveToolInputPath(filePath, this.promptPathRepresentationService);
4960
const range = options.input.ranges?.[i];
5061
if (!uri) {
@@ -57,16 +68,28 @@ class GetErrorsTool extends Disposable implements ICopilotTool<IGetErrorsParams>
5768

5869
diagnostics = diagnostics.filter(d => d.severity <= DiagnosticSeverity.Warning);
5970

60-
const document = await this.workspaceService.openTextDocumentAndSnapshot(uri);
61-
checkCancellation(token);
62-
6371
return {
64-
context: { document, language: getLanguage(document) },
6572
diagnostics,
6673
uri,
6774
};
68-
}));
69-
75+
});
76+
77+
const ds = options.input.filePaths?.length ? getSome(options.input.filePaths) : getAll();
78+
79+
const diagnostics = coalesce(await Promise.all(ds.map((async ({ uri, diagnostics }) => {
80+
try {
81+
const document = await this.workspaceService.openTextDocumentAndSnapshot(uri);
82+
checkCancellation(token);
83+
return {
84+
uri,
85+
diagnostics,
86+
context: { document, language: getLanguage(document) }
87+
};
88+
} catch (e) {
89+
this.logService.error(e, 'get_errors failed to open doc with diagnostics');
90+
return undefined;
91+
}
92+
}))));
7093
checkCancellation(token);
7194

7295
const result = new ExtendedLanguageModelToolResult([
@@ -76,27 +99,41 @@ class GetErrorsTool extends Disposable implements ICopilotTool<IGetErrorsParams>
7699
]);
77100

78101
const numDiagnostics = diagnostics.reduce((acc, { diagnostics }) => acc + diagnostics.length, 0);
79-
result.toolResultMessage = numDiagnostics === 0 ?
80-
new MarkdownString(l10n.t`Checked ${this.formatURIs(diagnostics.map(d => d.uri))}, no problems found`) :
81-
numDiagnostics === 1 ?
82-
new MarkdownString(l10n.t`Checked ${this.formatURIs(diagnostics.map(d => d.uri))}, 1 problem found`) :
83-
new MarkdownString(l10n.t`Checked ${this.formatURIs(diagnostics.map(d => d.uri))}, ${numDiagnostics} problems found`);
102+
const formattedURIs = this.formatURIs(diagnostics.map(d => d.uri));
103+
if (options.input.filePaths?.length) {
104+
result.toolResultMessage = numDiagnostics === 0 ?
105+
new MarkdownString(l10n.t`Checked ${formattedURIs}, no problems found`) :
106+
numDiagnostics === 1 ?
107+
new MarkdownString(l10n.t`Checked ${formattedURIs}, 1 problem found`) :
108+
new MarkdownString(l10n.t`Checked ${formattedURIs}, ${numDiagnostics} problems found`);
109+
} else {
110+
result.toolResultMessage = numDiagnostics === 0 ?
111+
new MarkdownString(l10n.t`Checked workspace, no problems found`) :
112+
numDiagnostics === 1 ?
113+
new MarkdownString(l10n.t`Checked workspace, 1 problem found in ${formattedURIs}`) :
114+
new MarkdownString(l10n.t`Checked workspace, ${numDiagnostics} problems found in ${formattedURIs}`);
115+
}
116+
84117
return result;
85118
}
86119

87120
prepareInvocation(options: vscode.LanguageModelToolInvocationPrepareOptions<IGetErrorsParams>, token: vscode.CancellationToken): vscode.ProviderResult<vscode.PreparedToolInvocation> {
88121
if (!options.input.filePaths?.length) {
89-
throw new Error('No file paths provided');
122+
// When no file paths provided, check all files with diagnostics
123+
return {
124+
invocationMessage: new MarkdownString(l10n.t`Checking workspace for problems`),
125+
};
90126
}
127+
else {
128+
const uris = options.input.filePaths.map(filePath => resolveToolInputPath(filePath, this.promptPathRepresentationService));
129+
if (uris.some(uri => uri === undefined)) {
130+
throw new Error('Invalid file path provided');
131+
}
91132

92-
const uris = options.input.filePaths.map(filePath => resolveToolInputPath(filePath, this.promptPathRepresentationService));
93-
if (uris.some(uri => uri === undefined)) {
94-
throw new Error('Invalid file path provided');
133+
return {
134+
invocationMessage: new MarkdownString(l10n.t`Checking ${this.formatURIs(uris)}`),
135+
};
95136
}
96-
97-
return {
98-
invocationMessage: new MarkdownString(l10n.t`Checking ${this.formatURIs(uris)}`),
99-
};
100137
}
101138

102139
private formatURIs(uris: URI[]): string {

src/platform/languages/vscode/languageDiagnosticsServiceImpl.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,16 @@
66
import * as vscode from 'vscode';
77
import { AbstractLanguageDiagnosticsService } from '../common/languageDiagnosticsService';
88

9-
109
export class LanguageDiagnosticsServiceImpl extends AbstractLanguageDiagnosticsService {
11-
12-
10+
private static ignoredSchemes = new Set(['git', 'chat-editing-snapshot-text-model', 'chat-editing-text-model']);
1311
override onDidChangeDiagnostics: vscode.Event<vscode.DiagnosticChangeEvent> = vscode.languages.onDidChangeDiagnostics;
1412

1513
override getDiagnostics(resource: vscode.Uri): vscode.Diagnostic[] {
1614
return vscode.languages.getDiagnostics(resource);
1715
}
1816

1917
override getAllDiagnostics(): [vscode.Uri, vscode.Diagnostic[]][] {
20-
return vscode.languages.getDiagnostics();
18+
return vscode.languages.getDiagnostics()
19+
.filter(([uri]) => !LanguageDiagnosticsServiceImpl.ignoredSchemes.has(uri.scheme));
2120
}
2221
}

0 commit comments

Comments
 (0)