Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/intents/node/testIntent/setupTestsFrameworkQueryInvocation.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 * as l10n from '@vscode/l10n';
7
import { BasePromptElementProps, PromptElement, PromptPiece, PromptSizing, RenderPromptResult, SystemMessage, UserMessage } from '@vscode/prompt-tsx';
8
import type * as vscode from 'vscode';
9
import { IResponsePart } from '../../../../platform/chat/common/chatMLFetcher';
10
import { ChatLocation } from '../../../../platform/chat/common/commonTypes';
11
import { IRunCommandExecutionService } from '../../../../platform/commands/common/runCommandExecutionService';
12
import { TextDocumentSnapshot } from '../../../../platform/editing/common/textDocumentSnapshot';
13
import { IChatEndpoint } from '../../../../platform/networking/common/networking';
14
import { testExtensionsForLanguage } from '../../../../platform/testing/common/setupTestExtensions';
15
import { CancellationToken } from '../../../../util/vs/base/common/cancellation';
16
import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation';
17
import { Intent } from '../../../common/constants';
18
import { IBuildPromptContext } from '../../../prompt/common/intents';
19
import { IToken, StreamingGrammar } from '../../../prompt/common/streamingGrammar';
20
import { IDocumentContext } from '../../../prompt/node/documentContext';
21
import { IIntent, IIntentInvocation, IResponseProcessorContext } from '../../../prompt/node/intents';
22
import { CopilotIdentityRules } from '../../../prompts/node/base/copilotIdentity';
23
import { PromptRenderer } from '../../../prompts/node/base/promptRenderer';
24
import { ResponseTranslationRules } from '../../../prompts/node/base/responseTranslationRules';
25
import { SafetyRules } from '../../../prompts/node/base/safetyRules';
26
import { ChatVariablesAndQuery } from '../../../prompts/node/panel/chatVariables';
27
import { EditorIntegrationRules } from '../../../prompts/node/panel/editorIntegrationRules';
28
import { WorkspaceStructure } from '../../../prompts/node/panel/workspace/workspaceStructure';
29
30
export class SetupTestsFrameworkQueryInvocationRaw {
31
constructor(
32
public readonly endpoint: IChatEndpoint,
33
private readonly documentContext: IDocumentContext | undefined,
34
@IInstantiationService private readonly instantiationService: IInstantiationService,
35
@IRunCommandExecutionService private readonly commandService: IRunCommandExecutionService,
36
) {
37
}
38
public async buildPrompt(
39
context: IBuildPromptContext,
40
progress: vscode.Progress<vscode.ChatResponseReferencePart | vscode.ChatResponseProgressPart> | undefined,
41
token: vscode.CancellationToken,
42
): Promise<RenderPromptResult> {
43
const renderer = PromptRenderer.create(this.instantiationService, this.endpoint, SetupTestsPrompt, {
44
endpoint: this.endpoint,
45
promptContext: context,
46
document: this.documentContext?.document,
47
selection: this.documentContext?.selection,
48
});
49
50
return renderer.render(progress, token);
51
}
52
53
public async processResponse(context: IResponseProcessorContext, inputStream: AsyncIterable<IResponsePart>, outputStream: vscode.ChatResponseStream, token: CancellationToken): Promise<void> {
54
const enum State {
55
Reasoning,
56
Frameworks,
57
}
58
59
const pushTokens = (tokens: Iterable<IToken<State>>) => {
60
for (const token of tokens) {
61
if (token.state === State.Reasoning && token.transitionTo === undefined) {
62
outputStream.markdown(token.token);
63
}
64
}
65
};
66
67
const grammar = new StreamingGrammar(State.Reasoning, {
68
[State.Reasoning]: { [frameworkPrefix]: State.Frameworks },
69
});
70
71
for await (const { delta } of inputStream) {
72
if (token.isCancellationRequested) {
73
return;
74
}
75
76
pushTokens(grammar.append(delta.text));
77
}
78
pushTokens(grammar.flush());
79
80
const frameworks = grammar.accumulate(undefined, undefined, State.Frameworks)
81
.split('\n')
82
.map(line => line.replace(frameworkPrefix, '').trim())
83
.filter(l => !!l);
84
85
if (frameworks.length) {
86
outputStream.confirmation(
87
l10n.t('Pick a testing framework'),
88
l10n.t('Pick from these options, or use chat to tell me what you\'d prefer:'),
89
undefined,
90
frameworks,
91
);
92
} else {
93
outputStream.markdown(l10n.t('Use chat to tell me which framework you\'d prefer.'));
94
}
95
96
await this.commandService.executeCommand('workbench.action.chat.open', {
97
query: `/${Intent.SetupTests} `,
98
isPartialQuery: true,
99
});
100
}
101
}
102
103
/**
104
* Asks the user what framework they want to use to set up their tests.
105
*/
106
export class SetupTestsFrameworkQueryInvocation extends SetupTestsFrameworkQueryInvocationRaw implements IIntentInvocation {
107
constructor(
108
public readonly intent: IIntent,
109
endpoint: IChatEndpoint,
110
public readonly location: ChatLocation,
111
documentContext: IDocumentContext | undefined,
112
@IInstantiationService instantiationService: IInstantiationService,
113
@IRunCommandExecutionService commandService: IRunCommandExecutionService,
114
) {
115
super(endpoint, documentContext, instantiationService, commandService);
116
}
117
}
118
119
const frameworkPrefix = 'FRAMEWORK: ';
120
121
interface WorkspacePromptProps extends BasePromptElementProps {
122
promptContext: IBuildPromptContext;
123
document?: TextDocumentSnapshot;
124
selection?: vscode.Selection;
125
endpoint: IChatEndpoint;
126
}
127
128
class SetupTestsPrompt extends PromptElement<WorkspacePromptProps> {
129
override render(state: void, sizing: PromptSizing): PromptPiece<any, any> | undefined {
130
const { query, chatVariables } = this.props.promptContext;
131
return <>
132
<SystemMessage priority={1000}>
133
You are a software engineer with expert knowledge around software testing frameworks.<br />
134
<br />
135
<CopilotIdentityRules />
136
<SafetyRules />
137
<EditorIntegrationRules />
138
<ResponseTranslationRules />
139
# Additional Rules<br />
140
1. Examine the workspace structure the user is giving you.<br />
141
2. Determine the best testing frameworks that should be used for the project.<br />
142
3. Give a brief explanation why a user would choose one framework over the other, but be concise and never give the user steps to set up the framework.<br />
143
4. If you're unsure which specific framework is best, you can suggest multiple frameworks.<br />
144
5. Suggest only frameworks that are used to run tests. Do not suggest things like assertion libraries or build tools.<br />
145
6. After determining the best framework to use, write out the name of 1 to 3 suggested frameworks prefixed by the phrase "{frameworkPrefix}", for example: "{frameworkPrefix}vitest".<br />
146
<br />
147
DO NOT mention that you cannot read files in the workspace.<br />
148
DO NOT ask the user to provide additional information about files in the workspace.<br />
149
<br />
150
# Example<br />
151
## Question:<br />
152
I am working in a workspace that has the following structure:<br />{`\`\`\`
153
src/
154
index.ts
155
package.json
156
tsconfig.json
157
vite.config.ts
158
\`\`\``}
159
<br />
160
## Response:<br />
161
Because you have a `vite.config.ts` file, it looks like you're working on a browser or Node.js application. If you're working on a browser application, I recommend using Playwright. Otherwise, Vitest is a good choice for Node.js.<br />
162
{frameworkPrefix}playwright<br />
163
{frameworkPrefix}vitest<br />
164
</SystemMessage>
165
{this.props.document && <PreferredExtensions document={this.props.document} />}
166
<UserMessage flexGrow={2}>
167
<SetupWorkspaceStructure />
168
</UserMessage>
169
<ChatVariablesAndQuery flexGrow={2} priority={900} chatVariables={chatVariables} query={query} embeddedInsideUserMessage={false} />
170
</>;
171
}
172
}
173
174
class SetupWorkspaceStructure extends PromptElement {
175
override render(_state: void, sizing: PromptSizing): PromptPiece {
176
return <WorkspaceStructure maxSize={(sizing.tokenBudget * 4) / 3} />;
177
}
178
}
179
180
class PreferredExtensions extends PromptElement<{ document: TextDocumentSnapshot } & BasePromptElementProps> {
181
override render(): PromptPiece | undefined {
182
const extensions = testExtensionsForLanguage.get(this.props.document.languageId);
183
if (!extensions?.perFramework) {
184
return;
185
}
186
187
return <SystemMessage priority={600}>
188
These are the preferred test frameworks for {this.props.document.languageId}:<br />
189
<br />
190
{[...extensions.perFramework.keys()].map(f => `- ${f}`).join('\n')}<br />
191
</SystemMessage>;
192
}
193
}
194
195