Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompts/node/panel/notebookEditCodePrompt.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 } from '@vscode/prompt-tsx';
7
import type { Uri } from 'vscode';
8
import { IAlternativeNotebookContentService } from '../../../../platform/notebook/common/alternativeContent';
9
import { INotebookService } from '../../../../platform/notebook/common/notebookService';
10
import { IPromptPathRepresentationService } from '../../../../platform/prompts/common/promptPathRepresentationService';
11
import { IWorkspaceService } from '../../../../platform/workspace/common/workspaceService';
12
import { findNotebook, getNotebookAndCellFromUri, isJupyterNotebookUri } from '../../../../util/common/notebooks';
13
import { isLocation, isUri } from '../../../../util/common/types';
14
import { coalesce } from '../../../../util/vs/base/common/arrays';
15
import { ResourceSet } from '../../../../util/vs/base/common/map';
16
import { Schemas } from '../../../../util/vs/base/common/network';
17
import { extname } from '../../../../util/vs/base/common/resources';
18
import { URI } from '../../../../util/vs/base/common/uri';
19
import { isNotebookVariable } from '../../../intents/node/editCodeStep';
20
import { ChatVariablesCollection } from '../../../prompt/common/chatVariablesCollection';
21
import { isNotebookWorkingSetEntry, IWorkingSet } from '../../../prompt/common/intents';
22
import { IPromptEndpoint } from '../base/promptRenderer';
23
import { Tag } from '../base/tag';
24
import { ExampleCodeBlock } from './safeElements';
25
26
27
export interface NotebookFormatPromptProps extends BasePromptElementProps {
28
readonly chatVariables: ChatVariablesCollection | IWorkingSet;
29
readonly query: string;
30
}
31
32
export class NotebookReminderInstructions extends PromptElement<NotebookFormatPromptProps> {
33
constructor(
34
props: NotebookFormatPromptProps,
35
@INotebookService private readonly notebookService: INotebookService,
36
@IWorkspaceService private readonly _workspaceService: IWorkspaceService,
37
) {
38
super(props);
39
}
40
41
public override render(_state: void, _sizing: PromptSizing) {
42
const notebookRelatedUris = this.props.chatVariables instanceof ChatVariablesCollection ?
43
getNotebookUrisFromChatVariables(this.props.chatVariables, this._workspaceService, this.notebookService) :
44
this.props.chatVariables.filter(entry => isNotebookWorkingSetEntry(entry)).map(entry => entry.document.uri);
45
if (notebookRelatedUris.length || queryContainsNotebookSpecificKeywords(this.props.query)) {
46
return <>Do not show Cell IDs to the user.<br /></>;
47
}
48
}
49
}
50
51
export class NotebookFormat extends PromptElement<NotebookFormatPromptProps> {
52
constructor(
53
props: NotebookFormatPromptProps,
54
@IPromptPathRepresentationService private readonly promptPathRepresentationService: IPromptPathRepresentationService,
55
@IAlternativeNotebookContentService private readonly alternativeNotebookContentService: IAlternativeNotebookContentService,
56
@INotebookService private readonly notebookService: INotebookService,
57
@IWorkspaceService private readonly _workspaceService: IWorkspaceService,
58
@IPromptEndpoint private readonly _promptEndpoint: IPromptEndpoint,
59
) {
60
super(props);
61
}
62
63
public override render(_state: void, _sizing: PromptSizing) {
64
// These could be cell uris or output uris etc.
65
const notebookRelatedUris = this.props.chatVariables instanceof ChatVariablesCollection ?
66
getNotebookUrisFromChatVariables(this.props.chatVariables, this._workspaceService, this.notebookService) :
67
this.props.chatVariables.filter(entry => isNotebookWorkingSetEntry(entry)).map(entry => entry.document.uri);
68
if (notebookRelatedUris.length || queryContainsNotebookSpecificKeywords(this.props.query)) {
69
const notebookUris = getNotebookUris(notebookRelatedUris, this._workspaceService);
70
return <>
71
<Tag name='notebookFormatInstructions'>
72
{this.getNotebookFormatInstructions(notebookUris)}
73
</Tag>
74
{this.getListOfNotebookFiles(notebookUris)}
75
</>;
76
}
77
}
78
private getListOfNotebookFiles(notebookUris: Uri[]) {
79
if (notebookUris.length) {
80
return <>
81
<br />
82
The following files are notebooks:<br />
83
{notebookUris.map(uri => (<>- {uri.toString()}<br /></>))}
84
<br />
85
</>;
86
} else {
87
return <></>;
88
}
89
}
90
91
private getNotebookFormatInstructions(notebookUris: Uri[]) {
92
const hasJupyterNotebook = notebookUris.some(uri => isJupyterNotebookUri(uri));
93
const extension = (hasJupyterNotebook || notebookUris.length === 0) ? '.ipynb' : extname(notebookUris[0]);
94
const tsExampleFilePath = this.promptPathRepresentationService.getExampleFilePath(`/Users/someone/proj01/example${extension}`);
95
switch (this.alternativeNotebookContentService.getFormat(this._promptEndpoint)) {
96
case 'xml':
97
return <NotebookXmlFormatPrompt tsExampleFilePath={tsExampleFilePath} />;
98
case 'text':
99
return <NotebookTextFormatPrompt tsExampleFilePath={tsExampleFilePath} />;
100
default:
101
return <NotebookJsonFormatPrompt tsExampleFilePath={tsExampleFilePath} />;
102
103
}
104
}
105
}
106
107
function queryContainsNotebookSpecificKeywords(query: string): boolean {
108
const keywords = ['notebook', 'jupyter'];
109
return keywords.some(keyword => query.toLowerCase().includes(keyword));
110
}
111
112
function getNotebookUris(uris: Uri[], workspace: IWorkspaceService): Uri[] {
113
return Array.from(new ResourceSet(coalesce(uris.map(uri => {
114
const nb = findNotebook(uri, workspace.notebookDocuments);
115
if (nb) {
116
return nb.uri;
117
}
118
const info = getNotebookAndCellFromUri(uri, workspace.notebookDocuments);
119
if (info[0]) {
120
return info[0].uri;
121
}
122
return undefined;
123
}))));
124
}
125
126
function getNotebookUrisFromChatVariables(chatVariables: ChatVariablesCollection, workspaceService: IWorkspaceService, notebookService: INotebookService): URI[] {
127
const notebookUris = [];
128
for (const chatVar of chatVariables) {
129
let notebookUri: Uri | undefined;
130
if (isNotebookVariable(chatVar.value)) {
131
// Notebook cell or output
132
const [notebook,] = getNotebookAndCellFromUri(chatVar.value, workspaceService.notebookDocuments);
133
if (chatVar.value.scheme === Schemas.vscodeNotebookCellOutput) {
134
continue;
135
}
136
notebookUri = notebook?.uri;
137
} else if (isUri(chatVar.value)) {
138
notebookUri = chatVar.value;
139
} else if (isLocation(chatVar.value)) {
140
notebookUri = chatVar.value.uri;
141
}
142
if (notebookUri && notebookService.hasSupportedNotebooks(notebookUri)) {
143
notebookUris.push(notebookUri);
144
}
145
}
146
return notebookUris;
147
}
148
149
interface NotebookFormatCommonPromptProps extends BasePromptElementProps {
150
readonly tsExampleFilePath: string;
151
}
152
153
export class NotebookXmlFormatPrompt extends PromptElement<NotebookFormatCommonPromptProps> {
154
constructor(
155
props: NotebookFormatCommonPromptProps
156
) {
157
super(props);
158
}
159
async render(_state: void, _sizing: PromptSizing) {
160
return <>
161
When generating notebook content, use an XML-based format. <br />
162
1. Each cell must be wrapped in a {'<VSCode.Cell>'} with a `language` attribute indicating the type of content. (e.g., `markdown`, `python`). <br />
163
2. Existing cells must contain the `id` attribute to uniquely identify each cell. <br />
164
3. New cells do not need an `id` attribute. <br />
165
4. Ensure that each {'<VSCode.Cell>'} is valid XML and logically structured. <br />
166
5. Do not XML encode the contents within each {'<VSCode.Cell>'} cell. <br />
167
6. Do not reference the XML tags {`<VSCode.Cell>`} in user messages. <br />
168
7. Do not reference Cell Ids (as users cannot see these values) in user messages, instead use the Cell number (starting from 1). <br />
169
<br />
170
Here is sample content of a Notebook document:<br />
171
<br />
172
<Tag name='example'>
173
<ExampleCodeBlock languageId='xml' examplePath={this.props.tsExampleFilePath} includeFilepath={true} minNumberOfBackticks={4}
174
code={[
175
`<VSCode.Cell id="f8939937" language="markdown">`,
176
`# Import Required Libraries`,
177
`Import the necessary libraries, including pandas and plotly.`,
178
`</VSCode.Cell>`,
179
`<VSCode.Cell id="0b4e03d1" language="python">`,
180
`# Import Required Libraries`,
181
`import pandas as pd`,
182
`import plotly.express as px`,
183
`</VSCode.Cell>`,
184
].join('\n')}
185
/>
186
</Tag>
187
</>;
188
}
189
}
190
191
class NotebookJsonFormatPrompt extends PromptElement<NotebookFormatCommonPromptProps> {
192
constructor(
193
props: NotebookFormatCommonPromptProps
194
) {
195
super(props);
196
}
197
async render(_state: void, _sizing: PromptSizing) {
198
return <>
199
When generating notebook content, use a JSON format. <br />
200
1. Each cell must be a valid JSON object within the {'cells'} array property with a `metadata.language` property indicating the type of content (e.g., `markdown`, `python`). <br />
201
2. Existing cells must contain the `metadata.id` property to uniquely identify each cell. <br />
202
3. New cells do not need a `metadata.id` property. <br />
203
4. Ensure the content is valid JSON and logically structured. <br />
204
5. Do not reference Cell Ids (as users cannot see these values) in user messages, instead use the Cell number (starting from 1). <br />
205
<br />
206
Here is sample content of a Notebook document:<br />
207
<br />
208
<Tag name='example'>
209
<ExampleCodeBlock languageId='json' examplePath={this.props.tsExampleFilePath} includeFilepath={true} minNumberOfBackticks={4}
210
code={[
211
`{`,
212
` cells: [`,
213
` {`,
214
` cell_type: "markdown",`,
215
` metadata: {`,
216
` id: "f8939937",`,
217
` language: "markdown"`,
218
` },`,
219
` source: [`,
220
` "# Import Required Libraries",`,
221
` "Import the necessary libraries, including pandas and plotly."`,
222
` ]`,
223
` },`,
224
` {`,
225
` cell_type: "code",`,
226
` metadata: {`,
227
` id: "0b4e03d1",`,
228
` language: "python"`,
229
` },`,
230
` source: [`,
231
` "# Import Required Libraries",`,
232
` "import pandas as pd",`,
233
` "import plotly.express as px"`,
234
` ]`,
235
` }`,
236
` ]`,
237
`}`,
238
].join('\n')}
239
/>
240
</Tag>
241
</>;
242
}
243
}
244
245
class NotebookTextFormatPrompt extends PromptElement<NotebookFormatCommonPromptProps> {
246
constructor(
247
props: NotebookFormatCommonPromptProps
248
) {
249
super(props);
250
}
251
async render(_state: void, _sizing: PromptSizing) {
252
return <>
253
When generating notebook content, use a Jupytext like format. <br />
254
1. Each cell must begin with a comment beginning with `#%% vscode.cell` followed by the cell attributes.<br />
255
2. For existing cell in the document, use the `id` attribute to identify the cell. If the cell is new, DO NOT include the `id` attribute.<br />
256
3. Use the `language` attribute to define the language of the content (e.g., `markdown`, `python`). <br />
257
4. For markdown cells, use triple quotes to wrap the content.<br />
258
5. Ensure that each cell is logically structured. <br />
259
6. Do not reference Cell Ids (as users cannot see these values) in user messages, instead use the Cell number (starting from 1). <br />
260
<br />
261
Here is sample content of a Notebook document:<br />
262
<br />
263
<Tag name='example'>
264
<ExampleCodeBlock languageId='python' examplePath={this.props.tsExampleFilePath} includeFilepath={true} minNumberOfBackticks={4}
265
code={[
266
`#%% vscode.cell [id=0fd89b28] [language=markdown]`,
267
`"""`,
268
`# Import Required Libraries`,
269
`Import the necessary libraries, including pandas and plotly.`,
270
`"""`,
271
`#%% vscode.cell [id=0b4e03d1] [language=python]`,
272
`# Import Required Libraries`,
273
`import pandas as pd`,
274
`import plotly.express as px`,
275
].join('\n')}
276
/>
277
</Tag>
278
</>;
279
}
280
}
281
282