Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/intents/node/testIntent/userQueryParser.tsx
13405 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { PromptElement, PromptElementProps, PromptSizing, SystemMessage, UserMessage } from '@vscode/prompt-tsx';
7
import { ChatLocation } from '../../../../platform/chat/common/commonTypes';
8
import { IEndpointProvider } from '../../../../platform/endpoint/common/endpointProvider';
9
import { ILogService } from '../../../../platform/log/common/logService';
10
import { CancellationToken } from '../../../../util/vs/base/common/cancellation';
11
import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation';
12
import { PromptRenderer } from '../../../prompts/node/base/promptRenderer';
13
14
15
type ParsedUserQuery = {
16
/**
17
* File reference to test.
18
*/
19
fileToTest?: string;
20
/**
21
* Symbols in {fileToTest} to generate tests for.
22
* Can be undefined if cannot be identified from user query.
23
*/
24
symbolsToTest?: string[];
25
};
26
27
export class UserQueryParser {
28
constructor(
29
@IEndpointProvider private readonly endpointProvider: IEndpointProvider,
30
@IInstantiationService private readonly instantiationService: IInstantiationService,
31
@ILogService private readonly logService: ILogService,
32
) { }
33
34
public async parse(query: string): Promise<ParsedUserQuery | null> {
35
const endpoint = await this.endpointProvider.getChatEndpoint('copilot-fast');
36
const promptRenderer = PromptRenderer.create(
37
this.instantiationService,
38
endpoint,
39
Prompt,
40
{ query }
41
);
42
const renderResult = await promptRenderer.render();
43
const r = await endpoint.makeChatRequest(
44
'testGenParseUserQuery',
45
renderResult.messages,
46
undefined,
47
CancellationToken.None,
48
ChatLocation.Other
49
);
50
return r.type === 'success' ? this.processResponse(r.value) : null;
51
}
52
53
private processResponse(response: string) {
54
55
// remove first (1-based) and last lines of response if they're backticks (```)
56
const lines = response.split(/\r\n|\r|\n/).filter(s => s !== '');
57
if (lines.at(0) !== '```') {
58
lines.splice(0, 1);
59
if (lines.at(-1) === '```') {
60
lines.splice(lines.length - 1, 1);
61
}
62
response = lines.join('\n');
63
}
64
65
let parsedJson: unknown;
66
try {
67
parsedJson = JSON.parse(response);
68
} catch (e) {
69
this.logService.error(`Failed to parse user query response\nResponse:\n${response}\nError:\n${e}`);
70
return null;
71
}
72
return this.isParsedUserQuery(parsedJson) ? parsedJson : null;
73
}
74
75
private isParsedUserQuery(obj: unknown): obj is ParsedUserQuery {
76
if (typeof obj !== 'object' || obj === null) {
77
return false;
78
}
79
80
const parsedUserQuery = obj as ParsedUserQuery;
81
82
if (parsedUserQuery.fileToTest !== undefined && typeof parsedUserQuery.fileToTest !== 'string') {
83
return false;
84
}
85
86
if (parsedUserQuery.symbolsToTest !== undefined) {
87
if (!Array.isArray(parsedUserQuery.symbolsToTest)) {
88
return false;
89
}
90
for (const symbol of parsedUserQuery.symbolsToTest) {
91
if (typeof symbol !== 'string') {
92
return false;
93
}
94
}
95
}
96
97
return true;
98
}
99
100
}
101
102
type Props = PromptElementProps<{
103
query: string;
104
}>;
105
106
class Prompt extends PromptElement<Props> {
107
108
constructor(
109
props: PromptElementProps<{ query: string }>,
110
) {
111
super(props);
112
}
113
114
override render(state: void, sizing: PromptSizing) {
115
116
const { query } = this.props;
117
118
const format = `
119
You are a helpful assistant that parses user queries.
120
The user is a software developer that is asking an AI programming assistant to generate tests.
121
Your job is to parse the user query into a JSON object of the following shape:
122
123
\`\`\`typescript
124
{
125
/**
126
* File reference to test.
127
*/
128
fileToTest?: string;
129
/**
130
* Symbols in {fileToTest} to generate tests for.
131
* Can be undefined if cannot be identified from user query.
132
*/
133
symbolsToTest?: string[];
134
}
135
\`\`\`
136
137
You must return a JSON object of the given shape.
138
`;
139
return (<>
140
<SystemMessage>
141
{format}
142
</SystemMessage>
143
<UserMessage>
144
User query: {query}<br />
145
Parsed query:<br />
146
</UserMessage>
147
</>);
148
}
149
}
150
151