Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompts/node/inline/inlineChatNotebookFixPrompt.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 { BasePromptElementProps, PromptElement, PromptSizing, SystemMessage, UserMessage } from '@vscode/prompt-tsx';
7
import type * as vscode from 'vscode';
8
import { TextDocumentSnapshot } from '../../../../platform/editing/common/textDocumentSnapshot';
9
import { IIgnoreService } from '../../../../platform/ignore/common/ignoreService';
10
import { ILanguageDiagnosticsService, rangeSpanningDiagnostics } from '../../../../platform/languages/common/languageDiagnosticsService';
11
import { IParserService } from '../../../../platform/parser/node/parserService';
12
import { ITabsAndEditorsService } from '../../../../platform/tabs/common/tabsAndEditorsService';
13
import { IWorkspaceService } from '../../../../platform/workspace/common/workspaceService';
14
import { ILanguage } from '../../../../util/common/languages';
15
import { isJupyterNotebookUri, isNotebookCellOrNotebookChatInput } from '../../../../util/common/notebooks';
16
import { illegalArgument } from '../../../../util/vs/base/common/errors';
17
import { Schemas } from '../../../../util/vs/base/common/network';
18
import { StringEdit } from '../../../../util/vs/editor/common/core/edits/stringEdit';
19
import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation';
20
import { Range, Uri } from '../../../../vscodeTypes';
21
import { findDiagnosticForSelectionAndPrompt, findFixRangeOfInterest, generateFixContext } from '../../../context/node/resolvers/fixSelection';
22
import { generateNotebookCellContext } from '../../../context/node/resolvers/inlineChatSelection';
23
import { InlineFixProps } from '../../../context/node/resolvers/inlineFixIntentInvocation';
24
import { getStructure } from '../../../context/node/resolvers/selectionContextHelpers';
25
import { CodeContextRegion } from '../../../inlineChat/node/codeContextRegion';
26
import { IDocumentContext } from '../../../prompt/node/documentContext';
27
import { ReplyInterpreterMetaData } from '../../../prompt/node/intents';
28
import { CompositeElement } from '../base/common';
29
import { InstructionMessage } from '../base/instructionMessage';
30
import { IPromptEndpoint } from '../base/promptRenderer';
31
import { LegacySafetyRules } from '../base/safetyRules';
32
import { Tag } from '../base/tag';
33
import { PatchEditExamplePatch, PatchEditInputCodeBlock, PatchEditRules } from '../codeMapper/patchEditGeneration';
34
import { JupyterNotebookRules } from '../notebook/commonPrompts';
35
import { ChatToolReferences, ChatVariables, UserQuery } from '../panel/chatVariables';
36
import { HistoryWithInstructions } from '../panel/conversationHistory';
37
import { CustomInstructions } from '../panel/customInstructions';
38
import { CodeBlock } from '../panel/safeElements';
39
import { PatchEditFixReplyInterpreter } from './inlineChatFix3Prompt';
40
import { NotebookPromptPriority, promptPriorities } from './inlineChatNotebookCommon';
41
import { InlineChatCellSelectionProps, InlineChatCustomNotebookCellsContextRenderer, InlineChatCustomNotebookInfoRenderer, InlineChatNotebookSelectionCommonProps, InlineChatNotebookVariables, NotebookCellList } from './inlineChatNotebookCommonPromptElements';
42
import { ProjectedDocument } from './summarizedDocument/summarizeDocument';
43
import { summarizeDocumentSync } from './summarizedDocument/summarizeDocumentHelpers';
44
45
const FIX_SELECTION_LENGTH_THRESHOLD = 15;
46
interface InlineChatNotebookFixPromptState {
47
isIgnored: boolean;
48
priorities: NotebookPromptPriority;
49
}
50
51
52
export class InlineFixNotebookPrompt extends PromptElement<InlineFixProps, InlineChatNotebookFixPromptState> {
53
54
constructor(
55
props: InlineFixProps,
56
@IIgnoreService private readonly _ignoreService: IIgnoreService,
57
@IInstantiationService private readonly _instantiationService: IInstantiationService,
58
@ILanguageDiagnosticsService private readonly _languageDiagnosticsService: ILanguageDiagnosticsService,
59
@IParserService private readonly _parserService: IParserService,
60
@ITabsAndEditorsService private readonly _tabsAndEditorsService: ITabsAndEditorsService,
61
@IWorkspaceService private readonly _workspaceService: IWorkspaceService,
62
@IPromptEndpoint private readonly _promptEndpoint: IPromptEndpoint
63
) {
64
super(props);
65
}
66
67
override async prepare(sizing: PromptSizing): Promise<InlineChatNotebookFixPromptState> {
68
const { documentContext: context } = this.props;
69
const isIgnored = await this._ignoreService.isCopilotIgnored(context.document.uri);
70
71
return {
72
isIgnored,
73
priorities: promptPriorities
74
};
75
}
76
77
async render(state: InlineChatNotebookFixPromptState, sizing: PromptSizing) {
78
const documentContext = this.props.documentContext;
79
80
if (!isNotebookCellOrNotebookChatInput(documentContext.document.uri)) {
81
throw illegalArgument('InlineFixNotebookPrompt should not be used with a non-notebook!');
82
}
83
84
if (state.isIgnored) {
85
return <ignoredFiles value={[documentContext.document.uri]} />;
86
}
87
88
const { query, history, chatVariables } = this.props.promptContext;
89
const selection = documentContext.selection;
90
91
// find the diagnostics of interest and the selection of interest surrounding the diagnostics
92
const diagnostics = findDiagnosticForSelectionAndPrompt(this._languageDiagnosticsService, documentContext.document.uri, documentContext.selection, query);
93
const range = diagnostics.length > 0 ? rangeSpanningDiagnostics(diagnostics) : documentContext.selection;
94
95
const treeSitterAST = this._parserService.getTreeSitterAST(documentContext.document);
96
const rangeOfInterest = treeSitterAST ? await findFixRangeOfInterest(treeSitterAST, range, FIX_SELECTION_LENGTH_THRESHOLD) : range;
97
98
const fixContext = generateFixContext(this._promptEndpoint, documentContext, range, rangeOfInterest);
99
const inputDocCharLimit = (sizing.endpoint.modelMaxPromptTokens / 3) * 4; // consume one 3rd of the model window, estimating roughly 4 chars per token;
100
let projectedDocument: ProjectedDocument;
101
let isSummarized = false;
102
if (documentContext.document.getText().length > inputDocCharLimit) {
103
// only compute the summarized document if needed
104
const structure = await getStructure(this._parserService, documentContext.document, documentContext.fileIndentInfo);
105
projectedDocument = summarizeDocumentSync(inputDocCharLimit, documentContext.document, documentContext.wholeRange, structure, { tryPreserveTypeChecking: true });
106
isSummarized = true;
107
} else {
108
projectedDocument = new ProjectedDocument(documentContext.document.getText(), StringEdit.empty, documentContext.document.languageId);
109
}
110
111
const adjustedSelection = projectedDocument.projectRange(selection);
112
const selectedLinesContent = documentContext.document.getText(new Range(selection.start.line, 0, selection.end.line + 1, 0)).trimEnd();
113
114
const contextInfo = generateNotebookCellContext(this._tabsAndEditorsService, this._workspaceService, documentContext, fixContext.contextInfo, fixContext.tracker);
115
const replyInterpreter = this._instantiationService.createInstance(PatchEditFixReplyInterpreter, projectedDocument, documentContext.document.uri, adjustedSelection);
116
117
// const replyInterpreter = this._instantiationService.createInstance(FixNotebookReplyInterpreter, range, contextInfo, documentContext);
118
const exampleUri = Uri.file('/someFolder/myFile.ts');
119
const priorities = state.priorities;
120
return (
121
<>
122
<meta value={new ReplyInterpreterMetaData(replyInterpreter)} />
123
<SystemMessage priority={priorities.core}>
124
You are an AI programming assistant.<br />
125
When asked for your name, you must respond with "GitHub Copilot".<br />
126
You are a world class expert in programming, and especially good at {documentContext.language.languageId}.<br />
127
Source code is always contained in ``` blocks.<br />
128
</SystemMessage>
129
<HistoryWithInstructions inline={true} passPriority historyPriority={priorities.history ?? 700} history={history}>
130
<InstructionMessage priority={priorities.core}>
131
The user needs help to write some new code.<br />
132
<JupyterNotebookRules />
133
When dealing with Jupyter Notebook, do not generate CELL INDEX in the code blocks in your answer, it is only used to help you understand the context.<br />
134
If you suggest to run a terminal command, use a code block that starts with ```bash.<br />
135
When fixing "ModuleNotFoundError" or "Import could not be resolved" errors, always use magic command "%pip install" to add the missing packages. The imports MUST be inserted at the top of the code block and it should not replace existing code.<br />
136
You should not import the same module twice.<br />
137
<PatchEditRules />
138
<LegacySafetyRules />
139
<Tag name='example' priority={100}>
140
<Tag name='user'>
141
I have the following code open in the editor.<br />
142
<PatchEditInputCodeBlock
143
uri={exampleUri}
144
languageId='csharp'
145
code={['// This is my class', 'class C { }', '', 'new C().Field = 9;']}
146
/>
147
</Tag>
148
<Tag name='assistant'>
149
The problem is that the class 'C' does not have a field or property named 'Field'. To fix this, you need to add a 'Field' property to the 'C' class.<br />
150
<br />
151
<PatchEditExamplePatch
152
changes={
153
[
154
{
155
uri: exampleUri,
156
find: ['// This is my class', 'class C { }'],
157
replace: ['// This is my class', 'class C {', 'public int Field { get; set; }', '}']
158
},
159
{
160
uri: exampleUri,
161
find: ['new C().Field = 9;'],
162
replace: ['// set the field to 9', 'new C().Field = 9;']
163
}
164
]
165
}
166
/>
167
</Tag>
168
</Tag>
169
</InstructionMessage>
170
</HistoryWithInstructions>
171
<UserMessage priority={priorities.context}>
172
<CustomInstructions languageId={documentContext.language.languageId} chatVariables={chatVariables} />
173
</UserMessage>
174
<ChatToolReferences priority={priorities.context} promptContext={this.props.promptContext} flexGrow={1} embeddedInsideUserMessage={false} />
175
<ChatVariables priority={priorities.context} chatVariables={chatVariables} embeddedInsideUserMessage={false} />
176
<InlineChatFixNotebookSelectionRenderer
177
priority={priorities.core}
178
documentContext={documentContext}
179
aboveCells={contextInfo.aboveCells}
180
belowCells={contextInfo.belowCells}
181
document={documentContext.document}
182
projectedDocument={projectedDocument}
183
language={documentContext.language}
184
diagnostics={diagnostics}
185
selection={documentContext.selection}
186
adjustedSelection={adjustedSelection}
187
isSummarized={isSummarized}
188
selectedLinesContent={selectedLinesContent}
189
/>
190
<InlineChatNotebookVariables notebookURI={this.props.documentContext.document.uri} priority={priorities.runtimeCore} priorities={priorities} query={query} />
191
<UserMessage priority={priorities.core}>
192
{/* <Diagnostics documentContext={documentContext} diagnostics={diagnostics} /> */}
193
{/* Describe in a single sentence how you would solve the problem. After that sentence, add an empty line. Then add a code block with the fix. */}
194
Please find a fix for my code so that the result is without any errors.<br />
195
<UserQuery chatVariables={chatVariables} query={query} /><br />
196
</UserMessage>
197
</>
198
);
199
}
200
}
201
202
interface InlineChatNotebookSelectionRendererProps extends InlineChatNotebookSelectionCommonProps {
203
aboveCells?: CodeContextRegion[];
204
belowCells?: CodeContextRegion[];
205
206
readonly document: TextDocumentSnapshot;
207
readonly projectedDocument: ProjectedDocument;
208
readonly language: ILanguage;
209
readonly diagnostics: vscode.Diagnostic[];
210
readonly selection: vscode.Selection;
211
readonly adjustedSelection: Range;
212
readonly isSummarized: boolean;
213
readonly selectedLinesContent: string;
214
}
215
216
class InlineChatFixNotebookSelectionRenderer extends PromptElement<InlineChatNotebookSelectionRendererProps> {
217
218
render(state: void, sizing: PromptSizing) {
219
if (this.props.documentContext.document.uri.scheme !== Schemas.vscodeNotebookCell) {
220
throw illegalArgument('InlineChatNotebookSelectionRenderer should be used only with a notebook!');
221
}
222
223
const jupyterNotebook = isJupyterNotebookUri(this.props.documentContext.document.uri);
224
const { projectedDocument, aboveCells, belowCells } = this.props;
225
const aboveCellsInfo = aboveCells || [];
226
const belowCellsInfo = belowCells || [];
227
const lang = this.props.documentContext.language;
228
229
return (
230
<>
231
{
232
jupyterNotebook
233
? <>
234
<InlineChatFixNotebookCellsContextRenderer documentContext={this.props.documentContext} aboveCells={aboveCellsInfo} belowCells={belowCellsInfo} />
235
</>
236
: <>
237
<InlineChatCustomNotebookInfoRenderer documentContext={this.props.documentContext} />
238
<InlineChatCustomNotebookCellsContextRenderer documentContext={this.props.documentContext} aboveCells={aboveCellsInfo} belowCells={belowCellsInfo} />
239
</>
240
}
241
<NotebookCellSelection cellIndex={aboveCellsInfo.length} document={this.props.document} projectedDocument={projectedDocument} language={lang} diagnostics={this.props.diagnostics} selection={this.props.selection} adjustedSelection={this.props.adjustedSelection} isSummarized={this.props.isSummarized} selectedLinesContent={this.props.selectedLinesContent} />
242
</>
243
);
244
}
245
}
246
247
class NotebookCellSelection extends PromptElement<InlineChatCellSelectionProps> {
248
override render() {
249
const { cellIndex, document, projectedDocument, diagnostics, language, selection, adjustedSelection, isSummarized, selectedLinesContent } = this.props;
250
const notebookType = isNotebookCellOrNotebookChatInput(document.uri) ? 'Jupyter' : 'custom';
251
const isMarkdown = language.languageId === 'markdown';
252
253
return <>
254
<UserMessage>
255
Now I create a new cell in this {notebookType} Notebook document at index {this.props.cellIndex}.<br />
256
{isMarkdown && <>This is a markdown cell. Markdown cell is used to describe and document your workflow.<br /></>}
257
<NotebookCellRenderer cellIndex={cellIndex} document={document} projectedDocument={projectedDocument} diagnostics={diagnostics} language={language} selection={selection} adjustedSelection={adjustedSelection} isSummarized={isSummarized} selectedLinesContent={selectedLinesContent} />
258
</UserMessage>
259
</>;
260
}
261
}
262
263
class NotebookCellRenderer extends PromptElement<InlineChatCellSelectionProps> {
264
constructor(
265
props: InlineChatCellSelectionProps
266
) {
267
super(props);
268
}
269
270
override render() {
271
const { document, projectedDocument, diagnostics, language, selection, adjustedSelection, isSummarized, selectedLinesContent } = this.props;
272
const isMarkdown = language.languageId === 'markdown';
273
274
return <>
275
<CompositeElement>
276
{
277
projectedDocument.text.length > 0 ?
278
<>
279
{
280
isMarkdown ?
281
<>I have the following markdown content in this cell, starting from line 1 to line {projectedDocument.lineCount}.<br /></> :
282
<>I have the following code in this cell, starting from line 1 to line {projectedDocument.lineCount}.<br /></>
283
}
284
285
<PatchEditInputCodeBlock uri={document.uri} languageId={language.languageId} code={projectedDocument.text} shouldTrim={false} isSummarized={isSummarized} /><br />
286
</> :
287
<>
288
I am in an empty file:
289
<PatchEditInputCodeBlock uri={document.uri} languageId={language.languageId} code={projectedDocument.text} shouldTrim={false} isSummarized={isSummarized} /><br />
290
</>
291
}
292
</CompositeElement >
293
<CompositeElement>
294
{
295
selection.isEmpty ?
296
<>
297
I have the selection at line {adjustedSelection.start.line + 1}, column {adjustedSelection.start.character + 1}<br />
298
</> :
299
<>
300
I have currently selected from line {adjustedSelection.start.line + 1}, column {adjustedSelection.start.character + 1} to line {adjustedSelection.end.line + 1} column {adjustedSelection.end.character + 1}.<br />
301
</>
302
}
303
</CompositeElement >
304
<CompositeElement>
305
{
306
selectedLinesContent.length && !diagnostics.some(d => d.range.contains(selection)) &&
307
<>
308
The content of the lines at the selection is
309
<CodeBlock uri={document.uri} languageId={language.languageId} code={selectedLinesContent} shouldTrim={false} /><br />
310
</>
311
}
312
</CompositeElement >
313
</>;
314
}
315
}
316
317
interface InlineChatFixNotebookCellsContextRendererProps extends BasePromptElementProps {
318
documentContext: IDocumentContext;
319
aboveCells: CodeContextRegion[];
320
belowCells: CodeContextRegion[];
321
}
322
323
/**
324
* Notebook cell context renderer. Used by Fix intents.
325
* It's using following example for llm response:
326
* ---FILEPATH Untitled-1<br />
327
---FIND<br />
328
---REPLACE<br />
329
```python<br />
330
df.plot(x='Name', y='Age', kind='bar')<br />
331
```<br />
332
---COMPLETE<br />
333
* However, we don't use this in Generate and Edit intents yet.
334
*/
335
class InlineChatFixNotebookCellsContextRenderer extends PromptElement<InlineChatFixNotebookCellsContextRendererProps> {
336
render(state: void, sizing: PromptSizing) {
337
if (!isNotebookCellOrNotebookChatInput(this.props.documentContext.document.uri)) {
338
throw illegalArgument('InlineChatNotebookSelectionRenderer should be used only with a notebook!');
339
}
340
341
const { aboveCells: aboveCellsInfo, belowCells: belowCellsInfo } = this.props;
342
const lang = this.props.documentContext.language;
343
344
return (
345
<>
346
{
347
(aboveCellsInfo.length > 0 || belowCellsInfo.length > 0) &&
348
<UserMessage>
349
I am working on a Jupyter notebook.<br />
350
This Jupyter Notebook already contains multiple cells.<br />
351
The content of cells are listed below, each cell starts with CELL INDEX and a code block started with ```{lang.languageId}<br />
352
Each cell is a block of code that can be executed independently.<br />
353
Since it is Jupyter Notebook, if a module is already imported in a cell, it can be used in other cells as well.<br />
354
For the same reason, if a variable is defined in a cell, it can be used in other cells as well.<br />
355
We should not repeat the same import or variable definition in multiple cells, unless we want to overwrite the previous definition.<br />
356
Do not generate CELL INDEX in your answer, it is only used to help you understand the context.<br />
357
<br />
358
<>Below you will find a set of examples of what you should respond with. Please follow the exmaples on how to avoid repeating code.<br />
359
## Examples starts here<br />
360
Here are the cells in this Jupyter Notebook:<br />
361
`CELL INDEX: 0<br />
362
```python<br />
363
import pandas as pd<br />
364
<br />
365
# create a dataframe with sample data<br />
366
df = pd.DataFrame(&#123;'Name': ['Alice', 'Bob', 'Charlie'], 'Age': [25, 30, 35], 'Gender': ['F', 'M', 'M']&#125;)<br />
367
print(df)<br />
368
```<br />
369
---------------------------------<br />
370
USER:<br />
371
Now I create a new cell in this Jupyter Notebook document at index 1.<br />
372
I have the following code open in this cell, starting from line 1 to line 1.<br />
373
```python<br />
374
```<br />
375
---------------------------------<br />
376
USER:<br />
377
plot the data frame<br />
378
<br />
379
---------------------------------<br />
380
Assistant Answer<br />
381
---------------------------------<br />
382
To plot the dataframe, we can use the `plot()` method of pandas dataframe.<br />
383
<br />
384
---FILEPATH Untitled-1<br />
385
---FIND<br />
386
---REPLACE<br />
387
```python<br />
388
df.plot(x='Name', y='Age', kind='bar')<br />
389
```<br />
390
---COMPLETE<br />
391
## Example ends here<br />
392
</>
393
394
{aboveCellsInfo.length > 0 && <NotebookCellList cells={aboveCellsInfo} title={'Here are the cells in this Jupyter Notebook:\n'} />}
395
{belowCellsInfo.length > 0 && <NotebookCellList cells={belowCellsInfo} cellIndexDelta={aboveCellsInfo.length + 1} title={'Here are the cells below the current cell that I am editing in this Jupyter Notebook:\n'} />}
396
</UserMessage>
397
}
398
</>
399
);
400
}
401
}
402